mirror of https://github.com/hyperledger/besu
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>pull/5727/head
parent
d923b8eb8f
commit
1c099e8970
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 |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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"); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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())); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue