Integrate the Wallet support with the Deposits creation; Produce Launchpad-compatible deposits
This commit is contained in:
parent
fcd412f7a1
commit
40ea9e9886
16
Makefile
16
Makefile
|
@ -174,12 +174,20 @@ testnet0 testnet1: | beacon_node
|
|||
clean-altona:
|
||||
rm -rf build/data/shared_altona*
|
||||
|
||||
altona-deposit: | beacon_node
|
||||
altona-deposit: | beacon_node deposit_contract
|
||||
build/beacon_node deposits create \
|
||||
--network=altona \
|
||||
--count=$(VALIDATORS) \
|
||||
--out-deposits-file=nbc-altona-deposits.json \
|
||||
--count=$(VALIDATORS)
|
||||
|
||||
# TODO
|
||||
# The --min-delay is needed only until we fix the invalid
|
||||
# nonce generation on multiple transactions in web3
|
||||
build/deposit_contract makeDeposits \
|
||||
--web3-url=$(GOERLI_WEB3_URL) \
|
||||
--deposit-contract=$$(cat vendor/eth2-testnets/shared/altona/deposit_contract.txt) \
|
||||
--deposits-file=nbc-altona-deposits.json \
|
||||
--ask-for-key \
|
||||
--web3-url=$(GOERLI_WEB3_URL)
|
||||
--min-delay=60
|
||||
|
||||
altona: | beacon_node
|
||||
build/beacon_node \
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
import
|
||||
# Standard library
|
||||
algorithm, os, tables, strutils, times, math, terminal, bearssl, random,
|
||||
algorithm, os, tables, strutils, sequtils, times, math, terminal,
|
||||
|
||||
# Nimble packages
|
||||
stew/[objects, byteutils, endians2], stew/shims/macros,
|
||||
chronos, confutils, metrics, json_rpc/[rpcserver, jsonmarshal],
|
||||
chronicles,
|
||||
chronicles, bearssl,
|
||||
json_serialization/std/[options, sets, net], serialization/errors,
|
||||
|
||||
eth/[keys, async_utils],
|
||||
|
@ -27,7 +27,7 @@ import
|
|||
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
||||
beacon_node_common, beacon_node_types, block_pools/block_pools_types,
|
||||
nimbus_binary_common, network_metadata,
|
||||
mainchain_monitor, version, ssz/[merkleization], sszdump,
|
||||
mainchain_monitor, version, ssz/[merkleization], sszdump, merkle_minimal,
|
||||
sync_protocol, request_manager, keystore_management, interop, statusbar,
|
||||
sync_manager, validator_duties, validator_api, attestation_aggregation
|
||||
|
||||
|
@ -1082,32 +1082,18 @@ programMain:
|
|||
|
||||
case config.cmd
|
||||
of createTestnet:
|
||||
var
|
||||
depositDirs: seq[string]
|
||||
deposits: seq[Deposit]
|
||||
i = -1
|
||||
for kind, dir in walkDir(config.testnetDepositsDir.string):
|
||||
if kind != pcDir:
|
||||
continue
|
||||
let launchPadDeposits = try:
|
||||
Json.loadFile(config.testnetDepositsFile.string, seq[LaunchPadDeposit])
|
||||
except SerializationError as err:
|
||||
error "Invalid LaunchPad deposits file",
|
||||
err = formatMsg(err, config.testnetDepositsFile.string)
|
||||
quit 1
|
||||
|
||||
inc i
|
||||
if i < config.firstValidator.int:
|
||||
continue
|
||||
var deposits: seq[Deposit]
|
||||
for i in config.firstValidator.int ..< launchPadDeposits.len:
|
||||
deposits.add Deposit(data: launchPadDeposits[i] as DepositData)
|
||||
|
||||
depositDirs.add dir
|
||||
|
||||
# Add deposits, in order, to pass Merkle validation
|
||||
sort(depositDirs, system.cmp)
|
||||
|
||||
for dir in depositDirs:
|
||||
let depositFile = dir / "deposit.json"
|
||||
try:
|
||||
deposits.add Json.loadFile(depositFile, Deposit)
|
||||
except SerializationError as err:
|
||||
stderr.write "Error while loading a deposit file:\n"
|
||||
stderr.write err.formatMsg(depositFile), "\n"
|
||||
stderr.write "Please regenerate the deposit files by running 'beacon_node deposits create' again\n"
|
||||
quit 1
|
||||
attachMerkleProofs(deposits)
|
||||
|
||||
let
|
||||
startTime = uint64(times.toUnix(times.getTime()) + config.genesisOffset)
|
||||
|
@ -1188,18 +1174,26 @@ programMain:
|
|||
of deposits:
|
||||
case config.depositsCmd
|
||||
of DepositsCmd.create:
|
||||
if config.askForKey and config.depositPrivateKey.len == 0:
|
||||
let
|
||||
depositsWord = if config.totalDeposits > 1: "deposits"
|
||||
else: "deposit"
|
||||
totalEthNeeded = 32 * config.totalDeposits
|
||||
var walletData = if config.existingWalletId.isSome:
|
||||
let id = config.existingWalletId.get
|
||||
let found = keystore_management.findWallet(config, id)
|
||||
if found.isErr:
|
||||
fatal "Unable to find wallet with the specified name/uuid",
|
||||
id, err = found.error
|
||||
quit 1
|
||||
let unlocked = unlockWalletInteractively(found.get)
|
||||
if unlocked.isOk:
|
||||
unlocked.get
|
||||
else:
|
||||
quit 1
|
||||
else:
|
||||
let walletData = createWalletInteractively(rng[], config)
|
||||
if walletData.isErr:
|
||||
fatal "Unable to create wallet", err = walletData.error
|
||||
quit 1
|
||||
walletData.get
|
||||
|
||||
echo "Please enter your Goerli Eth1 private key in hex form " &
|
||||
"(e.g. 0x1a2...f3c) in order to make your $1 (you'll need " &
|
||||
"access to $2 GoETH)" % [depositsWord, $totalEthNeeded]
|
||||
|
||||
if not readPasswordFromStdin("> ", TaintedString config.depositPrivateKey):
|
||||
error "Failed to read an Eth1 private key from standard input"
|
||||
defer: burnMem(walletData.mnemonic)
|
||||
|
||||
createDir(config.outValidatorsDir)
|
||||
createDir(config.outSecretsDir)
|
||||
|
@ -1207,6 +1201,7 @@ programMain:
|
|||
let deposits = generateDeposits(
|
||||
config.runtimePreset,
|
||||
rng[],
|
||||
walletData,
|
||||
config.totalDeposits,
|
||||
config.outValidatorsDir,
|
||||
config.outSecretsDir)
|
||||
|
@ -1215,24 +1210,22 @@ programMain:
|
|||
fatal "Failed to generate deposits", err = deposits.error
|
||||
quit 1
|
||||
|
||||
if not config.dontSend:
|
||||
waitFor sendDeposits(config, deposits.value)
|
||||
try:
|
||||
let depositDataPath = if config.outDepositsFile.isSome:
|
||||
config.outDepositsFile.get.string
|
||||
else:
|
||||
config.outValidatorsDir / "deposit_data-" & $epochTime()
|
||||
|
||||
of DepositsCmd.send:
|
||||
var delayGenerator: DelayGenerator
|
||||
if config.maxDelay > 0.0:
|
||||
delayGenerator = proc (): chronos.Duration {.gcsafe.} =
|
||||
chronos.milliseconds (rand(config.minDelay..config.maxDelay)*1000).int
|
||||
let launchPadDeposits =
|
||||
mapIt(deposits.value, LaunchPadDeposit.init(config.runtimePreset, it))
|
||||
|
||||
if config.minDelay > config.maxDelay:
|
||||
echo "The minimum delay should not be larger than the maximum delay"
|
||||
Json.saveFile(depositDataPath, launchPadDeposits)
|
||||
info "Deposit data written", filename = depositDataPath
|
||||
except CatchableError as err:
|
||||
error "Failed to create launchpad deposit data file", err = err.msg
|
||||
quit 1
|
||||
|
||||
let deposits = loadDeposits(config.depositsDir)
|
||||
waitFor sendDeposits(config, deposits, delayGenerator)
|
||||
|
||||
of DepositsCmd.status:
|
||||
# TODO
|
||||
echo "The status command is not implemented yet"
|
||||
quit 1
|
||||
|
||||
|
@ -1241,7 +1234,7 @@ programMain:
|
|||
of WalletsCmd.create:
|
||||
let status = createWalletInteractively(rng[], config)
|
||||
if status.isErr:
|
||||
echo status.error
|
||||
fatal "Unable to create wallet", err = status.error
|
||||
quit 1
|
||||
|
||||
of WalletsCmd.list:
|
||||
|
|
|
@ -4,7 +4,7 @@ import
|
|||
os, options,
|
||||
chronicles, chronicles/options as chroniclesOptions,
|
||||
confutils, confutils/defs, confutils/std/net,
|
||||
stew/byteutils, json_serialization, web3/ethtypes,
|
||||
json_serialization, web3/[ethtypes, confutils_defs],
|
||||
network_metadata, spec/[crypto, keystore, digest, datatypes]
|
||||
|
||||
export
|
||||
|
@ -27,7 +27,6 @@ type
|
|||
|
||||
DepositsCmd* {.pure.} = enum
|
||||
create = "Creates validator keystores and deposits"
|
||||
send = "Sends prepared deposits to the validator deposit contract"
|
||||
status = "Displays status information about all deposits"
|
||||
|
||||
VCStartUpCmd* = enum
|
||||
|
@ -55,6 +54,10 @@ type
|
|||
abbr: "d"
|
||||
name: "data-dir" }: OutDir
|
||||
|
||||
walletsDirFlag* {.
|
||||
desc: "A directory containing wallet files"
|
||||
name: "wallets-dir" }: Option[InputDir]
|
||||
|
||||
web3Url* {.
|
||||
defaultValue: ""
|
||||
desc: "URL of the Web3 server to observe Eth1"
|
||||
|
@ -126,10 +129,6 @@ type
|
|||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" }: Option[InputDir]
|
||||
|
||||
walletsDirFlag* {.
|
||||
desc: "A directory containing wallet files"
|
||||
name: "wallets-dir" }: Option[InputDir]
|
||||
|
||||
stateSnapshot* {.
|
||||
desc: "SSZ file specifying a recent state snapshot"
|
||||
abbr: "s"
|
||||
|
@ -211,9 +210,9 @@ type
|
|||
name: "dump" }: bool
|
||||
|
||||
of createTestnet:
|
||||
testnetDepositsDir* {.
|
||||
desc: "Directory containing validator keystores"
|
||||
name: "validators-dir" }: InputDir
|
||||
testnetDepositsFile* {.
|
||||
desc: "A LaunchPad deposits file for the genesis state validators"
|
||||
name: "deposits-file" }: InputFile
|
||||
|
||||
totalValidators* {.
|
||||
desc: "The number of validator deposits in the newly created chain"
|
||||
|
@ -260,46 +259,37 @@ type
|
|||
of wallets:
|
||||
case walletsCmd* {.command.}: WalletsCmd
|
||||
of WalletsCmd.create:
|
||||
createdWalletName* {.
|
||||
desc: "An easy-to-remember name for the wallet of your choice"
|
||||
name: "name"}: Option[WalletName]
|
||||
|
||||
nextAccount* {.
|
||||
desc: "Initial value for the 'nextaccount' property of the wallet"
|
||||
name: "next-account" }: Option[Natural]
|
||||
|
||||
outWalletsDirFlag* {.
|
||||
desc: "A directory containing wallet files"
|
||||
name: "wallets-dir" }: Option[OutDir]
|
||||
createdWalletNameFlag* {.
|
||||
desc: "An easy-to-remember name for the wallet of your choice"
|
||||
name: "name"}: Option[WalletName]
|
||||
|
||||
createdWalletFile* {.
|
||||
createdWalletFileFlag* {.
|
||||
desc: "Output wallet file"
|
||||
name: "out" }: Option[OutFile]
|
||||
|
||||
of WalletsCmd.restore:
|
||||
restoredWalletName* {.
|
||||
restoredWalletNameFlag* {.
|
||||
desc: "An easy-to-remember name for the wallet of your choice"
|
||||
name: "name"}: Option[WalletName]
|
||||
|
||||
restoredWalletFileFlag* {.
|
||||
desc: "Output wallet file"
|
||||
name: "out" }: Option[OutFile]
|
||||
|
||||
restoredDepositsCount* {.
|
||||
desc: "Expected number of deposits to recover. If not specified, " &
|
||||
"Nimbus will try to guess the number by inspecting the latest " &
|
||||
"beacon state"
|
||||
name: "deposits".}: Option[Natural]
|
||||
|
||||
restoredWalletFile* {.
|
||||
desc: "Output wallet file"
|
||||
name: "out" }: Option[OutFile]
|
||||
|
||||
of WalletsCmd.list:
|
||||
discard
|
||||
|
||||
of deposits:
|
||||
depositPrivateKey* {.
|
||||
defaultValue: ""
|
||||
desc: "Private key of the controlling (sending) account",
|
||||
name: "deposit-private-key" }: string
|
||||
|
||||
depositsDir* {.
|
||||
defaultValue: "validators"
|
||||
desc: "A folder with validator metadata created by the `deposits create` command"
|
||||
|
@ -326,29 +316,17 @@ type
|
|||
desc: "Output folder for randomly generated keystore passphrases"
|
||||
name: "out-secrets-dir" }: string
|
||||
|
||||
askForKey* {.
|
||||
defaultValue: false
|
||||
desc: "Ask for an Eth1 private key used to fund the deposits"
|
||||
name: "ask-for-key" }: bool
|
||||
outDepositsFile* {.
|
||||
desc: "The name of generated deposits file"
|
||||
name: "out-deposits-file" }: Option[OutFile]
|
||||
|
||||
dontSend* {.
|
||||
defaultValue: false,
|
||||
desc: "By default, all created deposits are also immediately sent " &
|
||||
"to the validator deposit contract. You can use this option to " &
|
||||
"prevent this behavior. Use the `deposits send` command to send " &
|
||||
"the deposit transactions at your convenience later"
|
||||
name: "dont-send" .}: bool
|
||||
newWalletNameFlag* {.
|
||||
desc: "An easy-to-remember name for the wallet of your choice"
|
||||
name: "new-wallet-name"}: Option[WalletName]
|
||||
|
||||
of DepositsCmd.send:
|
||||
minDelay* {.
|
||||
defaultValue: 0.0
|
||||
desc: "Minimum possible delay between making two deposits (in seconds)"
|
||||
name: "min-delay" }: float
|
||||
|
||||
maxDelay* {.
|
||||
defaultValue: 0.0
|
||||
desc: "Maximum possible delay between making two deposits (in seconds)"
|
||||
name: "max-delay" }: float
|
||||
newWalletFileFlag* {.
|
||||
desc: "Output wallet file"
|
||||
name: "new-wallet-file" }: Option[OutFile]
|
||||
|
||||
of DepositsCmd.status:
|
||||
discard
|
||||
|
@ -439,20 +417,6 @@ proc createDumpDirs*(conf: BeaconNodeConf) =
|
|||
# Dumping is mainly a debugging feature, so ignore these..
|
||||
warn "Cannot create dump directories", msg = err.msg
|
||||
|
||||
func parseCmdArg*(T: type Eth1Address, input: TaintedString): T
|
||||
{.raises: [ValueError, Defect].} =
|
||||
fromHex(T, string input)
|
||||
|
||||
func completeCmdArg*(T: type Eth1Address, input: TaintedString): seq[string] =
|
||||
return @[]
|
||||
|
||||
func parseCmdArg*(T: type Eth1BlockHash, input: TaintedString): T
|
||||
{.raises: [ValueError, Defect].} =
|
||||
fromHex(T, string input)
|
||||
|
||||
func completeCmdArg*(T: type Eth1BlockHash, input: TaintedString): seq[string] =
|
||||
return @[]
|
||||
|
||||
func parseCmdArg*(T: type GraffitiBytes, input: TaintedString): T
|
||||
{.raises: [ValueError, Defect].} =
|
||||
GraffitiBytes.init(string input)
|
||||
|
@ -478,18 +442,44 @@ func secretsDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
|||
string conf.secretsDirFlag.get(InputDir(conf.dataDir / "secrets"))
|
||||
|
||||
func walletsDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
||||
case conf.cmd
|
||||
of noCommand:
|
||||
if conf.walletsDirFlag.isSome:
|
||||
return conf.walletsDirFlag.get.string
|
||||
of wallets:
|
||||
doAssert conf.walletsCmd == WalletsCmd.create
|
||||
if conf.outWalletsDirFlag.isSome:
|
||||
return conf.outWalletsDirFlag.get.string
|
||||
if conf.walletsDirFlag.isSome:
|
||||
conf.walletsDirFlag.get.string
|
||||
else:
|
||||
raiseAssert "Inappropraite call to walletsDir"
|
||||
conf.dataDir / "wallets"
|
||||
|
||||
return conf.dataDir / "wallets"
|
||||
func outWalletName*(conf: BeaconNodeConf): Option[WalletName] =
|
||||
proc fail {.noReturn.} =
|
||||
raiseAssert "outWalletName should be used only in the right context"
|
||||
|
||||
case conf.cmd
|
||||
of wallets:
|
||||
case conf.walletsCmd
|
||||
of WalletsCmd.create: conf.createdWalletNameFlag
|
||||
of WalletsCmd.restore: conf.restoredWalletNameFlag
|
||||
of WalletsCmd.list: fail()
|
||||
of deposits:
|
||||
case conf.depositsCmd
|
||||
of DepositsCmd.create: conf.newWalletNameFlag
|
||||
else: fail()
|
||||
else:
|
||||
fail()
|
||||
|
||||
func outWalletFile*(conf: BeaconNodeConf): Option[OutFile] =
|
||||
proc fail {.noReturn.} =
|
||||
raiseAssert "outWalletName should be used only in the right context"
|
||||
|
||||
case conf.cmd
|
||||
of wallets:
|
||||
case conf.walletsCmd
|
||||
of WalletsCmd.create: conf.createdWalletFileFlag
|
||||
of WalletsCmd.restore: conf.restoredWalletFileFlag
|
||||
of WalletsCmd.list: fail()
|
||||
of deposits:
|
||||
case conf.depositsCmd
|
||||
of DepositsCmd.create: conf.newWalletFileFlag
|
||||
else: fail()
|
||||
else:
|
||||
fail()
|
||||
|
||||
func databaseDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
||||
conf.dataDir / "db"
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,27 +1,22 @@
|
|||
import
|
||||
std/[os, strutils, terminal, wordwrap],
|
||||
std/[os, json, strutils, terminal, wordwrap],
|
||||
stew/byteutils, chronicles, chronos, web3, stint, json_serialization,
|
||||
serialization, blscurve, eth/common/eth_types, eth/keys, confutils, bearssl,
|
||||
spec/[datatypes, digest, crypto, keystore],
|
||||
conf, ssz/merkleization, merkle_minimal, network_metadata
|
||||
conf, ssz/merkleization, network_metadata
|
||||
|
||||
export
|
||||
keystore
|
||||
|
||||
contract(DepositContract):
|
||||
proc deposit(pubkey: Bytes48, withdrawalCredentials: Bytes32, signature: Bytes96, deposit_data_root: FixedBytes[32])
|
||||
{.push raises: [Defect].}
|
||||
|
||||
const
|
||||
keystoreFileName* = "keystore.json"
|
||||
depositFileName* = "deposit.json"
|
||||
|
||||
type
|
||||
DelayGenerator* = proc(): chronos.Duration {.closure, gcsafe.}
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
proc ethToWei(eth: UInt256): UInt256 =
|
||||
eth * 1000000000000000000.u256
|
||||
WalletDataForDeposits* = object
|
||||
mnemonic*: Mnemonic
|
||||
nextAccount*: Natural
|
||||
|
||||
proc loadKeyStore(conf: BeaconNodeConf|ValidatorClientConf,
|
||||
validatorsDir, keyName: string): Option[ValidatorPrivKey] =
|
||||
|
@ -100,27 +95,40 @@ type
|
|||
FailedToCreateValidatoDir
|
||||
FailedToCreateSecretFile
|
||||
FailedToCreateKeystoreFile
|
||||
FailedToCreateDepositFile
|
||||
|
||||
proc generateDeposits*(preset: RuntimePreset,
|
||||
rng: var BrHmacDrbgContext,
|
||||
walletData: WalletDataForDeposits,
|
||||
totalValidators: int,
|
||||
validatorsDir: string,
|
||||
secretsDir: string): Result[seq[Deposit], GenerateDepositsError] =
|
||||
var deposits: seq[Deposit]
|
||||
secretsDir: string): Result[seq[DepositData], GenerateDepositsError] =
|
||||
var deposits: seq[DepositData]
|
||||
|
||||
info "Generating deposits", totalValidators, validatorsDir, secretsDir
|
||||
for i in 0 ..< totalValidators:
|
||||
let password = KeyStorePass getRandomBytes(rng, 32).toHex
|
||||
let credentials = generateCredentials(rng, password = password)
|
||||
|
||||
let withdrawalKeyPath = makeKeyPath(0, withdrawalKeyKind)
|
||||
# TODO: Explain why we are using an empty password
|
||||
var withdrawalKey = keyFromPath(walletData.mnemonic, KeyStorePass"", withdrawalKeyPath)
|
||||
defer: burnMem(withdrawalKey)
|
||||
let withdrawalPubKey = withdrawalKey.toPubKey
|
||||
|
||||
for i in 0 ..< totalValidators:
|
||||
let keyStoreIdx = walletData.nextAccount + i
|
||||
let password = KeyStorePass getRandomBytes(rng, 32).toHex
|
||||
|
||||
let signingKeyPath = withdrawalKeyPath.append keyStoreIdx
|
||||
var signingKey = deriveChildKey(withdrawalKey, keyStoreIdx)
|
||||
defer: burnMem(signingKey)
|
||||
let signingPubKey = signingKey.toPubKey
|
||||
|
||||
let keyStore = encryptKeystore(KdfPbkdf2, rng, signingKey,
|
||||
password, signingKeyPath)
|
||||
let
|
||||
keyName = intToStr(i, 6) & "_" & $(credentials.signingKey.toPubKey)
|
||||
keyName = $signingPubKey
|
||||
validatorDir = validatorsDir / keyName
|
||||
depositFile = validatorDir / depositFileName
|
||||
keystoreFile = validatorDir / keystoreFileName
|
||||
|
||||
if existsDir(validatorDir) and existsFile(depositFile):
|
||||
if existsDir(validatorDir):
|
||||
continue
|
||||
|
||||
try: createDir validatorDir
|
||||
|
@ -129,39 +137,13 @@ proc generateDeposits*(preset: RuntimePreset,
|
|||
try: writeFile(secretsDir / keyName, password.string)
|
||||
except IOError: return err FailedToCreateSecretFile
|
||||
|
||||
try: writeFile(keystoreFile, credentials.keyStore.string)
|
||||
try: writeFile(keystoreFile, keyStore.string)
|
||||
except IOError: return err FailedToCreateKeystoreFile
|
||||
|
||||
deposits.add credentials.prepareDeposit(preset)
|
||||
|
||||
# Does quadratic additional work, but fast enough, and otherwise more
|
||||
# cleanly allows free intermixing of pre-existing and newly generated
|
||||
# deposit and private key files. TODO: only generate new Merkle proof
|
||||
# for the most recent deposit if this becomes bottleneck.
|
||||
attachMerkleProofs(deposits)
|
||||
try: Json.saveFile(depositFile, deposits[^1], pretty = true)
|
||||
except: return err FailedToCreateDepositFile
|
||||
deposits.add preset.prepareDeposit(withdrawalPubKey, signingKey, signingPubKey)
|
||||
|
||||
ok deposits
|
||||
|
||||
proc loadDeposits*(depositsDir: string): seq[Deposit] =
|
||||
try:
|
||||
for kind, dir in walkDir(depositsDir):
|
||||
if kind == pcDir:
|
||||
let depositFile = dir / depositFileName
|
||||
try:
|
||||
result.add Json.loadFile(depositFile, Deposit)
|
||||
except IOError as err:
|
||||
error "Failed to open deposit file", depositFile, err = err.msg
|
||||
quit 1
|
||||
except SerializationError as err:
|
||||
error "Invalid deposit file", error = formatMsg(err, depositFile)
|
||||
quit 1
|
||||
except OSError as err:
|
||||
error "Deposits directory not accessible",
|
||||
path = depositsDir, err = err.msg
|
||||
quit 1
|
||||
|
||||
const
|
||||
minPasswordLen = 10
|
||||
|
||||
|
@ -170,12 +152,56 @@ const
|
|||
"../vendor/nimbus-security-resources/passwords/10-million-password-list-top-100000.txt",
|
||||
minWordLength = minPasswordLen)
|
||||
|
||||
proc saveWallet*(wallet: Wallet, outWalletPath: string): Result[void, string] =
|
||||
let
|
||||
uuid = wallet.uuid
|
||||
walletContent = WalletContent Json.encode(wallet, pretty = true)
|
||||
|
||||
try: createDir splitFile(outWalletPath).dir
|
||||
except OSError, IOError:
|
||||
let e = getCurrentException()
|
||||
return err("failure to create wallet directory: " & e.msg)
|
||||
|
||||
try: writeFile(outWalletPath, string walletContent)
|
||||
except IOError as e:
|
||||
return err("failure to write file: " & e.msg)
|
||||
|
||||
ok()
|
||||
|
||||
proc readPasswordInput(prompt: string, password: var TaintedString): bool =
|
||||
try: readPasswordFromStdin(prompt, password)
|
||||
except IOError: false
|
||||
|
||||
proc setStyleNoError(styles: set[Style]) =
|
||||
when defined(windows):
|
||||
try: stdout.setStyle(styles)
|
||||
except: discard
|
||||
else:
|
||||
try: stdout.setStyle(styles)
|
||||
except IOError, ValueError: discard
|
||||
|
||||
proc setForegroundColorNoError(color: ForegroundColor) =
|
||||
when defined(windows):
|
||||
try: stdout.setForegroundColor(color)
|
||||
except: discard
|
||||
else:
|
||||
try: stdout.setForegroundColor(color)
|
||||
except IOError, ValueError: discard
|
||||
|
||||
proc resetAttributesNoError() =
|
||||
when defined(windows):
|
||||
try: stdout.resetAttributes()
|
||||
except: discard
|
||||
else:
|
||||
try: stdout.resetAttributes()
|
||||
except IOError: discard
|
||||
|
||||
proc createWalletInteractively*(
|
||||
rng: var BrHmacDrbgContext,
|
||||
conf: BeaconNodeConf): Result[OutFile, cstring] =
|
||||
conf: BeaconNodeConf): Result[WalletDataForDeposits, string] =
|
||||
|
||||
if conf.nonInteractive:
|
||||
return err "Wallets can be created only in interactive mode"
|
||||
return err "not running in interactive mode"
|
||||
|
||||
var mnemonic = generateMnemonic(rng)
|
||||
defer: burnMem(mnemonic)
|
||||
|
@ -185,15 +211,11 @@ proc createWalletInteractively*(
|
|||
stdout.write prompt, ": "
|
||||
stdin.readLine()
|
||||
except IOError:
|
||||
return err "Failed to read data from stdin"
|
||||
return err "failure to read data from stdin"
|
||||
|
||||
template echo80(msg: string) =
|
||||
echo wrapWords(msg, 80)
|
||||
|
||||
proc readPasswordInput(prompt: string, password: var TaintedString): bool =
|
||||
try: readPasswordFromStdin(prompt, password)
|
||||
except IOError: false
|
||||
|
||||
echo80 "The generated wallet is uniquely identified by a seed phrase " &
|
||||
"consisting of 24 words. In case you lose your wallet and you " &
|
||||
"need to restore it on a different machine, you must use the " &
|
||||
|
@ -201,13 +223,13 @@ proc createWalletInteractively*(
|
|||
|
||||
try:
|
||||
echo ""
|
||||
stdout.setStyle({styleBright})
|
||||
stdout.setForegroundColor fgCyan
|
||||
setStyleNoError({styleBright})
|
||||
setForegroundColorNoError fgCyan
|
||||
echo80 $mnemonic
|
||||
stdout.resetAttributes()
|
||||
resetAttributesNoError()
|
||||
echo ""
|
||||
except IOError, ValueError:
|
||||
return err "Failed to write to the standard output"
|
||||
return err "failure to write to the standard output"
|
||||
|
||||
echo80 "Please back up the seed phrase now to a safe location as " &
|
||||
"if you are protecting a sensitive password. The seed phrase " &
|
||||
|
@ -220,7 +242,7 @@ proc createWalletInteractively*(
|
|||
while true:
|
||||
let answer = ask "Answer"
|
||||
if answer == "":
|
||||
return err "Wallet creation aborted"
|
||||
return err "aborted wallet creation"
|
||||
elif answer != "yes":
|
||||
echo "To continue, please type 'yes' (without the quotes) or press enter to quit"
|
||||
else:
|
||||
|
@ -246,7 +268,7 @@ proc createWalletInteractively*(
|
|||
|
||||
while true:
|
||||
if not readPasswordInput(prompt, password):
|
||||
return err "Failed to read a password from stdin"
|
||||
return err "failure to read a password from stdin"
|
||||
|
||||
if password.len < minPasswordLen:
|
||||
try:
|
||||
|
@ -263,15 +285,16 @@ proc createWalletInteractively*(
|
|||
firstTry = false
|
||||
|
||||
if not readPasswordInput("Please repeat the password:", confirmedPassword):
|
||||
return err "Failed to read a password from stdin"
|
||||
return err "failure to read a password from stdin"
|
||||
|
||||
if password != confirmedPassword:
|
||||
echo "Passwords don't match, please try again"
|
||||
continue
|
||||
|
||||
var name: WalletName
|
||||
if conf.createdWalletName.isSome:
|
||||
name = conf.createdWalletName.get
|
||||
let outWalletName = conf.outWalletName
|
||||
if outWalletName.isSome:
|
||||
name = outWalletName.get
|
||||
else:
|
||||
echo ""
|
||||
echo80 "For your convenience, the wallet can be identified with a name " &
|
||||
|
@ -287,74 +310,108 @@ proc createWalletInteractively*(
|
|||
continue
|
||||
break
|
||||
|
||||
let (uuid, walletContent) = KdfPbkdf2.createWalletContent(
|
||||
rng, mnemonic,
|
||||
name = name,
|
||||
password = KeyStorePass password)
|
||||
try:
|
||||
var outWalletFile: OutFile
|
||||
let wallet = KdfPbkdf2.createWallet(rng, mnemonic,
|
||||
name = name,
|
||||
password = KeyStorePass password)
|
||||
|
||||
if conf.createdWalletFile.isSome:
|
||||
outWalletFile = conf.createdWalletFile.get
|
||||
createDir splitFile(string outWalletFile).dir
|
||||
else:
|
||||
let walletsDir = conf.walletsDir
|
||||
createDir walletsDir
|
||||
outWalletFile = OutFile(walletsDir / addFileExt(string uuid, "json"))
|
||||
let outWalletFileFlag = conf.outWalletFile
|
||||
let outWalletFile = if outWalletFileFlag.isSome:
|
||||
string outWalletFileFlag.get
|
||||
else:
|
||||
conf.walletsDir / addFileExt(string wallet.uuid, "json")
|
||||
|
||||
writeFile(string outWalletFile, string walletContent)
|
||||
echo "Wallet file written to ", outWalletFile
|
||||
return ok outWalletFile
|
||||
except CatchableError as err:
|
||||
return err "Failed to write wallet file"
|
||||
let status = saveWallet(wallet, outWalletFile)
|
||||
if status.isErr:
|
||||
return err("failure to create wallet file due to " & status.error)
|
||||
|
||||
info "Wallet file written", path = outWalletFile
|
||||
return ok WalletDataForDeposits(mnemonic: mnemonic, nextAccount: 0)
|
||||
|
||||
finally:
|
||||
burnMem(password)
|
||||
burnMem(confirmedPassword)
|
||||
|
||||
{.pop.}
|
||||
proc loadWallet*(fileName: string): Result[Wallet, string] =
|
||||
try:
|
||||
ok Json.loadFile(fileName, Wallet)
|
||||
except CatchableError as e:
|
||||
err e.msg
|
||||
|
||||
# TODO: async functions should note take `seq` inputs because
|
||||
# this leads to full copies.
|
||||
proc sendDeposits*(deposits: seq[Deposit],
|
||||
web3Url, privateKey: string,
|
||||
depositContractAddress: Eth1Address,
|
||||
delayGenerator: DelayGenerator = nil) {.async.} =
|
||||
var web3 = await newWeb3(web3Url)
|
||||
if privateKey.len != 0:
|
||||
web3.privateKey = some(PrivateKey.fromHex(privateKey).tryGet)
|
||||
else:
|
||||
let accounts = await web3.provider.eth_accounts()
|
||||
if accounts.len == 0:
|
||||
error "No account offered by the web3 provider", web3Url
|
||||
return
|
||||
web3.defaultAccount = accounts[0]
|
||||
proc unlockWalletInteractively*(wallet: Wallet): Result[WalletDataForDeposits, string] =
|
||||
var json: JsonNode
|
||||
try:
|
||||
json = parseJson wallet.crypto.string
|
||||
except Exception as e: # TODO: parseJson shouldn't raise general `Exception`
|
||||
if e[] of Defect: raise (ref Defect)(e)
|
||||
else: return err "failure to parse crypto field"
|
||||
|
||||
let depositContract = web3.contractSender(DepositContract,
|
||||
Address depositContractAddress)
|
||||
for i, dp in deposits:
|
||||
let status = await depositContract.deposit(
|
||||
Bytes48(dp.data.pubKey.toRaw()),
|
||||
Bytes32(dp.data.withdrawal_credentials.data),
|
||||
Bytes96(dp.data.signature.toRaw()),
|
||||
FixedBytes[32](hash_tree_root(dp.data).data)).send(value = 32.u256.ethToWei, gasPrice = 1)
|
||||
var password: TaintedString
|
||||
|
||||
info "Deposit sent", status = $status
|
||||
echo "Please enter the password for unlocking the wallet"
|
||||
|
||||
if delayGenerator != nil:
|
||||
await sleepAsync(delayGenerator())
|
||||
for i in 1..3:
|
||||
try:
|
||||
if not readPasswordInput("Password: ", password):
|
||||
return err "failure to read password from stdin"
|
||||
|
||||
proc sendDeposits*(config: BeaconNodeConf,
|
||||
deposits: seq[Deposit],
|
||||
delayGenerator: DelayGenerator = nil) {.async.} =
|
||||
info "Sending deposits",
|
||||
web3 = config.web3Url,
|
||||
depositContract = config.depositContractAddress
|
||||
var status = decryptoCryptoField(json, KeyStorePass password)
|
||||
if status.isOk:
|
||||
defer: burnMem(status.value)
|
||||
return ok WalletDataForDeposits(
|
||||
mnemonic: Mnemonic string.fromBytes(status.value))
|
||||
else:
|
||||
echo "Unlocking of the wallet failed. Please try again."
|
||||
finally:
|
||||
burnMem(password)
|
||||
|
||||
await sendDeposits(
|
||||
deposits,
|
||||
config.web3Url,
|
||||
config.depositPrivateKey,
|
||||
config.depositContractAddress.get,
|
||||
delayGenerator)
|
||||
return err "failure to unlock wallet"
|
||||
|
||||
proc findWallet*(config: BeaconNodeConf, name: WalletName): Result[Wallet, string] =
|
||||
var walletFiles = newSeq[string]()
|
||||
|
||||
try:
|
||||
for kind, walletFile in walkDir(config.walletsDir):
|
||||
if kind != pcFile: continue
|
||||
let fullPath = config.walletsDir / walletFile
|
||||
if walletFile == name.string:
|
||||
return loadWallet(fullPath)
|
||||
walletFiles.add fullPath
|
||||
except OSError:
|
||||
return err "failure to list wallet directory"
|
||||
|
||||
for walletFile in walletFiles:
|
||||
let wallet = loadWallet(walletFile)
|
||||
if wallet.isOk and wallet.get.name == name:
|
||||
return wallet
|
||||
|
||||
return err "failure to locate wallet file"
|
||||
|
||||
type
|
||||
# This is not particularly well-standardized yet.
|
||||
# Some relevant code for generating (1) and validating (2) the data can be found below:
|
||||
# 1) https://github.com/ethereum/eth2.0-deposit-cli/blob/dev/eth2deposit/credentials.py
|
||||
# 2) https://github.com/ethereum/eth2.0-deposit/blob/dev/src/pages/UploadValidator/validateDepositKey.ts
|
||||
LaunchPadDeposit* = object
|
||||
pubkey*: ValidatorPubKey
|
||||
withdrawal_credentials*: Eth2Digest
|
||||
amount*: Gwei
|
||||
signature*: ValidatorSig
|
||||
deposit_message_root*: Eth2Digest
|
||||
deposit_data_root*: Eth2Digest
|
||||
fork_version*: Version
|
||||
|
||||
func init*(T: type LaunchPadDeposit,
|
||||
preset: RuntimePreset, d: DepositData): T =
|
||||
T(pubkey: d.pubkey,
|
||||
withdrawal_credentials: d.withdrawal_credentials,
|
||||
amount: d.amount,
|
||||
signature: d.signature,
|
||||
deposit_message_root: hash_tree_root(d as DepositMessage),
|
||||
deposit_data_root: hash_tree_root(d),
|
||||
fork_version: preset.GENESIS_FORK_VERSION)
|
||||
|
||||
func `as`*(copied: LaunchPadDeposit, T: type DepositData): T =
|
||||
T(pubkey: copied.pubkey,
|
||||
withdrawal_credentials: copied.withdrawal_credentials,
|
||||
amount: copied.amount,
|
||||
signature: copied.signature)
|
||||
|
|
|
@ -136,16 +136,16 @@ proc readLogFileForAttsMessages(file: string): seq[SlotAttMessage] =
|
|||
try:
|
||||
while not(stream.atEnd()):
|
||||
line = stream.readLine()
|
||||
let m = Json.decode(line, LogMessage, forwardCompatible = true)
|
||||
let m = Json.decode(line, LogMessage, allowUnknownFields = true)
|
||||
if m.msg == "Attestation sent":
|
||||
let am = Json.decode(line, AttestationSentMessage,
|
||||
forwardCompatible = true)
|
||||
allowUnknownFields = true)
|
||||
let m = SlotAttMessage(kind: SaMessageType.AttestationSent,
|
||||
asmsg: am)
|
||||
res.add(m)
|
||||
elif m.msg == "Slot start":
|
||||
let sm = Json.decode(line, SlotStartMessage,
|
||||
forwardCompatible = true)
|
||||
allowUnknownFields = true)
|
||||
let m = SlotAttMessage(kind: SaMessageType.SlotStart,
|
||||
ssmsg: sm)
|
||||
res.add(m)
|
||||
|
|
|
@ -343,3 +343,7 @@ func init*(T: typedesc[ValidatorSig], data: array[RawSigSize, byte]): T {.noInit
|
|||
if v.isErr:
|
||||
raise (ref ValueError)(msg: $v.error)
|
||||
v[]
|
||||
|
||||
proc burnMem*(key: var ValidatorPrivKey) =
|
||||
key = default(ValidatorPrivKey)
|
||||
|
||||
|
|
|
@ -544,6 +544,11 @@ proc `<`*(x, y: ValidatorIndex) : bool {.borrow.}
|
|||
proc hash*(x: ValidatorIndex): Hash {.borrow.}
|
||||
func `$`*(x: ValidatorIndex): auto = $(x.int64)
|
||||
|
||||
func `as`*(d: DepositData, T: type DepositMessage): T =
|
||||
T(pubkey: d.pubkey,
|
||||
withdrawal_credentials: d.withdrawal_credentials,
|
||||
amount: d.amount)
|
||||
|
||||
ethTimeUnit Slot
|
||||
ethTimeUnit Epoch
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import
|
|||
./datatypes, ./crypto, ./digest, ./signatures
|
||||
|
||||
export
|
||||
results
|
||||
results, burnMem
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
|
@ -127,6 +127,12 @@ WalletName.serializesAsBaseIn Json
|
|||
template `$`*(m: Mnemonic): string =
|
||||
string(m)
|
||||
|
||||
template `==`*(lhs, rhs: WalletName): bool =
|
||||
string(lhs) == string(rhs)
|
||||
|
||||
template `$`*(x: WalletName): string =
|
||||
string(x)
|
||||
|
||||
template burnMem*(m: var (SensitiveData|TaintedString)) =
|
||||
# TODO: `burnMem` in nimcrypto could use distinctBase
|
||||
# to make its usage less error-prone.
|
||||
|
@ -450,48 +456,21 @@ proc createWalletContent*(T: type[KdfParams],
|
|||
T, rng, mnemonic, name, salt, iv, password, nextAccount, pretty)
|
||||
(wallet.uuid, WalletContent Json.encode(wallet, pretty = pretty))
|
||||
|
||||
proc restoreCredentials*(rng: var BrHmacDrbgContext,
|
||||
mnemonic: Mnemonic,
|
||||
password = KeyStorePass ""): Credentials =
|
||||
let
|
||||
withdrawalKeyPath = makeKeyPath(0, withdrawalKeyKind)
|
||||
withdrawalKey = keyFromPath(mnemonic, password, withdrawalKeyPath)
|
||||
|
||||
signingKeyPath = withdrawalKeyPath.append 0
|
||||
signingKey = deriveChildKey(withdrawalKey, 0)
|
||||
|
||||
Credentials(
|
||||
mnemonic: mnemonic,
|
||||
keyStore: encryptKeystore(KdfPbkdf2, rng, signingKey, password, signingKeyPath),
|
||||
signingKey: signingKey,
|
||||
withdrawalKey: withdrawalKey)
|
||||
|
||||
proc generateCredentials*(rng: var BrHmacDrbgContext,
|
||||
entropy: openarray[byte] = @[],
|
||||
password = KeyStorePass ""): Credentials =
|
||||
let mnemonic = generateMnemonic(rng, englishWords, entropy)
|
||||
restoreCredentials(rng, mnemonic, password)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/deposit-contract.md#withdrawal-credentials
|
||||
proc makeWithdrawalCredentials*(k: ValidatorPubKey): Eth2Digest =
|
||||
var bytes = eth2digest(k.toRaw())
|
||||
bytes.data[0] = BLS_WITHDRAWAL_PREFIX.uint8
|
||||
bytes
|
||||
|
||||
proc prepareDeposit*(credentials: Credentials,
|
||||
preset: RuntimePreset,
|
||||
amount = MAX_EFFECTIVE_BALANCE.Gwei): Deposit =
|
||||
let
|
||||
withdrawalPubKey = credentials.withdrawalKey.toPubKey
|
||||
signingPubKey = credentials.signingKey.toPubKey
|
||||
proc prepareDeposit*(preset: RuntimePreset,
|
||||
withdrawalPubKey: ValidatorPubKey,
|
||||
signingKey: ValidatorPrivKey, signingPubKey: ValidatorPubKey,
|
||||
amount = MAX_EFFECTIVE_BALANCE.Gwei): DepositData =
|
||||
var res = DepositData(
|
||||
amount: amount,
|
||||
pubkey: signingPubKey,
|
||||
withdrawal_credentials: makeWithdrawalCredentials(withdrawalPubKey))
|
||||
|
||||
var
|
||||
ret = Deposit(
|
||||
data: DepositData(
|
||||
amount: amount,
|
||||
pubkey: signingPubKey,
|
||||
withdrawal_credentials: makeWithdrawalCredentials(withdrawalPubKey)))
|
||||
res.signature = preset.get_deposit_signature(res, signingKey)
|
||||
return res
|
||||
|
||||
ret.data.signature = preset.get_deposit_signature(ret.data,
|
||||
credentials.signingKey)
|
||||
ret
|
||||
|
|
|
@ -121,6 +121,8 @@ NETWORK="testnet${TESTNET}"
|
|||
|
||||
rm -rf "${DATA_DIR}"
|
||||
|
||||
DEPOSITS_FILE="${DATA_DIR}/deposits.json"
|
||||
|
||||
DEPOSITS_DIR="${DATA_DIR}/deposits_dir"
|
||||
mkdir -p "${DEPOSITS_DIR}"
|
||||
|
||||
|
@ -154,9 +156,10 @@ NETWORK_METADATA_FILE="${DATA_DIR}/network.json"
|
|||
|
||||
./build/beacon_node deposits create \
|
||||
--count=${TOTAL_VALIDATORS} \
|
||||
--non-interactive \
|
||||
--out-deposits-dir="${DEPOSITS_DIR}" \
|
||||
--out-secrets-dir="${SECRETS_DIR}" \
|
||||
--dont-send
|
||||
--out-deposits-file="${DEPOSITS_FILE}"
|
||||
|
||||
if [[ $USE_GANACHE == "0" ]]; then
|
||||
GENESIS_OFFSET=30
|
||||
|
@ -197,9 +200,8 @@ else
|
|||
|
||||
BOOTSTRAP_TIMEOUT=$(( MAX_DELAY * TOTAL_VALIDATORS ))
|
||||
|
||||
./build/beacon_node deposits send \
|
||||
--non-interactive \
|
||||
--deposits-dir="${DEPOSITS_DIR}" \
|
||||
./build/deposit_contract makeDeposits \
|
||||
--deposits-file="${DEPOSITS_FILE}" \
|
||||
--min-delay=$MIN_DELAY --max-delay=$MAX_DELAY \
|
||||
$WEB3_ARG \
|
||||
--deposit-contract=${DEPOSIT_CONTRACT_ADDRESS} > "${DATA_DIR}/log_deposit_maker.txt" 2>&1 &
|
||||
|
|
|
@ -101,22 +101,23 @@ fi
|
|||
|
||||
$MAKE -j2 --no-print-directory NIMFLAGS="$CUSTOM_NIMFLAGS $DEFS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" beacon_node validator_client
|
||||
|
||||
count_files () {
|
||||
{ ls -1q $1 2> /dev/null || true ; } | wc -l
|
||||
}
|
||||
EXISTING_VALIDATORS=0
|
||||
if [[ -f "$DEPOSITS_FILE" ]]; then
|
||||
# We count the number of deposits by counting the number of
|
||||
# occurrences of the 'deposit_data_root' field:
|
||||
EXISTING_VALIDATORS=$(grep -o -i deposit_data_root "$DEPOSITS_FILE" | wc -l)
|
||||
fi
|
||||
|
||||
EXISTING_VALIDATORS=$(count_files "$VALIDATORS_DIR/*/deposit.json")
|
||||
|
||||
if [[ $EXISTING_VALIDATORS -lt $NUM_VALIDATORS ]]; then
|
||||
if [[ $EXISTING_VALIDATORS -ne $NUM_VALIDATORS ]]; then
|
||||
rm -rf "$VALIDATORS_DIR"
|
||||
rm -rf "$SECRETS_DIR"
|
||||
|
||||
$BEACON_NODE_BIN deposits create \
|
||||
--count="${NUM_VALIDATORS}" \
|
||||
--non-interactive \
|
||||
--new-wallet-file="${SIMULATION_DIR}/wallet.json" \
|
||||
--out-deposits-dir="$VALIDATORS_DIR" \
|
||||
--out-secrets-dir="$SECRETS_DIR" \
|
||||
--dont-send
|
||||
--out-deposits-file="$DEPOSITS_FILE"
|
||||
|
||||
echo "All deposits prepared"
|
||||
fi
|
||||
|
@ -128,7 +129,7 @@ if [ ! -f "${SNAPSHOT_FILE}" ]; then
|
|||
--data-dir="${SIMULATION_DIR}/node-$BOOTSTRAP_NODE" \
|
||||
createTestnet \
|
||||
$WEB3_ARG \
|
||||
--validators-dir="${VALIDATORS_DIR}" \
|
||||
--deposits-file="${DEPOSITS_FILE}" \
|
||||
--total-validators="${NUM_VALIDATORS}" \
|
||||
--output-genesis="${SNAPSHOT_FILE}" \
|
||||
--output-bootstrap-file="${NETWORK_BOOTSTRAP_FILE}" \
|
||||
|
@ -167,7 +168,7 @@ if [ "$USE_GANACHE" != "no" ]; then
|
|||
make deposit_contract
|
||||
echo Deploying the validator deposit contract...
|
||||
|
||||
DEPLOY_CMD_OUTPUT=$($DEPLOY_DEPOSIT_CONTRACT_BIN deploy $WEB3_ARG)
|
||||
DEPLOY_CMD_OUTPUT=$($DEPOSIT_CONTRACT_BIN deploy $WEB3_ARG)
|
||||
# https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash
|
||||
OUTPUT_PIECES=(${DEPLOY_CMD_OUTPUT//;/ })
|
||||
DEPOSIT_CONTRACT_ADDRESS=${OUTPUT_PIECES[0]}
|
||||
|
@ -176,9 +177,8 @@ if [ "$USE_GANACHE" != "no" ]; then
|
|||
echo Contract deployed at $DEPOSIT_CONTRACT_ADDRESS:$DEPOSIT_CONTRACT_BLOCK
|
||||
|
||||
if [[ "$WAIT_GENESIS" == "yes" ]]; then
|
||||
run_cmd "(deposit maker)" "$BEACON_NODE_BIN deposits send \
|
||||
--non-interactive \
|
||||
--deposits-dir='$VALIDATORS_DIR' \
|
||||
run_cmd "(deposit maker)" "$DEPOSIT_CONTRACT_BIN makeDeposits \
|
||||
--deposits-file='$DEPOSITS_FILE' \
|
||||
--min-delay=0 --max-delay=1 \
|
||||
$WEB3_ARG \
|
||||
--deposit-contract=${DEPOSIT_CONTRACT_ADDRESS}"
|
||||
|
|
|
@ -34,9 +34,10 @@ SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.ssz"
|
|||
NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt"
|
||||
BEACON_NODE_BIN="${GIT_ROOT}/build/beacon_node"
|
||||
VALIDATOR_CLIENT_BIN="${GIT_ROOT}/build/validator_client"
|
||||
DEPLOY_DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract"
|
||||
DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract"
|
||||
BOOTSTRAP_ENR_FILE="${SIMULATION_DIR}/node-${BOOTSTRAP_NODE}/beacon_node.enr"
|
||||
NETWORK_METADATA_FILE="${SIMULATION_DIR}/network.json"
|
||||
DEPOSITS_FILE="${SIMULATION_DIR}/deposits.json"
|
||||
|
||||
if [[ "$USE_GANACHE" == "yes" ]]; then
|
||||
WEB3_ARG="--web3-url=ws://localhost:8545"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f0dfca17cf2b27e390ebd18de1538d84db35f9ed
|
||||
Subproject commit 6326a51b0b6e10ab5eab992c7b089c3b78e9241f
|
|
@ -1 +1 @@
|
|||
Subproject commit 1cf51931f1037a2c44fa0912386273c01a0e0e42
|
||||
Subproject commit d5eb9427b85c7f234b2bec2d70b962dfade1fa35
|
|
@ -1 +1 @@
|
|||
Subproject commit 04be808890ced3f47e9bb93267992f07c25acfbc
|
||||
Subproject commit 4fe1a81ce7e16413b01467a2f88058127941170e
|
Loading…
Reference in New Issue