Skip to content

Commit b89f4bd

Browse files
committed
Installer mines straight to a wallet address given as GHOST_MINE_TO
install.sh, install.ps1 and install.cmd read GHOST_MINE_TO. It must be 0x followed by exactly 40 hex characters; anything else is refused before anything is downloaded or installed. With it, the config's Etherbase is that address and no mining key or password file is made. A re-run, or an install.sh --update, with GHOST_MINE_TO set rewrites only the Etherbase line and restarts the node; without it the config is kept as before. Unset, the installers make a new key exactly as before. Every install now ends by printing 'mining to: <address>', and each script's header gives the one-line examples for sh and PowerShell. The Windows scripts write or rewrite the config before ghost-setup runs, since ghost-setup keeps an existing config and so makes no key. ghost-setup's own --update, the daily one at login, does not read the variable: a Windows tester changes the address by running the install line again. Tests cover a fresh install mining to the wallet with no keystore account, re-runs and updates with a new, the same or no address, malformed addresses refused with nothing installed, the unset case, and the Windows scripts' check, config template and order of steps.
1 parent 853ca88 commit b89f4bd

5 files changed

Lines changed: 716 additions & 23 deletions

File tree

‎install/install.cmd‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ rem GHOST_RELEASE_URL sets where the release is downloaded from, over https
2222
rem only, and GHOST_INSTALL_CA_FILE names a certificate file to trust for that
2323
rem server; ghost-setup reads both too.
2424
rem
25+
rem GHOST_MINE_TO mines straight to a wallet address of your own, such as the
26+
rem Ghost pop-up wallet's, instead of a newly generated mining key. In CMD,
27+
rem run set "GHOST_MINE_TO=0x..." before the line above. The same in
28+
rem PowerShell and on a Mac or Linux:
29+
rem
30+
rem $env:GHOST_MINE_TO='0x...'; irm https://get.ghostcoin.com/win | iex
31+
rem curl -fsSL https://get.ghostcoin.com | GHOST_MINE_TO=0x... sh
32+
rem
33+
rem It must be 0x followed by exactly 40 hex characters; anything else is
34+
rem refused with nothing installed. This script then writes the config with
35+
rem that address as Etherbase before ghost-setup runs, so ghost-setup keeps it
36+
rem and makes no mining key or password file; on a re-run it rewrites the
37+
rem config's Etherbase to the address, and ghost-setup restarts the node.
38+
rem Unset, the config is left to ghost-setup as before. Either way the script
39+
rem ends by printing the address the node mines to.
40+
rem
2541
rem It uses no labels, so it runs the same whatever its line endings.
2642

2743
setlocal EnableExtensions DisableDelayedExpansion
@@ -42,6 +58,41 @@ if /i not "%RELEASE_URL:~0,8%"=="https://" (
4258
exit /b 1
4359
)
4460

