From aab53570a65194c523e5d58a7d38c23bb34deb44 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Thu, 18 Apr 2024 09:00:39 +1200 Subject: [PATCH] EIP-7002: Validator Exit contract helper and adding exits to created blocks (#6883) Signed-off-by: Lucas Saldanha Signed-off-by: Antonio Mota --- .../jsonrpc/engine/prague/genesis.json | 18 ++ .../test-cases/01_cancun_prepare_payload.json | 8 +- .../test-cases/02_cancun_getPayloadV3.json | 8 +- .../test-cases/03_cancun_newPayloadV3.json | 8 +- .../04_cancun_forkchoiceUpdatedV3.json | 8 +- .../05_prague_forkchoiceUpdatedV3.json | 10 +- .../test-cases/06_prague_getPayloadV4.json | 15 +- .../test-cases/09_prague_newPayloadV4.json | 25 ++- .../10_prague_forkchoiceUpdatedV3.json | 10 +- .../test-cases/11_prague_getPayloadV4.json | 8 +- .../test-cases/12_cancun_newPayloadV4.json | 42 ++++ ...ague_send_raw_transaction_create_exit.json | 14 ++ .../14_prague_forkchoiceUpdatedV3.json | 34 +++ .../test-cases/15_prague_getPayloadV4.json | 54 +++++ .../results/EngineGetPayloadResultV4.java | 4 +- .../engine/EngineNewPayloadV4Test.java | 5 +- .../blockcreation/AbstractBlockCreator.java | 8 +- .../besu/ethereum/core/ValidatorExit.java | 2 +- .../mainnet/AbstractBlockProcessor.java | 6 + .../mainnet/MainnetBlockBodyValidator.java | 13 ++ .../mainnet/MainnetProtocolSpecs.java | 2 +- .../PragueValidatorExitsValidator.java | 85 ++++++++ .../mainnet/ValidatorExitContractHelper.java | 146 +++++++++++++ .../mainnet/ValidatorExitsValidator.java | 38 +++- .../BlockImportExceptionHandlingTest.java | 3 + .../mainnet/AbstractBlockProcessorTest.java | 3 + .../MainnetBlockBodyValidatorTest.java | 54 ++++- .../mainnet/MainnetBlockProcessorTest.java | 2 + .../PragueValidatorExitsValidatorTest.java | 81 +++++++ .../ValidatorExitContractHelperTest.java | 197 ++++++++++++++++++ .../mainnet/ValidatorExitsValidatorTest.java | 77 +++++++ .../ValidatorExitsValidatorTestFixtures.java | 155 ++++++++++++++ .../MainnetPrecompiledContracts.java | 2 - 33 files changed, 1071 insertions(+), 74 deletions(-) create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/13_prague_send_raw_transaction_create_exit.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json create mode 100644 acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidator.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelper.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidatorTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelperTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTestFixtures.java diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/genesis.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/genesis.json index 98d388cc0f7..1c8a221858a 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/genesis.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/genesis.json @@ -32,6 +32,24 @@ "0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": { "balance": "1000000000000000000000000000" }, + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": { + "comment": "This is the account used to sign the transaction that creates a validator exit", + "balance": "1000000000000000000000000000" + }, + "0x0f1ee3e66777F27a7703400644C6fCE41527E017": { + "comment": "This is the runtime bytecode for the Validator Exit Smart Contract. It was created from the deployment transaction in EIP-7002 (https://eips.ethereum.org/EIPS/eip-7002#deployment)", + "balance": "0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603014156101325760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013257600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460ed5780604402838201600302600401805490600101805490600101549160601b8160a01c17835260601b8160a01c17826020015260601b906040015260010160a6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f546001546002828201116101205750505f610126565b01600290035b5f555f6001556044025ff35b5f5ffd", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000004": "000000000000000000000000a4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb", + "0x0000000000000000000000000000000000000000000000000000000000000005": "b10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee9922355", + "0x0000000000000000000000000000000000000000000000000000000000000006": "5d8601f0cb3bcc4ce1af9864779a416e00000000000000000000000000000000" + } + }, "0x4242424242424242424242424242424242424242": { "balance": "0", "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/01_cancun_prepare_payload.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/01_cancun_prepare_payload.json index 47063a25e4b..8ac474dea4a 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/01_cancun_prepare_payload.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/01_cancun_prepare_payload.json @@ -4,8 +4,8 @@ "method": "engine_forkchoiceUpdatedV3", "params": [ { - "headBlockHash": "0x78a301e0d846bd169889c9755c9aa4ce2972dfc4bd63de61f3303887d3e81f98", - "safeBlockHash": "0x78a301e0d846bd169889c9755c9aa4ce2972dfc4bd63de61f3303887d3e81f98", + "headBlockHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9", + "safeBlockHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9", "finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000" }, { @@ -24,10 +24,10 @@ "result": { "payloadStatus": { "status": "VALID", - "latestValidHash": "0x78a301e0d846bd169889c9755c9aa4ce2972dfc4bd63de61f3303887d3e81f98", + "latestValidHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9", "validationError": null }, - "payloadId": "0x282643d459a6f711" + "payloadId": "0x2826439412796511" } }, "statusCode": 200 diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/02_cancun_getPayloadV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/02_cancun_getPayloadV3.json index c6180edaad3..6af7c3d2f91 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/02_cancun_getPayloadV3.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/02_cancun_getPayloadV3.json @@ -3,7 +3,7 @@ "jsonrpc": "2.0", "method": "engine_getPayloadV3", "params": [ - "0x282643d459a6f711" + "0x2826439412796511" ], "id": 67 }, @@ -12,9 +12,9 @@ "id": 67, "result": { "executionPayload": { - "parentHash": "0x78a301e0d846bd169889c9755c9aa4ce2972dfc4bd63de61f3303887d3e81f98", + "parentHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9", "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "stateRoot": "0x9b8c4a9a86cb49252075c0db2f0e72fb1e49350a0f70ea36f26f700201961e62", + "stateRoot": "0xaddb5efeb344ec083c36c46c789e48e6509d82d754aaf4830ca4a51f5c904d84", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x1c9c380", @@ -29,7 +29,7 @@ "blockNumber": "0x1", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "blobGasUsed": "0x0", - "blockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315" + "blockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331" }, "blockValue": "0x0", "blobsBundle": { diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/03_cancun_newPayloadV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/03_cancun_newPayloadV3.json index b8ae8dbd89a..a5394d55b51 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/03_cancun_newPayloadV3.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/03_cancun_newPayloadV3.json @@ -4,9 +4,9 @@ "method": "engine_newPayloadV3", "params": [ { - "parentHash": "0x78a301e0d846bd169889c9755c9aa4ce2972dfc4bd63de61f3303887d3e81f98", + "parentHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9", "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "stateRoot": "0x9b8c4a9a86cb49252075c0db2f0e72fb1e49350a0f70ea36f26f700201961e62", + "stateRoot": "0xaddb5efeb344ec083c36c46c789e48e6509d82d754aaf4830ca4a51f5c904d84", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x1c9c380", @@ -17,7 +17,7 @@ "transactions": [], "withdrawals": [], "blockNumber": "0x1", - "blockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "blockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "excessBlobGas": "0x0", "blobGasUsed": "0x0" @@ -32,7 +32,7 @@ "id": 67, "result": { "status": "VALID", - "latestValidHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "validationError": null } }, diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/04_cancun_forkchoiceUpdatedV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/04_cancun_forkchoiceUpdatedV3.json index 3e7d9adcb42..f9025cc13f8 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/04_cancun_forkchoiceUpdatedV3.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/04_cancun_forkchoiceUpdatedV3.json @@ -4,9 +4,9 @@ "method": "engine_forkchoiceUpdatedV3", "params": [ { - "headBlockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", - "safeBlockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", - "finalizedBlockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315" + "headBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", + "safeBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", + "finalizedBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331" }, null ], @@ -18,7 +18,7 @@ "result": { "payloadStatus": { "status": "VALID", - "latestValidHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "validationError": null }, "payloadId": null diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/05_prague_forkchoiceUpdatedV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/05_prague_forkchoiceUpdatedV3.json index 30317597867..dd6f9edc87d 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/05_prague_forkchoiceUpdatedV3.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/05_prague_forkchoiceUpdatedV3.json @@ -4,9 +4,9 @@ "method": "engine_forkchoiceUpdatedV3", "params": [ { - "headBlockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", - "safeBlockHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", - "finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + "headBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", + "safeBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", + "finalizedBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331" }, { "timestamp": "0x20", @@ -24,10 +24,10 @@ "result": { "payloadStatus": { "status": "VALID", - "latestValidHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "validationError": null }, - "payloadId": "0x282643b909febddf" + "payloadId": "0x282643d6e5fecedf" } }, "statusCode": 200 diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json index 7405dbae026..41b58811455 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json @@ -3,7 +3,7 @@ "jsonrpc": "2.0", "method": "engine_getPayloadV4", "params": [ - "0x282643b909febddf" + "0x282643d6e5fecedf" ], "id": 67 }, @@ -12,9 +12,9 @@ "id": 67, "result": { "executionPayload": { - "parentHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "parentHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "stateRoot": "0x9b8c4a9a86cb49252075c0db2f0e72fb1e49350a0f70ea36f26f700201961e62", + "stateRoot": "0xa194d7c0cff95750c211567fba96e394faa89644a661dd1c1b75426dc90728e2", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x1c9c380", @@ -27,9 +27,14 @@ "transactions": [], "withdrawals": [], "depositReceipts": [], - "exits": [], + "exits": [ + { + "sourceAddress": "0xa4664c40aacebd82a2db79f0ea36c06bc6a19adb", + "validatorPubKey": "0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e" + } + ], "blockNumber": "0x2", - "blockHash": "0x194d190af2a85c418947fecca405eb168c832481f33f618b0c36326ba65d4767", + "blockHash": "0x8494d3fc0fd54898100ffa2bda4c3ffdfb9faa3a4c2b2c1638970721e646c35b", "blobGasUsed": "0x0", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" }, diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json index 1f63502a891..f2c3bee85f5 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json @@ -4,9 +4,9 @@ "method": "engine_newPayloadV4", "params": [ { - "parentHash": "0x1dd4f141551d53ce393845e2873754e43396101a8ebc0fd0eeb2e6798a591315", + "parentHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331", "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "stateRoot": "0x14208ac0e218167936e220b72d5d5887a963cb858ea2f2d268518f014a3da3fa", + "stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384", "logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000", "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x1c9c380", @@ -19,12 +19,23 @@ "0x02f9021c8217de808459682f008459682f0e830271009442424242424242424242424242424242424242428901bc16d674ec800000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120749715de5d1226545c6b3790f515d551a5cc5bf1d49c87a696860554d2fc4f14000000000000000000000000000000000000000000000000000000000000003096a96086cff07df17668f35f7418ef8798079167e3f4f9b72ecde17b28226137cf454ab1dd20ef5d924786ab3483c2f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef20000000000000000000000000000000000000000000000000000000000000060b1acdb2c4d3df3f1b8d3bfd33421660df358d84d78d16c4603551935f4b67643373e7eb63dcb16ec359be0ec41fee33b03a16e80745f2374ff1d3c352508ac5d857c6476d3c3bcf7e6ca37427c9209f17be3af5264c0e2132b3dd1156c28b4e9c080a09f597089338d7f44f5c59f8230bb38f243849228a8d4e9d2e2956e6050f5b2c7a076486996c7e62802b8f95eee114783e4b403fd11093ba96286ff42c595f24452" ], "withdrawals": [], - "depositReceipts" : [ - {"amount":"0x773594000","index":"0x0","pubkey":"0x96a96086cff07df17668f35f7418ef8798079167e3f4f9b72ecde17b28226137cf454ab1dd20ef5d924786ab3483c2f9","signature":"0xb1acdb2c4d3df3f1b8d3bfd33421660df358d84d78d16c4603551935f4b67643373e7eb63dcb16ec359be0ec41fee33b03a16e80745f2374ff1d3c352508ac5d857c6476d3c3bcf7e6ca37427c9209f17be3af5264c0e2132b3dd1156c28b4e9","withdrawalCredentials":"0x003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef2"} + "depositReceipts": [ + { + "amount": "0x773594000", + "index": "0x0", + "pubkey": "0x96a96086cff07df17668f35f7418ef8798079167e3f4f9b72ecde17b28226137cf454ab1dd20ef5d924786ab3483c2f9", + "signature": "0xb1acdb2c4d3df3f1b8d3bfd33421660df358d84d78d16c4603551935f4b67643373e7eb63dcb16ec359be0ec41fee33b03a16e80745f2374ff1d3c352508ac5d857c6476d3c3bcf7e6ca37427c9209f17be3af5264c0e2132b3dd1156c28b4e9", + "withdrawalCredentials": "0x003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef2" + } + ], + "exits": [ + { + "sourceAddress": "0xa4664c40aacebd82a2db79f0ea36c06bc6a19adb", + "validatorPubKey": "0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e" + } ], - "exits": [], "blockNumber": "0x2", - "blockHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", + "blockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", "receiptsRoot": "0x79ee3424eb720a3ad4b1c5a372bb8160580cbe4d893778660f34213c685627a9", "blobGasUsed": "0x0" }, @@ -38,7 +49,7 @@ "id": 67, "result": { "status": "VALID", - "latestValidHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", + "latestValidHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", "validationError": null } }, diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json index cf4554ddfdc..188503e6215 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json @@ -4,9 +4,9 @@ "method": "engine_forkchoiceUpdatedV3", "params": [ { - "headBlockHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", - "safeBlockHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", - "finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + "headBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", + "safeBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", + "finalizedBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae" }, { "timestamp": "0x30", @@ -24,10 +24,10 @@ "result": { "payloadStatus": { "status": "VALID", - "latestValidHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", + "latestValidHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", "validationError": null }, - "payloadId": "0x282643f559414ecf" + "payloadId": "0x2826439bac38c031" } }, "statusCode" : 200 diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json index 7f2a1884518..61d4e0976c8 100644 --- a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json @@ -3,7 +3,7 @@ "jsonrpc": "2.0", "method": "engine_getPayloadV4", "params": [ - "0x282643f559414ecf" + "0x2826439bac38c031" ], "id": 67 }, @@ -12,9 +12,9 @@ "id": 67, "result": { "executionPayload": { - "parentHash": "0x84e13dc50074ba4be5841bd7e453a87b6f77261a8907518a78f3de8c9d877ee7", + "parentHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "stateRoot": "0x14208ac0e218167936e220b72d5d5887a963cb858ea2f2d268518f014a3da3fa", + "stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x1c9c380", @@ -29,7 +29,7 @@ "depositReceipts": [], "exits": [], "blockNumber": "0x3", - "blockHash": "0xec4741580be2d83cde0dcd6346a67a71636d915f5da592f39d4470aecef72020", + "blockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "blobGasUsed": "0x0" }, diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json new file mode 100644 index 00000000000..9a0e933e3e3 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json @@ -0,0 +1,42 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_newPayloadV3", + "params": [ + { + "parentHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x30", + "extraData": "0x", + "baseFeePerGas": "0x7", + "transactions": [], + "withdrawals": [], + "depositReceipts": [], + "exits": [], + "blockNumber": "0x3", + "blockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "excessBlobGas": "0x0", + "blobGasUsed": "0x0" + }, + [], + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "status": "VALID", + "latestValidHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "validationError": null + } + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/13_prague_send_raw_transaction_create_exit.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/13_prague_send_raw_transaction_create_exit.json new file mode 100644 index 00000000000..28521cd657a --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/13_prague_send_raw_transaction_create_exit.json @@ -0,0 +1,14 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "eth_sendRawTransaction", + "params": ["0xf8978085e8d4a51000832dc6c0940f1ee3e66777f27a7703400644c6fce41527e01702b08706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243822fdfa01527e82d4155c70f3dc6c1df4ba26f9fb9d7cea03a402a17d630dd5465a82a9aa0378b1a45916be48d98b8ef547df0daf34f2e85037360887d954ccacdc069b222"], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": "0xf4aaedb9020f067d720daf555a4ccb6756741365defb4cd9c94c5ba39d64a5e5" + }, + "statusCode": 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json new file mode 100644 index 00000000000..1f2540d97dd --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json @@ -0,0 +1,34 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_forkchoiceUpdatedV3", + "params": [ + { + "headBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "safeBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "finalizedBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771" + }, + { + "timestamp": "0x40", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "suggestedFeeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "withdrawals": [], + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "payloadStatus": { + "status": "VALID", + "latestValidHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "validationError": null + }, + "payloadId": "0x282643bbede61941" + } + }, + "statusCode" : 200 +} \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json new file mode 100644 index 00000000000..8cb90d0c357 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json @@ -0,0 +1,54 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "engine_getPayloadV4", + "params": [ + "0x282643bbede61941" + ], + "id": 67 + }, + "response": { + "jsonrpc": "2.0", + "id": 67, + "result": { + "executionPayload": { + "parentHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771", + "feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "stateRoot": "0x99b256355fb804ab33458099469f9a2904b4b4e9171d023334b84d3f0e3a8d43", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x1c9c380", + "gasUsed": "0x145b3", + "timestamp": "0x40", + "extraData": "0x", + "baseFeePerGas": "0x7", + "excessBlobGas": "0x0", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactions": [ + "0xf8978085e8d4a51000832dc6c0940f1ee3e66777f27a7703400644c6fce41527e01702b08706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243822fdfa01527e82d4155c70f3dc6c1df4ba26f9fb9d7cea03a402a17d630dd5465a82a9aa0378b1a45916be48d98b8ef547df0daf34f2e85037360887d954ccacdc069b222" + ], + "withdrawals": [], + "depositReceipts": [], + "exits": [ + { + "sourceAddress": "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f", + "validatorPubKey": "0x8706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243" + } + ], + "receiptsRoot": "0xf2e2f11f0c553ed811be4460880996149ab3947bd0d2c1330457925a11254514", + "blobGasUsed": "0x0", + "blockHash": "0xb26d2fa98315d4d4cdcae8e5590964787b3343c11ff64eb548179687a612d467", + "blockNumber": "0x4" + }, + "blockValue": "0x12838c23cb1481b", + "blobsBundle": { + "commitments": [], + "proofs": [], + "blobs": [] + }, + "shouldOverrideBuilder": false + } + }, + "statusCode": 200, + "waitTime": 1500 +} \ No newline at end of file diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV4.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV4.java index b88049bee45..de96619853f 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV4.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV4.java @@ -132,8 +132,8 @@ public PayloadResult( this.exits = exits .map( - ds -> - ds.stream() + exit -> + exit.stream() .map(ValidatorExitParameter::fromValidatorExit) .collect(Collectors.toList())) .orElse(null); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java index 54dbc3d8e6a..4695d63dd3b 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java @@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.DepositsValidator; +import org.hyperledger.besu.ethereum.mainnet.PragueValidatorExitsValidator; import org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidator; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; @@ -196,7 +197,7 @@ public void shouldReturnValidIfExitsIsNull_WhenExitsProhibited() { @Test public void shouldReturnInvalidIfExitsIsNull_WhenExitsAllowed() { - when(protocolSpec.getExitsValidator()).thenReturn(new ValidatorExitsValidator.AllowedExits()); + when(protocolSpec.getExitsValidator()).thenReturn(new PragueValidatorExitsValidator()); var resp = resp( @@ -216,7 +217,7 @@ public void shouldReturnValidIfExitsIsNotNull_WhenExitsAllowed() { final List validatorExitsParams = List.of(VALIDATOR_EXIT_PARAMETER_1); final List validatorExits = List.of(VALIDATOR_EXIT_PARAMETER_1.toValidatorExit()); - when(protocolSpec.getExitsValidator()).thenReturn(new ValidatorExitsValidator.AllowedExits()); + when(protocolSpec.getExitsValidator()).thenReturn(new PragueValidatorExitsValidator()); BlockHeader mockHeader = setupValidPayload( diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index a1fec9475d1..aaa6dce2c4c 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper; import org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidator; import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; @@ -255,12 +256,11 @@ protected BlockCreationResult createBlock( throwIfStopped(); - // TODO implement logic to retrieve validator exits from precompile - // https://github.com/hyperledger/besu/issues/6800 final ValidatorExitsValidator exitsValidator = newProtocolSpec.getExitsValidator(); Optional> maybeExits = Optional.empty(); - if (exitsValidator instanceof ValidatorExitsValidator.AllowedExits) { - maybeExits = Optional.of(List.of()); + if (exitsValidator.allowValidatorExits()) { + maybeExits = + Optional.of(ValidatorExitContractHelper.popExitsFromQueue(disposableWorldState)); } throwIfStopped(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ValidatorExit.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ValidatorExit.java index 5ebc75a6dc8..d45bb0d465b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ValidatorExit.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ValidatorExit.java @@ -64,7 +64,7 @@ public String toString() { return "ValidatorExit{" + "sourceAddress=" + sourceAddress - + "validatorPubKey=" + + " validatorPubKey=" + validatorPubKey + '}'; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index c6cf6898eb1..339582a3da3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -197,6 +197,12 @@ public BlockProcessingResult processBlock( } } + final ValidatorExitsValidator exitsValidator = protocolSpec.getExitsValidator(); + if (exitsValidator.allowValidatorExits()) { + // Performing system-call logic + ValidatorExitContractHelper.popExitsFromQueue(worldState); + } + if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) { // no need to log, rewardCoinbase logs the error. if (worldState instanceof BonsaiWorldState) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java index abaab920666..cb26168f6a9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.core.ValidatorExit; import org.hyperledger.besu.evm.log.LogsBloomFilter; import java.util.HashSet; @@ -108,6 +109,12 @@ public boolean validateBodyLight( return false; } + if (body.getExits().isPresent()) { + if (!validateExits(block, body.getExits().get())) { + return false; + } + } + return true; } @@ -323,4 +330,10 @@ private boolean validateDeposits(final Block block, final List exits) { + final ValidatorExitsValidator exitsValidator = + protocolSchedule.getByBlockHeader(block.getHeader()).getExitsValidator(); + return exitsValidator.validateExitsInBlock(block, exits); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 0e6dcf91eeb..6483223d9ca 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -764,7 +764,7 @@ static ProtocolSpecBuilder pragueDefinition( // use prague precompiled contracts .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) .depositsValidator(new DepositsValidator.AllowedDeposits(depositContractAddress)) - .exitsValidator(new ValidatorExitsValidator.AllowedExits()) + .exitsValidator(new PragueValidatorExitsValidator()) .name("Prague"); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidator.java new file mode 100644 index 00000000000..a5fd3bb72cd --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidator.java @@ -0,0 +1,85 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.ValidatorExit; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PragueValidatorExitsValidator implements ValidatorExitsValidator { + + private static final Logger LOG = LoggerFactory.getLogger(PragueValidatorExitsValidator.class); + + @Override + public boolean allowValidatorExits() { + return true; + } + + @Override + public boolean validateValidatorExitParameter( + final Optional> validatorExits) { + return validatorExits.isPresent(); + } + + @Override + public boolean validateExitsInBlock(final Block block, final List expectedExits) { + final Hash blockHash = block.getHash(); + + if (block.getHeader().getExitsRoot().isEmpty()) { + LOG.warn("Block {} must contain exits_root", blockHash); + return false; + } + + if (block.getBody().getExits().isEmpty()) { + LOG.warn("Block {} must contain exits (even if empty list)", blockHash); + return false; + } + + final List exitsInBlock = block.getBody().getExits().get(); + // TODO Do we need to allow for customization? (e.g. if the value changes in the next fork) + if (exitsInBlock.size() > ValidatorExitContractHelper.MAX_EXITS_PER_BLOCK) { + LOG.warn("Block {} has more than the allowed maximum number of exits", blockHash); + return false; + } + + // Validate exits_root + final Hash expectedExitsRoot = BodyValidation.exitsRoot(exitsInBlock); + if (!expectedExitsRoot.equals(block.getHeader().getExitsRoot().get())) { + LOG.warn( + "Block {} exits_root does not match expected hash root for exits in block", blockHash); + return false; + } + + // Validate exits + final boolean expectedExitsMatch = expectedExits.equals(exitsInBlock); + if (!expectedExitsMatch) { + LOG.warn( + "Block {} has a mismatch between its exits and expected exits (in_block = {}, expected = {})", + blockHash, + exitsInBlock, + expectedExits); + return false; + } + + return true; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelper.java new file mode 100644 index 00000000000..52e41a641c8 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelper.java @@ -0,0 +1,146 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BLSPublicKey; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ValidatorExit; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +/** + * Helper for interacting with the Validator Exit Contract (https://eips.ethereum.org/EIPS/eip-7002) + * + *

TODO: Please note that this is not the spec-way of interacting with the Validator Exit + * contract. See https://github.com/hyperledger/besu/issues/6918 for more information. + */ +public class ValidatorExitContractHelper { + + public static final Address VALIDATOR_EXIT_ADDRESS = + Address.fromHexString("0x0f1ee3e66777F27a7703400644C6fCE41527E017"); + + @VisibleForTesting + // Storage slot to store the difference between number of exits since last block and target exits + // per block + static final UInt256 EXCESS_EXITS_STORAGE_SLOT = UInt256.valueOf(0L); + + @VisibleForTesting + // Storage slot to store the number of exits added since last block + static final UInt256 EXIT_COUNT_STORAGE_SLOT = UInt256.valueOf(1L); + + @VisibleForTesting + static final UInt256 EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT = UInt256.valueOf(2L); + + @VisibleForTesting + static final UInt256 EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT = UInt256.valueOf(3L); + + private static final UInt256 EXIT_MESSAGE_QUEUE_STORAGE_OFFSET = UInt256.valueOf(4L); + // How many slots each exit occupies in the account state + private static final int EXIT_MESSAGE_STORAGE_SLOT_SIZE = 3; + @VisibleForTesting static final int MAX_EXITS_PER_BLOCK = 16; + private static final int TARGET_EXITS_PER_BLOCK = 2; + + /* + Pop the expected list of exits from the validator exit smart contract, updating the queue pointers and other + control variables in the contract state. + */ + public static List popExitsFromQueue(final MutableWorldState mutableWorldState) { + final WorldUpdater worldUpdater = mutableWorldState.updater(); + final MutableAccount account = worldUpdater.getAccount(VALIDATOR_EXIT_ADDRESS); + if (Hash.EMPTY.equals(account.getCodeHash())) { + return List.of(); + } + + final List exits = dequeueExits(account); + updateExcessExits(account); + resetExitCount(account); + + worldUpdater.commit(); + + return exits; + } + + private static List dequeueExits(final MutableAccount account) { + final UInt256 queueHeadIndex = account.getStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT); + final UInt256 queueTailIndex = account.getStorageValue(EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT); + + final List exits = peekExpectedExits(account, queueHeadIndex, queueTailIndex); + + final UInt256 newQueueHeadIndex = queueHeadIndex.plus(exits.size()); + if (newQueueHeadIndex.equals(queueTailIndex)) { + // Queue is empty, reset queue pointers + account.setStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT, UInt256.valueOf(0L)); + account.setStorageValue(EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT, UInt256.valueOf(0L)); + } else { + account.setStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT, newQueueHeadIndex); + } + + return exits; + } + + private static List peekExpectedExits( + final Account account, final UInt256 queueHeadIndex, final UInt256 queueTailIndex) { + final long numExitsInQueue = queueTailIndex.subtract(queueHeadIndex).toLong(); + final long numExitsDequeued = Long.min(numExitsInQueue, MAX_EXITS_PER_BLOCK); + + final List exits = new ArrayList<>(); + + for (int i = 0; i < numExitsDequeued; i++) { + final UInt256 queueStorageSlot = + EXIT_MESSAGE_QUEUE_STORAGE_OFFSET.plus( + queueHeadIndex.plus(i).multiply(EXIT_MESSAGE_STORAGE_SLOT_SIZE)); + final Address sourceAddress = + Address.wrap(account.getStorageValue(queueStorageSlot).toBytes().slice(12, 20)); + final BLSPublicKey validatorPubKey = + BLSPublicKey.wrap( + Bytes.concatenate( + account + .getStorageValue(queueStorageSlot.plus(1)) + .toBytes() + .slice(0, 32), // no need to slice + account.getStorageValue(queueStorageSlot.plus(2)).toBytes().slice(0, 16))); + + exits.add(new ValidatorExit(sourceAddress, validatorPubKey)); + } + + return exits; + } + + private static void updateExcessExits(final MutableAccount account) { + final UInt256 previousExcessExits = account.getStorageValue(EXCESS_EXITS_STORAGE_SLOT); + final UInt256 exitCount = account.getStorageValue(EXIT_COUNT_STORAGE_SLOT); + + UInt256 newExcessExits = UInt256.valueOf(0L); + if (previousExcessExits.plus(exitCount).toLong() > TARGET_EXITS_PER_BLOCK) { + newExcessExits = previousExcessExits.plus(exitCount).subtract(TARGET_EXITS_PER_BLOCK); + } + + account.setStorageValue(EXCESS_EXITS_STORAGE_SLOT, newExcessExits); + } + + private static void resetExitCount(final MutableAccount account) { + account.setStorageValue(EXIT_COUNT_STORAGE_SLOT, UInt256.valueOf(0L)); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidator.java index 06bc803ba5e..762947597ba 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidator.java @@ -16,18 +16,33 @@ package org.hyperledger.besu.ethereum.mainnet; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.ValidatorExit; import java.util.List; import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public interface ValidatorExitsValidator { + boolean allowValidatorExits(); + boolean validateValidatorExitParameter(Optional> validatorExits); + boolean validateExitsInBlock(Block block, List validatorExits); + /** Used before Prague */ class ProhibitedExits implements ValidatorExitsValidator { + private static final Logger LOG = LoggerFactory.getLogger(ProhibitedExits.class); + + @Override + public boolean allowValidatorExits() { + return false; + } + /** * Before Prague we do not expect to have execution layer triggered exits, so it is expected the * optional parameter will be empty @@ -40,17 +55,22 @@ public boolean validateValidatorExitParameter( final Optional> validatorExits) { return validatorExits.isEmpty(); } - } - - /** Used after Prague */ - class AllowedExits implements ValidatorExitsValidator { @Override - public boolean validateValidatorExitParameter( - final Optional> validatorExits) { - // TODO implement any extra required validation (see - // https://github.com/hyperledger/besu/issues/6800) - return validatorExits.isPresent(); + public boolean validateExitsInBlock( + final Block block, final List validatorExits) { + final Optional> maybeExits = block.getBody().getExits(); + if (maybeExits.isPresent()) { + LOG.warn("Block {} contains exits but exits are prohibited", block.getHash()); + return false; + } + + if (block.getHeader().getExitsRoot().isPresent()) { + LOG.warn("Block {} header contains exits_root but exits are prohibited", block.getHash()); + return false; + } + + return true; } } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java index d451a6fb7b2..01d9359a6a5 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java @@ -40,6 +40,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidator; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; @@ -109,6 +110,8 @@ public void setup() { when(protocolContext.getBlockchain()).thenReturn(blockchain); when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive); when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); + when(protocolSpec.getExitsValidator()) + .thenReturn(new ValidatorExitsValidator.ProhibitedExits()); mainnetBlockValidator = new MainnetBlockValidator( blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java index 867bfeb7e5f..030db38139f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java @@ -66,6 +66,9 @@ abstract class AbstractBlockProcessorTest { @BeforeEach void baseSetup() { lenient().when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); + lenient() + .when(protocolSpec.getExitsValidator()) + .thenReturn(new ValidatorExitsValidator.ProhibitedExits()); blockProcessor = new TestBlockProcessor( transactionProcessor, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java index 8543321b470..fa97168e30e 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode.NONE; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.Address; @@ -34,6 +35,7 @@ import java.util.Optional; import org.apache.tuweni.units.bigints.UInt64; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -50,6 +52,23 @@ class MainnetBlockBodyValidatorTest { @Mock private ProtocolSpec protocolSpec; @Mock private WithdrawalsValidator withdrawalsValidator; @Mock private DepositsValidator depositsValidator; + @Mock private ValidatorExitsValidator exitsValidator; + + @BeforeEach + public void setUp() { + lenient().when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); + + lenient().when(protocolSpec.getWithdrawalsValidator()).thenReturn(withdrawalsValidator); + lenient().when(withdrawalsValidator.validateWithdrawals(any())).thenReturn(true); + lenient().when(withdrawalsValidator.validateWithdrawalsRoot(any())).thenReturn(true); + + lenient().when(protocolSpec.getDepositsValidator()).thenReturn(depositsValidator); + lenient().when(depositsValidator.validateDeposits(any(), any())).thenReturn(true); + lenient().when(depositsValidator.validateDepositsRoot(any())).thenReturn(true); + + lenient().when(protocolSpec.getExitsValidator()).thenReturn(exitsValidator); + lenient().when(exitsValidator.validateExitsInBlock(any(), any())).thenReturn(true); + } @Test void validatesWithdrawals() { @@ -67,13 +86,7 @@ void validatesWithdrawals() { .setWithdrawalsRoot(BodyValidation.withdrawalsRoot(withdrawals))); blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList()); - when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); - when(protocolSpec.getWithdrawalsValidator()).thenReturn(withdrawalsValidator); - when(protocolSpec.getDepositsValidator()).thenReturn(depositsValidator); when(withdrawalsValidator.validateWithdrawals(Optional.of(withdrawals))).thenReturn(true); - when(withdrawalsValidator.validateWithdrawalsRoot(block)).thenReturn(true); - when(depositsValidator.validateDeposits(any(), any())).thenReturn(true); - when(depositsValidator.validateDepositsRoot(block)).thenReturn(true); assertThat( new MainnetBlockBodyValidator(protocolSchedule) @@ -97,8 +110,6 @@ void validationFailsIfWithdrawalsValidationFails() { .setWithdrawalsRoot(BodyValidation.withdrawalsRoot(withdrawals))); blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList()); - when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); - when(protocolSpec.getWithdrawalsValidator()).thenReturn(withdrawalsValidator); when(withdrawalsValidator.validateWithdrawals(Optional.empty())).thenReturn(false); assertThat( @@ -123,9 +134,6 @@ void validationFailsIfWithdrawalsRootValidationFails() { .setWithdrawals(Optional.of(withdrawals))); blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList()); - when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); - when(protocolSpec.getWithdrawalsValidator()).thenReturn(withdrawalsValidator); - when(withdrawalsValidator.validateWithdrawals(Optional.of(withdrawals))).thenReturn(true); when(withdrawalsValidator.validateWithdrawalsRoot(block)).thenReturn(false); assertThat( @@ -134,4 +142,28 @@ void validationFailsIfWithdrawalsRootValidationFails() { blockchainSetupUtil.getProtocolContext(), block, emptyList(), NONE)) .isFalse(); } + + @Test + public void validationFailsIfExitsValidationFails() { + final Block block = + blockDataGenerator.block( + new BlockOptions() + .setBlockNumber(1) + .setGasUsed(0) + .hasTransactions(false) + .hasOmmers(false) + .setReceiptsRoot(BodyValidation.receiptsRoot(emptyList())) + .setLogsBloom(LogsBloomFilter.empty()) + .setParentHash(blockchainSetupUtil.getBlockchain().getChainHeadHash()) + .setExits(Optional.of(List.of()))); + blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList()); + + when(exitsValidator.validateExitsInBlock(any(), any())).thenReturn(false); + + assertThat( + new MainnetBlockBodyValidator(protocolSchedule) + .validateBodyLight( + blockchainSetupUtil.getProtocolContext(), block, emptyList(), NONE)) + .isFalse(); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java index 1b106686bfc..34fe31731d3 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java @@ -48,6 +48,8 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest { @BeforeEach public void setup() { when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); + when(protocolSpec.getExitsValidator()) + .thenReturn(new ValidatorExitsValidator.ProhibitedExits()); } @Test diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidatorTest.java new file mode 100644 index 00000000000..df888f88a89 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidatorTest.java @@ -0,0 +1,81 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsAndExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsMismatch; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsRootMismatch; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsWithoutExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithMoreThanMaximumExits; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithoutExitsAndExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithoutExitsWithExitsRoot; + +import org.hyperledger.besu.ethereum.core.ValidatorExit; +import org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.ValidateExitTestParameter; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PragueValidatorExitsValidatorTest { + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("paramsForValidateValidatorExitParameter") + public void validateValidatorExitParameter( + final String description, + final Optional> maybeExits, + final boolean expectedValidity) { + assertThat(new PragueValidatorExitsValidator().validateValidatorExitParameter(maybeExits)) + .isEqualTo(expectedValidity); + } + + private static Stream paramsForValidateValidatorExitParameter() { + return Stream.of( + Arguments.of("Allowed exits - validating empty exits", Optional.empty(), false), + Arguments.of("Allowed exits - validating present exits", Optional.of(List.of()), true)); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("validateExitsInBlockParamsForPrague") + public void validateExitsInBlock_WhenPrague( + final ValidateExitTestParameter param, final boolean expectedValidity) { + assertThat( + new PragueValidatorExitsValidator() + .validateExitsInBlock(param.block, param.expectedExits)) + .isEqualTo(expectedValidity); + } + + private static Stream validateExitsInBlockParamsForPrague() { + return Stream.of( + Arguments.of(blockWithExitsAndExitsRoot(), true), + Arguments.of(blockWithExitsWithoutExitsRoot(), false), + Arguments.of(blockWithoutExitsWithExitsRoot(), false), + Arguments.of(blockWithoutExitsAndExitsRoot(), false), + Arguments.of(blockWithExitsRootMismatch(), false), + Arguments.of(blockWithExitsMismatch(), false), + Arguments.of(blockWithMoreThanMaximumExits(), false)); + } + + @Test + public void allowExitsShouldReturnTrue() { + assertThat(new PragueValidatorExitsValidator().allowValidatorExits()).isTrue(); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelperTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelperTest.java new file mode 100644 index 00000000000..daf6c48fb95 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelperTest.java @@ -0,0 +1,197 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.EXCESS_EXITS_STORAGE_SLOT; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.EXIT_COUNT_STORAGE_SLOT; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.VALIDATOR_EXIT_ADDRESS; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BLSPublicKey; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ValidatorExit; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ValidatorExitContractHelperTest { + + private MutableWorldState worldState; + private MutableAccount contract; + + @BeforeEach + public void setUp() { + worldState = createInMemoryWorldStateArchive().getMutable(); + } + + @Test + public void popExitsFromQueue_ReadExitsCorrectly() { + final List validatorExits = List.of(createExit(), createExit(), createExit()); + loadContractStorage(worldState, validatorExits); + + final List poppedExits = + ValidatorExitContractHelper.popExitsFromQueue(worldState); + + assertThat(poppedExits).isEqualTo(validatorExits); + } + + @Test + public void popExitsFromQueue_whenContractCodeIsEmpty_ReturnsEmptyListOfExits() { + // Create account with empty code + final WorldUpdater updater = worldState.updater(); + updater.createAccount(VALIDATOR_EXIT_ADDRESS); + updater.commit(); + + assertThat(ValidatorExitContractHelper.popExitsFromQueue(worldState)).isEmpty(); + } + + @Test + public void popExitsFromQueue_WhenMoreExits_UpdatesQueuePointers() { + // Loading contract with more than 16 exits + final List validatorExits = + IntStream.range(0, 30).mapToObj(__ -> createExit()).collect(Collectors.toList()); + loadContractStorage(worldState, validatorExits); + // After loading the contract, the exit count since last block should match the size of the list + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, validatorExits.size()); + + final List poppedExits = + ValidatorExitContractHelper.popExitsFromQueue(worldState); + assertThat(poppedExits).hasSize(16); + + // Check that queue pointers were updated successfully (head advanced to index 16) + assertContractStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT, 16); + assertContractStorageValue(EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT, 30); + + // We had 30 exits in the queue, and target per block is 2, so we have 28 excess + assertContractStorageValue(EXCESS_EXITS_STORAGE_SLOT, 28); + + // We always reset the exit count after processing the queue + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0); + } + + @Test + public void popExitsFromQueue_WhenNoMoreExits_ZeroQueuePointers() { + final List validatorExits = List.of(createExit(), createExit(), createExit()); + loadContractStorage(worldState, validatorExits); + // After loading the contract, the exit count since last block should match the size of the list + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, validatorExits.size()); + + final List poppedExits = + ValidatorExitContractHelper.popExitsFromQueue(worldState); + assertThat(poppedExits).hasSize(3); + + // Check that queue pointers were updated successfully (head and tail zero because queue is + // empty) + assertContractStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT, 0); + assertContractStorageValue(EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT, 0); + + // We had 3 exits in the queue, target per block is 2, so we have 1 excess + assertContractStorageValue(EXCESS_EXITS_STORAGE_SLOT, 1); + + // We always reset the exit count after processing the queue + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0); + } + + @Test + public void popExitsFromQueue_WhenNoExits_DoesNothing() { + // Loading contract with 0 exits + loadContractStorage(worldState, List.of()); + // After loading storage, we have the exit count as zero because no exits were aded + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0); + + final List poppedExits = + ValidatorExitContractHelper.popExitsFromQueue(worldState); + assertThat(poppedExits).hasSize(0); + + // Check that queue pointers are correct (head and tail are zero) + assertContractStorageValue(EXIT_MESSAGE_QUEUE_HEAD_STORAGE_SLOT, 0); + assertContractStorageValue(EXIT_MESSAGE_QUEUE_TAIL_STORAGE_SLOT, 0); + + // We had 0 exits in the queue, and target per block is 2, so we have 0 excess + assertContractStorageValue(EXCESS_EXITS_STORAGE_SLOT, 0); + + // We always reset the exit count after processing the queue + assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0); + } + + private void assertContractStorageValue(final UInt256 slot, final int expectedValue) { + assertContractStorageValue(slot, UInt256.valueOf(expectedValue)); + } + + private void assertContractStorageValue(final UInt256 slot, final UInt256 expectedValue) { + assertThat(worldState.get(VALIDATOR_EXIT_ADDRESS).getStorageValue(slot)) + .isEqualTo(expectedValue); + } + + private void loadContractStorage( + final MutableWorldState worldState, final List exits) { + final WorldUpdater updater = worldState.updater(); + contract = updater.getOrCreate(VALIDATOR_EXIT_ADDRESS); + + contract.setCode( + Bytes.fromHexString( + "0x61013680600a5f395ff33373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603014156101325760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013257600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460ed5780604402838201600302600401805490600101805490600101549160601b8160a01c17835260601b8160a01c17826020015260601b906040015260010160a6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f546001546002828201116101205750505f610126565b01600290035b5f555f6001556044025ff35b5f5ffd")); + // excess exits + contract.setStorageValue(UInt256.valueOf(0), UInt256.valueOf(0)); + // exits count + contract.setStorageValue(UInt256.valueOf(1), UInt256.valueOf(exits.size())); + // exits queue head pointer + contract.setStorageValue(UInt256.valueOf(2), UInt256.valueOf(0)); + // exits queue tail pointer + contract.setStorageValue(UInt256.valueOf(3), UInt256.valueOf(exits.size())); + + int offset = 4; + for (int i = 0; i < exits.size(); i++) { + final ValidatorExit exit = exits.get(i); + // source_account + contract.setStorageValue( + // set account to slot, with 12 bytes padding on the left + UInt256.valueOf(offset++), + UInt256.fromBytes( + Bytes.concatenate( + Bytes.fromHexString("0x000000000000000000000000"), exit.getSourceAddress()))); + // validator_pubkey + contract.setStorageValue( + UInt256.valueOf(offset++), UInt256.fromBytes(exit.getValidatorPubKey().slice(0, 32))); + contract.setStorageValue( + // set public key to slot, with 16 bytes padding on the right + UInt256.valueOf(offset++), + UInt256.fromBytes( + Bytes.concatenate( + exit.getValidatorPubKey().slice(32, 16), + Bytes.fromHexString("0x00000000000000000000000000000000")))); + } + updater.commit(); + } + + private ValidatorExit createExit() { + return new ValidatorExit( + Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random())); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTest.java new file mode 100644 index 00000000000..d4ccf7474df --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTest.java @@ -0,0 +1,77 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsAndExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithExitsWithoutExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithoutExitsAndExitsRoot; +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.blockWithoutExitsWithExitsRoot; + +import org.hyperledger.besu.ethereum.core.ValidatorExit; +import org.hyperledger.besu.ethereum.mainnet.ValidatorExitsValidatorTestFixtures.ValidateExitTestParameter; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ValidatorExitsValidatorTest { + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("paramsForValidateValidatorExitParameter") + public void validateValidatorExitParameter( + final String description, + final Optional> maybeExits, + final boolean expectedValidity) { + assertThat( + new ValidatorExitsValidator.ProhibitedExits() + .validateValidatorExitParameter(maybeExits)) + .isEqualTo(expectedValidity); + } + + private static Stream paramsForValidateValidatorExitParameter() { + return Stream.of( + Arguments.of("Prohibited exits - validating empty exits", Optional.empty(), true), + Arguments.of("Prohibited exits - validating present exits", Optional.of(List.of()), false)); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("validateExitsInBlockParamsForProhibited") + public void validateExitsInBlock_WhenProhibited( + final ValidateExitTestParameter param, final boolean expectedValidity) { + assertThat( + new ValidatorExitsValidator.ProhibitedExits() + .validateExitsInBlock(param.block, param.expectedExits)) + .isEqualTo(expectedValidity); + } + + private static Stream validateExitsInBlockParamsForProhibited() { + return Stream.of( + Arguments.of(blockWithExitsAndExitsRoot(), false), + Arguments.of(blockWithExitsWithoutExitsRoot(), false), + Arguments.of(blockWithoutExitsWithExitsRoot(), false), + Arguments.of(blockWithoutExitsAndExitsRoot(), true)); + } + + @Test + public void allowExitsShouldReturnFalse() { + assertThat(new ValidatorExitsValidator.ProhibitedExits().allowValidatorExits()).isFalse(); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTestFixtures.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTestFixtures.java new file mode 100644 index 00000000000..f030a574163 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTestFixtures.java @@ -0,0 +1,155 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.besu.ethereum.mainnet; + +import static org.hyperledger.besu.ethereum.mainnet.ValidatorExitContractHelper.MAX_EXITS_PER_BLOCK; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BLSPublicKey; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.ValidatorExit; + +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; + +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; + +public class ValidatorExitsValidatorTestFixtures { + + private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + + static ValidateExitTestParameter blockWithExitsAndExitsRoot() { + final Optional> maybeExits = Optional.of(List.of(createExit())); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create() + .setExitsRoot(BodyValidation.exitsRoot(maybeExits.get())) + .setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter("Block with exits and exits_root", block, maybeExits); + } + + static ValidateExitTestParameter blockWithoutExitsWithExitsRoot() { + final Optional> maybeExits = Optional.empty(); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create().setExitsRoot(Hash.EMPTY).setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter( + "Block with exits_root but without exits", block, maybeExits); + } + + static ValidateExitTestParameter blockWithExitsWithoutExitsRoot() { + final Optional> maybeExits = Optional.of(List.of(createExit())); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create().setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter( + "Block with exits but without exits_root", block, maybeExits); + } + + static ValidateExitTestParameter blockWithoutExitsAndExitsRoot() { + final Optional> maybeExits = Optional.empty(); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create().setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter("Block without exits and exits_root", block, maybeExits); + } + + static ValidateExitTestParameter blockWithExitsRootMismatch() { + final Optional> maybeExits = Optional.of(List.of(createExit())); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create().setExitsRoot(Hash.EMPTY).setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter("Block with exits_root mismatch", block, maybeExits); + } + + static ValidateExitTestParameter blockWithExitsMismatch() { + final Optional> maybeExits = + Optional.of(List.of(createExit(), createExit())); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create() + .setExitsRoot(BodyValidation.exitsRoot(maybeExits.get())) + .setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter( + "Block with exits mismatch", block, maybeExits, List.of(createExit())); + } + + static ValidateExitTestParameter blockWithMoreThanMaximumExits() { + final List validatorExits = + IntStream.range(0, MAX_EXITS_PER_BLOCK + 1).mapToObj(__ -> createExit()).toList(); + final Optional> maybeExits = Optional.of(validatorExits); + + final BlockDataGenerator.BlockOptions blockOptions = + BlockDataGenerator.BlockOptions.create() + .setExitsRoot(BodyValidation.exitsRoot(maybeExits.get())) + .setExits(maybeExits); + final Block block = blockDataGenerator.block(blockOptions); + + return new ValidateExitTestParameter("Block with more than maximum exits", block, maybeExits); + } + + static ValidatorExit createExit() { + return new ValidatorExit( + Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random())); + } + + static class ValidateExitTestParameter { + + String description; + Block block; + Optional> maybeExits; + List expectedExits; + + public ValidateExitTestParameter( + final String description, + final Block block, + final Optional> maybeExits) { + this(description, block, maybeExits, maybeExits.orElseGet(List::of)); + } + + public ValidateExitTestParameter( + final String description, + final Block block, + final Optional> maybeExits, + final List expectedExits) { + this.description = description; + this.block = block; + this.maybeExits = maybeExits; + this.expectedExits = expectedExits; + } + + @Override + public String toString() { + return description; + } + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java index 4fa64db2683..3f9f567dc2d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java @@ -163,8 +163,6 @@ static void populateForPrague( populateForCancun(registry, gasCalculator); // TODO: add Prague precompiles here - // EIP-7002 - Execution layer triggerable exits - // (https://github.com/hyperledger/besu/issues/6800) } /**