From 2d3614ff69f2a620260875d8fdc460d4dcf5799d Mon Sep 17 00:00:00 2001 From: tersec Date: Thu, 17 Oct 2024 14:53:37 +0000 Subject: [PATCH] update to devnet-4-compatible engine API (#6657) --- Makefile | 4 +- beacon_chain/el/el_manager.nim | 25 +++- beacon_chain/el/engine_api_conversions.nim | 133 ++++-------------- beacon_chain/libnimbus_lc/libnimbus_lc.nim | 129 +---------------- beacon_chain/spec/datatypes/electra.nim | 1 + beacon_chain/spec/state_transition.nim | 29 ++-- beacon_chain/validators/beacon_validators.nim | 50 +++++-- research/block_sim.nim | 6 +- vendor/nim-web3 | 2 +- 9 files changed, 118 insertions(+), 261 deletions(-) diff --git a/Makefile b/Makefile index 314dd52145..a68198e53d 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,7 @@ local-testnet-minimal: --remote-validators-count 512 \ --signer-type $(SIGNER_TYPE) \ --deneb-fork-epoch 0 \ - --electra-fork-epoch 5 \ + --electra-fork-epoch 50 \ --stop-at-epoch 6 \ --disable-htop \ --enable-payload-builder \ @@ -264,7 +264,7 @@ local-testnet-mainnet: --data-dir $@ \ --nodes 2 \ --deneb-fork-epoch 0 \ - --electra-fork-epoch 5 \ + --electra-fork-epoch 50 \ --stop-at-epoch 6 \ --disable-htop \ --base-port $$(( $(MAINNET_TESTNET_BASE_PORT) + EXECUTOR_NUMBER * 400 + 0 )) \ diff --git a/beacon_chain/el/el_manager.nim b/beacon_chain/el/el_manager.nim index 940667909e..e8b59306ca 100644 --- a/beacon_chain/el/el_manager.nim +++ b/beacon_chain/el/el_manager.nim @@ -821,13 +821,15 @@ proc sendNewPayloadToSingleEL( proc sendNewPayloadToSingleEL( connection: ELConnection, - payload: engine_api.ExecutionPayloadV4, + payload: engine_api.ExecutionPayloadV3, versioned_hashes: seq[engine_api.VersionedHash], - parent_beacon_block_root: FixedBytes[32] + parent_beacon_block_root: FixedBytes[32], + executionRequests: array[3, seq[byte]] ): Future[PayloadStatusV1] {.async: (raises: [CatchableError]).} = let rpcClient = await connection.connectedRpcClient() await rpcClient.engine_newPayloadV4( - payload, versioned_hashes, Hash32 parent_beacon_block_root) + payload, versioned_hashes, Hash32 parent_beacon_block_root, + executionRequests) type StatusRelation = enum @@ -954,8 +956,18 @@ proc sendNewPayload*( let requests = m.elConnections.mapIt: let req = - when payload is engine_api.ExecutionPayloadV3 or - payload is engine_api.ExecutionPayloadV4: + when typeof(blck).kind == ConsensusFork.Electra: + # https://github.com/ethereum/execution-apis/blob/4140e528360fea53c34a766d86a000c6c039100e/src/engine/prague.md#engine_newpayloadv4 + let versioned_hashes = mapIt( + blck.body.blob_kzg_commitments, + engine_api.VersionedHash(kzg_commitment_to_versioned_hash(it))) + sendNewPayloadToSingleEL( + it, payload, versioned_hashes, + FixedBytes[32] blck.parent_root.data, + [SSZ.encode(blck.body.execution_requests.deposits), + SSZ.encode(blck.body.execution_requests.withdrawals), + SSZ.encode(blck.body.execution_requests.consolidations)]) + elif typeof(blck).kind == ConsensusFork.Deneb: # https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.1/specs/deneb/beacon-chain.md#process_execution_payload # Verify the execution payload is valid # [Modified in Deneb] Pass `versioned_hashes` to Execution Engine @@ -965,8 +977,7 @@ proc sendNewPayload*( sendNewPayloadToSingleEL( it, payload, versioned_hashes, FixedBytes[32] blck.parent_root.data) - elif payload is engine_api.ExecutionPayloadV1 or - payload is engine_api.ExecutionPayloadV2: + elif typeof(blck).kind in [ConsensusFork.Bellatrix, ConsensusFork.Capella]: sendNewPayloadToSingleEL(it, payload) else: static: doAssert false diff --git a/beacon_chain/el/engine_api_conversions.nim b/beacon_chain/el/engine_api_conversions.nim index f6972ec8c0..9438ed9cbb 100644 --- a/beacon_chain/el/engine_api_conversions.nim +++ b/beacon_chain/el/engine_api_conversions.nim @@ -129,53 +129,11 @@ func asConsensusType*(rpcExecutionPayload: ExecutionPayloadV3): blob_gas_used: rpcExecutionPayload.blobGasUsed.uint64, excess_blob_gas: rpcExecutionPayload.excessBlobGas.uint64) -func asConsensusType*(payload: engine_api.GetPayloadV3Response): - deneb.ExecutionPayloadForSigning = - deneb.ExecutionPayloadForSigning( - executionPayload: payload.executionPayload.asConsensusType, - blockValue: payload.blockValue, - # TODO - # The `mapIt` calls below are necessary only because we use different distinct - # types for KZG commitments and Blobs in the `web3` and the `deneb` spec types. - # Both are defined as `array[N, byte]` under the hood. - blobsBundle: deneb.BlobsBundle( - commitments: KzgCommitments.init( - payload.blobsBundle.commitments.mapIt( - kzg_abi.KzgCommitment(bytes: it.data))), - proofs: KzgProofs.init( - payload.blobsBundle.proofs.mapIt( - kzg_abi.KzgProof(bytes: it.data))), - blobs: Blobs.init( - payload.blobsBundle.blobs.mapIt(it.data)))) - -func asConsensusType*(rpcExecutionPayload: ExecutionPayloadV4): +func asElectraConsensusPayload(rpcExecutionPayload: ExecutionPayloadV3): electra.ExecutionPayload = template getTransaction(tt: TypedTransaction): bellatrix.Transaction = bellatrix.Transaction.init(tt.distinctBase) - template getDepositRequest( - dr: DepositRequestV1): electra.DepositRequest = - electra.DepositRequest( - pubkey: ValidatorPubKey(blob: dr.pubkey.distinctBase), - withdrawal_credentials: dr.withdrawalCredentials.asEth2Digest, - amount: dr.amount.Gwei, - signature: ValidatorSig(blob: dr.signature.distinctBase), - index: dr.index.uint64) - - template getWithdrawalRequest( - wr: WithdrawalRequestV1): electra.WithdrawalRequest = - electra.WithdrawalRequest( - source_address: ExecutionAddress(data: wr.sourceAddress.distinctBase), - validator_pubkey: ValidatorPubKey(blob: wr.validatorPubkey.distinctBase), - amount: wr.amount.Gwei) - - template getConsolidationRequest( - cr: ConsolidationRequestV1): electra.ConsolidationRequest = - electra.ConsolidationRequest( - source_address: ExecutionAddress(data: cr.sourceAddress.distinctBase), - source_pubkey: ValidatorPubKey(blob: cr.sourcePubkey.distinctBase), - target_pubkey: ValidatorPubKey(blob: cr.targetPubkey.distinctBase)) - electra.ExecutionPayload( parent_hash: rpcExecutionPayload.parentHash.asEth2Digest, feeRecipient: @@ -188,8 +146,7 @@ func asConsensusType*(rpcExecutionPayload: ExecutionPayloadV4): 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.data), + extra_data: List[byte, MAX_EXTRA_DATA_BYTES].init(rpcExecutionPayload.extraData.data), base_fee_per_gas: rpcExecutionPayload.baseFeePerGas, block_hash: rpcExecutionPayload.blockHash.asEth2Digest, transactions: List[bellatrix.Transaction, MAX_TRANSACTIONS_PER_PAYLOAD].init( @@ -199,9 +156,9 @@ func asConsensusType*(rpcExecutionPayload: ExecutionPayloadV4): blob_gas_used: rpcExecutionPayload.blobGasUsed.uint64, excess_blob_gas: rpcExecutionPayload.excessBlobGas.uint64) -func asConsensusType*(payload: engine_api.GetPayloadV4Response): - electra.ExecutionPayloadForSigning = - electra.ExecutionPayloadForSigning( +func asConsensusType*(payload: engine_api.GetPayloadV3Response): + deneb.ExecutionPayloadForSigning = + deneb.ExecutionPayloadForSigning( executionPayload: payload.executionPayload.asConsensusType, blockValue: payload.blockValue, # TODO @@ -218,6 +175,27 @@ func asConsensusType*(payload: engine_api.GetPayloadV4Response): blobs: Blobs.init( payload.blobsBundle.blobs.mapIt(it.data)))) +func asConsensusType*( + payload: engine_api.GetPayloadV4Response): + electra.ExecutionPayloadForSigning = + electra.ExecutionPayloadForSigning( + executionPayload: payload.executionPayload.asElectraConsensusPayload, + blockValue: payload.blockValue, + # TODO + # The `mapIt` calls below are necessary only because we use different distinct + # types for KZG commitments and Blobs in the `web3` and the `deneb` spec types. + # Both are defined as `array[N, byte]` under the hood. + blobsBundle: deneb.BlobsBundle( + commitments: KzgCommitments.init( + payload.blobsBundle.commitments.mapIt( + kzg_abi.KzgCommitment(bytes: it.data))), + proofs: KzgProofs.init( + payload.blobsBundle.proofs.mapIt( + kzg_abi.KzgProof(bytes: it.data))), + blobs: Blobs.init( + payload.blobsBundle.blobs.mapIt(it.data))), + executionRequests: payload.executionRequests) + func asEngineExecutionPayload*(blockBody: bellatrix.BeaconBlockBody): ExecutionPayloadV1 = template executionPayload(): untyped = blockBody.execution_payload @@ -273,7 +251,8 @@ func asEngineExecutionPayload*(blockBody: capella.BeaconBlockBody): transactions: mapIt(executionPayload.transactions, it.getTypedTransaction), withdrawals: mapIt(executionPayload.withdrawals, it.toEngineWithdrawal)) -func asEngineExecutionPayload*(blockBody: deneb.BeaconBlockBody): +func asEngineExecutionPayload*( + blockBody: deneb.BeaconBlockBody | electra.BeaconBlockBody): ExecutionPayloadV3 = template executionPayload(): untyped = blockBody.execution_payload @@ -299,59 +278,3 @@ func asEngineExecutionPayload*(blockBody: deneb.BeaconBlockBody): withdrawals: mapIt(executionPayload.withdrawals, it.asEngineWithdrawal), blobGasUsed: Quantity(executionPayload.blob_gas_used), excessBlobGas: Quantity(executionPayload.excess_blob_gas)) - -func asEngineExecutionPayload*(blockBody: electra.BeaconBlockBody): - ExecutionPayloadV4 = - template executionPayload(): untyped = blockBody.execution_payload - - template getTypedTransaction(tt: bellatrix.Transaction): TypedTransaction = - TypedTransaction(tt.distinctBase) - - template getDepositRequest( - dr: electra.DepositRequest): DepositRequestV1 = - DepositRequestV1( - pubkey: FixedBytes[RawPubKeySize](dr.pubkey.blob), - withdrawalCredentials: FixedBytes[32](dr.withdrawal_credentials.data), - amount: dr.amount.Quantity, - signature: FixedBytes[RawSigSize](dr.signature.blob), - index: dr.index.Quantity) - - template getWithdrawalRequest( - wr: electra.WithdrawalRequest): WithdrawalRequestV1 = - WithdrawalRequestV1( - sourceAddress: Address(wr.source_address.data), - validatorPubkey: FixedBytes[RawPubKeySize](wr.validator_pubkey.blob), - amount: wr.amount.Quantity) - - template getConsolidationRequest( - cr: electra.ConsolidationRequest): ConsolidationRequestV1 = - ConsolidationRequestV1( - sourceAddress: Address(cr.source_address.data), - sourcePubkey: FixedBytes[RawPubKeySize](cr.source_pubkey.blob), - targetPubkey: FixedBytes[RawPubKeySize](cr.target_pubkey.blob)) - - engine_api.ExecutionPayloadV4( - parentHash: executionPayload.parent_hash.asBlockHash, - feeRecipient: Address(executionPayload.fee_recipient.data), - stateRoot: executionPayload.state_root.asBlockHash, - receiptsRoot: executionPayload.receipts_root.asBlockHash, - logsBloom: - FixedBytes[BYTES_PER_LOGS_BLOOM](executionPayload.logs_bloom.data), - prevRandao: executionPayload.prev_randao.data.to(Bytes32), - blockNumber: Quantity(executionPayload.block_number), - gasLimit: Quantity(executionPayload.gas_limit), - gasUsed: Quantity(executionPayload.gas_used), - timestamp: Quantity(executionPayload.timestamp), - 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), - withdrawals: mapIt(executionPayload.withdrawals, it.asEngineWithdrawal), - blobGasUsed: Quantity(executionPayload.blob_gas_used), - excessBlobGas: Quantity(executionPayload.excess_blob_gas), - depositRequests: - mapIt(blockBody.execution_requests.deposits, it.getDepositRequest), - withdrawalRequests: mapIt( - blockBody.execution_requests.withdrawals, it.getWithdrawalRequest), - consolidationRequests: mapIt( - blockBody.execution_requests.consolidations, it.getConsolidationRequest)) diff --git a/beacon_chain/libnimbus_lc/libnimbus_lc.nim b/beacon_chain/libnimbus_lc/libnimbus_lc.nim index 6c306bc82a..e8f651ceac 100644 --- a/beacon_chain/libnimbus_lc/libnimbus_lc.nim +++ b/beacon_chain/libnimbus_lc/libnimbus_lc.nim @@ -1255,31 +1255,23 @@ proc ETHExecutionBlockHeaderCreateFromJson( return nil # Check fork consistency - static: doAssert totalSerializedFields(BlockObject) == 30, + static: doAssert totalSerializedFields(BlockObject) == 27, "Only update this number once code is adjusted to check new fields!" if data.baseFeePerGas.isNone and ( data.withdrawals.isSome or data.withdrawalsRoot.isSome or data.blobGasUsed.isSome or data.excessBlobGas.isSome or - data.depositRequests.isSome or data.withdrawalRequests.isSome or - data.consolidationRequests.isSome or data.requestsRoot.isSome): + data.requestsHash.isSome): return nil if data.withdrawalsRoot.isNone and ( data.blobGasUsed.isSome or data.excessBlobGas.isSome or - data.depositRequests.isSome or data.withdrawalRequests.isSome or - data.consolidationRequests.isSome or data.requestsRoot.isSome): + data.requestsHash.isSome): return nil - if data.blobGasUsed.isNone and ( - data.depositRequests.isSome or data.withdrawalRequests.isSome or - data.consolidationRequests.isSome or data.requestsRoot.isSome): + if data.blobGasUsed.isNone and data.requestsHash.isSome: return nil if data.withdrawals.isSome != data.withdrawalsRoot.isSome: return nil if data.blobGasUsed.isSome != data.excessBlobGas.isSome: return nil - if data.depositRequests.isSome != data.requestsRoot.isSome or - data.withdrawalRequests.isSome != data.requestsRoot.isSome or - data.consolidationRequests.isSome != data.requestsRoot.isSome: - return nil # Construct block header static: # `GasInt` is signed. We only use it for hashing. @@ -1325,8 +1317,8 @@ proc ETHExecutionBlockHeaderCreateFromJson( else: Opt.none(Hash32), requestsHash: - if data.requestsRoot.isSome: - Opt.some(data.requestsRoot.get.asEth2Digest.to(Hash32)) + if data.requestsHash.isSome: + Opt.some data.requestsHash.get.asEth2Digest.to(Hash32) else: Opt.none(Hash32)) if rlpHash(blockHeader) != executionHash[]: @@ -1367,119 +1359,12 @@ proc ETHExecutionBlockHeaderCreateFromJson( if tr != data.withdrawalsRoot.get.asEth2Digest: return nil - # Construct deposit requests - var depositRequests: seq[ETHDepositRequest] - if data.depositRequests.isSome: - depositRequests = newSeqOfCap[ETHDepositRequest]( - data.depositRequests.get.len) - for data in data.depositRequests.get: - # Check fork consistency - static: doAssert totalSerializedFields(DepositRequestObject) == 5, - "Only update this number once code is adjusted to check new fields!" - - # Construct deposit request - let - req = eth_types.EthDepositRequest( - pubkey: distinctBase(data.pubkey).to(Bytes48), - withdrawalCredentials: distinctBase(data.withdrawalCredentials).to(Bytes32), - amount: distinctBase(data.amount), - signature: distinctBase(data.signature).to(Bytes96), - index: distinctBase(data.index)) - rlpBytes = - try: - rlp.encode(req) - except RlpError: - raiseAssert "Unreachable" - - depositRequests.add ETHDepositRequest( - pubkey: ValidatorPubKey(blob: req.pubkey.data), - withdrawalCredentials: req.withdrawalCredentials.data, - amount: req.amount, - signature: ValidatorSig(blob: req.signature.data), - index: req.index, - bytes: rlpBytes) - - # Construct withdrawal requests - var withdrawalRequests: seq[ETHWithdrawalRequest] - if data.withdrawalRequests.isSome: - withdrawalRequests = newSeqOfCap[ETHWithdrawalRequest]( - data.withdrawalRequests.get.len) - for data in data.withdrawalRequests.get: - # Check fork consistency - static: doAssert totalSerializedFields(WithdrawalRequestObject) == 3, - "Only update this number once code is adjusted to check new fields!" - - # Construct withdrawal request - let - req = eth_types.EthWithdrawalRequest( - sourceAddress: distinctBase(data.sourceAddress).to(EthAddress), - validatorPubkey: distinctBase(data.validatorPubkey).to(Bytes48), - amount: distinctBase(data.amount)) - rlpBytes = - try: - rlp.encode(req) - except RlpError: - raiseAssert "Unreachable" - - withdrawalRequests.add ETHWithdrawalRequest( - sourceAddress: ExecutionAddress(data: req.sourceAddress.data), - validatorPubkey: ValidatorPubKey(blob: req.validatorPubkey.data), - amount: req.amount, - bytes: rlpBytes) - - # Construct consolidation requests - var consolidationRequests: seq[ETHConsolidationRequest] - if data.consolidationRequests.isSome: - consolidationRequests = newSeqOfCap[ETHConsolidationRequest]( - data.consolidationRequests.get.len) - for data in data.consolidationRequests.get: - # Check fork consistency - static: doAssert totalSerializedFields(ConsolidationRequestObject) == 3, - "Only update this number once code is adjusted to check new fields!" - - # Construct consolidation request - let - req = eth_types.EthConsolidationRequest( - sourceAddress: distinctBase(data.sourceAddress).to(EthAddress), - sourcePubkey: distinctBase(data.sourcePubkey).to(Bytes48), - targetPubkey: distinctBase(data.targetPubkey).to(Bytes48)) - rlpBytes = - try: - rlp.encode(req) - except RlpError: - raiseAssert "Unreachable" - - consolidationRequests.add ETHConsolidationRequest( - sourceAddress: ExecutionAddress(data: req.sourceAddress.data), - sourcePubkey: ValidatorPubKey(blob: req.sourcePubkey.data), - targetPubkey: ValidatorPubKey(blob: req.targetPubkey.data), - bytes: rlpBytes) - - # Verify requests hash - if data.depositRequests.isSome or - data.withdrawalRequests.isSome or - data.consolidationRequests.isSome: - doAssert data.requestsRoot.isSome # Checked above - - var b = OrderedTrieRootBuilder.init( - depositRequests.len + withdrawalRequests.len + consolidationRequests.len) - - b.add(depositRequests) - b.add(withdrawalRequests) - b.add(consolidationRequests) - - if b.rootHash() != data.requestsRoot.get.asEth2Digest: - return nil - let executionBlockHeader = ETHExecutionBlockHeader.new() executionBlockHeader[] = ETHExecutionBlockHeader( transactionsRoot: blockHeader.txRoot, withdrawalsRoot: blockHeader.withdrawalsRoot.get(zeroHash32), withdrawals: wds, - requestsHash: blockHeader.requestsHash.get(zeroHash32), - depositRequests: depositRequests, - withdrawalRequests: withdrawalRequests, - consolidationRequests: consolidationRequests) + requestsHash: blockHeader.requestsHash.get(zeroHash32)) executionBlockHeader.toUnmanagedPtr() proc ETHExecutionBlockHeaderDestroy( diff --git a/beacon_chain/spec/datatypes/electra.nim b/beacon_chain/spec/datatypes/electra.nim index 288f854deb..e3a4fac849 100644 --- a/beacon_chain/spec/datatypes/electra.nim +++ b/beacon_chain/spec/datatypes/electra.nim @@ -119,6 +119,7 @@ type executionPayload*: ExecutionPayload blockValue*: Wei blobsBundle*: BlobsBundle + executionRequests*: array[3, seq[byte]] # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/deneb/beacon-chain.md#executionpayloadheader ExecutionPayloadHeader* = object diff --git a/beacon_chain/spec/state_transition.nim b/beacon_chain/spec/state_transition.nim index 8b41699d70..0f15075038 100644 --- a/beacon_chain/spec/state_transition.nim +++ b/beacon_chain/spec/state_transition.nim @@ -361,8 +361,8 @@ func partialBeaconBlock*( deposits: seq[Deposit], validator_changes: BeaconBlockValidatorChanges, sync_aggregate: SyncAggregate, - execution_payload: ForkyExecutionPayloadForSigning -): auto = + execution_payload: ForkyExecutionPayloadForSigning, + _: ExecutionRequests): auto = const consensusFork = typeof(state).kind # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/phase0/validator.md#preparing-for-a-beaconblock @@ -411,8 +411,8 @@ func partialBeaconBlock*( deposits: seq[Deposit], validator_changes: BeaconBlockValidatorChanges, sync_aggregate: SyncAggregate, - execution_payload: ForkyExecutionPayloadForSigning -): auto = + execution_payload: ForkyExecutionPayloadForSigning, + execution_requests: ExecutionRequests): auto = const consensusFork = typeof(state).kind # https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/validator.md#preparing-for-a-beaconblock @@ -433,7 +433,8 @@ func partialBeaconBlock*( sync_aggregate: sync_aggregate, execution_payload: execution_payload.executionPayload, bls_to_execution_changes: validator_changes.bls_to_execution_changes, - blob_kzg_commitments: execution_payload.blobsBundle.commitments)) + blob_kzg_commitments: execution_payload.blobsBundle.commitments, + execution_requests: execution_requests)) proc makeBeaconBlockWithRewards*( cfg: RuntimeConfig, @@ -455,7 +456,8 @@ proc makeBeaconBlockWithRewards*( verificationFlags: UpdateFlags, transactions_root: Opt[Eth2Digest], execution_payload_root: Opt[Eth2Digest], - kzg_commitments: Opt[KzgCommitments]): + kzg_commitments: Opt[KzgCommitments], + execution_requests: ExecutionRequests): Result[tuple[blck: ForkedBeaconBlock, rewards: BlockRewards], cstring] = ## Create a block for the given state. The latest block applied to it will ## be used for the parent_root value, and the slot will be take from @@ -473,7 +475,7 @@ proc makeBeaconBlockWithRewards*( partialBeaconBlock( cfg, state.`kind Data`, proposer_index, randao_reveal, eth1_data, graffiti, attestations, deposits, validator_changes, sync_aggregate, - executionPayload)) + executionPayload, execution_requests)) let res = process_block( cfg, state.`kind Data`.data, blck.`kind Data`.asSigVerified(), @@ -516,6 +518,7 @@ proc makeBeaconBlockWithRewards*( forkyState.data.latest_execution_payload_header.transactions_root = transactions_root.get + debugComment "verify (again) that this is what builder API needs" when executionPayload is electra.ExecutionPayloadForSigning: # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#beaconblockbody forkyState.data.latest_block_header.body_root = hash_tree_root( @@ -579,14 +582,16 @@ proc makeBeaconBlock*( verificationFlags: UpdateFlags, transactions_root: Opt[Eth2Digest], execution_payload_root: Opt[Eth2Digest], - kzg_commitments: Opt[KzgCommitments]): + kzg_commitments: Opt[KzgCommitments], + execution_requests: ExecutionRequests): Result[ForkedBeaconBlock, cstring] = let blockAndRewards = ? makeBeaconBlockWithRewards( cfg, state, proposer_index, randao_reveal, eth1_data, graffiti, attestations, deposits, validator_changes, sync_aggregate, executionPayload, rollback, cache, verificationFlags, - transactions_root, execution_payload_root, kzg_commitments) + transactions_root, execution_payload_root, kzg_commitments, + execution_requests) ok(blockAndRewards.blck) proc makeBeaconBlock*( @@ -606,7 +611,8 @@ proc makeBeaconBlock*( executionPayload, rollback, cache, verificationFlags = {}, transactions_root = Opt.none Eth2Digest, execution_payload_root = Opt.none Eth2Digest, - kzg_commitments = Opt.none KzgCommitments) + kzg_commitments = Opt.none KzgCommitments, + execution_requests = default(ExecutionRequests)) proc makeBeaconBlock*( cfg: RuntimeConfig, state: var ForkedHashedBeaconState, @@ -627,4 +633,5 @@ proc makeBeaconBlock*( verificationFlags = verificationFlags, transactions_root = Opt.none Eth2Digest, execution_payload_root = Opt.none Eth2Digest, - kzg_commitments = Opt.none KzgCommitments) + kzg_commitments = Opt.none KzgCommitments, + execution_requests = default(ExecutionRequests)) diff --git a/beacon_chain/validators/beacon_validators.nim b/beacon_chain/validators/beacon_validators.nim index 06bf9dcb12..8245bdeee8 100644 --- a/beacon_chain/validators/beacon_validators.nim +++ b/beacon_chain/validators/beacon_validators.nim @@ -76,7 +76,7 @@ declarePublicGauge(attached_validator_balance_total, logScope: topics = "beacval" type - EngineBid* = object + EngineBid = object blck*: ForkedBeaconBlock executionPayloadValue*: Wei consensusBlockValue*: UInt256 @@ -457,7 +457,8 @@ proc makeBeaconBlockForHeadAndSlot*( transactions_root: Opt[Eth2Digest], execution_payload_root: Opt[Eth2Digest], withdrawals_root: Opt[Eth2Digest], - kzg_commitments: Opt[KzgCommitments]): + kzg_commitments: Opt[KzgCommitments], + execution_requests: ExecutionRequests): # TODO probably need this for builder API, otherwise remove, maybe needs to be Opt Future[ForkedBlockResult] {.async: (raises: [CancelledError]).} = # Advance state to the slot that we're proposing for var cache = StateCache() @@ -536,6 +537,26 @@ proc makeBeaconBlockForHeadAndSlot*( slot, validator_index return err("Unable to get execution payload") + # Don't use the requests passed in, TODO remove that + let execution_requests_actual = + when PayloadType.kind >= ConsensusFork.Electra: + # Don't want un-decoded SSZ going any further/deeper + try: + ExecutionRequests( + deposits: SSZ.decode( + payload.executionRequests[0], + List[DepositRequest, Limit MAX_DEPOSIT_REQUESTS_PER_PAYLOAD]), + withdrawals: SSZ.decode( + payload.executionRequests[1], + List[WithdrawalRequest, Limit MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]), + consolidations: SSZ.decode( + payload.executionRequests[2], + List[ConsolidationRequest, Limit MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD])) + except CatchableError: + return err("Unable to deserialize execution layer requests") + else: + default(ExecutionRequests) # won't be used by block builder + let res = makeBeaconBlockWithRewards( node.dag.cfg, state[], @@ -553,7 +574,8 @@ proc makeBeaconBlockForHeadAndSlot*( verificationFlags = {}, transactions_root = transactions_root, execution_payload_root = execution_payload_root, - kzg_commitments = kzg_commitments).mapErr do (error: cstring) -> string: + kzg_commitments = kzg_commitments, + execution_requests = execution_requests_actual).mapErr do (error: cstring) -> string: # This is almost certainly a bug, but it's complex enough that there's a # small risk it might happen even when most proposals succeed - thus we # log instead of asserting @@ -571,11 +593,12 @@ proc makeBeaconBlockForHeadAndSlot*( blck: res.get().blck, executionPayloadValue: payload.blockValue, consensusBlockValue: res.get().rewards.blockConsensusValue(), - blobsBundleOpt: blobsBundleOpt + blobsBundleOpt: blobsBundleOpt, )) else: err(res.error) +# TODO what is this for proc makeBeaconBlockForHeadAndSlot*( PayloadType: type ForkyExecutionPayloadForSigning, node: BeaconNode, randao_reveal: ValidatorSig, validator_index: ValidatorIndex, graffiti: GraffitiBytes, head: BlockRef, @@ -587,7 +610,8 @@ proc makeBeaconBlockForHeadAndSlot*( transactions_root = Opt.none(Eth2Digest), execution_payload_root = Opt.none(Eth2Digest), withdrawals_root = Opt.none(Eth2Digest), - kzg_commitments = Opt.none(KzgCommitments)) + kzg_commitments = Opt.none(KzgCommitments), + execution_requests = static(default(ExecutionRequests))) proc getBlindedExecutionPayload[ EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle | @@ -861,6 +885,7 @@ proc getBlindedBlockParts[ copyFields( shimExecutionPayload.executionPayload, actualEPH, getFieldNames(DenebEPH)) elif EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle: + debugComment "verify (again, after change) this is what builder API needs" type PayloadType = electra.ExecutionPayloadForSigning template actualEPH: untyped = executionPayloadHeader.get.blindedBlckPart.execution_payload_header @@ -877,13 +902,15 @@ proc getBlindedBlockParts[ else: static: doAssert false + debugComment "the electra builder API bids have these requests" let newBlock = await makeBeaconBlockForHeadAndSlot( PayloadType, node, randao, validator_index, graffiti, head, slot, execution_payload = Opt.some shimExecutionPayload, transactions_root = Opt.some actualEPH.transactions_root, execution_payload_root = Opt.some hash_tree_root(actualEPH), withdrawals_root = withdrawals_root, - kzg_commitments = kzg_commitments) + kzg_commitments = kzg_commitments, + execution_requests = default(ExecutionRequests)) if newBlock.isErr(): # Haven't committed to the MEV block, so allow EL fallback. @@ -1058,6 +1085,7 @@ proc collectBids( let payloadBuilderBidFut = if usePayloadBuilder: + # TODO apparently some capella support still here? when not (EPS is bellatrix.ExecutionPayloadForSigning): getBuilderBid[SBBB](node, payloadBuilderClient, head, validator_pubkey, slot, randao, graffitiBytes, @@ -2057,11 +2085,11 @@ proc makeMaybeBlindedBeaconBlockForHeadAndSlotImpl[ResultType]( collectedBids = await collectBids(consensusFork.SignedBlindedBeaconBlock, - consensusFork.ExecutionPayloadForSigning, - node, - payloadBuilderClient, proposerKey, - proposer, graffiti, head, slot, - randao_reveal) + consensusFork.ExecutionPayloadForSigning, + node, + payloadBuilderClient, proposerKey, + proposer, graffiti, head, slot, + randao_reveal) useBuilderBlock = if collectedBids.builderBid.isSome(): collectedBids.engineBid.isNone() or builderBetterBid( diff --git a/research/block_sim.nim b/research/block_sim.nim index 0e4785c0ec..9fcc7b636d 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -85,7 +85,8 @@ proc makeSimulationBlock( var blck = partialBeaconBlock( cfg, state, proposer_index, randao_reveal, eth1_data, graffiti, - attestations, deposits, exits, sync_aggregate, execution_payload) + attestations, deposits, exits, sync_aggregate, execution_payload, + default(ExecutionRequests)) let res = process_block( cfg, state.data, blck.asSigVerified(), verificationFlags, cache) @@ -128,7 +129,8 @@ proc makeSimulationBlock( var blck = partialBeaconBlock( cfg, state, proposer_index, randao_reveal, eth1_data, graffiti, - attestations, deposits, exits, sync_aggregate, execution_payload) + attestations, deposits, exits, sync_aggregate, execution_payload, + default(ExecutionRequests)) let res = process_block( cfg, state.data, blck.asSigVerified(), verificationFlags, cache) diff --git a/vendor/nim-web3 b/vendor/nim-web3 index c38791832c..a271022206 160000 --- a/vendor/nim-web3 +++ b/vendor/nim-web3 @@ -1 +1 @@ -Subproject commit c38791832cac2d23eab57cdc32decdd8123e5d36 +Subproject commit a2710222060de4936ca582ef677614dc4585ff12