61+
rem GHOST_MINE_TO, when set, is the wallet address to mine to: 0x and 40 hex
62+
rem characters. It is read with delayed expansion only, so no character in it
63+
rem can run as a command, and used with %% only once it is known to be hex.
64+
rem MINE_TO_OK is 1 when it is unset or such an address. GHOST_HTTP_PORT is
65+
rem checked the same way, as the config written for it holds the port.
66+
set "MINE_TO_OK=1"
67+
set "HTTP_PORT=8545"
68+
if defined GHOST_MINE_TO (
69+
setlocal EnableDelayedExpansion
70+
set "MINE_TO_OK="
71+
set "REST=!GHOST_MINE_TO:~2!"
72+
for %%C in (0 1 2 3 4 5 6 7 8 9 a b c d e f) do if defined REST set "REST=!REST:%%C=!"
73+
if "!GHOST_MINE_TO:~0,2!"=="0x" if not "!GHOST_MINE_TO:~41,1!"=="" if "!GHOST_MINE_TO:~42!"=="" if not defined REST set "MINE_TO_OK=1"
74+
if defined GHOST_HTTP_PORT (
75+
set "REST=!GHOST_HTTP_PORT!"
76+
for %%C in (0 1 2 3 4 5 6 7 8 9) do if defined REST set "REST=!REST:%%C=!"
77+
if defined REST set "MINE_TO_OK="
78+
if "!GHOST_HTTP_PORT:~0,1!"=="0" set "MINE_TO_OK="
79+
if not "!GHOST_HTTP_PORT:~5!"=="" set "MINE_TO_OK="
80+
if not defined REST if !GHOST_HTTP_PORT! gtr 65535 set "MINE_TO_OK="
81+
)
82+
for %%R in ("!MINE_TO_OK!") do endlocal & set "MINE_TO_OK=%%~R"
83+
)
84+
if not defined MINE_TO_OK (
85+
echo ghost-node: GHOST_MINE_TO must be a wallet address, 0x followed by exactly 40 hex characters, and GHOST_HTTP_PORT a port number; %REFUSAL% 1>&2
86+
exit /b 1
87+
)
88+
if defined GHOST_MINE_TO if defined GHOST_HTTP_PORT set "HTTP_PORT=%GHOST_HTTP_PORT%"
89+
set "NODE_HOME=%USERPROFILE%\.ghost-node"
90+
set "CONFIG=%NODE_HOME%\config.toml"
91+
if defined GHOST_MINE_TO if not defined USERPROFILE (
92+
echo ghost-node: USERPROFILE is not set to a directory; %REFUSAL% 1>&2
93+
exit /b 1
94+
)
95+
4596
set "MACHINE=%PROCESSOR_ARCHITECTURE%"
4697
if defined PROCESSOR_ARCHITEW6432 set "MACHINE=%PROCESSOR_ARCHITEW6432%"
4798
set "ARCH="
@@ -93,7 +144,52 @@ if /i not "%HASH%"=="%PINNED%" (
93144
exit /b 1
94145
)
95146

