From 87052eba4e71f08a3630ade7b1f05b37ff3c8563 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 31 Jan 2024 03:18:55 +0000 Subject: [PATCH] implement getBlindedBlock REST API (#5829) --- AllTests-mainnet.md | 9 +- beacon_chain/rpc/rest_beacon_api.nim | 44 ++++- .../eth2_apis/eth2_rest_serialization.nim | 28 +++ beacon_chain/spec/forks.nim | 7 +- beacon_chain/spec/mev/bellatrix_mev.nim | 64 +++++- beacon_chain/spec/mev/capella_mev.nim | 41 ++++ beacon_chain/spec/mev/deneb_mev.nim | 42 ++++ ncli/resttest-rules.json | 184 ++++++++++++++++++ tests/all_tests.nim | 1 + tests/test_toblindedblock.nim | 131 +++++++++++++ 10 files changed, 546 insertions(+), 5 deletions(-) create mode 100644 tests/test_toblindedblock.nim diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 96e71c1e0..4357e69d7 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -74,6 +74,13 @@ OK: 7/7 Fail: 0/7 Skip: 0/7 + basics OK ``` OK: 2/2 Fail: 0/2 Skip: 0/2 +## Blinded block conversions +```diff ++ Bellatrix toSignedBlindedBlock OK ++ Capella toSignedBlindedBlock OK ++ Deneb toSignedBlindedBlock OK +``` +OK: 3/3 Fail: 0/3 Skip: 0/3 ## Block pool altair processing [Preset: mainnet] ```diff + Invalid signatures [Preset: mainnet] OK @@ -979,4 +986,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 664/669 Fail: 0/669 Skip: 5/669 +OK: 667/672 Fail: 0/672 Skip: 5/672 diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 32561c271..de7aba1ae 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -14,7 +14,7 @@ import ../beacon_node, ../consensus_object_pools/[blockchain_dag, spec_cache, validator_change_pool], ../spec/[deposit_snapshots, eth2_merkleization, forks, network, validator], - ../spec/datatypes/[phase0, altair, deneb], + ../spec/mev/bellatrix_mev, ../validators/message_router_mev export rest_utils @@ -1012,6 +1012,48 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = RestApiResponse.jsonMsgResponse(BlockValidationSuccess) + # https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.2#/Beacon/getBlindedBlock + # https://github.com/ethereum/beacon-APIs/blob/v2.4.2/apis/beacon/blocks/blinded_block.yaml + router.api2(MethodGet, "/eth/v1/beacon/blinded_blocks/{block_id}") do ( + block_id: BlockIdent) -> RestApiResponse: + let + blockIdent = block_id.valueOr: + return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError, + $error) + bid = node.getBlockId(blockIdent).valueOr: + return RestApiResponse.jsonError(Http404, BlockNotFoundError) + contentType = + block: + let res = preferredContentType(jsonMediaType, + sszMediaType) + if res.isErr(): + return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) + res.get() + bdata = node.dag.getForkedBlock(bid).valueOr: + return RestApiResponse.jsonError(Http404, BlockNotFoundError) + + template respondSszOrJson( + signedMaybeBlindedBlck: auto, consensusFork: ConsensusFork): untyped = + if contentType == sszMediaType: + RestApiResponse.sszResponse( + signedMaybeBlindedBlck, + [("eth-consensus-version", consensusFork.toString())]) + elif contentType == jsonMediaType: + RestApiResponse.jsonResponseBlock( + signedMaybeBlindedBlck, + consensusFork, + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) + ) + else: + RestApiResponse.jsonError(Http500, InvalidAcceptError) + + withBlck(bdata.asSigned()): + when consensusFork <= ConsensusFork.Altair: + respondSszOrJson(forkyBlck, consensusFork) + else: + respondSszOrJson(toSignedBlindedBeaconBlock(forkyBlck), consensusFork) + # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock # https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml router.api(MethodPost, "/eth/v1/beacon/blinded_blocks") do ( diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index 2d142d000..c45079a13 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -204,6 +204,7 @@ RestJson.useDefaultSerializationFor( bellatrix.ExecutionPayload, bellatrix.ExecutionPayloadHeader, bellatrix.SignedBeaconBlock, + bellatrix_mev.BlindedBeaconBlockBody, bellatrix_mev.BlindedBeaconBlock, bellatrix_mev.SignedBlindedBeaconBlock, capella.BeaconBlock, @@ -561,6 +562,33 @@ proc jsonResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse = default RestApiResponse.response(res, Http200, "application/json") +proc jsonResponseBlock*(t: typedesc[RestApiResponse], + data: ForkySignedBlindedBeaconBlock, + consensusFork: ConsensusFork, + execOpt: Opt[bool], + finalized: bool): RestApiResponse = + let + headers = [("eth-consensus-version", consensusFork.toString())] + res = + block: + var default: seq[byte] + try: + var stream = memoryOutput() + var writer = JsonWriter[RestJson].init(stream) + writer.beginRecord() + writer.writeField("version", consensusFork.toString()) + if execOpt.isSome(): + writer.writeField("execution_optimistic", execOpt.get()) + writer.writeField("finalized", finalized) + writer.writeField("data", data) + writer.endRecord() + stream.getOutput(seq[byte]) + except SerializationError: + default + except IOError: + default + RestApiResponse.response(res, Http200, "application/json", headers = headers) + proc jsonResponseBlock*(t: typedesc[RestApiResponse], data: ForkedSignedBeaconBlock, execOpt: Opt[bool], diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index 1bd219a36..e555235fa 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -202,7 +202,9 @@ type ForkySignedBlindedBeaconBlock* = phase0.SignedBeaconBlock | altair.SignedBeaconBlock | - capella_mev.SignedBlindedBeaconBlock + bellatrix_mev.SignedBlindedBeaconBlock | + capella_mev.SignedBlindedBeaconBlock | + deneb_mev.SignedBlindedBeaconBlock ForkedSignedBlindedBeaconBlock* = object case kind*: ConsensusFork @@ -318,7 +320,8 @@ template kind*( bellatrix.TrustedBeaconBlockBody | bellatrix.SigVerifiedSignedBeaconBlock | bellatrix.MsgTrustedSignedBeaconBlock | - bellatrix.TrustedSignedBeaconBlock]): ConsensusFork = + bellatrix.TrustedSignedBeaconBlock] | + bellatrix_mev.SignedBlindedBeaconBlock): ConsensusFork = ConsensusFork.Bellatrix template kind*( diff --git a/beacon_chain/spec/mev/bellatrix_mev.nim b/beacon_chain/spec/mev/bellatrix_mev.nim index 41dedce94..e742470e8 100644 --- a/beacon_chain/spec/mev/bellatrix_mev.nim +++ b/beacon_chain/spec/mev/bellatrix_mev.nim @@ -7,11 +7,36 @@ {.push raises: [].} -from ../datatypes/base import Eth1Data +import ".."/datatypes/altair +from ".."/datatypes/bellatrix import ExecutionPayloadHeader +from ".."/eth2_merkleization import hash_tree_root type + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#blindedbeaconblockbody + BlindedBeaconBlockBody* = object + randao_reveal*: ValidatorSig + eth1_data*: Eth1Data + graffiti*: GraffitiBytes + proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS] + attester_slashings*: List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS] + attestations*: List[Attestation, Limit MAX_ATTESTATIONS] + deposits*: List[Deposit, Limit MAX_DEPOSITS] + voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] + sync_aggregate*: SyncAggregate + execution_payload_header*: bellatrix.ExecutionPayloadHeader + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#blindedbeaconblock BlindedBeaconBlock* = object + slot*: Slot + proposer_index*: uint64 + parent_root*: Eth2Digest + state_root*: Eth2Digest + body*: BlindedBeaconBlockBody + + # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#signedblindedbeaconblock SignedBlindedBeaconBlock* = object + message*: BlindedBeaconBlock + signature*: ValidatorSig func shortLog*(v: BlindedBeaconBlock): auto = ( @@ -40,3 +65,40 @@ func shortLog*(v: SignedBlindedBeaconBlock): auto = blck: shortLog(default(BlindedBeaconBlock)), signature: "" ) + +func toSignedBlindedBeaconBlock*(blck: bellatrix.SignedBeaconBlock): + SignedBlindedBeaconBlock = + SignedBlindedBeaconBlock( + message: BlindedBeaconBlock( + slot: blck.message.slot, + proposer_index: blck.message.proposer_index, + parent_root: blck.message.parent_root, + state_root: blck.message.state_root, + body: BlindedBeaconBlockBody( + randao_reveal: blck.message.body.randao_reveal, + eth1_data: blck.message.body.eth1_data, + graffiti: blck.message.body.graffiti, + proposer_slashings: blck.message.body.proposer_slashings, + attester_slashings: blck.message.body.attester_slashings, + attestations: blck.message.body.attestations, + deposits: blck.message.body.deposits, + voluntary_exits: blck.message.body.voluntary_exits, + sync_aggregate: blck.message.body.sync_aggregate, + execution_payload_header: ExecutionPayloadHeader( + parent_hash: blck.message.body.execution_payload.parent_hash, + fee_recipient: blck.message.body.execution_payload.fee_recipient, + state_root: blck.message.body.execution_payload.state_root, + receipts_root: blck.message.body.execution_payload.receipts_root, + logs_bloom: blck.message.body.execution_payload.logs_bloom, + prev_randao: blck.message.body.execution_payload.prev_randao, + block_number: blck.message.body.execution_payload.block_number, + gas_limit: blck.message.body.execution_payload.gas_limit, + gas_used: blck.message.body.execution_payload.gas_used, + timestamp: blck.message.body.execution_payload.timestamp, + extra_data: blck.message.body.execution_payload.extra_data, + base_fee_per_gas: + blck.message.body.execution_payload.base_fee_per_gas, + block_hash: blck.message.body.execution_payload.block_hash, + transactions_root: + hash_tree_root(blck.message.body.execution_payload.transactions)))), + signature: blck.signature) diff --git a/beacon_chain/spec/mev/capella_mev.nim b/beacon_chain/spec/mev/capella_mev.nim index 90ab220af..6f4f790b0 100644 --- a/beacon_chain/spec/mev/capella_mev.nim +++ b/beacon_chain/spec/mev/capella_mev.nim @@ -11,6 +11,7 @@ import ".."/datatypes/[altair, capella] from stew/byteutils import to0xHex from ../datatypes/bellatrix import ExecutionAddress +from ../eth2_merkleization import hash_tree_root type # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#validatorregistrationv1 @@ -116,3 +117,43 @@ func shortLog*(v: SignedBlindedBeaconBlock): auto = blck: shortLog(v.message), signature: shortLog(v.signature) ) + +func toSignedBlindedBeaconBlock*(blck: capella.SignedBeaconBlock): + SignedBlindedBeaconBlock = + SignedBlindedBeaconBlock( + message: BlindedBeaconBlock( + slot: blck.message.slot, + proposer_index: blck.message.proposer_index, + parent_root: blck.message.parent_root, + state_root: blck.message.state_root, + body: BlindedBeaconBlockBody( + randao_reveal: blck.message.body.randao_reveal, + eth1_data: blck.message.body.eth1_data, + graffiti: blck.message.body.graffiti, + proposer_slashings: blck.message.body.proposer_slashings, + attester_slashings: blck.message.body.attester_slashings, + attestations: blck.message.body.attestations, + deposits: blck.message.body.deposits, + voluntary_exits: blck.message.body.voluntary_exits, + sync_aggregate: blck.message.body.sync_aggregate, + execution_payload_header: ExecutionPayloadHeader( + parent_hash: blck.message.body.execution_payload.parent_hash, + fee_recipient: blck.message.body.execution_payload.fee_recipient, + state_root: blck.message.body.execution_payload.state_root, + receipts_root: blck.message.body.execution_payload.receipts_root, + logs_bloom: blck.message.body.execution_payload.logs_bloom, + prev_randao: blck.message.body.execution_payload.prev_randao, + block_number: blck.message.body.execution_payload.block_number, + gas_limit: blck.message.body.execution_payload.gas_limit, + gas_used: blck.message.body.execution_payload.gas_used, + timestamp: blck.message.body.execution_payload.timestamp, + extra_data: blck.message.body.execution_payload.extra_data, + base_fee_per_gas: + blck.message.body.execution_payload.base_fee_per_gas, + block_hash: blck.message.body.execution_payload.block_hash, + transactions_root: + hash_tree_root(blck.message.body.execution_payload.transactions), + withdrawals_root: + hash_tree_root(blck.message.body.execution_payload.withdrawals)), + bls_to_execution_changes: blck.message.body.bls_to_execution_changes)), + signature: blck.signature) diff --git a/beacon_chain/spec/mev/deneb_mev.nim b/beacon_chain/spec/mev/deneb_mev.nim index f3b470a18..0f6ca22db 100644 --- a/beacon_chain/spec/mev/deneb_mev.nim +++ b/beacon_chain/spec/mev/deneb_mev.nim @@ -11,6 +11,7 @@ import ".."/datatypes/[altair, deneb] from stew/byteutils import to0xHex from ".."/datatypes/capella import SignedBLSToExecutionChange +from ".."/eth2_merkleization import hash_tree_root type # https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/deneb/builder.md#builderbid @@ -103,3 +104,44 @@ func shortLog*(v: SignedBlindedBeaconBlock): auto = blck: shortLog(v.message), signature: shortLog(v.signature) ) + +func toSignedBlindedBeaconBlock*(blck: deneb.SignedBeaconBlock): + SignedBlindedBeaconBlock = + SignedBlindedBeaconBlock( + message: BlindedBeaconBlock( + slot: blck.message.slot, + proposer_index: blck.message.proposer_index, + parent_root: blck.message.parent_root, + state_root: blck.message.state_root, + body: BlindedBeaconBlockBody( + randao_reveal: blck.message.body.randao_reveal, + eth1_data: blck.message.body.eth1_data, + graffiti: blck.message.body.graffiti, + proposer_slashings: blck.message.body.proposer_slashings, + attester_slashings: blck.message.body.attester_slashings, + attestations: blck.message.body.attestations, + deposits: blck.message.body.deposits, + voluntary_exits: blck.message.body.voluntary_exits, + sync_aggregate: blck.message.body.sync_aggregate, + execution_payload_header: ExecutionPayloadHeader( + parent_hash: blck.message.body.execution_payload.parent_hash, + fee_recipient: blck.message.body.execution_payload.fee_recipient, + state_root: blck.message.body.execution_payload.state_root, + receipts_root: blck.message.body.execution_payload.receipts_root, + logs_bloom: blck.message.body.execution_payload.logs_bloom, + prev_randao: blck.message.body.execution_payload.prev_randao, + block_number: blck.message.body.execution_payload.block_number, + gas_limit: blck.message.body.execution_payload.gas_limit, + gas_used: blck.message.body.execution_payload.gas_used, + timestamp: blck.message.body.execution_payload.timestamp, + extra_data: blck.message.body.execution_payload.extra_data, + base_fee_per_gas: + blck.message.body.execution_payload.base_fee_per_gas, + block_hash: blck.message.body.execution_payload.block_hash, + transactions_root: + hash_tree_root(blck.message.body.execution_payload.transactions), + withdrawals_root: + hash_tree_root(blck.message.body.execution_payload.withdrawals)), + bls_to_execution_changes: blck.message.body.bls_to_execution_changes, + blob_kzg_commitments: blck.message.body.blob_kzg_commitments)), + signature: blck.signature) diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index 772068f10..42c1b375d 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -3369,6 +3369,190 @@ }, "response": {"status": {"operator": "equals", "value": "400"}} }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/head", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "value": ["phase0", "altair", "bellatrix"], "operator": "oneof"} + ], + "body": [{"operator": "jstructcmpns", "value": {"version": "", "data": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/genesis", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "value": ["phase0", "altair", "bellatrix"], "operator": "oneof"} + ], + "body": [{"operator": "jstructcmpns", "value": {"version": "", "data": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/finalized", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "value": ["phase0", "altair", "bellatrix"], "operator": "oneof"} + ], + "body": [{"operator": "jstructcmpns", "value": {"version": "", "data": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "value": ["phase0", "altair", "bellatrix"], "operator": "oneof"} + ], + "body": [{"operator": "jstructcmpns", "value": {"version": "", "data": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "operator": "notexists"} + ], + "body": [{"operator": "jstructcmpns", "value": {"code": 404, "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "operator": "notexists"} + ], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "operator": "notexists"} + ], + "body": [{"operator": "jstructcmpns", "value": {"code": 404, "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [ + {"key": "Content-Type", "value": "application/json", "operator": "equals"}, + {"key": "Eth-Consensus-Version", "operator": "notexists"} + ], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/heat", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/geneziz", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/finalised", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/foobar", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0x", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0x0", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0x00", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blindedblocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blinded_blocks/0x000000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, { "topics": ["beacon", "beacon_block_blinded_blocks"], "request": { diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 7f14fad7b..99e068ac6 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -44,6 +44,7 @@ import # Unit test ./test_statediff, ./test_sync_committee_pool, ./test_sync_manager, + ./test_toblindedblock, ./test_validator_change_pool, ./test_validator_pool, ./test_zero_signature, diff --git a/tests/test_toblindedblock.nim b/tests/test_toblindedblock.nim new file mode 100644 index 000000000..566c50d38 --- /dev/null +++ b/tests/test_toblindedblock.nim @@ -0,0 +1,131 @@ +# beacon_chain +# Copyright (c) 2024 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. + +{.used.} + +import + # Beacon chain internals + ../beacon_chain/spec/helpers, + ../beacon_chain/spec/datatypes/[bellatrix, capella], + ../beacon_chain/spec/mev/[bellatrix_mev, capella_mev, deneb_mev], + # Test utilities + unittest2 + + +template do_check() = + check: + hash_tree_root(b.message) == hash_tree_root( + b.toSignedBlindedBeaconBlock.message) + b.signature == b.toSignedBlindedBeaconBlock.signature + +const + nondefaultEth1Data = Eth1Data( + deposit_root: Eth2Digest.fromHex( + "0x55aaf2ee893f67db190d617070bd10d1583b00194fbcfda03d89baa24626f5bb"), + deposit_count: 1, + block_hash: Eth2Digest.fromHex( + "0xe617d58db390a10741ab7d3de0ba9460b5df5e0772e9721fe33c0422a63b2677")) + +let nondefaultValidatorSig = ValidatorSig.fromHex( + "0xac08ca70066c6ea0525aa54dd867f82b86945818cb9305aae30f3bee13275dcf13d6d0680a47e889482ff2bb9a9f3cdb0588746f9e30c04645eda6d01bbd0ce6326ceb695294cb338ebace5b130c5b8f2e4f8efa63d63d5bb255c21a39da9c12")[] + +template bellatrix_steps() = + b.message.slot = 1.Slot + do_check + b.message.proposer_index = 1 + do_check + b.message.state_root = Eth2Digest.fromHex( + "0xb277ed302ade6685d0f0765fd0659c4b448656ab697409f2935cd9ab7189e48e") + do_check + b.message.parent_root = Eth2Digest.fromHex( + "0x2f6eaa73ec39aeb864884a2371f3e4a8abc29d277074459e46c987418f5df430") + do_check + b.message.body.randao_reveal = nondefaultValidatorSig + do_check + b.message.body.eth1_data = nondefaultEth1Data + do_check + distinctBase(b.message.body.graffiti)[0] = 1 + do_check + check: b.message.body.proposer_slashings.add(default(ProposerSlashing)) + do_check + check: b.message.body.attester_slashings.add(default(AttesterSlashing)) + do_check + check: b.message.body.attestations.add( + Attestation(aggregation_bits: CommitteeValidatorsBits.init(1))) + do_check + check: b.message.body.deposits.add(default(Deposit)) + do_check + check: b.message.body.voluntary_exits.add(default(SignedVoluntaryExit)) + do_check + b.message.body.sync_aggregate.sync_committee_signature = + nondefaultValidatorSig + do_check + b.message.body.execution_payload.parent_hash = Eth2Digest.fromHex( + "0x941bdf6ccf731a7ede6bac0c9533ecee5e3dc5081ea59d57c3fd8c624eeca85d") + do_check + b.message.body.execution_payload.fee_recipient = + ExecutionAddress.fromHex("0x1234567812345678123456781234567812345678") + do_check + b.message.body.execution_payload.state_root = Eth2Digest.fromHex( + "0x9e7d9bca96a9d0af9013ad6abb8708988beef02d58c16ba1a90075960b99c2ff") + do_check + b.message.body.execution_payload.receipts_root = Eth2Digest.fromHex( + "0x0e66a5007cf7bb16f4398adbbd01b34067a80faaef41a0a6be324c5fdb93a6df") + do_check + b.message.body.execution_payload.logs_bloom.data[0] = 2 + do_check + b.message.body.execution_payload.prev_randao = Eth2Digest.fromHex( + "0x8aa830156370e6a5ec7679d7e5ee712dd87f24fef76a1954a03c1df8c68bc0fd") + do_check + b.message.body.execution_payload.block_number = 3 + do_check + b.message.body.execution_payload.gas_limit = 4 + do_check + b.message.body.execution_payload.gas_used = 5 + do_check + b.message.body.execution_payload.timestamp = 6 + do_check + check: b.message.body.execution_payload.extra_data.add 0'u8 + do_check + b.message.body.execution_payload.base_fee_per_gas = 7.u256 + do_check + b.message.body.execution_payload.block_hash = Eth2Digest.fromHex( + "0x4b1aed517ac48bfbf6ab19846923d5256897fbc934c20ca5b8c486bfe71c6ef1") + do_check + check: b.message.body.execution_payload.transactions.add default(Transaction) + do_check + +template capella_steps() = + check: b.message.body.bls_to_execution_changes.add( + default(SignedBLSToExecutionChange)) + do_check + check: b.message.body.execution_payload.withdrawals.add(default( + Withdrawal)) + do_check + +template deneb_steps() = + check: b.message.body.blob_kzg_commitments.add(default(KzgCommitment)) + do_check + +suite "Blinded block conversions": + test "Bellatrix toSignedBlindedBlock": + var b = default(bellatrix.SignedBeaconBlock) + do_check + bellatrix_steps + + test "Capella toSignedBlindedBlock": + var b = default(capella.SignedBeaconBlock) + do_check + bellatrix_steps + capella_steps + + test "Deneb toSignedBlindedBlock": + var b = default(deneb.SignedBeaconBlock) + do_check + bellatrix_steps + capella_steps + deneb_steps