EIP-7220: Updates (Exit -> Withdrawal Request) (#6967)

Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
pull/7029/head
Lucas Saldanha 7 months ago committed by GitHub
parent 34acf8c839
commit 9cd50e1405
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/genesis.json
  2. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/01_cancun_prepare_payload.json
  3. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/02_cancun_getPayloadV3.json
  4. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/03_cancun_newPayloadV3.json
  5. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/04_cancun_forkchoiceUpdatedV3.json
  6. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/05_prague_forkchoiceUpdatedV3.json
  7. 11
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json
  8. 11
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json
  9. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json
  10. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json
  11. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json
  12. 4
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/13_prague_send_raw_transaction_create_exit.json
  13. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json
  14. 19
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json
  15. 2
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcResponseKey.java
  16. 9
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcResponseUtils.java
  17. 27
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java
  18. 15
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/WithdrawalRequestValidatorProvider.java
  19. 13
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/EnginePayloadParameter.java
  20. 49
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameter.java
  21. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java
  22. 29
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EngineGetPayloadResultV4.java
  23. 20
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayloadTest.java
  24. 2
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4Test.java
  25. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java
  26. 53
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4Test.java
  27. 31
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameterTest.java
  28. 18
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestTestFixture.java
  29. 24
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  30. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java
  31. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Block.java
  32. 32
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockBody.java
  33. 13
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java
  34. 16
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java
  35. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SealableBlockHeader.java
  36. 36
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WithdrawalRequest.java
  37. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoder.java
  38. 13
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoder.java
  39. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
  40. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidation.java
  41. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java
  42. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  43. 85
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidator.java
  44. 94
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueWithdrawalRequestValidator.java
  45. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpec.java
  46. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  47. 146
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitContractHelper.java
  48. 76
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidator.java
  49. 186
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java
  50. 82
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidator.java
  51. 27
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java
  52. 8
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockHeaderTestFixture.java
  53. 2
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/NonBesuBlockHeader.java
  54. 6
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java
  55. 22
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoderTest.java
  56. 18
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoderTest.java
  57. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java
  58. 10
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java
  59. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java
  60. 81
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueValidatorExitsValidatorTest.java
  61. 82
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueWithdrawalRequestValidatorTest.java
  62. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidationTestUtils.java
  63. 77
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTest.java
  64. 155
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ValidatorExitsValidatorTestFixtures.java
  65. 121
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java
  66. 76
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTest.java
  67. 175
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTestFixtures.java
  68. 21
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBodiesFromPeerTask.java
  69. 9
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBodiesFromPeerTaskTest.java
  70. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ChainForTestCreator.java
  71. 8
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java
  72. 2
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java
  73. 2
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java
  74. 2
      plugin-api/build.gradle
  75. 6
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockBody.java
  76. 10
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/BlockHeader.java
  77. 18
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/WithdrawalRequest.java

@ -36,10 +36,10 @@
"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)",
"0xEd8EA01d70Cb49726175BCf2778B9C982912e017": {
"comment": "This is the runtime bytecode for the Withdrawal Request Smart Contract. It was created from the deployment transaction in EIP-7002 (https://eips.ethereum.org/EIPS/eip-7002#deployment)",
"balance": "0",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603014156101325760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013257600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460ed5780604402838201600302600401805490600101805490600101549160601b8160a01c17835260601b8160a01c17826020015260601b906040015260010160a6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f546001546002828201116101205750505f610126565b01600290035b5f555f6001556044025ff35b5f5ffd",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603814156101215760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012157600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f5460015460028282011161010f5750505f610115565b01600290035b5f555f600155604c025ff35b5f5ffd",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001",

@ -4,8 +4,8 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9",
"safeBlockHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9",
"headBlockHash": "0x292042b10484fc182be8aba2a4c1863f6712cc7f0c74489c226792138134248f",
"safeBlockHash": "0x292042b10484fc182be8aba2a4c1863f6712cc7f0c74489c226792138134248f",
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
{
@ -24,10 +24,10 @@
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9",
"latestValidHash": "0x292042b10484fc182be8aba2a4c1863f6712cc7f0c74489c226792138134248f",
"validationError": null
},
"payloadId": "0x2826439412796511"
"payloadId": "0x282643efdc841a11"
}
},
"statusCode": 200

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV3",
"params": [
"0x2826439412796511"
"0x282643efdc841a11"
],
"id": 67
},
@ -12,9 +12,9 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9",
"parentHash": "0x292042b10484fc182be8aba2a4c1863f6712cc7f0c74489c226792138134248f",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xaddb5efeb344ec083c36c46c789e48e6509d82d754aaf4830ca4a51f5c904d84",
"stateRoot": "0xf4e9cba3bdb0cf3aa214612d87f2a1ab18cdc604a0af18f71110754a85de5d15",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -29,7 +29,7 @@
"blockNumber": "0x1",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blobGasUsed": "0x0",
"blockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331"
"blockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a"
},
"blockValue": "0x0",
"blobsBundle": {

@ -4,9 +4,9 @@
"method": "engine_newPayloadV3",
"params": [
{
"parentHash": "0x4202d36ad886b24b9bb8c2451217884925577755ae053c8202ef4737134f3ae9",
"parentHash": "0x292042b10484fc182be8aba2a4c1863f6712cc7f0c74489c226792138134248f",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xaddb5efeb344ec083c36c46c789e48e6509d82d754aaf4830ca4a51f5c904d84",
"stateRoot": "0xf4e9cba3bdb0cf3aa214612d87f2a1ab18cdc604a0af18f71110754a85de5d15",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -17,7 +17,7 @@
"transactions": [],
"withdrawals": [],
"blockNumber": "0x1",
"blockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"blockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"excessBlobGas": "0x0",
"blobGasUsed": "0x0"
@ -32,7 +32,7 @@
"id": 67,
"result": {
"status": "VALID",
"latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"latestValidHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"validationError": null
}
},

@ -4,9 +4,9 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"safeBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"finalizedBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331"
"headBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"safeBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"finalizedBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a"
},
null
],
@ -18,7 +18,7 @@
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"latestValidHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"validationError": null
},
"payloadId": null

@ -4,9 +4,9 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"safeBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"finalizedBlockHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331"
"headBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"safeBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"finalizedBlockHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a"
},
{
"timestamp": "0x20",
@ -24,10 +24,10 @@
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"latestValidHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"validationError": null
},
"payloadId": "0x282643d6e5fecedf"
"payloadId": "0x282643882dfdc121"
}
},
"statusCode": 200

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV4",
"params": [
"0x282643d6e5fecedf"
"0x282643882dfdc121"
],
"id": 67
},
@ -12,9 +12,9 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"parentHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xa194d7c0cff95750c211567fba96e394faa89644a661dd1c1b75426dc90728e2",
"stateRoot": "0x9d7467981e875b5b81ec6b9ab44300fa390d253b5d83da24fe58700f66a3925e",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -27,14 +27,15 @@
"transactions": [],
"withdrawals": [],
"depositReceipts": [],
"exits": [
"withdrawalRequests": [
{
"sourceAddress": "0xa4664c40aacebd82a2db79f0ea36c06bc6a19adb",
"amount" : "0x0",
"validatorPubKey": "0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"
}
],
"blockNumber": "0x2",
"blockHash": "0x8494d3fc0fd54898100ffa2bda4c3ffdfb9faa3a4c2b2c1638970721e646c35b",
"blockHash": "0xb4be7cd1193f86d725c93275f9f9fe902c5fc34e1758b9ebcac371de370a1bdd",
"blobGasUsed": "0x0",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
},

@ -4,9 +4,9 @@
"method": "engine_newPayloadV4",
"params": [
{
"parentHash": "0xd93892197c5bd2130e6167edc292a1e92021bb9f292e6460aa889c3e2f972331",
"parentHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384",
"stateRoot": "0xdcd8284a4904f95448a522e4c47d9c2f1b4541c5f6f711b42692fb26e1e0d986",
"logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -28,14 +28,15 @@
"withdrawalCredentials": "0x003f5102dabe0a27b1746098d1dc17a5d3fbd478759fea9287e4e419b3c3cef2"
}
],
"exits": [
"withdrawalRequests": [
{
"sourceAddress": "0xa4664c40aacebd82a2db79f0ea36c06bc6a19adb",
"amount" : "0x0",
"validatorPubKey": "0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"
}
],
"blockNumber": "0x2",
"blockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"blockHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"receiptsRoot": "0x79ee3424eb720a3ad4b1c5a372bb8160580cbe4d893778660f34213c685627a9",
"blobGasUsed": "0x0"
},
@ -49,7 +50,7 @@
"id": 67,
"result": {
"status": "VALID",
"latestValidHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"latestValidHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"validationError": null
}
},

@ -4,9 +4,9 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"safeBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"finalizedBlockHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae"
"headBlockHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"safeBlockHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"finalizedBlockHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c"
},
{
"timestamp": "0x30",
@ -24,10 +24,10 @@
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"latestValidHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"validationError": null
},
"payloadId": "0x2826439bac38c031"
"payloadId": "0x282643832633dccf"
}
},
"statusCode" : 200

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV4",
"params": [
"0x2826439bac38c031"
"0x282643832633dccf"
],
"id": 67
},
@ -12,9 +12,9 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"parentHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384",
"stateRoot": "0xdcd8284a4904f95448a522e4c47d9c2f1b4541c5f6f711b42692fb26e1e0d986",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -27,9 +27,9 @@
"transactions": [],
"withdrawals": [],
"depositReceipts": [],
"exits": [],
"withdrawalRequests": [],
"blockNumber": "0x3",
"blockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"blockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blobGasUsed": "0x0"
},

@ -4,9 +4,9 @@
"method": "engine_newPayloadV3",
"params": [
{
"parentHash": "0x2b3ae3a4c482f3dab43f0606af50dc8fd3ab981ba0659d477fa96955927736ae",
"parentHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xd5d6e8c8d57e328871c5b81f078ab69e02466ab0e487c2c597effb4ffc185384",
"stateRoot": "0xdcd8284a4904f95448a522e4c47d9c2f1b4541c5f6f711b42692fb26e1e0d986",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -17,9 +17,9 @@
"transactions": [],
"withdrawals": [],
"depositReceipts": [],
"exits": [],
"withdrawalRequests": [],
"blockNumber": "0x3",
"blockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"blockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"excessBlobGas": "0x0",
"blobGasUsed": "0x0"
@ -34,7 +34,7 @@
"id": 67,
"result": {
"status": "VALID",
"latestValidHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"latestValidHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"validationError": null
}
},

@ -2,13 +2,13 @@
"request": {
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": ["0xf8978085e8d4a51000832dc6c0940f1ee3e66777f27a7703400644c6fce41527e01702b08706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243822fdfa01527e82d4155c70f3dc6c1df4ba26f9fb9d7cea03a402a17d630dd5465a82a9aa0378b1a45916be48d98b8ef547df0daf34f2e85037360887d954ccacdc069b222"],
"params": ["0xf8a08085e8d4a51000832dc6c094ed8ea01d70cb49726175bcf2778b9c982912e01702b8388706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf2430000000000000000822fe0a008135b1be1734b1b446de871eba10dbc317437eff377a7444f2ff6c06b5e5345a01bc61e0d4dd6d8e0b1f41d843fd9e07260c8be8c664ec2fa8c364477fa021176"],
"id": 67
},
"response": {
"jsonrpc": "2.0",
"id": 67,
"result": "0xf4aaedb9020f067d720daf555a4ccb6756741365defb4cd9c94c5ba39d64a5e5"
"result": "0x764bfa879d0df4ff3962e32dfd45dc39ea18a35ccbd0dadf0bb58672537b1db2"
},
"statusCode": 200
}