147+
rem With GHOST_MINE_TO, the config mines to its address before ghost-setup
148+
rem runs: ghost-setup keeps an existing config, and so makes no mining key. A
149+
rem new config is written whole; an existing one gets its Etherbase line
150+
rem replaced, every other line kept.
151+
set "FRESH="
152+
set "REWRITE="
153+
if defined GHOST_MINE_TO if not exist "%CONFIG%" set "FRESH=1"
154+
if defined GHOST_MINE_TO if exist "%CONFIG%" set "REWRITE=1"
155+
set "DATA_TOML=%NODE_HOME:\=\\%\\data"
156+
if defined FRESH mkdir "%NODE_HOME%" 2>nul
157+
if defined FRESH >"%CONFIG%.new" echo # The Ghost node's config, written by install.cmd. The node mines to Etherbase,
158+
if defined FRESH >>"%CONFIG%.new" echo # the wallet address GHOST_MINE_TO named, whose key is not on this computer.
159+
if defined FRESH >>"%CONFIG%.new" echo # Its HTTP RPC listens on this computer only, for the Ghost web apps.
160+
if defined FRESH >>"%CONFIG%.new" echo(
161+
if defined FRESH >>"%CONFIG%.new" echo [Eth.Miner]
162+
if defined FRESH >>"%CONFIG%.new" echo Etherbase = "%GHOST_MINE_TO%"
163+
if defined FRESH >>"%CONFIG%.new" echo(
164+
if defined FRESH >>"%CONFIG%.new" echo [Node]
165+
if defined FRESH >>"%CONFIG%.new" echo DataDir = "%DATA_TOML%"
166+
if defined FRESH >>"%CONFIG%.new" echo HTTPHost = "127.0.0.1"
167+
if defined FRESH >>"%CONFIG%.new" echo HTTPPort = %HTTP_PORT%
168+
if defined FRESH >>"%CONFIG%.new" echo HTTPModules = ["eth", "net", "web3"]
169+
if defined FRESH >>"%CONFIG%.new" echo HTTPCors = ["https://ghost-webapp-alpha.vercel.app", "https://ghost-wallet-nine.vercel.app"]
170+
if defined REWRITE type nul >"%CONFIG%.new"
171+
if defined REWRITE for /f "delims=" %%L in ('findstr /n "^" "%CONFIG%"') do (
172+
set "LINE=%%L"
173+
setlocal EnableDelayedExpansion
174+
set "LINE=!LINE:*:=!"
175+
if "!LINE:~0,10!"=="Etherbase " (
176+
>>"%CONFIG%.new" echo Etherbase = "%GHOST_MINE_TO%"
177+
) else (
178+
>>"%CONFIG%.new" echo(!LINE!
179+
)
180+
endlocal
181+
)
182+
set "HAS_ETHERBASE="
183+
if defined REWRITE findstr /b /l /c:"Etherbase " "%CONFIG%" >nul 2>nul && set "HAS_ETHERBASE=1"
184+
if defined REWRITE if not defined HAS_ETHERBASE >>"%CONFIG%.new" echo(
185+
if defined REWRITE if not defined HAS_ETHERBASE >>"%CONFIG%.new" echo [Eth.Miner]
186+
if defined REWRITE if not defined HAS_ETHERBASE >>"%CONFIG%.new" echo Etherbase = "%GHOST_MINE_TO%"
187+
if defined GHOST_MINE_TO move /y "%CONFIG%.new" "%CONFIG%" >nul
188+
if defined GHOST_MINE_TO echo ghost-node: the config "%CONFIG%" mines to %GHOST_MINE_TO%
189+
96190
"%SETUP%"
97191
set "STATUS=%ERRORLEVEL%"
98192
rmdir /s /q "%WORK%"
193+
rem Either way, say the address the node mines to.
194+
if "%STATUS%"=="0" if exist "%CONFIG%" for /f "tokens=2 delims==" %%A in ('findstr /b /l /c:"Etherbase " "%CONFIG%"') do for %%B in (%%A) do echo ghost-node: mining to: %%~B
99195
exit /b %STATUS%

‎install/install.ps1‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@
2121
# server; ghost-setup reads both too. The tests use them for a local release
2222
# server.
2323
#
24+
# GHOST_MINE_TO mines straight to a wallet address of your own, such as the
25+
# Ghost pop-up wallet's, instead of a newly generated mining key:
26+
#
27+
# $env:GHOST_MINE_TO='0x...'; irm https://get.ghostcoin.com/win | iex
28+
#
29+
# (on a Mac or Linux: curl -fsSL https://get.ghostcoin.com | GHOST_MINE_TO=0x... sh)
30+
#
31+
# It must be 0x followed by exactly 40 hex characters; anything else is
32+
# refused with nothing installed. This script then writes the config with
33+
# that address as Etherbase before ghost-setup runs, so ghost-setup keeps it
34+
# and makes no mining key or password file; on a re-run it rewrites the
35+
# config's Etherbase to the address, and ghost-setup restarts the node. Unset,
36+
# the config is left to ghost-setup as before. Either way the script ends by
37+
# printing the address the node mines to.
38+
#
2439
# The whole script is one script block that runs at its last line, so a
2540
# download cut short runs nothing.
2641

@@ -44,6 +59,30 @@
4459
throw "ghost-node: GHOST_RELEASE_URL must be an https URL, not $ReleaseUrl"
4560
}
4661

62+
# GHOST_MINE_TO, when set, is the wallet address to mine to.
63+
$MineTo = $env:GHOST_MINE_TO
64+
$HttpPort = 8545
65+
if ($MineTo) {
66+
if ($MineTo -cnotmatch '^0x[0-9a-fA-F]{40}\z') {
67+
throw "ghost-node: GHOST_MINE_TO must be a wallet address, 0x followed by exactly 40 hex characters, not '$MineTo'; $Refusal"
68+
}
69+
if (-not $env:USERPROFILE) {
70+
throw "ghost-node: USERPROFILE is not set to a directory; $Refusal"
71+
}
72+
if ($env:GHOST_HTTP_PORT) {
73+
if ($env:GHOST_HTTP_PORT -cnotmatch '^[1-9][0-9]{0,4}\z' -or [int]$env:GHOST_HTTP_PORT -gt 65535) {
74+
throw "ghost-node: GHOST_HTTP_PORT is not a port number: $env:GHOST_HTTP_PORT; $Refusal"
75+
}
76+
$HttpPort = [int]$env:GHOST_HTTP_PORT
77+
}
78+
}
79+
$NodeHome = $null
80+
$Config = $null
81+
if ($env:USERPROFILE) {
82+
$NodeHome = Join-Path $env:USERPROFILE '.ghost-node'
83+
$Config = Join-Path $NodeHome 'config.toml'
84+
}
85+
4786
$Machine = $env:PROCESSOR_ARCHITECTURE
4887
if ($env:PROCESSOR_ARCHITEW6432) { $Machine = $env:PROCESSOR_ARCHITEW6432 }
4988
if ($Machine -eq 'AMD64') {
@@ -78,10 +117,52 @@
78117
throw "ghost-node: the downloaded $Name has the SHA-256 $Hash, not the $Pinned pinned in this installer; $Refusal"
79118
}
80119

120+
# With GHOST_MINE_TO, the config mines to its address before ghost-setup
121+
# runs: ghost-setup keeps an existing config, and so makes no mining key.
122+
if ($MineTo) {
123+
$Etherbase = "Etherbase = `"$MineTo`""
124+
if (Test-Path -LiteralPath $Config -PathType Leaf) {
125+
$Text = [IO.File]::ReadAllText($Config)
126+
if ($Text -cmatch '(?m)^Etherbase = ') {
127+
$Text = [regex]::Replace($Text, '(?m)^Etherbase = [^\r\n]*', $Etherbase)
128+
} else {
129+
$Text = $Text.TrimEnd() + "`n`n[Eth.Miner]`n$Etherbase`n"
130+
}
131+
} else {
132+
$DataDir = (Join-Path $NodeHome 'data').Replace('\', '\\').Replace('"', '\"')
133+
$Text = @'
134+
# The Ghost node's config, written by install.ps1. The node mines to Etherbase,
135+
# the wallet address GHOST_MINE_TO named, whose key is not on this computer.
136+
# Its HTTP RPC listens on this computer only, for the Ghost web apps.
137+
138+
[Eth.Miner]
139+
Etherbase = "{0}"
140+
141+
[Node]
142+
DataDir = "{1}"
143+
HTTPHost = "127.0.0.1"
144+
HTTPPort = {2}
145+
HTTPModules = ["eth", "net", "web3"]
146+
HTTPCors = ["https://ghost-webapp-alpha.vercel.app", "https://ghost-wallet-nine.vercel.app"]
147+
'@ -f $MineTo, $DataDir, $HttpPort
148+
$Text = $Text.Replace("`r`n", "`n") + "`n"
149+
}
150+
New-Item -ItemType Directory -Force -Path $NodeHome | Out-Null
151+
[IO.File]::WriteAllText($Config, $Text)
152+
Write-Host "ghost-node: the config $Config mines to $MineTo"
153+
}
154+
81155
& $Setup
82156
if ($LASTEXITCODE -ne 0) {
83157
throw "ghost-node: the setup stopped with exit code $LASTEXITCODE; see why above."
84158
}
159+
160+
if ($Config -and (Test-Path -LiteralPath $Config -PathType Leaf)) {
161+
$Mined = [regex]::Match([IO.File]::ReadAllText($Config), '(?m)^Etherbase = "(0x[0-9a-fA-F]{40})"\r?$')
162+
if ($Mined.Success) {
163+
Write-Host "ghost-node: mining to: $($Mined.Groups[1].Value)"
164+
}
165+
}
85166
} finally {
86167
Remove-Item -LiteralPath $Work -Recurse -Force -ErrorAction SilentlyContinue
87168
}

‎install/install.sh‎

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# with nothing installed and says why.
1818
#
1919
# It then installs the node in ~/.ghost-node, writes the node's config with a
20-
# newly generated mining address, and starts the node as a user service with
20+
# newly generated mining address (or GHOST_MINE_TO's, below), and starts the node as a user service with
2121
# mining on, which also starts it at boot: a systemd user unit on Linux, a
2222
# launchd agent on macOS. The node joins the network built into it, the public
2323
# test network; an archive that still holds a genesis.json gets that chain
@@ -59,6 +59,19 @@
5959
# moves the node's HTTP RPC off 8545. The tests use them for a local release
6060
# server.
6161
#
62+
# GHOST_MINE_TO mines straight to a wallet address of your own, such as the
63+
# Ghost pop-up wallet's, instead of a newly generated mining key:
64+
#
65+
# curl -fsSL https://get.ghostcoin.com | GHOST_MINE_TO=0x... sh
66+
#
67+
# (on Windows, in PowerShell: $env:GHOST_MINE_TO='0x...'; irm https://get.ghostcoin.com/win | iex)
68+
#
69+
# It must be 0x followed by exactly 40 hex characters; anything else is
70+
# refused with nothing installed. The config's Etherbase is then that address,
71+
# and no mining key or password file is made. Set on a re-run or an update, it
72+
# rewrites Etherbase to the address it names and restarts the node; unset, the
73+
# config keeps the Etherbase it has.
74+
#
6275
# The whole installer is the text of installer_source, which the last lines
6376
# run. So a download cut short runs nothing, and the installer can write
6477
# itself out as the update script.
@@ -244,25 +257,64 @@ toml_string() {
244257
printf '%s' "$1" | sed 's/[\\"]/\\&/g'
245258
}
246259
247-
# configure writes the config with a new mining address, unless there is one.
260+
# config_address prints the mining address of the config.
261+
config_address() {
262+
sed -n 's/^Etherbase = "\(0x[0-9a-fA-F]*\)"$/\1/p' "$config" | head -n 1
263+
}
264+
265+
# set_etherbase rewrites the config's Etherbase to GHOST_MINE_TO's address,
266+
# keeping the rest of the config.
267+
set_etherbase() {
268+
if grep -q '^Etherbase = ' "$config"; then
269+
sed "s/^Etherbase = .*\$/Etherbase = \"$mine_to\"/" "$config" >"$config.new" ||
270+
die "could not rewrite the mining address of $config"
271+
else
272+
{
273+
cat "$config"
274+
printf '\n[Eth.Miner]\nEtherbase = "%s"\n' "$mine_to"
275+
} >"$config.new" || die "could not rewrite the mining address of $config"
276+
fi
277+
mv -f "$config.new" "$config"
278+
say "the config $config now mines to $mine_to"
279+
}
280+
281+
# configure writes the config with GHOST_MINE_TO's address or a new mining
282+
# address, unless there is one; an existing config gets GHOST_MINE_TO's
283+
# address when it is set.
248284
configure() {
249285
if [ -f "$config" ]; then
250-
say "keeping the existing config $config"
286+
if [ -n "$mine_to" ]; then
287+
set_etherbase
288+
else
289+
say "keeping the existing config $config"
290+
fi
251291
return 0
252292
fi
253-
password=$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')
254-
[ "${#password}" = 64 ] || die "could not make a password for the mining key"
255-
printf '%s\n' "$password" >"$base/password" || die "could not write the password of the mining key"
256-
created=$("$geth" --datadir "$data" account new --password "$base/password" 2>"$work/account.log" </dev/null) ||
257-
die "could not create the mining key: $(tail -n 3 "$work/account.log" | tr '\n' ' ')"
258-
address=$(printf '%s\n' "$created" | sed -n 's/^Public address of the key: *\(0x[0-9a-fA-F]\{40\}\)$/\1/p')
259-
[ -n "$address" ] || die "could not read the new mining address from: $created"
260-
cat >"$config.new" <<EOF
293+
if [ -n "$mine_to" ]; then
294+
address=$mine_to
295+
cat >"$config.new" <<EOF
296+
# The Ghost node's config, written by install.sh. The node mines to Etherbase,
297+
# the wallet address GHOST_MINE_TO named, whose key is not on this computer.
298+
# Its HTTP RPC listens on this computer only, for the Ghost web apps.
299+
300+
EOF
301+
else
302+
password=$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')
303+
[ "${#password}" = 64 ] || die "could not make a password for the mining key"
304+
printf '%s\n' "$password" >"$base/password" || die "could not write the password of the mining key"
305+
created=$("$geth" --datadir "$data" account new --password "$base/password" 2>"$work/account.log" </dev/null) ||
306+
die "could not create the mining key: $(tail -n 3 "$work/account.log" | tr '\n' ' ')"
307+
address=$(printf '%s\n' "$created" | sed -n 's/^Public address of the key: *\(0x[0-9a-fA-F]\{40\}\)$/\1/p')
308+
[ -n "$address" ] || die "could not read the new mining address from: $created"
309+
cat >"$config.new" <<EOF
261310
# The Ghost node's config, written by install.sh. The node mines to Etherbase,
262311
# whose key is in the keystore of DataDir, locked with the password in
263312
# $(toml_string "$base/password"). Its HTTP RPC listens on this computer only,
264313
# for the Ghost web apps.
265314
315+
EOF
316+
fi
317+
cat >>"$config.new" <<EOF
266318
[Eth.Miner]
267319
Etherbase = "$address"
268320
@@ -558,6 +610,7 @@ update() {
558610
mv -f "$geth.new" "$geth"
559611
mark_start
560612
if restart_node && wait_started; then
613+
restarted=1
561614
rm -f "$geth.previous"
562615
say "updated the node to the latest release and restarted it"
563616
return 0
@@ -587,6 +640,19 @@ main() {
587640
'' | *[!0-9]* | 0*) die "GHOST_HTTP_PORT is not a port number: $http_port" ;;
588641
esac
589642
[ "$http_port" -le 65535 ] || die "GHOST_HTTP_PORT is not a port number: $http_port"
643+
mine_to=${GHOST_MINE_TO:-}
644+
if [ -n "$mine_to" ]; then
645+
m_hex=${mine_to#0x}
646+
case "$mine_to" in
647+
0x*) ;;
648+
*) m_hex=bad ;;
649+
esac
650+
case "$m_hex" in
651+
*[!0-9a-fA-F]*) m_hex=bad ;;
652+
esac
653+
[ "${#m_hex}" = 40 ] ||
654+
refuse "GHOST_MINE_TO must be a wallet address, 0x followed by exactly 40 hex characters, not '$mine_to'"
655+
fi
590656
release_url=${release_url%/}
591657
case "$release_url" in
592658
https://*) ;;
@@ -602,6 +668,7 @@ main() {
602668
agents="$HOME/Library/LaunchAgents"
603669
domain="gui/$(id -u)"
604670
update_hint=
671+
restarted=0
605672
if [ "$mode" = update ] && { [ ! -x "$geth" ] || [ ! -f "$config" ]; }; then
606673
die "there is no Ghost node installed in $base to update; install one with: curl -fsSL https://get.ghostcoin.com | sh"
607674
fi
@@ -623,7 +690,18 @@ main() {
623690
unpack "$work/$asset"
624691
625692
if [ "$mode" = update ]; then
693+
rewritten=0
694+
if [ -n "$mine_to" ] && [ "$(config_address)" != "$mine_to" ]; then
695+
set_etherbase
696+
rewritten=1
697+
fi
626698
update
699+
if [ "$rewritten" = 1 ] && [ "$restarted" != 1 ]; then
700+
mark_start
701+
{ restart_node && wait_started; } || die "the node did not start again to mine to $mine_to; see its log"
702+
say "restarted the node to mine to $mine_to"
703+
fi
704+
say "mining to: $(config_address)"
627705
return 0
628706
fi
629707
@@ -644,7 +722,7 @@ main() {
644722
645723
address=$(sed -n 's/^Etherbase = "\(0x[0-9a-fA-F]*\)"$/\1/p' "$config")
646724
say "the node is installed in $base, running, syncing and mining"
647-
say "your mining address: $address"
725+
say "mining to: $address"
648726
say "check its status with: $status_hint"
649727
say "follow its log with: $log_hint"
650728
say "ask it for the chain head with: $geth attach --exec eth.blockNumber $data/geth.ipc"

0 commit comments

Comments
 (0)