diff --git a/Makefile b/Makefile index 613603c85..111d7706f 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,8 @@ TOOLS_CORE := \ nimbus_light_client \ nimbus_validator_client \ nimbus_signing_node \ - validator_db_aggregator + validator_db_aggregator \ + ncli_testnet # This TOOLS/TOOLS_CORE decomposition is a workaroud so nimbus_beacon_node can # build on its own, and if/when that becomes a non-issue, it can be recombined @@ -184,11 +185,12 @@ libbacktrace: # - --base-metrics-port + [0, --nodes) # - --base-vc-keymanager-port + [0, --nodes) # - --base-vc-metrics-port + [0, --nodes] -# - --base-remote-signer-port + [0, --remote-signers) +# - --base-remote-signer-port + [0, --nimbus-signer-nodes | --web3signer-nodes) +# - --base-remote-signer-metrics-port + [0, --nimbus-signer-node | --web3signer-nodes) # # Local testnets with --run-geth or --run-nimbus (only these ports): # - --base-el-net-port + --el-port-offset * [0, --nodes + --light-clients) -# - --base-el-http-port + --el-port-offset * [0, --nodes + --light-clients) +# - --base-el-rpc-port + --el-port-offset * [0, --nodes + --light-clients) # - --base-el-ws-port + --el-port-offset * [0, --nodes + --light-clients) # - --base-el-auth-rpc-port + --el-port-offset * [0, --nodes + --light-clients) UNIT_TEST_BASE_PORT := 9950 @@ -202,19 +204,19 @@ restapi-test: --resttest-delay 30 \ --kill-old-processes -ifneq ($(shell uname -p), arm) -TESTNET_EXTRA_FLAGS := --run-geth --dl-geth -else -TESTNET_EXTRA_FLAGS := -endif - local-testnet-minimal: ./scripts/launch_local_testnet.sh \ --data-dir $@ \ --preset minimal \ - --nodes 4 \ + --nodes 2 \ + --capella-fork-epoch 3 \ + --deneb-fork-epoch 20 \ --stop-at-epoch 6 \ --disable-htop \ + --remote-validators-count 512 \ + --enable-payload-builder \ + --nimbus-signer-nodes 1 \ + --threshold 1 \ --enable-logtrace \ --base-port $$(( 6001 + EXECUTOR_NUMBER * 500 )) \ --base-rest-port $$(( 6031 + EXECUTOR_NUMBER * 500 )) \ @@ -222,14 +224,15 @@ local-testnet-minimal: --base-vc-keymanager-port $$(( 6131 + EXECUTOR_NUMBER * 500 )) \ --base-vc-metrics-port $$(( 6161 + EXECUTOR_NUMBER * 500 )) \ --base-remote-signer-port $$(( 6201 + EXECUTOR_NUMBER * 500 )) \ + --base-remote-signer-metrics-port $$(( 6251 + EXECUTOR_NUMBER * 500 )) \ --base-el-net-port $$(( 6301 + EXECUTOR_NUMBER * 500 )) \ - --base-el-http-port $$(( 6302 + EXECUTOR_NUMBER * 500 )) \ + --base-el-rpc-port $$(( 6302 + EXECUTOR_NUMBER * 500 )) \ --base-el-ws-port $$(( 6303 + EXECUTOR_NUMBER * 500 )) \ --base-el-auth-rpc-port $$(( 6304 + EXECUTOR_NUMBER * 500 )) \ --el-port-offset 5 \ --timeout 648 \ --kill-old-processes \ - $(TESTNET_EXTRA_FLAGS) \ + --run-geth --dl-geth \ -- \ --verify-finalization \ --discv5:no @@ -237,24 +240,28 @@ local-testnet-minimal: local-testnet-mainnet: ./scripts/launch_local_testnet.sh \ --data-dir $@ \ - --nodes 4 \ + --nodes 2 \ --stop-at-epoch 6 \ --disable-htop \ --enable-logtrace \ + --nimbus-signer-nodes 3 \ + --threshold 2 \ + --remote-validators-count 512 \ --base-port $$(( 7001 + EXECUTOR_NUMBER * 500 )) \ --base-rest-port $$(( 7031 + EXECUTOR_NUMBER * 500 )) \ --base-metrics-port $$(( 7061 + EXECUTOR_NUMBER * 500 )) \ --base-vc-keymanager-port $$(( 7131 + EXECUTOR_NUMBER * 500 )) \ --base-vc-metrics-port $$(( 7161 + EXECUTOR_NUMBER * 500 )) \ --base-remote-signer-port $$(( 7201 + EXECUTOR_NUMBER * 500 )) \ + --base-remote-signer-metrics-port $$(( 7251 + EXECUTOR_NUMBER * 500 )) \ --base-el-net-port $$(( 7301 + EXECUTOR_NUMBER * 500 )) \ - --base-el-http-port $$(( 7302 + EXECUTOR_NUMBER * 500 )) \ + --base-el-rpc-port $$(( 7302 + EXECUTOR_NUMBER * 500 )) \ --base-el-ws-port $$(( 7303 + EXECUTOR_NUMBER * 500 )) \ --base-el-auth-rpc-port $$(( 7304 + EXECUTOR_NUMBER * 500 )) \ --el-port-offset 5 \ --timeout 2784 \ --kill-old-processes \ - $(TESTNET_EXTRA_FLAGS) \ + --run-geth --dl-geth \ -- \ --verify-finalization \ --discv5:no diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index d37ce1b2b..1043c573b 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -95,7 +95,6 @@ type dynamicFeeRecipientsStore*: ref DynamicFeeRecipientsStore externalBuilderRegistrations*: Table[ValidatorPubKey, SignedValidatorRegistrationV1] - mergeAtEpoch*: Epoch dutyValidatorCount*: int ## Number of validators that we've checked for activation diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 7f173ff92..556d61810 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -64,7 +64,6 @@ else: type BNStartUpCmd* {.pure.} = enum noCommand - createTestnet deposits wallets record @@ -227,12 +226,6 @@ type desc: "The slashing DB flavour to use" name: "slashing-db-kind" .}: SlashingDbKind - mergeAtEpoch* {. - hidden - desc: "Debugging argument not for external use; may be removed at any time" - defaultValue: FAR_FUTURE_EPOCH - name: "merge-at-epoch-debug-internal" .}: uint64 - numThreads* {. defaultValue: 0, desc: "Number of worker threads (\"0\" = use as many threads as there are CPU cores available)" @@ -324,6 +317,10 @@ type desc: "SSZ file specifying a recent finalized state" name: "finalized-checkpoint-state" .}: Option[InputFile] + finalizedDepositTreeSnapshot* {. + desc: "SSZ file specifying a recent finalized EIP-4881 deposit tree snapshot" + name: "finalized-deposit-tree-snapshot" .}: Option[InputFile] + finalizedCheckpointBlock* {. hidden desc: "SSZ file specifying a recent finalized block" @@ -607,40 +604,6 @@ type defaultValue: HistoryMode.Archive name: "history".}: HistoryMode - of BNStartUpCmd.createTestnet: - 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" - name: "total-validators" .}: uint64 - - bootstrapAddress* {. - desc: "The public IP address that will be advertised as a bootstrap node for the testnet" - defaultValue: init(ValidIpAddress, defaultAdminListenAddress) - defaultValueDesc: $defaultAdminListenAddressDesc - name: "bootstrap-address" .}: ValidIpAddress - - bootstrapPort* {. - desc: "The TCP/UDP port that will be used by the bootstrap node" - defaultValue: defaultEth2TcpPort - defaultValueDesc: $defaultEth2TcpPortDesc - name: "bootstrap-port" .}: Port - - genesisOffset* {. - desc: "Seconds from now to add to genesis time" - defaultValue: 5 - name: "genesis-offset" .}: int - - outputGenesis* {. - desc: "Output file where to write the initial state snapshot" - name: "output-genesis" .}: OutFile - - outputBootstrapFile* {. - desc: "Output file with list of bootstrap nodes for the network" - name: "output-bootstrap-file" .}: OutFile - of BNStartUpCmd.wallets: case walletsCmd* {.command.}: WalletsCmd of WalletsCmd.create: @@ -1136,6 +1099,13 @@ func parseCmdArg*(T: type Checkpoint, input: string): T func completeCmdArg*(T: type Checkpoint, input: string): seq[string] = return @[] +func parseCmdArg*(T: type Epoch, input: string): T + {.raises: [ValueError, Defect].} = + Epoch parseBiggestUInt(input) + +func completeCmdArg*(T: type Epoch, input: string): seq[string] = + return @[] + func isPrintable(rune: Rune): bool = # This can be eventually replaced by the `unicodeplus` package, but a single # proc does not justify the extra dependencies at the moment: diff --git a/beacon_chain/consensus_object_pools/blockchain_dag.nim b/beacon_chain/consensus_object_pools/blockchain_dag.nim index ac0c18b38..2ff271165 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag.nim @@ -1073,7 +1073,13 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB, of ConsensusFork.EIP4844: denebFork(cfg) stateFork = getStateField(dag.headState, fork) - if stateFork != configFork: + # Here, we check only the `current_version` field because the spec + # mandates that testnets starting directly from a particular fork + # should have `previous_version` set to `current_version` while + # this doesn't happen to be the case in network that go through + # regular hard-fork upgrades. See for example: + # https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#testing + if stateFork.current_version != configFork.current_version: error "State from database does not match network, check --network parameter", tail = dag.tail, headRef, stateFork, configFork quit 1 diff --git a/beacon_chain/eth1/eth1_monitor.nim b/beacon_chain/eth1/eth1_monitor.nim index dfc6b9639..0d97b308d 100644 --- a/beacon_chain/eth1/eth1_monitor.nim +++ b/beacon_chain/eth1/eth1_monitor.nim @@ -269,9 +269,7 @@ func asConsensusExecutionPayload*(rpcExecutionPayload: ExecutionPayloadV1): gas_limit: rpcExecutionPayload.gasLimit.uint64, gas_used: rpcExecutionPayload.gasUsed.uint64, timestamp: rpcExecutionPayload.timestamp.uint64, - extra_data: - List[byte, MAX_EXTRA_DATA_BYTES].init( - rpcExecutionPayload.extraData.distinctBase), + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(rpcExecutionPayload.extraData.bytes), base_fee_per_gas: rpcExecutionPayload.baseFeePerGas, block_hash: rpcExecutionPayload.blockHash.asEth2Digest, transactions: List[bellatrix.Transaction, MAX_TRANSACTIONS_PER_PAYLOAD].init( @@ -294,9 +292,7 @@ func asConsensusExecutionPayload*(rpcExecutionPayload: ExecutionPayloadV2): gas_limit: rpcExecutionPayload.gasLimit.uint64, gas_used: rpcExecutionPayload.gasUsed.uint64, timestamp: rpcExecutionPayload.timestamp.uint64, - extra_data: - List[byte, MAX_EXTRA_DATA_BYTES].init( - rpcExecutionPayload.extraData.distinctBase), + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(rpcExecutionPayload.extraData.bytes), base_fee_per_gas: rpcExecutionPayload.baseFeePerGas, block_hash: rpcExecutionPayload.blockHash.asEth2Digest, transactions: List[bellatrix.Transaction, MAX_TRANSACTIONS_PER_PAYLOAD].init( @@ -321,9 +317,7 @@ func asConsensusExecutionPayload*(rpcExecutionPayload: ExecutionPayloadV3): gas_limit: rpcExecutionPayload.gasLimit.uint64, gas_used: rpcExecutionPayload.gasUsed.uint64, timestamp: rpcExecutionPayload.timestamp.uint64, - extra_data: - List[byte, MAX_EXTRA_DATA_BYTES].init( - rpcExecutionPayload.extraData.distinctBase), + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(rpcExecutionPayload.extraData.bytes), base_fee_per_gas: rpcExecutionPayload.baseFeePerGas, excess_data_gas: rpcExecutionPayload.excessDataGas, block_hash: rpcExecutionPayload.blockHash.asEth2Digest, @@ -349,8 +343,7 @@ func asEngineExecutionPayload*(executionPayload: bellatrix.ExecutionPayload): gasLimit: Quantity(executionPayload.gas_limit), gasUsed: Quantity(executionPayload.gas_used), timestamp: Quantity(executionPayload.timestamp), - extraData: - DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), + extraData: DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), baseFeePerGas: executionPayload.base_fee_per_gas, blockHash: executionPayload.block_hash.asBlockHash, transactions: mapIt(executionPayload.transactions, it.getTypedTransaction)) @@ -372,8 +365,7 @@ func asEngineExecutionPayload*(executionPayload: capella.ExecutionPayload): gasLimit: Quantity(executionPayload.gas_limit), gasUsed: Quantity(executionPayload.gas_used), timestamp: Quantity(executionPayload.timestamp), - extraData: - DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), + extraData: DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), baseFeePerGas: executionPayload.base_fee_per_gas, blockHash: executionPayload.block_hash.asBlockHash, transactions: mapIt(executionPayload.transactions, it.getTypedTransaction), @@ -396,8 +388,7 @@ func asEngineExecutionPayload*(executionPayload: eip4844.ExecutionPayload): gasLimit: Quantity(executionPayload.gas_limit), gasUsed: Quantity(executionPayload.gas_used), timestamp: Quantity(executionPayload.timestamp), - extraData: - DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), + extraData: DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data), baseFeePerGas: executionPayload.base_fee_per_gas, excessDataGas: executionPayload.excess_data_gas, blockHash: executionPayload.block_hash.asBlockHash, @@ -591,7 +582,7 @@ proc forkchoiceUpdated*( Future[engine_api.ForkchoiceUpdatedResponse] = # Eth1 monitor can recycle connections without (external) warning; at least, # don't crash. - if p.isNil or p.dataProvider.isNil: + if p.isNil or p.dataProvider.isNil or headBlock.isZeroMemory: let fcuR = newFuture[engine_api.ForkchoiceUpdatedResponse]("forkchoiceUpdated") fcuR.complete(engine_api.ForkchoiceUpdatedResponse( @@ -613,7 +604,7 @@ proc forkchoiceUpdated*( Future[engine_api.ForkchoiceUpdatedResponse] = # Eth1 monitor can recycle connections without (external) warning; at least, # don't crash. - if p.isNil or p.dataProvider.isNil: + if p.isNil or p.dataProvider.isNil or headBlock.isZeroMemory: let fcuR = newFuture[engine_api.ForkchoiceUpdatedResponse]("forkchoiceUpdated") fcuR.complete(engine_api.ForkchoiceUpdatedResponse( @@ -849,8 +840,7 @@ proc pruneOldBlocks(chain: var Eth1Chain, depositIndex: uint64) = chain.db.putDepositTreeSnapshot DepositTreeSnapshot( eth1Block: lastBlock.hash, depositContractState: chain.finalizedDepositsMerkleizer.toDepositContractState, - blockHeight: lastBlock.number, - ) + blockHeight: lastBlock.number) eth1_finalized_head.set lastBlock.number.toGaugeValue eth1_finalized_deposits.set lastBlock.depositCount.toGaugeValue @@ -1144,6 +1134,10 @@ proc init*(T: type Eth1Monitor, for url in mitems(web3Urls): fixupWeb3Urls url + debug "Initializing Eth1Monitor", + depositContractBlockNumber, + depositContractBlockHash + let eth1Chain = Eth1Chain.init( cfg, db, depositContractBlockNumber, depositContractBlockHash) diff --git a/beacon_chain/networking/eth2_network.nim b/beacon_chain/networking/eth2_network.nim index a16fb22cf..5ec0fd111 100644 --- a/beacon_chain/networking/eth2_network.nim +++ b/beacon_chain/networking/eth2_network.nim @@ -2151,7 +2151,7 @@ proc getRandomNetKeys*(rng: var HmacDrbgContext): NetKeyPair = quit QuitFailure initNetKeys(privKey) -proc getPersistentNetKeys( +proc getPersistentNetKeys*( rng: var HmacDrbgContext, dataDir, netKeyFile: string, netKeyInsecurePassword: bool, @@ -2214,15 +2214,6 @@ proc getPersistentNetKeys*( rng.getPersistentNetKeys( string(config.dataDir), config.netKeyFile, config.netKeyInsecurePassword, allowLoadExisting = true) - - of BNStartUpCmd.createTestnet: - if config.netKeyFile == "random": - fatal "Could not create testnet using `random` network key" - quit QuitFailure - - rng.getPersistentNetKeys( - string(config.dataDir), config.netKeyFile, config.netKeyInsecurePassword, - allowLoadExisting = false) else: rng.getRandomNetKeys() diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 2aa4d37a5..759bcd639 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -472,7 +472,7 @@ proc init*(T: type BeaconNode, newClone(readSszForkedHashedBeaconState( cfg, readAllBytes(checkpointStatePath).tryGet())) except SszError as err: - fatal "Checkpoint state deserialization failed", + fatal "Checkpoint state loading failed", err = formatMsg(err, checkpointStatePath) quit 1 except CatchableError as err: @@ -487,6 +487,20 @@ proc init*(T: type BeaconNode, else: nil + if config.finalizedDepositTreeSnapshot.isSome: + let + depositTreeSnapshotPath = config.finalizedDepositTreeSnapshot.get.string + depositTreeSnapshot = try: + SSZ.loadFile(depositTreeSnapshotPath, DepositTreeSnapshot) + except SszError as err: + fatal "Deposit tree snapshot loading failed", + err = formatMsg(err, depositTreeSnapshotPath) + quit 1 + except CatchableError as err: + fatal "Failed to read deposit tree snapshot file", err = err.msg + quit 1 + db.putDepositTreeSnapshot(depositTreeSnapshot) + let optJwtSecret = rng[].loadJwtSecret(config, allowCreate = false) if config.web3Urls.len() == 0: @@ -689,8 +703,7 @@ proc init*(T: type BeaconNode, # Delay first call by that time to allow for EL syncing to begin; it can # otherwise generate an EL warning by claiming a zero merge block. Moment.now + chronos.seconds(60), - dynamicFeeRecipientsStore: newClone(DynamicFeeRecipientsStore.init()), - mergeAtEpoch: config.mergeAtEpoch.Epoch) + dynamicFeeRecipientsStore: newClone(DynamicFeeRecipientsStore.init())) node.initLightClient( rng, cfg, dag.forkDigests, getBeaconTime, dag.genesis_validators_root) @@ -1896,69 +1909,6 @@ proc doRunBeaconNode(config: var BeaconNodeConf, rng: ref HmacDrbgContext) {.rai else: node.start() -proc doCreateTestnet*(config: BeaconNodeConf, rng: var HmacDrbgContext) {.raises: [Defect, CatchableError].} = - 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 - - var deposits: seq[DepositData] - for i in 0 ..< launchPadDeposits.len: - deposits.add(launchPadDeposits[i] as DepositData) - - let - startTime = uint64(times.toUnix(times.getTime()) + config.genesisOffset) - outGenesis = config.outputGenesis.string - eth1Hash = if config.web3Urls.len == 0: eth1BlockHash - else: (waitFor getEth1BlockHash( - config.web3Urls[0], blockId("latest"), - rng.loadJwtSecret(config, allowCreate = true))).asEth2Digest - cfg = getRuntimeConfig(config.eth2Network) - var - initialState = newClone(initialize_beacon_state_from_eth1( - cfg, eth1Hash, startTime, deposits, {skipBlsValidation})) - - # https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state - initialState.genesis_time = startTime - - doAssert initialState.validators.len > 0 - - let outGenesisExt = splitFile(outGenesis).ext - if cmpIgnoreCase(outGenesisExt, ".json") == 0: - Json.saveFile(outGenesis, initialState, pretty = true) - echo "Wrote ", outGenesis - - let outSszGenesis = outGenesis.changeFileExt "ssz" - SSZ.saveFile(outSszGenesis, initialState[]) - echo "Wrote ", outSszGenesis - - let bootstrapFile = config.outputBootstrapFile.string - if bootstrapFile.len > 0: - type MetaData = altair.MetaData - let - networkKeys = getPersistentNetKeys(rng, config) - - netMetadata = MetaData() - forkId = getENRForkID( - cfg, - initialState[].slot.epoch, - initialState[].genesis_validators_root) - bootstrapEnr = enr.Record.init( - 1, # sequence number - networkKeys.seckey.asEthKey, - some(config.bootstrapAddress), - some(config.bootstrapPort), - some(config.bootstrapPort), - [ - toFieldPair(enrForkIdField, SSZ.encode(forkId)), - toFieldPair(enrAttestationSubnetsField, SSZ.encode(netMetadata.attnets)) - ]) - - writeFile(bootstrapFile, bootstrapEnr.tryGet().toURI) - echo "Wrote ", bootstrapFile - proc doRecord(config: BeaconNodeConf, rng: var HmacDrbgContext) {. raises: [Defect, CatchableError].} = case config.recordCmd: @@ -2056,7 +2006,6 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [Defect, CatchableEr let rng = keys.newRng() case config.cmd - of BNStartUpCmd.createTestnet: doCreateTestnet(config, rng[]) of BNStartUpCmd.noCommand: doRunBeaconNode(config, rng) of BNStartUpCmd.deposits: doDeposits(config, rng[]) of BNStartUpCmd.wallets: doWallets(config, rng[]) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index f8a3da309..e43c0d52d 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -16,12 +16,11 @@ import "."/[eth2_merkleization, forks, signatures, validator] from std/algorithm import fill -from std/math import `^` from std/sequtils import anyIt, mapIt from ./datatypes/capella import BeaconState, ExecutionPayloadHeader, Withdrawal -export extras, forks, validator +export extras, forks, validator, chronicles # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/phase0/beacon-chain.md#increase_balance func increase_balance*(balance: var Gwei, delta: Gwei) = @@ -223,108 +222,6 @@ proc slash_validator*( func genesis_time_from_eth1_timestamp*(cfg: RuntimeConfig, eth1_timestamp: uint64): uint64 = eth1_timestamp + cfg.GENESIS_DELAY -# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/phase0/beacon-chain.md#genesis -proc initialize_beacon_state_from_eth1*( - cfg: RuntimeConfig, - eth1_block_hash: Eth2Digest, - eth1_timestamp: uint64, - deposits: openArray[DepositData], - flags: UpdateFlags = {}): phase0.BeaconState = - ## Get the genesis ``BeaconState``. - ## - ## Before the beacon chain starts, validators will register in the Eth1 chain - ## and deposit ETH. When enough many validators have registered, a - ## `ChainStart` log will be emitted and the beacon chain can start beaconing. - ## - ## Because the state root hash is part of the genesis block, the beacon state - ## must be calculated before creating the genesis block. - - # Induct validators - # Not in spec: the system doesn't work unless there are at least SLOTS_PER_EPOCH - # validators - there needs to be at least one member in each committee - - # good to know for testing, though arguably the system is not that useful at - # at that point :) - doAssert deposits.lenu64 >= SLOTS_PER_EPOCH - - # TODO https://github.com/nim-lang/Nim/issues/19094 - template state(): untyped = result - state = phase0.BeaconState( - fork: genesisFork(cfg), - genesis_time: genesis_time_from_eth1_timestamp(cfg, eth1_timestamp), - eth1_data: - Eth1Data(block_hash: eth1_block_hash, deposit_count: uint64(len(deposits))), - latest_block_header: - BeaconBlockHeader( - body_root: hash_tree_root(default(phase0.BeaconBlockBody)))) - - # Seed RANDAO with Eth1 entropy - state.randao_mixes.fill(eth1_block_hash) - - var merkleizer = createMerkleizer(2'i64^DEPOSIT_CONTRACT_TREE_DEPTH) - for i, deposit in deposits: - let htr = hash_tree_root(deposit) - merkleizer.addChunk(htr.data) - - # This is already known in the Eth1 monitor, but it would be too - # much work to refactor all the existing call sites in the test suite - state.eth1_data.deposit_root = mixInLength(merkleizer.getFinalHash(), - deposits.len) - state.eth1_deposit_index = deposits.lenu64 - - var pubkeyToIndex = initTable[ValidatorPubKey, ValidatorIndex]() - for idx, deposit in deposits: - let - pubkey = deposit.pubkey - amount = deposit.amount - - pubkeyToIndex.withValue(pubkey, foundIdx) do: - # Increase balance by deposit amount - increase_balance(state, foundIdx[], amount) - do: - if skipBlsValidation in flags or - verify_deposit_signature(cfg, deposit): - pubkeyToIndex[pubkey] = ValidatorIndex(state.validators.len) - if not state.validators.add(get_validator_from_deposit(deposit)): - raiseAssert "too many validators" - if not state.balances.add(amount): - raiseAssert "same as validators" - - else: - # Invalid deposits are perfectly possible - trace "Skipping deposit with invalid signature", - deposit = shortLog(deposit) - - # Process activations - for vidx in state.validators.vindices: - let - balance = state.balances.item(vidx) - validator = addr state.validators.mitem(vidx) - - validator.effective_balance = min( - balance - balance mod EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) - - if validator.effective_balance == MAX_EFFECTIVE_BALANCE: - validator.activation_eligibility_epoch = GENESIS_EPOCH - validator.activation_epoch = GENESIS_EPOCH - - # Set genesis validators root for domain separation and chain versioning - state.genesis_validators_root = hash_tree_root(state.validators) - - # TODO https://github.com/nim-lang/Nim/issues/19094 - # state - -proc initialize_hashed_beacon_state_from_eth1*( - cfg: RuntimeConfig, - eth1_block_hash: Eth2Digest, - eth1_timestamp: uint64, - deposits: openArray[DepositData], - flags: UpdateFlags = {}): phase0.HashedBeaconState = - # TODO https://github.com/nim-lang/Nim/issues/19094 - result = phase0.HashedBeaconState( - data: initialize_beacon_state_from_eth1( - cfg, eth1_block_hash, eth1_timestamp, deposits, flags)) - result.root = hash_tree_root(result.data) - # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/phase0/beacon-chain.md#genesis-block func get_initial_beacon_block*(state: phase0.HashedBeaconState): phase0.TrustedSignedBeaconBlock = @@ -920,7 +817,248 @@ func get_next_sync_committee*( res.aggregate_pubkey = finish(attestersAgg).toPubKey() res -# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/altair/fork.md#upgrading-the-state +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/phase0/beacon-chain.md#genesis +proc initialize_beacon_state_from_eth1*( + cfg: RuntimeConfig, + eth1_block_hash: Eth2Digest, + eth1_timestamp: uint64, + deposits: openArray[DepositData], + flags: UpdateFlags = {}): phase0.BeaconState = + ## Get the genesis ``BeaconState``. + ## + ## Before the beacon chain starts, validators will register in the Eth1 chain + ## and deposit ETH. When enough many validators have registered, a + ## `ChainStart` log will be emitted and the beacon chain can start beaconing. + ## + ## Because the state root hash is part of the genesis block, the beacon state + ## must be calculated before creating the genesis block. + + # Induct validators + # Not in spec: the system doesn't work unless there are at least SLOTS_PER_EPOCH + # validators - there needs to be at least one member in each committee - + # good to know for testing, though arguably the system is not that useful at + # at that point :) + doAssert deposits.lenu64 >= SLOTS_PER_EPOCH + + # TODO https://github.com/nim-lang/Nim/issues/19094 + template state(): untyped = result + state = phase0.BeaconState( + fork: genesisFork(cfg), + genesis_time: genesis_time_from_eth1_timestamp(cfg, eth1_timestamp), + eth1_data: + Eth1Data(block_hash: eth1_block_hash, deposit_count: uint64(len(deposits))), + latest_block_header: + BeaconBlockHeader( + body_root: hash_tree_root(default(phase0.BeaconBlockBody)))) + + # Seed RANDAO with Eth1 entropy + state.randao_mixes.fill(eth1_block_hash) + + var merkleizer = createMerkleizer(DEPOSIT_CONTRACT_LIMIT) + for i, deposit in deposits: + let htr = hash_tree_root(deposit) + merkleizer.addChunk(htr.data) + + # This is already known in the Eth1 monitor, but it would be too + # much work to refactor all the existing call sites in the test suite + state.eth1_data.deposit_root = mixInLength(merkleizer.getFinalHash(), + deposits.len) + state.eth1_deposit_index = deposits.lenu64 + + var pubkeyToIndex = initTable[ValidatorPubKey, ValidatorIndex]() + for idx, deposit in deposits: + let + pubkey = deposit.pubkey + amount = deposit.amount + + pubkeyToIndex.withValue(pubkey, foundIdx) do: + # Increase balance by deposit amount + increase_balance(state, foundIdx[], amount) + do: + if skipBlsValidation in flags or + verify_deposit_signature(cfg, deposit): + pubkeyToIndex[pubkey] = ValidatorIndex(state.validators.len) + if not state.validators.add(get_validator_from_deposit(deposit)): + raiseAssert "too many validators" + if not state.balances.add(amount): + raiseAssert "same as validators" + + else: + # Invalid deposits are perfectly possible + trace "Skipping deposit with invalid signature", + deposit = shortLog(deposit) + + # Process activations + for vidx in state.validators.vindices: + let + balance = state.balances.item(vidx) + validator = addr state.validators.mitem(vidx) + + validator.effective_balance = min( + balance - balance mod EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) + + if validator.effective_balance == MAX_EFFECTIVE_BALANCE: + validator.activation_eligibility_epoch = GENESIS_EPOCH + validator.activation_epoch = GENESIS_EPOCH + + # Set genesis validators root for domain separation and chain versioning + state.genesis_validators_root = hash_tree_root(state.validators) + + # TODO https://github.com/nim-lang/Nim/issues/19094 + # state + +proc initialize_hashed_beacon_state_from_eth1*( + cfg: RuntimeConfig, + eth1_block_hash: Eth2Digest, + eth1_timestamp: uint64, + deposits: openArray[DepositData], + flags: UpdateFlags = {}): phase0.HashedBeaconState = + # TODO https://github.com/nim-lang/Nim/issues/19094 + result = phase0.HashedBeaconState( + data: initialize_beacon_state_from_eth1( + cfg, eth1_block_hash, eth1_timestamp, deposits, flags)) + result.root = hash_tree_root(result.data) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/bellatrix/beacon-chain.md#testing +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/capella/beacon-chain.md#testing +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/eip4844/beacon-chain.md#testing +proc initialize_beacon_state_from_eth1*( + cfg: RuntimeConfig, + eth1_block_hash: Eth2Digest, + eth1_timestamp: uint64, + deposits: openArray[DepositData], + execution_payload_header: ForkyExecutionPayloadHeader, + flags: UpdateFlags = {}): auto = + ## Get the genesis ``BeaconState``. + ## + ## Before the beacon chain starts, validators will register in the Eth1 chain + ## and deposit ETH. When enough many validators have registered, a + ## `ChainStart` log will be emitted and the beacon chain can start beaconing. + ## + ## Because the state root hash is part of the genesis block, the beacon state + ## must be calculated before creating the genesis block. + + # Induct validators + # Not in spec: the system doesn't work unless there are at least SLOTS_PER_EPOCH + # validators - there needs to be at least one member in each committee - + # good to know for testing, though arguably the system is not that useful at + # at that point :) + doAssert deposits.lenu64 >= SLOTS_PER_EPOCH + + const consensusFork = typeof(execution_payload_header).toFork + let + forkVersion = cfg.forkVersion(consensusFork) + fork = Fork( + previous_version: forkVersion, + current_version: forkVersion, + epoch: GENESIS_EPOCH) + + type BeaconState = BeaconStateType(consensusFork) + + # TODO https://github.com/nim-lang/Nim/issues/19094 + template state(): untyped = result + result = BeaconState( + fork: fork, + genesis_time: genesis_time_from_eth1_timestamp(cfg, eth1_timestamp), + eth1_data: + Eth1Data(block_hash: eth1_block_hash, deposit_count: uint64(len(deposits))), + latest_block_header: + BeaconBlockHeader( + body_root: hash_tree_root(default BeaconBlockBodyType(consensusFork)))) + + # Seed RANDAO with Eth1 entropy + state.randao_mixes.data.fill(eth1_block_hash) + + var merkleizer = createMerkleizer(DEPOSIT_CONTRACT_LIMIT) + for i, deposit in deposits: + let htr = hash_tree_root(deposit) + merkleizer.addChunk(htr.data) + + # This is already known in the Eth1 monitor, but it would be too + # much work to refactor all the existing call sites in the test suite + state.eth1_data.deposit_root = mixInLength(merkleizer.getFinalHash(), + deposits.len) + state.eth1_deposit_index = deposits.lenu64 + + var pubkeyToIndex = initTable[ValidatorPubKey, ValidatorIndex]() + for idx, deposit in deposits: + let + pubkey = deposit.pubkey + amount = deposit.amount + + pubkeyToIndex.withValue(pubkey, foundIdx) do: + # Increase balance by deposit amount + increase_balance(state, foundIdx[], amount) + do: + if skipBlsValidation in flags or + verify_deposit_signature(cfg, deposit): + pubkeyToIndex[pubkey] = ValidatorIndex(state.validators.len) + if not state.validators.add(get_validator_from_deposit(deposit)): + raiseAssert "too many validators" + if not state.balances.add(amount): + raiseAssert "same as validators" + + else: + # Invalid deposits are perfectly possible + trace "Skipping deposit with invalid signature", + deposit = shortLog(deposit) + + # Initialize epoch participations - TODO (This must be added to the spec) + var + empty_participation: EpochParticipationFlags + inactivity_scores = HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]() + + doAssert empty_participation.asList.setLen(state.validators.len) + doAssert inactivity_scores.data.setLen(state.validators.len) + inactivity_scores.resetCache() + + state.previous_epoch_participation = empty_participation + state.current_epoch_participation = empty_participation + state.inactivity_scores = inactivity_scores + + # Process activations + for vidx in state.validators.vindices: + let + balance = state.balances.item(vidx) + validator = addr state.validators.mitem(vidx) + + validator.effective_balance = min( + balance - balance mod EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) + + if validator.effective_balance == MAX_EFFECTIVE_BALANCE: + validator.activation_eligibility_epoch = GENESIS_EPOCH + validator.activation_epoch = GENESIS_EPOCH + + # Set genesis validators root for domain separation and chain versioning + state.genesis_validators_root = hash_tree_root(state.validators) + + # Fill in sync committees + # Note: A duplicate committee is assigned for the current and next committee at genesis + state.current_sync_committee = get_next_sync_committee(state) + state.next_sync_committee = get_next_sync_committee(state) + + # [New in Bellatrix] Initialize the execution payload header + # If empty, will initialize a chain that has not yet gone through the Merge transition + state.latest_execution_payload_header = execution_payload_header + + # TODO https://github.com/nim-lang/Nim/issues/19094 + # state + +proc initialize_hashed_beacon_state_from_eth1*( + cfg: RuntimeConfig, + eth1_block_hash: Eth2Digest, + eth1_timestamp: uint64, + deposits: openArray[DepositData], + execution_payload_header: ForkyExecutionPayloadHeader, + flags: UpdateFlags = {}): auto = + # TODO https://github.com/nim-lang/Nim/issues/19094 + result = initHashedBeaconState( + initialize_beacon_state_from_eth1( + cfg, eth1_block_hash, eth1_timestamp, deposits, + execution_payload_header, flags)) + result.root = hash_tree_root(result.data) + +# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.1/specs/altair/fork.md#upgrading-the-state func translate_participation( state: var altair.BeaconState, pending_attestations: openArray[phase0.PendingAttestation]) = diff --git a/beacon_chain/spec/datatypes/altair.nim b/beacon_chain/spec/datatypes/altair.nim index be0983cb7..32b749a11 100644 --- a/beacon_chain/spec/datatypes/altair.nim +++ b/beacon_chain/spec/datatypes/altair.nim @@ -249,6 +249,8 @@ type ## safety threshold) current_max_active_participants*: uint64 + InactivityScores* = HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] + # https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.3/specs/altair/beacon-chain.md#beaconstate BeaconState* = object # Versioning @@ -298,7 +300,7 @@ type finalized_checkpoint*: Checkpoint # Inactivity - inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] # [New in Altair] + inactivity_scores*: InactivityScores # [New in Altair] # Light client sync committees current_sync_committee*: SyncCommittee # [New in Altair] diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index d0cf4eb97..6a89e84d2 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -1015,3 +1015,9 @@ const eip4844ImplementationMissing* = false #template debugRaiseAssert*(x: string) = raiseAssert x template debugRaiseAssert*(x: string) = discard + +func ofLen*[T, N](ListType: type List[T, N], n: int): ListType = + if n < N: + distinctBase(result).setLen(n) + else: + raise newException(SszSizeMismatchError) diff --git a/beacon_chain/spec/datatypes/bellatrix.nim b/beacon_chain/spec/datatypes/bellatrix.nim index 0d3607e8e..7fb436fd0 100644 --- a/beacon_chain/spec/datatypes/bellatrix.nim +++ b/beacon_chain/spec/datatypes/bellatrix.nim @@ -137,7 +137,7 @@ type finalized_checkpoint*: Checkpoint # Inactivity - inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] # [New in Altair] + inactivity_scores*: InactivityScores # [New in Altair] # Light client sync committees current_sync_committee*: SyncCommittee # [New in Altair] @@ -152,6 +152,7 @@ type BeaconStateRef* = ref BeaconState not nil NilableBeaconStateRef* = ref BeaconState + # TODO: There should be only a single generic HashedBeaconState definition HashedBeaconState* = object data*: BeaconState root*: Eth2Digest # hash_tree_root(data) @@ -347,6 +348,10 @@ type parentHash*: string timestamp*: string +# TODO: There should be only a single generic HashedBeaconState definition +func initHashedBeaconState*(s: BeaconState): HashedBeaconState = + HashedBeaconState(data: s) + func fromHex*(T: typedesc[BloomLogs], s: string): T {. raises: [Defect, ValueError].} = hexToByteArray(s, result.data) diff --git a/beacon_chain/spec/datatypes/capella.nim b/beacon_chain/spec/datatypes/capella.nim index 39f7ac24d..9462ebd4c 100644 --- a/beacon_chain/spec/datatypes/capella.nim +++ b/beacon_chain/spec/datatypes/capella.nim @@ -255,7 +255,7 @@ type finalized_checkpoint*: Checkpoint # Inactivity - inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] + inactivity_scores*: InactivityScores # Light client sync committees current_sync_committee*: SyncCommittee @@ -279,6 +279,7 @@ type BeaconStateRef* = ref BeaconState not nil NilableBeaconStateRef* = ref BeaconState + # TODO: There should be only a single generic HashedBeaconState definition HashedBeaconState* = object data*: BeaconState root*: Eth2Digest # hash_tree_root(data) @@ -488,6 +489,10 @@ type bls_to_execution_changes*: List[SignedBLSToExecutionChange, Limit MAX_BLS_TO_EXECUTION_CHANGES] +# TODO: There should be only a single generic HashedBeaconState definition +func initHashedBeaconState*(s: BeaconState): HashedBeaconState = + HashedBeaconState(data: s) + func shortLog*(v: SomeBeaconBlock): auto = ( slot: shortLog(v.slot), diff --git a/beacon_chain/spec/datatypes/eip4844.nim b/beacon_chain/spec/datatypes/eip4844.nim index f25b023d7..568ff6438 100644 --- a/beacon_chain/spec/datatypes/eip4844.nim +++ b/beacon_chain/spec/datatypes/eip4844.nim @@ -280,7 +280,7 @@ type finalized_checkpoint*: Checkpoint # Inactivity - inactivity_scores*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT] + inactivity_scores*: InactivityScores # Light client sync committees current_sync_committee*: SyncCommittee @@ -303,6 +303,7 @@ type BeaconStateRef* = ref BeaconState not nil NilableBeaconStateRef* = ref BeaconState + # TODO: There should be only a single generic HashedBeaconState definition HashedBeaconState* = object data*: BeaconState root*: Eth2Digest # hash_tree_root(data) @@ -501,6 +502,10 @@ type parentHash*: string timestamp*: string +# TODO: There should be only a single generic HashedBeaconState definition +func initHashedBeaconState*(s: BeaconState): HashedBeaconState = + HashedBeaconState(data: s) + func shortLog*(v: SomeBeaconBlock): auto = ( slot: shortLog(v.slot), diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index c72bb3348..9bb1bd800 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -441,7 +441,6 @@ template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: capella.MsgTrust template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: deneb.MsgTrustedSignedBeaconBlock): T = T(kind: ConsensusFork.EIP4844, eip4844Data: blck) - template init*(T: type ForkedTrustedSignedBeaconBlock, blck: phase0.TrustedSignedBeaconBlock): T = T(kind: ConsensusFork.Phase0, phase0Data: blck) template init*(T: type ForkedTrustedSignedBeaconBlock, blck: altair.TrustedSignedBeaconBlock): T = diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index d0ad65cbc..716a3a2a1 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -1192,14 +1192,15 @@ proc process_epoch*( process_justification_and_finalization(state, info.balances, flags) # state.slot hasn't been incremented yet. - if strictVerification in flags and currentEpoch >= 2: - doAssert state.current_justified_checkpoint.epoch + 2 >= currentEpoch - - if strictVerification in flags and currentEpoch >= 3: + if strictVerification in flags: # Rule 2/3/4 finalization results in the most pessimal case. The other # three finalization rules finalize more quickly as long as the any of # the finalization rules triggered. - doAssert state.finalized_checkpoint.epoch + 3 >= currentEpoch + if (currentEpoch >= 2 and state.current_justified_checkpoint.epoch + 2 < currentEpoch) or + (currentEpoch >= 3 and state.finalized_checkpoint.epoch + 3 < currentEpoch): + fatal "The network did not finalize", + currentEpoch, finalizedEpoch = state.finalized_checkpoint.epoch + quit 1 process_inactivity_updates(cfg, state, info) diff --git a/beacon_chain/validators/validator_duties.nim b/beacon_chain/validators/validator_duties.nim index 53e7a0d24..bc2180dd0 100644 --- a/beacon_chain/validators/validator_duties.nim +++ b/beacon_chain/validators/validator_duties.nim @@ -387,10 +387,9 @@ proc getExecutionPayload[T]( let beaconHead = node.attestationPool[].getBeaconHead(node.dag.head) - executionBlockRoot = node.dag.loadExecutionBlockRoot(beaconHead.blck) - latestHead = - if not executionBlockRoot.isZero: - executionBlockRoot + executionHead = withState(proposalState[]): + when stateFork >= ConsensusFork.Bellatrix: + forkyState.data.latest_execution_payload_header.block_hash else: (static(default(Eth2Digest))) latestSafe = beaconHead.safeExecutionPayloadHash @@ -405,7 +404,7 @@ proc getExecutionPayload[T]( Opt.none(seq[Withdrawal]) payload_id = if lastFcU.isSome and - lastFcU.get.headBlockRoot == latestHead and + lastFcU.get.headBlockRoot == executionHead and lastFcU.get.safeBlockRoot == latestSafe and lastFcU.get.finalizedBlockRoot == latestFinalized and lastFcU.get.timestamp == timestamp and @@ -414,7 +413,7 @@ proc getExecutionPayload[T]( some bellatrix.PayloadID(lastFcU.get.payloadId) else: debug "getExecutionPayload: didn't find payloadId, re-querying", - latestHead, latestSafe, latestFinalized, + executionHead, latestSafe, latestFinalized, timestamp, feeRecipient, cachedForkchoiceUpdateInformation = lastFcU @@ -422,7 +421,7 @@ proc getExecutionPayload[T]( let random = withState(proposalState[]): get_randao_mix( forkyState.data, get_current_epoch(forkyState.data)) let fcu_payload_id = (await forkchoice_updated( - latestHead, latestSafe, latestFinalized, timestamp, random, + executionHead, latestSafe, latestFinalized, timestamp, random, feeRecipient, withdrawals, node.consensusManager.eth1Monitor)) await sleepAsync(500.milliseconds) @@ -534,9 +533,8 @@ proc makeBeaconBlockForHeadAndSlot*[EP]( let fut = newFuture[Opt[EP]]("given-payload") fut.complete(modified_execution_payload) fut - elif slot.epoch < node.dag.cfg.BELLATRIX_FORK_EPOCH or not ( - state[].is_merge_transition_complete or - slot.epoch >= node.mergeAtEpoch): + elif slot.epoch < node.dag.cfg.BELLATRIX_FORK_EPOCH or + not state[].is_merge_transition_complete: let fut = newFuture[Opt[EP]]("empty-payload") fut.complete(Opt.some(default(EP))) fut diff --git a/ncli/logtrace.nim b/ncli/logtrace.nim index 0964101ad..91e5fcad1 100644 --- a/ncli/logtrace.nim +++ b/ncli/logtrace.nim @@ -4,9 +4,11 @@ # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -import confutils, json, times, streams, os, strutils, options, chronicles, - tables, sequtils -import json_serialization +import + std/[tables, sequtils, json, times, streams, os, strutils, options, typetraits], + confutils, chronicles, json_serialization + +from stew/io2 import IoErrorCode const LogTraceName = "Beacon-Chain LogTrace Tool" @@ -22,55 +24,55 @@ const LogTraceCopyright & "\r\n" type - StartUpCommand {.pure.} = enum + StartUpCommand* {.pure.} = enum pubsub, asl, asr, aggasr, scmsr, csr, lat, traceAll, localSimChecks - LogTraceConf = object - logFiles {. + LogTraceConf* = object + logFiles* {. desc: "Specifies one or more log files", abbr: "f", name: "log-file" .}: seq[string] - simDir {. + simDir* {. desc: "Specifies path to eth2_network_simulation directory", defaultValue: "", name: "sim-dir" .}: string - netDir {. + netDir* {. desc: "Specifies path to network build directory", defaultValue: "", name: "net-dir" .}: string - logDir {. + logDir* {. desc: "Specifies path with bunch of logs", defaultValue: "", name: "log-dir" .}: string - ignoreSerializationErrors {. + ignoreSerializationErrors* {. desc: "Ignore serialization errors while parsing log files", defaultValue: true, name: "ignore-errors" .}: bool - dumpSerializationErrors {. + dumpSerializationErrors* {. desc: "Dump full serialization errors while parsing log files", defaultValue: false , name: "dump-errors" .}: bool - nodes {. + nodes* {. desc: "Specifies node names which logs will be used", name: "nodes" .}: seq[string] - allowedLag {. + allowedLag* {. desc: "Allowed latency lag multiplier", defaultValue: 2.0, name: "lag" .}: float - constPreset {. + constPreset* {. desc: "The const preset being used" defaultValue: "mainnet" name: "const-preset" .}: string - case cmd {.command.}: StartUpCommand + case cmd* {.command.}: StartUpCommand of pubsub: discard of asl: @@ -316,13 +318,24 @@ template warning(issuesGroup: IssuesGroup, msg: string) = proc new(T: type IssuesGroup, name: string): T = T(name: name) -proc readValue(reader: var JsonReader, value: var DateTime) = +# TODO These definition can be moved to a more widely accessible module. +# It's needed when we compile logtrace itself with JSON logging. +proc writeValue*(writer: var JsonWriter, value: DateTime) = + writer.writeValue($value) + +proc readValue*(reader: var JsonReader, value: var DateTime) = let s = reader.readValue(string) try: value = parse(s, "YYYY-MM-dd HH:mm:ss'.'fffzzz", utc()) except CatchableError: raiseUnexpectedValue(reader, "Invalid date time") +proc writeValue*(writer: var JsonWriter, value: IoErrorCode) = + writer.writeValue(distinctBase value) + +proc readValue*(reader: var JsonReader, value: var IoErrorCode) = + IoErrorCode reader.readValue(distinctBase IoErrorCode) + proc init(t: typedesc[GossipMessage], kind: GossipDirection, id, datestr: string): GossipMessage = GossipMessage( @@ -1099,7 +1112,7 @@ proc runLatencyCheck(logConf: LogTraceConf, logFiles: seq[string], info "Latency statistics", min_time = minTime, max_time = maxTime, avg_time = avgTime, seconds_count = len(msgs) -proc run(conf: LogTraceConf) = +proc run*(conf: LogTraceConf) = var logFiles: seq[string] var logNodes: seq[NodeDirectory] diff --git a/ncli/ncli_testnet.nim b/ncli/ncli_testnet.nim new file mode 100644 index 000000000..1310fcf92 --- /dev/null +++ b/ncli/ncli_testnet.nim @@ -0,0 +1,649 @@ +# beacon_chain +# Copyright (c) 2018-2023 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/[os, sequtils, strutils, options, json, terminal, times], + chronos, bearssl/rand, chronicles, confutils, stint, json_serialization, + web3, web3/confutils_defs, eth/keys, eth/p2p/discoveryv5/random2, + stew/[io2, byteutils], json_rpc/jsonmarshal, + ../beacon_chain/[conf, filepath], + ../beacon_chain/eth1/eth1_monitor, + ../beacon_chain/networking/eth2_network, + ../beacon_chain/spec/[beaconstate, eth2_merkleization], + ../beacon_chain/spec/datatypes/base, + ../beacon_chain/spec/eth2_apis/eth2_rest_serialization, + ../beacon_chain/validators/keystore_management, + ./logtrace + +# Compiled version of /scripts/depositContract.v.py in this repo +# The contract was compiled in Remix (https://remix.ethereum.org/) with vyper (remote) compiler. +const depositContractCode = staticRead "../beacon_chain/eth1/deposit_contract_code.txt" + +type + Eth1Address = web3.Address + + StartUpCommand {.pure.} = enum + generateDeposits + createTestnet + run + sendDeposits + analyzeLogs + deployDepositContract + sendEth + + CliConfig* = object + web3Url* {. + defaultValue: "", + desc: "URL of the Web3 server to observe Eth1" + name: "web3-url" }: string + + privateKey* {. + defaultValue: "" + desc: "Private key of the controlling account" + name: "private-key" }: string + + askForKey* {. + defaultValue: false + desc: "Ask for an Eth1 private key interactively" + name: "ask-for-key" }: bool + + eth2Network* {. + desc: "The Eth2 network preset to use" + name: "network" }: Option[string] + + case cmd* {.command.}: StartUpCommand + of StartUpCommand.deployDepositContract: + discard + + of StartUpCommand.sendEth: + toAddress* {.name: "to".}: Eth1Address + valueEth* {.name: "eth".}: string + + of StartUpCommand.generateDeposits: + simulationDepositsCount* {. + desc: "The number of validator keystores to generate" + name: "count" }: Natural + + outValidatorsDir* {. + desc: "A directory to store the generated validator keystores" + name: "out-validators-dir" }: OutDir + + outSecretsDir* {. + desc: "A directory to store the generated keystore password files" + name: "out-secrets-dir" }: OutDir + + outDepositsFile* {. + desc: "A LaunchPad deposits file to write" + name: "out-deposits-file" }: OutFile + + threshold* {. + defaultValue: 1 + desc: "Used to generate distributed keys" + name: "threshold" }: uint32 + + remoteValidatorsCount* {. + defaultValue: 0 + desc: "The number of distributed validators validator" + name: "remote-validators-count" }: uint32 + + remoteSignersUrls* {. + desc: "URLs of the remote signers" + name: "remote-signer" }: seq[string] + + of StartUpCommand.createTestnet: + 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" + name: "total-validators" .}: uint64 + + bootstrapAddress* {. + desc: "The public IP address that will be advertised as a bootstrap node for the testnet" + defaultValue: init(ValidIpAddress, defaultAdminListenAddress) + defaultValueDesc: $defaultAdminListenAddressDesc + name: "bootstrap-address" .}: ValidIpAddress + + bootstrapPort* {. + desc: "The TCP/UDP port that will be used by the bootstrap node" + defaultValue: defaultEth2TcpPort + defaultValueDesc: $defaultEth2TcpPortDesc + name: "bootstrap-port" .}: Port + + dataDir* {. + desc: "Nimbus data directory where the keys of the bootstrap node will be placed" + name: "data-dir" .}: OutDir + + netKeyFile* {. + desc: "Source of network (secp256k1) private key file" + name: "netkey-file" .}: OutFile + + netKeyInsecurePassword* {. + desc: "Use pre-generated INSECURE password for network private key file" + defaultValue: false, + name: "insecure-netkey-password" .}: bool + + genesisTime* {. + desc: "Unix epoch time of the network genesis" + name: "genesis-time" .}: Option[uint64] + + genesisOffset* {. + desc: "Seconds from now to add to genesis time" + name: "genesis-offset" .}: Option[int] + + executionGenesisBlock* {. + desc: "The execution genesis block in a merged testnet" + name: "execution-genesis-block" .}: Option[InputFile] + + capellaForkEpoch* {. + defaultValue: FAR_FUTURE_EPOCH + desc: "The epoch of the Capella hard-fork" + name: "capella-fork-epoch" .}: Epoch + + denebForkEpoch* {. + defaultValue: FAR_FUTURE_EPOCH + desc: "The epoch of the Deneb hard-fork" + name: "deneb-fork-epoch" .}: Epoch + + outputGenesis* {. + desc: "Output file where to write the initial state snapshot" + name: "output-genesis" .}: OutFile + + outputDepositTreeSnapshot* {. + desc: "Output file where to write the initial deposit tree snapshot" + name: "output-deposit-tree-snapshot" .}: OutFile + + outputBootstrapFile* {. + desc: "Output file with list of bootstrap nodes for the network" + name: "output-bootstrap-file" .}: OutFile + + of StartUpCommand.sendDeposits: + depositsFile* {. + desc: "A LaunchPad deposits file" + name: "deposits-file" }: InputFile + + depositContractAddress* {. + desc: "Address of the deposit contract" + name: "deposit-contract" }: Eth1Address + + 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 + + of StartUpCommand.run: + discard + + of StartUpCommand.analyzeLogs: + logFiles* {. + desc: "Specifies one or more log files", + abbr: "f", + name: "log-file" .}: seq[string] + + simDir* {. + desc: "Specifies path to eth2_network_simulation directory", + defaultValue: "", + name: "sim-dir" .}: string + + netDir* {. + desc: "Specifies path to network build directory", + defaultValue: "", + name: "net-dir" .}: string + + logDir* {. + desc: "Specifies path with bunch of logs", + defaultValue: "", + name: "log-dir" .}: string + + ignoreSerializationErrors* {. + desc: "Ignore serialization errors while parsing log files", + defaultValue: true, + name: "ignore-errors" .}: bool + + dumpSerializationErrors* {. + desc: "Dump full serialization errors while parsing log files", + defaultValue: false , + name: "dump-errors" .}: bool + + nodes* {. + desc: "Specifies node names which logs will be used", + name: "nodes" .}: seq[string] + + allowedLag* {. + desc: "Allowed latency lag multiplier", + defaultValue: 2.0, + name: "lag" .}: float + + constPreset* {. + desc: "The const preset being used" + defaultValue: "mainnet" + name: "const-preset" .}: string + +type + PubKeyBytes = DynamicBytes[48, 48] + WithdrawalCredentialsBytes = DynamicBytes[32, 32] + SignatureBytes = DynamicBytes[96, 96] + +contract(DepositContract): + proc deposit(pubkey: PubKeyBytes, + withdrawalCredentials: WithdrawalCredentialsBytes, + signature: SignatureBytes, + deposit_data_root: FixedBytes[32]) + +template `as`(address: ethtypes.Address, T: type bellatrix.ExecutionAddress): T = + T(data: distinctBase(address)) + +template `as`(address: BlockHash, T: type Eth2Digest): T = + asEth2Digest(address) + +func getOrDefault[T](x: Option[T]): T = + if x.isSome: + x.get + else: + default T + +func `as`(blk: BlockObject, T: type bellatrix.ExecutionPayloadHeader): T = + T(parent_hash: blk.parentHash as Eth2Digest, + fee_recipient: blk.miner as ExecutionAddress, + state_root: blk.stateRoot as Eth2Digest, + receipts_root: blk.receiptsRoot as Eth2Digest, + logs_bloom: BloomLogs(data: distinctBase(blk.logsBloom)), + prev_randao: Eth2Digest(data: blk.difficulty.toByteArrayBE), # Is BE correct here? + block_number: uint64 blk.number, + gas_limit: uint64 blk.gasLimit, + gas_used: uint64 blk.gasUsed, + timestamp: uint64 blk.timestamp, + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(blk.extraData.bytes), + base_fee_per_gas: blk.baseFeePerGas.getOrDefault(), + block_hash: blk.hash as Eth2Digest, + transactions_root: blk.transactionsRoot as Eth2Digest) + +func `as`(blk: BlockObject, T: type capella.ExecutionPayloadHeader): T = + T(parent_hash: blk.parentHash as Eth2Digest, + fee_recipient: blk.miner as ExecutionAddress, + state_root: blk.stateRoot as Eth2Digest, + receipts_root: blk.receiptsRoot as Eth2Digest, + logs_bloom: BloomLogs(data: distinctBase(blk.logsBloom)), + prev_randao: Eth2Digest(data: blk.difficulty.toByteArrayBE), + block_number: uint64 blk.number, + gas_limit: uint64 blk.gasLimit, + gas_used: uint64 blk.gasUsed, + timestamp: uint64 blk.timestamp, + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(blk.extraData.bytes), + base_fee_per_gas: blk.baseFeePerGas.getOrDefault(), + block_hash: blk.hash as Eth2Digest, + transactions_root: blk.transactionsRoot as Eth2Digest, + withdrawals_root: blk.withdrawalsRoot.getOrDefault() as Eth2Digest) + +func `as`(blk: BlockObject, T: type deneb.ExecutionPayloadHeader): T = + T(parent_hash: blk.parentHash as Eth2Digest, + fee_recipient: blk.miner as ExecutionAddress, + state_root: blk.stateRoot as Eth2Digest, + receipts_root: blk.receiptsRoot as Eth2Digest, + logs_bloom: BloomLogs(data: distinctBase(blk.logsBloom)), + prev_randao: Eth2Digest(data: blk.difficulty.toByteArrayBE), + block_number: uint64 blk.number, + gas_limit: uint64 blk.gasLimit, + gas_used: uint64 blk.gasUsed, + timestamp: uint64 blk.timestamp, + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(blk.extraData.bytes), + base_fee_per_gas: blk.baseFeePerGas.getOrDefault(), + excess_data_gas: blk.excessDataGas.getOrDefault(), + block_hash: blk.hash as Eth2Digest, + transactions_root: blk.transactionsRoot as Eth2Digest, + withdrawals_root: blk.withdrawalsRoot.getOrDefault() as Eth2Digest) + +proc createDepositTreeSnapshot(deposits: seq[DepositData], + blockHash: Eth2Digest, + blockHeight: uint64): DepositTreeSnapshot = + var merkleizer = DepositsMerkleizer.init() + for i, deposit in deposits: + let htr = hash_tree_root(deposit) + merkleizer.addChunk(htr.data) + + DepositTreeSnapshot( + eth1Block: blockHash, + depositContractState: merkleizer.toDepositContractState, + blockHeight: blockHeight) + +proc doCreateTestnet*(config: CliConfig, + rng: var HmacDrbgContext) + {.raises: [Defect, CatchableError].} = + 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 + + var deposits: seq[DepositData] + for i in 0 ..< launchPadDeposits.len: + deposits.add(launchPadDeposits[i] as DepositData) + + let + startTime = if config.genesisTime.isSome: + config.genesisTime.get + else: + uint64(times.toUnix(times.getTime()) + config.genesisOffset.get(0)) + outGenesis = config.outputGenesis.string + eth1Hash = eth1BlockHash # TODO: Can we set a more appropriate value? + cfg = getRuntimeConfig(config.eth2Network) + + # This is intentionally left default initialized, when the user doesn't + # provide an execution genesis block. The generated genesis state will + # then be considered non-finalized merged state according to the spec. + var genesisBlock = BlockObject() + + if config.executionGenesisBlock.isSome: + logScope: + path = config.executionGenesisBlock.get.string + + if not fileExists(config.executionGenesisBlock.get.string): + error "The specified execution genesis block file doesn't exist" + quit 1 + + let genesisBlockContents = readAllChars(config.executionGenesisBlock.get.string) + if genesisBlockContents.isErr: + error "Failed to read the specified execution genesis block file", + err = genesisBlockContents.error + quit 1 + + try: + let blockAsJson = try: + parseJson genesisBlockContents.get + except CatchableError as err: + error "Failed to parse the genesis block json", err = err.msg + quit 1 + except: + # TODO The Nim json library should not raise bare exceptions + raiseAssert "The Nim json library raise a bare exception" + fromJson(blockAsJson, "", genesisBlock) + except CatchableError as err: + error "Failed to load the genesis block from json", + err = err.msg + quit 1 + + template createAndSaveState(genesisExecutionPayloadHeader: auto): Eth2Digest = + var initialState = newClone(initialize_beacon_state_from_eth1( + cfg, eth1Hash, startTime, deposits, genesisExecutionPayloadHeader, + {skipBlsValidation})) + # https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state + initialState.genesis_time = startTime + + doAssert initialState.validators.len > 0 + + # let outGenesisExt = splitFile(outGenesis).ext + #if cmpIgnoreCase(outGenesisExt, ".json") == 0: + + # let outGenesisJson = outGenesis & ".json" + # RestJson.saveFile(outGenesisJson, initialState, pretty = true) + # info "JSON genesis file written", path = outGenesisJson + + let outSszGenesis = outGenesis.changeFileExt "ssz" + SSZ.saveFile(outSszGenesis, initialState[]) + info "SSZ genesis file written", + path = outSszGenesis, fork = toFork(typeof initialState[]) + + SSZ.saveFile( + config.outputDepositTreeSnapshot.string, + createDepositTreeSnapshot( + deposits, + genesisExecutionPayloadHeader.block_hash, + genesisExecutionPayloadHeader.block_number)) + + initialState[].genesis_validators_root + + let genesisValidatorsRoot = + if config.denebForkEpoch == 0: + createAndSaveState(genesisBlock as deneb.ExecutionPayloadHeader) + elif config.capellaForkEpoch == 0: + createAndSaveState(genesisBlock as capella.ExecutionPayloadHeader) + else: + createAndSaveState(genesisBlock as bellatrix.ExecutionPayloadHeader) + + let bootstrapFile = string config.outputBootstrapFile + if bootstrapFile.len > 0: + type MetaData = altair.MetaData + let + networkKeys = rng.getPersistentNetKeys( + string config.dataDir, string config.netKeyFile, + config.netKeyInsecurePassword, allowLoadExisting = false) + + netMetadata = MetaData() + forkId = getENRForkID( + cfg, + Epoch(0), + genesisValidatorsRoot) + bootstrapEnr = enr.Record.init( + 1, # sequence number + networkKeys.seckey.asEthKey, + some(config.bootstrapAddress), + some(config.bootstrapPort), + some(config.bootstrapPort), + [ + toFieldPair(enrForkIdField, SSZ.encode(forkId)), + toFieldPair(enrAttestationSubnetsField, SSZ.encode(netMetadata.attnets)) + ]) + + writeFile(bootstrapFile, bootstrapEnr.tryGet().toURI) + echo "Wrote ", bootstrapFile + +proc deployContract*(web3: Web3, code: string): Future[ReceiptObject] {.async.} = + var code = code + if code[1] notin {'x', 'X'}: + code = "0x" & code + let tr = EthSend( + source: web3.defaultAccount, + data: code, + gas: Quantity(3000000).some, + gasPrice: 1.some) + + let r = await web3.send(tr) + result = await web3.getMinedTransactionReceipt(r) + +proc sendEth(web3: Web3, to: Eth1Address, valueEth: int): Future[TxHash] = + let tr = EthSend( + source: web3.defaultAccount, + gas: Quantity(3000000).some, + gasPrice: 1.some, + value: some(valueEth.u256 * 1000000000000000000.u256), + to: some(to)) + web3.send(tr) + +type + DelayGenerator* = proc(): chronos.Duration {.gcsafe, raises: [Defect].} + +proc ethToWei(eth: UInt256): UInt256 = + eth * 1000000000000000000.u256 + +proc initWeb3(web3Url, privateKey: string): Future[Web3] {.async.} = + result = await newWeb3(web3Url) + if privateKey.len != 0: + result.privateKey = some(keys.PrivateKey.fromHex(privateKey)[]) + else: + let accounts = await result.provider.eth_accounts() + doAssert(accounts.len > 0) + result.defaultAccount = accounts[0] + +# TODO: async functions should note take `seq` inputs because +# this leads to full copies. +proc sendDeposits*(deposits: seq[LaunchPadDeposit], + web3Url, privateKey: string, + depositContractAddress: Eth1Address, + delayGenerator: DelayGenerator = nil) {.async.} = + notice "Sending deposits", + web3 = web3Url, + depositContract = depositContractAddress + + var web3 = await initWeb3(web3Url, privateKey) + let gasPrice = int(await web3.provider.eth_gasPrice()) * 2 + let depositContract = web3.contractSender(DepositContract, + Eth1Address depositContractAddress) + for i in 4200 ..< deposits.len: + let dp = deposits[i] as DepositData + + while true: + try: + let tx = depositContract.deposit( + PubKeyBytes(@(dp.pubkey.toRaw())), + WithdrawalCredentialsBytes(@(dp.withdrawal_credentials.data)), + SignatureBytes(@(dp.signature.toRaw())), + FixedBytes[32](hash_tree_root(dp).data)) + + let status = await tx.send(value = 32.u256.ethToWei, gasPrice = gasPrice) + + info "Deposit sent", tx = $status + + if delayGenerator != nil: + await sleepAsync(delayGenerator()) + + break + except CatchableError: + await sleepAsync(chronos.seconds 60) + web3 = await initWeb3(web3Url, privateKey) + +{.pop.} # TODO confutils.nim(775, 17) Error: can raise an unlisted exception: ref IOError +proc main() {.async.} = + var conf = try: CliConfig.load() + except CatchableError as exc: + raise exc + except Exception as exc: # TODO fix confutils + raiseAssert exc.msg + + let rng = keys.newRng() + + if conf.cmd == StartUpCommand.generateDeposits: + let + mnemonic = generateMnemonic(rng[]) + seed = getSeed(mnemonic, KeystorePass.init "") + cfg = getRuntimeConfig(conf.eth2Network) + + if (let res = secureCreatePath(string conf.outValidatorsDir); res.isErr): + warn "Could not create validators folder", + path = string conf.outValidatorsDir, err = ioErrorMsg(res.error) + + if (let res = secureCreatePath(string conf.outSecretsDir); res.isErr): + warn "Could not create secrets folder", + path = string conf.outSecretsDir, err = ioErrorMsg(res.error) + + let deposits = generateDeposits( + cfg, + rng[], + seed, + 0, conf.simulationDepositsCount, + string conf.outValidatorsDir, + string conf.outSecretsDir, + conf.remoteSignersUrls, + conf.threshold, + conf.remoteValidatorsCount, + KeystoreMode.Fast) + + if deposits.isErr: + fatal "Failed to generate deposits", err = deposits.error + quit 1 + + let launchPadDeposits = + mapIt(deposits.value, LaunchPadDeposit.init(cfg, it)) + + Json.saveFile(string conf.outDepositsFile, launchPadDeposits) + notice "Deposit data written", filename = conf.outDepositsFile + quit 0 + + var deposits: seq[LaunchPadDeposit] + if conf.cmd == StartUpCommand.sendDeposits: + deposits = Json.loadFile(string conf.depositsFile, seq[LaunchPadDeposit]) + + if conf.askForKey: + var + privateKey: string # TODO consider using a SecretString type + reasonForKey = "" + + if conf.cmd == StartUpCommand.sendDeposits: + let + depositsWord = if deposits.len > 1: "deposits" else: "deposit" + totalEthNeeded = 32 * deposits.len + reasonForKey = " in order to make your $1 (you'll need access to $2 ETH)" % + [depositsWord, $totalEthNeeded] + + echo "Please enter your Goerli Eth1 private key in hex form (e.g. 0x1a2...f3c)" & + reasonForKey + + if not readPasswordFromStdin("> ", privateKey): + error "Failed to read an Eth1 private key from standard input" + + if privateKey.len > 0: + conf.privateKey = privateKey.string + + case conf.cmd + of StartUpCommand.createTestnet: + let rng = keys.newRng() + doCreateTestnet(conf, rng[]) + + of StartUpCommand.deployDepositContract: + let web3 = await initWeb3(conf.web3Url, conf.privateKey) + let receipt = await web3.deployContract(depositContractCode) + echo receipt.contractAddress.get, ";", receipt.blockHash + + of StartUpCommand.sendEth: + let web3 = await initWeb3(conf.web3Url, conf.privateKey) + echo await sendEth(web3, conf.toAddress, conf.valueEth.parseInt) + + of StartUpCommand.sendDeposits: + var delayGenerator: DelayGenerator + if not (conf.maxDelay > 0.0): + conf.maxDelay = conf.minDelay + elif conf.minDelay > conf.maxDelay: + echo "The minimum delay should not be larger than the maximum delay" + quit 1 + + if conf.maxDelay > 0.0: + delayGenerator = proc (): chronos.Duration = + let + minDelay = (conf.minDelay*1000).int64 + maxDelay = (conf.maxDelay*1000).int64 + chronos.milliseconds (rng[].rand(maxDelay - minDelay) + minDelay) + + await sendDeposits(deposits, conf.web3Url, conf.privateKey, + conf.depositContractAddress, delayGenerator) + + of StartUpCommand.run: + discard + + of StartUpCommand.analyzeLogs: + try: + logtrace.run(LogTraceConf( + cmd: logtrace.StartUpCommand.localSimChecks, + logFiles: conf.logFiles, + simDir: conf.simDir, + netDir: conf.netDir, + logDir: conf.logDir, + ignoreSerializationErrors: conf.ignoreSerializationErrors, + dumpSerializationErrors: conf.dumpSerializationErrors, + nodes: conf.nodes, + allowedLag: conf.allowedLag, + constPreset: conf.constPreset + )) + except CatchableError as err: + fatal "Unexpected error in logtrace", err = err.msg + except Exception as exc: + # TODO: Investigate where is this coming from? + fatal "Unexpected exception in logtrace", err = exc.msg + + of StartUpCommand.generateDeposits: + # This is handled above before the case statement + discard + +when isMainModule: + waitFor main() diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index 86012d853..bb3479c01 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -2346,7 +2346,7 @@ "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": [],"value": [[]]}] + "body": [{"operator": "jstructcmps", "start": [],"value": [{"version":"","data":{"attested_header":{"beacon":{"slot":"6","proposer_index":"28","parent_root":"0xc3640959c639e6fd3e3404842f09e211c0b74e71bb44fc32939a013bf385fe77","state_root":"0x3c61bd0b9bdb706d42b4b2a093fc3bfc50444b60d67e66f0f90ac5a1bdf0fdd2","body_root":"0x41d46fd59c2b75b47f1b2a9904f1af14a0b21b2d00108e9c2c7db63355b4c14d"}},"next_sync_committee":{"pubkeys":["0x9799063b332cfbdd6c5913fdcf47d83b6277be3f174d2acd8059dcadb84dab131a9c8ec4ddb4e1e42c0acd543deebfa9"],"aggregate_pubkey":"0x8675cdecf7cb5aa6c14de5ad24f638d6c384b65737d9f60618e0a9c1411bc11659a080617d1e07d46789739ceb929d02"},"next_sync_committee_branch":["0x40cfc59c70b042b86576ff570f8c4f84ca397e436c9a13a16fc713723a53f983"],"finalized_header":{"beacon":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"finality_branch":["0x0000000000000000000000000000000000000000000000000000000000000000"],"sync_aggregate":{"sync_committee_bits":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","sync_committee_signature":"0xa9e05329da094eebe7874f1664c2dfccd0430420c720e8243c25e4031a678f8d99a69270f7b72ba09d6672a7e61e155612f1e0ccf260ede2344322ea53a27acfefe6cd09bd3a543dab1f9678e474f3ac01da94af28373031c2f09e3aeeba2de8"},"signature_slot":"7"}}]}] } }, { @@ -2387,7 +2387,11 @@ "url": "/eth/v1/beacon/light_client/finality_update", "headers": {"Accept": "application/json"} }, - "response": {"status": {"operator": "equals", "value": "404"}} + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": {"attested_header":{"beacon":{"slot":"2","proposer_index":"18","parent_root":"0x87cc18e17479f1af290f078dee05f78fb61a493866874d4c7319737f8470d8c3","state_root":"0x9b33428ca370d41a89e94239d7bb4f117bd6f3d828f84307523660481613f036","body_root":"0x2546d749931de6be38ea6aa16f0633d46aef84543f3b25804f0e14a41774ca0f"}},"finalized_header":{"beacon":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"finality_branch":["0x0000000000000000000000000000000000000000000000000000000000000000"],"sync_aggregate":{"sync_committee_bits":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","sync_committee_signature":"0xb73ccf4a5ed492a75e6d569de7caf3595203b5b0687c6ea02531a9ae2fbbc7f34b9fe86e8b8f0e0ae976f199641a296b0e29a757114dc5c3cb2516d6affead47df71078f2216ec0196bfd11d6ff1881722e95317c7e6f3446ff5aacbdc94f300"},"signature_slot":"3"}}] + } }, { "topics": ["beacon", "beacon_light_client_optimistic_update"], @@ -2395,7 +2399,11 @@ "url": "/eth/v1/beacon/light_client/optimistic_update", "headers": {"Accept": "application/json"} }, - "response": {"status": {"operator": "equals", "value": "404"}} + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": {"attested_header":{"beacon":{"slot":"2","proposer_index":"18","parent_root":"0x87cc18e17479f1af290f078dee05f78fb61a493866874d4c7319737f8470d8c3","state_root":"0x9b33428ca370d41a89e94239d7bb4f117bd6f3d828f84307523660481613f036","body_root":"0x2546d749931de6be38ea6aa16f0633d46aef84543f3b25804f0e14a41774ca0f"}},"sync_aggregate":{"sync_committee_bits":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","sync_committee_signature":"0xb73ccf4a5ed492a75e6d569de7caf3595203b5b0687c6ea02531a9ae2fbbc7f34b9fe86e8b8f0e0ae976f199641a296b0e29a757114dc5c3cb2516d6affead47df71078f2216ec0196bfd11d6ff1881722e95317c7e6f3446ff5aacbdc94f300"},"signature_slot":"3"}}] + } }, { "topics": ["beacon", "pool_attestations"], @@ -2883,7 +2891,7 @@ "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": {"head_slot": "", "sync_distance": "", "is_syncing": false}}] + "body": [{"operator": "jstructcmps", "start": ["data"], "value": {"head_slot": "", "sync_distance": "", "is_syncing": false, "is_optimistic": false}}] } }, { @@ -2903,7 +2911,7 @@ "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data": [{"pubkey": "", "validator_index": "", "slot": ""}]}}] + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "execution_optimistic": false, "data": [{"pubkey": "", "validator_index": "", "slot": ""}]}}] } }, { @@ -3146,7 +3154,7 @@ "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "execution_optimistic": false, "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] } }, { @@ -3252,7 +3260,7 @@ "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "execution_optimistic": false, "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] } }, { diff --git a/research/timing.nim b/research/timing.nim new file mode 100644 index 000000000..982a77600 --- /dev/null +++ b/research/timing.nim @@ -0,0 +1,21 @@ +import + std/[times, stats] + +template withTimer*(stats: var RunningStat, body: untyped) = + # TODO unify timing somehow + let start = cpuTime() + + block: + body + + let stop = cpuTime() + stats.push stop - start + +template withTimerRet*(stats: var RunningStat, body: untyped): untyped = + let start = cpuTime() + let tmp = block: + body + let stop = cpuTime() + stats.push stop - start + + tmp diff --git a/scripts/execution_genesis.json.template b/scripts/execution_genesis.json.template new file mode 100644 index 000000000..3c4e585d6 --- /dev/null +++ b/scripts/execution_genesis.json.template @@ -0,0 +1,79 @@ +{ + "config": { + "chainId":9999, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "shanghaiTime": SHANGHAI_FORK_TIME, + "shardingForkTime": SHARDING_FORK_TIME, + "terminalTotalDifficulty":0 + }, + "nonce":"0x42", + "timestamp":"0x00", + "extraData":"", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b":{"balance":"0x6d6172697573766477000000"}, + "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf":{"balance":"0x6d6172697573766477000000"}, + "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf":{"balance":"0x6d6172697573766477000000"}, + "0x6813eb9362372eef6200f3b1dbc3f819671cba69":{"balance":"0x6d6172697573766477000000"}, + "0x1eff47bc3a10a45d4b230b5d10e37751fe6aa718":{"balance":"0x6d6172697573766477000000"}, + "0xe1ab8145f7e55dc933d51a18c793f901a3a0b276":{"balance":"0x6d6172697573766477000000"}, + "0xe57bfe9f44b819898f47bf37e5af72a0783e1141":{"balance":"0x6d6172697573766477000000"}, + "0xd41c057fd1c78805aac12b0a94a405c0461a6fbb":{"balance":"0x6d6172697573766477000000"}, + "0xf1f6619b38a98d6de0800f1defc0a6399eb6d30c":{"balance":"0x6d6172697573766477000000"}, + "0xf7edc8fa1ecc32967f827c9043fcae6ba73afa5c":{"balance":"0x6d6172697573766477000000"}, + "0x4242424242424242424242424242424242424242": { + "balance": "0", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + } + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} diff --git a/scripts/geth_binaries.sh b/scripts/geth_binaries.sh index d967672ae..afd5d8bda 100644 --- a/scripts/geth_binaries.sh +++ b/scripts/geth_binaries.sh @@ -14,7 +14,7 @@ source "${SCRIPTS_DIR}/bash_utils.sh" download_geth_stable() { if [[ ! -e "${STABLE_GETH_BINARY}" ]]; then - GETH_VERSION="1.10.26-e5eb32ac" + GETH_VERSION="1.10.26-e5eb32ac" GETH_URL="https://gethstore.blob.core.windows.net/builds/" case "${OS}-${ARCH}" in diff --git a/scripts/geth_vars.sh b/scripts/geth_vars.sh index e626969b2..3c772bded 100644 --- a/scripts/geth_vars.sh +++ b/scripts/geth_vars.sh @@ -5,13 +5,28 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -GETH_BINARY="${GETH_BINARY:-"${HOME}/go-ethereum/build/bin/geth"}" +if [ -z "${GETH_VARS_SOURCED:-}" ]; then +GETH_VARS_SOURCED=1 + GETH_NUM_NODES="${GETH_NUM_NODES:-4}" -GETH_BINARY="${GETH_BINARY:-${HOME}/go-ethereum/build/bin/geth}" GETH_BASE_NET_PORT="${BASE_EL_NET_PORT:-30303}" -GETH_BASE_HTTP_PORT="${BASE_EL_HTTP_PORT:-8545}" +GETH_BASE_RPC_PORT="${BASE_EL_RPC_PORT:-8545}" GETH_BASE_WS_PORT="${BASE_EL_WS_PORT:-8546}" GETH_BASE_AUTH_RPC_PORT="${BASE_EL_AUTH_RPC_PORT:-8551}" -GETH_PORT_OFFSET="${EL_PORT_OFFSET:-10}" -GENESISJSON="${GENESISJSON:-${BASEDIR}/geth_genesis.json}" +GETH_PORT_OFFSET="${EL_PORT_OFFSET:-20}" DISCOVER="--nodiscover" + +GETH_NET_PORTS=() +GETH_AUTH_RPC_PORTS=() +GETH_DATA_DIRS=() + +GETH_LAST_NODE_IDX=$((GETH_NUM_NODES - 1)) + +for GETH_NODE_IDX in $(seq 0 $GETH_LAST_NODE_IDX); do + GETH_NET_PORTS+=($(( GETH_NODE_IDX * GETH_PORT_OFFSET + GETH_BASE_NET_PORT ))) + GETH_RPC_PORTS+=($(( GETH_NODE_IDX * GETH_PORT_OFFSET + GETH_BASE_RPC_PORT ))) + GETH_AUTH_RPC_PORTS+=($(( GETH_NODE_IDX * GETH_PORT_OFFSET + GETH_BASE_AUTH_RPC_PORT ))) + GETH_DATA_DIRS+=("${DATA_DIR}/geth-${GETH_NODE_IDX}") +done + +fi diff --git a/scripts/launch_local_testnet.sh b/scripts/launch_local_testnet.sh index 24f1a7bf4..4ca85c90c 100755 --- a/scripts/launch_local_testnet.sh +++ b/scripts/launch_local_testnet.sh @@ -12,8 +12,9 @@ set -euo pipefail -BASEDIR="$(dirname "${BASH_SOURCE[0]}")" -cd "$BASEDIR/.." +SCRIPTS_DIR="$(dirname "${BASH_SOURCE[0]}")" +cd "$SCRIPTS_DIR/.." +BUILD_DIR="$(pwd)/build" VERBOSE="0" @@ -34,6 +35,9 @@ fi # architecture detection ARCH="$(uname -m)" +# Created processed that will be cleaned up when the script exits +PIDS="" + #################### # argument parsing # #################### @@ -55,60 +59,71 @@ CURL_BINARY="$(command -v curl)" || { echo "Curl not installed. Aborting."; exit JQ_BINARY="$(command -v jq)" || { echo "Jq not installed. Aborting."; exit 1; } OPTS="ht:n:d:g" -LONGOPTS="help,preset:,nodes:,data-dir:,remote-validators-count:,threshold:,remote-signers:,with-ganache,stop-at-epoch:,disable-htop,disable-vc,enable-logtrace,log-level:,base-port:,base-rest-port:,base-metrics-port:,base-vc-keymanager-port:,base-vc-metrics-port:,base-remote-signer-port:,base-el-net-port:,base-el-http-port:,base-el-ws-port:,base-el-auth-rpc-port:,el-port-offset:,reuse-existing-data-dir,reuse-binaries,timeout:,kill-old-processes,eth2-docker-image:,lighthouse-vc-nodes:,run-geth,dl-geth,dl-eth2,light-clients:,run-nimbus-el,verbose" +LONGOPTS="help,preset:,nodes:,data-dir:,remote-validators-count:,threshold:,nimbus-signer-nodes:,web3signer-nodes:,with-ganache,stop-at-epoch:,disable-htop,disable-vc,enable-payload-builder,enable-logtrace,log-level:,base-port:,base-rest-port:,base-metrics-port:,base-vc-metrics-port:,base-vc-keymanager-port:,base-remote-signer-port:,base-remote-signer-metrics-port:,base-el-net-port:,base-el-rpc-port:,base-el-ws-port:,base-el-auth-rpc-port:,el-port-offset:,reuse-existing-data-dir,reuse-binaries,timeout:,kill-old-processes,eth2-docker-image:,lighthouse-vc-nodes:,run-geth,dl-geth,dl-nimbus-eth1,dl-nimbus-eth2,light-clients:,run-nimbus-eth1,verbose,altair-fork-epoch:,bellatrix-fork-epoch:,capella-fork-epoch:,deneb-fork-epoch:" # default values BINARIES="" -NIMFLAGS="${NIMFLAGS:-""}" NUM_NODES="10" DATA_DIR="local_testnet_data" USE_HTOP="1" USE_VC="1" +USE_PAYLOAD_BUILDER="false" +: ${PAYLOAD_BUILDER_HOST:=127.0.0.1} +: ${PAYLOAD_BUILDER_PORT:=4888} LIGHTHOUSE_VC_NODES="0" -USE_GANACHE="0" LOG_LEVEL="DEBUG; TRACE:networking" BASE_PORT="9000" BASE_REMOTE_SIGNER_PORT="6000" +BASE_REMOTE_SIGNER_METRICS_PORT="6100" BASE_METRICS_PORT="8008" BASE_REST_PORT="7500" BASE_VC_KEYMANAGER_PORT="8500" BASE_VC_METRICS_PORT="9008" BASE_EL_NET_PORT="30303" -BASE_EL_HTTP_PORT="8545" +BASE_EL_RPC_PORT="8545" BASE_EL_WS_PORT="8546" BASE_EL_AUTH_RPC_PORT="8551" EL_PORT_OFFSET="10" -REUSE_EXISTING_DATA_DIR="0" -REUSE_BINARIES="0" -NIMFLAGS="" +: ${REUSE_EXISTING_DATA_DIR:=0} +: ${REUSE_BINARIES:=0} +: ${NIMFLAGS:=""} +: ${MIN_DEPOSIT_SENDING_DELAY:=1} +: ${MAX_DEPOSIT_SENDING_DELAY:=25} ENABLE_LOGTRACE="0" +STOP_AT_EPOCH=9999999 STOP_AT_EPOCH_FLAG="" TIMEOUT_DURATION="0" CONST_PRESET="mainnet" KILL_OLD_PROCESSES="0" ETH2_DOCKER_IMAGE="" -REMOTE_SIGNER_NODES=0 +NIMBUS_SIGNER_NODES=0 REMOTE_SIGNER_THRESHOLD=1 REMOTE_VALIDATORS_COUNT=0 LC_NODES=1 ACCOUNT_PASSWORD="nimbus" RUN_GETH="0" DL_GETH="0" -DL_ETH2="0" -BEACON_NODE_COMMAND="./build/nimbus_beacon_node" -WEB3_ARG=() -CLEANUP_DIRS=() +: ${DL_NIMBUS_ETH1:="0"} +: ${DL_NIMBUS_ETH2:="0"} +# TODO: Add command-line flags for these +: ${NIMBUS_ETH2_VERSION:=22.12.0} +: ${NIMBUS_ETH2_REVISION:=f6a5a5b1} + +: ${BEACON_NODE_COMMAND:="./build/nimbus_beacon_node"} +: ${CAPELLA_FORK_EPOCH:=40} +: ${DENEB_FORK_EPOCH:=50} #NIMBUS EL VARS -RUN_NIMBUS="0" -NIMBUSEL_BINARY="${NIMBUSEL_BINARY:-../nimbus-eth1/build/nimbus}" -echo "${NIMBUSEL_BINARY}" - -EL_HTTP_PORTS=() -EL_RPC_PORTS=() -EL_DATA_DIRS=() +RUN_NIMBUS_ETH1="0" +: ${NIMBUS_ETH1_BINARY:="./build/downloads/nimbus"} +: ${WEB3SIGNER_VERSION:=22.11.0} +: ${WEB3SIGNER_DIR:="${BUILD_DIR}/downloads/web3signer-${WEB3SIGNER_VERSION}"} +: ${WEB3SIGNER_BINARY:="${WEB3SIGNER_DIR}/bin/web3signer"} +WEB3SIGNER_NODES=0 PROCS_TO_KILL=("nimbus_beacon_node" "nimbus_validator_client" "nimbus_signing_node" "nimbus_light_client") PORTS_TO_KILL=() +WEB3_ARG=() +CLEANUP_DIRS=() print_help() { cat < "${DATA_DIR}/keymanager-token" +JWT_FILE="${DATA_DIR}/jwtsecret" +echo "Generating JWT file '$JWT_FILE'..." +openssl rand -hex 32 | tr -d "\n" > "${JWT_FILE}" + +if [[ "$CONST_PRESET" == "minimal" ]]; then + SECONDS_PER_SLOT=6 + SLOTS_PER_EPOCH=8 +else + SECONDS_PER_SLOT=12 + SLOTS_PER_EPOCH=32 +fi + VALIDATORS_DIR="${DATA_DIR}/validators" scripts/makedir.sh "${VALIDATORS_DIR}" @@ -378,21 +428,6 @@ else NPROC="$(nproc)" fi -if [[ "${RUN_NIMBUS}" == "1" && "${RUN_GETH}" == "1" ]]; then - echo "Use only one EL - geth or nimbus" - exit 1 -fi - - -if [[ "${RUN_GETH}" == "1" ]]; then - . ./scripts/geth_vars.sh -fi - -if [[ "${RUN_NIMBUS}" == "1" ]]; then - . ./scripts/nimbus_el_vars.sh -fi - - # Kill all processes which have open ports in the array passed as parameter kill_by_port() { local ports=("$@") @@ -411,41 +446,84 @@ kill_by_port() { } GETH_NUM_NODES="$(( NUM_NODES + LC_NODES ))" -NIMBUSEL_NUM_NODES="$(( NUM_NODES + LC_NODES ))" +NIMBUS_ETH1_NUM_NODES="$(( NUM_NODES + LC_NODES ))" +REMOTE_SIGNER_NODES=$(( NIMBUS_SIGNER_NODES + WEB3SIGNER_NODES )) +LAST_REMOTE_SIGNER_NODE_IDX=$(( REMOTE_SIGNER_NODES - 1 )) + +if [[ "${RUN_GETH}" == "1" ]]; then + source "${SCRIPTS_DIR}/geth_binaries.sh" + + if [[ $DENEB_FORK_EPOCH -lt $STOP_AT_EPOCH ]]; then + download_geth_eip_4844 + GETH_BINARY="$GETH_EIP_4844_BINARY" + elif [[ $CAPELLA_FORK_EPOCH -lt $STOP_AT_EPOCH ]]; then + download_geth_capella + GETH_BINARY="$GETH_CAPELLA_BINARY" + else + # TODO We should run Geth stable here, but it lacks an ARM build for macOS, + # so we will run our own Geth capella build until we add some Geth stable + # binaries to our nimbus-simulation-binaries project: + download_geth_capella + GETH_BINARY="$GETH_CAPELLA_BINARY" + fi + + source ./scripts/geth_vars.sh +fi + +if [[ "${RUN_NIMBUS_ETH1}" == "1" ]]; then + . ./scripts/nimbus_el_vars.sh +fi # kill lingering processes from a previous run if [[ "${OS}" != "windows" ]]; then which lsof &>/dev/null || \ { echo "'lsof' not installed and we need it to check for ports already in use. Aborting."; exit 1; } - #Stop geth nodes + # Stop geth nodes if [[ "${RUN_GETH}" == "1" ]]; then - for NUM_NODE in $(seq 0 $(( GETH_NUM_NODES - 1 ))); do - for PORT in $(( NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_NET_PORT )) \ - $(( NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_HTTP_PORT )) \ - $(( NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_WS_PORT )) \ - $(( NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_AUTH_RPC_PORT )); + for GETH_NODE_IDX in $(seq 0 $GETH_LAST_NODE_IDX); do + for PORT in ${GETH_NET_PORTS[GETH_NODE_IDX]} \ + ${GETH_RPC_PORTS[GETH_NODE_IDX]} \ + ${GETH_AUTH_RPC_PORTS[GETH_NODE_IDX]}; do PORTS_TO_KILL+=("${PORT}") done done fi - #Stop Nimbus EL nodes - if [[ "${RUN_NIMBUS}" == "1" ]]; then - for NUM_NODE in $(seq 0 $(( NIMBUSEL_NUM_NODES - 1 ))); do - for PORT in $(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_NET_PORT )) \ - $(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_HTTP_PORT )) \ - $(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_WS_PORT )) \ - $(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_AUTH_RPC_PORT )); + # Stop Nimbus EL nodes + if [[ "${RUN_NIMBUS_ETH1}" == "1" ]]; then + for NIMBUS_ETH1_NODE_IDX in $(seq 0 $NIMBUS_ETH1_LAST_NODE_IDX); do + for PORT in ${NIMBUS_ETH1_NET_PORTS[GETH_NODE_IDX]} \ + ${NIMBUS_ETH1_RPC_PORTS[GETH_NODE_IDX]} \ + ${NIMBUS_ETH1_AUTH_RPC_PORTS[GETH_NODE_IDX]}; do PORTS_TO_KILL+=("${PORT}") done done fi - for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do - for PORT in $(( BASE_PORT + NUM_NODE )) $(( BASE_METRICS_PORT + NUM_NODE )) $(( BASE_REST_PORT + NUM_NODE )); do + # Stop Remote Signers + for NUM_REMOTE in $(seq 0 $LAST_REMOTE_SIGNER_NODE_IDX); do + for PORT in $(( BASE_REMOTE_SIGNER_PORT + NUM_REMOTE )) \ + $(( BASE_REMOTE_SIGNER_METRICS_PORT + NUM_REMOTE )) ; do + PORTS_TO_KILL+=("${PORT}") + done + done + + # Stop Nimbus validator clients + if [[ "${USE_VC}" == "1" ]]; then + for NUM_NODE in $(seq 1 $NUM_NODES); do + for PORT in $(( BASE_VC_METRICS_PORT + NUM_NODE - 1 )) \ + $(( BASE_VC_KEYMANAGER_PORT + NUM_NODE - 1 )); do + PORTS_TO_KILL+=("${PORT}") + done + done + fi + + # Stop Nimbus CL nodes + for NUM_NODE in $(seq 1 $NUM_NODES); do + for PORT in $(( BASE_PORT + NUM_NODE - 1 )) $(( BASE_METRICS_PORT + NUM_NODE - 1)) $(( BASE_REST_PORT + NUM_NODE - 1)); do PORTS_TO_KILL+=("${PORT}") done done @@ -453,105 +531,106 @@ if [[ "${OS}" != "windows" ]]; then kill_by_port "${PORTS_TO_KILL[@]}" fi +download_web3signer() { + if [[ ! -d "${WEB3SIGNER_DIR}" ]]; then + log "Downloading Web3Signer binary" -download_geth() { - GETH_VERSION="1.10.26-e5eb32ac" + WEB3SIGNER_TARBALL="web3signer-${WEB3SIGNER_VERSION}.tar.gz" + WEB3SIGNER_URL="https://artifacts.consensys.net/public/web3signer/raw/names/web3signer.tar.gz/versions/${WEB3SIGNER_VERSION}/${WEB3SIGNER_TARBALL}" -# https://geth.ethereum.org/downloads/ -# "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.26-e5eb32ac.tar.gz" -# "https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-1.10.26-e5eb32ac.tar.gz" -# "https://gethstore.blob.core.windows.net/builds/geth-windows-amd64-1.10.26-e5eb32ac.zip" - - GETH_URL="https://gethstore.blob.core.windows.net/builds/" - - case "${OS}" in - linux) - GETH_TARBALL="geth-linux-amd64-${GETH_VERSION}.tar.gz" - ;; - macos) - GETH_TARBALL="geth-darwin-amd64-${GETH_VERSION}.tar.gz" - ;; - windows) - GETH_TARBALL="geth-windows-amd64-${GETH_VERSION}.zip" - ;; - esac - - if [[ ! -e "build/${GETH_BINARY}" ]]; then - log "Downloading Geth binary" - mkdir -p "build" - pushd "build" >/dev/null - "${CURL_BINARY}" -sSLO "${GETH_URL}/${GETH_TARBALL}" - local tmp_extract_dir - tmp_extract_dir=$(mktemp -d geth-extract-XXX) - CLEANUP_DIRS+=("${tmp_extract_dir}") - tar -xzf "${GETH_TARBALL}" --directory "${tmp_extract_dir}" --strip-components=1 - mv "${tmp_extract_dir}/geth" . - GETH_BINARY="${PWD}/geth" - popd >/dev/null + mkdir -p "${WEB3SIGNER_DIR}" + "${CURL_BINARY}" -sSL "${WEB3SIGNER_URL}" \ + | tar -xzf - --directory "${WEB3SIGNER_DIR}" --strip-components=1 fi } -download_eth2() { +download_nimbus_eth1() { + if [[ ! -e "${NIMBUS_ETH1_BINARY}" ]]; then + case "${OS}-${ARCH}" in + linux-amd64|linux-x86_64) + NIMBUS_ETH1_PLATFORM=Linux_amd64 + ;; + linux-arm|linux-arm32|linux-aarch32) + NIMBUS_PLATFORM=Linux_arm32v7 + ;; + linux-arm64|linux-aarch64) + NIMBUS_ETH1_PLATFORM=Linux_arm64v8 + ;; + macos-amd64|macos-x86_64) + NIMBUS_ETH1_PLATFORM=macOS_arm64 + ;; + macos-arm64|macos-aarch64) + NIMBUS_ETH1_PLATFORM=macOS_amd64 + ;; + windows-amd64|windows-x86_64) + NIMBUS_ETH1_PLATFORM=Windows_amd64 + ;; + *) + echo "No nimbus-eth1 binaries available for ${OS}-${ARCH}" + exit 1 + ;; + esac - # https://github.com/status-im/nimbus-eth2/releases/download/nightly/nimbus-eth2_Linux_amd64_nightly_latest.tar.gz + NIMBUS_ETH1_FULL_BINARY_VERSION=20221205_f4cacdfc + NIMBUS_ETH1_TARBALL_NAME="nimbus-eth1_${NIMBUS_ETH1_PLATFORM}_${NIMBUS_ETH1_FULL_BINARY_VERSION}.tar.gz" - ETH2_URL="https://github.com/status-im/nimbus-eth2/releases/download/nightly/" - ETH2_VERSION="nightly_latest" - case "${OS}" in - linux) - ETH2_TARBALL="nimbus-eth2_Linux_amd64_${ETH2_VERSION}.tar.gz" - ;; - macos) - ETH2_TARBALL="nimbus-eth2_macOS_amd64_${ETH2_VERSION}.tar.gz" - ;; - windows) - ETH2_TARBALL="nimbus-eth2_Windows_amd64_${ETH2_VERSION}.tar.gz" - ;; - esac + NIMBUS_ETH1_TARBALL_URL="https://github.com/status-im/nimbus-simulation-binaries/raw/master/nimbus-eth1/nightly-20221205/${NIMBUS_ETH1_TARBALL_NAME}" + log "Downloading Nimbus ETH1 binary" + + "${CURL_BINARY}" -o "$NIMBUS_ETH1_TARBALL_NAME" -sSLO "$NIMBUS_ETH1_TARBALL_URL" + local tmp_extract_dir + tmp_extract_dir=$(mktemp -d nimbus-eth1-tarball-XXX) + CLEANUP_DIRS+=("$tmp_extract_dir") + tar -xzf "${NIMBUS_ETH1_TARBALL_NAME}" -C "$tmp_extract_dir" --strip-components=1 + mkdir -p "$(dirname "$NIMBUS_ETH1_BINARY")" + mv "$tmp_extract_dir/build/nimbus" "$NIMBUS_ETH1_BINARY" + chmod +x "$NIMBUS_ETH1_BINARY" + fi +} + +download_nimbus_eth2() { if [[ ! -e "${BEACON_NODE_COMMAND}" ]]; then + case "${OS}-${ARCH}" in + linux-amd64|linux-x86_64) + NIMBUS_PLATFORM=Linux_amd64 + ;; + linux-arm|linux-arm32|linux-aarch32) + NIMBUS_PLATFORM=Linux_arm32v7 + ;; + linux-arm64|linux-aarch64) + NIMBUS_PLATFORM=Linux_arm64v8 + ;; + macos-amd64|macos-x86_64) + NIMBUS_PLATFORM=macOS_amd64 + ;; + macos-arm64|macos-aarch64) + NIMBUS_PLATFORM=macOS_arm64 + ;; + windows-amd64|windows-x86_64) + NIMBUS_PLATFORM=Windows_amd64 + ;; + esac + + NIMBUS_ETH2_FULL_BINARY_VERSION="${NIMBUS_ETH2_VERSION}_${NIMBUS_ETH2_REVISION}" + NIMBUS_ETH2_TARBALL_NAME="nimbus-eth2_${NIMBUS_PLATFORM}_${NIMBUS_ETH2_FULL_BINARY_VERSION}.tar.gz" + NIMBUS_ETH2_TARBALL_URL="https://github.com/status-im/nimbus-eth2/releases/download/v${NIMBUS_ETH2_VERSION}/${NIMBUS_ETH2_TARBALL_NAME}" + log "Downloading Nimbus ETH2 binary" - "${CURL_BINARY}" -sSLO "${ETH2_URL}/${ETH2_TARBALL}" - # will extract it in build/ directory - tar -xzf "${ETH2_TARBALL}" --strip-components=1 + + "${CURL_BINARY}" -o "$NIMBUS_ETH2_TARBALL_NAME" -sSL "$NIMBUS_ETH2_TARBALL_URL" + local tmp_extract_dir + tmp_extract_dir=$(mktemp -d nimbus-eth2-tarball-XXX) + CLEANUP_DIRS+=("$tmp_extract_dir") + tar -xzf "${NIMBUS_ETH2_TARBALL_NAME}" -C "$tmp_extract_dir" --strip-components=1 + mkdir -p "$(dirname "$BEACON_NODE_COMMAND")" + mv "$tmp_extract_dir/build/nimbus_beacon_node" "$BEACON_NODE_COMMAND" + chmod +x "$BEACON_NODE_COMMAND" + REUSE_BINARIES=1 fi } -if [[ "${RUN_GETH}" == "1" ]]; then - if [[ ! -e "${GETH_BINARY}" ]]; then - if [[ "${DL_GETH}" == "1" ]]; then - log "Downloading geth ..." - download_geth - else - echo "Missing geth executable" - exit 1 - fi - fi - - log "Starting ${GETH_NUM_NODES} Geth Nodes ..." - . "./scripts/start_geth_nodes.sh" - EL_HTTP_PORTS+=("${GETH_HTTP_PORTS[@]}") - EL_RPC_PORTS+=("${GETH_RPC_PORTS[@]}") - EL_DATA_DIRS+=("${GETH_DATA_DIRS[@]}") - PROCS_TO_KILL+=("${GETH_BINARY}") - CLEANUP_DIRS+=("${GETH_DATA_DIRS[@]}") -fi - -if [[ "${RUN_NIMBUS}" == "1" ]]; then - if [[ ! -e "${NIMBUSEL_BINARY}" ]]; then - echo "Missing nimbus EL executable" - exit 1 - fi - - . "./scripts/start_nimbus_el_nodes.sh" - EL_HTTP_PORTS+=("${NIMBUSEL_HTTP_PORTS[@]}") - EL_RPC_PORTS+=("${NIMBUSEL_RPC_PORTS[@]}") - EL_DATA_DIRS+=("${NIMBUSEL_DATA_DIRS[@]}") - PROCS_TO_KILL+=("${NIMBUSEL_BINARY}") - CLEANUP_DIRS+=("${NIMBUSEL_DATA_DIRS[@]}") -fi - # Download the Lighthouse binary. LH_VERSION="2.1.3" LH_ARCH="${ARCH}" @@ -585,11 +664,11 @@ fi # Don't build binaries if we are downloading them -if [[ "${DL_ETH2}" != "1" ]]; then +if [[ "${DL_NIMBUS_ETH2}" != "1" ]]; then # Build the binaries - BINARIES="deposit_contract" + BINARIES="ncli_testnet" - if [ "$REMOTE_SIGNER_NODES" -ge "0" ]; then + if [[ "$NIMBUS_SIGNER_NODES" -gt "0" ]]; then BINARIES="${BINARIES} nimbus_signing_node" fi @@ -597,17 +676,22 @@ if [[ "${DL_ETH2}" != "1" ]]; then BINARIES="${BINARIES} nimbus_validator_client" fi - if [ "$LC_NODES" -ge "1" ]; then + if [[ "$LC_NODES" -ge "1" ]]; then BINARIES="${BINARIES} nimbus_light_client" fi - if [[ "$ENABLE_LOGTRACE" == "1" ]]; then - BINARIES="${BINARIES} logtrace" - fi - BINARIES="${BINARIES} nimbus_beacon_node" fi +if [[ "$WEB3SIGNER_NODES" -gt "0" ]]; then + download_web3signer +fi + +if [[ "$WEB3SIGNER_NODES" -gt "0" && "$NIMBUS_SIGNER_NODES" -gt "0" ]]; then + echo "You can use either --web3signer-nodes or --nimbus-signer-nodes, but not together" + exit 1 +fi + if [[ -n "${ETH2_DOCKER_IMAGE}" ]]; then DATA_DIR_FULL_PATH="$(cd "${DATA_DIR}"; pwd)" # CONTAINER_DATA_DIR must be used everywhere where paths are supplied to BEACON_NODE_COMMAND executions. @@ -617,9 +701,8 @@ if [[ -n "${ETH2_DOCKER_IMAGE}" ]]; then else # When docker is not used CONTAINER_DATA_DIR is just an alias for DATA_DIR CONTAINER_DATA_DIR="${DATA_DIR}" - if [[ "${DL_ETH2}" == "1" ]]; then - log "Downloading nimbus_eth2" - download_eth2 + if [[ "${DL_NIMBUS_ETH2}" == "1" ]]; then + download_nimbus_eth2 BINARIES="" fi fi @@ -634,28 +717,47 @@ for BINARY in ${BINARIES}; do done if [[ "${REUSE_BINARIES}" == "0" || "${BINARIES_MISSING}" == "1" ]]; then - if [[ "${DL_ETH2}" == "0" ]]; then + if [[ "${DL_NIMBUS_ETH2}" == "0" ]]; then log "Rebuilding binaries ${BINARIES}" ${MAKE} -j ${NPROC} LOG_LEVEL=TRACE NIMFLAGS="${NIMFLAGS} -d:local_testnet -d:const_preset=${CONST_PRESET}" ${BINARIES} fi fi +if [[ "${RUN_NIMBUS_ETH1}" == "1" ]]; then + if [[ "${DL_NIMBUS_ETH1}" == "1" ]]; then + download_nimbus_eth1 + fi +fi + # Kill child processes on Ctrl-C/SIGTERM/exit, passing the PID of this shell # instance as the parent and the target process name as a pattern to the # "pkill" command. cleanup() { - log "Cleaning up" + echo "Cleaning up" + + # Avoid the trap enterring an infinite loop + trap - SIGINT SIGTERM EXIT for proc in "${PROCS_TO_KILL[@]}" do - pkill -f -P $$ "${proc}" || true + # TODO Previously, the code here used the '-P $$' option to limit + # the kill command only to children of this shell process. + # Unfortunately, this doesn't seem to work at the moment. + # Perhaps the child processes are not direct children of + # the current shell process? + pkill -f "$(basename "$proc")" || true done sleep 2 for proc in "${PROCS_TO_KILL[@]}" do - pkill -SIGKILL -f -P $$ "${proc}" || true + # TODO Previously, the code here used the '-P $$' option to limit + # the kill command only to children of this shell process. + # Unfortunately, this doesn't seem to work at the moment. + # Perhaps the child processes are not direct children of + # the current shell process? + pkill -SIGKILL -f "$(basename "$proc")" || true done # Delete all binaries we just built, because these are unusable outside this @@ -695,24 +797,22 @@ fi REMOTE_URLS="" -for NUM_REMOTE in $(seq 0 $(( REMOTE_SIGNER_NODES - 1 ))); do +for NUM_REMOTE in $(seq 0 $LAST_REMOTE_SIGNER_NODE_IDX); do REMOTE_PORT=$(( BASE_REMOTE_SIGNER_PORT + NUM_REMOTE )) REMOTE_URLS="${REMOTE_URLS} --remote-signer=http://127.0.0.1:${REMOTE_PORT}" done # deposit and testnet creation -PIDS="" BOOTSTRAP_TIMEOUT=30 # in seconds -DEPOSIT_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000" -DEPOSIT_CONTRACT_BLOCK="0x0000000000000000000000000000000000000000000000000000000000000000" RUNTIME_CONFIG_FILE="${DATA_DIR}/config.yaml" NUM_JOBS=${NUM_NODES} DEPOSITS_FILE="${DATA_DIR}/deposits.json" CONTAINER_DEPOSITS_FILE="${CONTAINER_DATA_DIR}/deposits.json" +CONTAINER_DEPOSIT_TREE_SNAPSHOT_FILE="${CONTAINER_DATA_DIR}/deposit_tree_snapshot.ssz" if [[ "$REUSE_EXISTING_DATA_DIR" == "0" ]]; then - ./build/deposit_contract generateSimulationDeposits \ + ./build/ncli_testnet generateDeposits \ --count=${TOTAL_VALIDATORS} \ --out-validators-dir="${VALIDATORS_DIR}" \ --out-secrets-dir="${SECRETS_DIR}" \ @@ -722,95 +822,116 @@ if [[ "$REUSE_EXISTING_DATA_DIR" == "0" ]]; then ${REMOTE_URLS} fi -if [[ $USE_GANACHE == "0" ]]; then - GENESIS_OFFSET=30 - BOOTSTRAP_IP="127.0.0.1" +GENESIS_OFFSET=40 +NOW_UNIX_TIMESTAMP=$(date +%s) +GENESIS_TIME=$((NOW_UNIX_TIMESTAMP + GENESIS_OFFSET)) +SHANGHAI_FORK_TIME=$((GENESIS_TIME + SECONDS_PER_SLOT * SLOTS_PER_EPOCH * CAPELLA_FORK_EPOCH)) +SHARDING_FORK_TIME=$((GENESIS_TIME + SECONDS_PER_SLOT * SLOTS_PER_EPOCH * DENEB_FORK_EPOCH)) - $BEACON_NODE_COMMAND createTestnet \ - --data-dir="${CONTAINER_DATA_DIR}" \ - --deposits-file="${CONTAINER_DEPOSITS_FILE}" \ - --total-validators=${TOTAL_VALIDATORS} \ - --output-genesis="${CONTAINER_DATA_DIR}/genesis.ssz" \ - --output-bootstrap-file="${CONTAINER_DATA_DIR}/bootstrap_nodes.txt" \ - --bootstrap-address=${BOOTSTRAP_IP} \ - --bootstrap-port=${BASE_PORT} \ - --netkey-file=network_key.json \ - --insecure-netkey-password=true \ - --genesis-offset=${GENESIS_OFFSET} # Delay in seconds +EXECUTION_GENESIS_JSON="${DATA_DIR}/execution_genesis.json" +EXECUTION_GENESIS_BLOCK_JSON="${DATA_DIR}/execution_genesis_block.json" -else - echo "Launching ganache" - ganache-cli --blockTime 17 --gasLimit 100000000 -e 100000 --verbose > "${DATA_DIR}/log_ganache.txt" 2>&1 & - PIDS="${PIDS},$!" +# TODO The storage state of the deposit contract that is baked into the execution genesis state +# currently hard-codes some merkle branches that won't match the random deposits generated +# by this simulation. This doesn't happen to produce problems only by accident. If we enable +# the `deposit_root` safety-checks in the deposit downloader, it will detect the discrepancy. +sed "s/SHANGHAI_FORK_TIME/${SHANGHAI_FORK_TIME}/g; s/SHARDING_FORK_TIME/${SHARDING_FORK_TIME}/g" \ + "${SCRIPTS_DIR}/execution_genesis.json.template" > "$EXECUTION_GENESIS_JSON" - WEB3_ARG=("--web3-url=ws://localhost:8545") +DEPOSIT_CONTRACT_ADDRESS="0x4242424242424242424242424242424242424242" +DEPOSIT_CONTRACT_BLOCK=0 - echo "Deploying deposit contract" - DEPLOY_CMD_OUTPUT=$(./build/deposit_contract 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]} - DEPOSIT_CONTRACT_BLOCK=${OUTPUT_PIECES[1]} +get_execution_genesis_block() { + ${CURL_BINARY} -s -X POST \ + -H 'Content-Type: application/json' \ + --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", true],"id":1}' \ + $1 | jq '.result' +} - echo Contract deployed at "$DEPOSIT_CONTRACT_ADDRESS":"$DEPOSIT_CONTRACT_BLOCK" +if [[ "${RUN_GETH}" == "1" ]]; then + if [[ ! -e "${GETH_BINARY}" ]]; then + echo "Missing geth executable" + exit 1 + fi - MIN_DELAY=1 - MAX_DELAY=5 + source "./scripts/start_geth_nodes.sh" - BOOTSTRAP_TIMEOUT=$(( MAX_DELAY * TOTAL_VALIDATORS )) - - ./build/deposit_contract sendDeposits \ - --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 & - - PIDS="${PIDS},$!" + PROCS_TO_KILL+=("${GETH_BINARY}") + CLEANUP_DIRS+=("${GETH_DATA_DIRS[@]}") + MAIN_WEB3_URL="http://127.0.0.1:${GETH_RPC_PORTS[0]}" + get_execution_genesis_block "${MAIN_WEB3_URL}" > "$EXECUTION_GENESIS_BLOCK_JSON" fi +if [[ "${RUN_NIMBUS_ETH1}" == "1" ]]; then + if [[ ! -e "${NIMBUS_ETH1_BINARY}" ]]; then + echo "Missing nimbus EL executable" + exit 1 + fi + + source "./scripts/start_nimbus_el_nodes.sh" + + PROCS_TO_KILL+=("${NIMBUS_ETH1_BINARY}") + CLEANUP_DIRS+=("${NIMBUS_ETH1_DATA_DIRS[@]}") + + MAIN_WEB3_URL="http://127.0.0.1:${NIMBUS_ETH1_RPC_PORTS[0]}" + get_execution_genesis_block "$MAIN_WEB3_URL" > "$EXECUTION_GENESIS_BLOCK_JSON.nimbus" + if [ -f "$EXECUTION_GENESIS_BLOCK_JSON" ]; then + if ! cmp <(jq --compact-output --sort-keys . "$EXECUTION_GENESIS_BLOCK_JSON") <(jq --compact-output --sort-keys . "$EXECUTION_GENESIS_BLOCK_JSON.nimbus"); then + echo "Nimbus and Geth disagree regarding the genesis execution block" + exit 1 + fi + else + mv "$EXECUTION_GENESIS_BLOCK_JSON.nimbus" "$EXECUTION_GENESIS_BLOCK_JSON" + fi +fi + +jq -r '.hash' "$EXECUTION_GENESIS_BLOCK_JSON" > "${DATA_DIR}/deposit_contract_block_hash.txt" + +./build/ncli_testnet createTestnet \ + --data-dir="$CONTAINER_DATA_DIR" \ + --deposits-file="$CONTAINER_DEPOSITS_FILE" \ + --total-validators=$TOTAL_VALIDATORS \ + --output-genesis="$CONTAINER_DATA_DIR/genesis.ssz" \ + --output-bootstrap-file="$CONTAINER_DATA_DIR/bootstrap_nodes.txt" \ + --output-deposit-tree-snapshot="$CONTAINER_DEPOSIT_TREE_SNAPSHOT_FILE" \ + --bootstrap-address=127.0.0.1 \ + --bootstrap-port=$BASE_PORT \ + --netkey-file=network_key.json \ + --insecure-netkey-password=true \ + --genesis-time=$GENESIS_TIME \ + --capella-fork-epoch=$CAPELLA_FORK_EPOCH \ + --deneb-fork-epoch=$DENEB_FORK_EPOCH \ + --execution-genesis-block="$EXECUTION_GENESIS_BLOCK_JSON" + ./scripts/make_prometheus_config.sh \ --nodes ${NUM_NODES} \ --base-metrics-port ${BASE_METRICS_PORT} \ --config-file "${DATA_DIR}/prometheus.yml" || true # TODO: this currently fails on macOS, # but it can be considered non-critical -echo Wrote $RUNTIME_CONFIG_FILE: +cp "$SCRIPTS_DIR/$CONST_PRESET-non-overriden-config.yaml" "$RUNTIME_CONFIG_FILE" # TODO the runtime config file should be used during deposit generation as well! -tee "$RUNTIME_CONFIG_FILE" < "${DATA_DIR}/deposit_contract_block.txt" + if [[ "${LIGHTHOUSE_VC_NODES}" != "0" ]]; then # I don't know what this is, but Lighthouse wants it, so we recreate it from # Lighthouse's own local testnet. - echo 0 > "${DATA_DIR}/deploy_block.txt" - - # Lighthouse wants all these variables here. Copying them from "beacon_chain/spec/presets.nim". - # Note: our parser can't handle quotes around numerical values. - cat >> "$RUNTIME_CONFIG_FILE" < "${DATA_DIR}/deploy_block.txt" fi dump_logs() { @@ -824,12 +945,12 @@ dump_logs() { dump_logtrace() { if [[ "$ENABLE_LOGTRACE" == "1" ]]; then - find "${DATA_DIR}" -maxdepth 1 -type f -regex '.*/log[0-9]+.txt' | sed -e"s/${DATA_DIR}\//--nodes=/" | sort | xargs ./build/logtrace localSimChecks --log-dir="${DATA_DIR}" --const-preset=${CONST_PRESET} || true + find "${DATA_DIR}" -maxdepth 1 -type f -regex '.*/log[0-9]+.txt' | sed -e"s/${DATA_DIR}\//--nodes=/" | sort | xargs ./build/ncli_testnet analyzeLogs --log-dir="${DATA_DIR}" --const-preset=${CONST_PRESET} || true fi } -NODES_WITH_VALIDATORS=${NODES_WITH_VALIDATORS:-4} -BOOTSTRAP_NODE=0 +NODES_WITH_VALIDATORS=${NODES_WITH_VALIDATORS:-$NUM_NODES} +BOOTSTRAP_NODE=1 SYSTEM_VALIDATORS=$(( TOTAL_VALIDATORS - USER_VALIDATORS )) VALIDATORS_PER_NODE=$(( SYSTEM_VALIDATORS / NODES_WITH_VALIDATORS )) if [[ "${USE_VC}" == "1" ]]; then @@ -840,31 +961,41 @@ if [[ "${USE_VC}" == "1" ]]; then NUM_JOBS=$(( NUM_JOBS * 2 )) fi -if [ "$REMOTE_SIGNER_NODES" -ge "0" ]; then +if [[ "$REMOTE_SIGNER_NODES" -ge "0" ]]; then NUM_JOBS=$(( NUM_JOBS + REMOTE_SIGNER_NODES )) fi -if [ "$LC_NODES" -ge "1" ]; then +if [[ "$LC_NODES" -ge "1" ]]; then NUM_JOBS=$(( NUM_JOBS + LC_NODES )) fi -if [ "${RUN_GETH}" == "1" ]; then +if [[ "${RUN_GETH}" == "1" ]]; then NUM_JOBS=$(( NUM_JOBS + GETH_NUM_NODES )) fi -if [ "${RUN_NIMBUS}" == "1" ]; then - NUM_JOBS=$(( NUM_JOBS + NIMBUSEL_NUM_NODES )) +if [[ "${RUN_NIMBUS_ETH1}" == "1" ]]; then + NUM_JOBS=$(( NUM_JOBS + NIMBUS_ETH1_NUM_NODES )) fi VALIDATORS_PER_VALIDATOR=$(( (SYSTEM_VALIDATORS / NODES_WITH_VALIDATORS) / 2 )) -VALIDATOR_OFFSET=$((SYSTEM_VALIDATORS / 2)) +VALIDATOR_OFFSET=$(( SYSTEM_VALIDATORS / 2 )) BOOTSTRAP_ENR="${DATA_DIR}/node${BOOTSTRAP_NODE}/beacon_node.enr" CONTAINER_BOOTSTRAP_ENR="${CONTAINER_DATA_DIR}/node${BOOTSTRAP_NODE}/beacon_node.enr" CONTAINER_NETWORK_KEYFILE="network_key.json" -for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do +# TODO The deposit generator tool needs to gain support for generating two sets +# of deposits (genesis + submitted ones). Then we can enable the sending of +# deposits here. +# +#./build/ncli_testnet sendDeposits \ +# --deposits-file="$DEPOSITS_FILE" \ +# --min-delay=$MIN_DEPOSIT_SENDING_DELAY --max-delay=$MAX_DEPOSIT_SENDING_DELAY \ +# --web3-url="$MAIN_WEB3_URL" \ +# --deposit-contract=$DEPOSIT_CONTRACT_ADDRESS > "$DATA_DIR/log_deposit_maker.txt" 2>&1 & + +for NUM_NODE in $(seq 1 $NUM_NODES); do # Copy validators to individual nodes. # The first $NODES_WITH_VALIDATORS nodes split them equally between them, # after skipping the first $USER_VALIDATORS. @@ -874,27 +1005,25 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do scripts/makedir.sh "${NODE_DATA_DIR}/validators" 2>&1 scripts/makedir.sh "${NODE_DATA_DIR}/secrets" 2>&1 - if [[ $NUM_NODE -lt $NODES_WITH_VALIDATORS ]]; then + if [[ $NUM_NODE -le $NODES_WITH_VALIDATORS ]]; then if [[ "${USE_VC}" == "1" ]]; then VALIDATOR_DATA_DIR="${DATA_DIR}/validator${NUM_NODE}" rm -rf "${VALIDATOR_DATA_DIR}" scripts/makedir.sh "${VALIDATOR_DATA_DIR}" 2>&1 scripts/makedir.sh "${VALIDATOR_DATA_DIR}/validators" 2>&1 scripts/makedir.sh "${VALIDATOR_DATA_DIR}/secrets" 2>&1 - for VALIDATOR in $(ls "${VALIDATORS_DIR}" | tail -n +$(( $USER_VALIDATORS + ($VALIDATORS_PER_VALIDATOR * $NUM_NODE) + 1 + $VALIDATOR_OFFSET )) | head -n $VALIDATORS_PER_VALIDATOR); do - if [[ -f "${VALIDATORS_DIR}/${VALIDATOR}/keystore.json" ]]; then - cp -a "${VALIDATORS_DIR}/${VALIDATOR}" "${VALIDATOR_DATA_DIR}/validators/" 2>&1 + for VALIDATOR in $(ls "${VALIDATORS_DIR}" | tail -n +$(( USER_VALIDATORS + (VALIDATORS_PER_VALIDATOR * (NUM_NODE - 1)) + 1 + VALIDATOR_OFFSET )) | head -n $VALIDATORS_PER_VALIDATOR); do + cp -a "${VALIDATORS_DIR}/${VALIDATOR}" "${VALIDATOR_DATA_DIR}/validators/" 2>&1 + # Remote validators won't have a secret file + if [ -f "${SECRETS_DIR}/${VALIDATOR}" ]; then cp -a "${SECRETS_DIR}/${VALIDATOR}" "${VALIDATOR_DATA_DIR}/secrets/" 2>&1 - else - # TODO: validators support remote signers - cp -a "${VALIDATORS_DIR}/${VALIDATOR}" "${NODE_DATA_DIR}/validators/" 2>&1 fi done if [[ "${OS}" == "Windows_NT" ]]; then find "${VALIDATOR_DATA_DIR}" -type f \( -iname "*.json" -o ! -iname "*.*" \) -exec icacls "{}" /inheritance:r /grant:r ${USERDOMAIN}\\${USERNAME}:\(F\) \; fi fi - for VALIDATOR in $(ls "${VALIDATORS_DIR}" | tail -n +$(( $USER_VALIDATORS + ($VALIDATORS_PER_NODE * $NUM_NODE) + 1 )) | head -n $VALIDATORS_PER_NODE); do + for VALIDATOR in $(ls "${VALIDATORS_DIR}" | tail -n +$(( USER_VALIDATORS + (VALIDATORS_PER_NODE * (NUM_NODE - 1)) + 1 )) | head -n $VALIDATORS_PER_NODE); do cp -a "${VALIDATORS_DIR}/${VALIDATOR}" "${NODE_DATA_DIR}/validators/" 2>&1 if [[ -f "${VALIDATORS_DIR}/${VALIDATOR}/keystore.json" ]]; then # Only remote key stores doesn't have a secret @@ -906,7 +1035,8 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do fi fi done -for NUM_LC in $(seq 0 $(( LC_NODES - 1 ))); do + +for NUM_LC in $(seq 1 $LC_NODES); do LC_DATA_DIR="${DATA_DIR}/lc${NUM_LC}" rm -rf "${LC_DATA_DIR}" scripts/makedir.sh "${LC_DATA_DIR}" 2>&1 @@ -928,22 +1058,79 @@ END_CLI_CONFIG # https://ss64.com/osx/seq.html documents that at macOS seq(1) counts backwards # as probably do some others -if ((REMOTE_SIGNER_NODES > 0)); then - for NUM_REMOTE in $(seq 0 $(( REMOTE_SIGNER_NODES - 1 ))); do +if ((NIMBUS_SIGNER_NODES > 0)); then + launch_nimbus_signing_node() { + SIGNING_NODE_IDX=$1 + ./build/nimbus_signing_node \ + --validators-dir="${DATA_DIR}/validators_shares/${SIGNING_NODE_IDX}" \ + --secrets-dir="${DATA_DIR}/secrets_shares/${SIGNING_NODE_IDX}" \ + --bind-port=$(( BASE_REMOTE_SIGNER_PORT + SIGNING_NODE_IDX - 1 )) + echo "Signing not exited with code $?" + } + + for NUM_REMOTE in $(seq 1 $NIMBUS_SIGNER_NODES); do # TODO find some way for this and other background-launched processes to # still participate in set -e, ideally - ./build/nimbus_signing_node \ - --validators-dir="${DATA_DIR}/validators_shares/${NUM_REMOTE}" \ - --secrets-dir="${DATA_DIR}/secrets_shares/${NUM_REMOTE}" \ - --bind-port=$(( BASE_REMOTE_SIGNER_PORT + NUM_REMOTE )) \ - > "${DATA_DIR}/log_remote_signer_${NUM_REMOTE}.txt" & + launch_nimbus_signing_node $NUM_REMOTE > "${DATA_DIR}/log_nimbus_signing_node_${NUM_REMOTE}.txt" & + done + + PROCS_TO_KILL+=("nimbus_signing_node") +fi + +if ((WEB3SIGNER_NODES > 0)); then + if ! command javac > /dev/null || ! javac -version > /dev/null; then + # On macOS, homebrew doesn't make java available in your PATH by default. + # Instead, macOS ships with a stub executable that displays a message that + # Java is not installed (javac -version exits with an error code 1). + # If the user is running under these default settings, but a homebrew + # installation is disovered, we are happy to use it just in this script: + if [[ -d /opt/homebrew/opt/openjdk/bin ]]; then + export PATH="/opt/homebrew/opt/openjdk/bin:$PATH" + fi + fi + + launch_web3signer() { + WEB3SIGNER_NODE_IDX=$1 + + local secrets_dir="${DATA_DIR}/secrets_shares/${WEB3SIGNER_NODE_IDX}" + local keystores_dir="${DATA_DIR}/validators_shares/${WEB3SIGNER_NODE_IDX}" + + # We re-arrange the keystore files to match the layout expected by the Web3Signer + # TODO generateSimulationDeposits can be refactored to produce the right layout from the start + for validator_pubkey in $(ls "$secrets_dir") + do + mv "$secrets_dir/$validator_pubkey" "$secrets_dir/$validator_pubkey.txt" + mv "$keystores_dir/$validator_pubkey/keystore.json" "$keystores_dir/$validator_pubkey.json" + done + + # still participate in set -e, ideally + # TODO find some way for this and other background-launched processes to + "${WEB3SIGNER_BINARY}" \ + --http-listen-port=$(( BASE_REMOTE_SIGNER_PORT + WEB3SIGNER_NODE_IDX - 1 )) \ + --logging=DEBUG \ + --metrics-enabled=true \ + --metrics-port=$(( BASE_REMOTE_SIGNER_METRICS_PORT + WEB3SIGNER_NODE_IDX - 1 )) \ + eth2 \ + --slashing-protection-enabled=false \ + --keystores-passwords-path="${secrets_dir}" \ + --keystores-path="${keystores_dir}" \ + --network="${RUNTIME_CONFIG_FILE}" + + echo "Web3Signer exited with code $?" + } + + PROCS_TO_KILL+=("${WEB3SIGNER_BINARY}") + PROCS_TO_KILL+=("java") + + for NUM_REMOTE in $(seq 1 $WEB3SIGNER_NODES); do + launch_web3signer $NUM_REMOTE > "${DATA_DIR}/log_web3signer_${NUM_REMOTE}.txt" & done fi # give each node time to load keys sleep 10 -for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do +for NUM_NODE in $(seq 1 $NUM_NODES); do NODE_DATA_DIR="${DATA_DIR}/node${NUM_NODE}" CONTAINER_NODE_DATA_DIR="${CONTAINER_DATA_DIR}/node${NUM_NODE}" VALIDATOR_DATA_DIR="${DATA_DIR}/validator${NUM_NODE}" @@ -975,18 +1162,21 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do done fi - if [ ${#EL_RPC_PORTS[@]} -eq 0 ]; then # check if the array is empty - WEB3_ARG=( - "--require-engine-api-in-bellatrix=no" - ) - else - WEB3_ARG=( - "--web3-url=http://127.0.0.1:${EL_RPC_PORTS[${NUM_NODE}]}" - "--jwt-secret=${EL_DATA_DIRS[${NUM_NODE}]}/jwtsecret" - ) + WEB3_ARG=() + if [ "${RUN_NIMBUS_ETH1}" == "1" ]; then + WEB3_ARG+=("--web3-url=http://127.0.0.1:${NIMBUS_ETH1_RPC_PORTS[$(( NUM_NODE - 1 ))]}") fi - # We enabled the keymanager on half of the nodes + if [ "${RUN_GETH}" == "1" ]; then + WEB3_ARG+=("--web3-url=http://127.0.0.1:${GETH_AUTH_RPC_PORTS[$((NUM_NODE - 1))]}") + fi + + if [ ${#WEB3_ARG[@]} -eq 0 ]; then # check if the array is empty + WEB3_ARG=("--require-engine-api-in-bellatrix=no") + fi + + # We enabled the keymanager on half of the nodes in order + # to make sure that the client can work without it. KEYMANAGER_FLAG="" if [ $((NUM_NODE % 2)) -eq 0 ]; then KEYMANAGER_FLAG="--keymanager" @@ -994,25 +1184,33 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do ${BEACON_NODE_COMMAND} \ --config-file="${CLI_CONF_FILE}" \ - --tcp-port=$(( BASE_PORT + NUM_NODE )) \ - --udp-port=$(( BASE_PORT + NUM_NODE )) \ + --tcp-port=$(( BASE_PORT + NUM_NODE - 1 )) \ + --udp-port=$(( BASE_PORT + NUM_NODE - 1 )) \ --max-peers=$(( NUM_NODES + LC_NODES - 1 )) \ --data-dir="${CONTAINER_NODE_DATA_DIR}" \ ${BOOTSTRAP_ARG} \ + --jwt-secret=${JWT_FILE} \ "${WEB3_ARG[@]}" \ + --payload-builder=${USE_PAYLOAD_BUILDER} \ + --payload-builder-url="http://${PAYLOAD_BUILDER_HOST}:${PAYLOAD_BUILDER_PORT}" \ + --light-client-data-serve=on \ + --light-client-data-import-mode=full \ + --light-client-data-max-periods=999999 \ ${STOP_AT_EPOCH_FLAG} \ ${KEYMANAGER_FLAG} \ --keymanager-token-file="${DATA_DIR}/keymanager-token" \ - --rest-port="$(( BASE_REST_PORT + NUM_NODE ))" \ - --metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \ + --finalized-deposit-tree-snapshot="$CONTAINER_DEPOSIT_TREE_SNAPSHOT_FILE" \ + --rest-port="$(( BASE_REST_PORT + NUM_NODE - 1 ))" \ + --metrics-port="$(( BASE_METRICS_PORT + NUM_NODE - 1 ))" \ --sync-light-client=on \ + --doppelganger-detection=off \ ${EXTRA_ARGS} \ &> "${DATA_DIR}/log${NUM_NODE}.txt" & PIDS="${PIDS},$!" if [[ "${USE_VC}" == "1" ]]; then - if [[ "${LIGHTHOUSE_VC_NODES}" -gt "${NUM_NODE}" ]]; then + if [[ "${LIGHTHOUSE_VC_NODES}" -ge "${NUM_NODE}" ]]; then # Lighthouse needs a different keystore filename for its auto-discovery process. for D in "${VALIDATOR_DATA_DIR}/validators"/0x*; do if [[ -e "${D}/keystore.json" ]]; then @@ -1038,11 +1236,12 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do ${STOP_AT_EPOCH_FLAG} \ --data-dir="${VALIDATOR_DATA_DIR}" \ --metrics \ - --metrics-port:$((BASE_VC_METRICS_PORT + NUM_NODE)) \ + --metrics-port=$(( BASE_VC_METRICS_PORT + NUM_NODE - 1 )) \ + --payload-builder=${USE_PAYLOAD_BUILDER} \ ${KEYMANAGER_FLAG} \ - --keymanager-port=$((BASE_VC_KEYMANAGER_PORT + NUM_NODE)) \ + --keymanager-port=$(( BASE_VC_KEYMANAGER_PORT + NUM_NODE - 1 )) \ --keymanager-token-file="${DATA_DIR}/keymanager-token" \ - --beacon-node="http://127.0.0.1:$((BASE_REST_PORT + NUM_NODE))" \ + --beacon-node="http://127.0.0.1:$(( BASE_REST_PORT + NUM_NODE - 1 ))" \ &> "${DATA_DIR}/log_val${NUM_NODE}.txt" & PIDS="${PIDS},$!" fi @@ -1053,20 +1252,20 @@ done if [ "$LC_NODES" -ge "1" ]; then echo "Waiting for Altair finalization" while :; do - ALTAIR_FORK_EPOCH="$( + BN_ALTAIR_FORK_EPOCH="$( "${CURL_BINARY}" -s "http://localhost:${BASE_REST_PORT}/eth/v1/config/spec" | \ "${JQ_BINARY}" -r '.data.ALTAIR_FORK_EPOCH')" - if [ "${ALTAIR_FORK_EPOCH}" -eq "${ALTAIR_FORK_EPOCH}" ]; then # check for number + if [ "${BN_ALTAIR_FORK_EPOCH}" -eq "${BN_ALTAIR_FORK_EPOCH}" ]; then # check for number break fi - echo "ALTAIR_FORK_EPOCH: ${ALTAIR_FORK_EPOCH}" + echo "ALTAIR_FORK_EPOCH: ${BN_ALTAIR_FORK_EPOCH}" sleep 1 done while :; do CURRENT_FORK_EPOCH="$( "${CURL_BINARY}" -s "http://localhost:${BASE_REST_PORT}/eth/v1/beacon/states/finalized/fork" | \ "${JQ_BINARY}" -r '.data.epoch')" - if [ "${CURRENT_FORK_EPOCH}" -ge "${ALTAIR_FORK_EPOCH}" ]; then + if [ "${CURRENT_FORK_EPOCH}" -ge "${BN_ALTAIR_FORK_EPOCH}" ]; then break fi sleep 1 @@ -1081,16 +1280,16 @@ if [ "$LC_NODES" -ge "1" ]; then LC_TRUSTED_BLOCK_ROOT="$( "${CURL_BINARY}" -s "http://localhost:${BASE_REST_PORT}/eth/v1/beacon/headers/finalized" | \ "${JQ_BINARY}" -r '.data.root')" - for NUM_LC in $(seq 0 $(( LC_NODES - 1 ))); do + for NUM_LC in $(seq 1 $LC_NODES); do LC_DATA_DIR="${DATA_DIR}/lc${NUM_LC}" - if [ ${#EL_RPC_PORTS[@]} -eq 0 ]; then # check if the array is empty - WEB3_ARG=() - else - WEB3_ARG=( - "--web3-url=http://127.0.0.1:${EL_RPC_PORTS[$(( NUM_NODES + NUM_LC ))]}" - "--jwt-secret=${EL_DATA_DIRS[$(( NUM_NODES + NUM_LC ))]}/jwtsecret" - ) + WEB3_ARG=() + if [ "${RUN_NIMBUS_ETH1}" == "1" ]; then + WEB3_ARG+=("--web3-url=http://127.0.0.1:${NIMBUS_ETH1_RPC_PORTS[$(( NUM_NODES + NUM_LC - 1 ))]}") + fi + + if [ "${RUN_GETH}" == "1" ]; then + WEB3_ARG+=("--web3-url=http://127.0.0.1:${GETH_AUTH_RPC_PORTS[$(( NUM_NODES + NUM_LC - 1 ))]}") fi ./build/nimbus_light_client \ @@ -1099,11 +1298,12 @@ if [ "$LC_NODES" -ge "1" ]; then --data-dir="${LC_DATA_DIR}" \ --network="${CONTAINER_DATA_DIR}" \ --bootstrap-node="${LC_BOOTSTRAP_NODE}" \ - --tcp-port=$(( BASE_PORT + NUM_NODES + NUM_LC )) \ - --udp-port=$(( BASE_PORT + NUM_NODES + NUM_LC )) \ + --tcp-port=$(( BASE_PORT + NUM_NODES + NUM_LC - 1 )) \ + --udp-port=$(( BASE_PORT + NUM_NODES + NUM_LC - 1 )) \ --max-peers=$(( NUM_NODES + LC_NODES - 1 )) \ --nat="extip:127.0.0.1" \ --trusted-block-root="${LC_TRUSTED_BLOCK_ROOT}" \ + --jwt-secret="${JWT_FILE}" \ "${WEB3_ARG[@]}" \ ${STOP_AT_EPOCH_FLAG} \ &> "${DATA_DIR}/log_lc${NUM_LC}.txt" & @@ -1124,6 +1324,8 @@ if [[ "$BG_JOBS" != "$NUM_JOBS" ]]; then exit 1 fi +echo "About to wait for the following sub-processes: " $PIDS + # launch "htop" or wait for background jobs if [[ "$USE_HTOP" == "1" ]]; then htop -p "$PIDS" @@ -1132,6 +1334,7 @@ else FAILED=0 for PID in $(echo "$PIDS" | tr ',' ' '); do wait "$PID" || FAILED="$(( FAILED += 1 ))" + echo $PID has completed done if [[ "$FAILED" != "0" ]]; then echo "${FAILED} child processes had non-zero exit codes (or exited early)." @@ -1157,3 +1360,6 @@ if [[ "${TIMEOUT_DURATION}" != "0" ]]; then pkill -HUP -P ${WATCHER_PID} fi fi + +echo The simulation completed successfully +exit 0 diff --git a/scripts/mainnet-non-overriden-config.yaml b/scripts/mainnet-non-overriden-config.yaml new file mode 100644 index 000000000..e63447dcb --- /dev/null +++ b/scripts/mainnet-non-overriden-config.yaml @@ -0,0 +1,103 @@ +# This file should contain the origin run-time config for the mainnet +# network [1] without all properties overriden in the local network +# simulation. We use to generate a full run-time config as required +# by third-party binaries, such as Lighthouse and Web3Signer. +# +# [1]: https://raw.githubusercontent.com/ethereum/consensus-specs/dev/configs/mainnet.yaml + +# Mainnet config + +# Extends the mainnet preset +# (overriden in launch_local_testnet.sh) PRESET_BASE: 'mainnet' + +# Free-form short name of the network that this configuration applies to - known +# canonical network names include: +# * 'mainnet' - there can be only one +# * 'prater' - testnet +# Must match the regex: [a-z0-9\-] +CONFIG_NAME: 'mainnet' + +# Transition +# --------------------------------------------------------------- +# Estimated on Sept 15, 2022 +# (overriden in launch_local_testnet.sh) TERMINAL_TOTAL_DIFFICULTY: 58750000000000000000000 +# By default, don't use these params +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 +TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 + + + +# Genesis +# --------------------------------------------------------------- +# `2**14` (= 16,384) +# (overriden in launch_local_testnet.sh) MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384 +# Dec 1, 2020, 12pm UTC +# (overriden in launch_local_testnet.sh) MIN_GENESIS_TIME: 1606824000 +# Mainnet initial fork version, recommend altering for testnets +GENESIS_FORK_VERSION: 0x00000000 +# 604800 seconds (7 days) +# (overriden in launch_local_testnet.sh) GENESIS_DELAY: 604800 + + +# Forking +# --------------------------------------------------------------- +# Some forks are disabled for now: +# - These may be re-assigned to another fork-version later +# - Temporarily set to max uint64 value: 2**64 - 1 + +# Altair +ALTAIR_FORK_VERSION: 0x01000000 +# (overriden in launch_local_testnet.sh) ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC +# Bellatrix +BELLATRIX_FORK_VERSION: 0x02000000 +# (overriden in launch_local_testnet.sh) BELLATRIX_FORK_EPOCH: 144896 # Sept 6, 2022, 11:34:47am UTC +# Capella +CAPELLA_FORK_VERSION: 0x03000000 +# (overriden in launch_local_testnet.sh) CAPELLA_FORK_EPOCH: 18446744073709551615 +# EIP4844 +EIP4844_FORK_VERSION: 0x04000000 +# (overriden in launch_local_testnet.sh) EIP4844_FORK_EPOCH: 18446744073709551615 + + + + +# Time parameters +# --------------------------------------------------------------- +# 12 seconds +SECONDS_PER_SLOT: 12 +# 14 (estimate from Eth1 mainnet) +SECONDS_PER_ETH1_BLOCK: 14 +# 2**8 (= 256) epochs ~27 hours +MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 +# 2**8 (= 256) epochs ~27 hours +SHARD_COMMITTEE_PERIOD: 256 +# 2**11 (= 2,048) Eth1 blocks ~8 hours +# (overriden in launch_local_testnet.sh) ETH1_FOLLOW_DISTANCE: 2048 + + +# Validator cycle +# --------------------------------------------------------------- +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 +# 2**4 * 10**9 (= 16,000,000,000) Gwei +EJECTION_BALANCE: 16000000000 +# 2**2 (= 4) +MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**16 (= 65,536) +CHURN_LIMIT_QUOTIENT: 65536 + + +# Fork choice +# --------------------------------------------------------------- +# 40% +PROPOSER_SCORE_BOOST: 40 + +# Deposit contract +# --------------------------------------------------------------- +# Ethereum PoW Mainnet +DEPOSIT_CHAIN_ID: 1 +DEPOSIT_NETWORK_ID: 1 +# (overriden in launch_local_testnet.sh) DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa + diff --git a/scripts/make_prometheus_config.sh b/scripts/make_prometheus_config.sh index 0e734de85..defcf2a6e 100755 --- a/scripts/make_prometheus_config.sh +++ b/scripts/make_prometheus_config.sh @@ -90,7 +90,7 @@ scrape_configs: - job_name: "nimbus" static_configs: EOF -for NUM_NODE in $(seq 0 $(( ${NUM_NODES} - 1 ))); do +for NUM_NODE in $(seq 1 $NUM_NODES); do cat >> "${CONFIG_FILE}" < build/mainnet/jwtsecret +fi + +# build/nimbus_beacon_node --non-interactive --udp-port=9123 --tcp-port=9123 --network=mainnet --log-level=DEBUG --data-dir=build/mainnet/nimbus --web3-url=http://localhost:9551/ --rest:on --metrics:on --doppelganger-detection=no --jwt-secret=build/mainnet/jwtsecret diff --git a/scripts/start_geth_nodes.sh b/scripts/start_geth_nodes.sh index 16fee8115..862e807aa 100755 --- a/scripts/start_geth_nodes.sh +++ b/scripts/start_geth_nodes.sh @@ -2,58 +2,63 @@ set -euo pipefail -BASEDIR="$(dirname "${BASH_SOURCE[0]}")" +SCRIPTS_DIR="$(dirname "${BASH_SOURCE[0]}")" -. "${BASEDIR}/geth_vars.sh" +source "${SCRIPTS_DIR}/geth_binaries.sh" +source "${SCRIPTS_DIR}/geth_vars.sh" #These are used in the caller script GETH_ENODES=() -GETH_HTTP_PORTS=() -GETH_NET_PORTS=() -GETH_WS_PORTS=() -GETH_RPC_PORTS=() -GETH_DATA_DIRS=() log "Using ${GETH_BINARY}" -for GETH_NUM_NODE in $(seq 0 $(( GETH_NUM_NODES - 1 ))); do - GETH_NET_PORT=$(( GETH_NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_NET_PORT )) - GETH_HTTP_PORT=$(( GETH_NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_HTTP_PORT )) - GETH_WS_PORT=$(( GETH_NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_WS_PORT )) - GETH_AUTH_RPC_PORT=$(( GETH_NUM_NODE * GETH_PORT_OFFSET + GETH_BASE_AUTH_RPC_PORT )) - log "Starting geth node ${GETH_NUM_NODE} on net port ${GETH_NET_PORT} HTTP port ${GETH_HTTP_PORT} WS port ${GETH_WS_PORT}" - GETHDATADIR=$(mktemp -d "${DATA_DIR}"/geth-data-XXX) - GETH_DATA_DIRS+=(${GETHDATADIR}) - openssl rand -hex 32 | tr -d "\n" > "${GETHDATADIR}/jwtsecret" - ${GETH_BINARY} --http --ws -http.api "engine" --datadir "${GETHDATADIR}" init "${GENESISJSON}" - ${GETH_BINARY} --syncmode full --http --ws --http.corsdomain '*' --http.api "eth,net,engine" -ws.api "eth,net,engine" --datadir "${GETHDATADIR}" ${DISCOVER} --port ${GETH_NET_PORT} --http.port ${GETH_HTTP_PORT} --ws.port ${GETH_WS_PORT} --authrpc.port ${GETH_AUTH_RPC_PORT} --authrpc.jwtsecret "${GETHDATADIR}/jwtsecret" &> "${DATA_DIR}/geth-log${GETH_NUM_NODE}.txt" & - GETH_RETRY=0 - while :; do - if [[ -S "${GETHDATADIR}/geth.ipc" ]]; then - echo "Geth ${GETH_NUM_NODE} started in $(( GETH_RETRY * 100 ))ms" - break - fi - if (( ++GETH_RETRY >= 300 )); then - echo "Geth ${GETH_NUM_NODE} failed to start" - exit 1 - fi - sleep 0.1 - done - NODE_ID=$(${GETH_BINARY} attach --datadir "${GETHDATADIR}" --exec admin.nodeInfo.enode) - GETH_ENODES+=("${NODE_ID}") - GETH_HTTP_PORTS+=("${GETH_HTTP_PORT}") - GETH_NET_PORTS+=("${GETH_NET_PORT}") - GETH_WS_PORTS+=("${GETH_WS_PORT}") - GETH_RPC_PORTS+=("${GETH_AUTH_RPC_PORT}") +start_geth_node() { + GETH_NODE_IDX=$1 + mkdir -p "${GETH_DATA_DIRS[GETH_NODE_IDX]}" + set -x + + ${GETH_BINARY} version + ${GETH_BINARY} --datadir "${GETH_DATA_DIRS[GETH_NODE_IDX]}" init "${EXECUTION_GENESIS_JSON}" + ${GETH_BINARY} \ + --syncmode full \ + --datadir "${GETH_DATA_DIRS[GETH_NODE_IDX]}" \ + ${DISCOVER} \ + --http \ + --http.port ${GETH_RPC_PORTS[GETH_NODE_IDX]} \ + --port ${GETH_NET_PORTS[GETH_NODE_IDX]} \ + --authrpc.port ${GETH_AUTH_RPC_PORTS[GETH_NODE_IDX]} \ + --authrpc.jwtsecret "${JWT_FILE}" +} + +for GETH_NODE_IDX in $(seq 0 $GETH_LAST_NODE_IDX); do + start_geth_node $GETH_NODE_IDX \ + &> "${DATA_DIR}/geth-log${GETH_NODE_IDX}.txt" & +done + +for GETH_NODE_IDX in $(seq 0 $GETH_LAST_NODE_IDX); do + GETH_RETRY=0 + while :; do + if [[ -S "${GETH_DATA_DIRS[GETH_NODE_IDX]}/geth.ipc" ]]; then + echo "Geth ${GETH_NODE_IDX} started in $(( GETH_RETRY * 100 ))ms" + break + fi + if (( ++GETH_RETRY >= 300 )); then + echo "Geth ${GETH_NODE_IDX} failed to start" + exit 1 + fi + sleep 0.1 + done + NODE_ID=$(${GETH_BINARY} attach --datadir "${GETH_DATA_DIRS[GETH_NODE_IDX]}" --exec admin.nodeInfo.enode) + GETH_ENODES+=("${NODE_ID}") done #Add all nodes as peers for dir in "${GETH_DATA_DIRS[@]}" do - for enode in "${GETH_ENODES[@]}" - do - ${GETH_BINARY} attach --datadir "${dir}" --exec "admin.addPeer(${enode})" - done + for enode in "${GETH_ENODES[@]}" + do + ${GETH_BINARY} attach --datadir "${dir}" --exec "admin.addPeer(${enode})" & + done done -log "GETH HTTP Ports: ${GETH_HTTP_PORTS[*]}" +log "GETH RPC Ports: ${GETH_AUTH_RPC_PORTS[*]}" diff --git a/scripts/start_nimbus_el_nodes.sh b/scripts/start_nimbus_el_nodes.sh index 45278db4a..2973bdcc7 100755 --- a/scripts/start_nimbus_el_nodes.sh +++ b/scripts/start_nimbus_el_nodes.sh @@ -2,66 +2,74 @@ set -euo pipefail -BASEDIR="$(dirname "${BASH_SOURCE[0]}")" +SCRIPTS_DIR="$(dirname "${BASH_SOURCE[0]}")" -. "${BASEDIR}/nimbus_el_vars.sh" +. "${SCRIPTS_DIR}/nimbus_el_vars.sh" -#These are used in the caller script -NIMBUSEL_ENODES=() -NIMBUSEL_HTTP_PORTS=() -NIMBUSEL_NET_PORTS=() -NIMBUSEL_WS_PORTS=() -NIMBUSEL_RPC_PORTS=() -NIMBUSEL_DATA_DIRS=() +NIMBUS_ETH1_ENODES=() +NIMBUS_ETH1_DATA_DIRS=() wait_for_port() { for EXPONENTIAL_BACKOFF in {1..10}; do - nc -w 1 -z $1 $2 && break; + nc -w 1 -z $1 $2 > /dev/null && break; DELAY=$((2**$EXPONENTIAL_BACKOFF)) echo "Port ${2} not yet available. Waiting ${DELAY} seconds" sleep $DELAY done } -log "Using ${NIMBUSEL_BINARY}" +if [ -d /opt/homebrew/lib ]; then + # BEWARE + # The recent versions of homebrew/macOS can't add the libraries + # installed by Homebrew in the system's library search path, so + # Nimbus will fail to load RocksDB on start-up. THe new rules in + # macOS make it very difficult for the user to solve the problem + # in their profile, so we add an override here as the lessed evil: + export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH:-}:/opt/homebrew/lib" + # See https://github.com/Homebrew/brew/issues/13481 for more details +fi -for NUM_NODE in $(seq 0 $(( NIMBUSEL_NUM_NODES - 1 ))); do - NIMBUSEL_NET_PORT=$(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_NET_PORT )) - NIMBUSEL_HTTP_PORT=$(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_HTTP_PORT )) - NIMBUSEL_WS_PORT=$(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_WS_PORT )) - NIMBUSEL_AUTH_RPC_PORT=$(( NUM_NODE * NIMBUSEL_PORT_OFFSET + NIMBUSEL_BASE_AUTH_RPC_PORT )) - log "Starting nimbus EL node ${NUM_NODE} on net port ${NIMBUSEL_NET_PORT} HTTP port ${NIMBUSEL_HTTP_PORT} WS port ${NIMBUSEL_WS_PORT}" - NIMBUSEL_DATADIR=$(mktemp -d nimbusel-data-XXX) - NIMBUSEL_DATA_DIRS+=("${NIMBUSEL_DATADIR}") - openssl rand -hex 32 | tr -d "\n" > "${NIMBUSEL_DATADIR}/jwtsecret" - ${NIMBUSEL_BINARY} --data-dir="${NIMBUSEL_DATADIR}" --custom-network="${NIMBUSEL_GENESIS}" "${NIMBUSEL_DISCOVERY}" \ - --tcp-port="${NIMBUSEL_NET_PORT}" --engine-api --engine-api-port="${NIMBUSEL_AUTH_RPC_PORT}" \ - --rpc --rpc-port="${NIMBUSEL_HTTP_PORT}" &> "${DATA_DIR}/nimbusel_log${NUM_NODE}.txt" & +PROCS_TO_KILL+="(${NIMBUS_ETH1_BINARY})" - wait_for_port localhost "${NIMBUSEL_HTTP_PORT}" +for NIMBUS_ETH1_NODE_IDX in $(seq 0 $NIMBUS_ETH1_LAST_NODE_IDX); do + NIMBUS_ETH1_DATA_DIR=$(mktemp -d "${DATA_DIR}/nimbus-eth1-data-XXXXXX") + NIMBUS_ETH1_DATA_DIRS+=("${NIMBUS_ETH1_DATA_DIR}") - NODE_ID=$( - "${CURL_BINARY}" -sS -X POST \ - -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","id":"id","method":"net_nodeInfo"}' \ - "http://localhost:${NIMBUSEL_HTTP_PORT}" | "${JQ_BINARY}" .result.enode) - log "EL Node ID" "${NODE_ID}" - NIMBUSEL_ENODES+=("${NODE_ID}") - NIMBUSEL_HTTP_PORTS+=("${NIMBUSEL_HTTP_PORT}") - NIMBUSEL_NET_PORTS+=("${NIMBUSEL_NET_PORT}") - NIMBUSEL_WS_PORTS+=("${NIMBUSEL_WS_PORT}") - NIMBUSEL_RPC_PORTS+=("${NIMBUSEL_AUTH_RPC_PORT}") + ${NIMBUS_ETH1_BINARY} \ + --data-dir="${NIMBUS_ETH1_DATA_DIR}" \ + --custom-network="${EXECUTION_GENESIS_JSON}" \ + --discovery=None \ + --tcp-port="${NIMBUS_ETH1_NET_PORTS[NIMBUS_ETH1_NODE_IDX]}" \ + --jwt-secret="${JWT_FILE}" \ + --engine-api --engine-api-port="${NIMBUS_ETH1_AUTH_RPC_PORTS[NIMBUS_ETH1_NODE_IDX]}" \ + --rpc --rpc-port="${NIMBUS_ETH1_RPC_PORTS[NIMBUS_ETH1_NODE_IDX]}" \ + &> "${DATA_DIR}/nimbus_eth1_log${NIMBUS_ETH1_NODE_IDX}.txt" & done -for enode in "${NIMBUSEL_ENODES[@]}" +echo "Waiting for the Nimbus ETH1 nodes to come online..." +for NIMBUS_ETH1_NODE_IDX in $(seq 0 $NIMBUS_ETH1_LAST_NODE_IDX); do + wait_for_port localhost "${NIMBUS_ETH1_RPC_PORTS[NIMBUS_ETH1_NODE_IDX]}" + + NODE_ID=$( + "${CURL_BINARY}" -sS -X POST \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"id","method":"net_nodeInfo"}' \ + "http://localhost:${NIMBUS_ETH1_RPC_PORTS[NIMBUS_ETH1_NODE_IDX]}" | "${JQ_BINARY}" .result.enode) + log "EL Node ID" "${NODE_ID}" + NIMBUS_ETH1_ENODES+=("${NODE_ID}") +done + +# TODO Here we should connect to the Geth nodes as well +echo "Connect all nodes though the nimbus_addPeer RPC call..." +for enode in "${NIMBUS_ETH1_ENODES[@]}" do - for port in "${NIMBUSEL_HTTP_PORTS[@]}" + for port in "${NIMBUS_ETH1_RPC_PORTS[@]}" do "${CURL_BINARY}" -sS -X POST \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":"1","method":"nimbus_addPeer","params": ['"${enode}"']}' \ - "http://localhost:${port}" - done + "http://localhost:${port}" & + done done -echo "NimbusEL HTTP Ports: ${NIMBUSEL_HTTP_PORTS[*]}" +echo "Nimbus ETH1 HTTP Ports: ${NIMBUS_ETH1_RPC_PORTS[*]}" diff --git a/scripts/test_merge_node.nim b/scripts/test_merge_node.nim index 3516ee758..d6e1e8472 100644 --- a/scripts/test_merge_node.nim +++ b/scripts/test_merge_node.nim @@ -53,14 +53,13 @@ proc run() {.async.} = echo "args are: web3url jwtsecretfilename" let - eth1Monitor = Eth1Monitor.init( + elManager = newClone ELManager.init( defaultRuntimeConfig, db = nil, nil, @[paramStr(1)], none(DepositTreeSnapshot), none(Eth1Network), false, some readJwtSecret(paramStr(2)).get) - await eth1Monitor.ensureDataProvider() try: - await eth1Monitor.exchangeTransitionConfiguration() + await elManager.exchangeTransitionConfiguration() except ValueError as exc: # Expected, since nothing here sets up the Nimbus TTD correctly echo "exchangeTransitionConfiguration ValueError: " & exc.msg diff --git a/tests/nim.cfg b/tests/nim.cfg index cdcd4a8e9..4bc210a45 100644 --- a/tests/nim.cfg +++ b/tests/nim.cfg @@ -1,6 +1,5 @@ # Use only `secp256k1` public key cryptography as an identity in LibP2P. -d:"libp2p_pki_schemes=secp256k1" - -d:chronosStrictException --styleCheck:usages --styleCheck:hint diff --git a/tests/simulation/restapi.sh b/tests/simulation/restapi.sh index 6e4fa7ccb..129bda109 100755 --- a/tests/simulation/restapi.sh +++ b/tests/simulation/restapi.sh @@ -101,11 +101,12 @@ LOG_TEST_FILE="${TEST_DIR}/client_log.txt" VALIDATORS_DIR="${TEST_DIR}/validators" SECRETS_DIR="${TEST_DIR}/secrets" SNAPSHOT_FILE="${TEST_DIR}/genesis.ssz" +DEPOSIT_TREE_SNAPSHOT_FILE="${TEST_DIR}/deposit_tree_snapshot.ssz" NETWORK_BOOTSTRAP_FILE="${TEST_DIR}/bootstrap_nodes.txt" RESTTEST_RULES="${GIT_ROOT}/ncli/resttest-rules.json" -DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract" RESTTEST_BIN="${GIT_ROOT}/build/resttest" NIMBUS_BEACON_NODE_BIN="${GIT_ROOT}/build/nimbus_beacon_node" +LOCAL_TESTNET_SIMULATION_BIN="${GIT_ROOT}/build/ncli_testnet" BOOTSTRAP_ENR_FILE="${TEST_DIR}/beacon_node.enr" RUNTIME_CONFIG_FILE="${TEST_DIR}/config.yaml" DEPOSITS_FILE="${TEST_DIR}/deposits.json" @@ -163,10 +164,13 @@ if [[ -f "${DEPOSITS_FILE}" ]]; then EXISTING_VALIDATORS=$(grep -o -i deposit_data_root "${DEPOSITS_FILE}" | wc -l) fi +build_if_missing nimbus_beacon_node +build_if_missing ncli_testnet +build_if_missing resttest + if [[ ${EXISTING_VALIDATORS} -ne ${NUM_VALIDATORS} ]]; then - build_if_missing deposit_contract rm -rf "${VALIDATORS_DIR}" "${SECRETS_DIR}" - ${DEPOSIT_CONTRACT_BIN} generateSimulationDeposits \ + ${LOCAL_TESTNET_SIMULATION_BIN} generateDeposits \ --count="${NUM_VALIDATORS}" \ --out-validators-dir="${VALIDATORS_DIR}" \ --out-secrets-dir="${SECRETS_DIR}" \ @@ -174,9 +178,6 @@ if [[ ${EXISTING_VALIDATORS} -ne ${NUM_VALIDATORS} ]]; then echo "All deposits prepared" fi -build_if_missing nimbus_beacon_node -build_if_missing resttest - # Kill child processes on Ctrl-C/SIGTERM/exit, passing the PID of this shell # instance as the parent and the target process name as a pattern to the # "pkill" command. @@ -189,22 +190,6 @@ cleanup() { } trap 'cleanup' SIGINT SIGTERM EXIT -echo "Creating testnet genesis..." -${NIMBUS_BEACON_NODE_BIN} \ - --data-dir="${TEST_DIR}" \ - createTestnet \ - --deposits-file="${DEPOSITS_FILE}" \ - --total-validators="${NUM_VALIDATORS}" \ - --output-genesis="${SNAPSHOT_FILE}" \ - --output-bootstrap-file="${NETWORK_BOOTSTRAP_FILE}" \ - --netkey-file=network_key.json \ - --insecure-netkey-password=true \ - --genesis-offset=-12 # Chain that has already started allows testing empty slots - -# Make sure we use the newly generated genesis -echo "Removing existing database..." -rm -rf "${TEST_DIR}/db" - DEPOSIT_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000" DEPOSIT_CONTRACT_BLOCK="0x0000000000000000000000000000000000000000000000000000000000000000" @@ -218,8 +203,26 @@ GENESIS_DELAY: 10 GENESIS_FORK_VERSION: 0x00000000 DEPOSIT_CONTRACT_ADDRESS: ${DEPOSIT_CONTRACT_ADDRESS} ETH1_FOLLOW_DISTANCE: 1 +ALTAIR_FORK_EPOCH: 0 +BELLATRIX_FORK_EPOCH: 0 EOF +echo "Creating testnet genesis..." +${LOCAL_TESTNET_SIMULATION_BIN} \ + createTestnet \ + --data-dir="${TEST_DIR}" \ + --deposits-file="${DEPOSITS_FILE}" \ + --total-validators="${NUM_VALIDATORS}" \ + --output-genesis="${SNAPSHOT_FILE}" \ + --output-deposit-tree-snapshot="${DEPOSIT_TREE_SNAPSHOT_FILE}" \ + --output-bootstrap-file="${NETWORK_BOOTSTRAP_FILE}" \ + --netkey-file=network_key.json \ + --insecure-netkey-password=true \ + --genesis-offset=-60 # Chain that has already started allows testing empty slots +# Make sure we use the newly generated genesis +echo "Removing existing database..." +rm -rf "${TEST_DIR}/db" "${TEST_DIR}/validators/slashing_protection.sqlite3" + ${NIMBUS_BEACON_NODE_BIN} \ --tcp-port=${BASE_PORT} \ --udp-port=${BASE_PORT} \ diff --git a/tests/simulation/run_node.sh b/tests/simulation/run_node.sh deleted file mode 100755 index 7dd0b4709..000000000 --- a/tests/simulation/run_node.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NODE_ID=${1} -shift - -# Read in variables -# shellcheck source=/dev/null -source "$(dirname "$0")/vars.sh" - -if [[ -n "$1" ]]; then - ADDITIONAL_BEACON_NODE_ARGS=$1 - shift -else - ADDITIONAL_BEACON_NODE_ARGS="" -fi - -BOOTSTRAP_ARG="" - -if [[ -n "$1" ]]; then - BOOTSTRAP_NODE_ID=$1 - shift -else - BOOTSTRAP_NODE_ID=$BOOTSTRAP_NODE -fi - -BOOTSTRAP_ADDRESS_FILE="${SIMULATION_DIR}/node-${BOOTSTRAP_NODE_ID}/beacon_node.enr" - -if [[ "$NODE_ID" != "$BOOTSTRAP_NODE" ]]; then - BOOTSTRAP_ARG="--bootstrap-file=$BOOTSTRAP_ADDRESS_FILE" -else - BOOTSTRAP_ARG="--netkey-file=network_key.json --insecure-netkey-password" -fi - -# set up the environment -# shellcheck source=/dev/null -source "${SIM_ROOT}/../../env.sh" - -cd "$GIT_ROOT" - -NODE_DATA_DIR="${SIMULATION_DIR}/node-$NODE_ID" -NODE_VALIDATORS_DIR=$NODE_DATA_DIR/validators/ -NODE_SECRETS_DIR=$NODE_DATA_DIR/secrets/ -MAKEDIR=$GIT_ROOT/scripts/makedir.sh -COPYFILE=$GIT_ROOT/scripts/copyfile.sh - -PORT=$(( BASE_P2P_PORT + NODE_ID )) - -NAT_ARG="--nat:extip:127.0.0.1" -if [ "${NAT:-}" == "1" ]; then - NAT_ARG="--nat:any" -fi - -"$MAKEDIR" "$NODE_DATA_DIR" - -rm -rf "$NODE_VALIDATORS_DIR" -"$MAKEDIR" "$NODE_VALIDATORS_DIR" - -rm -rf "$NODE_SECRETS_DIR" -"$MAKEDIR" "$NODE_SECRETS_DIR" - -VALIDATORS_PER_NODE=$(( NUM_VALIDATORS / (TOTAL_NODES - 1) )) -if [ "${USE_BN_VC_VALIDATOR_SPLIT:-}" == "yes" ]; then - # if using validator client binaries in addition to beacon nodes we will - # split the keys for this instance in half between the BN and the VC - # and the validators for the BNs will be from the first half of all validators - VALIDATORS_PER_NODE=$((VALIDATORS_PER_NODE / 2 )) -fi - -if [[ $NODE_ID -lt $BOOTSTRAP_NODE ]]; then - pushd "$VALIDATORS_DIR" >/dev/null - for VALIDATOR in $(ls | tail -n +$(( ($VALIDATORS_PER_NODE * $NODE_ID) + 1 )) | head -n $VALIDATORS_PER_NODE); do - "$COPYFILE" "$VALIDATOR" "$NODE_VALIDATORS_DIR" - "$COPYFILE" "$SECRETS_DIR/$VALIDATOR" "$NODE_SECRETS_DIR" - done - popd >/dev/null -fi - -rm -rf "$NODE_DATA_DIR/dump" -"$MAKEDIR" "$NODE_DATA_DIR/dump" - -cd "$NODE_DATA_DIR" - -$BEACON_NODE_BIN \ - --log-level=${LOG_LEVEL:-DEBUG} \ - $BOOTSTRAP_ARG \ - --network="$SIMULATION_DIR" \ - --data-dir=$NODE_DATA_DIR \ - --secrets-dir=$NODE_SECRETS_DIR \ - --node-name=$NODE_ID \ - --tcp-port=$PORT \ - --udp-port=$PORT \ - $NAT_ARG \ - $WEB3_ARG \ - --rest \ - --rest-address="127.0.0.1" \ - --rest-port="$(( $BASE_REST_PORT + $NODE_ID ))" \ - --metrics \ - --metrics-address="127.0.0.1" \ - --metrics-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \ - --doppelganger-detection=off \ - ${ADDITIONAL_BEACON_NODE_ARGS} \ - "$@" diff --git a/tests/simulation/run_validator.sh b/tests/simulation/run_validator.sh deleted file mode 100755 index ab80ba0c4..000000000 --- a/tests/simulation/run_validator.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash - -set -e - -NODE_ID=${1} -shift - -# Read in variables -# shellcheck source=/dev/null -source "$(dirname "$0")/vars.sh" - -# set up the environment -# shellcheck source=/dev/null -source "${SIM_ROOT}/../../env.sh" - -cd "$GIT_ROOT" - -NODE_DATA_DIR="${SIMULATION_DIR}/validator-$NODE_ID" -NODE_VALIDATORS_DIR=$NODE_DATA_DIR/validators/ -NODE_SECRETS_DIR=$NODE_DATA_DIR/secrets/ -MAKEDIR=$GIT_ROOT/scripts/makedir.sh -COPYFILE=$GIT_ROOT/scripts/copyfile.sh - -rm -rf "$NODE_VALIDATORS_DIR" -"$MAKEDIR" "$NODE_VALIDATORS_DIR" - -rm -rf "$NODE_SECRETS_DIR" -"$MAKEDIR" "$NODE_SECRETS_DIR" - -# we will split the keys for this instance in half between the BN and the VC -# and the validators for the VCs will be from the second half of all validators -VALIDATORS_PER_NODE=$(( (NUM_VALIDATORS / TOTAL_NODES) / 2 )) -VALIDATOR_OFFSET=$((NUM_VALIDATORS / 2)) - -if [[ $NODE_ID -lt $TOTAL_NODES ]]; then - - pushd "$VALIDATORS_DIR" >/dev/null - for VALIDATOR in $(ls | tail -n +$(( $VALIDATOR_OFFSET + ($VALIDATORS_PER_NODE * $NODE_ID) + 1 )) | head -n $VALIDATORS_PER_NODE); do - "$COPYFILE" "$VALIDATOR" "$NODE_VALIDATORS_DIR" - "$COPYFILE" "$SECRETS_DIR/$VALIDATOR" "$NODE_SECRETS_DIR" - done - popd >/dev/null -fi - -cd "$NODE_DATA_DIR" - -$VALIDATOR_CLIENT_BIN \ - --log-level=${LOG_LEVEL:-DEBUG} \ - --data-dir=$NODE_DATA_DIR \ - --secrets-dir=$NODE_SECRETS_DIR \ - --beacon-node="http://127.0.0.1:$(( $BASE_REST_PORT + $NODE_ID ))" diff --git a/tests/simulation/start-in-tmux.sh b/tests/simulation/start-in-tmux.sh deleted file mode 100755 index d37894bf7..000000000 --- a/tests/simulation/start-in-tmux.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -cd "$(dirname "$0")" - -TMUX_CMD="${TMUX_CMD:-tmux}" -USE_TMUX="${USE_TMUX:-yes}" - -if [[ "$USE_MULTITAIL" == "yes" ]]; then - USE_TMUX="no" -fi - -if [[ "$USE_TMUX" != "no" ]]; then - type "$TMUX_CMD" &>/dev/null || { echo "${TMUX_CMD}" is missing; USE_TMUX="no"; } -fi - -export TMUX_CMD USE_TMUX - -if [[ "$USE_TMUX" != "no" ]]; then - TMUX_SESSION_NAME="${TMUX_SESSION_NAME:-nbc-sim}" - - $TMUX_CMD kill-session -t "${TMUX_SESSION_NAME}" &>/dev/null || true - $TMUX_CMD new-session -s "${TMUX_SESSION_NAME}" -d - $TMUX_CMD setenv -t "${TMUX_SESSION_NAME}" USE_TMUX yes - - $TMUX_CMD bind-key -n q kill-session - - # maybe these should be moved to a user config file - $TMUX_CMD set-option -t "${TMUX_SESSION_NAME}" history-limit 999999 - $TMUX_CMD set-option -t "${TMUX_SESSION_NAME}" remain-on-exit on - $TMUX_CMD set -t "${TMUX_SESSION_NAME}" mouse on - - # We create a new window, so the above settings can take place - $TMUX_CMD new-window -d -t "${TMUX_SESSION_NAME}" -n "sim" - $TMUX_CMD kill-pane -t "${TMUX_SESSION_NAME}:0" - - $TMUX_CMD new-window -t "${TMUX_SESSION_NAME}" -n "start-script" "if ! $PWD/start.sh; then echo -en '\nPress any key to exit... '; read; tmux kill-session; fi" - $TMUX_CMD select-window -t "${TMUX_SESSION_NAME}:start-script" - - $TMUX_CMD attach-session -t "${TMUX_SESSION_NAME}" -else - ./start.sh -fi - diff --git a/tests/simulation/start.sh b/tests/simulation/start.sh deleted file mode 100755 index 61dbd10d1..000000000 --- a/tests/simulation/start.sh +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -# To allow overriding the program names -TMUX_CMD="${TMUX_CMD:-tmux}" -MULTITAIL_CMD="${MULTITAIL_CMD:-multitail}" -GANACHE_CMD="${GANACHE_CMD:-ganache-cli}" -PROMETHEUS_CMD="${PROMETHEUS_CMD:-prometheus}" -CTAIL_CMD="${CTAIL_CMD:-ctail}" - -TMUX_SESSION_NAME="${TMUX_SESSION_NAME:-nbc-sim}" - -WAIT_GENESIS="${WAIT_GENESIS:-no}" - -USE_MULTITAIL="${USE_MULTITAIL:-no}" -if [[ "$USE_MULTITAIL" != "no" ]]; then - type "$MULTITAIL_CMD" &>/dev/null || { echo "${MULTITAIL_CMD}" is missing; USE_MULTITAIL="no"; } -fi - -USE_TMUX="${USE_TMUX:-no}" -if [[ "$USE_TMUX" == "yes" ]]; then - type "$TMUX_CMD" &>/dev/null || { echo "${TMUX_CMD}" is missing; USE_TMUX="no"; } -fi - -USE_GANACHE="${USE_GANACHE:-yes}" -if [[ "$USE_GANACHE" == "yes" ]]; then - type "$GANACHE_CMD" &>/dev/null || { echo $GANACHE_CMD is missing; USE_GANACHE="no"; } -fi - -USE_PROMETHEUS="${USE_PROMETHEUS:-yes}" -if [[ "$USE_PROMETHEUS" == "yes" ]]; then - type "$PROMETHEUS_CMD" &>/dev/null || { echo $PROMETHEUS_CMD is missing; USE_PROMETHEUS="no"; } -fi - -USE_CTAIL="${USE_CTAIL:-yes}" -if [[ "$USE_CTAIL" == "yes" ]]; then - type "$CTAIL_CMD" &>/dev/null || { USE_CTAIL="no"; } -fi - -# Read in variables -# shellcheck source=/dev/null -source "$(dirname "$0")/vars.sh" - -cd "$SIM_ROOT" -mkdir -p "$SIMULATION_DIR" -mkdir -p "$VALIDATORS_DIR" -mkdir -p "$SECRETS_DIR" - -cd "$GIT_ROOT" - -CUSTOM_NIMFLAGS="${NIMFLAGS} -d:useSysAsserts -d:const_preset=mainnet -d:local_testnet" -GANACHE_BLOCK_TIME=5 - -# Run with "SLOTS_PER_EPOCH=8 ./start.sh" to change these -DEFS="" -DEFS+="-d:SECONDS_PER_ETH1_BLOCK=$GANACHE_BLOCK_TIME " -DEFS+="-d:MAX_COMMITTEES_PER_SLOT=${MAX_COMMITTEES_PER_SLOT:-1} " # Spec default: 64 -DEFS+="-d:SLOTS_PER_EPOCH=${SLOTS_PER_EPOCH:-6} " # Spec default: 32 -DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-6} " # Spec default: 12 - -# Windows detection -if uname | grep -qiE "mingw|msys"; then - MAKE="mingw32-make" -else - MAKE="make" -fi - -make_once () { - target_flag_var="$1_name" - if [[ -z "${!target_flag_var}" ]]; then - export $target_flag_var=1 - $MAKE $1 - fi -} - -mkdir -p "${METRICS_DIR}" -./scripts/make_prometheus_config.sh \ - --nodes ${TOTAL_NODES} \ - --base-metrics-port ${BASE_METRICS_PORT} \ - --config-file "${METRICS_DIR}/prometheus.yml" || true # TODO: this currently fails on macOS, - # but it can be considered non-critical - -COMMANDS=() - -if [[ "$USE_GANACHE" == "yes" ]]; then - if [[ "$USE_TMUX" == "yes" ]]; then - $TMUX_CMD new-window -d -t $TMUX_SESSION_NAME -n "$GANACHE_CMD" "$GANACHE_CMD --blockTime $GANACHE_BLOCK_TIME --gasLimit 100000000 -e 100000 --verbose" - else - echo NOTICE: $GANACHE_CMD will be started automatically only with USE_TMUX=yes - USE_GANACHE="no" - fi -fi - -if [[ "$USE_PROMETHEUS" == "yes" ]]; then - if [[ "$USE_TMUX" == "yes" ]]; then - rm -rf "${METRICS_DIR}/data" - mkdir -p "${METRICS_DIR}/data" - # TODO: Prometheus is not shut down properly on tmux kill-session - killall prometheus &>/dev/null || true - PROMETHEUS_FLAGS="--config.file=./prometheus.yml --storage.tsdb.path=./prometheus" - $TMUX_CMD new-window -d -t $TMUX_SESSION_NAME -n "$PROMETHEUS_CMD" "cd '$METRICS_DIR' && $PROMETHEUS_CMD $PROMETHEUS_FLAGS" - else - echo NOTICE: $PROMETHEUS_CMD will be started automatically only with USE_TMUX=yes - USE_PROMETHEUS="no" - fi -fi - -$MAKE -j2 --no-print-directory NIMFLAGS="$CUSTOM_NIMFLAGS $DEFS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" nimbus_beacon_node nimbus_validator_client - -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 - -if [[ $EXISTING_VALIDATORS -ne $NUM_VALIDATORS ]]; then - make_once deposit_contract - - rm -rf "$VALIDATORS_DIR" - rm -rf "$SECRETS_DIR" - - build/deposit_contract generateSimulationDeposits \ - --count="${NUM_VALIDATORS}" \ - --out-validators-dir="$VALIDATORS_DIR" \ - --out-secrets-dir="$SECRETS_DIR" \ - --out-deposits-file="$DEPOSITS_FILE" - - echo "All deposits prepared" -fi - -if [ ! -f "${SNAPSHOT_FILE}" ]; then - if [[ "${WAIT_GENESIS}" != "yes" ]]; then - echo Creating testnet genesis... - $BEACON_NODE_BIN \ - --data-dir="${SIMULATION_DIR}/node-$BOOTSTRAP_NODE" \ - createTestnet \ - $WEB3_ARG \ - --deposits-file="${DEPOSITS_FILE}" \ - --total-validators="${NUM_VALIDATORS}" \ - --output-genesis="${SNAPSHOT_FILE}" \ - --output-bootstrap-file="${NETWORK_BOOTSTRAP_FILE}" \ - --bootstrap-address=127.0.0.1 \ - --bootstrap-port=$(( BASE_P2P_PORT + BOOTSTRAP_NODE )) \ - --netkey-file=network_key.json \ - --insecure-netkey-password=true \ - --genesis-offset=30 # Delay in seconds - fi -fi - -function run_cmd { - i=$1 - CMD=$2 - bin_name=$3 - if [[ "$USE_TMUX" == "yes" ]]; then - echo "Starting node $i..." - $TMUX_CMD select-window -t "${TMUX_SESSION_NAME}:sim" - $TMUX_CMD split-window -t "${TMUX_SESSION_NAME}" "if ! $CMD; then read; fi" - $TMUX_CMD select-layout -t "${TMUX_SESSION_NAME}:sim" tiled - elif [[ "$USE_MULTITAIL" != "no" ]]; then - if [[ "$i" == "$BOOTSTRAP_NODE" ]]; then - SLEEP="0" - else - SLEEP="3" - fi - # "multitail" closes the corresponding panel when a command exits, so let's make sure it doesn't exit - COMMANDS+=( " -cT ansi -t '$bin_name #$i' -l 'sleep $SLEEP; $CMD; echo [node execution completed]; while true; do sleep 100; done'" ) - else - eval "${CMD}" & - fi -} - -DEPOSIT_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000" -DEPOSIT_CONTRACT_BLOCK="0x0000000000000000000000000000000000000000000000000000000000000000" - -if [ "$USE_GANACHE" != "no" ]; then - make_once deposit_contract - echo Deploying the validator deposit contract... - - 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]} - DEPOSIT_CONTRACT_BLOCK=${OUTPUT_PIECES[1]} - - echo Contract deployed at $DEPOSIT_CONTRACT_ADDRESS:$DEPOSIT_CONTRACT_BLOCK - - if [[ "$WAIT_GENESIS" == "yes" ]]; then - run_cmd "(deposit maker)" "$DEPOSIT_CONTRACT_BIN sendDeposits \ - --deposits-file='$DEPOSITS_FILE' \ - --min-delay=0 --max-delay=1 \ - $WEB3_ARG \ - --deposit-contract=${DEPOSIT_CONTRACT_ADDRESS}" - fi -fi - -echo Wrote $RUNTIME_CONFIG_FILE: - -tee "$RUNTIME_CONFIG_FILE" </dev/null - -# When changing these, also update the readme section on running simulation -# so that the run_node example is correct! -NUM_VALIDATORS=${VALIDATORS:-128} -TOTAL_NODES=${NODES:-4} -TOTAL_USER_NODES=${USER_NODES:-0} -TOTAL_SYSTEM_NODES=$(( TOTAL_NODES - TOTAL_USER_NODES )) -BOOTSTRAP_NODE=$(( TOTAL_NODES - 1 )) -USE_BN_VC_VALIDATOR_SPLIT=${BN_VC_VALIDATOR_SPLIT:-yes} - -SIMULATION_DIR="${SIM_ROOT}/data" -METRICS_DIR="${SIM_ROOT}/prometheus" -VALIDATORS_DIR="${SIM_ROOT}/validators" -SECRETS_DIR="${SIM_ROOT}/secrets" -SNAPSHOT_FILE="${SIMULATION_DIR}/genesis.ssz" -NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt" -BEACON_NODE_BIN="${GIT_ROOT}/build/nimbus_beacon_node" -VALIDATOR_CLIENT_BIN="${GIT_ROOT}/build/nimbus_validator_client" -DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract" -BOOTSTRAP_ENR_FILE="${SIMULATION_DIR}/node-${BOOTSTRAP_NODE}/beacon_node.enr" -RUNTIME_CONFIG_FILE="${SIMULATION_DIR}/config.yaml" -DEPOSITS_FILE="${SIMULATION_DIR}/deposits.json" - -if [[ "$USE_GANACHE" == "yes" ]]; then - WEB3_ARG="--web3-url=ws://localhost:8545" -else - WEB3_ARG="" -fi - -BASE_P2P_PORT=30000 -BASE_REST_PORT=5052 -BASE_METRICS_PORT=8008 diff --git a/tests/test_keymanager_api.nim b/tests/test_keymanager_api.nim index 610cac0dc..2c4940e6b 100644 --- a/tests/test_keymanager_api.nim +++ b/tests/test_keymanager_api.nim @@ -23,7 +23,7 @@ import nimbus_beacon_node, beacon_node_status, nimbus_validator_client], ../beacon_chain/validator_client/common, - + ../ncli/ncli_testnet, ./testutil type @@ -46,7 +46,9 @@ const validatorsDir = dataDir / "validators" secretsDir = dataDir / "secrets" depositsFile = dataDir / "deposits.json" + runtimeConfigFile = dataDir / "config.yaml" genesisFile = dataDir / "genesis.ssz" + depositTreeSnapshotFile = dataDir / "deposit_tree_snapshot.ssz" bootstrapEnrFile = dataDir / "bootstrap_node.enr" tokenFilePath = dataDir / "keymanager-token.txt" defaultBasePort = 49000 @@ -156,12 +158,24 @@ proc prepareNetwork = Json.saveFile(depositsFile, launchPadDeposits) notice "Deposit data written", filename = depositsFile - let createTestnetConf = try: BeaconNodeConf.load(cmdLine = mapIt([ - "--data-dir=" & dataDir, + let runtimeConfigWritten = secureWriteFile(runtimeConfigFile, """ +ALTAIR_FORK_EPOCH: 0 +BELLATRIX_FORK_EPOCH: 0 +""") + + if runtimeConfigWritten.isOk: + notice "Run-time config written", filename = runtimeConfigFile + else: + fatal "Failed to write run-time config", filename = runtimeConfigFile + quit 1 + + let createTestnetConf = try: ncli_testnet.CliConfig.load(cmdLine = mapIt([ "createTestnet", + "--data-dir=" & dataDir, "--total-validators=" & $simulationDepositsCount, "--deposits-file=" & depositsFile, "--output-genesis=" & genesisFile, + "--output-deposit-tree-snapshot=" & depositTreeSnapshotFile, "--output-bootstrap-file=" & bootstrapEnrFile, "--netkey-file=network_key.json", "--insecure-netkey-password=true", diff --git a/vendor/nim-eth b/vendor/nim-eth index 68f4c5382..5b189ce5e 160000 --- a/vendor/nim-eth +++ b/vendor/nim-eth @@ -1 +1 @@ -Subproject commit 68f4c53828cec749e398864bcc6b0097c8d66c31 +Subproject commit 5b189ce5e22a785d1a3a3ea6a4d34387378df2b4 diff --git a/vendor/nim-web3 b/vendor/nim-web3 index 98fba0fb0..4726fdc22 160000 --- a/vendor/nim-web3 +++ b/vendor/nim-web3 @@ -1 +1 @@ -Subproject commit 98fba0fb0471abffdbe69fb8e66bb59152a7075c +Subproject commit 4726fdc223d7cc8c3fe490e9ab58a7b43eae742a