diff --git a/beacon_chain/eth1_monitor.nim b/beacon_chain/eth1_monitor.nim index 30bd39eb2..414c854cc 100644 --- a/beacon_chain/eth1_monitor.nim +++ b/beacon_chain/eth1_monitor.nim @@ -430,9 +430,9 @@ proc init*(T: type Eth1Monitor, dataProvider: dataProvider, eth1Progress: newAsyncEvent()) -proc allDepositsUpTo(m: Eth1Monitor, totalDeposits: uint64): seq[Deposit] = +proc allDepositsUpTo(m: Eth1Monitor, totalDeposits: uint64): seq[DepositData] = for i in 0'u64 ..< totalDeposits: - result.add Deposit(data: m.db.deposits.get(i)) + result.add m.db.deposits.get(i) proc createGenesisState(m: Eth1Monitor, eth1Block: Eth1Block): BeaconStateRef = notice "Generating genesis state", @@ -443,7 +443,6 @@ proc createGenesisState(m: Eth1Monitor, eth1Block: Eth1Block): BeaconStateRef = activeValidators = eth1Block.activeValidatorsCount var deposits = m.allDepositsUpTo(eth1Block.voteData.deposit_count) - attachMerkleProofs deposits result = initialize_beacon_state_from_eth1( m.preset, diff --git a/beacon_chain/interop.nim b/beacon_chain/interop.nim index 72ec7a22b..243dc9279 100644 --- a/beacon_chain/interop.nim +++ b/beacon_chain/interop.nim @@ -39,15 +39,12 @@ func makeDeposit*( preset: RuntimePreset, pubkey: ValidatorPubKey, privkey: ValidatorPrivKey, epoch = 0.Epoch, amount: Gwei = MAX_EFFECTIVE_BALANCE.Gwei, - flags: UpdateFlags = {}): Deposit = - var - ret = Deposit( - data: DepositData( - amount: amount, - pubkey: pubkey, - withdrawal_credentials: makeWithdrawalCredentials(pubkey))) + flags: UpdateFlags = {}): DepositData = + result = DepositData( + amount: amount, + pubkey: pubkey, + withdrawal_credentials: makeWithdrawalCredentials(pubkey)) if skipBLSValidation notin flags: - ret.data.signature = preset.get_deposit_signature(ret.data, privkey) + result.signature = preset.get_deposit_signature(result, privkey) - ret diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 8147eb623..541ea3619 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -1080,11 +1080,9 @@ programMain: err = formatMsg(err, config.testnetDepositsFile.string) quit 1 - var deposits: seq[Deposit] + var deposits: seq[DepositData] for i in config.firstValidator.int ..< launchPadDeposits.len: - deposits.add Deposit(data: launchPadDeposits[i] as DepositData) - - attachMerkleProofs(deposits) + deposits.add(launchPadDeposits[i] as DepositData) let startTime = uint64(times.toUnix(times.getTime()) + config.genesisOffset) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 41ff9226f..b0a5b2107 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -52,16 +52,16 @@ func decrease_balance*( state.balances[index] - delta # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#deposits -func get_validator_from_deposit(state: BeaconState, deposit: Deposit): +func get_validator_from_deposit(state: BeaconState, deposit: DepositData): Validator = let - amount = deposit.data.amount + amount = deposit.amount effective_balance = min( amount - amount mod EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) Validator( - pubkey: deposit.data.pubkey, - withdrawal_credentials: deposit.data.withdrawal_credentials, + pubkey: deposit.pubkey, + withdrawal_credentials: deposit.withdrawal_credentials, activation_eligibility_epoch: FAR_FUTURE_EPOCH, activation_epoch: FAR_FUTURE_EPOCH, exit_epoch: FAR_FUTURE_EPOCH, @@ -110,7 +110,7 @@ proc process_deposit*(preset: RuntimePreset, return ok() # Add validator and balance entries - state.validators.add(get_validator_from_deposit(state, deposit)) + state.validators.add(get_validator_from_deposit(state, deposit.data)) state.balances.add(amount) else: # Increase balance by deposit amount @@ -221,7 +221,7 @@ proc initialize_beacon_state_from_eth1*( preset: RuntimePreset, eth1_block_hash: Eth2Digest, eth1_timestamp: uint64, - deposits: openArray[Deposit], + deposits: openArray[DepositData], flags: UpdateFlags = {}): BeaconStateRef {.nbench.} = ## Get the genesis ``BeaconState``. ## @@ -261,15 +261,36 @@ proc initialize_beacon_state_from_eth1*( # Seed RANDAO with Eth1 entropy state.randao_mixes.fill(eth1_block_hash) - # Process deposits - let - leaves = deposits.mapIt(it.data) - var i = 0 - for prefix_root in hash_tree_roots_prefix( - leaves, 2'i64^DEPOSIT_CONTRACT_TREE_DEPTH): - state.eth1_data.deposit_root = prefix_root - discard process_deposit(preset, state[], deposits[i], flags) - i += 1 + 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, int]() + 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[], ValidatorIndex foundIdx[], amount) + do: + if skipBlsValidation in flags or + verify_deposit_signature(preset, deposit): + state.validators.add(get_validator_from_deposit(state[], deposit)) + state.balances.add(amount) + pubkeyToIndex[pubkey] = idx + else: + # Invalid deposits are perfectly possible + trace "Skipping deposit with invalid signature", + deposit = shortLog(deposit) # Process activations for validator_index in 0 ..< state.validators.len: @@ -293,7 +314,7 @@ proc initialize_hashed_beacon_state_from_eth1*( preset: RuntimePreset, eth1_block_hash: Eth2Digest, eth1_timestamp: uint64, - deposits: openArray[Deposit], + deposits: openArray[DepositData], flags: UpdateFlags = {}): HashedBeaconState = let genesisState = initialize_beacon_state_from_eth1( preset, eth1_block_hash, eth1_timestamp, deposits, flags) diff --git a/beacon_chain/ssz/merkleization.nim b/beacon_chain/ssz/merkleization.nim index d4f5c8290..ed98183a8 100644 --- a/beacon_chain/ssz/merkleization.nim +++ b/beacon_chain/ssz/merkleization.nim @@ -684,13 +684,3 @@ func hash_tree_root*(x: auto): Eth2Digest {.raises: [Defect].} = trs "HASH TREE ROOT FOR ", name(type x), " = ", "0x", $result -iterator hash_tree_roots_prefix*[T](lst: openArray[T], limit: static Limit): Eth2Digest = - # This is a particular type's instantiation of a general fold, reduce, - # accumulation, prefix sums, etc family of operations. As long as that - # Eth1 deposit case is the only notable example -- the usual uses of a - # list involve, at some point, tree-hashing it -- finalized hashes are - # the only abstraction that escapes from this module this way. - var merkleizer = createMerkleizer(limit) - for i, elem in lst: - merkleizer.addChunk(hash_tree_root(elem).data) - yield mixInLength(merkleizer.getFinalHash(), i + 1) diff --git a/research/simutils.nim b/research/simutils.nim index 0431adb14..b60c42b1c 100644 --- a/research/simutils.nim +++ b/research/simutils.nim @@ -66,8 +66,13 @@ proc loadGenesis*(validators: Natural, validate: bool): ref HashedBeaconState = echo "Generating Genesis..." - res.data = - initialize_beacon_state_from_eth1(defaultRuntimePreset, Eth2Digest(), 0, deposits, flags)[] + res.data = initialize_beacon_state_from_eth1( + defaultRuntimePreset, + Eth2Digest(), + 0, + deposits, + flags)[] + res.root = hash_tree_root(res.data) echo &"Saving to {fn}..." diff --git a/tests/mocking/mock_deposits.nim b/tests/mocking/mock_deposits.nim index aa3c4f2b0..8043e314d 100644 --- a/tests/mocking/mock_deposits.nim +++ b/tests/mocking/mock_deposits.nim @@ -46,7 +46,7 @@ func mockDepositData( ret template mockGenesisDepositsImpl( - result: seq[Deposit], + result: seq[DepositData], validatorCount: uint64, amount: untyped, flags: UpdateFlags = {}, @@ -65,7 +65,7 @@ template mockGenesisDepositsImpl( updateAmount # DepositData - result[valIdx].data = mockDepositData(MockPubKeys[valIdx], amount) + result[valIdx] = mockDepositData(MockPubKeys[valIdx], amount) else: # With signing var depositsDataHash: seq[Eth2Digest] var depositsData: seq[DepositData] @@ -78,17 +78,17 @@ template mockGenesisDepositsImpl( updateAmount # DepositData - result[valIdx].data = mockDepositData( + result[valIdx] = mockDepositData( MockPubKeys[valIdx], MockPrivKeys[valIdx], amount, flags) - depositsData.add result[valIdx].data - depositsDataHash.add hash_tree_root(result[valIdx].data) + depositsData.add result[valIdx] + depositsDataHash.add hash_tree_root(result[valIdx]) proc mockGenesisBalancedDeposits*( validatorCount: uint64, amountInEth: Positive, flags: UpdateFlags = {} - ): seq[Deposit] = + ): seq[DepositData] = ## The amount should be strictly positive ## - 1 is the minimum deposit amount (MIN_DEPOSIT_AMOUNT) ## - 16 is the ejection balance (EJECTION_BALANCE) @@ -100,28 +100,6 @@ proc mockGenesisBalancedDeposits*( let amount = amountInEth.uint64 * 10'u64^9 mockGenesisDepositsImpl(result, validatorCount,amount,flags): discard - attachMerkleProofs(result) - -proc mockGenesisUnBalancedDeposits*( - validatorCount: uint64, - amountRangeInEth: Slice[int], # TODO: use "Positive", Nim range bug - flags: UpdateFlags = {} - ): seq[Deposit] = - - ## The range of deposit amount should be strictly positive - ## - 1 is the minimum deposit amount (MIN_DEPOSIT_AMOUNT) - ## - 16 is the ejection balance (EJECTION_BALANCE) - ## - 32 is the max effective balance (MAX_EFFECTIVE_BALANCE) - ## ETH beyond do not contribute more for staking. - ## - ## Only validators with 32 ETH will be active at genesis - - var rng {.global.} = initRand(0x42) # Fixed seed for reproducibility - var amount: uint64 - - mockGenesisDepositsImpl(result, validatorCount, amount, flags): - amount = rng.rand(amountRangeInEth).uint64 * 10'u64^9 - attachMerkleProofs(result) proc mockUpdateStateForNewDeposit*( state: var BeaconState, diff --git a/tests/test_interop.nim b/tests/test_interop.nim index 24c51b57b..2ae93bf94 100644 --- a/tests/test_interop.nim +++ b/tests/test_interop.nim @@ -141,12 +141,11 @@ suiteReport "Interop": # Check against https://github.com/protolambda/zcli: # zcli keys generate --to 64 | zcli genesis mock --genesis-time 1570500000 > /tmp/state.ssz # zcli hash-tree-root state /tmp/state.ssz - var deposits: seq[Deposit] + var deposits: seq[DepositData] for i in 0..<64: let privKey = makeInteropPrivKey(i) deposits.add makeDeposit(defaultRuntimePreset, privKey.toPubKey(), privKey) - attachMerkleProofs(deposits) const genesis_time = 1570500000 var diff --git a/tests/test_ssz_merkleization.nim b/tests/test_ssz_merkleization.nim index d38fba32f..58b488ba6 100644 --- a/tests/test_ssz_merkleization.nim +++ b/tests/test_ssz_merkleization.nim @@ -215,6 +215,18 @@ for prelude in [0, 1, 2, 5, 6, 12, 13, 16]: testMultiProofsGeneration(prelude, proofs, followUpHashes, 128) testMultiProofsGeneration(prelude, proofs, followUpHashes, 5000) +iterator hash_tree_roots_prefix[T](lst: openArray[T], + limit: static Limit): Eth2Digest = + # This is a particular type's instantiation of a general fold, reduce, + # accumulation, prefix sums, etc family of operations. As long as that + # Eth1 deposit case is the only notable example -- the usual uses of a + # list involve, at some point, tree-hashing it -- finalized hashes are + # the only abstraction that escapes from this module this way. + var merkleizer = createMerkleizer(limit) + for i, elem in lst: + merkleizer.addChunk(hash_tree_root(elem).data) + yield mixInLength(merkleizer.getFinalHash(), i + 1) + func attachMerkleProofsReferenceImpl(deposits: var openArray[Deposit]) = let deposit_data_roots = mapIt(deposits, it.data.hash_tree_root) diff --git a/tests/testblockutil.nim b/tests/testblockutil.nim index df18ac2e1..051987bc7 100644 --- a/tests/testblockutil.nim +++ b/tests/testblockutil.nim @@ -34,7 +34,7 @@ func hackPrivKey*(v: Validator): ValidatorPrivKey = let i = int(uint64.fromBytesLE(bytes)) makeFakeValidatorPrivKey(i) -func makeDeposit(i: int, flags: UpdateFlags): Deposit = +func makeDeposit(i: int, flags: UpdateFlags): DepositData = ## Ugly hack for now: we stick the private key in withdrawal_credentials ## which means we can repro private key and randao reveal from this data, ## for testing :) @@ -43,31 +43,20 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit = pubkey = privkey.toPubKey() withdrawal_credentials = makeFakeHash(i) - result = Deposit( - data: DepositData( - pubkey: pubkey, - withdrawal_credentials: withdrawal_credentials, - amount: MAX_EFFECTIVE_BALANCE, - ) - ) + result = DepositData( + pubkey: pubkey, + withdrawal_credentials: withdrawal_credentials, + amount: MAX_EFFECTIVE_BALANCE) if skipBLSValidation notin flags: - result.data.signature = get_deposit_signature( - defaultRuntimePreset, result.data, privkey) + result.signature = get_deposit_signature( + defaultRuntimePreset, result, privkey) proc makeInitialDeposits*( - n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[Deposit] = + n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}): seq[DepositData] = for i in 0..