@ -4,9 +4,9 @@
"method": "engine_forkchoiceUpdatedV3",
"params": [
{
"headBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"safeBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"finalizedBlockHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771"
"headBlockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"safeBlockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"finalizedBlockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b"
},
{
"timestamp": "0x40",
@ -24,10 +24,10 @@
"result": {
"payloadStatus": {
"status": "VALID",
"latestValidHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"latestValidHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"validationError": null
},
"payloadId": "0x282643bbede61941"
"payloadId": "0x282643f22efc45bf"
}
},
"statusCode" : 200

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV4",
"params": [
"0x282643bbede61941"
"0x282643f22efc45bf"
],
"id": 67
},
@ -12,35 +12,36 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0x0bd5e56ac3552719a1af061ec3f48248e817fc8ac7306d611d195ae023e9f771",
"parentHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0x99b256355fb804ab33458099469f9a2904b4b4e9171d023334b84d3f0e3a8d43",
"stateRoot": "0x4a387af05417b5767993052457ca85b2a5a172b3f809eb5cbcf17f070f398c3f",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
"gasUsed": "0x145b3",
"gasUsed": "0x145d3",
"timestamp": "0x40",
"extraData": "0x",
"baseFeePerGas": "0x7",
"excessBlobGas": "0x0",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"transactions": [
"0xf8978085e8d4a51000832dc6c0940f1ee3e66777f27a7703400644c6fce41527e01702b08706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243822fdfa01527e82d4155c70f3dc6c1df4ba26f9fb9d7cea03a402a17d630dd5465a82a9aa0378b1a45916be48d98b8ef547df0daf34f2e85037360887d954ccacdc069b222"
"0xf8a08085e8d4a51000832dc6c094ed8ea01d70cb49726175bcf2778b9c982912e01702b8388706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf2430000000000000000822fe0a008135b1be1734b1b446de871eba10dbc317437eff377a7444f2ff6c06b5e5345a01bc61e0d4dd6d8e0b1f41d843fd9e07260c8be8c664ec2fa8c364477fa021176"
],
"withdrawals": [],
"depositReceipts": [],
"exits": [
"withdrawalRequests": [
{
"sourceAddress": "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f",
"amount": "0x0",
"validatorPubKey": "0x8706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243"
}
],
"receiptsRoot": "0xf2e2f11f0c553ed811be4460880996149ab3947bd0d2c1330457925a11254514",
"receiptsRoot": "0x765bd9d63cc10fa47117d6cc0958f15e55a3bde540d4ed15d220f573fbb82cba",
"blobGasUsed": "0x0",
"blockHash": "0xb26d2fa98315d4d4cdcae8e5590964787b3343c11ff64eb548179687a612d467",
"blockHash": "0xe589c7673025f6844ffbaeddcf38d77d652669c60412e8506a2ae62ac80e9de4",
"blockNumber": "0x4"
},
"blockValue": "0x12838c23cb1481b",
"blockValue": "0x12855dcd153473b",
"blobsBundle": {
"commitments": [],
"proofs": [],

@ -39,5 +39,5 @@ public enum JsonRpcResponseKey {
BASEFEE,
WITHDRAWALS_ROOT,
DEPOSITS_ROOT,
EXITS_ROOT
WITHDRAWAL_REQUESTS_ROOT
}

@ -18,7 +18,6 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.BASEF
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.COINBASE;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.DEPOSITS_ROOT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.DIFFICULTY;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.EXITS_ROOT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.EXTRA_DATA;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.GAS_LIMIT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.GAS_USED;
@ -35,6 +34,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.TIMES
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.TOTAL_DIFFICULTY;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.TRANSACTION_ROOT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.WITHDRAWALS_ROOT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcResponseKey.WITHDRAWAL_REQUESTS_ROOT;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
@ -107,7 +107,10 @@ public class JsonRpcResponseUtils {
values.containsKey(WITHDRAWALS_ROOT) ? hash(values.get(WITHDRAWALS_ROOT)) : null;
final Hash depositsRoot =
values.containsKey(DEPOSITS_ROOT) ? hash(values.get(DEPOSITS_ROOT)) : null;
final Hash exitsRoot = values.containsKey(EXITS_ROOT) ? hash(values.get(EXITS_ROOT)) : null;
final Hash withdrawalRequestsRoot =
values.containsKey(WITHDRAWAL_REQUESTS_ROOT)
? hash(values.get(WITHDRAWAL_REQUESTS_ROOT))
: null;
final List<JsonNode> ommers = new ArrayList<>();
final BlockHeader header =
@ -133,7 +136,7 @@ public class JsonRpcResponseUtils {
null, // ToDo 4844: set with the value of excess_blob_gas field
null, // TODO 4788: set with the value of the parent beacon block root field
depositsRoot,
exitsRoot,
withdrawalRequestsRoot,
blockHeaderFunctions);
return new JsonRpcSuccessResponse(

@ -21,7 +21,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.Executi
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.DepositsValidatorProvider.getDepositsValidator;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.ValidatorExitsValidatorProvider.getValidatorExitsValidator;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.WithdrawalRequestValidatorProvider.getWithdrawalRequestValidator;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.WithdrawalsValidatorProvider.getWithdrawalsValidator;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
@ -37,8 +37,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
@ -52,8 +52,8 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
@ -170,15 +170,18 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
return new JsonRpcErrorResponse(reqId, new JsonRpcError(INVALID_PARAMS, "Invalid deposits"));
}
final Optional<List<ValidatorExit>> maybeExits =
Optional.ofNullable(blockParam.getExits())
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests =
Optional.ofNullable(blockParam.getWithdrawalRequests())
.map(
exits ->
exits.stream().map(ValidatorExitParameter::toValidatorExit).collect(toList()));
if (!getValidatorExitsValidator(
withdrawalRequest ->
withdrawalRequest.stream()
.map(WithdrawalRequestParameter::toWithdrawalRequest)
.collect(toList()));
if (!getWithdrawalRequestValidator(
protocolSchedule.get(), blockParam.getTimestamp(), blockParam.getBlockNumber())
.validateValidatorExitParameter(maybeExits)) {
return new JsonRpcErrorResponse(reqId, new JsonRpcError(INVALID_PARAMS, "Invalid exits"));
.validateWithdrawalRequestParameter(maybeWithdrawalRequests)) {
return new JsonRpcErrorResponse(
reqId, new JsonRpcError(INVALID_PARAMS, "Invalid withdrawal requests"));
}
if (mergeContext.get().isSyncing()) {
@ -249,7 +252,7 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
: BlobGas.fromHexString(blockParam.getExcessBlobGas()),
maybeParentBeaconBlockRoot.orElse(null),
maybeDeposits.map(BodyValidation::depositsRoot).orElse(null),
maybeExits.map(BodyValidation::exitsRoot).orElse(null),
maybeWithdrawalRequests.map(BodyValidation::withdrawalRequestsRoot).orElse(null),
headerFunctions);
// ensure the block hash matches the blockParam hash
@ -317,7 +320,7 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
Collections.emptyList(),
maybeWithdrawals,
maybeDeposits,
maybeExits));
maybeWithdrawalRequests));
if (maybeParentHeader.isEmpty()) {
LOG.atDebug()

@ -19,13 +19,14 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
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.mainnet.WithdrawalRequestValidator;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import java.util.Optional;
public class ValidatorExitsValidatorProvider {
public class WithdrawalRequestValidatorProvider {
static ValidatorExitsValidator getValidatorExitsValidator(
static WithdrawalRequestValidator getWithdrawalRequestValidator(
final ProtocolSchedule protocolSchedule, final long blockTimestamp, final long blockNumber) {
final BlockHeader blockHeader =
@ -33,13 +34,13 @@ public class ValidatorExitsValidatorProvider {
.timestamp(blockTimestamp)
.number(blockNumber)
.buildBlockHeader();
return getValidatorExitsValidator(protocolSchedule.getByBlockHeader(blockHeader));
return getWithdrawalRequestValidator(protocolSchedule.getByBlockHeader(blockHeader));
}
private static ValidatorExitsValidator getValidatorExitsValidator(
private static WithdrawalRequestValidator getWithdrawalRequestValidator(
final ProtocolSpec protocolSchedule) {
return Optional.ofNullable(protocolSchedule)
.map(ProtocolSpec::getExitsValidator)
.orElseGet(ValidatorExitsValidator.ProhibitedExits::new);
.map(ProtocolSpec::getWithdrawalRequestValidator)
.orElseGet(ProhibitedWithdrawalRequests::new);
}
}

@ -44,7 +44,7 @@ public class EnginePayloadParameter {
private final Long blobGasUsed;
private final String excessBlobGas;
private final List<DepositParameter> deposits;
private final List<ValidatorExitParameter> exits;
private final List<WithdrawalRequestParameter> withdrawalRequests;
/**
* Creates an instance of EnginePayloadParameter.
@ -67,7 +67,7 @@ public class EnginePayloadParameter {
* @param blobGasUsed QUANTITY, 64 Bits
* @param excessBlobGas QUANTITY, 64 Bits
* @param deposits List of deposit parameters.
* @param exits List of exits parameters.
* @param withdrawalRequestParameters List of withdrawal requests parameters.
*/
@JsonCreator
public EnginePayloadParameter(
@ -89,7 +89,8 @@ public class EnginePayloadParameter {
@JsonProperty("blobGasUsed") final UnsignedLongParameter blobGasUsed,
@JsonProperty("excessBlobGas") final String excessBlobGas,
@JsonProperty("depositReceipts") final List<DepositParameter> deposits,
@JsonProperty("exits") final List<ValidatorExitParameter> exits) {
@JsonProperty("withdrawalRequests")
final List<WithdrawalRequestParameter> withdrawalRequestParameters) {
this.blockHash = blockHash;
this.parentHash = parentHash;
this.feeRecipient = feeRecipient;
@ -108,7 +109,7 @@ public class EnginePayloadParameter {
this.blobGasUsed = blobGasUsed == null ? null : blobGasUsed.getValue();
this.excessBlobGas = excessBlobGas;
this.deposits = deposits;
this.exits = exits;
this.withdrawalRequests = withdrawalRequestParameters;
}
public Hash getBlockHash() {
@ -183,7 +184,7 @@ public class EnginePayloadParameter {
return deposits;
}
public List<ValidatorExitParameter> getExits() {
return exits;
public List<WithdrawalRequestParameter> getWithdrawalRequests() {
return withdrawalRequests;
}
}

@ -16,7 +16,8 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import java.util.Objects;
@ -25,33 +26,42 @@ import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.vertx.core.json.JsonObject;
public class ValidatorExitParameter {
public class WithdrawalRequestParameter {
private final String sourceAddress;
private final String validatorPubKey;
private final String amount;
@JsonCreator
public ValidatorExitParameter(
public WithdrawalRequestParameter(
@JsonProperty("sourceAddress") final String sourceAddress,
@JsonProperty("pubkey") final String validatorPubKey) {
@JsonProperty("pubkey") final String validatorPubKey,
@JsonProperty("amount") final String amount) {
this.sourceAddress = sourceAddress;
this.validatorPubKey = validatorPubKey;
this.amount = amount;
}
public static ValidatorExitParameter fromValidatorExit(final ValidatorExit exit) {
return new ValidatorExitParameter(
exit.getSourceAddress().toHexString(), exit.getValidatorPubKey().toHexString());
public static WithdrawalRequestParameter fromWithdrawalRequest(
final WithdrawalRequest withdrawalRequest) {
return new WithdrawalRequestParameter(
withdrawalRequest.getSourceAddress().toHexString(),
withdrawalRequest.getValidatorPubKey().toHexString(),
withdrawalRequest.getAmount().toShortHexString());
}
public ValidatorExit toValidatorExit() {
return new ValidatorExit(
Address.fromHexString(sourceAddress), BLSPublicKey.fromHexString(validatorPubKey));
public WithdrawalRequest toWithdrawalRequest() {
return new WithdrawalRequest(
Address.fromHexString(sourceAddress),
BLSPublicKey.fromHexString(validatorPubKey),
GWei.fromHexString(amount));
}
public JsonObject asJsonObject() {
return new JsonObject()
.put("sourceAddress", sourceAddress)
.put("validatorPubKey", validatorPubKey);
.put("validatorPubKey", validatorPubKey)
.put("amount", amount);
}
@JsonGetter
@ -64,29 +74,38 @@ public class ValidatorExitParameter {
return validatorPubKey;
}
@JsonGetter
public String getAmount() {
return amount;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ValidatorExitParameter that = (ValidatorExitParameter) o;
final WithdrawalRequestParameter that = (WithdrawalRequestParameter) o;
return Objects.equals(sourceAddress, that.sourceAddress)
&& Objects.equals(validatorPubKey, that.validatorPubKey);
&& Objects.equals(validatorPubKey, that.validatorPubKey)
&& Objects.equals(amount, that.amount);
}
@Override
public int hashCode() {
return Objects.hash(sourceAddress, validatorPubKey);
return Objects.hash(sourceAddress, validatorPubKey, amount);
}
@Override
public String toString() {
return "DepositParameter{"
return "WithdrawalRequestParameter{"
+ "sourceAddress='"
+ sourceAddress
+ '\''
+ ", validatorPubKey='"
+ validatorPubKey
+ '\''
+ ", amount='"
+ amount
+ '\''
+ '}';
}
}

@ -173,7 +173,7 @@ public class BlockResultFactory {
txs,
blockWithReceipts.getBlock().getBody().getWithdrawals(),
blockWithReceipts.getBlock().getBody().getDeposits(),
blockWithReceipts.getBlock().getBody().getExits(),
blockWithReceipts.getBlock().getBody().getWithdrawalRequests(),
Quantity.create(blockValue),
blobsBundleV1);
}

@ -15,12 +15,12 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestParameter;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import java.util.List;
import java.util.Optional;
@ -43,10 +43,11 @@ public class EngineGetPayloadResultV4 {
final List<String> transactions,
final Optional<List<Withdrawal>> withdrawals,
final Optional<List<Deposit>> deposits,
final Optional<List<ValidatorExit>> exits,
final Optional<List<WithdrawalRequest>> withdrawalRequests,
final String blockValue,
final BlobsBundleV1 blobsBundle) {
this.executionPayload = new PayloadResult(header, transactions, withdrawals, deposits, exits);
this.executionPayload =
new PayloadResult(header, transactions, withdrawals, deposits, withdrawalRequests);
this.blockValue = blockValue;
this.blobsBundle = blobsBundle;
this.shouldOverrideBuilder = false;
@ -94,14 +95,14 @@ public class EngineGetPayloadResultV4 {
protected final List<String> transactions;
private final List<WithdrawalParameter> withdrawals;
private final List<DepositParameter> deposits;
private final List<ValidatorExitParameter> exits;
private final List<WithdrawalRequestParameter> withdrawalRequests;
public PayloadResult(
final BlockHeader header,
final List<String> transactions,
final Optional<List<Withdrawal>> withdrawals,
final Optional<List<Deposit>> deposits,
final Optional<List<ValidatorExit>> exits) {
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
this.blockNumber = Quantity.create(header.getNumber());
this.blockHash = header.getHash().toString();
this.parentHash = header.getParentHash().toString();
@ -129,12 +130,12 @@ public class EngineGetPayloadResultV4 {
.map(
ds -> ds.stream().map(DepositParameter::fromDeposit).collect(Collectors.toList()))
.orElse(null);
this.exits =
exits
this.withdrawalRequests =
withdrawalRequests
.map(
exit ->
exit.stream()
.map(ValidatorExitParameter::fromValidatorExit)
wr ->
wr.stream()
.map(WithdrawalRequestParameter::fromWithdrawalRequest)
.collect(Collectors.toList()))
.orElse(null);
this.blobGasUsed = header.getBlobGasUsed().map(Quantity::create).orElse(Quantity.HEX_ZERO);
@ -219,9 +220,9 @@ public class EngineGetPayloadResultV4 {
return deposits;
}
@JsonGetter(value = "exits")
public List<ValidatorExitParameter> getExits() {
return exits;
@JsonGetter(value = "withdrawalRequests")
public List<WithdrawalRequestParameter> getWithdrawalRequests() {
return withdrawalRequests;
}
@JsonGetter(value = "feeRecipient")

@ -41,8 +41,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngin
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.UnsignedLongParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
@ -55,8 +55,8 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.DepositsValidator;
@ -411,7 +411,7 @@ public abstract class AbstractEngineNewPayloadTest extends AbstractScheduledApiT
final List<String> txs,
final List<WithdrawalParameter> withdrawals,
final List<DepositParameter> deposits,
final List<ValidatorExitParameter> exits) {
final List<WithdrawalRequestParameter> withdrawalRequests) {
return new EnginePayloadParameter(
header.getHash(),
header.getParentHash(),
@ -431,16 +431,17 @@ public abstract class AbstractEngineNewPayloadTest extends AbstractScheduledApiT
header.getBlobGasUsed().map(UnsignedLongParameter::new).orElse(null),
header.getExcessBlobGas().map(BlobGas::toHexString).orElse(null),
deposits,
exits);
withdrawalRequests);
}
protected BlockHeader setupValidPayload(
final BlockProcessingResult value,
final Optional<List<Withdrawal>> maybeWithdrawals,
final Optional<List<Deposit>> maybeDeposits,
final Optional<List<ValidatorExit>> maybeExits) {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests) {
BlockHeader mockHeader = createBlockHeader(maybeWithdrawals, maybeDeposits, maybeExits);
BlockHeader mockHeader =
createBlockHeader(maybeWithdrawals, maybeDeposits, maybeWithdrawalRequests);
when(blockchain.getBlockByHash(mockHeader.getHash())).thenReturn(Optional.empty());
// when(blockchain.getBlockHeader(mockHeader.getParentHash()))
// .thenReturn(Optional.of(mock(BlockHeader.class)));
@ -474,14 +475,15 @@ public abstract class AbstractEngineNewPayloadTest extends AbstractScheduledApiT
protected BlockHeader createBlockHeader(
final Optional<List<Withdrawal>> maybeWithdrawals,
final Optional<List<Deposit>> maybeDeposits,
final Optional<List<ValidatorExit>> maybeExits) {
return createBlockHeaderFixture(maybeWithdrawals, maybeDeposits, maybeExits).buildHeader();
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests) {
return createBlockHeaderFixture(maybeWithdrawals, maybeDeposits, maybeWithdrawalRequests)
.buildHeader();
}
protected BlockHeaderTestFixture createBlockHeaderFixture(
final Optional<List<Withdrawal>> maybeWithdrawals,
final Optional<List<Deposit>> maybeDeposits,
final Optional<List<ValidatorExit>> maybeExits) {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests) {
BlockHeader parentBlockHeader =
new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
return new BlockHeaderTestFixture()

@ -148,7 +148,7 @@ public class EngineGetPayloadV4Test extends AbstractEngineGetPayloadTest {
final EngineGetPayloadResultV4 res = (EngineGetPayloadResultV4) r.getResult();
assertThat(res.getExecutionPayload().getWithdrawals()).isNotNull();
assertThat(res.getExecutionPayload().getDeposits()).isNotNull();
assertThat(res.getExecutionPayload().getExits()).isNotNull();
assertThat(res.getExecutionPayload().getWithdrawalRequests()).isNotNull();
assertThat(res.getExecutionPayload().getHash())
.isEqualTo(header.getHash().toString());
assertThat(res.getBlockValue()).isEqualTo(Quantity.create(0));

@ -47,8 +47,8 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
@ -151,7 +151,7 @@ public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
protected BlockHeader createBlockHeader(
final Optional<List<Withdrawal>> maybeWithdrawals,
final Optional<List<Deposit>> maybeDeposits,
final Optional<List<ValidatorExit>> maybeExits) {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests) {
BlockHeader parentBlockHeader =
new BlockHeaderTestFixture()
.baseFeePerGas(Wei.ONE)

@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameterTestFixture.DEPOSIT_PARAM_1;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitTestFixture.VALIDATOR_EXIT_PARAMETER_1;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestTestFixture.WITHDRAWAL_REQUEST_PARAMETER_1;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
@ -34,18 +34,18 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
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.ethereum.mainnet.PragueWithdrawalRequestValidator;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import java.util.Collections;
@ -175,9 +175,9 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
}
@Test
public void shouldReturnValidIfExitsIsNull_WhenExitsProhibited() {
when(protocolSpec.getExitsValidator())
.thenReturn(new ValidatorExitsValidator.ProhibitedExits());
public void shouldReturnValidIfWithdrawalRequestsIsNull_WhenWithdrawalRequestsAreProhibited() {
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
BlockHeader mockHeader =
setupValidPayload(
@ -196,8 +196,9 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
}
@Test
public void shouldReturnInvalidIfExitsIsNull_WhenExitsAllowed() {
when(protocolSpec.getExitsValidator()).thenReturn(new PragueValidatorExitsValidator());
public void shouldReturnInvalidIfWithdrawalRequestsIsNull_WhenWithdrawalRequestsAreAllowed() {
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new PragueWithdrawalRequestValidator());
var resp =
resp(
@ -213,18 +214,20 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
}
@Test
public void shouldReturnValidIfExitsIsNotNull_WhenExitsAllowed() {
final List<ValidatorExitParameter> validatorExitsParams = List.of(VALIDATOR_EXIT_PARAMETER_1);
final List<ValidatorExit> validatorExits =
List.of(VALIDATOR_EXIT_PARAMETER_1.toValidatorExit());
when(protocolSpec.getExitsValidator()).thenReturn(new PragueValidatorExitsValidator());
public void shouldReturnValidIfWithdrawalRequestsIsNotNull_WhenWithdrawalRequestsAreAllowed() {
final List<WithdrawalRequestParameter> withdrawalRequestsParams =
List.of(WITHDRAWAL_REQUEST_PARAMETER_1);
final List<WithdrawalRequest> withdrawalRequests =
List.of(WITHDRAWAL_REQUEST_PARAMETER_1.toWithdrawalRequest());
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new PragueWithdrawalRequestValidator());
BlockHeader mockHeader =
setupValidPayload(
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
Optional.empty(),
Optional.empty(),
Optional.of(validatorExits));
Optional.of(withdrawalRequests));
when(blockchain.getBlockHeader(mockHeader.getParentHash()))
.thenReturn(Optional.of(mock(BlockHeader.class)));
when(mergeCoordinator.getLatestValidAncestor(mockHeader))
@ -232,16 +235,17 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
var resp =
resp(
mockEnginePayload(
mockHeader, Collections.emptyList(), null, null, validatorExitsParams));
mockHeader, Collections.emptyList(), null, null, withdrawalRequestsParams));
assertValidResponse(mockHeader, resp);
}
@Test
public void shouldReturnInvalidIfExitsIsNotNull_WhenExitsProhibited() {
final List<ValidatorExitParameter> validatorExits = List.of();
when(protocolSpec.getExitsValidator())
.thenReturn(new ValidatorExitsValidator.ProhibitedExits());
public void
shouldReturnInvalidIfWithdrawalRequestsIsNotNull_WhenWithdrawalRequestsAreProhibited() {
final List<WithdrawalRequestParameter> withdrawalRequests = List.of();
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
var resp =
resp(
@ -251,7 +255,7 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
Collections.emptyList(),
null,
null,
validatorExits));
withdrawalRequests));
final JsonRpcError jsonRpcError = fromErrorResp(resp);
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
@ -262,7 +266,7 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
protected BlockHeader createBlockHeader(
final Optional<List<Withdrawal>> maybeWithdrawals,
final Optional<List<Deposit>> maybeDeposits,
final Optional<List<ValidatorExit>> maybeExits) {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests) {
BlockHeader parentBlockHeader =
new BlockHeaderTestFixture()
.baseFeePerGas(Wei.ONE)
@ -281,7 +285,8 @@ public class EngineNewPayloadV4Test extends EngineNewPayloadV3Test {
.excessBlobGas(BlobGas.ZERO)
.blobGasUsed(0L)
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
.exitsRoot(maybeExits.map(BodyValidation::exitsRoot).orElse(null))
.withdrawalRequestsRoot(
maybeWithdrawalRequests.map(BodyValidation::withdrawalRequestsRoot).orElse(null))
.parentBeaconBlockRoot(
maybeParentBeaconBlockRoot.isPresent() ? maybeParentBeaconBlockRoot : null)
.buildHeader();

@ -17,35 +17,38 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.ValidatorExitTestFixture.VALIDATOR_EXIT_PARAMETER_1;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalRequestTestFixture.WITHDRAWAL_REQUEST_PARAMETER_1;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.junit.jupiter.api.Test;
public class ValidatorExitParameterTest {
public class WithdrawalRequestParameterTest {
@Test
public void toValidatorExit() {
ValidatorExit expected =
new ValidatorExit(
public void toWithdrawalRequest() {
WithdrawalRequest expected =
new WithdrawalRequest(
Address.fromHexString("0x814FaE9f487206471B6B0D713cD51a2D35980000"),
BLSPublicKey.fromHexString(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
assertThat(VALIDATOR_EXIT_PARAMETER_1.toValidatorExit()).isEqualTo(expected);
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"),
GWei.ONE);
assertThat(WITHDRAWAL_REQUEST_PARAMETER_1.toWithdrawalRequest()).isEqualTo(expected);
}
@Test
public void fromValidatorExit() {
ValidatorExit validatorExit =
new ValidatorExit(
public void fromWithdrawalRequest() {
WithdrawalRequest withdrawalRequest =
new WithdrawalRequest(
Address.fromHexString("0x814FaE9f487206471B6B0D713cD51a2D35980000"),
BLSPublicKey.fromHexString(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"),
GWei.ONE);
assertThat(ValidatorExitParameter.fromValidatorExit(validatorExit))
.isEqualTo(VALIDATOR_EXIT_PARAMETER_1);
assertThat(WithdrawalRequestParameter.fromWithdrawalRequest(withdrawalRequest))
.isEqualTo(WITHDRAWAL_REQUEST_PARAMETER_1);
}
}

@ -15,14 +15,18 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
public class ValidatorExitTestFixture {
import org.hyperledger.besu.datatypes.GWei;
public static final ValidatorExitParameter VALIDATOR_EXIT_PARAMETER_1 =
new ValidatorExitParameter(
public class WithdrawalRequestTestFixture {
public static final WithdrawalRequestParameter WITHDRAWAL_REQUEST_PARAMETER_1 =
new WithdrawalRequestParameter(
"0x814fae9f487206471b6b0d713cd51a2d35980000",
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e");
static final ValidatorExitParameter VALIDATOR_EXIT_PARAMETER_2 =
new ValidatorExitParameter(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e",
GWei.ONE.toShortHexString());
static final WithdrawalRequestParameter WITHDRAWAL_REQUEST_PARAMETER_2 =
new WithdrawalRequestParameter(
"0x758b8178a9a4b7206d1f648c4a77c515cbac7000",
"0x8706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243");
"0x8706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243",
GWei.ONE.toShortHexString());
}

@ -36,8 +36,8 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.core.encoding.DepositDecoder;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -50,8 +50,8 @@ import org.hyperledger.besu.ethereum.mainnet.ParentBeaconBlockRootHelper;
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.WithdrawalRequestContractHelper;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator;
@ -256,11 +256,14 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
throwIfStopped();
final ValidatorExitsValidator exitsValidator = newProtocolSpec.getExitsValidator();
Optional<List<ValidatorExit>> maybeExits = Optional.empty();
if (exitsValidator.allowValidatorExits()) {
maybeExits =
Optional.of(ValidatorExitContractHelper.popExitsFromQueue(disposableWorldState));
final WithdrawalRequestValidator withdrawalRequestsValidator =
newProtocolSpec.getWithdrawalRequestValidator();
Optional<List<WithdrawalRequest>> maybeWithdrawalRequests = Optional.empty();
if (withdrawalRequestsValidator.allowWithdrawalRequests()) {
maybeWithdrawalRequests =
Optional.of(
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(
disposableWorldState));
}
throwIfStopped();
@ -300,7 +303,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
? BodyValidation.withdrawalsRoot(maybeWithdrawals.get())
: null)
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
.exitsRoot(maybeExits.map(BodyValidation::exitsRoot).orElse(null));
.withdrawalRequestsRoot(
maybeWithdrawalRequests.map(BodyValidation::withdrawalRequestsRoot).orElse(null));
if (usage != null) {
builder.blobGasUsed(usage.used.toLong()).excessBlobGas(usage.excessBlobGas);
}
@ -317,7 +321,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
ommers,
withdrawals,
maybeDeposits,
maybeExits);
maybeWithdrawalRequests);
final Block block = new Block(blockHeader, blockBody);
operationTracer.traceEndBlock(blockHeader, blockBody);

@ -30,8 +30,8 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
@ -151,10 +151,10 @@ public final class GenesisState {
isShanghaiAtGenesis(config) ? Optional.of(emptyList()) : Optional.empty();
final Optional<List<Deposit>> deposits =
isExperimentalEipsTimeAtGenesis(config) ? Optional.of(emptyList()) : Optional.empty();
final Optional<List<ValidatorExit>> exits =
final Optional<List<WithdrawalRequest>> withdrawalRequests =
isPragueAtGenesis(config) ? Optional.of(emptyList()) : Optional.empty();
return new BlockBody(emptyList(), emptyList(), withdrawals, deposits, exits);
return new BlockBody(emptyList(), emptyList(), withdrawals, deposits, withdrawalRequests);
}
public Block getBlock() {

@ -63,7 +63,9 @@ public class Block {
out.writeList(body.getOmmers(), BlockHeader::writeTo);
body.getWithdrawals().ifPresent(withdrawals -> out.writeList(withdrawals, Withdrawal::writeTo));
body.getDeposits().ifPresent(deposits -> out.writeList(deposits, Deposit::writeTo));
body.getExits().ifPresent(exits -> out.writeList(exits, ValidatorExit::writeTo));
body.getWithdrawalRequests()
.ifPresent(
withdrawalRequests -> out.writeList(withdrawalRequests, WithdrawalRequest::writeTo));
out.endList();
}
@ -77,13 +79,14 @@ public class Block {
in.isEndOfCurrentList() ? Optional.empty() : Optional.of(in.readList(Withdrawal::readFrom));
final Optional<List<Deposit>> deposits =
in.isEndOfCurrentList() ? Optional.empty() : Optional.of(in.readList(Deposit::readFrom));
final Optional<List<ValidatorExit>> exits =
final Optional<List<WithdrawalRequest>> withdrawalRequests =
in.isEndOfCurrentList()
? Optional.empty()
: Optional.of(in.readList(ValidatorExit::readFrom));
: Optional.of(in.readList(WithdrawalRequest::readFrom));
in.leaveList();
return new Block(header, new BlockBody(transactions, ommers, withdrawals, deposits, exits));
return new Block(
header, new BlockBody(transactions, ommers, withdrawals, deposits, withdrawalRequests));
}
@Override

@ -38,14 +38,14 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
private final List<BlockHeader> ommers;
private final Optional<List<Withdrawal>> withdrawals;
private final Optional<List<Deposit>> deposits;
private final Optional<List<ValidatorExit>> exits;
private final Optional<List<WithdrawalRequest>> withdrawalRequests;
public BlockBody(final List<Transaction> transactions, final List<BlockHeader> ommers) {
this.transactions = transactions;
this.ommers = ommers;
this.withdrawals = Optional.empty();
this.deposits = Optional.empty();
this.exits = Optional.empty();
this.withdrawalRequests = Optional.empty();
}
public BlockBody(
@ -53,12 +53,12 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
final List<BlockHeader> ommers,
final Optional<List<Withdrawal>> withdrawals,
final Optional<List<Deposit>> deposits,
final Optional<List<ValidatorExit>> exits) {
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
this.transactions = transactions;
this.ommers = ommers;
this.withdrawals = withdrawals;
this.deposits = deposits;
this.exits = exits;
this.withdrawalRequests = withdrawalRequests;
}
public static BlockBody empty() {
@ -92,13 +92,13 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
}
/**
* Returns the exits of the block.
* Returns the withdrawal requests of the block.
*
* @return The optional list of exits included in the block.
* @return The optional list of withdrawal requests included in the block.
*/
@Override
public Optional<List<ValidatorExit>> getExits() {
return exits;
public Optional<List<WithdrawalRequest>> getWithdrawalRequests() {
return withdrawalRequests;
}
/**
@ -127,7 +127,8 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
output.writeList(getOmmers(), BlockHeader::writeTo);
withdrawals.ifPresent(withdrawals -> output.writeList(withdrawals, Withdrawal::writeTo));
deposits.ifPresent(deposits -> output.writeList(deposits, Deposit::writeTo));
exits.ifPresent(exits -> output.writeList(exits, ValidatorExit::writeTo));
withdrawalRequests.ifPresent(
withdrawalRequests -> output.writeList(withdrawalRequests, WithdrawalRequest::writeTo));
}
public static BlockBody readWrappedBodyFrom(
@ -182,7 +183,7 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
: Optional.of(input.readList(Deposit::readFrom)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(ValidatorExit::readFrom)));
: Optional.of(input.readList(WithdrawalRequest::readFrom)));
}
@Override
@ -194,12 +195,12 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
&& Objects.equals(ommers, blockBody.ommers)
&& Objects.equals(withdrawals, blockBody.withdrawals)
&& Objects.equals(deposits, blockBody.deposits)
&& Objects.equals(exits, blockBody.exits);
&& Objects.equals(withdrawalRequests, blockBody.withdrawalRequests);
}
@Override
public int hashCode() {
return Objects.hash(transactions, ommers, withdrawals, deposits, exits);
return Objects.hash(transactions, ommers, withdrawals, deposits, withdrawalRequests);
}
public boolean isEmpty() {
@ -207,7 +208,7 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
&& ommers.isEmpty()
&& withdrawals.isEmpty()
&& deposits.isEmpty()
&& exits.isEmpty();
&& withdrawalRequests.isEmpty();
}
@Override
@ -221,8 +222,9 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
+ withdrawals
+ ", deposits="
+ deposits
+ ", exits="
+ exits
+ ", withdrawal_requests="
+ ", withdrawal_requests="
+ withdrawalRequests
+ '}';
}
}

@ -176,8 +176,8 @@ public class BlockHeader extends SealableBlockHeader
if (depositsRoot != null) {
out.writeBytes(depositsRoot);
}
if (exitsRoot != null) {
out.writeBytes(exitsRoot);
if (withdrawalRequestsRoot != null) {
out.writeBytes(withdrawalRequestsRoot);
}
out.endList();
}
@ -289,8 +289,8 @@ public class BlockHeader extends SealableBlockHeader
if (depositsRoot != null) {
sb.append("depositsRoot=").append(depositsRoot);
}
if (exitsRoot != null) {
sb.append("exitsRoot=").append(exitsRoot);
if (withdrawalRequestsRoot != null) {
sb.append("exitsRoot=").append(withdrawalRequestsRoot);
}
return sb.append("}").toString();
}
@ -326,7 +326,10 @@ public class BlockHeader extends SealableBlockHeader
.getDepositsRoot()
.map(h -> Hash.fromHexString(h.toHexString()))
.orElse(null),
pluginBlockHeader.getExitsRoot().map(h -> Hash.fromHexString(h.toHexString())).orElse(null),
pluginBlockHeader
.getWithdrawalRequestsRoot()
.map(h -> Hash.fromHexString(h.toHexString()))
.orElse(null),
blockHeaderFunctions);
}

@ -46,7 +46,7 @@ public class BlockHeaderBuilder {
private Hash withdrawalsRoot = null;
private Hash depositsRoot = null;
private Hash exitsRoot = null;
private Hash withdrawalRequests = null;
private Hash receiptsRoot;
@ -126,7 +126,7 @@ public class BlockHeaderBuilder {
.excessBlobGas(header.getExcessBlobGas().orElse(null))
.parentBeaconBlockRoot(header.getParentBeaconBlockRoot().orElse(null))
.depositsRoot(header.getDepositsRoot().orElse(null))
.exitsRoot(header.getExitsRoot().orElse(null));
.withdrawalRequestsRoot(header.getWithdrawalRequestsRoot().orElse(null));
}
public static BlockHeaderBuilder fromBuilder(final BlockHeaderBuilder fromBuilder) {
@ -151,7 +151,7 @@ public class BlockHeaderBuilder {
.excessBlobGas(fromBuilder.excessBlobGas)
.parentBeaconBlockRoot(fromBuilder.parentBeaconBlockRoot)
.depositsRoot(fromBuilder.depositsRoot)
.exitsRoot(fromBuilder.exitsRoot)
.withdrawalRequestsRoot(fromBuilder.withdrawalRequests)
.blockHeaderFunctions(fromBuilder.blockHeaderFunctions);
toBuilder.nonce = fromBuilder.nonce;
return toBuilder;
@ -182,7 +182,7 @@ public class BlockHeaderBuilder {
excessBlobGas,
parentBeaconBlockRoot,
depositsRoot,
exitsRoot,
withdrawalRequests,
blockHeaderFunctions);
}
@ -225,7 +225,7 @@ public class BlockHeaderBuilder {
excessBlobGas,
parentBeaconBlockRoot,
depositsRoot,
exitsRoot);
withdrawalRequests);
}
private void validateBlockHeader() {
@ -290,7 +290,7 @@ public class BlockHeaderBuilder {
sealableBlockHeader.getExcessBlobGas().ifPresent(this::excessBlobGas);
sealableBlockHeader.getParentBeaconBlockRoot().ifPresent(this::parentBeaconBlockRoot);
depositsRoot(sealableBlockHeader.getDepositsRoot().orElse(null));
exitsRoot(sealableBlockHeader.getExitsRoot().orElse(null));
withdrawalRequestsRoot(sealableBlockHeader.getWithdrawalRequestsRoot().orElse(null));
return this;
}
@ -410,8 +410,8 @@ public class BlockHeaderBuilder {
return this;
}
public BlockHeaderBuilder exitsRoot(final Hash hash) {
this.exitsRoot = hash;
public BlockHeaderBuilder withdrawalRequestsRoot(final Hash hash) {
this.withdrawalRequests = hash;
return this;
}

@ -45,7 +45,7 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
protected final Hash depositsRoot;
protected final Hash exitsRoot;
protected final Hash withdrawalRequestsRoot;
protected final Long blobGasUsed;
@ -72,7 +72,7 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
final BlobGas excessBlobGas,
final Bytes32 parentBeaconBlockRoot,
final Hash depositsRoot,
final Hash exitsRoot) {
final Hash withdrawalRequestsRoot) {
super(
parentHash,
coinbase,
@ -89,7 +89,7 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
this.withdrawalsRoot = withdrawalsRoot;
this.depositsRoot = depositsRoot;
this.receiptsRoot = receiptsRoot;
this.exitsRoot = exitsRoot;
this.withdrawalRequestsRoot = withdrawalRequestsRoot;
this.logsBloom = logsBloom;
this.gasUsed = gasUsed;
this.extraData = extraData;
@ -178,8 +178,8 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
return Optional.ofNullable(depositsRoot);
}
public Optional<Hash> getExitsRoot() {
return Optional.ofNullable(exitsRoot);
public Optional<Hash> getWithdrawalRequestsRoot() {
return Optional.ofNullable(withdrawalRequestsRoot);
}
/**

@ -16,9 +16,10 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.PublicKey;
import org.hyperledger.besu.ethereum.core.encoding.ValidatorExitDecoder;
import org.hyperledger.besu.ethereum.core.encoding.ValidatorExitEncoder;
import org.hyperledger.besu.ethereum.core.encoding.WithdrawalRequestDecoder;
import org.hyperledger.besu.ethereum.core.encoding.WithdrawalRequestEncoder;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
@ -27,26 +28,29 @@ import java.util.Objects;
import org.apache.tuweni.bytes.Bytes;
public class ValidatorExit implements org.hyperledger.besu.plugin.data.ValidatorExit {
public class WithdrawalRequest implements org.hyperledger.besu.plugin.data.WithdrawalRequest {
private final Address sourceAddress;
private final BLSPublicKey validatorPubKey;
private final GWei amount;
public ValidatorExit(final Address sourceAddress, final BLSPublicKey validatorPubKey) {
public WithdrawalRequest(
final Address sourceAddress, final BLSPublicKey validatorPubKey, final GWei amount) {
this.sourceAddress = sourceAddress;
this.validatorPubKey = validatorPubKey;
this.amount = amount;
}
public static ValidatorExit readFrom(final Bytes rlpBytes) {
public static WithdrawalRequest readFrom(final Bytes rlpBytes) {
return readFrom(RLP.input(rlpBytes));
}
public static ValidatorExit readFrom(final RLPInput rlpInput) {
return ValidatorExitDecoder.decode(rlpInput);
public static WithdrawalRequest readFrom(final RLPInput rlpInput) {
return WithdrawalRequestDecoder.decode(rlpInput);
}
public void writeTo(final RLPOutput out) {
ValidatorExitEncoder.encode(this, out);
WithdrawalRequestEncoder.encode(this, out);
}
@Override
@ -59,13 +63,20 @@ public class ValidatorExit implements org.hyperledger.besu.plugin.data.Validator
return validatorPubKey;
}
@Override
public GWei getAmount() {
return amount;
}
@Override
public String toString() {
return "ValidatorExit{"
return "WithdrawalRequest{"
+ "sourceAddress="
+ sourceAddress
+ " validatorPubKey="
+ validatorPubKey
+ " amount="
+ amount
+ '}';
}
@ -77,13 +88,14 @@ public class ValidatorExit implements org.hyperledger.besu.plugin.data.Validator
if (o == null || getClass() != o.getClass()) {
return false;
}
final ValidatorExit that = (ValidatorExit) o;
final WithdrawalRequest that = (WithdrawalRequest) o;
return Objects.equals(sourceAddress, that.sourceAddress)
&& Objects.equals(validatorPubKey, that.validatorPubKey);
&& Objects.equals(validatorPubKey, that.validatorPubKey)
&& Objects.equals(amount, that.amount);
}
@Override
public int hashCode() {
return Objects.hash(sourceAddress, validatorPubKey);
return Objects.hash(sourceAddress, validatorPubKey, amount);
}
}

@ -16,17 +16,19 @@ package org.hyperledger.besu.ethereum.core.encoding;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
public class ValidatorExitDecoder {
public class WithdrawalRequestDecoder {
public static ValidatorExit decode(final RLPInput rlpInput) {
public static WithdrawalRequest decode(final RLPInput rlpInput) {
rlpInput.enterList();
final Address sourceAddress = Address.readFrom(rlpInput);
final BLSPublicKey validatorPublicKey = BLSPublicKey.readFrom(rlpInput);
final GWei amount = GWei.of(rlpInput.readUInt64Scalar());
rlpInput.leaveList();
return new ValidatorExit(sourceAddress, validatorPublicKey);
return new WithdrawalRequest(sourceAddress, validatorPublicKey, amount);
}
}

@ -14,22 +14,23 @@
*/
package org.hyperledger.besu.ethereum.core.encoding;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.apache.tuweni.bytes.Bytes;
public class ValidatorExitEncoder {
public class WithdrawalRequestEncoder {
public static void encode(final ValidatorExit exit, final RLPOutput rlpOutput) {
public static void encode(final WithdrawalRequest withdrawalRequest, final RLPOutput rlpOutput) {
rlpOutput.startList();
rlpOutput.writeBytes(exit.getSourceAddress());
rlpOutput.writeBytes(exit.getValidatorPubKey());
rlpOutput.writeBytes(withdrawalRequest.getSourceAddress());
rlpOutput.writeBytes(withdrawalRequest.getValidatorPubKey());
rlpOutput.writeUInt64Scalar(withdrawalRequest.getAmount());
rlpOutput.endList();
}
public static Bytes encodeOpaqueBytes(final ValidatorExit exit) {
public static Bytes encodeOpaqueBytes(final WithdrawalRequest exit) {
return RLP.encode(rlpOutput -> encode(exit, rlpOutput));
}
}

@ -196,10 +196,10 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
}
final ValidatorExitsValidator exitsValidator = protocolSpec.getExitsValidator();
if (exitsValidator.allowValidatorExits()) {
final WithdrawalRequestValidator exitsValidator = protocolSpec.getWithdrawalRequestValidator();
if (exitsValidator.allowWithdrawalRequests()) {
// Performing system-call logic
ValidatorExitContractHelper.popExitsFromQueue(worldState);
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState);
}
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {

@ -21,13 +21,13 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.core.encoding.DepositEncoder;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.ValidatorExitEncoder;
import org.hyperledger.besu.ethereum.core.encoding.WithdrawalEncoder;
import org.hyperledger.besu.ethereum.core.encoding.WithdrawalRequestEncoder;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.trie.MerkleTrie;
import org.hyperledger.besu.ethereum.trie.patricia.SimpleMerklePatriciaTrie;
@ -106,16 +106,20 @@ public final class BodyValidation {
}
/**
* Generates the exits root for a list of exits
* Generates the withdrawal request root for a list of withdrawal request
*
* @param exits list of exits
* @return the exits root
* @param withdrawalRequests list of withdrawal request
* @return the withdrawal request root
*/
public static Hash exitsRoot(final List<ValidatorExit> exits) {
public static Hash withdrawalRequestsRoot(final List<WithdrawalRequest> withdrawalRequests) {
final MerkleTrie<Bytes, Bytes> trie = trie();
IntStream.range(0, exits.size())
.forEach(i -> trie.put(indexKey(i), ValidatorExitEncoder.encodeOpaqueBytes(exits.get(i))));
IntStream.range(0, withdrawalRequests.size())
.forEach(
i ->
trie.put(
indexKey(i),
WithdrawalRequestEncoder.encodeOpaqueBytes(withdrawalRequests.get(i))));
return Hash.wrap(trie.getRootHash());
}

@ -20,7 +20,7 @@ import org.hyperledger.besu.ethereum.core.Block;
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.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import java.util.HashSet;
@ -109,8 +109,8 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
return false;
}
if (body.getExits().isPresent()) {
if (!validateExits(block, body.getExits().get())) {
if (body.getWithdrawalRequests().isPresent()) {
if (!validateWithdrawalRequests(block, body.getWithdrawalRequests().get())) {
return false;
}
}
@ -331,9 +331,10 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
return true;
}
private boolean validateExits(final Block block, final List<ValidatorExit> exits) {
final ValidatorExitsValidator exitsValidator =
protocolSchedule.getByBlockHeader(block.getHeader()).getExitsValidator();
return exitsValidator.validateExitsInBlock(block, exits);
private boolean validateWithdrawalRequests(
final Block block, final List<WithdrawalRequest> withdrawalRequests) {
final WithdrawalRequestValidator withdrawalRequestValidator =
protocolSchedule.getByBlockHeader(block.getHeader()).getWithdrawalRequestValidator();
return withdrawalRequestValidator.validateWithdrawalRequestsInBlock(block, withdrawalRequests);
}
}

@ -764,7 +764,7 @@ public abstract class MainnetProtocolSpecs {
// use prague precompiled contracts
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)
.depositsValidator(new DepositsValidator.AllowedDeposits(depositContractAddress))
.exitsValidator(new PragueValidatorExitsValidator())
.withdrawalRequestsValidator(new PragueWithdrawalRequestValidator())
.name("Prague");
}

@ -1,85 +0,0 @@
/*
* 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<List<ValidatorExit>> validatorExits) {
return validatorExits.isPresent();
}
@Override
public boolean validateExitsInBlock(final Block block, final List<ValidatorExit> 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<ValidatorExit> 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;
}
}

@ -0,0 +1,94 @@
/*
* 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.WithdrawalRequest;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PragueWithdrawalRequestValidator implements WithdrawalRequestValidator {
private static final Logger LOG = LoggerFactory.getLogger(PragueWithdrawalRequestValidator.class);
@Override
public boolean allowWithdrawalRequests() {
return true;
}
@Override
public boolean validateWithdrawalRequestParameter(
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
return withdrawalRequests.isPresent();
}
@Override
public boolean validateWithdrawalRequestsInBlock(
final Block block, final List<WithdrawalRequest> withdrawalRequests) {
final Hash blockHash = block.getHash();
if (block.getHeader().getWithdrawalRequestsRoot().isEmpty()) {
LOG.warn("Block {} must contain withdrawal_requests_root", blockHash);
return false;
}
if (block.getBody().getWithdrawalRequests().isEmpty()) {
LOG.warn("Block {} must contain withdrawal requests (even if empty list)", blockHash);
return false;
}
final List<WithdrawalRequest> withdrawalRequestsInBlock =
block.getBody().getWithdrawalRequests().get();
// TODO Do we need to allow for customization? (e.g. if the value changes in the next fork)
if (withdrawalRequestsInBlock.size()
> WithdrawalRequestContractHelper.MAX_WITHDRAWAL_REQUESTS_PER_BLOCK) {
LOG.warn(
"Block {} has more than the allowed maximum number of withdrawal requests", blockHash);
return false;
}
// Validate exits_root
final Hash expectedWithdrawalsRequestRoot =
BodyValidation.withdrawalRequestsRoot(withdrawalRequestsInBlock);
if (!expectedWithdrawalsRequestRoot.equals(
block.getHeader().getWithdrawalRequestsRoot().get())) {
LOG.warn(
"Block {} withdrawal_requests_root does not match expected hash root for withdrawal requests in block",
blockHash);
return false;
}
// Validate exits
final boolean expectedWithdrawalRequestMatch =
withdrawalRequests.equals(withdrawalRequestsInBlock);
if (!expectedWithdrawalRequestMatch) {
LOG.warn(
"Block {} has a mismatch between its withdrawal requests and expected withdrawal requests (in_block = {}, "
+ "expected = {})",
blockHash,
withdrawalRequestsInBlock,
withdrawalRequests);
return false;
}
return true;
}
}

@ -76,7 +76,7 @@ public class ProtocolSpec {
private final WithdrawalsValidator withdrawalsValidator;
private final Optional<WithdrawalsProcessor> withdrawalsProcessor;
private final DepositsValidator depositsValidator;
private final ValidatorExitsValidator exitsValidator;
private final WithdrawalRequestValidator withdrawalRequestValidator;
private final boolean isPoS;
private final boolean isReplayProtectionSupported;
@ -139,7 +139,7 @@ public class ProtocolSpec {
final WithdrawalsValidator withdrawalsValidator,
final Optional<WithdrawalsProcessor> withdrawalsProcessor,
final DepositsValidator depositsValidator,
final ValidatorExitsValidator exitsValidator,
final WithdrawalRequestValidator withdrawalRequestValidator,
final boolean isPoS,
final boolean isReplayProtectionSupported) {
this.name = name;
@ -167,7 +167,7 @@ public class ProtocolSpec {
this.withdrawalsValidator = withdrawalsValidator;
this.withdrawalsProcessor = withdrawalsProcessor;
this.depositsValidator = depositsValidator;
this.exitsValidator = exitsValidator;
this.withdrawalRequestValidator = withdrawalRequestValidator;
this.isPoS = isPoS;
this.isReplayProtectionSupported = isReplayProtectionSupported;
}
@ -373,8 +373,8 @@ public class ProtocolSpec {
return depositsValidator;
}
public ValidatorExitsValidator getExitsValidator() {
return exitsValidator;
public WithdrawalRequestValidator getWithdrawalRequestValidator() {
return withdrawalRequestValidator;
}
/**

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.FlexiblePrivacyPrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.PrivacyPluginPrecompiledContract;
@ -75,7 +76,8 @@ public class ProtocolSpecBuilder {
new WithdrawalsValidator.ProhibitedWithdrawals();
private WithdrawalsProcessor withdrawalsProcessor;
private DepositsValidator depositsValidator = new DepositsValidator.ProhibitedDeposits();
private ValidatorExitsValidator exitsValidator = new ValidatorExitsValidator.ProhibitedExits();
private WithdrawalRequestValidator withdrawalRequestValidator =
new ProhibitedWithdrawalRequests();
private FeeMarket feeMarket = FeeMarket.legacy();
private BadBlockManager badBlockManager;
private PoWHasher powHasher = PoWHasher.ETHASH_LIGHT;
@ -265,8 +267,9 @@ public class ProtocolSpecBuilder {
return this;
}
public ProtocolSpecBuilder exitsValidator(final ValidatorExitsValidator exitsValidator) {
this.exitsValidator = exitsValidator;
public ProtocolSpecBuilder withdrawalRequestsValidator(
final WithdrawalRequestValidator withdrawalRequestValidator) {
this.withdrawalRequestValidator = withdrawalRequestValidator;
return this;
}
@ -389,7 +392,7 @@ public class ProtocolSpecBuilder {
withdrawalsValidator,
Optional.ofNullable(withdrawalsProcessor),
depositsValidator,
exitsValidator,
withdrawalRequestValidator,
isPoS,
isReplayProtectionSupported);
}

@ -1,146 +0,0 @@
/*
* 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)
*
* <p>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<ValidatorExit> 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<ValidatorExit> exits = dequeueExits(account);
updateExcessExits(account);
resetExitCount(account);
worldUpdater.commit();
return exits;
}
private static List<ValidatorExit> 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<ValidatorExit> 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<ValidatorExit> 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<ValidatorExit> 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));
}
}

@ -1,76 +0,0 @@
/*
* 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.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<List<ValidatorExit>> validatorExits);
boolean validateExitsInBlock(Block block, List<ValidatorExit> 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
*
* @param validatorExits Optional list of exits
* @return true, if valid, false otherwise
*/
@Override
public boolean validateValidatorExitParameter(
final Optional<List<ValidatorExit>> validatorExits) {
return validatorExits.isEmpty();
}
@Override
public boolean validateExitsInBlock(
final Block block, final List<ValidatorExit> validatorExits) {
final Optional<List<ValidatorExit>> 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;
}
}
}

@ -0,0 +1,186 @@
/*
* 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.GWei;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
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;
import org.apache.tuweni.units.bigints.UInt64;
/**
* Helper for interacting with the Validator Withdrawal Request Contract
* (https://eips.ethereum.org/EIPS/eip-7002)
*
* <p>TODO: Please note that this is not the spec-way of interacting with the Validator Withdrawal
* Request contract. See https://github.com/hyperledger/besu/issues/6918 for more information.
*/
public class WithdrawalRequestContractHelper {
public static final Address WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS =
Address.fromHexString("0xEd8EA01d70Cb49726175BCf2778B9C982912e017");
@VisibleForTesting
// Storage slot to store the difference between number of withdrawal requests since last block and
// target withdrawal requests
// per block
static final UInt256 EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT = UInt256.valueOf(0L);
@VisibleForTesting
// Storage slot to store the number of withdrawal requests added since last block
static final UInt256 WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT = UInt256.valueOf(1L);
@VisibleForTesting
static final UInt256 WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT = UInt256.valueOf(2L);
@VisibleForTesting
static final UInt256 WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT = UInt256.valueOf(3L);
private static final UInt256 WITHDRAWAL_REQUEST_QUEUE_STORAGE_OFFSET = UInt256.valueOf(4L);
// How many slots each withdrawal request occupies in the account state
private static final int WITHDRAWAL_REQUEST_STORAGE_SLOT_SIZE = 3;
@VisibleForTesting static final int MAX_WITHDRAWAL_REQUESTS_PER_BLOCK = 16;
private static final int TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK = 2;
// TODO-lucas Add MIN_WITHDRAWAL_REQUEST_FEE and WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION
/*
Pop the expected list of withdrawal requests from the smart contract, updating the queue pointers and other
control variables in the contract state.
*/
public static List<WithdrawalRequest> popWithdrawalRequestsFromQueue(
final MutableWorldState mutableWorldState) {
final WorldUpdater worldUpdater = mutableWorldState.updater();
final MutableAccount account = worldUpdater.getAccount(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS);
if (Hash.EMPTY.equals(account.getCodeHash())) {
return List.of();
}
final List<WithdrawalRequest> withdrawalRequests = dequeueWithdrawalRequests(account);
updateExcessWithdrawalRequests(account);
resetWithdrawalRequestsCount(account);
worldUpdater.commit();
return withdrawalRequests;
}
private static List<WithdrawalRequest> dequeueWithdrawalRequests(final MutableAccount account) {
final UInt256 queueHeadIndex =
account.getStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT);
final UInt256 queueTailIndex =
account.getStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT);
final List<WithdrawalRequest> withdrawalRequests =
peekExpectedWithdrawalRequests(account, queueHeadIndex, queueTailIndex);
final UInt256 newQueueHeadIndex = queueHeadIndex.plus(withdrawalRequests.size());
if (newQueueHeadIndex.equals(queueTailIndex)) {
// Queue is empty, reset queue pointers
account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, UInt256.valueOf(0L));
account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, UInt256.valueOf(0L));
} else {
account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, newQueueHeadIndex);
}
return withdrawalRequests;
}
/*
;; Each stack element has the following layout:
;;
;; A: addr
;; 0x00 | 00 00 00 00 00 00 00 00 00 00 00 00 aa aa aa aa
;; 0x10 | aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
;;
;; B: pk[0:32]
;; 0x00 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb
;; 0x10 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb
;;
;; C: pk[32:48] ++ am[0:8] -> pk2_am
;; 0x00 | cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
;; 0x10 | dd dd dd dd dd dd dd dd 00 00 00 00 00 00 00 00
;;
;; To get these three stack elements into the correct contiguous format, it is
;; necessary to combine them in the follow form:
;;
;; (A[12:32] ++ B[0:12], B[12:32] ++ C[0:12], C[12:24])
*/
private static List<WithdrawalRequest> peekExpectedWithdrawalRequests(
final Account account, final UInt256 queueHeadIndex, final UInt256 queueTailIndex) {
final long numRequestsInQueue = queueTailIndex.subtract(queueHeadIndex).toLong();
final long numRequestsDequeued =
Long.min(numRequestsInQueue, MAX_WITHDRAWAL_REQUESTS_PER_BLOCK);
final List<WithdrawalRequest> withdrawalRequests = new ArrayList<>();
for (int i = 0; i < numRequestsDequeued; i++) {
final UInt256 queueStorageSlot =
WITHDRAWAL_REQUEST_QUEUE_STORAGE_OFFSET.plus(
queueHeadIndex.plus(i).multiply(WITHDRAWAL_REQUEST_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)));
final UInt64 amount =
UInt64.fromBytes(account.getStorageValue(queueStorageSlot.plus(2)).slice(16, 8));
withdrawalRequests.add(
new WithdrawalRequest(sourceAddress, validatorPubKey, GWei.of(amount)));
}
return withdrawalRequests;
}
private static void updateExcessWithdrawalRequests(final MutableAccount account) {
final UInt256 previousExcessRequests =
account.getStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT);
final UInt256 requestsCount = account.getStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT);
UInt256 newExcessRequests = UInt256.valueOf(0L);
if (previousExcessRequests.plus(requestsCount).toLong()
> TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK) {
newExcessRequests =
previousExcessRequests.plus(requestsCount).subtract(TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK);
}
account.setStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, newExcessRequests);
}
private static void resetWithdrawalRequestsCount(final MutableAccount account) {
account.setStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, UInt256.valueOf(0L));
}
}

@ -0,0 +1,82 @@
/*
* 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.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface WithdrawalRequestValidator {
boolean allowWithdrawalRequests();
boolean validateWithdrawalRequestParameter(Optional<List<WithdrawalRequest>> withdrawalRequests);
boolean validateWithdrawalRequestsInBlock(
Block block, List<WithdrawalRequest> withdrawalRequests);
/** Used before Prague */
class ProhibitedWithdrawalRequests implements WithdrawalRequestValidator {
private static final Logger LOG = LoggerFactory.getLogger(ProhibitedWithdrawalRequests.class);
@Override
public boolean allowWithdrawalRequests() {
return false;
}
/**
* Before Prague we do not expect to have execution layer withdrawal requests, so it is expected
* the optional parameter will be empty
*
* @param withdrawalRequests Optional list of withdrawal requests
* @return true, if valid, false otherwise
*/
@Override
public boolean validateWithdrawalRequestParameter(
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
return withdrawalRequests.isEmpty();
}
@Override
public boolean validateWithdrawalRequestsInBlock(
final Block block, final List<WithdrawalRequest> withdrawalRequests) {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests =
block.getBody().getWithdrawalRequests();
if (maybeWithdrawalRequests.isPresent()) {
LOG.warn(
"Block {} contains withdrawal requests but withdrawal requests are prohibited",
block.getHash());
return false;
}
if (block.getHeader().getWithdrawalRequestsRoot().isPresent()) {
LOG.warn(
"Block {} header contains withdrawal_requests_root but withdrawal requests are prohibited",
block.getHash());
return false;
}
return true;
}
}
}

@ -304,7 +304,7 @@ public class BlockDataGenerator {
.nonce(blockNonce)
.withdrawalsRoot(options.getWithdrawalsRoot(null))
.depositsRoot(options.getDepositsRoot(null))
.exitsRoot(options.getExitsRoot(null))
.withdrawalRequestsRoot(options.getWithdrawalRequestsRoot(null))
.blockHeaderFunctions(
options.getBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
options.getBaseFee(Optional.of(Wei.of(uint256(2)))).ifPresent(blockHeaderBuilder::baseFee);
@ -334,7 +334,7 @@ public class BlockDataGenerator {
ommers,
options.getWithdrawals(Optional.empty()),
options.getDeposits(Optional.empty()),
options.getExits(Optional.empty()));
options.getWithdrawalRequests(Optional.empty()));
}
private BlockHeader ommer() {
@ -641,7 +641,7 @@ public class BlockDataGenerator {
private Optional<Optional<List<Withdrawal>>> withdrawals = Optional.empty();
private Optional<Optional<List<Deposit>>> deposits = Optional.empty();
private Optional<Optional<List<ValidatorExit>>> exits = Optional.empty();
private Optional<Optional<List<WithdrawalRequest>>> withdrawalRequests = Optional.empty();
private Optional<Bytes> extraData = Optional.empty();
private Optional<BlockHeaderFunctions> blockHeaderFunctions = Optional.empty();
private Optional<Hash> receiptsRoot = Optional.empty();
@ -658,7 +658,7 @@ public class BlockDataGenerator {
private Optional<Hash> withdrawalsRoot = Optional.empty();
private Optional<Hash> depositsRoot = Optional.empty();
private Optional<Hash> exitsRoot = Optional.empty();
private Optional<Hash> withdrawalRequestsRoot = Optional.empty();
private Optional<Optional<Wei>> maybeMaxFeePerBlobGas = Optional.empty();
@ -735,13 +735,13 @@ public class BlockDataGenerator {
return deposits.orElse(defaultValue);
}
public Hash getExitsRoot(final Hash defaultValue) {
return exitsRoot.orElse(defaultValue);
public Hash getWithdrawalRequestsRoot(final Hash defaultValue) {
return withdrawalRequestsRoot.orElse(defaultValue);
}
public Optional<List<ValidatorExit>> getExits(
final Optional<List<ValidatorExit>> defaultValue) {
return exits.orElse(defaultValue);
public Optional<List<WithdrawalRequest>> getWithdrawalRequests(
final Optional<List<WithdrawalRequest>> defaultValue) {
return withdrawalRequests.orElse(defaultValue);
}
public boolean hasTransactions() {
@ -776,8 +776,9 @@ public class BlockDataGenerator {
return this;
}
public BlockOptions setExits(final Optional<List<ValidatorExit>> exits) {
this.exits = Optional.of(exits);
public BlockOptions setWithdrawalRequests(
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
this.withdrawalRequests = Optional.of(withdrawalRequests);
return this;
}
@ -874,8 +875,8 @@ public class BlockDataGenerator {
return this;
}
public BlockOptions setExitsRoot(final Hash exitsRoot) {
this.exitsRoot = Optional.of(exitsRoot);
public BlockOptions setWithdrawalRequestsRoot(final Hash withdrawalRequestsRoot) {
this.withdrawalRequestsRoot = Optional.of(withdrawalRequestsRoot);
return this;
}

@ -51,7 +51,7 @@ public class BlockHeaderTestFixture {
private long nonce = 0;
private Optional<Hash> withdrawalsRoot = Optional.empty();
private Optional<Hash> depositsRoot = Optional.empty();
private Optional<Hash> exitsRoot = Optional.empty();
private Optional<Hash> withdrawalsRequestRoot = Optional.empty();
private BlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions();
private Optional<BlobGas> excessBlobGas = Optional.empty();
private Optional<Long> blobGasUsed = Optional.empty();
@ -80,7 +80,7 @@ public class BlockHeaderTestFixture {
excessBlobGas.ifPresent(builder::excessBlobGas);
blobGasUsed.ifPresent(builder::blobGasUsed);
depositsRoot.ifPresent(builder::depositsRoot);
exitsRoot.ifPresent(builder::exitsRoot);
withdrawalsRequestRoot.ifPresent(builder::withdrawalRequestsRoot);
parentBeaconBlockRoot.ifPresent(builder::parentBeaconBlockRoot);
builder.blockHeaderFunctions(blockHeaderFunctions);
@ -182,8 +182,8 @@ public class BlockHeaderTestFixture {
return this;
}
public BlockHeaderTestFixture exitsRoot(final Hash exitsRoot) {
this.exitsRoot = Optional.ofNullable(exitsRoot);
public BlockHeaderTestFixture withdrawalRequestsRoot(final Hash withdrawalRequestsRoot) {
this.withdrawalsRequestRoot = Optional.ofNullable(withdrawalRequestsRoot);
return this;
}

@ -120,7 +120,7 @@ public class NonBesuBlockHeader implements BlockHeader {
}
@Override
public Optional<? extends Hash> getExitsRoot() {
public Optional<? extends Hash> getWithdrawalRequestsRoot() {
return Optional.empty();
}

@ -40,7 +40,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
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.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
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;
@ -110,8 +110,8 @@ class BlockImportExceptionHandlingTest {
when(protocolContext.getBlockchain()).thenReturn(blockchain);
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getExitsValidator())
.thenReturn(new ValidatorExitsValidator.ProhibitedExits());
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
mainnetBlockValidator =
new MainnetBlockValidator(
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);

@ -16,29 +16,31 @@ package org.hyperledger.besu.ethereum.core.encoding;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
class ValidatorExitDecoderTest {
class WithdrawalRequestDecoderTest {
@Test
public void shouldDecodeValidatorExit() {
final ValidatorExit expectedValidatorExit =
new ValidatorExit(
public void shouldDecodeWithdrawalRequest() {
final WithdrawalRequest expectedWithdrawalRequest =
new WithdrawalRequest(
Address.fromHexString("0x814FaE9f487206471B6B0D713cD51a2D35980000"),
BLSPublicKey.fromHexString(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"),
GWei.of(5));
final BytesValueRLPOutput out = new BytesValueRLPOutput();
expectedValidatorExit.writeTo(out);
expectedWithdrawalRequest.writeTo(out);
final ValidatorExit decodedValidatorExit =
ValidatorExitDecoder.decode(RLP.input(out.encoded()));
final WithdrawalRequest decodedWithdrawalRequest =
WithdrawalRequestDecoder.decode(RLP.input(out.encoded()));
Assertions.assertThat(decodedValidatorExit).isEqualTo(expectedValidatorExit);
Assertions.assertThat(decodedWithdrawalRequest).isEqualTo(expectedWithdrawalRequest);
}
}

@ -18,25 +18,27 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
class ValidatorExitEncoderTest {
class WithdrawalRequestEncoderTest {
@Test
void shouldEncodeExit() {
final ValidatorExit exit =
new ValidatorExit(
void shouldEncodeWithdrawalRequest() {
final WithdrawalRequest withdrawalRequest =
new WithdrawalRequest(
Address.fromHexString("0x763c396673F9c391DCe3361A9A71C8E161388000"),
BLSPublicKey.fromHexString(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"),
GWei.of(5));
final Bytes encoded = ValidatorExitEncoder.encodeOpaqueBytes(exit);
final Bytes encoded = WithdrawalRequestEncoder.encodeOpaqueBytes(withdrawalRequest);
assertThat(encoded)
.isEqualTo(
Bytes.fromHexString(
"0xf84694763c396673f9c391dce3361a9a71c8e161388000b0b10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
"0xf84794763c396673f9c391dce3361a9a71c8e161388000b0b10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e05"));
}
}

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
@ -67,8 +68,8 @@ abstract class AbstractBlockProcessorTest {
void baseSetup() {
lenient().when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
lenient()
.when(protocolSpec.getExitsValidator())
.thenReturn(new ValidatorExitsValidator.ProhibitedExits());
.when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
blockProcessor =
new TestBlockProcessor(
transactionProcessor,

@ -52,7 +52,7 @@ class MainnetBlockBodyValidatorTest {
@Mock private ProtocolSpec protocolSpec;
@Mock private WithdrawalsValidator withdrawalsValidator;
@Mock private DepositsValidator depositsValidator;
@Mock private ValidatorExitsValidator exitsValidator;
@Mock private WithdrawalRequestValidator exitsValidator;
@BeforeEach
public void setUp() {
@ -66,8 +66,8 @@ class MainnetBlockBodyValidatorTest {
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);
lenient().when(protocolSpec.getWithdrawalRequestValidator()).thenReturn(exitsValidator);
lenient().when(exitsValidator.validateWithdrawalRequestsInBlock(any(), any())).thenReturn(true);
}
@Test
@ -155,10 +155,10 @@ class MainnetBlockBodyValidatorTest {
.setReceiptsRoot(BodyValidation.receiptsRoot(emptyList()))
.setLogsBloom(LogsBloomFilter.empty())
.setParentHash(blockchainSetupUtil.getBlockchain().getChainHeadHash())
.setExits(Optional.of(List.of())));
.setWithdrawalRequests(Optional.of(List.of())));
blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList());
when(exitsValidator.validateExitsInBlock(any(), any())).thenReturn(false);
when(exitsValidator.validateWithdrawalRequestsInBlock(any(), any())).thenReturn(false);
assertThat(
new MainnetBlockBodyValidator(protocolSchedule)

@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
@ -48,8 +49,8 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest {
@BeforeEach
public void setup() {
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getExitsValidator())
.thenReturn(new ValidatorExitsValidator.ProhibitedExits());
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
}
@Test

@ -1,81 +0,0 @@
/*
* 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<List<ValidatorExit>> maybeExits,
final boolean expectedValidity) {
assertThat(new PragueValidatorExitsValidator().validateValidatorExitParameter(maybeExits))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> 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<Arguments> 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();
}
}

@ -0,0 +1,82 @@
/*
* 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.WithdrawalRequestValidatorTestFixtures.blockWithMoreThanMaximumWithdrawalRequests;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsAndWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsMismatch;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsRootMismatch;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsWithoutWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithoutWithdrawalRequestsAndWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithoutWithdrawalRequestsWithWithdrawalRequestsRoot;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.WithdrawalRequestTestParameter;
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 PragueWithdrawalRequestValidatorTest {
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("paramsForValidateWithdrawalRequestParameter")
public void validateWithdrawalRequestParameter(
final String description,
final Optional<List<WithdrawalRequest>> maybeExits,
final boolean expectedValidity) {
assertThat(
new PragueWithdrawalRequestValidator().validateWithdrawalRequestParameter(maybeExits))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> paramsForValidateWithdrawalRequestParameter() {
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("validateWithdrawalRequestsInBlockParamsForPrague")
public void validateWithdrawalRequestsInBlock_WhenPrague(
final WithdrawalRequestTestParameter param, final boolean expectedValidity) {
assertThat(
new PragueWithdrawalRequestValidator()
.validateWithdrawalRequestsInBlock(param.block, param.expectedWithdrawalRequest))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> validateWithdrawalRequestsInBlockParamsForPrague() {
return Stream.of(
Arguments.of(blockWithWithdrawalRequestsAndWithdrawalRequestsRoot(), true),
Arguments.of(blockWithWithdrawalRequestsWithoutWithdrawalRequestsRoot(), false),
Arguments.of(blockWithoutWithdrawalRequestsWithWithdrawalRequestsRoot(), false),
Arguments.of(blockWithoutWithdrawalRequestsAndWithdrawalRequestsRoot(), false),
Arguments.of(blockWithWithdrawalRequestsRootMismatch(), false),
Arguments.of(blockWithWithdrawalRequestsMismatch(), false),
Arguments.of(blockWithMoreThanMaximumWithdrawalRequests(), false));
}
@Test
public void allowExitsShouldReturnTrue() {
assertThat(new PragueWithdrawalRequestValidator().allowWithdrawalRequests()).isTrue();
}
}

@ -19,8 +19,8 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
@ -64,11 +64,11 @@ public final class ValidationTestUtils {
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(Deposit::readFrom));
final Optional<List<ValidatorExit>> exits =
final Optional<List<WithdrawalRequest>> withdrawalRequests =
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(ValidatorExit::readFrom));
return new BlockBody(transactions, ommers, withdrawals, deposits, exits);
: Optional.of(input.readList(WithdrawalRequest::readFrom));
return new BlockBody(transactions, ommers, withdrawals, deposits, withdrawalRequests);
}
public static Block readBlock(final long num) throws IOException {

@ -1,77 +0,0 @@
/*
* 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<List<ValidatorExit>> maybeExits,
final boolean expectedValidity) {
assertThat(
new ValidatorExitsValidator.ProhibitedExits()
.validateValidatorExitParameter(maybeExits))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> 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<Arguments> 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();
}
}

@ -1,155 +0,0 @@
/*
* 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<List<ValidatorExit>> 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<List<ValidatorExit>> 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<List<ValidatorExit>> 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<List<ValidatorExit>> 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<List<ValidatorExit>> 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<List<ValidatorExit>> 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<ValidatorExit> validatorExits =
IntStream.range(0, MAX_EXITS_PER_BLOCK + 1).mapToObj(__ -> createExit()).toList();
final Optional<List<ValidatorExit>> 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<List<ValidatorExit>> maybeExits;
List<ValidatorExit> expectedExits;
public ValidateExitTestParameter(
final String description,
final Block block,
final Optional<List<ValidatorExit>> maybeExits) {
this(description, block, maybeExits, maybeExits.orElseGet(List::of));
}
public ValidateExitTestParameter(
final String description,
final Block block,
final Optional<List<ValidatorExit>> maybeExits,
final List<ValidatorExit> expectedExits) {
this.description = description;
this.block = block;
this.maybeExits = maybeExits;
this.expectedExits = expectedExits;
}
@Override
public String toString() {
return description;
}
}
}

@ -16,16 +16,17 @@ 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 static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -40,7 +41,7 @@ import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ValidatorExitContractHelperTest {
class WithdrawalRequestContractHelperTest {
private MutableWorldState worldState;
private MutableAccount contract;
@ -51,93 +52,96 @@ class ValidatorExitContractHelperTest {
}
@Test
public void popExitsFromQueue_ReadExitsCorrectly() {
final List<ValidatorExit> validatorExits = List.of(createExit(), createExit(), createExit());
public void popExitsFromQueue_ReadWithdrawalRequestsCorrectly() {
final List<WithdrawalRequest> validatorExits =
List.of(createExit(), createExit(), createExit());
loadContractStorage(worldState, validatorExits);
final List<ValidatorExit> poppedExits =
ValidatorExitContractHelper.popExitsFromQueue(worldState);
final List<WithdrawalRequest> poppedExits =
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState);
assertThat(poppedExits).isEqualTo(validatorExits);
}
@Test
public void popExitsFromQueue_whenContractCodeIsEmpty_ReturnsEmptyListOfExits() {
public void popExitsFromQueue_whenContractCodeIsEmpty_ReturnsEmptyListOfWithdrawalRequests() {
// Create account with empty code
final WorldUpdater updater = worldState.updater();
updater.createAccount(VALIDATOR_EXIT_ADDRESS);
updater.createAccount(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS);
updater.commit();
assertThat(ValidatorExitContractHelper.popExitsFromQueue(worldState)).isEmpty();
assertThat(WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState))
.isEmpty();
}
@Test
public void popExitsFromQueue_WhenMoreExits_UpdatesQueuePointers() {
public void popExitsFromQueue_WhenMoreWithdrawalRequests_UpdatesQueuePointers() {
// Loading contract with more than 16 exits
final List<ValidatorExit> validatorExits =
final List<WithdrawalRequest> 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());
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, validatorExits.size());
final List<ValidatorExit> poppedExits =
ValidatorExitContractHelper.popExitsFromQueue(worldState);
final List<WithdrawalRequest> poppedExits =
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(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);
assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 16);
assertContractStorageValue(WITHDRAWAL_REQUEST_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);
assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 28);
// We always reset the exit count after processing the queue
assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0);
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0);
}
@Test
public void popExitsFromQueue_WhenNoMoreExits_ZeroQueuePointers() {
final List<ValidatorExit> validatorExits = List.of(createExit(), createExit(), createExit());
public void popExitsFromQueue_WhenNoMoreWithdrawalRequests_ZeroQueuePointers() {
final List<WithdrawalRequest> 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());
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, validatorExits.size());
final List<ValidatorExit> poppedExits =
ValidatorExitContractHelper.popExitsFromQueue(worldState);
final List<WithdrawalRequest> poppedExits =
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(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);
assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0);
assertContractStorageValue(WITHDRAWAL_REQUEST_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);
assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 1);
// We always reset the exit count after processing the queue
assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0);
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0);
}
@Test
public void popExitsFromQueue_WhenNoExits_DoesNothing() {
public void popExitsFromQueue_WhenNoWithdrawalRequests_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);
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0);
final List<ValidatorExit> poppedExits =
ValidatorExitContractHelper.popExitsFromQueue(worldState);
final List<WithdrawalRequest> poppedExits =
WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(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);
assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0);
assertContractStorageValue(WITHDRAWAL_REQUEST_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);
assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 0);
// We always reset the exit count after processing the queue
assertContractStorageValue(EXIT_COUNT_STORAGE_SLOT, 0);
assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0);
}
private void assertContractStorageValue(final UInt256 slot, final int expectedValue) {
@ -145,53 +149,54 @@ class ValidatorExitContractHelperTest {
}
private void assertContractStorageValue(final UInt256 slot, final UInt256 expectedValue) {
assertThat(worldState.get(VALIDATOR_EXIT_ADDRESS).getStorageValue(slot))
assertThat(worldState.get(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS).getStorageValue(slot))
.isEqualTo(expectedValue);
}
private void loadContractStorage(
final MutableWorldState worldState, final List<ValidatorExit> exits) {
final MutableWorldState worldState, final List<WithdrawalRequest> withdrawalRequests) {
final WorldUpdater updater = worldState.updater();
contract = updater.getOrCreate(VALIDATOR_EXIT_ADDRESS);
contract = updater.getOrCreate(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS);
contract.setCode(
Bytes.fromHexString(
"0x61013680600a5f395ff33373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603014156101325760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013257600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460ed5780604402838201600302600401805490600101805490600101549160601b8160a01c17835260601b8160a01c17826020015260601b906040015260010160a6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f546001546002828201116101205750505f610126565b01600290035b5f555f6001556044025ff35b5f5ffd"));
// excess exits
// excess requests
contract.setStorageValue(UInt256.valueOf(0), UInt256.valueOf(0));
// exits count
contract.setStorageValue(UInt256.valueOf(1), UInt256.valueOf(exits.size()));
// exits queue head pointer
// requests count
contract.setStorageValue(UInt256.valueOf(1), UInt256.valueOf(withdrawalRequests.size()));
// requests queue head pointer
contract.setStorageValue(UInt256.valueOf(2), UInt256.valueOf(0));
// exits queue tail pointer
contract.setStorageValue(UInt256.valueOf(3), UInt256.valueOf(exits.size()));
// requests queue tail pointer
contract.setStorageValue(UInt256.valueOf(3), UInt256.valueOf(withdrawalRequests.size()));
int offset = 4;
for (int i = 0; i < exits.size(); i++) {
final ValidatorExit exit = exits.get(i);
for (int i = 0; i < withdrawalRequests.size(); i++) {
final WithdrawalRequest request = withdrawalRequests.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())));
Bytes.fromHexString("0x000000000000000000000000"), request.getSourceAddress())));
// validator_pubkey
contract.setStorageValue(
UInt256.valueOf(offset++), UInt256.fromBytes(exit.getValidatorPubKey().slice(0, 32)));
UInt256.valueOf(offset++), UInt256.fromBytes(request.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"))));
request.getValidatorPubKey().slice(32, 16),
request.getAmount().toBytes(), // 8 bytes for amount
Bytes.fromHexString("0x0000000000000000"))));
}
updater.commit();
}
private ValidatorExit createExit() {
return new ValidatorExit(
Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random()));
private WithdrawalRequest createExit() {
return new WithdrawalRequest(
Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random()), GWei.ONE);
}
}

@ -0,0 +1,76 @@
/*
* 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.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsAndWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithWithdrawalRequestsWithoutWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithoutWithdrawalRequestsAndWithdrawalRequestsRoot;
import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.blockWithoutWithdrawalRequestsWithWithdrawalRequestsRoot;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidatorTestFixtures.WithdrawalRequestTestParameter;
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 WithdrawalRequestValidatorTest {
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("paramsForValidateWithdrawalRequestParameter")
public void validateWithdrawalRequestParameter(
final String description,
final Optional<List<WithdrawalRequest>> maybeExits,
final boolean expectedValidity) {
assertThat(new ProhibitedWithdrawalRequests().validateWithdrawalRequestParameter(maybeExits))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> paramsForValidateWithdrawalRequestParameter() {
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("validateWithdrawalRequestsInBlockParamsForProhibited")
public void validateWithdrawalRequestsInBlock_WhenProhibited(
final WithdrawalRequestTestParameter param, final boolean expectedValidity) {
assertThat(
new ProhibitedWithdrawalRequests()
.validateWithdrawalRequestsInBlock(param.block, param.expectedWithdrawalRequest))
.isEqualTo(expectedValidity);
}
private static Stream<Arguments> validateWithdrawalRequestsInBlockParamsForProhibited() {
return Stream.of(
Arguments.of(blockWithWithdrawalRequestsAndWithdrawalRequestsRoot(), false),
Arguments.of(blockWithWithdrawalRequestsWithoutWithdrawalRequestsRoot(), false),
Arguments.of(blockWithoutWithdrawalRequestsWithWithdrawalRequestsRoot(), false),
Arguments.of(blockWithoutWithdrawalRequestsAndWithdrawalRequestsRoot(), true));
}
@Test
public void allowExitsShouldReturnFalse() {
assertThat(new ProhibitedWithdrawalRequests().allowWithdrawalRequests()).isFalse();
}
}

@ -0,0 +1,175 @@
/*
* 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.WithdrawalRequestContractHelper.MAX_WITHDRAWAL_REQUESTS_PER_BLOCK;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BLSPublicKey;
import org.hyperledger.besu.datatypes.GWei;
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.WithdrawalRequest;
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 WithdrawalRequestValidatorTestFixtures {
private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator();
static WithdrawalRequestTestParameter blockWithWithdrawalRequestsAndWithdrawalRequestsRoot() {
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequests =
Optional.of(List.of(createWithdrawalRequest()));
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create()
.setWithdrawalRequestsRoot(
BodyValidation.withdrawalRequestsRoot(maybeWithdrawalRequests.get()))
.setWithdrawalRequests(maybeWithdrawalRequests);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with withdrawal requests and withdrawal_requests_root",
block,
maybeWithdrawalRequests);
}
static WithdrawalRequestTestParameter blockWithoutWithdrawalRequestsWithWithdrawalRequestsRoot() {
final Optional<List<WithdrawalRequest>> maybeExits = Optional.empty();
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create()
.setWithdrawalRequestsRoot(Hash.EMPTY)
.setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with withdrawal_requests_root but without withdrawal requests", block, maybeExits);
}
static WithdrawalRequestTestParameter blockWithWithdrawalRequestsWithoutWithdrawalRequestsRoot() {
final Optional<List<WithdrawalRequest>> maybeExits =
Optional.of(List.of(createWithdrawalRequest()));
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create().setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with withdrawal requests but without withdrawal_requests_root", block, maybeExits);
}
static WithdrawalRequestTestParameter blockWithoutWithdrawalRequestsAndWithdrawalRequestsRoot() {
final Optional<List<WithdrawalRequest>> maybeExits = Optional.empty();
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create().setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block without withdrawal requests and withdrawal_requests_root", block, maybeExits);
}
static WithdrawalRequestTestParameter blockWithWithdrawalRequestsRootMismatch() {
final Optional<List<WithdrawalRequest>> maybeExits =
Optional.of(List.of(createWithdrawalRequest()));
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create()
.setWithdrawalRequestsRoot(Hash.EMPTY)
.setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with withdrawal_requests_root mismatch", block, maybeExits);
}
static WithdrawalRequestTestParameter blockWithWithdrawalRequestsMismatch() {
final Optional<List<WithdrawalRequest>> maybeExits =
Optional.of(List.of(createWithdrawalRequest(), createWithdrawalRequest()));
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create()
.setWithdrawalRequestsRoot(BodyValidation.withdrawalRequestsRoot(maybeExits.get()))
.setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with withdrawal requests mismatch",
block,
maybeExits,
List.of(createWithdrawalRequest()));
}
static WithdrawalRequestTestParameter blockWithMoreThanMaximumWithdrawalRequests() {
final List<WithdrawalRequest> validatorExits =
IntStream.range(0, MAX_WITHDRAWAL_REQUESTS_PER_BLOCK + 1)
.mapToObj(__ -> createWithdrawalRequest())
.toList();
final Optional<List<WithdrawalRequest>> maybeExits = Optional.of(validatorExits);
final BlockDataGenerator.BlockOptions blockOptions =
BlockDataGenerator.BlockOptions.create()
.setWithdrawalRequestsRoot(BodyValidation.withdrawalRequestsRoot(maybeExits.get()))
.setWithdrawalRequests(maybeExits);
final Block block = blockDataGenerator.block(blockOptions);
return new WithdrawalRequestTestParameter(
"Block with more than maximum withdrawal requests", block, maybeExits);
}
static WithdrawalRequest createWithdrawalRequest() {
return new WithdrawalRequest(
Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random()), GWei.ONE);
}
static class WithdrawalRequestTestParameter {
String description;
Block block;
Optional<List<WithdrawalRequest>> maybeWithdrawalRequest;
List<WithdrawalRequest> expectedWithdrawalRequest;
public WithdrawalRequestTestParameter(
final String description,
final Block block,
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequest) {
this(description, block, maybeWithdrawalRequest, maybeWithdrawalRequest.orElseGet(List::of));
}
public WithdrawalRequestTestParameter(
final String description,
final Block block,
final Optional<List<WithdrawalRequest>> maybeWithdrawalRequest,
final List<WithdrawalRequest> expectedWithdrawalRequest) {
this.description = description;
this.block = block;
this.maybeWithdrawalRequest = maybeWithdrawalRequest;
this.expectedWithdrawalRequest = expectedWithdrawalRequest;
}
@Override
public String toString() {
return description;
}
}
}

@ -22,8 +22,8 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
@ -153,19 +153,19 @@ public class GetBodiesFromPeerTask extends AbstractPeerRequestTask<List<Block>>
private final Bytes32 ommersHash;
private final Bytes32 withdrawalsRoot;
private final Bytes32 depositsRoot;
private final Bytes32 exitsRoot;
private final Bytes32 withdrawalRequestsRoot;
public BodyIdentifier(
final Bytes32 transactionsRoot,
final Bytes32 ommersHash,
final Bytes32 withdrawalsRoot,
final Bytes32 depositsRoot,
final Bytes32 exitsRoot) {
final Bytes32 withdrawalRequestsRoot) {
this.transactionsRoot = transactionsRoot;
this.ommersHash = ommersHash;
this.withdrawalsRoot = withdrawalsRoot;
this.depositsRoot = depositsRoot;
this.exitsRoot = exitsRoot;
this.withdrawalRequestsRoot = withdrawalRequestsRoot;
}
public BodyIdentifier(final BlockBody body) {
@ -174,7 +174,7 @@ public class GetBodiesFromPeerTask extends AbstractPeerRequestTask<List<Block>>
body.getOmmers(),
body.getWithdrawals(),
body.getDeposits(),
body.getExits());
body.getWithdrawalRequests());
}
public BodyIdentifier(
@ -182,13 +182,13 @@ public class GetBodiesFromPeerTask extends AbstractPeerRequestTask<List<Block>>
final List<BlockHeader> ommers,
final Optional<List<Withdrawal>> withdrawals,
final Optional<List<Deposit>> deposits,
final Optional<List<ValidatorExit>> exits) {
final Optional<List<WithdrawalRequest>> withdrawalRequests) {
this(
BodyValidation.transactionsRoot(transactions),
BodyValidation.ommersHash(ommers),
withdrawals.map(BodyValidation::withdrawalsRoot).orElse(null),
deposits.map(BodyValidation::depositsRoot).orElse(null),
exits.map(BodyValidation::exitsRoot).orElse(null));
withdrawalRequests.map(BodyValidation::withdrawalRequestsRoot).orElse(null));
}
public BodyIdentifier(final BlockHeader header) {
@ -197,7 +197,7 @@ public class GetBodiesFromPeerTask extends AbstractPeerRequestTask<List<Block>>
header.getOmmersHash(),
header.getWithdrawalsRoot().orElse(null),
header.getDepositsRoot().orElse(null),
header.getExitsRoot().orElse(null));
header.getWithdrawalRequestsRoot().orElse(null));
}
@Override
@ -209,12 +209,13 @@ public class GetBodiesFromPeerTask extends AbstractPeerRequestTask<List<Block>>
&& Objects.equals(ommersHash, that.ommersHash)
&& Objects.equals(withdrawalsRoot, that.withdrawalsRoot)
&& Objects.equals(depositsRoot, that.depositsRoot)
&& Objects.equals(exitsRoot, that.exitsRoot);
&& Objects.equals(withdrawalRequestsRoot, that.withdrawalRequestsRoot);
}
@Override
public int hashCode() {
return Objects.hash(transactionsRoot, ommersHash, withdrawalsRoot, depositsRoot, exitsRoot);
return Objects.hash(
transactionsRoot, ommersHash, withdrawalsRoot, depositsRoot, withdrawalRequestsRoot);
}
}
}

@ -25,8 +25,8 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Deposit;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.PeerMessageTaskTest;
import java.util.ArrayList;
@ -124,11 +124,12 @@ public class GetBodiesFromPeerTaskTest extends PeerMessageTaskTest<List<Block>>
@Test
public void assertBodyIdentifierUsesExitsToGenerateBodyIdentifiers() {
final ValidatorExit validatorExit =
new ValidatorExit(
final WithdrawalRequest validatorExit =
new WithdrawalRequest(
Address.fromHexString("0x763c396673F9c391DCe3361A9A71C8E161388000"),
BLSPublicKey.fromHexString(
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"));
"0xb10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee99223555d8601f0cb3bcc4ce1af9864779a416e"),
GWei.ONE);
// Empty body block
final BlockBody emptyBodyBlock = BlockBody.empty();

@ -90,7 +90,7 @@ public class ChainForTestCreator {
blockHeader.getExcessBlobGas().orElse(null),
blockHeader.getParentBeaconBlockRoot().orElse(null),
blockHeader.getDepositsRoot().orElse(null),
blockHeader.getExitsRoot().orElse(null),
blockHeader.getWithdrawalRequestsRoot().orElse(null),
new MainnetBlockHeaderFunctions());
}

@ -34,8 +34,8 @@ import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ParsedExtraData;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.ValidatorExit;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
@ -167,7 +167,7 @@ public class BlockchainReferenceTestCaseSpec {
@JsonProperty("nonce") final String nonce,
@JsonProperty("withdrawalsRoot") final String withdrawalsRoot,
@JsonProperty("depositsRoot") final String depositsRoot,
@JsonProperty("exitsRoot") final String exitsRoot,
@JsonProperty("withdrawalRequestsRoot") final String withdrawalRequestsRoot,
@JsonProperty("dataGasUsed")
final String dataGasUsed, // TODO: remove once reference tests have been updated
@JsonProperty("excessDataGas")
@ -206,7 +206,7 @@ public class BlockchainReferenceTestCaseSpec {
: excessBlobGas != null ? BlobGas.fromHexString(excessBlobGas) : null,
parentBeaconBlockRoot != null ? Bytes32.fromHexString(parentBeaconBlockRoot) : null,
depositsRoot != null ? Hash.fromHexString(depositsRoot) : null,
exitsRoot != null ? Hash.fromHexString(exitsRoot) : null,
withdrawalRequestsRoot != null ? Hash.fromHexString(withdrawalRequestsRoot) : null,
new BlockHeaderFunctions() {
@Override
public Hash hash(final BlockHeader header) {
@ -297,7 +297,7 @@ public class BlockchainReferenceTestCaseSpec {
: Optional.of(input.readList(Deposit::readFrom)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(ValidatorExit::readFrom)));
: Optional.of(input.readList(WithdrawalRequest::readFrom)));
return new Block(header, body);
}
}

@ -145,7 +145,7 @@ public class ReferenceTestEnv extends BlockHeader {
currentExcessBlobGas == null ? null : BlobGas.of(Long.decode(currentExcessBlobGas)),
beaconRoot == null ? null : Bytes32.fromHexString(beaconRoot),
null, // depositsRoot
null, // exitsRoot
null, // withdrawalRequestsRoot
new MainnetBlockHeaderFunctions());
this.parentDifficulty = parentDifficulty;
this.parentBaseFee = parentBaseFee;

@ -89,7 +89,7 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
original.getWithdrawalsValidator(),
original.getWithdrawalsProcessor(),
original.getDepositsValidator(),
original.getExitsValidator(),
original.getWithdrawalRequestValidator(),
original.isPoS(),
original.isReplayProtectionSupported());
}

@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = '78xbZ20PDB9CDcaSVY92VA8cXWGu4GwaZkvegWgep24='
knownHash = 'wGHrCxNllf6j/YOB/6UzH5QXYcwwB2AEmYC4GkQVUuE='
}
check.dependsOn('checkAPIChanges')

@ -59,10 +59,10 @@ public interface BlockBody {
Optional<? extends List<? extends Deposit>> getDeposits();
/**
* Returns the list of exits of the block.
* Returns the list of withdrawal requests of the block.
*
* @return The list of exits of the block.
* @return The list of withdrawal requests of the block.
*/
@Unstable
Optional<? extends List<? extends ValidatorExit>> getExits();
Optional<? extends List<? extends WithdrawalRequest>> getWithdrawalRequests();
}

@ -131,14 +131,14 @@ public interface BlockHeader extends ProcessableBlockHeader {
Optional<? extends Hash> getDepositsRoot();
/**
* The Keccak 256-bit hash of the root node of the trie structure populated with each exit in the
* exits list portion of the block.
* The Keccak 256-bit hash of the root node of the trie structure populated with each withdrawal
* request in the withdrawal request list portion of the block.
*
* @return The Keccak 256-bit hash of the root node of the trie structure populated with each exit
* in the exits list portion of the block.
* @return The Keccak 256-bit hash of the root node of the trie structure populated with each
* withdrawal request in the withdrawal request list portion of the block.
*/
@Unstable
Optional<? extends Hash> getExitsRoot();
Optional<? extends Hash> getWithdrawalRequestsRoot();
/**
* The excess_blob_gas of this header.

@ -15,27 +15,35 @@
package org.hyperledger.besu.plugin.data;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.PublicKey;
import org.hyperledger.besu.plugin.Unstable;
/**
* A deposit is a system-level operation to support validator exitys that are pushed from the EVM to
* beacon chain.
* A withdrawal request is an operation sent to the Beacon Node for processing validator withdrawal
* requests (partial or complete)
*/
@Unstable
public interface ValidatorExit {
public interface WithdrawalRequest {
/**
* Withdrawal credential (0x01) associated with the validator to be exited
* Withdrawal credential (0x01) associated with the validator
*
* @return withdrawal credential address
*/
Address getSourceAddress();
/**
* Public key of the validator that is going to exit
* Public key of the validator that is going to request a withdrawal
*
* @return public key of validator
*/
PublicKey getValidatorPubKey();
/**
* The amount for withdrawal
*
* @return withdrawal amount
*/
GWei getAmount();
}
Loading…
Cancel
Save