EIP-2935: Serve historical block hashes from state (#7080)

* EIP-2935: Serve historical block hashes from state

Refactor the BlockHashOperation to move hash lookup into the
ProtocolSpec, and combine logic with beacon root storage.

Update t8n test to use block hash list in new format

Signed-off-by: Danno Ferrin <danno@numisight.com>
Author: Gabriel-Trintinalia gabriel.trintinalia@consensys.net
Co-authored-by: Danno Ferrin danno@numisight.com
Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
pull/7092/head
Danno Ferrin 6 months ago committed by GitHub
parent 2db3705b1d
commit 821daf12b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/06_prague_getPayloadV4.json
  2. 6
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/09_prague_newPayloadV4.json
  3. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/10_prague_forkchoiceUpdatedV3.json
  4. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/11_prague_getPayloadV4.json
  5. 8
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/12_cancun_newPayloadV4.json
  6. 10
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/14_prague_forkchoiceUpdatedV3.json
  7. 12
      acceptance-tests/tests/src/test/resources/jsonrpc/engine/prague/test-cases/15_prague_getPayloadV4.json
  8. 3
      besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java
  9. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java
  10. 12
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java
  11. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java
  12. 6
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java
  13. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java
  14. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java
  15. 10
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  16. 2
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockSelectionContext.java
  17. 10
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java
  18. 1
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  19. 1
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java
  20. 2
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java
  21. 16
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java
  22. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
  23. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  24. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  25. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ParentBeaconBlockRootHelper.java
  26. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpec.java
  27. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  28. 31
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessor.java
  29. 52
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/CancunBlockHashProcessor.java
  30. 38
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/FrontierBlockHashProcessor.java
  31. 153
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java
  32. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java
  33. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java
  34. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java
  35. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java
  36. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java
  37. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java
  38. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  39. 30
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/BlockHashLookup.java
  40. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookup.java
  41. 2
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java
  42. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java
  43. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorTest.java
  44. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java
  45. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java
  46. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java
  47. 187
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessorTest.java
  48. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContractTest.java
  49. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java
  50. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java
  51. 6
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationTest.java
  52. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java
  53. 64
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookupTest.java
  54. 6
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  55. 3
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java
  56. 37
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java
  57. 2
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java
  58. 206
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/t8n/T8nBlockchain.java
  59. 15
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-blockhash.json
  60. 8
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java
  61. 3
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java
  62. 2
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  63. 1
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java
  64. 6
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  65. 9
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  66. 10
      evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java
  67. 42
      evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java
  68. 2
      evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java
  69. 2
      evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java
  70. 2
      evm/src/test/java/org/hyperledger/besu/evm/frame/MessageFrameTest.java
  71. 2
      evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java
  72. 42
      evm/src/test/java/org/hyperledger/besu/evm/operations/BlockHashOperationTest.java
  73. 4
      evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java
  74. 2
      evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java
  75. 2
      evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java
  76. 2
      evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java
  77. 8
      evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java
  78. 2
      evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java

@ -14,7 +14,7 @@
"executionPayload": {
"parentHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0x9d7467981e875b5b81ec6b9ab44300fa390d253b5d83da24fe58700f66a3925e",
"stateRoot": "0xc809e88f0456bc45340fa03cedd1c1d658c1b947a7f23e6a17ed108af60d3afc",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -35,7 +35,7 @@
}
],
"blockNumber": "0x2",
"blockHash": "0xb4be7cd1193f86d725c93275f9f9fe902c5fc34e1758b9ebcac371de370a1bdd",
"blockHash": "0x41d9ad8707752c4e6c46eecc956e79fc1fafc44fc0cc5fd7a5b981b02e216932",
"blobGasUsed": "0x0",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
},

@ -6,7 +6,7 @@
{
"parentHash": "0x2b0ef8ec0a5f8b3bd5e64cde3f1ed7f61c472b79bea9e44598921c3c37890a6a",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xdcd8284a4904f95448a522e4c47d9c2f1b4541c5f6f711b42692fb26e1e0d986",
"stateRoot": "0xb25de115723e262bb1d5da7e3576dea47c3fe7e8ca52c430e6f9def712de830d",
"logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -36,7 +36,7 @@
}
],
"blockNumber": "0x2",
"blockHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"blockHash": "0xcf161dd8d9811f119f7377aa7cccd933955d1a6deb538200785191fb938fec5e",
"receiptsRoot": "0x79ee3424eb720a3ad4b1c5a372bb8160580cbe4d893778660f34213c685627a9",
"blobGasUsed": "0x0"
},
@ -50,7 +50,7 @@
"id": 67,
"result": {
"status": "VALID",
"latestValidHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"latestValidHash": "0xcf161dd8d9811f119f7377aa7cccd933955d1a6deb538200785191fb938fec5e",
"validationError": null
}
},

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

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV4",
"params": [
"0x282643832633dccf"
"0x28264390264496cf"
],
"id": 67
},
@ -12,9 +12,9 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0x57291a8c6fd912648046cae66dcae8b3f3d00b49e679c69566cd11286490458c",
"parentHash": "0xcf161dd8d9811f119f7377aa7cccd933955d1a6deb538200785191fb938fec5e",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0xdcd8284a4904f95448a522e4c47d9c2f1b4541c5f6f711b42692fb26e1e0d986",
"stateRoot": "0xf786274a52a498c61961f6faa82c8f2df6c65bf6f0cbdd53216821bc981494a4",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -29,7 +29,7 @@
"depositReceipts": [],
"withdrawalRequests": [],
"blockNumber": "0x3",
"blockHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"blockHash": "0xf1626764177578f1bf1f0fcce3f5ecab4bf7e4c8c7754d1d30e7c0b98334ad98",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blobGasUsed": "0x0"
},

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

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

