mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 23:38:08 -05:00
Consolidated EIP-4844 (#5724)
* Implements EIP-4844. * introduces a Hardfork class to the protocol schedule system * new Engine APIs required for CL to work on 4844 * new DataGas type for tracking block cost for 4844 data * new VersionedHash type to reflect that a versioned hash is not quite a pure sha256 * incorporates wrapped jc-kzg library for KZG point evaluations * New transaction type, and domain objects for constituent parts to represent the Blobs, KZGCommitments, and Proofs used for 4844 * RLP encoders and decoders to support new transaction type * gas pricing calculators for the new type of gas * plugin-api version was changed Signed-off-by: Justin Florentine <justin+github@florentine.us> Co-authored-by: Jiri Peinlich <jiri.peinlich@gmail.com> Co-authored-by: Jason Frame <jason.frame@consensys.net> Co-authored-by: garyschulte <garyschulte@gmail.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Gabriel Fukushima <gabrielfukushima@gmail.com> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Co-authored-by: Stefan <stefan.pingel@consensys.net> Co-authored-by: spencer-tb <spencer@spencertaylorbrown.uk> * junit5 updates Signed-off-by: Justin Florentine <justin+github@florentine.us> * update t8n test Cancun gas claculator was inheriting from london, should have been shanghai. Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com> --------- Signed-off-by: Justin Florentine <justin+github@florentine.us> Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com> Co-authored-by: Jiri Peinlich <jiri.peinlich@gmail.com> Co-authored-by: Jason Frame <jason.frame@consensys.net> Co-authored-by: garyschulte <garyschulte@gmail.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Gabriel Fukushima <gabrielfukushima@gmail.com> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Co-authored-by: Stefan <stefan.pingel@consensys.net> Co-authored-by: spencer-tb <spencer@spencertaylorbrown.uk> Co-authored-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
This commit is contained in:
committed by
GitHub
parent
d923b8eb8f
commit
1c099e8970
@@ -56,6 +56,7 @@ public class BlockUtils {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeaderFunctions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@ import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@Ignore("EIP-6110 is not yet implemented")
|
||||
public class ExecutionEngineEip6110AcceptanceTest extends AbstractJsonRpcTest {
|
||||
private static final String GENESIS_FILE = "/jsonrpc/engine/eip6110/genesis.json";
|
||||
private static final String TEST_CASE_PATH = "/jsonrpc/engine/eip6110/test-cases/";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "engine_forkchoiceUpdatedV2",
|
||||
"params": [
|
||||
{
|
||||
"headBlockHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
|
||||
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"timestamp": "0x1235",
|
||||
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
|
||||
"suggestedFeeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"withdrawals": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"payloadStatus": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
|
||||
"validationError": null
|
||||
},
|
||||
"payloadId": "0x006221426d1aefcc"
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "engine_getPayloadV2",
|
||||
"params": [
|
||||
"0x006221426d1aefcc"
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": {
|
||||
"executionPayload": {
|
||||
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
|
||||
"feeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
|
||||
"gasLimit": "0x2ff3d8",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1235",
|
||||
"extraData": "0x",
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"transactions": [],
|
||||
"withdrawals": [],
|
||||
"deposits": null,
|
||||
"blockNumber": "0x1",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b"
|
||||
},
|
||||
"blockValue": "0x0"
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "engine_newPayloadV2",
|
||||
"params": [
|
||||
{
|
||||
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
|
||||
"feeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
|
||||
"blockNumber": "0x1",
|
||||
"gasLimit": "0x2ff3d8",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1235",
|
||||
"extraData": "0x",
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"transactions": [],
|
||||
"withdrawals": [],
|
||||
"dataGasUsed": null,
|
||||
"excessDataGas": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"result": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"validationError": null
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "engine_newPayloadV3",
|
||||
"params": [
|
||||
{
|
||||
"parentHash": "0xd76266aeddab03ffe29e2cf11a808d2a86f9b8582a9f4a48372f9a7795a95ba4",
|
||||
"feeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prevRandao": "0x6c9619f4c25184d07b2369f87ffdf9b3786f05ed3bc12c4e086b8850cd9fd606",
|
||||
"blockNumber": "0x1",
|
||||
"gasLimit": "0x2ff3d8",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1235",
|
||||
"extraData": "0x",
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"blockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"transactions": [],
|
||||
"withdrawals": [],
|
||||
"dataGasUsed": null,
|
||||
"excessDataGas": null
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"result": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"validationError": null
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "engine_forkchoiceUpdatedV2",
|
||||
"params": [
|
||||
{
|
||||
"headBlockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"result": {
|
||||
"payloadStatus": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"validationError": null
|
||||
},
|
||||
"payloadId": null
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"method": "engine_forkchoiceUpdatedV2",
|
||||
"params": [
|
||||
{
|
||||
"headBlockHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"safeBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"finalizedBlockHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"timestamp": "0x1236",
|
||||
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
|
||||
"suggestedFeeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"withdrawals": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"result": {
|
||||
"payloadStatus": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"validationError": null
|
||||
},
|
||||
"payloadId": "0x0062166c2eaa44c9"
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"method": "engine_getPayloadV3",
|
||||
"params": [
|
||||
"0x0062166c2eaa44c9"
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 7,
|
||||
"result": {
|
||||
"executionPayload": {
|
||||
"parentHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"feeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
|
||||
"gasLimit": "0x2ff7d8",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1236",
|
||||
"extraData": "0x",
|
||||
"baseFeePerGas": "0x2da282a8",
|
||||
"excessDataGas": "0x0",
|
||||
"transactions": [],
|
||||
"withdrawals": [],
|
||||
"blockNumber": "0x2",
|
||||
"dataGasUsed": "0x0",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"blockHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927"
|
||||
},
|
||||
"blockValue": "0x0",
|
||||
"blobsBundle": {
|
||||
"commitments": [],
|
||||
"proofs": [],
|
||||
"blobs": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"request": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"method": "engine_newPayloadV3",
|
||||
"params": [
|
||||
{
|
||||
"parentHash": "0x50c02dc77082fe2060b600cba0e6ce5a491af6d5323b2a90a7bc6359dd18e97b",
|
||||
"feeRecipient": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x7f5cfe1375a61009a22d24512d18035bc8f855129452fa9c6a6be2ef4e9da7db",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prevRandao": "0x5079013331632e5f6db41eccd876e5659c014b8a0a0809794ad1bf64631e030d",
|
||||
"blockNumber": "0x2",
|
||||
"gasLimit": "0x2ff7d8",
|
||||
"gasUsed": "0x0",
|
||||
"timestamp": "0x1236",
|
||||
"extraData": "0x",
|
||||
"baseFeePerGas": "0x2da282a8",
|
||||
"blockHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927",
|
||||
"transactions": [],
|
||||
"withdrawals": [],
|
||||
"dataGasUsed": "0x0",
|
||||
"excessDataGas": "0x0"
|
||||
},
|
||||
[]
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 8,
|
||||
"result": {
|
||||
"status": "VALID",
|
||||
"latestValidHash": "0xc33d43425366d661ef70df12faf8ccd66ed7d0c6718d16d14868ba49e6786927",
|
||||
"validationError": null
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
"berlinBlock":0,
|
||||
"londonBlock":0,
|
||||
"terminalTotalDifficulty":0,
|
||||
"shanghaiTime":0,
|
||||
"cancunTime":0,
|
||||
"experimentalEipsTime":20,
|
||||
"clique": {
|
||||
"period": 5,
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
],
|
||||
"deposits" : [],
|
||||
"blockNumber": "0x2",
|
||||
"blockHash": "0x58ea3e01b03ac17c68ed3e3d724a021408273fac8a86f42cb30a26be8e93fbe9",
|
||||
"blockHash": "0x4c4418c408aeadb4659d31d1c05108f26fabf713bb6f8cc487dba8424a725bf5",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
},
|
||||
"blockValue": "0x0"
|
||||
|
||||
@@ -1562,6 +1562,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
runner.awaitStop();
|
||||
|
||||
} catch (final Exception e) {
|
||||
logger.error("Failed to start Besu", e);
|
||||
throw new ParameterException(this.commandLine, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
@@ -1875,12 +1876,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
}
|
||||
|
||||
if (getActualGenesisConfigOptions().getCancunTime().isPresent()) {
|
||||
// if custom genesis provided, then trusted setup file is mandatory
|
||||
if (genesisFile != null && kzgTrustedSetupFile == null) {
|
||||
throw new ParameterException(
|
||||
this.commandLine,
|
||||
"--kzg-trusted-setup is mandatory when providing a custom genesis that support data blobs");
|
||||
}
|
||||
if (kzgTrustedSetupFile != null) {
|
||||
KZGPointEvalPrecompiledContract.init(kzgTrustedSetupFile);
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.services;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
@@ -113,9 +114,11 @@ public class TraceServiceImpl implements TraceService {
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
.dataPricePerGas(
|
||||
maybeParentHeader
|
||||
.flatMap(BlockHeader::getExcessDataGas)
|
||||
.map(
|
||||
parent ->
|
||||
calculateExcessDataGasForParent(protocolSpec, parent))
|
||||
.orElse(DataGas.ZERO));
|
||||
|
||||
tracer.traceStartTransaction(transaction);
|
||||
|
||||
@@ -5663,18 +5663,6 @@ public class BesuCommandTest extends CommandTestAbstract {
|
||||
.contains("--kzg-trusted-setup can only be specified on networks with data blobs enabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kzgTrustedSetupFileIsMandatoryWithCustomGenesisFile()
|
||||
throws IOException, URISyntaxException {
|
||||
final Path genesisFileWithBlobs = createFakeGenesisFile(GENESIS_WITH_DATA_BLOBS_ENABLED);
|
||||
parseCommand("--genesis-file", genesisFileWithBlobs.toString());
|
||||
|
||||
assertThat(commandOutput.toString(UTF_8)).isEmpty();
|
||||
assertThat(commandErrorOutput.toString(UTF_8))
|
||||
.contains(
|
||||
"--kzg-trusted-setup is mandatory when providing a custom genesis that support data blobs");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kzgTrustedSetupFileLoadedWithCustomGenesisFile()
|
||||
throws IOException, URISyntaxException {
|
||||
|
||||
@@ -200,7 +200,7 @@ public class BesuEventsImplTest {
|
||||
mock(EthPeer.class),
|
||||
new org.hyperledger.besu.ethereum.core.BlockHeader(
|
||||
null, null, null, null, null, null, null, null, 1, 1, 1, 1, null, null, null, 1, null,
|
||||
null, null, null));
|
||||
null, null, null, null));
|
||||
}
|
||||
|
||||
private void clearSyncTarget() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -247,6 +247,24 @@ public class GenesisConfigFile {
|
||||
return JsonUtil.getValueAsString(configRoot, "nonce", "0x0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets excess data gas.
|
||||
*
|
||||
* @return the excess data gas
|
||||
*/
|
||||
public String getExcessDataGas() {
|
||||
return JsonUtil.getValueAsString(configRoot, "excessdatagas", "0x0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data gas used.
|
||||
*
|
||||
* @return the data gas used
|
||||
*/
|
||||
public String getDataGasUsed() {
|
||||
return JsonUtil.getValueAsString(configRoot, "datagasused", "0x0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets coinbase.
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,9 @@ public class CombinedProtocolScheduleFactory {
|
||||
Optional.ofNullable(forkSpecs.higher(spec)).map(ForkSpec::getBlock);
|
||||
protocolSchedule.getScheduledProtocolSpecs().stream()
|
||||
.filter(protocolSpecMatchesConsensusBlockRange(spec.getBlock(), endBlock))
|
||||
.forEach(s -> combinedProtocolSchedule.putBlockNumberMilestone(s.milestone(), s.spec()));
|
||||
.forEach(
|
||||
s ->
|
||||
combinedProtocolSchedule.putBlockNumberMilestone(s.fork().milestone(), s.spec()));
|
||||
|
||||
// When moving to a new consensus mechanism we want to use the last milestone but created by
|
||||
// our consensus mechanism's BesuControllerBuilder so any additional rules are applied
|
||||
@@ -67,7 +69,7 @@ public class CombinedProtocolScheduleFactory {
|
||||
private Predicate<ScheduledProtocolSpec> protocolSpecMatchesConsensusBlockRange(
|
||||
final long startBlock, final Optional<Long> endBlock) {
|
||||
return scheduledProtocolSpec ->
|
||||
scheduledProtocolSpec.milestone() >= startBlock
|
||||
&& endBlock.map(b -> scheduledProtocolSpec.milestone() < b).orElse(true);
|
||||
scheduledProtocolSpec.fork().milestone() >= startBlock
|
||||
&& endBlock.map(b -> scheduledProtocolSpec.fork().milestone() < b).orElse(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ public class BftProtocolSchedule extends DefaultProtocolSchedule {
|
||||
checkArgument(
|
||||
!protocolSpecs.isEmpty(), "At least 1 milestone must be provided to the protocol schedule");
|
||||
checkArgument(
|
||||
protocolSpecs.last().milestone() == 0, "There must be a milestone starting from block 0");
|
||||
protocolSpecs.last().fork().milestone() == 0,
|
||||
"There must be a milestone starting from block 0");
|
||||
// protocolSpecs is sorted in descending block order, so the first one we find that's lower than
|
||||
// the requested level will be the most appropriate spec
|
||||
for (final ScheduledProtocolSpec s : protocolSpecs) {
|
||||
if (number >= s.milestone()) {
|
||||
if (number >= s.fork().milestone()) {
|
||||
return s.spec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import com.google.common.collect.Lists;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class IbftExtraDataEncoderTest {
|
||||
public class IbftExtraDataRLPEncoderTest {
|
||||
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
@@ -203,6 +203,13 @@ public class TransitionProtocolSchedule implements ProtocolSchedule {
|
||||
"Should not use TransitionProtocolSchedule wrapper class to create milestones");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ScheduledProtocolSpec.Hardfork> hardforkFor(
|
||||
final Predicate<ScheduledProtocolSpec> predicate) {
|
||||
return this.transitionUtils.dispatchFunctionAccordingToMergeState(
|
||||
schedule -> schedule.hardforkFor(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* List milestones.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** Arbitrary data for use in the KZG scheme. */
|
||||
public class Blob {
|
||||
|
||||
final Bytes data;
|
||||
|
||||
/**
|
||||
* Create a new Blob.
|
||||
*
|
||||
* @param data that represents the blob.
|
||||
*/
|
||||
public Blob(final Bytes data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Blob from an RLPInput.
|
||||
*
|
||||
* @param input to read from.
|
||||
* @return the Blob.
|
||||
*/
|
||||
public static Blob readFrom(final RLPInput input) {
|
||||
final Bytes bytes = input.readBytes();
|
||||
return new Blob(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the Blob to an RLPOutput.
|
||||
*
|
||||
* @param out to write to.
|
||||
*/
|
||||
public void writeTo(final RLPOutput out) {
|
||||
out.writeBytes(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data of the Blob.
|
||||
*
|
||||
* @return the data.
|
||||
*/
|
||||
public Bytes getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.List;
|
||||
|
||||
/** A class to hold the blobs, commitments, proofs and versioned hashes for a set of blobs. */
|
||||
public class BlobsWithCommitments {
|
||||
private final List<KZGCommitment> kzgCommitments;
|
||||
private final List<Blob> blobs;
|
||||
private final List<KZGProof> kzgProofs;
|
||||
|
||||
private final List<VersionedHash> versionedHashes;
|
||||
|
||||
/**
|
||||
* A class to hold the blobs, commitments and proofs for a set of blobs.
|
||||
*
|
||||
* @param kzgCommitments commitments for the blobs
|
||||
* @param blobs list of blobs to be committed to
|
||||
* @param kzgProofs proofs for the commitments
|
||||
* @param versionedHashes hashes of the commitments
|
||||
*/
|
||||
public BlobsWithCommitments(
|
||||
final List<KZGCommitment> kzgCommitments,
|
||||
final List<Blob> blobs,
|
||||
final List<KZGProof> kzgProofs,
|
||||
final List<VersionedHash> versionedHashes) {
|
||||
if (blobs.size() != kzgCommitments.size()
|
||||
|| blobs.size() != kzgProofs.size()
|
||||
|| kzgCommitments.size() != versionedHashes.size()) {
|
||||
throw new InvalidParameterException(
|
||||
"There must be an equal number of blobs, commitments and proofs");
|
||||
}
|
||||
this.kzgCommitments = kzgCommitments;
|
||||
this.blobs = blobs;
|
||||
this.kzgProofs = kzgProofs;
|
||||
this.versionedHashes = versionedHashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blobs.
|
||||
*
|
||||
* @return the blobs
|
||||
*/
|
||||
public List<Blob> getBlobs() {
|
||||
return blobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the commitments.
|
||||
*
|
||||
* @return the commitments
|
||||
*/
|
||||
public List<KZGCommitment> getKzgCommitments() {
|
||||
return kzgCommitments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the proofs.
|
||||
*
|
||||
* @return the proofs
|
||||
*/
|
||||
public List<KZGProof> getKzgProofs() {
|
||||
return kzgProofs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hashes.
|
||||
*
|
||||
* @return the hashes
|
||||
*/
|
||||
public List<VersionedHash> getVersionedHashes() {
|
||||
return versionedHashes;
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ package org.hyperledger.besu.datatypes;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.BaseUInt256Value;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
import org.apache.tuweni.units.bigints.BaseUInt64Value;
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
|
||||
/** A particular quantity of DataGas */
|
||||
public final class DataGas extends BaseUInt256Value<DataGas> implements Quantity {
|
||||
public final class DataGas extends BaseUInt64Value<DataGas> implements Quantity {
|
||||
|
||||
/** The constant ZERO. */
|
||||
public static final DataGas ZERO = of(0);
|
||||
@@ -30,27 +30,27 @@ public final class DataGas extends BaseUInt256Value<DataGas> implements Quantity
|
||||
public static final DataGas ONE = of(1);
|
||||
|
||||
/** The constant MAX_DATA_GAS. */
|
||||
public static final DataGas MAX_DATA_GAS = of(UInt256.MAX_VALUE);
|
||||
public static final DataGas MAX_DATA_GAS = of(UInt64.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* Instantiates a new DataGas.
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
DataGas(final UInt256 value) {
|
||||
DataGas(final UInt64 value) {
|
||||
super(value, DataGas::new);
|
||||
}
|
||||
|
||||
private DataGas(final long v) {
|
||||
this(UInt256.valueOf(v));
|
||||
this(UInt64.valueOf(v));
|
||||
}
|
||||
|
||||
private DataGas(final BigInteger v) {
|
||||
this(UInt256.valueOf(v));
|
||||
this(UInt64.valueOf(v));
|
||||
}
|
||||
|
||||
private DataGas(final String hexString) {
|
||||
this(UInt256.fromHexString(hexString));
|
||||
this(UInt64.fromHexString(hexString));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +79,7 @@ public final class DataGas extends BaseUInt256Value<DataGas> implements Quantity
|
||||
* @param value the value
|
||||
* @return the data gas
|
||||
*/
|
||||
public static DataGas of(final UInt256 value) {
|
||||
public static DataGas of(final UInt64 value) {
|
||||
return new DataGas(value);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public final class DataGas extends BaseUInt256Value<DataGas> implements Quantity
|
||||
* @return the data gas
|
||||
*/
|
||||
public static DataGas wrap(final Bytes value) {
|
||||
return new DataGas(UInt256.fromBytes(value));
|
||||
return new DataGas(UInt64.fromBytes(value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,12 @@ public class Hash extends DelegatingBytes32 {
|
||||
*/
|
||||
public static final Hash EMPTY = hash(Bytes.EMPTY);
|
||||
|
||||
private Hash(final Bytes32 bytes) {
|
||||
/**
|
||||
* Instantiates a new Hash.
|
||||
*
|
||||
* @param bytes raw bytes
|
||||
*/
|
||||
protected Hash(final Bytes32 bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** This class contains the data for a KZG commitment. */
|
||||
public class KZGCommitment {
|
||||
final Bytes data;
|
||||
|
||||
/**
|
||||
* Constructor for a KZG commitment.
|
||||
*
|
||||
* @param data The data for the KZG commitment.
|
||||
*/
|
||||
public KZGCommitment(final Bytes data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a KZG commitment from the RLP input.
|
||||
*
|
||||
* @param input The RLP input.
|
||||
* @return The KZG commitment.
|
||||
*/
|
||||
public static KZGCommitment readFrom(final RLPInput input) {
|
||||
final Bytes bytes = input.readBytes();
|
||||
return new KZGCommitment(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the KZG commitment to the RLP output.
|
||||
*
|
||||
* @param out The RLP output.
|
||||
*/
|
||||
public void writeTo(final RLPOutput out) {
|
||||
out.writeBytes(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data for the KZG commitment.
|
||||
*
|
||||
* @return The data for the KZG commitment.
|
||||
*/
|
||||
public Bytes getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** This class contains the data for a KZG proof for a KZG commitment. */
|
||||
public class KZGProof {
|
||||
final Bytes data;
|
||||
|
||||
/**
|
||||
* Constructor for a KZG proof.
|
||||
*
|
||||
* @param data The data for the KZG proof.
|
||||
*/
|
||||
public KZGProof(final Bytes data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a KZG proof from the RLP input.
|
||||
*
|
||||
* @param input The RLP input.
|
||||
* @return The KZG proof.
|
||||
*/
|
||||
public static KZGProof readFrom(final RLPInput input) {
|
||||
final Bytes bytes = input.readBytes();
|
||||
return new KZGProof(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the KZG proof to the RLP output.
|
||||
*
|
||||
* @param out The RLP output.
|
||||
*/
|
||||
public void writeTo(final RLPOutput out) {
|
||||
out.writeBytes(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data for the KZG proof.
|
||||
*
|
||||
* @return The data for the KZG proof.
|
||||
*/
|
||||
public Bytes getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
/** A Sha256Hash is a Hash that has been generated using the SHA-256 algorithm. */
|
||||
public class Sha256Hash extends Hash {
|
||||
|
||||
/**
|
||||
* Construct a Sha256Hash from a Bytes32 value.
|
||||
*
|
||||
* @param bytes raw bytes of the hash
|
||||
*/
|
||||
private Sha256Hash(final Bytes32 bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Sha256Hash from a Bytes value.
|
||||
*
|
||||
* @param value The value to hash.
|
||||
* @return The Sha256Hash of the value.
|
||||
*/
|
||||
public static Hash sha256(final Bytes value) {
|
||||
return new Sha256Hash(org.hyperledger.besu.crypto.Hash.sha256(value));
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,10 @@ public enum TransactionType {
|
||||
public static TransactionType of(final int serializedTypeValue) {
|
||||
return Arrays.stream(
|
||||
new TransactionType[] {
|
||||
TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559
|
||||
TransactionType.FRONTIER,
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionType.EIP1559,
|
||||
TransactionType.BLOB
|
||||
})
|
||||
.filter(transactionType -> transactionType.typeValue == serializedTypeValue)
|
||||
.findFirst()
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
/**
|
||||
* A VersionedHash is a Hash that forfeits its most significant byte to indicate the hashing
|
||||
* algorithm which was used.
|
||||
*/
|
||||
public class VersionedHash {
|
||||
|
||||
/**
|
||||
* The versionedHash value. The first byte is the version id, the remainder is the subsequent
|
||||
* bytes of the hash.
|
||||
*/
|
||||
Bytes32 hashish;
|
||||
|
||||
/** The version id for sha256 hashes. */
|
||||
public static final byte SHA256_VERSION_ID = 1;
|
||||
|
||||
/** A default versioned hash, nonsensical but valid. */
|
||||
public static VersionedHash DEFAULT_VERSIONED_HASH =
|
||||
new VersionedHash(SHA256_VERSION_ID, Hash.ZERO);
|
||||
|
||||
/**
|
||||
* Construct a VersionedHash from a Bytes32 value.
|
||||
*
|
||||
* @param versionId The version id of the hash. 01 for sha256.
|
||||
* @param hash The hash value being versioned.
|
||||
*/
|
||||
public VersionedHash(final byte versionId, final Hash hash) {
|
||||
if (versionId != SHA256_VERSION_ID) {
|
||||
throw new IllegalArgumentException("Only supported hash version is 0x01, sha256 hash.");
|
||||
}
|
||||
this.hashish =
|
||||
Bytes32.wrap(
|
||||
Bytes.concatenate(Bytes.of(SHA256_VERSION_ID), hash.slice(1, hash.size() - 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a VersionedHash from a Bytes32 value.
|
||||
*
|
||||
* @param typedHash raw versioned hash bytes to parse.
|
||||
*/
|
||||
public VersionedHash(final Bytes32 typedHash) {
|
||||
byte versionId = typedHash.get(0);
|
||||
if (versionId != SHA256_VERSION_ID) {
|
||||
throw new IllegalArgumentException("Only supported hash version is 0x01, sha256 hash.");
|
||||
}
|
||||
this.hashish = typedHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert it to raw bytes.
|
||||
*
|
||||
* @return The hash value.
|
||||
*/
|
||||
public Bytes32 toBytes() {
|
||||
return this.hashish;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version id of the hash.
|
||||
*
|
||||
* @return the version id.
|
||||
*/
|
||||
public byte getVersionId() {
|
||||
return this.hashish.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VersionedHash that = (VersionedHash) o;
|
||||
return getVersionId() == that.getVersionId() && Objects.equals(this.toBytes(), that.toBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getVersionId(), hashish);
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.toBytes().toHexString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class BlobsWithCommitmentsTest {
|
||||
@Test
|
||||
public void blobsWithCommitmentsMustHaveSameNumberOfElements() {
|
||||
String actualMessage =
|
||||
assertThrows(
|
||||
InvalidParameterException.class,
|
||||
() ->
|
||||
new BlobsWithCommitments(
|
||||
List.of(new KZGCommitment(Bytes.of(1))), List.of(), List.of(), List.of()))
|
||||
.getMessage();
|
||||
final String expectedMessage = "There must be an equal number of blobs, commitments and proofs";
|
||||
assertThat(actualMessage).isEqualTo(expectedMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.datatypes;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class VersionedHashTest {
|
||||
|
||||
@Test
|
||||
public void throwsOnUnsupportedHashType() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new VersionedHash((byte) 0, Hash.ZERO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsOnParsingUnsupportedHashType() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new VersionedHash(Bytes32.ZERO));
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,7 @@ public class JsonRpcResponseUtils {
|
||||
mixHash,
|
||||
nonce,
|
||||
withdrawalsRoot,
|
||||
null, // ToDo 4844: set with the value of data_gas_used field
|
||||
null, // ToDo 4844: set with the value of excess_data_gas field
|
||||
depositsRoot,
|
||||
blockHeaderFunctions);
|
||||
@@ -194,7 +195,6 @@ public class JsonRpcResponseUtils {
|
||||
.byteValueExact()))
|
||||
.payload(bytes(input))
|
||||
.sender(address(fromAddress))
|
||||
.v(bigInteger(v))
|
||||
.build();
|
||||
|
||||
return new TransactionCompleteResult(
|
||||
@@ -224,10 +224,6 @@ public class JsonRpcResponseUtils {
|
||||
return prefixedHex.startsWith("0x") ? prefixedHex.substring(2) : prefixedHex;
|
||||
}
|
||||
|
||||
private BigInteger bigInteger(final String hex) {
|
||||
return hex == null ? null : new BigInteger(removeHexPrefix(hex), HEX_RADIX);
|
||||
}
|
||||
|
||||
private Wei wei(final String hex) {
|
||||
return Wei.fromHexString(hex);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ package org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.graphql.GraphQLContextType;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -46,12 +48,15 @@ public class TransactionAdapter extends AdapterBase {
|
||||
final DataFetchingEnvironment environment) {
|
||||
if (transactionReceiptWithMetadata == null) {
|
||||
final BlockchainQueries query = getBlockchainQueries(environment);
|
||||
final ProtocolSchedule protocolSchedule =
|
||||
environment.getGraphQlContext().get(GraphQLContextType.PROTOCOL_SCHEDULE);
|
||||
|
||||
final Transaction transaction = transactionWithMetadata.getTransaction();
|
||||
if (transaction == null) {
|
||||
transactionReceiptWithMetadata = Optional.empty();
|
||||
} else {
|
||||
transactionReceiptWithMetadata =
|
||||
query.transactionReceiptByTransactionHash(transaction.getHash());
|
||||
query.transactionReceiptByTransactionHash(transaction.getHash(), protocolSchedule);
|
||||
}
|
||||
}
|
||||
return transactionReceiptWithMetadata;
|
||||
@@ -187,6 +192,9 @@ public class TransactionAdapter extends AdapterBase {
|
||||
|
||||
public List<LogAdapter> getLogs(final DataFetchingEnvironment environment) {
|
||||
final BlockchainQueries query = getBlockchainQueries(environment);
|
||||
final ProtocolSchedule protocolSchedule =
|
||||
environment.getGraphQlContext().get(GraphQLContextType.PROTOCOL_SCHEDULE);
|
||||
|
||||
final Hash hash = transactionWithMetadata.getTransaction().getHash();
|
||||
|
||||
final Optional<BlockHeader> maybeBlockHeader =
|
||||
@@ -201,7 +209,7 @@ public class TransactionAdapter extends AdapterBase {
|
||||
}
|
||||
|
||||
final Optional<TransactionReceiptWithMetadata> maybeTransactionReceiptWithMetadata =
|
||||
query.transactionReceiptByTransactionHash(hash);
|
||||
query.transactionReceiptByTransactionHash(hash, protocolSchedule);
|
||||
final List<LogAdapter> results = new ArrayList<>();
|
||||
if (maybeTransactionReceiptWithMetadata.isPresent()) {
|
||||
final List<LogWithMetadata> logs =
|
||||
|
||||
@@ -52,8 +52,10 @@ public enum RpcMethod {
|
||||
DEBUG_GET_RAW_TRANSACTION("debug_getRawTransaction"),
|
||||
ENGINE_GET_PAYLOAD_V1("engine_getPayloadV1"),
|
||||
ENGINE_GET_PAYLOAD_V2("engine_getPayloadV2"),
|
||||
ENGINE_GET_PAYLOAD_V3("engine_getPayloadV3"),
|
||||
ENGINE_NEW_PAYLOAD_V1("engine_newPayloadV1"),
|
||||
ENGINE_NEW_PAYLOAD_V2("engine_newPayloadV2"),
|
||||
ENGINE_NEW_PAYLOAD_V3("engine_newPayloadV3"),
|
||||
ENGINE_FORKCHOICE_UPDATED_V1("engine_forkchoiceUpdatedV1"),
|
||||
ENGINE_FORKCHOICE_UPDATED_V2("engine_forkchoiceUpdatedV2"),
|
||||
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"),
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonR
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -137,6 +138,10 @@ public class JsonRpcRequest {
|
||||
return parameterAccessor.optional(params, index, paramClass);
|
||||
}
|
||||
|
||||
public <T> Optional<List<T>> getOptionalList(final int index, final Class<T> paramClass) {
|
||||
return parameterAccessor.optionalList(params, index, paramClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JsonRpcRequest{"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -68,6 +69,10 @@ public class JsonRpcRequestContext {
|
||||
return jsonRpcRequest.getOptionalParameter(index, paramClass);
|
||||
}
|
||||
|
||||
public <T> Optional<List<T>> getOptionalList(final int index, final Class<T> listOf) {
|
||||
return jsonRpcRequest.getOptionalList(index, listOf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -86,7 +86,8 @@ public class EthGetMinerDataByBlockHash implements JsonRpcMethod {
|
||||
.map(
|
||||
t ->
|
||||
blockchainQueries
|
||||
.transactionReceiptByTransactionHash(t.getTransaction().getHash())
|
||||
.transactionReceiptByTransactionHash(
|
||||
t.getTransaction().getHash(), protocolSchedule)
|
||||
.map(
|
||||
receipt ->
|
||||
receipt
|
||||
|
||||
@@ -24,14 +24,19 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TransactionRec
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TransactionReceiptStatusResult;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionReceiptType;
|
||||
|
||||
public class EthGetTransactionReceipt implements JsonRpcMethod {
|
||||
|
||||
private final BlockchainQueries blockchainQueries;
|
||||
|
||||
public EthGetTransactionReceipt(final BlockchainQueries blockchainQueries) {
|
||||
private final ProtocolSchedule protocolSchedule;
|
||||
|
||||
public EthGetTransactionReceipt(
|
||||
final BlockchainQueries blockchainQueries, final ProtocolSchedule protocolSchedule) {
|
||||
this.blockchainQueries = blockchainQueries;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +49,7 @@ public class EthGetTransactionReceipt implements JsonRpcMethod {
|
||||
final Hash hash = requestContext.getRequiredParameter(0, Hash.class);
|
||||
final TransactionReceiptResult result =
|
||||
blockchainQueries
|
||||
.transactionReceiptByTransactionHash(hash)
|
||||
.transactionReceiptByTransactionHash(hash, protocolSchedule)
|
||||
.map(this::getResult)
|
||||
.orElse(null);
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
|
||||
@@ -89,8 +91,10 @@ public class ExecuteTransactionStep implements Function<TransactionTrace, Transa
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
maybeParentHeader.flatMap(BlockHeader::getExcessDataGas).orElse(DataGas.ZERO));
|
||||
.dataPricePerGas(
|
||||
maybeParentHeader
|
||||
.map(parent -> calculateExcessDataGasForParent(protocolSpec, parent))
|
||||
.orElse(DataGas.ZERO));
|
||||
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain);
|
||||
result =
|
||||
transactionProcessor.processTransaction(
|
||||
|
||||
@@ -25,7 +25,9 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
@@ -52,10 +54,14 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
|
||||
import org.hyperledger.besu.plugin.services.exception.StorageException;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -65,6 +71,7 @@ import java.util.stream.Collectors;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.Json;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -97,13 +104,32 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
final EnginePayloadParameter blockParam =
|
||||
requestContext.getRequiredParameter(0, EnginePayloadParameter.class);
|
||||
|
||||
Optional<List<String>> maybeVersionedHashParam =
|
||||
requestContext.getOptionalList(1, String.class);
|
||||
|
||||
Object reqId = requestContext.getRequest().getId();
|
||||
Optional<List<VersionedHash>> maybeVersionedHashes;
|
||||
try {
|
||||
maybeVersionedHashes = extractVersionedHashes(maybeVersionedHashParam);
|
||||
} catch (RuntimeException ex) {
|
||||
return respondWithInvalid(reqId, blockParam, null, INVALID, "Invalid versionedHash");
|
||||
}
|
||||
|
||||
final Optional<BlockHeader> maybeParentHeader =
|
||||
protocolContext.getBlockchain().getBlockHeader(blockParam.getParentHash());
|
||||
|
||||
LOG.atTrace()
|
||||
.setMessage("blockparam: {}")
|
||||
.addArgument(() -> Json.encodePrettily(blockParam))
|
||||
.log();
|
||||
|
||||
/*
|
||||
ValidationResult<JsonRpcError> forkValidationResult = validateForkSupported(reqId, blockParam);
|
||||
if (!forkValidationResult.isValid()) {
|
||||
return new JsonRpcErrorResponse(reqId, forkValidationResult.getInvalidReason());
|
||||
}
|
||||
*/
|
||||
|
||||
final Optional<List<Withdrawal>> maybeWithdrawals =
|
||||
Optional.ofNullable(blockParam.getWithdrawals())
|
||||
.map(ws -> ws.stream().map(WithdrawalParameter::toWithdrawal).collect(toList()));
|
||||
@@ -172,7 +198,10 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
blockParam.getPrevRandao(),
|
||||
0,
|
||||
maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null),
|
||||
null,
|
||||
blockParam.getDataGasUsed() == null ? null : blockParam.getDataGasUsed(),
|
||||
blockParam.getExcessDataGas() == null
|
||||
? null
|
||||
: DataGas.fromHexString(blockParam.getExcessDataGas()),
|
||||
maybeDeposits.map(BodyValidation::depositsRoot).orElse(null),
|
||||
headerFunctions);
|
||||
|
||||
@@ -184,8 +213,30 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
"Computed block hash %s does not match block hash parameter %s",
|
||||
newBlockHeader.getBlockHash(), blockParam.getBlockHash());
|
||||
LOG.debug(errorMessage);
|
||||
return respondWithInvalid(reqId, blockParam, null, getInvalidBlockHashStatus(), errorMessage);
|
||||
return respondWithInvalid(
|
||||
reqId,
|
||||
blockParam,
|
||||
mergeCoordinator.getLatestValidAncestor(blockParam.getParentHash()).orElse(null),
|
||||
getInvalidBlockHashStatus(),
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
ValidationResult<RpcErrorType> blobValidationResult =
|
||||
validateBlobs(
|
||||
transactions,
|
||||
newBlockHeader,
|
||||
maybeParentHeader,
|
||||
maybeVersionedHashes,
|
||||
protocolSchedule.getByBlockHeader(newBlockHeader));
|
||||
if (!blobValidationResult.isValid()) {
|
||||
return respondWithInvalid(
|
||||
reqId,
|
||||
blockParam,
|
||||
null,
|
||||
getInvalidBlockHashStatus(),
|
||||
blobValidationResult.getErrorMessage());
|
||||
}
|
||||
|
||||
// do we already have this payload
|
||||
if (protocolContext.getBlockchain().getBlockByHash(newBlockHeader.getBlockHash()).isPresent()) {
|
||||
LOG.debug("block already present");
|
||||
@@ -202,8 +253,6 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
"Block already present in bad block manager.");
|
||||
}
|
||||
|
||||
final Optional<BlockHeader> maybeParentHeader =
|
||||
protocolContext.getBlockchain().getBlockHeader(blockParam.getParentHash());
|
||||
if (maybeParentHeader.isPresent()
|
||||
&& (blockParam.getTimestamp() <= maybeParentHeader.get().getTimestamp())) {
|
||||
return respondWithInvalid(
|
||||
@@ -225,7 +274,6 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
.addArgument(block::toLogString)
|
||||
.log();
|
||||
mergeCoordinator.appendNewPayloadToSync(block);
|
||||
|
||||
return respondWith(reqId, blockParam, null, SYNCING);
|
||||
}
|
||||
|
||||
@@ -341,6 +389,98 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
protected ValidationResult<RpcErrorType> validateForkSupported(
|
||||
final Object id, final EnginePayloadParameter payloadParameter) {
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
|
||||
protected ValidationResult<RpcErrorType> validateBlobs(
|
||||
final List<Transaction> transactions,
|
||||
final BlockHeader header,
|
||||
final Optional<BlockHeader> maybeParentHeader,
|
||||
final Optional<List<VersionedHash>> maybeVersionedHashes,
|
||||
final ProtocolSpec protocolSpec) {
|
||||
|
||||
var blobTransactions =
|
||||
transactions.stream().filter(transaction -> transaction.getType().supportsBlob()).toList();
|
||||
|
||||
final List<VersionedHash> transactionVersionedHashes = new ArrayList<>();
|
||||
for (Transaction transaction : blobTransactions) {
|
||||
var versionedHashes = transaction.getVersionedHashes();
|
||||
// blob transactions must have at least one blob
|
||||
if (versionedHashes.isEmpty()) {
|
||||
return ValidationResult.invalid(
|
||||
RpcErrorType.INVALID_PARAMS, "There must be at least one blob");
|
||||
}
|
||||
transactionVersionedHashes.addAll(versionedHashes.get());
|
||||
}
|
||||
|
||||
if (maybeVersionedHashes.isEmpty() && !transactionVersionedHashes.isEmpty()) {
|
||||
return ValidationResult.invalid(
|
||||
RpcErrorType.INVALID_PARAMS, "Payload must contain versioned hashes for transactions");
|
||||
}
|
||||
|
||||
// Validate versionedHashesParam
|
||||
if (maybeVersionedHashes.isPresent()
|
||||
&& !maybeVersionedHashes.get().equals(transactionVersionedHashes)) {
|
||||
return ValidationResult.invalid(
|
||||
RpcErrorType.INVALID_PARAMS,
|
||||
"Versioned hashes from blob transactions do not match expected values");
|
||||
}
|
||||
|
||||
// Validate excessDataGas
|
||||
if (maybeParentHeader.isPresent()) {
|
||||
if (!validateExcessDataGas(header, maybeParentHeader.get(), protocolSpec)) {
|
||||
return ValidationResult.invalid(
|
||||
RpcErrorType.INVALID_PARAMS,
|
||||
"Payload excessDataGas does not match calculated excessDataGas");
|
||||
}
|
||||
}
|
||||
|
||||
// Validate dataGasUsed
|
||||
if (header.getDataGasUsed().isPresent() && maybeVersionedHashes.isPresent()) {
|
||||
if (!validateDataGasUsed(header, maybeVersionedHashes.get(), protocolSpec)) {
|
||||
return ValidationResult.invalid(
|
||||
RpcErrorType.INVALID_PARAMS,
|
||||
"Payload DataGasUsed does not match calculated DataGasUsed");
|
||||
}
|
||||
}
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
|
||||
private boolean validateExcessDataGas(
|
||||
final BlockHeader header, final BlockHeader parentHeader, final ProtocolSpec protocolSpec) {
|
||||
DataGas calculatedDataGas =
|
||||
ExcessDataGasCalculator.calculateExcessDataGasForParent(protocolSpec, parentHeader);
|
||||
return header.getExcessDataGas().orElse(DataGas.ZERO).equals(calculatedDataGas);
|
||||
}
|
||||
|
||||
private boolean validateDataGasUsed(
|
||||
final BlockHeader header,
|
||||
final List<VersionedHash> maybeVersionedHashes,
|
||||
final ProtocolSpec protocolSpec) {
|
||||
var calculatedDataGas =
|
||||
protocolSpec.getGasCalculator().dataGasCost(maybeVersionedHashes.size());
|
||||
return header.getDataGasUsed().orElse(0L).equals(calculatedDataGas);
|
||||
}
|
||||
|
||||
private Optional<List<VersionedHash>> extractVersionedHashes(
|
||||
final Optional<List<String>> maybeVersionedHashParam) {
|
||||
return maybeVersionedHashParam.map(
|
||||
versionedHashes ->
|
||||
versionedHashes.stream()
|
||||
.map(Bytes32::fromHexString)
|
||||
.map(
|
||||
hash -> {
|
||||
try {
|
||||
return new VersionedHash(hash);
|
||||
} catch (InvalidParameterException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private void logImportedBlockInfo(final Block block, final double timeInS) {
|
||||
final StringBuilder message = new StringBuilder();
|
||||
message.append("Imported #%,d / %d tx");
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
|
||||
import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class EngineGetPayloadV3 extends AbstractEngineGetPayload {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EngineGetPayloadV3.class);
|
||||
private final Optional<ScheduledProtocolSpec.Hardfork> shanghai;
|
||||
private final Optional<ScheduledProtocolSpec.Hardfork> cancun;
|
||||
|
||||
public EngineGetPayloadV3(
|
||||
final Vertx vertx,
|
||||
final ProtocolContext protocolContext,
|
||||
final MergeMiningCoordinator mergeMiningCoordinator,
|
||||
final BlockResultFactory blockResultFactory,
|
||||
final EngineCallListener engineCallListener,
|
||||
final ProtocolSchedule schedule) {
|
||||
super(vertx, protocolContext, mergeMiningCoordinator, blockResultFactory, engineCallListener);
|
||||
this.shanghai = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Shanghai"));
|
||||
this.cancun = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RpcMethod.ENGINE_GET_PAYLOAD_V3.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonRpcResponse createResponse(
|
||||
final JsonRpcRequestContext request,
|
||||
final PayloadIdentifier payloadId,
|
||||
final BlockWithReceipts blockWithReceipts) {
|
||||
|
||||
try {
|
||||
long builtAt = blockWithReceipts.getHeader().getTimestamp();
|
||||
|
||||
if (this.shanghai.isPresent() && builtAt < this.shanghai.get().milestone()) {
|
||||
return new JsonRpcSuccessResponse(
|
||||
request.getRequest().getId(),
|
||||
blockResultFactory.payloadTransactionCompleteV1(blockWithReceipts.getBlock()));
|
||||
} else if (this.shanghai.isPresent()
|
||||
&& builtAt >= this.shanghai.get().milestone()
|
||||
&& this.cancun.isPresent()
|
||||
&& builtAt < this.cancun.get().milestone()) {
|
||||
return new JsonRpcSuccessResponse(
|
||||
request.getRequest().getId(),
|
||||
blockResultFactory.payloadTransactionCompleteV2(blockWithReceipts));
|
||||
} else {
|
||||
return new JsonRpcSuccessResponse(
|
||||
request.getRequest().getId(),
|
||||
blockResultFactory.payloadTransactionCompleteV3(blockWithReceipts));
|
||||
}
|
||||
|
||||
} catch (ClassCastException e) {
|
||||
LOG.error("configuration error, can't call V3 endpoint with non-default protocol schedule");
|
||||
return new JsonRpcErrorResponse(request.getRequest().getId(), RpcErrorType.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,19 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID_BLOCK_HASH;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
@@ -50,4 +59,14 @@ public class EngineNewPayloadV1 extends AbstractEngineNewPayload {
|
||||
protected EngineStatus getInvalidBlockHashStatus() {
|
||||
return INVALID_BLOCK_HASH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValidationResult<RpcErrorType> validateBlobs(
|
||||
final List<Transaction> transactions,
|
||||
final BlockHeader header,
|
||||
final Optional<BlockHeader> maybeParentHeader,
|
||||
final Optional<List<VersionedHash>> maybeVersionedHashParam,
|
||||
final ProtocolSpec protocolSpec) {
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,19 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
@@ -38,4 +47,14 @@ public class EngineNewPayloadV2 extends AbstractEngineNewPayload {
|
||||
public String getName() {
|
||||
return RpcMethod.ENGINE_NEW_PAYLOAD_V2.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValidationResult<RpcErrorType> validateBlobs(
|
||||
final List<Transaction> transactions,
|
||||
final BlockHeader header,
|
||||
final Optional<BlockHeader> maybeParentHeader,
|
||||
final Optional<List<VersionedHash>> maybeVersionedHashParam,
|
||||
final ProtocolSpec protocolSpec) {
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
public class EngineNewPayloadV3 extends AbstractEngineNewPayload {
|
||||
|
||||
private final ProtocolSchedule timestampSchedule;
|
||||
|
||||
public EngineNewPayloadV3(
|
||||
final Vertx vertx,
|
||||
final ProtocolSchedule timestampSchedule,
|
||||
final ProtocolContext protocolContext,
|
||||
final MergeMiningCoordinator mergeCoordinator,
|
||||
final EthPeers ethPeers,
|
||||
final EngineCallListener engineCallListener) {
|
||||
super(
|
||||
vertx, timestampSchedule, protocolContext, mergeCoordinator, ethPeers, engineCallListener);
|
||||
this.timestampSchedule = timestampSchedule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RpcMethod.ENGINE_NEW_PAYLOAD_V3.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValidationResult<RpcErrorType> validateForkSupported(
|
||||
final Object reqId, final EnginePayloadParameter payloadParameter) {
|
||||
var cancun = timestampSchedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun"));
|
||||
|
||||
if (cancun.isPresent() && payloadParameter.getTimestamp() >= cancun.get().milestone()) {
|
||||
if (payloadParameter.getDataGasUsed() == null
|
||||
|| payloadParameter.getExcessDataGas() == null) {
|
||||
return ValidationResult.invalid(RpcErrorType.INVALID_PARAMS, "Missing data gas fields");
|
||||
} else {
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
} else {
|
||||
return ValidationResult.invalid(RpcErrorType.INVALID_PARAMS, "Fork not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,9 @@ public class EnginePayloadParameter {
|
||||
private final LogsBloomFilter logsBloom;
|
||||
private final List<String> transactions;
|
||||
private final List<WithdrawalParameter> withdrawals;
|
||||
private final Long dataGasUsed;
|
||||
private final String excessDataGas;
|
||||
private final List<Bytes32> versionedHashes;
|
||||
private final List<DepositParameter> deposits;
|
||||
|
||||
@JsonCreator
|
||||
@@ -66,6 +69,9 @@ public class EnginePayloadParameter {
|
||||
@JsonProperty("prevRandao") final String prevRandao,
|
||||
@JsonProperty("transactions") final List<String> transactions,
|
||||
@JsonProperty("withdrawals") final List<WithdrawalParameter> withdrawals,
|
||||
@JsonProperty("dataGasUsed") final UnsignedLongParameter dataGasUsed,
|
||||
@JsonProperty("excessDataGas") final String excessDataGas,
|
||||
@JsonProperty("versionedHashes") final List<Bytes32> versionedHashes,
|
||||
@JsonProperty("deposits") final List<DepositParameter> deposits) {
|
||||
this.blockHash = blockHash;
|
||||
this.parentHash = parentHash;
|
||||
@@ -82,6 +88,9 @@ public class EnginePayloadParameter {
|
||||
this.prevRandao = Bytes32.fromHexString(prevRandao);
|
||||
this.transactions = transactions;
|
||||
this.withdrawals = withdrawals;
|
||||
this.dataGasUsed = dataGasUsed == null ? null : dataGasUsed.getValue();
|
||||
this.excessDataGas = excessDataGas;
|
||||
this.versionedHashes = versionedHashes;
|
||||
this.deposits = deposits;
|
||||
}
|
||||
|
||||
@@ -145,7 +154,19 @@ public class EnginePayloadParameter {
|
||||
return withdrawals;
|
||||
}
|
||||
|
||||
public Long getDataGasUsed() {
|
||||
return dataGasUsed;
|
||||
}
|
||||
|
||||
public String getExcessDataGas() {
|
||||
return excessDataGas;
|
||||
}
|
||||
|
||||
public List<DepositParameter> getDeposits() {
|
||||
return deposits;
|
||||
}
|
||||
|
||||
public List<Bytes32> getVersionedHashes() {
|
||||
return versionedHashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
|
||||
@@ -84,4 +86,26 @@ public class JsonRpcParameter {
|
||||
|
||||
return Optional.of(param);
|
||||
}
|
||||
|
||||
public <T> Optional<List<T>> optionalList(
|
||||
final Object[] params, final int index, final Class<T> listClass) {
|
||||
if (params == null || params.length <= index || params[index] == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Object rawParam = params[index];
|
||||
if (List.class.isAssignableFrom(rawParam.getClass())) {
|
||||
try {
|
||||
String listJson = mapper.writeValueAsString(rawParam);
|
||||
List<T> returnedList = mapper.readValue(listJson, new TypeReference<List<T>>() {});
|
||||
return Optional.of(returnedList);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new InvalidJsonRpcParameters(
|
||||
String.format(
|
||||
"Invalid json rpc parameter at index %d. Supplied value was: '%s' of type: '%s' - expected type: '%s'",
|
||||
index, rawParam, rawParam.getClass().getName(), listClass.getName()),
|
||||
e);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ public class UnsignedLongParameter {
|
||||
|
||||
@JsonCreator
|
||||
public UnsignedLongParameter(final String value) {
|
||||
checkArgument(value != null);
|
||||
this.value = Long.decode(value);
|
||||
checkArgument(this.value >= 0);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -52,10 +54,10 @@ public class BlockReplay {
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
.dataPricePerGas(
|
||||
blockchain
|
||||
.getBlockHeader(header.getParentHash())
|
||||
.flatMap(BlockHeader::getExcessDataGas)
|
||||
.map(parent -> calculateExcessDataGasForParent(protocolSpec, parent))
|
||||
.orElse(DataGas.ZERO));
|
||||
|
||||
final List<TransactionTrace> transactionTraces =
|
||||
@@ -86,10 +88,10 @@ public class BlockReplay {
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
.dataPricePerGas(
|
||||
blockchain
|
||||
.getBlockHeader(header.getParentHash())
|
||||
.flatMap(BlockHeader::getExcessDataGas)
|
||||
.map(parent -> calculateExcessDataGasForParent(protocolSpec, parent))
|
||||
.orElse(DataGas.ZERO));
|
||||
|
||||
for (final Transaction transaction : body.getTransactions()) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
|
||||
|
||||
import static java.util.function.Predicate.isEqual;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
@@ -118,10 +119,10 @@ public class TransactionTracer {
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
.dataPricePerGas(
|
||||
blockchain
|
||||
.getBlockHeader(header.getParentHash())
|
||||
.flatMap(BlockHeader::getExcessDataGas)
|
||||
.map(parent -> calculateExcessDataGasForParent(protocolSpec, parent))
|
||||
.orElse(DataGas.ZERO));
|
||||
for (int i = 0; i < body.getTransactions().size(); i++) {
|
||||
((StackedUpdater<?, ?>) stackedUpdater).markTransactionBoundary();
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.KZGProof;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@JsonPropertyOrder({"commitments", "proofs", "blobs"})
|
||||
public class BlobsBundleV1 {
|
||||
|
||||
private final List<String> commitments;
|
||||
|
||||
private final List<String> proofs;
|
||||
|
||||
private final List<String> blobs;
|
||||
|
||||
public BlobsBundleV1(final List<Transaction> transactions) {
|
||||
final List<BlobsWithCommitments> blobsWithCommitments =
|
||||
transactions.stream()
|
||||
.map(Transaction::getBlobsWithCommitments)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.toList();
|
||||
|
||||
this.commitments =
|
||||
blobsWithCommitments.stream()
|
||||
.flatMap(b -> b.getKzgCommitments().stream())
|
||||
.map(KZGCommitment::getData)
|
||||
.map(Bytes::toString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
this.proofs =
|
||||
blobsWithCommitments.stream()
|
||||
.flatMap(b -> b.getKzgProofs().stream())
|
||||
.map(KZGProof::getData)
|
||||
.map(Bytes::toString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
this.blobs =
|
||||
blobsWithCommitments.stream()
|
||||
.flatMap(b -> b.getBlobs().stream())
|
||||
.map(Blob::getData)
|
||||
.map(Bytes::toString)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public BlobsBundleV1(
|
||||
final List<String> commitments, final List<String> proofs, final List<String> blobs) {
|
||||
if (blobs.size() != commitments.size() || blobs.size() != proofs.size()) {
|
||||
throw new InvalidParameterException(
|
||||
"There must be an equal number of blobs, commitments and proofs");
|
||||
}
|
||||
this.commitments = commitments;
|
||||
this.proofs = proofs;
|
||||
this.blobs = blobs;
|
||||
}
|
||||
|
||||
@JsonGetter("commitments")
|
||||
public List<String> getCommitments() {
|
||||
return commitments;
|
||||
}
|
||||
|
||||
@JsonGetter("proofs")
|
||||
public List<String> getProofs() {
|
||||
return proofs;
|
||||
}
|
||||
|
||||
@JsonGetter("blobs")
|
||||
public List<String> getBlobs() {
|
||||
return blobs;
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,9 @@ public class BlockResult implements JsonRpcResult {
|
||||
private final String withdrawalsRoot;
|
||||
private final List<WithdrawalParameter> withdrawals;
|
||||
|
||||
private final String dataGasUsed;
|
||||
private final String excessDataGas;
|
||||
|
||||
public BlockResult(
|
||||
final BlockHeader header,
|
||||
final List<TransactionResult> transactions,
|
||||
@@ -128,6 +131,9 @@ public class BlockResult implements JsonRpcResult {
|
||||
withdrawals
|
||||
.map(w -> w.stream().map(WithdrawalParameter::fromWithdrawal).collect(toList()))
|
||||
.orElse(null);
|
||||
|
||||
this.dataGasUsed = header.getDataGasUsed().map(Quantity::create).orElse(null);
|
||||
this.excessDataGas = header.getExcessDataGas().map(Quantity::create).orElse(null);
|
||||
}
|
||||
|
||||
@JsonGetter(value = "number")
|
||||
@@ -250,4 +256,14 @@ public class BlockResult implements JsonRpcResult {
|
||||
public List<WithdrawalParameter> getWithdrawals() {
|
||||
return withdrawals;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "dataGasUsed")
|
||||
public String getDataGasUsed() {
|
||||
return dataGasUsed;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "excessDataGas")
|
||||
public String getExcessDataGas() {
|
||||
return excessDataGas;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,26 @@ public class BlockResultFactory {
|
||||
return new EngineGetPayloadBodiesResultV1(payloadBodies);
|
||||
}
|
||||
|
||||
public EngineGetPayloadResultV3 payloadTransactionCompleteV3(
|
||||
final BlockWithReceipts blockWithReceipts) {
|
||||
final List<String> txs =
|
||||
blockWithReceipts.getBlock().getBody().getTransactions().stream()
|
||||
.map(TransactionEncoder::encodeOpaqueBytes)
|
||||
.map(Bytes::toHexString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final Wei blockValue = new BlockValueCalculator().calculateBlockValue(blockWithReceipts);
|
||||
|
||||
final BlobsBundleV1 blobsBundleV1 =
|
||||
new BlobsBundleV1(blockWithReceipts.getBlock().getBody().getTransactions());
|
||||
return new EngineGetPayloadResultV3(
|
||||
blockWithReceipts.getHeader(),
|
||||
txs,
|
||||
blockWithReceipts.getBlock().getBody().getWithdrawals(),
|
||||
Quantity.create(blockValue),
|
||||
blobsBundleV1);
|
||||
}
|
||||
|
||||
public BlockResult transactionHash(final BlockWithMetadata<Hash, Hash> blockWithMetadata) {
|
||||
return transactionHash(blockWithMetadata, false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Withdrawal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
@JsonPropertyOrder({"executionPayload", "blockValue", "blobsBundle"})
|
||||
public class EngineGetPayloadResultV3 {
|
||||
protected final PayloadResult executionPayload;
|
||||
private final String blockValue;
|
||||
private final BlobsBundleV1 blobsBundle;
|
||||
|
||||
public EngineGetPayloadResultV3(
|
||||
final BlockHeader header,
|
||||
final List<String> transactions,
|
||||
final Optional<List<Withdrawal>> withdrawals,
|
||||
final String blockValue,
|
||||
final BlobsBundleV1 blobsBundle) {
|
||||
this.executionPayload = new PayloadResult(header, transactions, withdrawals);
|
||||
this.blockValue = blockValue;
|
||||
this.blobsBundle = blobsBundle;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "executionPayload")
|
||||
public PayloadResult getExecutionPayload() {
|
||||
return executionPayload;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blockValue")
|
||||
public String getBlockValue() {
|
||||
return blockValue;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blobsBundle")
|
||||
public BlobsBundleV1 getBlobsBundle() {
|
||||
return blobsBundle;
|
||||
}
|
||||
|
||||
public static class PayloadResult {
|
||||
|
||||
protected final String blockHash;
|
||||
private final String parentHash;
|
||||
private final String feeRecipient;
|
||||
private final String stateRoot;
|
||||
private final String receiptsRoot;
|
||||
private final String logsBloom;
|
||||
private final String prevRandao;
|
||||
private final String blockNumber;
|
||||
private final String gasLimit;
|
||||
private final String gasUsed;
|
||||
private final String timestamp;
|
||||
private final String extraData;
|
||||
private final String baseFeePerGas;
|
||||
|
||||
private final String excessDataGas;
|
||||
|
||||
private final String dataGasUsed;
|
||||
|
||||
protected final List<String> transactions;
|
||||
private final List<WithdrawalParameter> withdrawals;
|
||||
|
||||
public PayloadResult(
|
||||
final BlockHeader header,
|
||||
final List<String> transactions,
|
||||
final Optional<List<Withdrawal>> withdrawals) {
|
||||
this.blockNumber = Quantity.create(header.getNumber());
|
||||
this.blockHash = header.getHash().toString();
|
||||
this.parentHash = header.getParentHash().toString();
|
||||
this.logsBloom = header.getLogsBloom().toString();
|
||||
this.stateRoot = header.getStateRoot().toString();
|
||||
this.receiptsRoot = header.getReceiptsRoot().toString();
|
||||
this.extraData = header.getExtraData().toString();
|
||||
this.baseFeePerGas = header.getBaseFee().map(Quantity::create).orElse(null);
|
||||
this.gasLimit = Quantity.create(header.getGasLimit());
|
||||
this.gasUsed = Quantity.create(header.getGasUsed());
|
||||
this.timestamp = Quantity.create(header.getTimestamp());
|
||||
this.transactions = transactions;
|
||||
this.feeRecipient = header.getCoinbase().toString();
|
||||
this.prevRandao = header.getPrevRandao().map(Bytes32::toHexString).orElse(null);
|
||||
this.withdrawals =
|
||||
withdrawals
|
||||
.map(
|
||||
ws ->
|
||||
ws.stream()
|
||||
.map(WithdrawalParameter::fromWithdrawal)
|
||||
.collect(Collectors.toList()))
|
||||
.orElse(null);
|
||||
this.dataGasUsed = header.getDataGasUsed().map(Quantity::create).orElse(Quantity.HEX_ZERO);
|
||||
this.excessDataGas =
|
||||
header.getExcessDataGas().map(Quantity::create).orElse(Quantity.HEX_ZERO);
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blockNumber")
|
||||
public String getNumber() {
|
||||
return blockNumber;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blockHash")
|
||||
public String getHash() {
|
||||
return blockHash;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "parentHash")
|
||||
public String getParentHash() {
|
||||
return parentHash;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "logsBloom")
|
||||
public String getLogsBloom() {
|
||||
return logsBloom;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "prevRandao")
|
||||
public String getPrevRandao() {
|
||||
return prevRandao;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "stateRoot")
|
||||
public String getStateRoot() {
|
||||
return stateRoot;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "receiptsRoot")
|
||||
public String getReceiptRoot() {
|
||||
return receiptsRoot;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "extraData")
|
||||
public String getExtraData() {
|
||||
return extraData;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "baseFeePerGas")
|
||||
public String getBaseFeePerGas() {
|
||||
return baseFeePerGas;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "gasLimit")
|
||||
public String getGasLimit() {
|
||||
return gasLimit;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "gasUsed")
|
||||
public String getGasUsed() {
|
||||
return gasUsed;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "timestamp")
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "transactions")
|
||||
public List<String> getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "withdrawals")
|
||||
public List<WithdrawalParameter> getWithdrawals() {
|
||||
return withdrawals;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "feeRecipient")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public String getFeeRecipient() {
|
||||
return feeRecipient;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "excessDataGas")
|
||||
public String getExcessDataGas() {
|
||||
return excessDataGas;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "dataGasUsed")
|
||||
public String getDataGasUseds() {
|
||||
return dataGasUsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
import org.apache.tuweni.units.bigints.UInt256Value;
|
||||
import org.apache.tuweni.units.bigints.UInt64Value;
|
||||
|
||||
/**
|
||||
* Utility for formatting "quantity" fields and results to be returned. Quantity fields are
|
||||
@@ -30,7 +31,7 @@ import org.apache.tuweni.units.bigints.UInt256Value;
|
||||
public class Quantity {
|
||||
|
||||
private static final String HEX_PREFIX = "0x";
|
||||
private static final String HEX_ZERO = "0x0";
|
||||
public static final String HEX_ZERO = "0x0";
|
||||
|
||||
private Quantity() {}
|
||||
|
||||
@@ -38,6 +39,10 @@ public class Quantity {
|
||||
return uint256ToHex(value);
|
||||
}
|
||||
|
||||
public static String create(final UInt64Value<?> value) {
|
||||
return (value == null || value.isZero()) ? HEX_ZERO : value.toMinimalBytes().toShortHexString();
|
||||
}
|
||||
|
||||
public static String create(final int value) {
|
||||
return uint256ToHex(UInt256.valueOf(value));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -37,6 +38,7 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
"gasPrice",
|
||||
"maxPriorityFeePerGas",
|
||||
"maxFeePerGas",
|
||||
"maxFeePerDataGas",
|
||||
"hash",
|
||||
"input",
|
||||
"nonce",
|
||||
@@ -46,7 +48,8 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
"value",
|
||||
"v",
|
||||
"r",
|
||||
"s"
|
||||
"s",
|
||||
"blobVersionedHashes"
|
||||
})
|
||||
public class TransactionCompleteResult implements TransactionResult {
|
||||
|
||||
@@ -69,6 +72,9 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private final String maxFeePerGas;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private final String maxFeePerDataGas;
|
||||
|
||||
private final String hash;
|
||||
private final String input;
|
||||
private final String nonce;
|
||||
@@ -80,6 +86,9 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
private final String r;
|
||||
private final String s;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private final List<VersionedHash> versionedHashes;
|
||||
|
||||
public TransactionCompleteResult(final TransactionWithMetadata tx) {
|
||||
final Transaction transaction = tx.getTransaction();
|
||||
final TransactionType transactionType = transaction.getType();
|
||||
@@ -93,6 +102,8 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
tx.getTransaction().getMaxPriorityFeePerGas().map(Wei::toShortHexString).orElse(null);
|
||||
this.maxFeePerGas =
|
||||
tx.getTransaction().getMaxFeePerGas().map(Wei::toShortHexString).orElse(null);
|
||||
this.maxFeePerDataGas =
|
||||
transaction.getMaxFeePerDataGas().map(Wei::toShortHexString).orElse(null);
|
||||
this.gasPrice =
|
||||
Quantity.create(
|
||||
transaction
|
||||
@@ -111,6 +122,7 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
this.v = Quantity.create(transaction.getV());
|
||||
this.r = Quantity.create(transaction.getR());
|
||||
this.s = Quantity.create(transaction.getS());
|
||||
this.versionedHashes = transaction.getVersionedHashes().orElse(null);
|
||||
}
|
||||
|
||||
@JsonGetter(value = "accessList")
|
||||
@@ -153,6 +165,11 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
return maxFeePerGas;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "maxFeePerDataGas")
|
||||
public String getMaxFeePerDataGas() {
|
||||
return maxFeePerDataGas;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "gasPrice")
|
||||
public String getGasPrice() {
|
||||
return gasPrice;
|
||||
@@ -207,4 +224,9 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
public String getS() {
|
||||
return s;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blobVersionedHashes")
|
||||
public List<VersionedHash> getVersionedHashes() {
|
||||
return versionedHashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
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.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
|
||||
import org.hyperledger.besu.evm.AccessListEntry;
|
||||
|
||||
import java.util.List;
|
||||
@@ -81,7 +81,7 @@ public class TransactionPendingResult implements TransactionResult {
|
||||
private final String s;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private final List<Hash> versionedHashes;
|
||||
private final List<VersionedHash> versionedHashes;
|
||||
|
||||
public TransactionPendingResult(final Transaction transaction) {
|
||||
final TransactionType transactionType = transaction.getType();
|
||||
@@ -99,9 +99,7 @@ public class TransactionPendingResult implements TransactionResult {
|
||||
this.input = transaction.getPayload().toString();
|
||||
this.nonce = Quantity.create(transaction.getNonce());
|
||||
this.publicKey = transaction.getPublicKey().orElse(null);
|
||||
final BytesValueRLPOutput out = new BytesValueRLPOutput();
|
||||
transaction.writeTo(out);
|
||||
this.raw = out.encoded().toString();
|
||||
this.raw = TransactionEncoder.encodeOpaqueBytes(transaction).toString();
|
||||
this.to = transaction.getTo().map(Address::toHexString).orElse(null);
|
||||
this.type =
|
||||
transactionType.equals(TransactionType.FRONTIER)
|
||||
@@ -225,7 +223,7 @@ public class TransactionPendingResult implements TransactionResult {
|
||||
}
|
||||
|
||||
@JsonGetter(value = "blobVersionedHashes")
|
||||
public List<Hash> getVersionedHashes() {
|
||||
public List<VersionedHash> getVersionedHashes() {
|
||||
return versionedHashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,9 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
"transactionHash",
|
||||
"transactionIndex",
|
||||
"revertReason",
|
||||
"type"
|
||||
"type",
|
||||
"dataGasUsed",
|
||||
"dataGasPrice"
|
||||
})
|
||||
public abstract class TransactionReceiptResult {
|
||||
|
||||
@@ -67,6 +69,9 @@ public abstract class TransactionReceiptResult {
|
||||
protected final TransactionReceipt receipt;
|
||||
protected final String type;
|
||||
|
||||
private final String dataGasUsed;
|
||||
private final String dataGasPrice;
|
||||
|
||||
protected TransactionReceiptResult(final TransactionReceiptWithMetadata receiptWithMetadata) {
|
||||
final Transaction txn = receiptWithMetadata.getTransaction();
|
||||
this.receipt = receiptWithMetadata.getReceipt();
|
||||
@@ -76,6 +81,8 @@ public abstract class TransactionReceiptResult {
|
||||
this.cumulativeGasUsed = Quantity.create(receipt.getCumulativeGasUsed());
|
||||
this.from = txn.getSender().toString();
|
||||
this.gasUsed = Quantity.create(receiptWithMetadata.getGasUsed());
|
||||
this.dataGasUsed = receiptWithMetadata.getDataGasUsed().map(Quantity::create).orElse(null);
|
||||
this.dataGasPrice = receiptWithMetadata.getDataGasPrice().map(Quantity::create).orElse(null);
|
||||
this.effectiveGasPrice =
|
||||
Quantity.create(txn.getEffectiveGasPrice(receiptWithMetadata.getBaseFee()));
|
||||
|
||||
@@ -127,6 +134,18 @@ public abstract class TransactionReceiptResult {
|
||||
return gasUsed;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "dataGasUsed")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public String getDataGasUsed() {
|
||||
return dataGasUsed;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "dataGasPrice")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public String getDataGasPrice() {
|
||||
return dataGasPrice;
|
||||
}
|
||||
|
||||
@JsonGetter(value = "effectiveGasPrice")
|
||||
public String getEffectiveGasPrice() {
|
||||
return effectiveGasPrice;
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -37,4 +38,8 @@ public abstract class ApiGroupJsonRpcMethods implements JsonRpcMethods {
|
||||
return Arrays.stream(methods)
|
||||
.collect(Collectors.toMap(JsonRpcMethod::getName, method -> method));
|
||||
}
|
||||
|
||||
protected Map<String, JsonRpcMethod> mapOf(final List<JsonRpcMethod> methods) {
|
||||
return methods.stream().collect(Collectors.toMap(JsonRpcMethod::getName, method -> method));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
new EthGetTransactionByBlockHashAndIndex(blockchainQueries),
|
||||
new EthGetTransactionByBlockNumberAndIndex(blockchainQueries),
|
||||
new EthGetTransactionCount(blockchainQueries, transactionPool),
|
||||
new EthGetTransactionReceipt(blockchainQueries),
|
||||
new EthGetTransactionReceipt(blockchainQueries, protocolSchedule),
|
||||
new EthUninstallFilter(filterManager),
|
||||
new EthGetFilterChanges(filterManager),
|
||||
new EthGetFilterLogs(filterManager),
|
||||
|
||||
@@ -26,8 +26,10 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineG
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByRangeV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV2;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV3;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayloadV1;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayloadV2;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayloadV3;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EnginePreparePayloadDebug;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineQosTimer;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
|
||||
@@ -35,6 +37,9 @@ import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -74,56 +79,78 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
|
||||
@Override
|
||||
protected Map<String, JsonRpcMethod> create() {
|
||||
final EngineQosTimer engineQosTimer = new EngineQosTimer(consensusEngineServer);
|
||||
|
||||
if (mergeCoordinator.isPresent()) {
|
||||
return mapOf(
|
||||
new EngineGetPayloadV1(
|
||||
consensusEngineServer,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
blockResultFactory,
|
||||
engineQosTimer),
|
||||
new EngineGetPayloadV2(
|
||||
consensusEngineServer,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
blockResultFactory,
|
||||
engineQosTimer),
|
||||
new EngineNewPayloadV1(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
ethPeers,
|
||||
engineQosTimer),
|
||||
new EngineNewPayloadV2(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
ethPeers,
|
||||
engineQosTimer),
|
||||
new EngineForkchoiceUpdatedV1(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
engineQosTimer),
|
||||
new EngineForkchoiceUpdatedV2(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
engineQosTimer),
|
||||
new EngineExchangeTransitionConfiguration(
|
||||
consensusEngineServer, protocolContext, engineQosTimer),
|
||||
new EngineGetPayloadBodiesByHashV1(
|
||||
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
|
||||
new EngineGetPayloadBodiesByRangeV1(
|
||||
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
|
||||
new EngineExchangeCapabilities(consensusEngineServer, protocolContext, engineQosTimer),
|
||||
new EnginePreparePayloadDebug(
|
||||
consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get()));
|
||||
List<JsonRpcMethod> executionEngineApisSupported = new ArrayList<>();
|
||||
executionEngineApisSupported.addAll(
|
||||
Arrays.asList(
|
||||
new EngineGetPayloadV1(
|
||||
consensusEngineServer,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
blockResultFactory,
|
||||
engineQosTimer),
|
||||
new EngineGetPayloadV2(
|
||||
consensusEngineServer,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
blockResultFactory,
|
||||
engineQosTimer),
|
||||
new EngineNewPayloadV1(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
ethPeers,
|
||||
engineQosTimer),
|
||||
new EngineNewPayloadV2(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
ethPeers,
|
||||
engineQosTimer),
|
||||
new EngineNewPayloadV3(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
ethPeers,
|
||||
engineQosTimer),
|
||||
new EngineForkchoiceUpdatedV1(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
engineQosTimer),
|
||||
new EngineForkchoiceUpdatedV2(
|
||||
consensusEngineServer,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
engineQosTimer),
|
||||
new EngineExchangeTransitionConfiguration(
|
||||
consensusEngineServer, protocolContext, engineQosTimer),
|
||||
new EngineGetPayloadBodiesByHashV1(
|
||||
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
|
||||
new EngineGetPayloadBodiesByRangeV1(
|
||||
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
|
||||
new EngineExchangeCapabilities(
|
||||
consensusEngineServer, protocolContext, engineQosTimer),
|
||||
new EnginePreparePayloadDebug(
|
||||
consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get())));
|
||||
|
||||
if (protocolSchedule.anyMatch(p -> p.spec().getName().equalsIgnoreCase("cancun"))) {
|
||||
executionEngineApisSupported.add(
|
||||
new EngineGetPayloadV3(
|
||||
consensusEngineServer,
|
||||
protocolContext,
|
||||
mergeCoordinator.get(),
|
||||
blockResultFactory,
|
||||
engineQosTimer,
|
||||
protocolSchedule));
|
||||
}
|
||||
|
||||
return mapOf(executionEngineApisSupported);
|
||||
} else {
|
||||
return mapOf(
|
||||
new EngineExchangeTransitionConfiguration(
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.api.query;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.hyperledger.besu.ethereum.api.query.cache.TransactionLogBloomCacher.BLOCKS_PER_BLOOM_CACHE;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
@@ -33,6 +34,8 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter;
|
||||
@@ -612,7 +615,7 @@ public class BlockchainQueries {
|
||||
* @return The transaction receipt associated with the referenced transaction.
|
||||
*/
|
||||
public Optional<TransactionReceiptWithMetadata> transactionReceiptByTransactionHash(
|
||||
final Hash transactionHash) {
|
||||
final Hash transactionHash, final ProtocolSchedule protocolSchedule) {
|
||||
final Optional<TransactionLocation> maybeLocation =
|
||||
blockchain.getTransactionLocation(transactionHash);
|
||||
if (maybeLocation.isEmpty()) {
|
||||
@@ -639,6 +642,12 @@ public class BlockchainQueries {
|
||||
- transactionReceipts.get(location.getTransactionIndex() - 1).getCumulativeGasUsed();
|
||||
}
|
||||
|
||||
Optional<Long> maybeDataGasUsed =
|
||||
getDataGasUsed(transaction, protocolSchedule.getByBlockHeader(header));
|
||||
|
||||
Optional<Wei> maybeDataGasPrice =
|
||||
getDataGasPrice(transaction, header, protocolSchedule.getByBlockHeader(header));
|
||||
|
||||
return Optional.of(
|
||||
TransactionReceiptWithMetadata.create(
|
||||
transactionReceipt,
|
||||
@@ -648,7 +657,48 @@ public class BlockchainQueries {
|
||||
gasUsed,
|
||||
header.getBaseFee(),
|
||||
blockhash,
|
||||
header.getNumber()));
|
||||
header.getNumber(),
|
||||
maybeDataGasUsed,
|
||||
maybeDataGasPrice));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the data gas used for data in a transaction.
|
||||
*
|
||||
* @param transaction the transaction to calculate the gas for
|
||||
* @param protocolSpec the protocol specification to use for gas calculation
|
||||
* @return an Optional containing the data gas used for data if the transaction type supports
|
||||
* blobs, otherwise returns an empty Optional
|
||||
*/
|
||||
private Optional<Long> getDataGasUsed(
|
||||
final Transaction transaction, final ProtocolSpec protocolSpec) {
|
||||
return transaction.getType().supportsBlob()
|
||||
? Optional.of(protocolSpec.getGasCalculator().dataGasCost(transaction.getBlobCount()))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the data gas price for data in a transaction.
|
||||
*
|
||||
* @param transaction the transaction to calculate the gas price for
|
||||
* @param header the block header of the current block
|
||||
* @param protocolSpec the protocol specification to use for gas price calculation
|
||||
* @return an Optional containing the data gas price for data if the transaction type supports
|
||||
* blobs, otherwise returns an empty Optional
|
||||
*/
|
||||
private Optional<Wei> getDataGasPrice(
|
||||
final Transaction transaction, final BlockHeader header, final ProtocolSpec protocolSpec) {
|
||||
if (transaction.getType().supportsBlob()) {
|
||||
return blockchain
|
||||
.getBlockHeader(header.getParentHash())
|
||||
.map(
|
||||
parentHeader ->
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPricePerGas(
|
||||
calculateExcessDataGasForParent(protocolSpec, parentHeader)));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,8 @@ public class TransactionReceiptWithMetadata {
|
||||
private final long blockNumber;
|
||||
private final Hash blockHash;
|
||||
private final Transaction transaction;
|
||||
private final Optional<Long> dataGasUsed;
|
||||
private final Optional<Wei> dataGasPrice;
|
||||
|
||||
private TransactionReceiptWithMetadata(
|
||||
final TransactionReceipt receipt,
|
||||
@@ -39,7 +41,9 @@ public class TransactionReceiptWithMetadata {
|
||||
final long gasUsed,
|
||||
final Optional<Wei> baseFee,
|
||||
final Hash blockHash,
|
||||
final long blockNumber) {
|
||||
final long blockNumber,
|
||||
final Optional<Long> dataGasUsed,
|
||||
final Optional<Wei> dataGasPrice) {
|
||||
this.receipt = receipt;
|
||||
this.transactionHash = transactionHash;
|
||||
this.transactionIndex = transactionIndex;
|
||||
@@ -48,6 +52,8 @@ public class TransactionReceiptWithMetadata {
|
||||
this.blockHash = blockHash;
|
||||
this.blockNumber = blockNumber;
|
||||
this.transaction = transaction;
|
||||
this.dataGasUsed = dataGasUsed;
|
||||
this.dataGasPrice = dataGasPrice;
|
||||
}
|
||||
|
||||
public static TransactionReceiptWithMetadata create(
|
||||
@@ -58,7 +64,9 @@ public class TransactionReceiptWithMetadata {
|
||||
final long gasUsed,
|
||||
final Optional<Wei> baseFee,
|
||||
final Hash blockHash,
|
||||
final long blockNumber) {
|
||||
final long blockNumber,
|
||||
final Optional<Long> dataGasUsed,
|
||||
final Optional<Wei> dataGasPrice) {
|
||||
return new TransactionReceiptWithMetadata(
|
||||
receipt,
|
||||
transaction,
|
||||
@@ -67,7 +75,9 @@ public class TransactionReceiptWithMetadata {
|
||||
gasUsed,
|
||||
baseFee,
|
||||
blockHash,
|
||||
blockNumber);
|
||||
blockNumber,
|
||||
dataGasUsed,
|
||||
dataGasPrice);
|
||||
}
|
||||
|
||||
public TransactionReceipt getReceipt() {
|
||||
@@ -103,4 +113,12 @@ public class TransactionReceiptWithMetadata {
|
||||
public Optional<Wei> getBaseFee() {
|
||||
return baseFee;
|
||||
}
|
||||
|
||||
public Optional<Long> getDataGasUsed() {
|
||||
return dataGasUsed;
|
||||
}
|
||||
|
||||
public Optional<Wei> getDataGasPrice() {
|
||||
return dataGasPrice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ public class EthGasPriceTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
new BlockBody(
|
||||
List.of(
|
||||
@@ -204,6 +205,7 @@ public class EthGasPriceTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
new BlockBody(List.of(), List.of())));
|
||||
}
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -32,6 +35,10 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TransactionRec
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TransactionReceiptStatusResult;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
@@ -40,13 +47,18 @@ 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.feemarket.CancunFeeMarket;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256s;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -82,10 +94,28 @@ public class EthGetTransactionReceiptTest {
|
||||
|
||||
private final TransactionReceiptWithMetadata statusReceiptWithMetadata =
|
||||
TransactionReceiptWithMetadata.create(
|
||||
statusReceipt, transaction, hash, 1, 2, Optional.empty(), blockHash, 4);
|
||||
statusReceipt,
|
||||
transaction,
|
||||
hash,
|
||||
1,
|
||||
2,
|
||||
Optional.empty(),
|
||||
blockHash,
|
||||
4,
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
private final TransactionReceiptWithMetadata rootReceiptWithMetaData =
|
||||
TransactionReceiptWithMetadata.create(
|
||||
rootReceipt, transaction, hash, 1, 2, Optional.empty(), blockHash, 4);
|
||||
rootReceipt,
|
||||
transaction,
|
||||
hash,
|
||||
1,
|
||||
2,
|
||||
Optional.empty(),
|
||||
blockHash,
|
||||
4,
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
private final ProtocolSpec rootTransactionTypeSpec =
|
||||
new ProtocolSpec(
|
||||
@@ -151,9 +181,12 @@ public class EthGetTransactionReceiptTest {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class);
|
||||
|
||||
private final BlockchainQueries blockchain = mock(BlockchainQueries.class);
|
||||
private final Blockchain blockchain = mock(Blockchain.class);
|
||||
|
||||
private final BlockchainQueries blockchainQueries =
|
||||
spy(new BlockchainQueries(blockchain, mock(WorldStateArchive.class)));
|
||||
private final EthGetTransactionReceipt ethGetTransactionReceipt =
|
||||
new EthGetTransactionReceipt(blockchain);
|
||||
new EthGetTransactionReceipt(blockchainQueries, protocolSchedule);
|
||||
private final String receiptString =
|
||||
"0xcbef69eaf44af151aa66677ae4b8d8c343a09f667c873a3a6f4558fa4051fa5f";
|
||||
private final Hash receiptHash =
|
||||
@@ -164,8 +197,8 @@ public class EthGetTransactionReceiptTest {
|
||||
|
||||
@Test
|
||||
public void shouldCreateAStatusTransactionReceiptWhenStatusTypeProtocol() {
|
||||
when(blockchain.headBlockNumber()).thenReturn(1L);
|
||||
when(blockchain.transactionReceiptByTransactionHash(receiptHash))
|
||||
when(blockchainQueries.headBlockNumber()).thenReturn(1L);
|
||||
when(blockchainQueries.transactionReceiptByTransactionHash(receiptHash, protocolSchedule))
|
||||
.thenReturn(Optional.of(statusReceiptWithMetadata));
|
||||
when(protocolSchedule.getByBlockHeader(blockHeader(1))).thenReturn(statusTransactionTypeSpec);
|
||||
|
||||
@@ -180,8 +213,8 @@ public class EthGetTransactionReceiptTest {
|
||||
|
||||
@Test
|
||||
public void shouldCreateARootTransactionReceiptWhenRootTypeProtocol() {
|
||||
when(blockchain.headBlockNumber()).thenReturn(1L);
|
||||
when(blockchain.transactionReceiptByTransactionHash(receiptHash))
|
||||
when(blockchainQueries.headBlockNumber()).thenReturn(1L);
|
||||
when(blockchainQueries.transactionReceiptByTransactionHash(receiptHash, protocolSchedule))
|
||||
.thenReturn(Optional.of(rootReceiptWithMetaData));
|
||||
when(protocolSchedule.getByBlockHeader(blockHeader(1))).thenReturn(rootTransactionTypeSpec);
|
||||
|
||||
@@ -195,14 +228,23 @@ public class EthGetTransactionReceiptTest {
|
||||
|
||||
@Test
|
||||
public void shouldWorkFor1559Txs() {
|
||||
when(blockchain.headBlockNumber()).thenReturn(1L);
|
||||
when(blockchainQueries.headBlockNumber()).thenReturn(1L);
|
||||
final Transaction transaction1559 =
|
||||
new BlockDataGenerator().transaction(TransactionType.EIP1559);
|
||||
final Wei baseFee = Wei.ONE;
|
||||
final TransactionReceiptWithMetadata transactionReceiptWithMetadata =
|
||||
TransactionReceiptWithMetadata.create(
|
||||
statusReceipt, transaction1559, hash, 1, 2, Optional.of(baseFee), blockHash, 4);
|
||||
when(blockchain.transactionReceiptByTransactionHash(receiptHash))
|
||||
statusReceipt,
|
||||
transaction1559,
|
||||
hash,
|
||||
1,
|
||||
2,
|
||||
Optional.of(baseFee),
|
||||
blockHash,
|
||||
4,
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
when(blockchainQueries.transactionReceiptByTransactionHash(receiptHash, protocolSchedule))
|
||||
.thenReturn(Optional.of(transactionReceiptWithMetadata));
|
||||
when(protocolSchedule.getByBlockHeader(blockHeader(1))).thenReturn(rootTransactionTypeSpec);
|
||||
|
||||
@@ -220,6 +262,63 @@ public class EthGetTransactionReceiptTest {
|
||||
transaction1559.getMaxFeePerGas().get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case to verify that the TransactionReceiptStatusResult contains data gas used and data gas
|
||||
* price when the transaction type is TransactionType#BLOB
|
||||
*/
|
||||
@Test
|
||||
public void shouldContainDataGasUsedAndDataGasPriceWhenBlobTransaction() {
|
||||
|
||||
var hash = Hash.wrap(Bytes32.random());
|
||||
mockBlockWithBlobTransaction(hash, 1L);
|
||||
when(blockchain.getTxReceipts(hash)).thenReturn(Optional.of(List.of(statusReceipt)));
|
||||
// Call the real method to get the transaction receipt by transaction hash
|
||||
when(blockchainQueries.transactionReceiptByTransactionHash(receiptHash, protocolSchedule))
|
||||
.thenCallRealMethod();
|
||||
|
||||
final JsonRpcSuccessResponse response =
|
||||
(JsonRpcSuccessResponse) ethGetTransactionReceipt.response(request);
|
||||
final TransactionReceiptStatusResult result =
|
||||
(TransactionReceiptStatusResult) response.getResult();
|
||||
|
||||
assertThat(result.getType()).isEqualTo("0x3");
|
||||
assertThat(result.getDataGasUsed()).isEqualTo("0x20000");
|
||||
assertThat(result.getDataGasPrice()).isEqualTo("0x1");
|
||||
}
|
||||
|
||||
private void mockBlockWithBlobTransaction(final Hash blockHash, final long blockNumber) {
|
||||
Hash parentHash = Hash.wrap(Bytes32.random());
|
||||
TransactionLocation transactionLocation = mock(TransactionLocation.class);
|
||||
Block block = mock(Block.class);
|
||||
BlockBody body = mock(BlockBody.class);
|
||||
BlockHeader header = mock(BlockHeader.class);
|
||||
BlockHeader parentHeader = mock(BlockHeader.class);
|
||||
when(transactionLocation.getBlockHash()).thenReturn(blockHash);
|
||||
when(transactionLocation.getTransactionIndex()).thenReturn(0);
|
||||
when(header.getNumber()).thenReturn(blockNumber);
|
||||
when(header.getBlockHash()).thenReturn(blockHash);
|
||||
when(header.getParentHash()).thenReturn(parentHash);
|
||||
when(blockchain.getBlockHeader(parentHash)).thenReturn(Optional.of(parentHeader));
|
||||
when(block.getHeader()).thenReturn(header);
|
||||
when(block.getBody()).thenReturn(body);
|
||||
when(body.getTransactions())
|
||||
.thenReturn(List.of(new BlockDataGenerator().transaction(TransactionType.BLOB)));
|
||||
when(parentHeader.getExcessDataGas()).thenReturn(Optional.of(DataGas.of(1000)));
|
||||
when(blockchain.getBlockByHash(blockHash)).thenReturn(Optional.of(block));
|
||||
mockProtocolSpec(header);
|
||||
when(blockchain.getTransactionLocation(receiptHash))
|
||||
.thenReturn(Optional.of(transactionLocation));
|
||||
}
|
||||
|
||||
private void mockProtocolSpec(final BlockHeader blockHeader) {
|
||||
FeeMarket feeMarket = mock(CancunFeeMarket.class);
|
||||
when(feeMarket.dataPricePerGas(any())).thenCallRealMethod();
|
||||
ProtocolSpec spec = mock(ProtocolSpec.class);
|
||||
when(spec.getFeeMarket()).thenReturn(feeMarket);
|
||||
when(spec.getGasCalculator()).thenReturn(new CancunGasCalculator());
|
||||
when(protocolSchedule.getByBlockHeader(blockHeader)).thenReturn(spec);
|
||||
}
|
||||
|
||||
private BlockHeader blockHeader(final long number) {
|
||||
return new BlockHeaderTestFixture().number(number).buildHeader();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -22,6 +23,9 @@ import static org.mockito.Mockito.when;
|
||||
import org.hyperledger.besu.consensus.merge.MergeContext;
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier;
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
@@ -35,10 +39,14 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -51,6 +59,10 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class AbstractEngineGetPayloadTest {
|
||||
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
protected static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();
|
||||
|
||||
@FunctionalInterface
|
||||
interface MethodFactory {
|
||||
AbstractEngineGetPayload create(
|
||||
@@ -68,7 +80,11 @@ public abstract class AbstractEngineGetPayloadTest {
|
||||
this.methodFactory = methodFactory;
|
||||
}
|
||||
|
||||
private static final Vertx vertx = Vertx.vertx();
|
||||
public AbstractEngineGetPayloadTest() {
|
||||
this.methodFactory = null;
|
||||
}
|
||||
|
||||
protected static final Vertx vertx = Vertx.vertx();
|
||||
protected static final BlockResultFactory factory = new BlockResultFactory();
|
||||
protected static final PayloadIdentifier mockPid =
|
||||
PayloadIdentifier.forPayloadParams(
|
||||
@@ -77,7 +93,7 @@ public abstract class AbstractEngineGetPayloadTest {
|
||||
new BlockHeaderTestFixture().prevRandao(Bytes32.random()).buildHeader();
|
||||
private static final Block mockBlock =
|
||||
new Block(mockHeader, new BlockBody(Collections.emptyList(), Collections.emptyList()));
|
||||
private static final BlockWithReceipts mockBlockWithReceipts =
|
||||
protected static final BlockWithReceipts mockBlockWithReceipts =
|
||||
new BlockWithReceipts(mockBlock, Collections.emptyList());
|
||||
private static final Block mockBlockWithWithdrawals =
|
||||
new Block(
|
||||
@@ -101,17 +117,23 @@ public abstract class AbstractEngineGetPayloadTest {
|
||||
protected static final BlockWithReceipts mockBlockWithReceiptsAndDeposits =
|
||||
new BlockWithReceipts(mockBlockWithDeposits, Collections.emptyList());
|
||||
|
||||
@Mock private ProtocolContext protocolContext;
|
||||
@Mock protected ProtocolContext protocolContext;
|
||||
|
||||
@Mock protected MergeContext mergeContext;
|
||||
@Mock private MergeMiningCoordinator mergeMiningCoordinator;
|
||||
@Mock protected MergeMiningCoordinator mergeMiningCoordinator;
|
||||
|
||||
@Mock protected EngineCallListener engineCallListener;
|
||||
|
||||
@Mock protected ProtocolSchedule protocolSchedule;
|
||||
|
||||
protected static final long SHANGHAI_AT = 1337L;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
when(mergeContext.retrieveBlockById(mockPid)).thenReturn(Optional.of(mockBlockWithReceipts));
|
||||
when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext));
|
||||
when(protocolSchedule.hardforkFor(any()))
|
||||
.thenReturn(Optional.of(new ScheduledProtocolSpec.Hardfork("shanghai", SHANGHAI_AT)));
|
||||
this.method =
|
||||
methodFactory.create(
|
||||
vertx, protocolContext, mergeMiningCoordinator, factory, engineCallListener);
|
||||
|
||||
@@ -19,10 +19,8 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.Executi
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameterTestFixture.DEPOSIT_PARAM_1;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameterTestFixture.WITHDRAWAL_PARAM_1;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -32,13 +30,13 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.MergeContext;
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
|
||||
@@ -75,9 +73,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -87,6 +83,12 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class AbstractEngineNewPayloadTest {
|
||||
|
||||
protected final long LONDON_TIMESTAMP = 0;
|
||||
protected final long PARIS_TIMESTAMP = 10;
|
||||
protected final long SHANGHAI_TIMESTAMP = 20;
|
||||
protected final long CANCUN_TIMESTAMP = 30;
|
||||
protected final long EXPERIMENTAL_TIMESTAMP = 40;
|
||||
|
||||
@FunctionalInterface
|
||||
interface MethodFactory {
|
||||
AbstractEngineNewPayload create(
|
||||
@@ -98,29 +100,25 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
final EngineCallListener engineCallListener);
|
||||
}
|
||||
|
||||
private final MethodFactory methodFactory;
|
||||
// private final MethodFactory methodFactory;
|
||||
protected AbstractEngineNewPayload method;
|
||||
|
||||
public AbstractEngineNewPayloadTest(final MethodFactory methodFactory) {
|
||||
this.methodFactory = methodFactory;
|
||||
}
|
||||
public AbstractEngineNewPayloadTest() {}
|
||||
|
||||
private static final Vertx vertx = Vertx.vertx();
|
||||
private static final Hash mockHash = Hash.hash(Bytes32.fromHexStringLenient("0x1337deadbeef"));
|
||||
private static final Address depositContractAddress =
|
||||
Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");
|
||||
protected static final Vertx vertx = Vertx.vertx();
|
||||
protected static final Hash mockHash = Hash.hash(Bytes32.fromHexStringLenient("0x1337deadbeef"));
|
||||
|
||||
@Mock private ProtocolSpec protocolSpec;
|
||||
@Mock private ProtocolSchedule protocolSchedule;
|
||||
@Mock private ProtocolContext protocolContext;
|
||||
@Mock protected ProtocolSpec protocolSpec;
|
||||
@Mock protected ProtocolSchedule protocolSchedule;
|
||||
@Mock protected ProtocolContext protocolContext;
|
||||
|
||||
@Mock private MergeContext mergeContext;
|
||||
@Mock protected MergeContext mergeContext;
|
||||
|
||||
@Mock protected MergeMiningCoordinator mergeCoordinator;
|
||||
|
||||
@Mock protected MutableBlockchain blockchain;
|
||||
|
||||
@Mock private EthPeers ethPeers;
|
||||
@Mock protected EthPeers ethPeers;
|
||||
|
||||
@Mock protected EngineCallListener engineCallListener;
|
||||
|
||||
@@ -128,20 +126,14 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
public void before() {
|
||||
when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext));
|
||||
when(protocolContext.getBlockchain()).thenReturn(blockchain);
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
lenient()
|
||||
.when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
lenient()
|
||||
.when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.ProhibitedDeposits());
|
||||
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
|
||||
when(ethPeers.peerCount()).thenReturn(1);
|
||||
this.method =
|
||||
methodFactory.create(
|
||||
vertx,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator,
|
||||
ethPeers,
|
||||
engineCallListener);
|
||||
lenient().when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
|
||||
lenient().when(ethPeers.peerCount()).thenReturn(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -184,7 +176,8 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
|
||||
.thenReturn(Optional.empty());
|
||||
if (validateTerminalPoWBlock()) {
|
||||
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
lenient()
|
||||
.when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
.thenReturn(true);
|
||||
}
|
||||
|
||||
@@ -269,7 +262,8 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
|
||||
.thenReturn(Optional.of(mockHash));
|
||||
if (validateTerminalPoWBlock()) {
|
||||
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
lenient()
|
||||
.when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
.thenReturn(true);
|
||||
}
|
||||
when(mergeCoordinator.rememberBlock(any())).thenThrow(new MerkleTrieException("missing leaf"));
|
||||
@@ -285,7 +279,9 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
@Test
|
||||
public void shouldReturnInvalidBlockHashOnBadHashParameter() {
|
||||
BlockHeader mockHeader = new BlockHeaderTestFixture().buildHeader();
|
||||
|
||||
lenient()
|
||||
.when(mergeCoordinator.getLatestValidAncestor(mockHeader.getBlockHash()))
|
||||
.thenReturn(Optional.empty());
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
|
||||
|
||||
EnginePayloadStatusResult res = fromSuccessResp(resp);
|
||||
@@ -372,14 +368,6 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void shouldRespondWithInvalidTerminalPowBlock() {
|
||||
// TODO: implement this as part of https://github.com/hyperledger/besu/issues/3141
|
||||
// mergeContext is a mock
|
||||
// assertThat(mergeContext.getTerminalTotalDifficulty()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRespondWithInvalidIfExtraDataIsNull() {
|
||||
BlockHeader realHeader = new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE).buildHeader();
|
||||
@@ -409,146 +397,6 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfWithdrawalsIsNotNull_WhenWithdrawalsProhibited() {
|
||||
final List<WithdrawalParameter> withdrawals = List.of();
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.of(Collections.emptyList()), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
withdrawals,
|
||||
null));
|
||||
|
||||
final JsonRpcError jsonRpcError = fromErrorResp(resp);
|
||||
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfWithdrawalsIsNull_WhenWithdrawalsProhibited() {
|
||||
final List<WithdrawalParameter> withdrawals = null;
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), withdrawals, null));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfWithdrawalsIsNull_WhenWithdrawalsAllowed() {
|
||||
final List<WithdrawalParameter> withdrawals = null;
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
withdrawals,
|
||||
null));
|
||||
|
||||
assertThat(fromErrorResp(resp).getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfWithdrawalsIsNotNull_WhenWithdrawalsAllowed() {
|
||||
final List<WithdrawalParameter> withdrawalsParam = List.of(WITHDRAWAL_PARAM_1);
|
||||
final List<Withdrawal> withdrawals = List.of(WITHDRAWAL_PARAM_1.toWithdrawal());
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.of(withdrawals),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), withdrawalsParam, null));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfDepositsIsNotNull_WhenDepositsProhibited() {
|
||||
final List<DepositParameter> deposits = List.of();
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.ProhibitedDeposits());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.of(Collections.emptyList())),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
deposits));
|
||||
|
||||
final JsonRpcError jsonRpcError = fromErrorResp(resp);
|
||||
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfDepositsIsNull_WhenDepositsProhibited() {
|
||||
final List<DepositParameter> deposits = null;
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.ProhibitedDeposits());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), null, deposits));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfDepositsIsNull_WhenDepositsAllowed() {
|
||||
final List<DepositParameter> deposits = null;
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.AllowedDeposits(depositContractAddress));
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
deposits));
|
||||
|
||||
assertThat(fromErrorResp(resp).getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfDepositsIsNotNull_WhenDepositsAllowed() {
|
||||
final List<DepositParameter> depositsParam = List.of(DEPOSIT_PARAM_1);
|
||||
final List<Deposit> deposits = List.of(DEPOSIT_PARAM_1.toDeposit());
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.AllowedDeposits(depositContractAddress));
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.of(deposits));
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), null, depositsParam));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfProtocolScheduleIsEmpty() {
|
||||
when(protocolSchedule.getByBlockHeader(any())).thenReturn(null);
|
||||
@@ -566,35 +414,32 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
protected JsonRpcResponse resp(final EnginePayloadParameter payload) {
|
||||
return method.response(
|
||||
new JsonRpcRequestContext(
|
||||
new JsonRpcRequest(
|
||||
"2.0", RpcMethod.ENGINE_NEW_PAYLOAD_V2.getMethodName(), new Object[] {payload})));
|
||||
new JsonRpcRequest("2.0", this.method.getName(), new Object[] {payload})));
|
||||
}
|
||||
|
||||
protected EnginePayloadParameter mockPayload(final BlockHeader header, final List<String> txs) {
|
||||
return new EnginePayloadParameter(
|
||||
header.getHash(),
|
||||
header.getParentHash(),
|
||||
header.getCoinbase(),
|
||||
header.getStateRoot(),
|
||||
new UnsignedLongParameter(header.getNumber()),
|
||||
header.getBaseFee().map(w -> w.toHexString()).orElse("0x0"),
|
||||
new UnsignedLongParameter(header.getGasLimit()),
|
||||
new UnsignedLongParameter(header.getGasUsed()),
|
||||
new UnsignedLongParameter(header.getTimestamp()),
|
||||
header.getExtraData() == null ? null : header.getExtraData().toHexString(),
|
||||
header.getReceiptsRoot(),
|
||||
header.getLogsBloom(),
|
||||
header.getPrevRandao().map(Bytes32::toHexString).orElse("0x0"),
|
||||
txs,
|
||||
null,
|
||||
null);
|
||||
return mockPayload(header, txs, null, null, null);
|
||||
}
|
||||
|
||||
private EnginePayloadParameter mockPayload(
|
||||
protected EnginePayloadParameter mockPayload(
|
||||
final BlockHeader header,
|
||||
final List<String> txs,
|
||||
final List<WithdrawalParameter> withdrawals,
|
||||
final List<DepositParameter> deposits) {
|
||||
return mockPayload(
|
||||
header,
|
||||
txs,
|
||||
withdrawals,
|
||||
deposits,
|
||||
List.of(VersionedHash.DEFAULT_VERSIONED_HASH.toBytes()));
|
||||
}
|
||||
|
||||
protected EnginePayloadParameter mockPayload(
|
||||
final BlockHeader header,
|
||||
final List<String> txs,
|
||||
final List<WithdrawalParameter> withdrawals,
|
||||
final List<DepositParameter> deposits,
|
||||
final List<Bytes32> versionedHashes) {
|
||||
return new EnginePayloadParameter(
|
||||
header.getHash(),
|
||||
header.getParentHash(),
|
||||
@@ -611,14 +456,17 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
header.getPrevRandao().map(Bytes32::toHexString).orElse("0x0"),
|
||||
txs,
|
||||
withdrawals,
|
||||
header.getDataGasUsed().map(UnsignedLongParameter::new).orElse(null),
|
||||
header.getExcessDataGas().map(DataGas::toHexString).orElse(null),
|
||||
versionedHashes,
|
||||
deposits);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BlockHeader setupValidPayload(
|
||||
protected BlockHeader setupValidPayload(
|
||||
final BlockProcessingResult value,
|
||||
final Optional<List<Withdrawal>> maybeWithdrawals,
|
||||
final Optional<List<Deposit>> maybeDeposits) {
|
||||
|
||||
BlockHeader mockHeader = createBlockHeader(maybeWithdrawals, maybeDeposits);
|
||||
when(blockchain.getBlockByHash(mockHeader.getHash())).thenReturn(Optional.empty());
|
||||
when(blockchain.getBlockHeader(mockHeader.getParentHash()))
|
||||
@@ -626,7 +474,8 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
when(mergeCoordinator.getLatestValidAncestor(any(BlockHeader.class)))
|
||||
.thenReturn(Optional.of(mockHash));
|
||||
if (validateTerminalPoWBlock()) {
|
||||
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
lenient()
|
||||
.when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
|
||||
.thenReturn(true);
|
||||
}
|
||||
when(mergeCoordinator.rememberBlock(any())).thenReturn(value);
|
||||
@@ -650,7 +499,7 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
.get();
|
||||
}
|
||||
|
||||
private JsonRpcError fromErrorResp(final JsonRpcResponse resp) {
|
||||
protected JsonRpcError fromErrorResp(final JsonRpcResponse resp) {
|
||||
assertThat(resp.getType()).isEqualTo(JsonRpcResponseType.ERROR);
|
||||
return Optional.of(resp)
|
||||
.map(JsonRpcErrorResponse.class::cast)
|
||||
@@ -675,7 +524,7 @@ public abstract class AbstractEngineNewPayloadTest {
|
||||
return mockHeader;
|
||||
}
|
||||
|
||||
private void assertValidResponse(final BlockHeader mockHeader, final JsonRpcResponse resp) {
|
||||
protected void assertValidResponse(final BlockHeader mockHeader, final JsonRpcResponse resp) {
|
||||
EnginePayloadStatusResult res = fromSuccessResp(resp);
|
||||
assertThat(res.getLatestValidHash().get()).isEqualTo(mockHeader.getHash());
|
||||
assertThat(res.getStatusAsString()).isEqualTo(VALID.name());
|
||||
|
||||
@@ -252,6 +252,7 @@ public class EngineExchangeTransitionConfigurationTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new BlockHeaderFunctions() {
|
||||
@Override
|
||||
public Hash hash(final BlockHeader header) {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadResultV3;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
|
||||
import org.hyperledger.besu.ethereum.core.BlobTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(
|
||||
MockitoExtension.class) // mocks in parent class may not be used, throwing unnecessary stubbing
|
||||
public class EngineGetPayloadV3Test extends AbstractEngineGetPayloadTest {
|
||||
|
||||
private static final long CANCUN_AT = 31337L;
|
||||
|
||||
public EngineGetPayloadV3Test() {
|
||||
super();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void before() {
|
||||
lenient()
|
||||
.when(mergeContext.retrieveBlockById(mockPid))
|
||||
.thenReturn(Optional.of(mockBlockWithReceipts));
|
||||
when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext));
|
||||
when(protocolSchedule.hardforkFor(any()))
|
||||
.thenReturn(Optional.of(new ScheduledProtocolSpec.Hardfork("shanghai", SHANGHAI_AT)));
|
||||
this.method =
|
||||
new EngineGetPayloadV3(
|
||||
vertx,
|
||||
protocolContext,
|
||||
mergeMiningCoordinator,
|
||||
factory,
|
||||
engineCallListener,
|
||||
protocolSchedule);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldReturnExpectedMethodName() {
|
||||
assertThat(method.getName()).isEqualTo("engine_getPayloadV3");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldReturnBlockForKnownPayloadId() {
|
||||
|
||||
BlockHeader cancunHeader =
|
||||
new BlockHeaderTestFixture()
|
||||
.prevRandao(Bytes32.random())
|
||||
.timestamp(CANCUN_AT + 1)
|
||||
.excessDataGas(DataGas.of(10L))
|
||||
.buildHeader();
|
||||
// should return withdrawals and excessGas for a post-cancun block
|
||||
PayloadIdentifier postCancunPid =
|
||||
PayloadIdentifier.forPayloadParams(
|
||||
Hash.ZERO,
|
||||
CANCUN_AT,
|
||||
Bytes32.random(),
|
||||
Address.fromHexString("0x42"),
|
||||
Optional.empty());
|
||||
|
||||
BlobTestFixture blobTestFixture = new BlobTestFixture();
|
||||
BlobsWithCommitments bwc = blobTestFixture.createBlobsWithCommitments(1);
|
||||
Transaction blobTx =
|
||||
new TransactionTestFixture()
|
||||
.to(Optional.of(Address.fromHexString("0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF")))
|
||||
.type(TransactionType.BLOB)
|
||||
.chainId(Optional.of(BigInteger.ONE))
|
||||
.maxFeePerGas(Optional.of(Wei.of(15)))
|
||||
.maxFeePerDataGas(Optional.of(Wei.of(128)))
|
||||
.maxPriorityFeePerGas(Optional.of(Wei.of(1)))
|
||||
.blobsWithCommitments(Optional.of(bwc))
|
||||
.versionedHashes(Optional.of(bwc.getVersionedHashes()))
|
||||
.createTransaction(senderKeys);
|
||||
TransactionReceipt blobReceipt = mock(TransactionReceipt.class);
|
||||
when(blobReceipt.getCumulativeGasUsed()).thenReturn(100L);
|
||||
BlockWithReceipts postCancunBlock =
|
||||
new BlockWithReceipts(
|
||||
new Block(
|
||||
cancunHeader,
|
||||
new BlockBody(
|
||||
List.of(blobTx),
|
||||
Collections.emptyList(),
|
||||
Optional.of(Collections.emptyList()),
|
||||
Optional.empty())),
|
||||
List.of(blobReceipt));
|
||||
|
||||
when(mergeContext.retrieveBlockById(postCancunPid)).thenReturn(Optional.of(postCancunBlock));
|
||||
|
||||
final var resp = resp(RpcMethod.ENGINE_GET_PAYLOAD_V3.getMethodName(), postCancunPid);
|
||||
assertThat(resp).isInstanceOf(JsonRpcSuccessResponse.class);
|
||||
Optional.of(resp)
|
||||
.map(JsonRpcSuccessResponse.class::cast)
|
||||
.ifPresent(
|
||||
r -> {
|
||||
assertThat(r.getResult()).isInstanceOf(EngineGetPayloadResultV3.class);
|
||||
final EngineGetPayloadResultV3 res = (EngineGetPayloadResultV3) r.getResult();
|
||||
assertThat(res.getExecutionPayload().getWithdrawals()).isNotNull();
|
||||
assertThat(res.getExecutionPayload().getHash())
|
||||
.isEqualTo(cancunHeader.getHash().toString());
|
||||
assertThat(res.getBlockValue()).isEqualTo(Quantity.create(0));
|
||||
assertThat(res.getExecutionPayload().getPrevRandao())
|
||||
.isEqualTo(cancunHeader.getPrevRandao().map(Bytes32::toString).orElse(""));
|
||||
// excessDataGas: QUANTITY, 256 bits
|
||||
String expectedQuantityOf10 = Bytes32.leftPad(Bytes.of(10)).toQuantityHexString();
|
||||
assertThat(res.getExecutionPayload().getExcessDataGas()).isNotEmpty();
|
||||
assertThat(res.getExecutionPayload().getExcessDataGas())
|
||||
.isEqualTo(expectedQuantityOf10);
|
||||
});
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMethodName() {
|
||||
return RpcMethod.ENGINE_GET_PAYLOAD_V3.getMethodName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameterTestFixture.DEPOSIT_PARAM_1;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.DepositParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Deposit;
|
||||
import org.hyperledger.besu.ethereum.core.Withdrawal;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
|
||||
import org.hyperledger.besu.ethereum.mainnet.DepositsValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class EngineNewPayloadEIP6110Test extends EngineNewPayloadV3Test {
|
||||
private static final Address depositContractAddress =
|
||||
Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");
|
||||
|
||||
public EngineNewPayloadEIP6110Test() {}
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void before() {
|
||||
super.before();
|
||||
this.method =
|
||||
new EngineNewPayloadV3(
|
||||
vertx,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator,
|
||||
ethPeers,
|
||||
engineCallListener);
|
||||
lenient()
|
||||
.when(protocolSchedule.hardforkFor(any()))
|
||||
.thenReturn(
|
||||
Optional.of(new ScheduledProtocolSpec.Hardfork("Cancun", super.CANCUN_TIMESTAMP)));
|
||||
lenient().when(protocolSpec.getGasCalculator()).thenReturn(new CancunGasCalculator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shouldReturnExpectedMethodName() {
|
||||
assertThat(method.getName()).isEqualTo("engine_newPayloadV3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfDepositsIsNull_WhenDepositsProhibited() {
|
||||
final List<DepositParameter> deposits = null;
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.ProhibitedDeposits());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), null, deposits, null));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfDepositsIsNull_WhenDepositsAllowed() {
|
||||
final List<DepositParameter> deposits = null;
|
||||
lenient()
|
||||
.when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.AllowedDeposits(depositContractAddress));
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
deposits));
|
||||
|
||||
assertThat(fromErrorResp(resp).getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfDepositsIsNotNull_WhenDepositsAllowed() {
|
||||
final List<DepositParameter> depositsParam = List.of(DEPOSIT_PARAM_1);
|
||||
final List<Deposit> deposits = List.of(DEPOSIT_PARAM_1.toDeposit());
|
||||
when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.AllowedDeposits(depositContractAddress));
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.of(deposits));
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), null, depositsParam));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfDepositsIsNotNull_WhenDepositsProhibited() {
|
||||
final List<DepositParameter> deposits = List.of();
|
||||
lenient()
|
||||
.when(protocolSpec.getDepositsValidator())
|
||||
.thenReturn(new DepositsValidator.ProhibitedDeposits());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.of(Collections.emptyList())),
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
deposits));
|
||||
|
||||
final JsonRpcError jsonRpcError = fromErrorResp(resp);
|
||||
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockHeader createBlockHeader(
|
||||
final Optional<List<Withdrawal>> maybeWithdrawals,
|
||||
final Optional<List<Deposit>> maybeDeposits) {
|
||||
BlockHeader parentBlockHeader =
|
||||
new BlockHeaderTestFixture()
|
||||
.baseFeePerGas(Wei.ONE)
|
||||
.timestamp(super.EXPERIMENTAL_TIMESTAMP)
|
||||
.excessDataGas(DataGas.ZERO)
|
||||
.dataGasUsed(100L)
|
||||
.buildHeader();
|
||||
|
||||
BlockHeader mockHeader =
|
||||
new BlockHeaderTestFixture()
|
||||
.baseFeePerGas(Wei.ONE)
|
||||
.parentHash(parentBlockHeader.getParentHash())
|
||||
.number(parentBlockHeader.getNumber() + 1)
|
||||
.timestamp(parentBlockHeader.getTimestamp() + 1)
|
||||
.withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null))
|
||||
.excessDataGas(DataGas.ZERO)
|
||||
.dataGasUsed(100L)
|
||||
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
|
||||
.buildHeader();
|
||||
return mockHeader;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -42,8 +43,20 @@ import org.mockito.quality.Strictness;
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class EngineNewPayloadV1Test extends AbstractEngineNewPayloadTest {
|
||||
|
||||
public EngineNewPayloadV1Test() {
|
||||
super(EngineNewPayloadV1::new);
|
||||
public EngineNewPayloadV1Test() {}
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
super.before();
|
||||
this.method =
|
||||
new EngineNewPayloadV1(
|
||||
vertx,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator,
|
||||
ethPeers,
|
||||
engineCallListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,28 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameterTestFixture.WITHDRAWAL_PARAM_1;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Withdrawal;
|
||||
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -26,8 +47,20 @@ import org.mockito.quality.Strictness;
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class EngineNewPayloadV2Test extends AbstractEngineNewPayloadTest {
|
||||
|
||||
public EngineNewPayloadV2Test() {
|
||||
super(EngineNewPayloadV2::new);
|
||||
public EngineNewPayloadV2Test() {}
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
super.before();
|
||||
this.method =
|
||||
new EngineNewPayloadV2(
|
||||
vertx,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator,
|
||||
ethPeers,
|
||||
engineCallListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,4 +68,86 @@ public class EngineNewPayloadV2Test extends AbstractEngineNewPayloadTest {
|
||||
public void shouldReturnExpectedMethodName() {
|
||||
assertThat(method.getName()).isEqualTo("engine_newPayloadV2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfWithdrawalsIsNotNull_WhenWithdrawalsAllowed() {
|
||||
final List<WithdrawalParameter> withdrawalsParam = List.of(WITHDRAWAL_PARAM_1);
|
||||
final List<Withdrawal> withdrawals = List.of(WITHDRAWAL_PARAM_1.toWithdrawal());
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.of(withdrawals),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), withdrawalsParam, null));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnValidIfWithdrawalsIsNull_WhenWithdrawalsProhibited() {
|
||||
final List<WithdrawalParameter> withdrawals = null;
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
|
||||
BlockHeader mockHeader =
|
||||
setupValidPayload(
|
||||
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))),
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
var resp = resp(mockPayload(mockHeader, Collections.emptyList(), withdrawals, null, null));
|
||||
|
||||
assertValidResponse(mockHeader, resp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfWithdrawalsIsNotNull_WhenWithdrawalsProhibited() {
|
||||
final List<WithdrawalParameter> withdrawals = List.of();
|
||||
lenient()
|
||||
.when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.of(Collections.emptyList()), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
withdrawals,
|
||||
null,
|
||||
null));
|
||||
|
||||
final JsonRpcError jsonRpcError = fromErrorResp(resp);
|
||||
assertThat(jsonRpcError.getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInvalidIfWithdrawalsIsNull_WhenWithdrawalsAllowed() {
|
||||
final List<WithdrawalParameter> withdrawals = null;
|
||||
when(protocolSpec.getWithdrawalsValidator())
|
||||
.thenReturn(new WithdrawalsValidator.AllowedWithdrawals());
|
||||
|
||||
var resp =
|
||||
resp(
|
||||
mockPayload(
|
||||
createBlockHeader(Optional.empty(), Optional.empty()),
|
||||
Collections.emptyList(),
|
||||
withdrawals,
|
||||
null));
|
||||
|
||||
assertThat(fromErrorResp(resp).getCode()).isEqualTo(INVALID_PARAMS.getCode());
|
||||
verify(engineCallListener, times(1)).executionEngineCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validateTerminalPoWBlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionEngineJsonRpcMethod.EngineStatus getExpectedInvalidBlockHashStatus() {
|
||||
return INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePayloadStatusResult;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Deposit;
|
||||
import org.hyperledger.besu.ethereum.core.Withdrawal;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class EngineNewPayloadV3Test extends EngineNewPayloadV2Test {
|
||||
|
||||
public EngineNewPayloadV3Test() {}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldReturnExpectedMethodName() {
|
||||
assertThat(method.getName()).isEqualTo("engine_newPayloadV3");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void before() {
|
||||
super.before();
|
||||
this.method =
|
||||
new EngineNewPayloadV3(
|
||||
vertx,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
mergeCoordinator,
|
||||
ethPeers,
|
||||
engineCallListener);
|
||||
lenient().when(protocolSpec.getGasCalculator()).thenReturn(new CancunGasCalculator());
|
||||
lenient()
|
||||
.when(protocolSchedule.hardforkFor(any()))
|
||||
.thenReturn(
|
||||
Optional.of(new ScheduledProtocolSpec.Hardfork("Cancun", super.CANCUN_TIMESTAMP)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInvalidPayloadOnShortVersionedHash() {
|
||||
Bytes shortHash = Bytes.fromHexString("0x" + "69".repeat(31));
|
||||
EnginePayloadParameter payload = mock(EnginePayloadParameter.class);
|
||||
JsonRpcResponse badParam =
|
||||
method.response(
|
||||
new JsonRpcRequestContext(
|
||||
new JsonRpcRequest(
|
||||
"2.0",
|
||||
RpcMethod.ENGINE_NEW_PAYLOAD_V3.getMethodName(),
|
||||
new Object[] {payload, List.of(shortHash.toHexString())})));
|
||||
EnginePayloadStatusResult res = fromSuccessResp(badParam);
|
||||
assertThat(res.getStatusAsString()).isEqualTo(INVALID.name());
|
||||
assertThat(res.getError()).isEqualTo("Invalid versionedHash");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockHeader createBlockHeader(
|
||||
final Optional<List<Withdrawal>> maybeWithdrawals,
|
||||
final Optional<List<Deposit>> maybeDeposits) {
|
||||
BlockHeader parentBlockHeader =
|
||||
new BlockHeaderTestFixture()
|
||||
.baseFeePerGas(Wei.ONE)
|
||||
.timestamp(super.CANCUN_TIMESTAMP)
|
||||
.buildHeader();
|
||||
BlockHeader mockHeader =
|
||||
new BlockHeaderTestFixture()
|
||||
.baseFeePerGas(Wei.ONE)
|
||||
.parentHash(parentBlockHeader.getParentHash())
|
||||
.number(parentBlockHeader.getNumber() + 1)
|
||||
.timestamp(parentBlockHeader.getTimestamp() + 1)
|
||||
.withdrawalsRoot(maybeWithdrawals.map(BodyValidation::withdrawalsRoot).orElse(null))
|
||||
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
|
||||
.buildHeader();
|
||||
return mockHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldReturnValidIfProtocolScheduleIsEmpty() {
|
||||
// no longer the case, blob validation requires a protocol schedule
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,10 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -35,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
|
||||
import org.hyperledger.besu.evm.worldstate.StackedUpdater;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
@@ -80,6 +84,7 @@ public class TransactionTracerTest {
|
||||
@Mock private DebugOperationTracer tracer;
|
||||
|
||||
@Mock private ProtocolSpec protocolSpec;
|
||||
@Mock private GasCalculator gasCalculator;
|
||||
|
||||
@Mock private Tracer.TraceableState mutableWorldState;
|
||||
|
||||
@@ -114,6 +119,8 @@ public class TransactionTracerTest {
|
||||
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
|
||||
when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager());
|
||||
when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator);
|
||||
lenient().when(gasCalculator.computeExcessDataGas(anyLong(), anyInt())).thenReturn(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class BlobsBundleV1Test {
|
||||
@Test
|
||||
public void blobsBundleV1MustHaveSameNumberOfElements() {
|
||||
String actualMessage =
|
||||
assertThrows(
|
||||
InvalidParameterException.class,
|
||||
() -> new BlobsBundleV1(List.of(""), List.of(""), List.of()))
|
||||
.getMessage();
|
||||
final String expectedMessage = "There must be an equal number of blobs, commitments and proofs";
|
||||
assertThat(actualMessage).isEqualTo(expectedMessage);
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,7 @@ public class BlockchainQueriesLogCacheTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new MainnetBlockHeaderFunctions());
|
||||
testHash = fakeHeader.getHash();
|
||||
final BlockBody fakeBody = new BlockBody(Collections.emptyList(), Collections.emptyList());
|
||||
|
||||
@@ -108,6 +108,7 @@ public class TransactionLogBloomCacherTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new MainnetBlockHeaderFunctions());
|
||||
testHash = fakeHeader.getHash();
|
||||
when(blockchain.getBlockHeader(anyLong())).thenReturn(Optional.of(fakeHeader));
|
||||
@@ -281,6 +282,7 @@ public class TransactionLogBloomCacherTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new MainnetBlockHeaderFunctions());
|
||||
testHash = fakeHeader.getHash();
|
||||
when(blockchain.getBlockHeader(number)).thenReturn(Optional.of(fakeHeader));
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.blockcreation;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
@@ -169,10 +171,10 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
createPendingBlockHeader(timestamp, maybePrevRandao, newProtocolSpec);
|
||||
final Address miningBeneficiary =
|
||||
miningBeneficiaryCalculator.getMiningBeneficiary(processableBlockHeader.getNumber());
|
||||
final Wei dataGasPrice =
|
||||
Wei dataGasPrice =
|
||||
newProtocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(parentHeader.getExcessDataGas().orElse(DataGas.ZERO));
|
||||
.dataPricePerGas(calculateExcessDataGasForParent(newProtocolSpec, parentHeader));
|
||||
|
||||
throwIfStopped();
|
||||
|
||||
@@ -228,11 +230,11 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
|
||||
throwIfStopped();
|
||||
|
||||
final DataGas newExcessDataGas = computeExcessDataGas(transactionResults, newProtocolSpec);
|
||||
final GasUsage usage = computeExcessDataGas(transactionResults, newProtocolSpec);
|
||||
|
||||
throwIfStopped();
|
||||
|
||||
final SealableBlockHeader sealableBlockHeader =
|
||||
BlockHeaderBuilder builder =
|
||||
BlockHeaderBuilder.create()
|
||||
.populateFrom(processableBlockHeader)
|
||||
.ommersHash(BodyValidation.ommersHash(ommers))
|
||||
@@ -247,9 +249,12 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
withdrawalsCanBeProcessed
|
||||
? BodyValidation.withdrawalsRoot(maybeWithdrawals.get())
|
||||
: null)
|
||||
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null))
|
||||
.excessDataGas(newExcessDataGas)
|
||||
.buildSealableBlockHeader();
|
||||
.depositsRoot(maybeDeposits.map(BodyValidation::depositsRoot).orElse(null));
|
||||
if (usage != null) {
|
||||
builder.dataGasUsed(usage.used.toLong()).excessDataGas(usage.excessDataGas);
|
||||
}
|
||||
|
||||
final SealableBlockHeader sealableBlockHeader = builder.buildSealableBlockHeader();
|
||||
|
||||
final BlockHeader blockHeader = createFinalBlockHeader(sealableBlockHeader);
|
||||
|
||||
@@ -280,8 +285,11 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
.toList();
|
||||
}
|
||||
|
||||
private DataGas computeExcessDataGas(
|
||||
TransactionSelectionResults transactionResults, ProtocolSpec newProtocolSpec) {
|
||||
record GasUsage(DataGas excessDataGas, DataGas used) {}
|
||||
;
|
||||
|
||||
private GasUsage computeExcessDataGas(
|
||||
final TransactionSelectionResults transactionResults, final ProtocolSpec newProtocolSpec) {
|
||||
|
||||
if (newProtocolSpec.getFeeMarket().implementsDataFee()) {
|
||||
final var gasCalculator = newProtocolSpec.getGasCalculator();
|
||||
@@ -292,9 +300,12 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
.sum();
|
||||
// casting parent excess data gas to long since for the moment it should be well below that
|
||||
// limit
|
||||
return DataGas.of(
|
||||
gasCalculator.computeExcessDataGas(
|
||||
parentHeader.getExcessDataGas().map(DataGas::toLong).orElse(0L), newBlobsCount));
|
||||
DataGas ecessDataGas =
|
||||
DataGas.of(
|
||||
gasCalculator.computeExcessDataGas(
|
||||
parentHeader.getExcessDataGas().map(DataGas::toLong).orElse(0L), newBlobsCount));
|
||||
DataGas used = DataGas.of(gasCalculator.dataGasCost(newBlobsCount));
|
||||
return new GasUsage(ecessDataGas, used);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,15 +27,21 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.config.GenesisConfigOptions;
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.BLSPublicKey;
|
||||
import org.hyperledger.besu.datatypes.BLSSignature;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.GWei;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator.BlockCreationResult;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlobTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
|
||||
@@ -45,7 +51,9 @@ import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.MiningParameters;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Withdrawal;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
|
||||
@@ -61,6 +69,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
|
||||
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket;
|
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration;
|
||||
import org.hyperledger.besu.evm.log.Log;
|
||||
import org.hyperledger.besu.evm.log.LogTopic;
|
||||
@@ -76,6 +85,7 @@ import java.util.function.Supplier;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -260,6 +270,52 @@ abstract class AbstractBlockCreatorTest {
|
||||
assertThat(blockCreationResult.getBlock().getBody().getWithdrawals()).isEmpty();
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
public void computesGasUsageFromIncludedTransactions() {
|
||||
final KeyPair senderKeys = SignatureAlgorithmFactory.getInstance().generateKeyPair();
|
||||
final AbstractBlockCreator blockCreator = blockCreatorWithDataGasSupport();
|
||||
BlobTestFixture blobTestFixture = new BlobTestFixture();
|
||||
BlobsWithCommitments bwc = blobTestFixture.createBlobsWithCommitments(6);
|
||||
TransactionTestFixture ttf = new TransactionTestFixture();
|
||||
Transaction fullOfBlobs =
|
||||
ttf.to(Optional.of(Address.ZERO))
|
||||
.type(TransactionType.BLOB)
|
||||
.chainId(Optional.of(BigInteger.valueOf(42)))
|
||||
.maxFeePerGas(Optional.of(Wei.of(15)))
|
||||
.maxFeePerDataGas(Optional.of(Wei.of(128)))
|
||||
.maxPriorityFeePerGas(Optional.of(Wei.of(1)))
|
||||
.versionedHashes(Optional.of(bwc.getVersionedHashes()))
|
||||
.createTransaction(senderKeys);
|
||||
|
||||
ttf.blobsWithCommitments(Optional.of(bwc));
|
||||
final BlockCreationResult blockCreationResult =
|
||||
blockCreator.createBlock(
|
||||
Optional.of(List.of(fullOfBlobs)),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
1L,
|
||||
false);
|
||||
long dataGasUsage = blockCreationResult.getBlock().getHeader().getGasUsed();
|
||||
assertThat(dataGasUsage).isNotZero();
|
||||
DataGas excessDataGas = blockCreationResult.getBlock().getHeader().getExcessDataGas().get();
|
||||
assertThat(excessDataGas).isNotNull();
|
||||
}
|
||||
|
||||
private AbstractBlockCreator blockCreatorWithDataGasSupport() {
|
||||
final ProtocolSpecAdapters protocolSpecAdapters =
|
||||
ProtocolSpecAdapters.create(
|
||||
0,
|
||||
specBuilder -> {
|
||||
specBuilder.feeMarket(new CancunFeeMarket(0, Optional.empty()));
|
||||
specBuilder.isReplayProtectionSupported(true);
|
||||
specBuilder.withdrawalsProcessor(withdrawalsProcessor);
|
||||
return specBuilder;
|
||||
});
|
||||
return createBlockCreator(protocolSpecAdapters, EMPTY_DEPOSIT_CONTRACT_ADDRESS);
|
||||
}
|
||||
|
||||
private AbstractBlockCreator blockCreatorWithWithdrawalsProcessor() {
|
||||
final ProtocolSpecAdapters protocolSpecAdapters =
|
||||
ProtocolSpecAdapters.create(
|
||||
|
||||
@@ -60,6 +60,7 @@ dependencies {
|
||||
implementation 'io.tmio:tuweni-rlp'
|
||||
implementation 'org.hyperledger.besu:bls12-381'
|
||||
implementation 'org.immutables:value-annotations'
|
||||
implementation 'tech.pegasys:jc-kzg-4844'
|
||||
|
||||
implementation 'io.prometheus:simpleclient_guava'
|
||||
|
||||
@@ -93,6 +94,7 @@ dependencies {
|
||||
integrationTestImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||
integrationTestImplementation 'org.mockito:mockito-core'
|
||||
integrationTestImplementation 'org.testcontainers:testcontainers'
|
||||
integrationTestImplementation 'io.tmio:tuweni-bytes'
|
||||
|
||||
integrationTestRuntimeOnly 'org.junit.jupiter:junit-jupiter'
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum;
|
||||
|
||||
public interface GasLimitCalculator {
|
||||
|
||||
static final long DATA_GAS_LIMIT = 786432;
|
||||
|
||||
long nextGasLimit(long currentGasLimit, long targetGasLimit, long newBlockNumber);
|
||||
|
||||
static GasLimitCalculator constant() {
|
||||
@@ -23,6 +25,6 @@ public interface GasLimitCalculator {
|
||||
}
|
||||
|
||||
default long currentDataGasLimit() {
|
||||
return 0L;
|
||||
return DATA_GAS_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static java.util.Collections.emptyList;
|
||||
import org.hyperledger.besu.config.GenesisAllocation;
|
||||
import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
@@ -167,6 +168,8 @@ public final class GenesisState {
|
||||
.blockHeaderFunctions(ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))
|
||||
.baseFee(genesis.getGenesisBaseFeePerGas().orElse(null))
|
||||
.withdrawalsRoot(isShanghaiAtGenesis(genesis) ? Hash.EMPTY_TRIE_HASH : null)
|
||||
.dataGasUsed(isCancunAtGenesis(genesis) ? parseDataGasUsed(genesis) : null)
|
||||
.excessDataGas(isCancunAtGenesis(genesis) ? parseExcessDataGas(genesis) : null)
|
||||
.depositsRoot(isExperimentalEipsTimeAtGenesis(genesis) ? Hash.EMPTY_TRIE_HASH : null)
|
||||
.buildBlockHeader();
|
||||
}
|
||||
@@ -217,18 +220,38 @@ public final class GenesisState {
|
||||
return withNiceErrorMessage("nonce", genesis.getNonce(), GenesisState::parseUnsignedLong);
|
||||
}
|
||||
|
||||
private static long parseDataGasUsed(final GenesisConfigFile genesis) {
|
||||
return withNiceErrorMessage(
|
||||
"dataGasUsed", genesis.getDataGasUsed(), GenesisState::parseUnsignedLong);
|
||||
}
|
||||
|
||||
private static DataGas parseExcessDataGas(final GenesisConfigFile genesis) {
|
||||
long excessDataGas =
|
||||
withNiceErrorMessage(
|
||||
"excessDataGas", genesis.getExcessDataGas(), GenesisState::parseUnsignedLong);
|
||||
return DataGas.of(excessDataGas);
|
||||
}
|
||||
|
||||
private static long parseUnsignedLong(final String value) {
|
||||
String nonce = value.toLowerCase(Locale.US);
|
||||
if (nonce.startsWith("0x")) {
|
||||
nonce = nonce.substring(2);
|
||||
String v = value.toLowerCase(Locale.US);
|
||||
if (v.startsWith("0x")) {
|
||||
v = v.substring(2);
|
||||
}
|
||||
return Long.parseUnsignedLong(nonce, 16);
|
||||
return Long.parseUnsignedLong(v, 16);
|
||||
}
|
||||
|
||||
private static boolean isShanghaiAtGenesis(final GenesisConfigFile genesis) {
|
||||
final OptionalLong shanghaiTimestamp = genesis.getConfigOptions().getShanghaiTime();
|
||||
if (shanghaiTimestamp.isPresent()) {
|
||||
return shanghaiTimestamp.getAsLong() == genesis.getTimestamp();
|
||||
return genesis.getTimestamp() >= shanghaiTimestamp.getAsLong();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isCancunAtGenesis(final GenesisConfigFile genesis) {
|
||||
final OptionalLong cancunTimestamp = genesis.getConfigOptions().getCancunTime();
|
||||
if (cancunTimestamp.isPresent()) {
|
||||
return genesis.getTimestamp() >= cancunTimestamp.getAsLong();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -236,7 +259,7 @@ public final class GenesisState {
|
||||
private static boolean isExperimentalEipsTimeAtGenesis(final GenesisConfigFile genesis) {
|
||||
final OptionalLong experimentalEipsTime = genesis.getConfigOptions().getExperimentalEipsTime();
|
||||
if (experimentalEipsTime.isPresent()) {
|
||||
return experimentalEipsTime.getAsLong() == genesis.getTimestamp();
|
||||
return genesis.getTimestamp() >= experimentalEipsTime.getAsLong();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -57,7 +59,10 @@ public class Block {
|
||||
out.startList();
|
||||
|
||||
header.writeTo(out);
|
||||
body.writeTo(out);
|
||||
out.writeList(body.getTransactions(), Transaction::writeTo);
|
||||
out.writeList(body.getOmmers(), BlockHeader::writeTo);
|
||||
body.getWithdrawals().ifPresent(withdrawals -> out.writeList(withdrawals, Withdrawal::writeTo));
|
||||
body.getDeposits().ifPresent(deposits -> out.writeList(deposits, Deposit::writeTo));
|
||||
|
||||
out.endList();
|
||||
}
|
||||
@@ -65,10 +70,15 @@ public class Block {
|
||||
public static Block readFrom(final RLPInput in, final BlockHeaderFunctions hashFunction) {
|
||||
in.enterList();
|
||||
final BlockHeader header = BlockHeader.readFrom(in, hashFunction);
|
||||
final BlockBody body = BlockBody.readFrom(in, hashFunction);
|
||||
final List<Transaction> transactions = in.readList(Transaction::readFrom);
|
||||
final List<BlockHeader> ommers = in.readList(rlp -> BlockHeader.readFrom(rlp, hashFunction));
|
||||
final Optional<List<Withdrawal>> withdrawals =
|
||||
in.isEndOfCurrentList() ? Optional.empty() : Optional.of(in.readList(Withdrawal::readFrom));
|
||||
final Optional<List<Deposit>> deposits =
|
||||
in.isEndOfCurrentList() ? Optional.empty() : Optional.of(in.readList(Deposit::readFrom));
|
||||
in.leaveList();
|
||||
|
||||
return new Block(header, body);
|
||||
return new Block(header, new BlockBody(transactions, ommers, withdrawals, deposits));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,6 +62,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
final Bytes32 mixHashOrPrevRandao,
|
||||
final long nonce,
|
||||
final Hash withdrawalsRoot,
|
||||
final long dataGasUsed,
|
||||
final DataGas excessDataGas,
|
||||
final Hash depositsRoot,
|
||||
final BlockHeaderFunctions blockHeaderFunctions,
|
||||
@@ -83,6 +84,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
baseFee,
|
||||
mixHashOrPrevRandao,
|
||||
withdrawalsRoot,
|
||||
dataGasUsed,
|
||||
excessDataGas,
|
||||
depositsRoot);
|
||||
this.nonce = nonce;
|
||||
@@ -108,6 +110,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
final Bytes32 mixHashOrPrevRandao,
|
||||
final long nonce,
|
||||
final Hash withdrawalsRoot,
|
||||
final Long dataGasUsed,
|
||||
final DataGas excessDataGas,
|
||||
final Hash depositsRoot,
|
||||
final BlockHeaderFunctions blockHeaderFunctions) {
|
||||
@@ -128,6 +131,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
baseFee,
|
||||
mixHashOrPrevRandao,
|
||||
withdrawalsRoot,
|
||||
dataGasUsed,
|
||||
excessDataGas,
|
||||
depositsRoot);
|
||||
this.nonce = nonce;
|
||||
@@ -212,8 +216,9 @@ public class BlockHeader extends SealableBlockHeader
|
||||
if (withdrawalsRoot != null) {
|
||||
out.writeBytes(withdrawalsRoot);
|
||||
}
|
||||
if (excessDataGas != null) {
|
||||
out.writeUInt256Scalar(excessDataGas);
|
||||
if (excessDataGas != null && dataGasUsed != null) {
|
||||
out.writeLongScalar(dataGasUsed);
|
||||
out.writeUInt64Scalar(excessDataGas);
|
||||
}
|
||||
if (depositsRoot != null) {
|
||||
out.writeBytes(depositsRoot);
|
||||
@@ -241,9 +246,12 @@ public class BlockHeader extends SealableBlockHeader
|
||||
final long nonce = input.readLong();
|
||||
final Wei baseFee = !input.isEndOfCurrentList() ? Wei.of(input.readUInt256Scalar()) : null;
|
||||
final Hash withdrawalHashRoot =
|
||||
!input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null;
|
||||
!(input.isEndOfCurrentList() || input.isZeroLengthString())
|
||||
? Hash.wrap(input.readBytes32())
|
||||
: null;
|
||||
final Long dataGasUsed = !input.isEndOfCurrentList() ? input.readLongScalar() : null;
|
||||
final DataGas excessDataGas =
|
||||
!input.isEndOfCurrentList() ? DataGas.of(input.readUInt256Scalar()) : null;
|
||||
!input.isEndOfCurrentList() ? DataGas.of(input.readLongScalar()) : null;
|
||||
final Hash depositHashRoot =
|
||||
!input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null;
|
||||
input.leaveList();
|
||||
@@ -265,6 +273,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
mixHashOrPrevRandao,
|
||||
nonce,
|
||||
withdrawalHashRoot,
|
||||
dataGasUsed,
|
||||
excessDataGas,
|
||||
depositHashRoot,
|
||||
blockHeaderFunctions);
|
||||
@@ -311,6 +320,9 @@ public class BlockHeader extends SealableBlockHeader
|
||||
if (withdrawalsRoot != null) {
|
||||
sb.append("withdrawalsRoot=").append(withdrawalsRoot).append(", ");
|
||||
}
|
||||
if (dataGasUsed != null) {
|
||||
sb.append("dataGasUsed=").append(dataGasUsed).append(", ");
|
||||
}
|
||||
if (excessDataGas != null) {
|
||||
sb.append("excessDataGas=").append(excessDataGas).append(", ");
|
||||
}
|
||||
@@ -344,6 +356,7 @@ public class BlockHeader extends SealableBlockHeader
|
||||
.getWithdrawalsRoot()
|
||||
.map(h -> Hash.fromHexString(h.toHexString()))
|
||||
.orElse(null),
|
||||
pluginBlockHeader.getDataGasUsed().map(Long::longValue).orElse(null),
|
||||
pluginBlockHeader.getExcessDataGas().map(DataGas::fromQuantity).orElse(null),
|
||||
pluginBlockHeader
|
||||
.getDepositsRoot()
|
||||
|
||||
@@ -73,6 +73,7 @@ public class BlockHeaderBuilder {
|
||||
// instead of an invalid identifier such as -1.
|
||||
private OptionalLong nonce = OptionalLong.empty();
|
||||
|
||||
private Long dataGasUsed = null;
|
||||
private DataGas excessDataGas = null;
|
||||
|
||||
public static BlockHeaderBuilder create() {
|
||||
@@ -119,6 +120,7 @@ public class BlockHeaderBuilder {
|
||||
.nonce(header.getNonce())
|
||||
.prevRandao(header.getPrevRandao().orElse(null))
|
||||
.withdrawalsRoot(header.getWithdrawalsRoot().orElse(null))
|
||||
.dataGasUsed(header.getDataGasUsed().orElse(null))
|
||||
.excessDataGas(header.getExcessDataGas().orElse(null))
|
||||
.depositsRoot(header.getDepositsRoot().orElse(null));
|
||||
}
|
||||
@@ -170,6 +172,7 @@ public class BlockHeaderBuilder {
|
||||
mixHashOrPrevRandao,
|
||||
nonce.getAsLong(),
|
||||
withdrawalsRoot,
|
||||
dataGasUsed,
|
||||
excessDataGas,
|
||||
depositsRoot,
|
||||
blockHeaderFunctions);
|
||||
@@ -187,6 +190,7 @@ public class BlockHeaderBuilder {
|
||||
timestamp,
|
||||
baseFee,
|
||||
mixHashOrPrevRandao,
|
||||
dataGasUsed,
|
||||
excessDataGas);
|
||||
}
|
||||
|
||||
@@ -210,6 +214,7 @@ public class BlockHeaderBuilder {
|
||||
baseFee,
|
||||
mixHashOrPrevRandao,
|
||||
withdrawalsRoot,
|
||||
dataGasUsed,
|
||||
excessDataGas,
|
||||
depositsRoot);
|
||||
}
|
||||
@@ -251,6 +256,7 @@ public class BlockHeaderBuilder {
|
||||
timestamp(processableBlockHeader.getTimestamp());
|
||||
baseFee(processableBlockHeader.getBaseFee().orElse(null));
|
||||
processableBlockHeader.getPrevRandao().ifPresent(this::prevRandao);
|
||||
processableBlockHeader.getDataGasUsed().ifPresent(this::dataGasUsed);
|
||||
processableBlockHeader.getExcessDataGas().ifPresent(this::excessDataGas);
|
||||
return this;
|
||||
}
|
||||
@@ -273,6 +279,7 @@ public class BlockHeaderBuilder {
|
||||
baseFee(sealableBlockHeader.getBaseFee().orElse(null));
|
||||
sealableBlockHeader.getPrevRandao().ifPresent(this::prevRandao);
|
||||
withdrawalsRoot(sealableBlockHeader.getWithdrawalsRoot().orElse(null));
|
||||
sealableBlockHeader.getDataGasUsed().ifPresent(this::dataGasUsed);
|
||||
sealableBlockHeader.getExcessDataGas().ifPresent(this::excessDataGas);
|
||||
depositsRoot(sealableBlockHeader.getDepositsRoot().orElse(null));
|
||||
return this;
|
||||
@@ -399,4 +406,9 @@ public class BlockHeaderBuilder {
|
||||
this.excessDataGas = excessDataGas;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockHeaderBuilder dataGasUsed(final Long dataGasUsed) {
|
||||
this.dataGasUsed = dataGasUsed;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ public class ProcessableBlockHeader implements BlockValues {
|
||||
protected final Wei baseFee;
|
||||
// prevRandao is included for post-merge blocks
|
||||
protected final Bytes32 mixHashOrPrevRandao;
|
||||
// dataGasUsed is included for Cancun
|
||||
protected final Long dataGasUsed;
|
||||
// excessDataGas is included for Cancun
|
||||
protected final DataGas excessDataGas;
|
||||
|
||||
protected ProcessableBlockHeader(
|
||||
@@ -55,6 +58,7 @@ public class ProcessableBlockHeader implements BlockValues {
|
||||
final long timestamp,
|
||||
final Wei baseFee,
|
||||
final Bytes32 mixHashOrPrevRandao,
|
||||
final Long dataGasUsed,
|
||||
final DataGas excessDataGas) {
|
||||
this.parentHash = parentHash;
|
||||
this.coinbase = coinbase;
|
||||
@@ -64,6 +68,7 @@ public class ProcessableBlockHeader implements BlockValues {
|
||||
this.timestamp = timestamp;
|
||||
this.baseFee = baseFee;
|
||||
this.mixHashOrPrevRandao = mixHashOrPrevRandao;
|
||||
this.dataGasUsed = dataGasUsed;
|
||||
this.excessDataGas = excessDataGas;
|
||||
}
|
||||
|
||||
@@ -167,6 +172,10 @@ public class ProcessableBlockHeader implements BlockValues {
|
||||
return Optional.ofNullable(excessDataGas);
|
||||
}
|
||||
|
||||
public Optional<Long> getDataGasUsed() {
|
||||
return Optional.ofNullable(dataGasUsed);
|
||||
}
|
||||
|
||||
public String toLogString() {
|
||||
return getNumber() + " (time: " + getTimestamp() + ")";
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
|
||||
final Wei baseFee,
|
||||
final Bytes32 mixHashOrPrevRandao,
|
||||
final Hash withdrawalsRoot,
|
||||
final Long dataGasUsed,
|
||||
final DataGas excessDataGas,
|
||||
final Hash depositsRoot) {
|
||||
super(
|
||||
@@ -73,6 +74,7 @@ public class SealableBlockHeader extends ProcessableBlockHeader {
|
||||
timestamp,
|
||||
baseFee,
|
||||
mixHashOrPrevRandao,
|
||||
dataGasUsed,
|
||||
excessDataGas);
|
||||
this.ommersHash = ommersHash;
|
||||
this.stateRoot = stateRoot;
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
||||
import static org.hyperledger.besu.datatypes.VersionedHash.SHA256_VERSION_ID;
|
||||
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SECPPublicKey;
|
||||
@@ -24,10 +25,17 @@ import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.KZGProof;
|
||||
import org.hyperledger.besu.datatypes.Quantity;
|
||||
import org.hyperledger.besu.datatypes.Sha256Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
@@ -107,7 +115,9 @@ public class Transaction
|
||||
private final TransactionType transactionType;
|
||||
|
||||
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
|
||||
private final Optional<List<Hash>> versionedHashes;
|
||||
private final Optional<List<VersionedHash>> versionedHashes;
|
||||
|
||||
private final Optional<BlobsWithCommitments> blobsWithCommitments;
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
@@ -159,7 +169,8 @@ public class Transaction
|
||||
final Optional<List<AccessListEntry>> maybeAccessList,
|
||||
final Address sender,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<Hash>> versionedHashes) {
|
||||
final Optional<List<VersionedHash>> versionedHashes,
|
||||
final Optional<BlobsWithCommitments> blobsWithCommitments) {
|
||||
|
||||
if (transactionType.requiresChainId()) {
|
||||
checkArgument(
|
||||
@@ -207,6 +218,7 @@ public class Transaction
|
||||
this.sender = sender;
|
||||
this.chainId = chainId;
|
||||
this.versionedHashes = versionedHashes;
|
||||
this.blobsWithCommitments = blobsWithCommitments;
|
||||
|
||||
if (isUpfrontGasCostTooHigh()) {
|
||||
throw new IllegalArgumentException("Upfront gas cost exceeds UInt256");
|
||||
@@ -226,7 +238,8 @@ public class Transaction
|
||||
final Bytes payload,
|
||||
final Address sender,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<Hash>> versionedHashes) {
|
||||
final Optional<List<VersionedHash>> versionedHashes,
|
||||
final Optional<BlobsWithCommitments> blobsWithCommitments) {
|
||||
this(
|
||||
TransactionType.FRONTIER,
|
||||
nonce,
|
||||
@@ -242,7 +255,8 @@ public class Transaction
|
||||
Optional.empty(),
|
||||
sender,
|
||||
chainId,
|
||||
versionedHashes);
|
||||
versionedHashes,
|
||||
blobsWithCommitments);
|
||||
}
|
||||
|
||||
public Transaction(
|
||||
@@ -254,7 +268,8 @@ public class Transaction
|
||||
final SECPSignature signature,
|
||||
final Bytes payload,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<Hash>> versionedHashes) {
|
||||
final Optional<List<VersionedHash>> versionedHashes,
|
||||
final Optional<BlobsWithCommitments> blobsWithCommitments) {
|
||||
this(
|
||||
TransactionType.FRONTIER,
|
||||
nonce,
|
||||
@@ -270,7 +285,8 @@ public class Transaction
|
||||
Optional.empty(),
|
||||
null,
|
||||
chainId,
|
||||
versionedHashes);
|
||||
versionedHashes,
|
||||
blobsWithCommitments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +316,7 @@ public class Transaction
|
||||
final Bytes payload,
|
||||
final Address sender,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<Hash>> versionedHashes) {
|
||||
final Optional<List<VersionedHash>> versionedHashes) {
|
||||
this(
|
||||
nonce,
|
||||
Optional.of(gasPrice),
|
||||
@@ -314,7 +330,55 @@ public class Transaction
|
||||
payload,
|
||||
sender,
|
||||
chainId,
|
||||
versionedHashes);
|
||||
versionedHashes,
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a transaction instance.
|
||||
*
|
||||
* @param nonce the nonce
|
||||
* @param gasPrice the gas price
|
||||
* @param gasLimit the gas limit
|
||||
* @param to the transaction recipient
|
||||
* @param value the value being transferred to the recipient
|
||||
* @param signature the signature
|
||||
* @param payload the payload
|
||||
* @param sender the transaction sender
|
||||
* @param chainId the chain id to apply the transaction to
|
||||
* <p>The {@code to} will be an {@code Optional.empty()} for a contract creation transaction;
|
||||
* otherwise it should contain an address.
|
||||
* <p>The {@code chainId} must be greater than 0 to be applied to a specific chain; otherwise
|
||||
* it will default to any chain.
|
||||
*/
|
||||
public Transaction(
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final long gasLimit,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final SECPSignature signature,
|
||||
final Bytes payload,
|
||||
final Address sender,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<Wei> maxFeePerDataGas,
|
||||
final Optional<List<VersionedHash>> versionedHashes,
|
||||
final Optional<BlobsWithCommitments> blobsWithCommitments) {
|
||||
this(
|
||||
nonce,
|
||||
Optional.of(gasPrice),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
maxFeePerDataGas,
|
||||
gasLimit,
|
||||
to,
|
||||
value,
|
||||
signature,
|
||||
payload,
|
||||
sender,
|
||||
chainId,
|
||||
versionedHashes,
|
||||
blobsWithCommitments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,9 +681,15 @@ public class Transaction
|
||||
final Bytes bytes = TransactionEncoder.encodeOpaqueBytes(this);
|
||||
hash = Hash.hash(bytes);
|
||||
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
TransactionEncoder.encodeForWire(transactionType, bytes, rlpOutput);
|
||||
size = rlpOutput.encodedSize();
|
||||
if (transactionType.supportsBlob()) {
|
||||
if (getBlobsWithCommitments().isPresent()) {
|
||||
size = TransactionEncoder.encodeOpaqueBytes(this).size();
|
||||
}
|
||||
} else {
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
TransactionEncoder.encodeForWire(transactionType, bytes, rlpOutput);
|
||||
size = rlpOutput.encodedSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,8 +801,12 @@ public class Transaction
|
||||
return this.transactionType;
|
||||
}
|
||||
|
||||
public Optional<List<Hash>> getVersionedHashes() {
|
||||
return this.versionedHashes;
|
||||
public Optional<List<VersionedHash>> getVersionedHashes() {
|
||||
return versionedHashes;
|
||||
}
|
||||
|
||||
public Optional<BlobsWithCommitments> getBlobsWithCommitments() {
|
||||
return blobsWithCommitments;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -758,7 +832,7 @@ public class Transaction
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final Optional<List<AccessListEntry>> accessList,
|
||||
final List<Hash> versionedHashes,
|
||||
final List<VersionedHash> versionedHashes,
|
||||
final Optional<BigInteger> chainId) {
|
||||
if (transactionType.requiresChainId()) {
|
||||
checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType);
|
||||
@@ -907,7 +981,8 @@ public class Transaction
|
||||
final Bytes payload,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<AccessListEntry>> accessList,
|
||||
final List<Hash> versionedHashes) {
|
||||
final List<VersionedHash> versionedHashes) {
|
||||
|
||||
final Bytes encoded =
|
||||
RLP.encode(
|
||||
rlpOutput -> {
|
||||
@@ -924,7 +999,7 @@ public class Transaction
|
||||
accessList,
|
||||
rlpOutput);
|
||||
rlpOutput.writeUInt256Scalar(maxFeePerDataGas);
|
||||
TransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes);
|
||||
BlobTransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes);
|
||||
rlpOutput.endList();
|
||||
});
|
||||
return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded);
|
||||
@@ -989,7 +1064,11 @@ public class Transaction
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(isContractCreation() ? "ContractCreation" : "MessageCall").append("{");
|
||||
sb.append(
|
||||
transactionType.supportsBlob()
|
||||
? "Blob"
|
||||
: isContractCreation() ? "ContractCreation" : "MessageCall")
|
||||
.append("{");
|
||||
sb.append("type=").append(getType()).append(", ");
|
||||
sb.append("nonce=").append(getNonce()).append(", ");
|
||||
getGasPrice()
|
||||
@@ -1021,7 +1100,11 @@ public class Transaction
|
||||
public String toTraceLog() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(getHash()).append("={");
|
||||
sb.append(isContractCreation() ? "ContractCreation" : "MessageCall").append(", ");
|
||||
sb.append(
|
||||
transactionType.supportsBlob()
|
||||
? "Blob"
|
||||
: isContractCreation() ? "ContractCreation" : "MessageCall")
|
||||
.append(", ");
|
||||
sb.append(getNonce()).append(", ");
|
||||
sb.append(getSender()).append(", ");
|
||||
sb.append(getType()).append(", ");
|
||||
@@ -1080,9 +1163,9 @@ public class Transaction
|
||||
protected Address sender;
|
||||
|
||||
protected Optional<BigInteger> chainId = Optional.empty();
|
||||
|
||||
protected Optional<BigInteger> v = Optional.empty();
|
||||
protected List<Hash> versionedHashes = null;
|
||||
protected List<VersionedHash> versionedHashes = null;
|
||||
private BlobsWithCommitments blobsWithCommitments;
|
||||
|
||||
public Builder type(final TransactionType transactionType) {
|
||||
this.transactionType = transactionType;
|
||||
@@ -1162,7 +1245,7 @@ public class Transaction
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder versionedHashes(final List<Hash> versionedHashes) {
|
||||
public Builder versionedHashes(final List<VersionedHash> versionedHashes) {
|
||||
this.versionedHashes = versionedHashes;
|
||||
return this;
|
||||
}
|
||||
@@ -1201,7 +1284,8 @@ public class Transaction
|
||||
accessList,
|
||||
sender,
|
||||
chainId,
|
||||
Optional.ofNullable(versionedHashes));
|
||||
Optional.ofNullable(versionedHashes),
|
||||
Optional.ofNullable(blobsWithCommitments));
|
||||
}
|
||||
|
||||
public Transaction signAndBuild(final KeyPair keys) {
|
||||
@@ -1231,5 +1315,20 @@ public class Transaction
|
||||
chainId),
|
||||
keys);
|
||||
}
|
||||
|
||||
public Builder kzgBlobs(
|
||||
final List<KZGCommitment> kzgCommitments,
|
||||
final List<Blob> blobs,
|
||||
final List<KZGProof> kzgProofs) {
|
||||
if (this.versionedHashes == null || this.versionedHashes.isEmpty()) {
|
||||
this.versionedHashes =
|
||||
kzgCommitments.stream()
|
||||
.map(c -> new VersionedHash(SHA256_VERSION_ID, Sha256Hash.hash(c.getData())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
this.blobsWithCommitments =
|
||||
new BlobsWithCommitments(kzgCommitments, blobs, kzgProofs, versionedHashes);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.core.encoding;
|
||||
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.KZGProof;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.evm.AccessListEntry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
public class BlobTransactionDecoder {
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
|
||||
static Transaction decode(final RLPInput input) {
|
||||
Transaction transaction;
|
||||
|
||||
input.enterList();
|
||||
// BlobTransactionNetworkWrapper
|
||||
if (input.nextIsList()) {
|
||||
transaction = readNetworkWrapperInner(input);
|
||||
} else {
|
||||
transaction = readTransactionPayload(input);
|
||||
}
|
||||
input.leaveList();
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private static Transaction readTransactionPayload(final RLPInput input) {
|
||||
final Transaction.Builder builder = Transaction.builder();
|
||||
readTransactionPayloadInner(builder, input);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static void readTransactionPayloadInner(
|
||||
final Transaction.Builder builder, final RLPInput input) {
|
||||
builder
|
||||
.type(TransactionType.BLOB)
|
||||
.chainId(input.readBigIntegerScalar())
|
||||
.nonce(input.readLongScalar())
|
||||
.maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar()))
|
||||
.maxFeePerGas(Wei.of(input.readUInt256Scalar()))
|
||||
.gasLimit(input.readLongScalar())
|
||||
.to(input.readBytes(v -> v.size() == 0 ? null : Address.wrap(v)))
|
||||
.value(Wei.of(input.readUInt256Scalar()))
|
||||
.payload(input.readBytes())
|
||||
.accessList(
|
||||
input.readList(
|
||||
accessListEntryRLPInput -> {
|
||||
accessListEntryRLPInput.enterList();
|
||||
final AccessListEntry accessListEntry =
|
||||
new AccessListEntry(
|
||||
Address.wrap(accessListEntryRLPInput.readBytes()),
|
||||
accessListEntryRLPInput.readList(RLPInput::readBytes32));
|
||||
accessListEntryRLPInput.leaveList();
|
||||
return accessListEntry;
|
||||
}))
|
||||
.maxFeePerDataGas(Wei.of(input.readUInt256Scalar()))
|
||||
.versionedHashes(
|
||||
input.readList(versionedHashes -> new VersionedHash(versionedHashes.readBytes32())));
|
||||
|
||||
final byte recId = (byte) input.readIntScalar();
|
||||
builder.signature(
|
||||
SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.createSignature(
|
||||
input.readUInt256Scalar().toUnsignedBigInteger(),
|
||||
input.readUInt256Scalar().toUnsignedBigInteger(),
|
||||
recId));
|
||||
}
|
||||
|
||||
private static Transaction readNetworkWrapperInner(final RLPInput input) {
|
||||
final Transaction.Builder builder = Transaction.builder();
|
||||
input.enterList();
|
||||
readTransactionPayloadInner(builder, input);
|
||||
input.leaveList();
|
||||
|
||||
List<Blob> blobs = input.readList(Blob::readFrom);
|
||||
List<KZGCommitment> commitments = input.readList(KZGCommitment::readFrom);
|
||||
List<KZGProof> proofs = input.readList(KZGProof::readFrom);
|
||||
builder.kzgBlobs(commitments, blobs, proofs);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright Hyperledger Besu Contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.core.encoding;
|
||||
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.KZGProof;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class BlobTransactionEncoder {
|
||||
private static final Logger LOG = getLogger(BlobTransactionEncoder.class);
|
||||
|
||||
public static void encodeEIP4844(final Transaction transaction, final RLPOutput out) {
|
||||
out.startList();
|
||||
out.writeBigIntegerScalar(transaction.getChainId().orElseThrow());
|
||||
out.writeLongScalar(transaction.getNonce());
|
||||
out.writeUInt256Scalar(transaction.getMaxPriorityFeePerGas().orElseThrow());
|
||||
out.writeUInt256Scalar(transaction.getMaxFeePerGas().orElseThrow());
|
||||
out.writeLongScalar(transaction.getGasLimit());
|
||||
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
|
||||
out.writeUInt256Scalar(transaction.getValue());
|
||||
out.writeBytes(transaction.getPayload());
|
||||
TransactionEncoder.writeAccessList(out, transaction.getAccessList());
|
||||
out.writeUInt256Scalar(transaction.getMaxFeePerDataGas().orElseThrow());
|
||||
out.startList();
|
||||
transaction
|
||||
.getVersionedHashes()
|
||||
.get()
|
||||
.forEach(
|
||||
vh -> {
|
||||
out.writeBytes(vh.toBytes());
|
||||
});
|
||||
out.endList();
|
||||
TransactionEncoder.writeSignatureAndRecoveryId(transaction, out);
|
||||
out.endList();
|
||||
}
|
||||
|
||||
private static void encodeEIP4844Network(final Transaction transaction, final RLPOutput out) {
|
||||
LOG.trace("Encoding transaction with blobs {}", transaction);
|
||||
out.startList();
|
||||
var blobsWithCommitments = transaction.getBlobsWithCommitments().orElseThrow();
|
||||
encodeEIP4844(transaction, out);
|
||||
|
||||
out.writeList(blobsWithCommitments.getBlobs(), Blob::writeTo);
|
||||
out.writeList(blobsWithCommitments.getKzgCommitments(), KZGCommitment::writeTo);
|
||||
out.writeList(blobsWithCommitments.getKzgProofs(), KZGProof::writeTo);
|
||||
out.endList();
|
||||
}
|
||||
|
||||
public static void encodeForWireNetwork(
|
||||
final Transaction transaction, final RLPOutput rlpOutput) {
|
||||
rlpOutput.writeBytes(encodeOpaqueBytesNetwork(transaction));
|
||||
}
|
||||
|
||||
private static Bytes encodeOpaqueBytesNetwork(final Transaction transaction) {
|
||||
return Bytes.concatenate(
|
||||
Bytes.of(transaction.getType().getSerializedType()),
|
||||
RLP.encode(rlpOutput -> encodeEIP4844Network(transaction, rlpOutput)));
|
||||
}
|
||||
|
||||
public static void writeBlobVersionedHashes(
|
||||
final RLPOutput rlpOutput, final List<VersionedHash> versionedHashes) {
|
||||
rlpOutput.writeList(versionedHashes, (h, out) -> out.writeBytes(h.toBytes()));
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,9 @@ public class TransactionDecoder {
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionDecoder::decodeAccessList,
|
||||
TransactionType.EIP1559,
|
||||
TransactionDecoder::decodeEIP1559);
|
||||
TransactionDecoder::decodeEIP1559,
|
||||
TransactionType.BLOB,
|
||||
BlobTransactionDecoder::decode);
|
||||
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.core.encoding;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -44,7 +43,9 @@ public class TransactionEncoder {
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionEncoder::encodeAccessList,
|
||||
TransactionType.EIP1559,
|
||||
TransactionEncoder::encodeEIP1559);
|
||||
TransactionEncoder::encodeEIP1559,
|
||||
TransactionType.BLOB,
|
||||
BlobTransactionEncoder::encodeEIP4844);
|
||||
|
||||
public static void encodeForWire(final Transaction transaction, final RLPOutput rlpOutput) {
|
||||
final TransactionType transactionType =
|
||||
@@ -185,17 +186,12 @@ public class TransactionEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeBlobVersionedHashes(
|
||||
final RLPOutput rlpOutput, final List<Hash> versionedHashes) {
|
||||
// ToDo 4844: implement
|
||||
}
|
||||
|
||||
private static void writeSignatureAndV(final Transaction transaction, final RLPOutput out) {
|
||||
out.writeBigIntegerScalar(transaction.getV());
|
||||
writeSignature(transaction, out);
|
||||
}
|
||||
|
||||
private static void writeSignatureAndRecoveryId(
|
||||
public static void writeSignatureAndRecoveryId(
|
||||
final Transaction transaction, final RLPOutput out) {
|
||||
out.writeIntScalar(transaction.getSignature().getRecId());
|
||||
writeSignature(transaction, out);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
* Copyright Hyperledger Besu contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -14,9 +14,11 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.core.feemarket;
|
||||
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -42,4 +44,36 @@ public interface TransactionPriceCalculator {
|
||||
return price;
|
||||
};
|
||||
}
|
||||
|
||||
// curiously named as in the spec
|
||||
// https://eips.ethereum.org/EIPS/eip-4844#cryptographic-helpers
|
||||
private static BigInteger fakeExponential(
|
||||
final BigInteger factor, final BigInteger numerator, final BigInteger denominator) {
|
||||
int i = 1;
|
||||
BigInteger output = BigInteger.ZERO;
|
||||
BigInteger numeratorAccumulator = factor.multiply(denominator);
|
||||
while (numeratorAccumulator.signum() > 0) {
|
||||
output = output.add(numeratorAccumulator);
|
||||
numeratorAccumulator =
|
||||
(numeratorAccumulator.multiply(numerator))
|
||||
.divide(denominator.multiply(BigInteger.valueOf(i)));
|
||||
++i;
|
||||
}
|
||||
return output.divide(denominator);
|
||||
}
|
||||
|
||||
static TransactionPriceCalculator dataGas(
|
||||
final int minDataGasPrice,
|
||||
final int dataGasPriceUpdateFraction,
|
||||
final DataGas excessDataGas) {
|
||||
return ((transaction, baseFee) -> {
|
||||
final var dataGasPrice =
|
||||
Wei.of(
|
||||
fakeExponential(
|
||||
BigInteger.valueOf(minDataGasPrice),
|
||||
excessDataGas.toBigInteger(),
|
||||
BigInteger.valueOf(dataGasPriceUpdateFraction)));
|
||||
return dataGasPrice;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessDataGasCalculator.calculateExcessDataGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.DataGas;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
|
||||
@@ -112,14 +113,19 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain);
|
||||
final Address miningBeneficiary =
|
||||
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
|
||||
final Wei dataGasPrice =
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPrice(
|
||||
blockchain
|
||||
.getBlockHeader(blockHeader.getParentHash())
|
||||
.flatMap(BlockHeader::getExcessDataGas)
|
||||
.orElse(DataGas.ZERO));
|
||||
|
||||
Optional<BlockHeader> maybeParentHeader =
|
||||
blockchain.getBlockHeader(blockHeader.getParentHash());
|
||||
|
||||
Wei dataGasPrice =
|
||||
maybeParentHeader
|
||||
.map(
|
||||
(parentHeader) ->
|
||||
protocolSpec
|
||||
.getFeeMarket()
|
||||
.dataPricePerGas(
|
||||
calculateExcessDataGasForParent(protocolSpec, parentHeader)))
|
||||
.orElse(Wei.ZERO);
|
||||
|
||||
final TransactionProcessingResult result =
|
||||
transactionProcessor.processTransaction(
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
|
||||
|
||||
public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCalculator {
|
||||
private static final long MAX_DATA_GAS_PER_BLOCK = 1 << 19;
|
||||
private static final long MAX_DATA_GAS_PER_BLOCK = 786432L;
|
||||
|
||||
public CancunTargetingGasLimitCalculator(
|
||||
final long londonForkBlock, final BaseFeeMarket feeMarket) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class DefaultProtocolSchedule implements ProtocolSchedule {
|
||||
|
||||
@VisibleForTesting
|
||||
protected NavigableSet<ScheduledProtocolSpec> protocolSpecs =
|
||||
new TreeSet<>(Comparator.comparing(ScheduledProtocolSpec::milestone).reversed());
|
||||
new TreeSet<>(Comparator.comparing(ScheduledProtocolSpec::fork).reversed());
|
||||
|
||||
private final Optional<BigInteger> chainId;
|
||||
|
||||
@@ -54,12 +54,23 @@ public class DefaultProtocolSchedule implements ProtocolSchedule {
|
||||
this.protocolSpecs = protocolSchedule.protocolSpecs;
|
||||
}
|
||||
|
||||
public ScheduledProtocolSpec specScheduledForBlock(final ProcessableBlockHeader blockHeader) {
|
||||
return protocolSpecs.stream()
|
||||
.filter(s -> s.isOnOrAfterMilestoneBoundary(blockHeader))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
"No protocol spec found for block " + blockHeader.getNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSpec getByBlockHeader(final ProcessableBlockHeader blockHeader) {
|
||||
checkArgument(
|
||||
!protocolSpecs.isEmpty(), "At least 1 milestone must be provided to the protocol schedule");
|
||||
checkArgument(
|
||||
protocolSpecs.last().milestone() == 0, "There must be a milestone starting from block 0");
|
||||
protocolSpecs.last().fork().milestone() == 0,
|
||||
"There must be a milestone starting from block 0");
|
||||
|
||||
// protocolSpecs is sorted in descending block order, so the first one we find that's lower than
|
||||
// the requested level will be the most appropriate spec
|
||||
@@ -79,8 +90,8 @@ public class DefaultProtocolSchedule implements ProtocolSchedule {
|
||||
@Override
|
||||
public String listMilestones() {
|
||||
return protocolSpecs.stream()
|
||||
.sorted(Comparator.comparing(ScheduledProtocolSpec::milestone))
|
||||
.map(scheduledSpec -> scheduledSpec.spec().getName() + ": " + scheduledSpec.milestone())
|
||||
.sorted(Comparator.comparing(ScheduledProtocolSpec::fork))
|
||||
.map(scheduledSpec -> scheduledSpec.fork().toString())
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
@@ -110,6 +121,15 @@ public class DefaultProtocolSchedule implements ProtocolSchedule {
|
||||
return this.protocolSpecs.stream().anyMatch(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ScheduledProtocolSpec.Hardfork> hardforkFor(
|
||||
final Predicate<ScheduledProtocolSpec> predicate) {
|
||||
return this.protocolSpecs.stream()
|
||||
.filter(predicate)
|
||||
.findFirst()
|
||||
.map(ScheduledProtocolSpec::fork);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissionTransactionFilter(
|
||||
final PermissionTransactionFilter permissionTransactionFilter) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.BaseFeeMarket
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.CalculatedDifficultyValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ConstantFieldValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ConstantOmmersHashRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.DataGasValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ExtraDataMaxLengthValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasLimitRangeAndDeltaValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValidationRule;
|
||||
@@ -32,6 +33,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.NoNonceRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ProofOfWorkValidationRule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
|
||||
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -195,4 +197,9 @@ public final class MainnetBlockHeaderValidator {
|
||||
.addRule(new NoDifficultyRule())
|
||||
.addRule(new IncrementalTimestampRule());
|
||||
}
|
||||
|
||||
public static BlockHeaderValidator.Builder cancunBlockHeaderValidator(final FeeMarket feeMarket) {
|
||||
return mergeBlockHeaderValidator(feeMarket)
|
||||
.addRule(new DataGasValidationRule(new CancunGasCalculator()));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user