@ -3,7 +3,7 @@
"jsonrpc": "2.0",
"method": "engine_getPayloadV4",
"params": [
"0x282643f22efc45bf"
"0x282643d998c41241"
],
"id": 67
},
@ -12,9 +12,9 @@
"id": 67,
"result": {
"executionPayload": {
"parentHash": "0xbac2080fec3fecfb46b4fa1ce20767021f0e53e85b017d5b6a5f4ec43bdbf18b",
"parentHash": "0xf1626764177578f1bf1f0fcce3f5ecab4bf7e4c8c7754d1d30e7c0b98334ad98",
"feeRecipient": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"stateRoot": "0x4a387af05417b5767993052457ca85b2a5a172b3f809eb5cbcf17f070f398c3f",
"stateRoot": "0x4021e9832c7d9945cb681d670c238c6178bd2184e957a474a7989d06f7171349",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x1c9c380",
@ -36,10 +36,10 @@
"validatorPubKey": "0x8706d19a62f28a6a6549f96c5adaebac9124a61d44868ec94f6d2d707c6a2f82c9162071231dfeb40e24bfde4ffdf243"
}
],
"receiptsRoot": "0x765bd9d63cc10fa47117d6cc0958f15e55a3bde540d4ed15d220f573fbb82cba",
"blockNumber": "0x4",
"blobGasUsed": "0x0",
"blockHash": "0xe589c7673025f6844ffbaeddcf38d77d652669c60412e8506a2ae62ac80e9de4",
"blockNumber": "0x4"
"receiptsRoot": "0x765bd9d63cc10fa47117d6cc0958f15e55a3bde540d4ed15d220f573fbb82cba",
"blockHash": "0xc5c15f6a88d3576927b16de1c635cabf46bb9419a3bbb3be156ddbe229de5e64"
},
"blockValue": "0x12855dcd153473b",
"blobsBundle": {

@ -31,7 +31,6 @@ 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.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.BlockTraceResult;
@ -217,7 +216,7 @@ public class TraceServiceImpl implements TraceService {
transaction,
protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(header),
tracer,
new CachingBlockHashLookup(header, blockchain),
protocolSpec.getBlockHashProcessor().getBlockHashLookup(header, blockchain),
false,
blobGasPrice);

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei;
@ -26,8 +27,6 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.List;
@ -95,7 +94,8 @@ public class ExecuteTransactionStep implements Function<TransactionTrace, Transa
maybeParentHeader
.map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent))
.orElse(BlobGas.ZERO));
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain);
final BlockHashLookup blockHashLookup =
protocolSpec.getBlockHashProcessor().getBlockHashLookup(header, blockchain);
result =
transactionProcessor.processTransaction(
chainUpdater.getNextUpdater(),

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash;
@ -30,8 +31,6 @@ 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.TransactionValidationParams;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import java.util.List;
import java.util.Optional;
@ -90,7 +89,8 @@ public class BlockReplay {
return performActionWithBlock(
blockHash,
(body, header, blockchain, transactionProcessor, protocolSpec) -> {
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain);
final BlockHashLookup blockHashLookup =
protocolSpec.getBlockHashProcessor().getBlockHashLookup(header, blockchain);
final Wei blobGasPrice =
protocolSpec
.getFeeMarket()
@ -137,7 +137,7 @@ public class BlockReplay {
blockHeader,
transaction,
spec.getMiningBeneficiaryCalculator().calculateBeneficiary(blockHeader),
new CachingBlockHashLookup(blockHeader, blockchain),
spec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.blockReplay(),
blobGasPrice);
@ -180,6 +180,10 @@ public class BlockReplay {
return Optional.empty();
}
public ProtocolSpec getProtocolSpec(final BlockHeader header) {
return protocolSchedule.getByBlockHeader(header);
}
@FunctionalInterface
public interface BlockAction<T> {
Optional<T> perform(

@ -19,7 +19,6 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -69,7 +68,10 @@ public class BlockTracer {
transaction,
header.getCoinbase(),
tracer,
new CachingBlockHashLookup(header, blockchain),
blockReplay
.getProtocolSpec(header)
.getBlockHashProcessor()
.getBlockHashLookup(header, blockchain),
false,
blobGasPrice);
final List<TraceFrame> traceFrames = tracer.copyTraceFrames();

@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
@ -192,7 +191,10 @@ public class TransactionTracer {
transaction,
header.getCoinbase(),
tracer,
new CachingBlockHashLookup(header, blockchain),
blockReplay
.getProtocolSpec(header)
.getBlockHashProcessor()
.getBlockHashLookup(header, blockchain),
false,
ImmutableTransactionValidationParams.builder().isAllowFutureNonce(true).build(),
blobGasPrice);

@ -47,6 +47,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.PoWHasher;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.blockhash.FrontierBlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -147,6 +148,7 @@ public class EthGetTransactionReceiptTest {
Optional.empty(),
null,
null,
new FrontierBlockHashProcessor(),
true,
true);
private final ProtocolSpec statusTransactionTypeSpec =
@ -177,6 +179,7 @@ public class EthGetTransactionReceiptTest {
Optional.empty(),
null,
null,
new FrontierBlockHashProcessor(),
true,
true);

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
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.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
@ -87,6 +88,7 @@ public class TransactionTracerTest {
@Mock private ProtocolSpec protocolSpec;
@Mock private GasCalculator gasCalculator;
@Mock private BlockHashProcessor blockHashProcessor;
@Mock private Tracer.TraceableState mutableWorldState;
@ -123,6 +125,7 @@ public class TransactionTracerTest {
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator);
when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager);
lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L);
}

@ -46,7 +46,6 @@ import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.DepositsValidator;
import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
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;
@ -208,10 +207,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
final List<BlockHeader> ommers = maybeOmmers.orElse(selectOmmers());
maybeParentBeaconBlockRoot.ifPresent(
bytes32 ->
ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot(
disposableWorldState.updater(), timestamp, bytes32));
newProtocolSpec
.getBlockHashProcessor()
.processBlockHashes(
protocolContext.getBlockchain(), disposableWorldState, processableBlockHeader);
throwIfStopped();
@ -401,6 +400,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protocolSpec.getFeeMarket(),
protocolSpec.getGasCalculator(),
protocolSpec.getGasLimitCalculator(),
protocolSpec.getBlockHashProcessor(),
pluginTransactionSelector,
ethScheduler);

@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -27,6 +28,7 @@ public record BlockSelectionContext(
MiningParameters miningParameters,
GasCalculator gasCalculator,
GasLimitCalculator gasLimitCalculator,
BlockHashProcessor blockHashProcessor,
ProcessableBlockHeader processableBlockHeader,
FeeMarket feeMarket,
Wei blobGasPrice,

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.ethereum.blockcreation.txselection;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG;
@ -39,10 +40,9 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
@ -114,6 +114,7 @@ public class BlockTransactionSelector {
final FeeMarket feeMarket,
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final BlockHashProcessor blockHashProcessor,
final PluginTransactionSelector pluginTransactionSelector,
final EthScheduler ethScheduler) {
this.transactionProcessor = transactionProcessor;
@ -127,6 +128,7 @@ public class BlockTransactionSelector {
miningParameters,
gasCalculator,
gasLimitCalculator,
blockHashProcessor,
processableBlockHeader,
feeMarket,
blobGasPrice,
@ -324,7 +326,9 @@ public class BlockTransactionSelector {
private TransactionProcessingResult processTransaction(
final PendingTransaction pendingTransaction, final WorldUpdater worldStateUpdater) {
final BlockHashLookup blockHashLookup =
new CachingBlockHashLookup(blockSelectionContext.processableBlockHeader(), blockchain);
blockSelectionContext
.blockHashProcessor()
.getBlockHashLookup(blockSelectionContext.processableBlockHeader(), blockchain);
return transactionProcessor.processTransaction(
worldStateUpdater,
blockSelectionContext.processableBlockHeader(),

@ -1272,6 +1272,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
getFeeMarket(),
new LondonGasCalculator(),
GasLimitCalculator.constant(),
protocolSchedule.getByBlockHeader(blockHeader).getBlockHashProcessor(),
transactionSelectionService.createPluginTransactionSelector(),
ethScheduler);

@ -48,6 +48,7 @@ public class MinPriorityFeePerGasTransactionSelectorTest {
miningParameters,
null,
null,
null,
mock(ProcessableBlockHeader.class),
null,
null,

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@ -43,7 +44,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.vm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -32,6 +33,7 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.ethereum.debug.TraceOptions;
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.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
@ -72,11 +74,13 @@ public class TraceTransactionIntegrationTest {
blockchain = contextTestFixture.getBlockchain();
worldStateArchive = contextTestFixture.getStateArchive();
final ProtocolSchedule protocolSchedule = contextTestFixture.getProtocolSchedule();
transactionProcessor =
protocolSchedule
.getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader())
.getTransactionProcessor();
blockHashLookup = new CachingBlockHashLookup(genesisBlock.getHeader(), blockchain);
ProtocolSpec protocolSpec =
protocolSchedule.getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader());
transactionProcessor = protocolSpec.getTransactionProcessor();
blockHashLookup =
protocolSpec
.getBlockHashProcessor()
.getBlockHashLookup(genesisBlock.getHeader(), blockchain);
}
@Test
@ -178,7 +182,7 @@ public class TraceTransactionIntegrationTest {
transaction,
genesisBlockHeader.getCoinbase(),
tracer,
new CachingBlockHashLookup(genesisBlockHeader, blockchain),
blockHashLookup,
false,
Wei.ZERO);

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
@ -33,8 +34,6 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldState;
@ -106,11 +105,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
if (blockHeader.getParentBeaconBlockRoot().isPresent()) {
final WorldUpdater updater = worldState.updater();
ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot(
updater, blockHeader.getTimestamp(), blockHeader.getParentBeaconBlockRoot().get());
}
protocolSpec.getBlockHashProcessor().processBlockHashes(blockchain, worldState, blockHeader);
for (final Transaction transaction : transactions) {
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
@ -119,7 +114,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain);
final BlockHashLookup blockHashLookup =
protocolSpec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);

@ -31,6 +31,9 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder.BlockValidatorBuilder;
import org.hyperledger.besu.ethereum.mainnet.blockhash.CancunBlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.blockhash.FrontierBlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.blockhash.PragueBlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -169,6 +172,7 @@ public abstract class MainnetProtocolSpecs {
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
.miningBeneficiaryCalculator(BlockHeader::getCoinbase)
.evmConfiguration(evmConfiguration)
.blockHashProcessor(new FrontierBlockHashProcessor())
.name("Frontier");
}
@ -719,6 +723,7 @@ public abstract class MainnetProtocolSpecs {
SHANGHAI_INIT_CODE_SIZE_LIMIT))
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::cancun)
.blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::cancunBlockHeaderValidator)
.blockHashProcessor(new CancunBlockHashProcessor())
.name("Cancun");
}
@ -765,6 +770,8 @@ public abstract class MainnetProtocolSpecs {
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague)
.depositsValidator(new DepositsValidator.AllowedDeposits(depositContractAddress))
.withdrawalRequestsValidator(new PragueWithdrawalRequestValidator())
.blockHashProcessor(
new PragueBlockHashProcessor(genesisConfigOptions.getPragueTime().orElse(0)))
.name("Prague");
}

@ -18,6 +18,7 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PER
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.collections.trie.BytesTrieSet;
import org.hyperledger.besu.datatypes.AccessListEntry;
@ -31,7 +32,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.code.CodeV0;

@ -37,8 +37,8 @@ public interface ParentBeaconBlockRootHelper {
see EIP-4788: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4788.md
*/
// If code is not deployed don't do anything
final MutableAccount account = worldUpdater.getOrCreate(BEACON_ROOTS_ADDRESS);
if (Hash.EMPTY.equals(account.getCodeHash())) {
final MutableAccount account = worldUpdater.getAccount(BEACON_ROOTS_ADDRESS);
if (account == null || Hash.EMPTY.equals(account.getCodeHash())) {
return;
}

@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.evm.EVM;
@ -77,7 +78,7 @@ public class ProtocolSpec {
private final Optional<WithdrawalsProcessor> withdrawalsProcessor;
private final DepositsValidator depositsValidator;
private final WithdrawalRequestValidator withdrawalRequestValidator;
private final BlockHashProcessor blockHashProcessor;
private final boolean isPoS;
private final boolean isReplayProtectionSupported;
@ -109,6 +110,7 @@ public class ProtocolSpec {
* @param withdrawalsValidator the withdrawals validator to use
* @param withdrawalsProcessor the Withdrawals processor to use
* @param depositsValidator the withdrawals validator to use
* @param blockHashProcessor the blockHash processor to use
* @param isPoS indicates whether the current spec is PoS
* @param isReplayProtectionSupported indicates whether the current spec supports replay
* protection
@ -140,6 +142,7 @@ public class ProtocolSpec {
final Optional<WithdrawalsProcessor> withdrawalsProcessor,
final DepositsValidator depositsValidator,
final WithdrawalRequestValidator withdrawalRequestValidator,
final BlockHashProcessor blockHashProcessor,
final boolean isPoS,
final boolean isReplayProtectionSupported) {
this.name = name;
@ -168,6 +171,7 @@ public class ProtocolSpec {
this.withdrawalsProcessor = withdrawalsProcessor;
this.depositsValidator = depositsValidator;
this.withdrawalRequestValidator = withdrawalRequestValidator;
this.blockHashProcessor = blockHashProcessor;
this.isPoS = isPoS;
this.isReplayProtectionSupported = isReplayProtectionSupported;
}
@ -377,6 +381,10 @@ public class ProtocolSpec {
return withdrawalRequestValidator;
}
public BlockHashProcessor getBlockHashProcessor() {
return blockHashProcessor;
}
/**
* Returns true if the network is running Proof of Stake
*

@ -27,6 +27,7 @@ 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.blockhash.BlockHashProcessor;
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;
@ -78,6 +79,7 @@ public class ProtocolSpecBuilder {
private DepositsValidator depositsValidator = new DepositsValidator.ProhibitedDeposits();
private WithdrawalRequestValidator withdrawalRequestValidator =
new ProhibitedWithdrawalRequests();
protected BlockHashProcessor blockHashProcessor;
private FeeMarket feeMarket = FeeMarket.legacy();
private BadBlockManager badBlockManager;
private PoWHasher powHasher = PoWHasher.ETHASH_LIGHT;
@ -273,6 +275,11 @@ public class ProtocolSpecBuilder {
return this;
}
public ProtocolSpecBuilder blockHashProcessor(final BlockHashProcessor blockHashProcessor) {
this.blockHashProcessor = blockHashProcessor;
return this;
}
public ProtocolSpecBuilder isPoS(final boolean isPoS) {
this.isPoS = isPoS;
return this;
@ -393,6 +400,7 @@ public class ProtocolSpecBuilder {
Optional.ofNullable(withdrawalsProcessor),
depositsValidator,
withdrawalRequestValidator,
blockHashProcessor,
isPoS,
isReplayProtectionSupported);
}

@ -0,0 +1,31 @@
/*
* 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.blockhash;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
public interface BlockHashProcessor {
void processBlockHashes(
Blockchain blockchain,
MutableWorldState worldState,
ProcessableBlockHeader currentBlockHeader);
BlockHashLookup getBlockHashLookup(ProcessableBlockHeader currentHeader, Blockchain blockchain);
}

@ -0,0 +1,52 @@
/*
* 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.blockhash;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.ParentBeaconBlockRootHelper;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
/** Processes the beacon block storage if it is present in the block header. */
public class CancunBlockHashProcessor implements BlockHashProcessor {
@Override
public BlockHashLookup getBlockHashLookup(
final ProcessableBlockHeader currentHeader, final Blockchain blockchain) {
return new CachingBlockHashLookup(currentHeader, blockchain);
}
@Override
public void processBlockHashes(
final Blockchain blockchain,
final MutableWorldState mutableWorldState,
final ProcessableBlockHeader currentBlockHeader) {
currentBlockHeader
.getParentBeaconBlockRoot()
.ifPresent(
beaconBlockRoot -> {
if (!beaconBlockRoot.isEmpty()) {
WorldUpdater worldUpdater = mutableWorldState.updater();
ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot(
worldUpdater, currentBlockHeader.getTimestamp(), beaconBlockRoot);
worldUpdater.commit();
}
});
}
}

@ -0,0 +1,38 @@
/*
* 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.blockhash;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
public class FrontierBlockHashProcessor implements BlockHashProcessor {
@Override
public void processBlockHashes(
final Blockchain blockchain,
final MutableWorldState mutableWorldState,
final ProcessableBlockHeader currentBlockHeader) {
// do nothing
}
@Override
public BlockHashLookup getBlockHashLookup(
final ProcessableBlockHeader currentHeader, final Blockchain blockchain) {
return new CachingBlockHashLookup(currentHeader, blockchain);
}
}

@ -0,0 +1,153 @@
/*
* 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.blockhash;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.units.bigints.UInt256;
/**
* Processes and stores historical block hashes in accordance with EIP-2935. This class is
* responsible for managing the storage of block hashes to support EIP-2935, which introduces
* historical block hash access in smart contracts.
*/
public class PragueBlockHashProcessor extends CancunBlockHashProcessor {
public static final Address HISTORY_STORAGE_ADDRESS =
Address.fromHexString("0x25a219378dad9b3503c8268c9ca836a52427a4fb");
/** The HISTORY_SERVE_WINDOW */
public static final long HISTORY_SERVE_WINDOW = 8192;
private final long forkTimestamp;
private final long historySaveWindow;
private final Address historyStorageAddress;
/**
* Constructs a BlockHashProcessor with a specified fork timestamp.
*
* @param forkTimestamp The timestamp at which the fork becomes active.
*/
public PragueBlockHashProcessor(final long forkTimestamp) {
this(forkTimestamp, HISTORY_STORAGE_ADDRESS, HISTORY_SERVE_WINDOW);
}
/**
* Constructs a BlockHashProcessor with a specified fork timestamp and history save window. This
* constructor is primarily used for testing.
*
* @param forkTimestamp The timestamp at which the fork becomes active.
* @param historyStorageAddress the address of the contract storing the history
* @param historySaveWindow The number of blocks for which history should be saved.
*/
@VisibleForTesting
public PragueBlockHashProcessor(
final long forkTimestamp, final Address historyStorageAddress, final long historySaveWindow) {
this.forkTimestamp = forkTimestamp;
this.historyStorageAddress = historyStorageAddress;
this.historySaveWindow = historySaveWindow;
}
@Override
public BlockHashLookup getBlockHashLookup(
final ProcessableBlockHeader currentHeader, final Blockchain blockchain) {
return (frame, blockNumber) -> {
long currentBlockNumber = frame.getBlockValues().getNumber();
if (currentBlockNumber <= blockNumber
|| currentBlockNumber - blockNumber >= historySaveWindow
|| blockNumber < 0) {
return Hash.ZERO;
}
return Hash.wrap(
frame
.getWorldUpdater()
.get(historyStorageAddress)
.getStorageValue(UInt256.valueOf(blockNumber % historySaveWindow)));
};
}
@Override
public void processBlockHashes(
final Blockchain blockchain,
final MutableWorldState mutableWorldState,
final ProcessableBlockHeader currentBlockHeader) {
super.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
WorldUpdater worldUpdater = mutableWorldState.updater();
final MutableAccount historyStorageAccount = worldUpdater.getOrCreate(HISTORY_STORAGE_ADDRESS);
if (currentBlockHeader.getNumber() > 0) {
storeParentHash(historyStorageAccount, currentBlockHeader);
BlockHeader ancestor =
blockchain.getBlockHeader(currentBlockHeader.getParentHash()).orElseThrow();
// If fork block, add the parent's direct `HISTORY_SERVE_WINDOW - 1`
if (ancestor.getTimestamp() < forkTimestamp) {
for (int i = 0; i < (historySaveWindow - 1) && ancestor.getNumber() > 0; i++) {
ancestor = blockchain.getBlockHeader(ancestor.getParentHash()).orElseThrow();
storeBlockHeaderHash(historyStorageAccount, ancestor);
}
}
}
worldUpdater.commit();
}
/**
* Stores the hash of the parent block in the world state.
*
* @param account The account associated with the historical block hash storage.
* @param header The current block header being processed.
*/
private void storeParentHash(final MutableAccount account, final ProcessableBlockHeader header) {
storeHash(account, header.getNumber() - 1, header.getParentHash());
}
/**
* Stores the hash of a block in the world state.
*
* @param account The account associated with the historical block hash storage.
* @param header The block header whose hash is to be stored.
*/
private void storeBlockHeaderHash(final MutableAccount account, final BlockHeader header) {
storeHash(account, header.getNumber(), header.getHash());
}
/**
* Stores the hash in the world state.
*
* @param account The account associated with the historical block hash storage.
* @param number The slot to store.
* @param hash The hash to be stored.
*/
private void storeHash(final MutableAccount account, final long number, final Hash hash) {
System.out.printf(
"Writing to %s %s=%s%n",
account.getAddress(),
UInt256.valueOf(number % historySaveWindow).toDecimalString(),
UInt256.fromBytes(hash).toHexString());
account.setStorageValue(UInt256.valueOf(number % historySaveWindow), UInt256.fromBytes(hash));
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.privacy;
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
@ -36,8 +37,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.tracing.OperationTracer;
@ -91,6 +90,7 @@ public class PrivateGroupRehydrationBlockProcessor {
final PrivateStateStorage privateStateStorage,
final PrivateStateRootResolver privateStateRootResolver,
final Block block,
final BlockHashLookup blockHashLookup,
final Map<Hash, PrivateTransaction> forExecution,
final List<BlockHeader> ommers) {
long gasUsed = 0;
@ -114,7 +114,6 @@ public class PrivateGroupRehydrationBlockProcessor {
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
@ -151,7 +150,7 @@ public class PrivateGroupRehydrationBlockProcessor {
privateTransaction,
miningBeneficiary,
OperationTracer.NO_TRACING,
new CachingBlockHashLookup(blockHeader, blockchain),
blockHashLookup,
privateTransaction.getPrivacyGroupId().get());
privateWorldStateUpdater.commit();

@ -166,6 +166,7 @@ public class PrivateStateRehydration {
privateStateStorage,
privateStateRootResolver,
block,
protocolSpec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain),
pmtHashToPrivateTransactionMap,
block.getBody().getOmmers());

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.privacy;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
@ -35,7 +36,6 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Deque;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
@ -82,7 +82,7 @@ public class PrivateTransactionProcessor {
final PrivateTransaction transaction,
final Address miningBeneficiary,
final OperationTracer operationTracer,
final Function<Long, Hash> blockHashLookup,
final BlockHashLookup blockHashLookup,
final Bytes privacyGroupId) {
try {
LOG.trace("Starting private execution of {}", transaction);

@ -28,7 +28,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.tracing.OperationTracer;
@ -140,7 +139,7 @@ public class PrivateTransactionSimulator {
transaction,
protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(header),
OperationTracer.NO_TRACING,
new CachingBlockHashLookup(header, blockchain),
protocolSpec.getBlockHashProcessor().getBlockHashLookup(header, blockchain),
privacyGroupId);
return Optional.of(result);

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.privacy.storage.migration;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
@ -30,8 +32,6 @@ import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -79,6 +79,7 @@ public class PrivateMigrationBlockProcessor {
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHashLookup blockHashLookup,
final BlockHeader blockHeader,
final List<Transaction> transactions,
final List<BlockHeader> ommers) {
@ -97,7 +98,6 @@ public class PrivateMigrationBlockProcessor {
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);

@ -110,7 +110,12 @@ public class PrivateStorageMigration {
final List<BlockHeader> ommers = block.getBody().getOmmers();
privateMigrationBlockProcessor.processBlock(
blockchain, publicWorldState, blockHeader, transactionsToProcess, ommers);
blockchain,
publicWorldState,
protocolSpec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain),
blockHeader,
transactionsToProcess,
ommers);
}
}

@ -34,7 +34,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.account.Account;
@ -280,7 +279,9 @@ public class TransactionSimulator {
protocolSpec
.getMiningBeneficiaryCalculator()
.calculateBeneficiary(blockHeaderToProcess),
new CachingBlockHashLookup(blockHeaderToProcess, blockchain),
protocolSpec
.getBlockHashProcessor()
.getBlockHashLookup(blockHeaderToProcess, blockchain),
false,
transactionValidationParams,
operationTracer,

@ -1,30 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.vm;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.operation.BlockHashOperation;
import java.util.function.Function;
/**
* Calculates and caches block hashes by number following the chain for a specific branch. This is
* used by {@link BlockHashOperation} and ensures that the correct block hash is returned even when
* the block being imported is on a fork.
*
* <p>A new BlockHashCache must be created for each block being processed but should be reused for
* all transactions within that block.
*/
public interface BlockHashLookup extends Function<Long, Hash> {}

@ -15,11 +15,13 @@
package org.hyperledger.besu.ethereum.vm;
import static org.hyperledger.besu.datatypes.Hash.ZERO;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import java.util.HashMap;
import java.util.Map;
@ -36,17 +38,31 @@ public class CachingBlockHashLookup implements BlockHashLookup {
private ProcessableBlockHeader searchStartHeader;
private final Blockchain blockchain;
private final long maxLookback;
private final Map<Long, Hash> hashByNumber = new HashMap<>();
public CachingBlockHashLookup(
final ProcessableBlockHeader currentBlock, final Blockchain blockchain) {
this(currentBlock, blockchain, BlockHashOperation.MAX_RELATIVE_BLOCK);
}
public CachingBlockHashLookup(
final ProcessableBlockHeader currentBlock,
final Blockchain blockchain,
final int maxLookback) {
this.searchStartHeader = currentBlock;
this.blockchain = blockchain;
this.maxLookback = maxLookback;
hashByNumber.put(currentBlock.getNumber() - 1, currentBlock.getParentHash());
}
@Override
public Hash apply(final Long blockNumber) {
public Hash apply(final MessageFrame frame, final Long blockNumber) {
long currentBlockNumber = frame.getBlockValues().getNumber();
if (currentBlockNumber <= blockNumber || currentBlockNumber - blockNumber > maxLookback) {
return Hash.ZERO;
}
final Hash cachedHash = hashByNumber.get(blockNumber);
if (cachedHash != null) {
return cachedHash;

@ -15,11 +15,11 @@
package org.hyperledger.besu.ethereum.core;
import static org.hyperledger.besu.evm.frame.MessageFrame.DEFAULT_MAX_STACK_SIZE;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeV0;

@ -41,6 +41,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.mainnet.blockhash.FrontierBlockHashProcessor;
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;
@ -112,6 +113,7 @@ class BlockImportExceptionHandlingTest {
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
when(protocolSpec.getBlockHashProcessor()).thenReturn(new FrontierBlockHashProcessor());
mainnetBlockValidator =
new MainnetBlockValidator(
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);

@ -33,6 +33,7 @@ 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.mainnet.blockhash.FrontierBlockHashProcessor;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
@ -70,6 +71,9 @@ abstract class AbstractBlockProcessorTest {
lenient()
.when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
lenient()
.when(protocolSpec.getBlockHashProcessor())
.thenReturn(new FrontierBlockHashProcessor());
blockProcessor =
new TestBlockProcessor(
transactionProcessor,

@ -28,6 +28,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.mainnet.WithdrawalRequestValidator.ProhibitedWithdrawalRequests;
import org.hyperledger.besu.ethereum.mainnet.blockhash.FrontierBlockHashProcessor;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
@ -51,6 +52,7 @@ public class MainnetBlockProcessorTest extends AbstractBlockProcessorTest {
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getWithdrawalRequestValidator())
.thenReturn(new ProhibitedWithdrawalRequests());
when(protocolSpec.getBlockHashProcessor()).thenReturn(new FrontierBlockHashProcessor());
}
@Test

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
@ -28,7 +29,6 @@ import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -224,6 +225,8 @@ class PrivacyBlockProcessorTest {
when(protocolSpec.getMiningBeneficiaryCalculator())
.thenReturn(mock(MiningBeneficiaryCalculator.class));
when(protocolSpec.isSkipZeroBlockRewards()).thenReturn(true);
BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class);
when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
return protocolSpec;
}
}

@ -0,0 +1,187 @@
/*
* 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.blockhash;
import static org.hyperledger.besu.datatypes.Hash.fromHexStringLenient;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class BlockHashProcessorTest {
private Blockchain blockchain;
private WorldUpdater worldUpdater;
private MutableWorldState mutableWorldState;
private MutableAccount account;
private BlockHashProcessor processor;
private long historicalWindow = 8192;
@BeforeEach
void setUp() {
blockchain = mock(Blockchain.class);
mutableWorldState = mock(MutableWorldState.class);
worldUpdater = mock(WorldUpdater.class);
account = mock(MutableAccount.class);
when(mutableWorldState.updater()).thenReturn(worldUpdater);
when(worldUpdater.getOrCreate(PragueBlockHashProcessor.HISTORY_STORAGE_ADDRESS))
.thenReturn(account);
}
@Test
void shouldStoreParentBlockHash() {
long forkTimestamp = 0;
long currentBlock = 3;
processor = new PragueBlockHashProcessor(forkTimestamp);
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 3);
processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
// only parent slot number must be set
verify(account, times(1)).setStorageValue(any(), any());
verifyAccount(currentBlock - 1, historicalWindow);
}
@Test
void shouldNotStoreBlockHashForGenesisBlock() {
// For the fork to be activated at genesis, no history is written to the genesis state, and at
// the start of block 1, genesis hash will be written as a normal operation to slot 0.
long forkTimestamp = 0;
long currentBlock = 0;
processor = new PragueBlockHashProcessor(forkTimestamp);
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 0);
processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
verifyNoInteractions(account);
}
@Test
void shouldStoreAncestorBlockHashesAtForkCorrectlyParentIsGenesis() {
// for activation at block 1, only genesis hash will be written at slot 0 as there is no
// additional history that needs to be persisted.
long forkTimestamp = 1;
long currentBlock = 1;
processor = new PragueBlockHashProcessor(forkTimestamp);
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 10);
processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
verify(account, times(1)).setStorageValue(any(), any());
verifyAccount(0, historicalWindow);
}
@Test
void shouldStoreAncestorBlockHashesAtForkCorrectly() {
// for activation at block 32, block 31’s hash will be written to slot 31 and additional history
// for 0..30’s hashes will be persisted, so all in all 0..31’s hashes.
long forkTimestamp = 32;
long currentBlock = 32;
processor = new PragueBlockHashProcessor(forkTimestamp);
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 32);
processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
verifyAncestor(currentBlock, 32, historicalWindow);
}
@Test
void shouldStoreAncestorBlockHashesAtForkCorrectlyMaxWindows() {
long forkTimestamp = 10000;
long currentBlock = 10000;
historicalWindow = 8192;
processor =
new PragueBlockHashProcessor(
forkTimestamp, PragueBlockHashProcessor.HISTORY_STORAGE_ADDRESS, historicalWindow);
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 10000);
processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader);
// Total of historicalWindow hashes were stored
verify(account, times((int) historicalWindow)).setStorageValue(any(), any());
// for activation at block 10000, block 1808-9999’s hashes will be presisted in the slot
verifyAccount(1808, historicalWindow);
verifyAccount(9999, historicalWindow);
// BLOCKHASH for 1807 or less would resolve to 0 as only HISTORY_SERVE_WINDOW are persisted.
verifyAccountNoIteraction(1807, historicalWindow);
verifyAccountNoIteraction(10000, historicalWindow);
}
@Test
void shouldWriteGenesisHashAtSlot0() {
processor = new PragueBlockHashProcessor(0);
BlockHeader header = mockBlockHeader(1);
mockAncestorHeaders(header, 1);
processor.processBlockHashes(blockchain, mutableWorldState, header);
verify(account)
.setStorageValue(UInt256.valueOf(0), UInt256.fromHexString(Hash.ZERO.toHexString()));
}
private void verifyAncestor(
final long blockNumber, final int count, final long historicalWindow) {
int totalTouchedSlots = (int) (blockNumber - count <= 0 ? blockNumber : count);
long firstAncestor = Math.max(blockNumber - count - 1, 0);
verify(account, times(totalTouchedSlots)).setStorageValue(any(), any());
for (long i = firstAncestor; i < blockNumber; i++) {
verifyAccount(i, historicalWindow);
}
}
private void verifyAccount(final long number, final long historicalWindow) {
verify(account)
.setStorageValue(UInt256.valueOf(number % historicalWindow), UInt256.valueOf(number));
}
private void verifyAccountNoIteraction(final long number, final long historicalWindow) {
verify(account, times(0))
.setStorageValue(UInt256.valueOf(number % historicalWindow), UInt256.valueOf(number));
}
private void mockAncestorHeaders(final BlockHeader blockHeader, final int count) {
long firstAncestor = Math.max(blockHeader.getNumber() - count, 0);
var block = blockHeader;
for (long i = blockHeader.getNumber(); i > firstAncestor; i--) {
long parentNumber = block.getNumber() - 1;
block = mockBlockHeader(parentNumber);
}
}
private BlockHeader mockBlockHeader(final long currentNumber) {
BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getNumber()).thenReturn(currentNumber);
Hash hash = fromHexStringLenient("0x" + Long.toHexString(currentNumber));
Hash parentHash = fromHexStringLenient("0x" + Long.toHexString(currentNumber - 1));
when(blockHeader.getHash()).thenReturn(hash);
when(blockHeader.getTimestamp()).thenReturn(currentNumber);
when(blockHeader.getParentHash()).thenReturn(parentHash);
when(blockchain.getBlockHeader(hash)).thenReturn(Optional.of(blockHeader));
return blockHeader;
}
}

@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.versionedPrivateTransactionBesu;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
@ -49,7 +50,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;

@ -21,6 +21,7 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVAT
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.readFrom;
import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.serialize;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -42,7 +43,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.log.Log;

@ -21,6 +21,7 @@ import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.V
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateTransactionBesu;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
@ -52,7 +53,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;

@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
@ -93,6 +94,7 @@ public class PrivateStorageMigrationTest {
@Mock private TransactionReceiptFactory transactionReceiptFactory;
@Mock private MiningBeneficiaryCalculator miningBeneficiaryCalculator;
@Mock private PrivateMigrationBlockProcessor privateMigrationBlockProcessor;
@Mock private BlockHashProcessor blockHashProcessor;
private PrivateStateKeyValueStorage privateStateStorage;
private PrivateStateRootResolver privateStateRootResolver;
@ -115,6 +117,7 @@ public class PrivateStorageMigrationTest {
.when(protocolSpec.getMiningBeneficiaryCalculator())
.thenReturn(miningBeneficiaryCalculator);
lenient().when(protocolSpec.isSkipZeroBlockRewards()).thenReturn(false);
lenient().when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
migration =
new PrivateStorageMigration(
@ -199,6 +202,7 @@ public class PrivateStorageMigrationTest {
verify(privateMigrationBlockProcessor)
.processBlock(
any(),
any(),
any(),
eq(blockWithPMT.getHeader()),
@ -223,7 +227,7 @@ public class PrivateStorageMigrationTest {
final ArgumentCaptor<List> txsCaptor = ArgumentCaptor.forClass(List.class);
verify(privateMigrationBlockProcessor)
.processBlock(any(), any(), any(), txsCaptor.capture(), any());
.processBlock(any(), any(), any(), any(), txsCaptor.capture(), any());
// won't process transaction after PMT, that's why we only process 2 txs
final List<Transaction> processedTxs = txsCaptor.getValue();

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Status;
@ -721,12 +722,14 @@ public class TransactionSimulatorTest {
private void mockProtocolSpecForProcessWithWorldUpdater() {
final BlockHeaderFunctions blockHeaderFunctions = mock(BlockHeaderFunctions.class);
final BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class);
when(protocolSchedule.getChainId()).thenReturn(Optional.of(BigInteger.ONE));
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(protocolSpec.getBlockHeaderFunctions()).thenReturn(blockHeaderFunctions);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0));
when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
}
private void mockProcessorStatusForTransaction(

@ -14,8 +14,9 @@
*/
package org.hyperledger.besu.ethereum.vm;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@ -26,6 +27,8 @@ import org.hyperledger.besu.datatypes.Hash;
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.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
@ -34,76 +37,97 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class CachingBlockHashLookupTest {
class CachingBlockHashLookupTest {
private static final int CURRENT_BLOCK_NUMBER = 256;
private final Blockchain blockchain = mock(Blockchain.class);
@Mock private Blockchain blockchain;
@Mock private MessageFrame messageFrame;
@Mock private BlockValues blockValues;
private static final int CURRENT_BLOCK_NUMBER = 300;
private final BlockHeader[] headers = new BlockHeader[CURRENT_BLOCK_NUMBER];
private BlockHashLookup lookup;
@BeforeEach
public void setUp() {
void setUp() {
BlockHeader parentHeader = null;
for (int i = 0; i < headers.length; i++) {
final BlockHeader header = createHeader(i, parentHeader);
when(blockchain.getBlockHeader(header.getHash())).thenReturn(Optional.of(header));
lenient().when(blockchain.getBlockHeader(header.getHash())).thenReturn(Optional.of(header));
headers[i] = header;
parentHeader = headers[i];
}
when(messageFrame.getBlockValues()).thenReturn(blockValues);
when(blockValues.getNumber()).thenReturn((long) CURRENT_BLOCK_NUMBER);
lookup =
new CachingBlockHashLookup(
createHeader(CURRENT_BLOCK_NUMBER, headers[headers.length - 1]), blockchain);
}
@AfterEach
public void verifyBlocksNeverLookedUpByNumber() {
void verifyBlocksNeverLookedUpByNumber() {
// Looking up the block by number is incorrect because it always uses the canonical chain even
// if the block being imported is on a fork.
verify(blockchain, never()).getBlockHeader(anyLong());
}
@Test
public void shouldGetHashOfImmediateParent() {
void shouldGetHashOfImmediateParent() {
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1);
}
@Test
public void shouldGetHashOfGenesisBlock() {
assertHashForBlockNumber(0);
void shouldGetHashForRecentBlockAfterOlderBlock() {
assertHashForBlockNumber(100);
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1);
}
@Test
public void shouldGetHashForRecentBlockAfterOlderBlock() {
assertHashForBlockNumber(10);
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1);
void shouldReturnEmptyHashWhenRequestedGenesis() {
Assertions.assertThat(lookup.apply(messageFrame, 0L)).isEqualTo(Hash.ZERO);
}
@Test
public void shouldReturnEmptyHashWhenRequestedBlockNotOnchain() {
Assertions.assertThat(lookup.apply(CURRENT_BLOCK_NUMBER + 20L)).isEqualTo(Hash.ZERO);
void shouldReturnEmptyHashWhenRequestedTooFarBack() {
Assertions.assertThat(lookup.apply(messageFrame, CURRENT_BLOCK_NUMBER - 260L))
.isEqualTo(Hash.ZERO);
}
@Test
void shouldReturnEmptyHashWhenRequestedCurrentBlock() {
Assertions.assertThat(lookup.apply(messageFrame, (long) CURRENT_BLOCK_NUMBER))
.isEqualTo(Hash.ZERO);
}
@Test
void shouldReturnEmptyHashWhenRequestedBlockNotOnchain() {
Assertions.assertThat(lookup.apply(messageFrame, CURRENT_BLOCK_NUMBER + 20L))
.isEqualTo(Hash.ZERO);
}
@Test
public void shouldReturnEmptyHashWhenParentBlockNotOnchain() {
void shouldReturnEmptyHashWhenParentBlockNotOnchain() {
final BlockHashLookup lookupWithUnavailableParent =
new CachingBlockHashLookup(
new BlockHeaderTestFixture().number(CURRENT_BLOCK_NUMBER + 20).buildHeader(),
blockchain);
Assertions.assertThat(lookupWithUnavailableParent.apply((long) CURRENT_BLOCK_NUMBER))
Assertions.assertThat(
lookupWithUnavailableParent.apply(messageFrame, (long) CURRENT_BLOCK_NUMBER))
.isEqualTo(Hash.ZERO);
}
@Test
public void shouldGetParentHashFromCurrentBlock() {
void shouldGetParentHashFromCurrentBlock() {
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1);
verifyNoInteractions(blockchain);
}
@Test
public void shouldCacheBlockHashesWhileIteratingBackToPreviousHeader() {
void shouldCacheBlockHashesWhileIteratingBackToPreviousHeader() {
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 4);
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1);
verify(blockchain).getBlockHeader(headers[CURRENT_BLOCK_NUMBER - 1].getHash());
@ -113,7 +137,7 @@ public class CachingBlockHashLookupTest {
}
private void assertHashForBlockNumber(final int blockNumber) {
Assertions.assertThat(lookup.apply((long) blockNumber))
Assertions.assertThat(lookup.apply(messageFrame, (long) blockNumber))
.isEqualTo(headers[blockNumber].getHash());
}

@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
@ -430,7 +429,10 @@ public class EvmToolCommand implements Runnable {
.blockValues(blockHeader)
.completer(c -> {})
.miningBeneficiary(blockHeader.getCoinbase())
.blockHashLookup(new CachingBlockHashLookup(blockHeader, component.getBlockchain()))
.blockHashLookup(
protocolSpec
.getBlockHashProcessor()
.getBlockHashLookup(blockHeader, component.getBlockchain()))
.accessListWarmAddresses(addressList)
.build();
Deque<MessageFrame> messageFrameStack = initialMessageFrame.getMessageFrameStack();

@ -254,7 +254,8 @@ public class StateTestSubCommand implements Runnable {
blockHeader,
transaction,
blockHeader.getCoinbase(),
blockNumber -> Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))),
(blockheight, blockNumber) ->
Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))),
false,
TransactionValidationParams.processingBlock(),
tracer,

@ -30,13 +30,12 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
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.TransactionValidationParams;
@ -48,12 +47,15 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evmtool.exception.UnsupportedForkException;
import org.hyperledger.besu.evmtool.t8n.T8nBlockchain;
import java.io.IOException;
import java.io.PrintWriter;
@ -246,23 +248,21 @@ public class T8nExecutor {
throw new UnsupportedForkException(fork);
}
ProtocolSpec protocolSpec =
protocolSchedule.getByBlockHeader(BlockHeaderBuilder.createDefault().buildBlockHeader());
final BlockHeader blockHeader = referenceTestEnv.updateFromParentValues(protocolSpec);
ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(referenceTestEnv);
Blockchain blockchain = new T8nBlockchain(referenceTestEnv, protocolSpec);
final BlockHeader blockHeader = referenceTestEnv.parentBlockHeader(protocolSpec);
final MainnetTransactionProcessor processor = protocolSpec.getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final Wei blobGasPrice =
protocolSpec
.getFeeMarket()
.blobGasPricePerGas(calculateExcessBlobGasForParent(protocolSpec, blockHeader));
long blobGasLimit = protocolSpec.getGasLimitCalculator().currentBlobGasLimit();
referenceTestEnv
.getParentBeaconBlockRoot()
.ifPresent(
bytes32 ->
ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot(
worldStateUpdater.updater(), referenceTestEnv.getTimestamp(), bytes32));
protocolSpec
.getBlockHashProcessor()
.processBlockHashes(blockchain, worldState, referenceTestEnv);
final WorldUpdater worldStateUpdater = worldState.updater();
List<TransactionReceipt> receipts = new ArrayList<>();
List<RejectedTransaction> invalidTransactions = new ArrayList<>(rejections);
List<Transaction> validTransactions = new ArrayList<>();
@ -291,13 +291,24 @@ public class T8nExecutor {
tracer = tracerManager.getManagedTracer(i, transaction.getHash());
tracer.tracePrepareTransaction(worldStateUpdater, transaction);
tracer.traceStartTransaction(worldStateUpdater, transaction);
BlockHashOperation.BlockHashLookup blockHashLookup =
protocolSpec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain);
if (blockHashLookup instanceof CachingBlockHashLookup) {
// caching lookup won't work, use our own secret sauce
blockHashLookup =
(frame, number) ->
(referenceTestEnv.getNumber() - frame.getBlockValues().getNumber()
> BlockHashOperation.MAX_RELATIVE_BLOCK)
? Hash.ZERO
: referenceTestEnv.getBlockhashByNumber(number).orElse(Hash.ZERO);
}
result =
processor.processTransaction(
worldStateUpdater,
blockHeader,
transaction,
blockHeader.getCoinbase(),
blockNumber -> referenceTestEnv.getBlockhashByNumber(blockNumber).orElse(Hash.ZERO),
blockHashLookup,
false,
TransactionValidationParams.processingBlock(),
tracer,

@ -62,7 +62,7 @@ public abstract class BenchmarkExecutor {
.code(CodeV0.EMPTY_CODE)
.completer(__ -> {})
.address(Address.ZERO)
.blockHashLookup(n -> null)
.blockHashLookup((f, n) -> null)
.blockValues(new SimpleBlockValues())
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -0,0 +1,206 @@
/*
* Copyright ConsenSys AG.
*
* 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.evmtool.t8n;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.BlockAddedObserver;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.ChainHead;
import org.hyperledger.besu.ethereum.chain.ChainReorgObserver;
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestEnv;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* A blockchain mock for the Ethereum reference tests.
*
* <p>Operations which would lead to non-deterministic behaviour if executed while processing
* transactions throw {@link NonDeterministicOperationException}. For example all methods that
* lookup blocks by number since the block being processed may not be on the canonical chain but
* that must not affect the execution of its transactions.
*
* <p>The Ethereum reference tests for VM execution (VMTests) and transaction processing
* (GeneralStateTests) require a block's hash to be to be the hash of the string of it's block
* number.
*/
public class T8nBlockchain implements Blockchain {
// Maximum number of blocks prior to the chain head that can be retrieved by hash.
private static final String NUMBER_LOOKUP_ERROR =
"Blocks must not be looked up by number in the EVM. The block being processed may not be on the canonical chain.";
private static final String CHAIN_HEAD_ERROR =
"Chain head is inherently non-deterministic. The block currently being processed should be treated as the chain head.";
private static final String FINALIZED_ERROR =
"Finalized block is inherently non-deterministic. The block currently being processed should be treated as the finalized block.";
private static final String SAFE_BLOCK_ERROR =
"Safe block is inherently non-deterministic. The block currently being processed should be treated as the safe block.";
private final Map<Hash, BlockHeader> hashToHeader = new HashMap<>();
private final BlockHeader chainHeader;
/**
* Create a new blockchain object for T8n testing.
*
* @param referenceTestEnv the referenfe test environment, containing most of the block header
* stuff
* @param protocolSpec the protocol spec, which impacts what block header fields are implemente.
*/
public T8nBlockchain(final ReferenceTestEnv referenceTestEnv, final ProtocolSpec protocolSpec) {
Map<Long, Hash> blockHashes = referenceTestEnv.getBlockHashes();
chainHeader = referenceTestEnv.parentBlockHeader(protocolSpec);
blockHashes.forEach(
(num, blockHash) ->
hashToHeader.put(
blockHash,
BlockHeaderBuilder.createDefault()
.number(num)
.parentHash(blockHashes.getOrDefault(num - 1, Hash.ZERO))
.timestamp(0)
.coinbase(chainHeader.getCoinbase())
.difficulty(chainHeader.getDifficulty())
.gasLimit(chainHeader.getGasLimit())
.buildBlockHeader()));
hashToHeader.put(referenceTestEnv.getParentHash(), chainHeader);
}
@Override
public Optional<Hash> getBlockHashByNumber(final long number) {
throw new NonDeterministicOperationException(NUMBER_LOOKUP_ERROR);
}
@Override
public ChainHead getChainHead() {
return new ChainHead(chainHeader, chainHeader.getDifficulty(), chainHeader.getNumber());
}
@Override
public Optional<Hash> getFinalized() {
throw new NonDeterministicOperationException(FINALIZED_ERROR);
}
@Override
public Optional<Hash> getSafeBlock() {
throw new NonDeterministicOperationException(SAFE_BLOCK_ERROR);
}
@Override
public long getChainHeadBlockNumber() {
throw new NonDeterministicOperationException(CHAIN_HEAD_ERROR);
}
@Override
public Hash getChainHeadHash() {
return chainHeader.getHash();
}
@Override
public Optional<TransactionLocation> getTransactionLocation(final Hash transactionHash) {
throw new NonDeterministicOperationException("Transaction location may be different on forks");
}
@Override
public Optional<BlockHeader> getBlockHeader(final long blockNumber) {
throw new NonDeterministicOperationException(NUMBER_LOOKUP_ERROR);
}
@Override
public Optional<BlockHeader> getBlockHeader(final Hash blockHeaderHash) {
return Optional.ofNullable(hashToHeader.get(blockHeaderHash));
}
@Override
public synchronized Optional<BlockHeader> getBlockHeaderSafe(final Hash blockHeaderHash) {
return Optional.ofNullable(hashToHeader.get(blockHeaderHash));
}
@Override
public Optional<BlockBody> getBlockBody(final Hash blockHeaderHash) {
// Deterministic, but just not implemented.
throw new UnsupportedOperationException();
}
@Override
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
// Deterministic, but just not implemented.
throw new UnsupportedOperationException();
}
@Override
public Optional<Difficulty> getTotalDifficultyByHash(final Hash blockHeaderHash) {
// Deterministic, but just not implemented.
throw new UnsupportedOperationException();
}
@Override
public Optional<Transaction> getTransactionByHash(final Hash transactionHash) {
throw new NonDeterministicOperationException(
"Which transactions are on the chain may vary on different forks");
}
@Override
public long observeBlockAdded(final BlockAddedObserver observer) {
throw new NonDeterministicOperationException("Listening for new blocks is not deterministic");
}
@Override
public boolean removeObserver(final long observerId) {
throw new NonDeterministicOperationException("Listening for new blocks is not deterministic");
}
@Override
public long observeChainReorg(final ChainReorgObserver observer) {
throw new NonDeterministicOperationException("Listening for chain reorg is not deterministic");
}
@Override
public boolean removeChainReorgObserver(final long observerId) {
throw new NonDeterministicOperationException("Listening for chain reorg is not deterministic");
}
/** An exception thrown for methods not supported by the T8nBlockchain. */
public static class NonDeterministicOperationException extends RuntimeException {
NonDeterministicOperationException(final String message) {
super(message);
}
}
@Override
@SuppressWarnings("unused")
public Comparator<BlockHeader> getBlockChoiceRule() {
return (a, b) -> {
throw new NonDeterministicOperationException(
"ReferenceTestBlockchain for VMTest Chains do not support fork choice rules");
};
}
@Override
public void setBlockChoiceRule(final Comparator<BlockHeader> blockChoiceRule) {
throw new UnsupportedOperationException("Not Used for Reference Tests");
}
}

@ -68,41 +68,42 @@
"0x1000000000000000000000000000000000000000": {
"code": "0x60004060005560014060015500",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x651666d6d8727a6478a750dc8406c1bda0be85abc99094dca694807fa772f83c",
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0275fc82fe832100726ec7c26078ffb1c1dbff14a3b52d93dc359768fc8c7baa"
},
"balance": "0xba1a9ce0ba1a9ce"
},
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
"balance": "0x21378"
"balance": "0x2fcac"
},
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x1319ad1e2e70",
"balance": "0x1319ad1b2518",
"nonce": "0x1"
}
},
"body": "0xf862f860800a8307a12094100000000000000000000000000000000000000080801ca091df80a63242846dcf1cc16c35e6ba39212cde92aad946f9c286fa0029782b54a0446170c2fb714c717885485ea7d93bf9277f3fb3d89ffd73e9b7f5f8eda3997b",
"result": {
"stateRoot": "0x89412c0f1bb31b983d0317e6e2801a4e604e1ef9987a132ab63c52f2d5a3994b",
"stateRoot": "0xefe246c92bca7a73025ab56d2c19601329ee290b682ae191775dc9edfae52805",
"txRoot": "0x8e987be72f36f97a98838e03d9e5d1b3d22795606240f16684532b1c994b4ee7",
"receiptsRoot": "0x8046ec5911721c453253b45a350eb90247e1a047bdc7cf1e2c7885b4c2403276",
"receiptsRoot": "0xecc52ec2cee2a0aceb962c49145060a75e14513e53a01451b61547e9ff84f498",
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
{
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0xb128",
"cumulativeGasUsed": "0xfee4",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"logs": null,
"transactionHash": "0x6c4a22ee7d2d9ae9307b09aac69a34600978e34313088917a7e69fd12e8f959a",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0xb128",
"gasUsed": "0xfee4",
"blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"transactionIndex": "0x0"
}
],
"currentDifficulty": null,
"gasUsed": "0xb128",
"gasUsed": "0xfee4",
"currentBaseFee": "0x7",
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
}

@ -189,7 +189,7 @@ public class ReferenceTestEnv extends BlockHeader {
}
}
public BlockHeader updateFromParentValues(final ProtocolSpec protocolSpec) {
public BlockHeader parentBlockHeader(final ProtocolSpec protocolSpec) {
var builder =
BlockHeaderBuilder.fromHeader(this)
.blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions());
@ -222,6 +222,8 @@ public class ReferenceTestEnv extends BlockHeader {
builder.excessBlobGas(BlobGas.of(Long.decode(parentExcessBlobGas)));
builder.blobGasUsed(Long.decode(parentBlobGasUsed));
}
Hash grandParentHash = blockHashes.get(number - 2);
builder.parentHash(grandParentHash == null ? Hash.ZERO : grandParentHash);
return builder.buildBlockHeader();
}
@ -234,6 +236,10 @@ public class ReferenceTestEnv extends BlockHeader {
return Optional.ofNullable(blockHashes.get(number));
}
public Map<Long, Hash> getBlockHashes() {
return blockHashes;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;

@ -83,6 +83,9 @@ public class ReferenceTestProtocolSchedules {
builder.put("Cancun", createSchedule(genesisStub.clone().cancunTime(0)));
// also load KZG file for mainnet
KZGPointEvalPrecompiledContract.init();
builder.put(
"CancunToPragueAtTime15k",
createSchedule(genesisStub.clone().cancunTime(0).pragueTime(15000)));
builder.put("Prague", createSchedule(genesisStub.clone().pragueTime(0)));
builder.put("Future_EIPs", createSchedule(genesisStub.clone().futureEipsTime(0)));
builder.put("Experimental_EIPs", createSchedule(genesisStub.clone().experimentalEipsTime(0)));

@ -160,7 +160,7 @@ public class GeneralStateReferenceTestTools {
blockHeader,
transaction,
blockHeader.getCoinbase(),
new CachingBlockHashLookup(blockHeader, blockchain),
protocolSpec.getBlockHashProcessor().getBlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock(),
blobGasPrice);

@ -90,6 +90,7 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
original.getWithdrawalsProcessor(),
original.getDepositsValidator(),
original.getWithdrawalRequestValidator(),
original.getBlockHashProcessor(),
original.isPoS(),
original.isReplayProtectionSupported());
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.fluent;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.collections.trie.BytesTrieSet;
import org.hyperledger.besu.datatypes.Address;
@ -46,7 +47,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
@ -72,7 +72,7 @@ public class EVMExecutor {
private Wei ethValue = Wei.ZERO;
private Code code = CodeV0.EMPTY_CODE;
private BlockValues blockValues = new SimpleBlockValues();
private Function<Long, Hash> blockHashLookup = h -> null;
private BlockHashLookup blockHashLookup = (h, n) -> null;
private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
private OperationTracer tracer = OperationTracer.NO_TRACING;
private boolean requireDeposit = true;
@ -971,7 +971,7 @@ public class EVMExecutor {
* @param blockHashLookup the block hash lookup function
* @return the evm executor
*/
public EVMExecutor blockHashLookup(final Function<Long, Hash> blockHashLookup) {
public EVMExecutor blockHashLookup(final BlockHashLookup blockHashLookup) {
this.blockHashLookup = blockHashLookup;
return this;
}

@ -16,13 +16,13 @@ package org.hyperledger.besu.evm.frame;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Collections.emptySet;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.collections.trie.BytesTrieSet;
import org.hyperledger.besu.collections.undo.UndoScalar;
import org.hyperledger.besu.collections.undo.UndoSet;
import org.hyperledger.besu.collections.undo.UndoTable;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
@ -45,7 +45,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
@ -1243,7 +1242,7 @@ public class MessageFrame {
*
* @return the block hash lookup
*/
public Function<Long, Hash> getBlockHashLookup() {
public BlockHashLookup getBlockHashLookup() {
return txValues.blockHashLookup();
}
@ -1420,7 +1419,7 @@ public class MessageFrame {
private boolean isStatic = false;
private Consumer<MessageFrame> completer;
private Address miningBeneficiary;
private Function<Long, Hash> blockHashLookup;
private BlockHashLookup blockHashLookup;
private Map<String, Object> contextVariables;
private Optional<Bytes> reason = Optional.empty();
private Set<Address> accessListWarmAddresses = emptySet();
@ -1647,7 +1646,7 @@ public class MessageFrame {
* @param blockHashLookup the block hash lookup
* @return the builder
*/
public Builder blockHashLookup(final Function<Long, Hash> blockHashLookup) {
public Builder blockHashLookup(final BlockHashLookup blockHashLookup) {
this.blockHashLookup = blockHashLookup;
return this;
}

@ -14,18 +14,18 @@
*/
package org.hyperledger.besu.evm.frame;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.collections.undo.UndoScalar;
import org.hyperledger.besu.collections.undo.UndoSet;
import org.hyperledger.besu.collections.undo.UndoTable;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import java.util.Deque;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes32;
@ -36,8 +36,8 @@ import org.apache.tuweni.bytes.Bytes32;
*
* @param blockHashLookup The block hash lookup function
* @param maxStackSize The maximum stack size
* @param warmedUpAddresses The warmed up addresses
* @param warmedUpStorage The warmed up storage
* @param warmedUpAddresses The warmed-up addresses
* @param warmedUpStorage The warmed-up storage
* @param originator The originator address
* @param gasPrice The gas price
* @param blobGasPrice The blob gas price
@ -51,7 +51,7 @@ import org.apache.tuweni.bytes.Bytes32;
* @param gasRefunds The gas refunds
*/
public record TxValues(
Function<Long, Hash> blockHashLookup,
BlockHashLookup blockHashLookup,
int maxStackSize,
UndoSet<Address> warmedUpAddresses,
UndoTable<Address, Bytes32, Boolean> warmedUpStorage,

@ -16,20 +16,29 @@ package org.hyperledger.besu.evm.operation;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.function.Function;
import java.util.function.BiFunction;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
/** The Block hash operation. */
public class BlockHashOperation extends AbstractFixedCostOperation {
private static final int MAX_RELATIVE_BLOCK = 255;
/** Frontier maximum relative block delta */
public static final int MAX_RELATIVE_BLOCK = 255;
/**
* Function that gets the block hash, passed in as part of TxValues.
*
* <p>First arg is the current block number, second is the block argument from the stack. The
* Result is the Hash, which may be zero based on lookup rules.
*/
public interface BlockHashLookup extends BiFunction<MessageFrame, Long, Hash> {}
private static final int MAX_BLOCK_ARG_SIZE = 8;
/**
* Instantiates a new Block hash operation.
@ -41,32 +50,19 @@ public class BlockHashOperation extends AbstractFixedCostOperation {
}
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
final Bytes blockArg = frame.popStackItem().trimLeadingZeros();
// Short-circuit if value is unreasonably large
if (blockArg.size() > 8) {
// Short-circuit if value exceeds long
if (blockArg.size() > MAX_BLOCK_ARG_SIZE) {
frame.pushStackItem(UInt256.ZERO);
return successResponse;
}
final long soughtBlock = blockArg.toLong();
final BlockValues blockValues = frame.getBlockValues();
final long currentBlockNumber = blockValues.getNumber();
final long mostRecentBlockNumber = currentBlockNumber - 1;
// If the current block is the genesis block or the sought block is
// not within the last 256 completed blocks, zero is returned.
if (currentBlockNumber == 0
|| soughtBlock < (mostRecentBlockNumber - MAX_RELATIVE_BLOCK)
|| soughtBlock > mostRecentBlockNumber) {
frame.pushStackItem(Bytes32.ZERO);
} else {
final Function<Long, Hash> blockHashLookup = frame.getBlockHashLookup();
final Hash blockHash = blockHashLookup.apply(soughtBlock);
frame.pushStackItem(blockHash);
}
final BlockHashLookup blockHashLookup = frame.getBlockHashLookup();
final Hash blockHash = blockHashLookup.apply(frame, soughtBlock);
frame.pushStackItem(blockHash);
return successResponse;
}

@ -97,7 +97,7 @@ class CodeV0Test {
.blockValues(mock(BlockValues.class))
.completer(f -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(l -> Hash.EMPTY)
.blockHashLookup((f, n) -> Hash.EMPTY)
.build();
frame.setPC(CURRENT_PC);

@ -188,7 +188,7 @@ class EVMExecutorTest {
.number(1)
.timestamp(100L)
.gasLimit(15_000_000L)
.blockHashLookup(number -> Hash.ZERO)
.blockHashLookup((height, number) -> Hash.ZERO)
.versionedHashes(Optional.empty())
.precompileContractRegistry(new PrecompileContractRegistry())
.requireDeposit(false)

@ -48,7 +48,7 @@ class MessageFrameTest {
.blobGasPrice(Wei.ONE)
.blockValues(new ToyBlockValues())
.miningBeneficiary(Address.ZERO)
.blockHashLookup((l) -> Hash.ZERO)
.blockHashLookup((f, n) -> Hash.ZERO)
.type(MessageFrame.Type.MESSAGE_CALL)
.initialGas(1)
.address(Address.ZERO)

@ -169,7 +169,7 @@ class AbstractCreateOperationTest {
.code(CodeFactory.createCode(SIMPLE_CREATE, 0, true))
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(n -> Hash.hash(Words.longBytes(n)))
.blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.frame.MessageFrame;
@ -23,8 +24,6 @@ import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
@ -32,56 +31,27 @@ import org.junit.jupiter.api.Test;
class BlockHashOperationTest {
private static final int MAXIMUM_COMPLETE_BLOCKS_BEHIND = 256;
private final BlockHashOperation blockHashOperation =
new BlockHashOperation(new FrontierGasCalculator());
@Test
void shouldReturnZeroWhenArgIsBiggerThanALong() {
assertBlockHash(
Bytes32.fromHexString("F".repeat(64)), Bytes32.ZERO, 100, n -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnZeroWhenCurrentBlockIsGenesis() {
assertBlockHash(Bytes32.ZERO, Bytes32.ZERO, 0, block -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnZeroWhenRequestedBlockAheadOfCurrent() {
assertBlockHash(250, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnZeroWhenRequestedBlockTooFarBehindCurrent() {
final int requestedBlock = 10;
// Our block is the one after the chain head (it's a new block), hence the + 1.
final int importingBlockNumber = MAXIMUM_COMPLETE_BLOCKS_BEHIND + requestedBlock + 1;
assertBlockHash(
requestedBlock, Bytes32.ZERO, importingBlockNumber, block -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnZeroWhenRequestedBlockGreaterThanImportingBlock() {
assertBlockHash(101, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnZeroWhenRequestedBlockEqualToImportingBlock() {
assertBlockHash(100, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH);
Bytes32.fromHexString("F".repeat(64)), Bytes32.ZERO, 100, (h, n) -> Hash.EMPTY_LIST_HASH);
}
@Test
void shouldReturnBlockHashUsingLookupFromFrameWhenItIsWithinTheAllowedRange() {
final Hash blockHash = Hash.hash(Bytes.fromHexString("0x1293487297"));
assertBlockHash(100, blockHash, 200, block -> block == 100 ? blockHash : Hash.EMPTY_LIST_HASH);
assertBlockHash(
100, blockHash, 200, (h, block) -> block == 100 ? blockHash : Hash.EMPTY_LIST_HASH);
}
private void assertBlockHash(
final long requestedBlock,
final Bytes32 expectedOutput,
final long currentBlockNumber,
final Function<Long, Hash> blockHashLookup) {
final BlockHashLookup blockHashLookup) {
assertBlockHash(
UInt256.valueOf(requestedBlock), expectedOutput, currentBlockNumber, blockHashLookup);
}
@ -90,7 +60,7 @@ class BlockHashOperationTest {
final Bytes32 input,
final Bytes32 expectedOutput,
final long currentBlockNumber,
final Function<Long, Hash> blockHashLookup) {
final BlockHashLookup blockHashLookup) {
final MessageFrame frame =
new TestMessageFrameBuilder()
.blockHashLookup(blockHashLookup)

@ -155,7 +155,7 @@ public class Create2OperationTest {
.code(CodeFactory.createCode(codeBytes, 0, true))
.completer(__ -> {})
.address(Address.fromHexString(sender))
.blockHashLookup(n -> Hash.hash(Words.longBytes(n)))
.blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)
@ -269,7 +269,7 @@ public class Create2OperationTest {
.code(CodeFactory.createCode(SIMPLE_CREATE, 0, true))
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(n -> Hash.hash(Words.longBytes(n)))
.blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -289,7 +289,7 @@ class CreateOperationTest {
.code(CodeFactory.createCode(SIMPLE_CREATE, 0, true))
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(n -> Hash.hash(Words.longBytes(n)))
.blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -82,7 +82,7 @@ public class SelfDestructOperationTest {
.code(CodeFactory.createCode(SELFDESTRUCT_CODE, 0, true))
.completer(__ -> {})
.address(originatorAddress)
.blockHashLookup(n -> Hash.hash(Words.longBytes(n)))
.blockHashLookup((f, n) -> Hash.hash(Words.longBytes(n)))
.blockValues(mock(BlockValues.class))
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -65,7 +65,7 @@ public class Benchmarks {
.code(CodeV0.EMPTY_CODE)
.completer(__ -> {})
.address(Address.ZERO)
.blockHashLookup(n -> null)
.blockHashLookup((f, n) -> null)
.blockValues(new SimpleBlockValues())
.gasPrice(Wei.ZERO)
.miningBeneficiary(Address.ZERO)

@ -15,6 +15,7 @@
package org.hyperledger.besu.evm.testutils;
import static org.hyperledger.besu.evm.frame.MessageFrame.DEFAULT_MAX_STACK_SIZE;
import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
@ -30,7 +31,6 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
@ -54,7 +54,7 @@ public class TestMessageFrameBuilder {
private int pc = 0;
private int section = 0;
private final List<Bytes> stackItems = new ArrayList<>();
private Optional<Function<Long, Hash>> blockHashLookup = Optional.empty();
private Optional<BlockHashLookup> blockHashLookup = Optional.empty();
private Bytes memory = Bytes.EMPTY;
public TestMessageFrameBuilder worldUpdater(final WorldUpdater worldUpdater) {
@ -132,7 +132,7 @@ public class TestMessageFrameBuilder {
return this;
}
public TestMessageFrameBuilder blockHashLookup(final Function<Long, Hash> blockHashLookup) {
public TestMessageFrameBuilder blockHashLookup(final BlockHashLookup blockHashLookup) {
this.blockHashLookup = Optional.of(blockHashLookup);
return this;
}
@ -161,7 +161,7 @@ public class TestMessageFrameBuilder {
.blockValues(blockValues.orElseGet(() -> new FakeBlockValues(1337)))
.completer(c -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(blockHashLookup.orElse(number -> Hash.hash(Words.longBytes(number))))
.blockHashLookup(blockHashLookup.orElse((f, n) -> Hash.hash(Words.longBytes(n))))
.maxStackSize(maxStackSize)
.build();
frame.setPC(pc);

@ -190,7 +190,7 @@ public class EvmToyCommand implements Runnable {
.blockValues(new ToyBlockValues())
.completer(c -> {})
.miningBeneficiary(Address.ZERO)
.blockHashLookup(h -> null)
.blockHashLookup((f, n) -> null)
.build();
final MessageCallProcessor mcp = new MessageCallProcessor(evm, precompileContractRegistry);

Loading…
Cancel
Save