mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 23:38:08 -05:00
EIP-2930: Access List Transactions (#1712)
https://eips.ethereum.org/EIPS/eip-2930 Signed-off-by: Ratan Rai Sur <ratan.r.sur@gmail.com>
This commit is contained in:
@@ -80,7 +80,6 @@ import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
// todo request lucas look at this pr
|
||||
public class PrivacyReorgTest {
|
||||
@Rule public final TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
@@ -93,9 +92,9 @@ public class PrivacyReorgTest {
|
||||
Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=");
|
||||
|
||||
private static final String FIRST_BLOCK_WITH_NO_TRANSACTIONS_STATE_ROOT =
|
||||
"0xc9dcaffbebc7edc20839c80e706430a8fa885e8f703bb89f6e95633cc2b05d4d";
|
||||
"0xe368938a01d983e331eb0e4ea61224726d06075c1ad525569b369f664067ff26";
|
||||
private static final String FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT =
|
||||
"0x66fbc6ad12ef1da78740093ea2b3362e773e510e27d8f88b68c27bfc1f4d58c8";
|
||||
"0x9c88988f9602184efc538cf1c2f482a6b8757ff918d234602884dc8e3b983edd";
|
||||
private static final String BLOCK_WITH_SINGLE_TRANSACTION_RECEIPTS_ROOT =
|
||||
"0xc8267b3f9ed36df3ff8adb51a6d030716f23eeb50270e7fce8d9822ffa7f0461";
|
||||
private static final String STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE =
|
||||
@@ -338,7 +337,7 @@ public class PrivacyReorgTest {
|
||||
privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE);
|
||||
|
||||
final String secondForkBlockStateRoot =
|
||||
"0x57ccc80f4e50d2e669d82aefa7d3bbe763cf47df27665af14c90b2f8641953f5";
|
||||
"0x2c37a360a700c614b10c980138f64be9ad66fc4a14cd5145199cd0d8ec43d51d";
|
||||
final Block secondForkBlock =
|
||||
gen.block(
|
||||
getBlockOptionsNoTransactionWithDifficulty(
|
||||
@@ -368,8 +367,7 @@ public class PrivacyReorgTest {
|
||||
appendBlock(besuController, blockchain, protocolContext, thirdForkBlock);
|
||||
|
||||
// Check that the private state did change after reorg
|
||||
assertPrivateStateRoot(
|
||||
privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMPTY_STATE);
|
||||
assertPrivateStateRoot(privateStateRootResolver, blockchain, EMPTY_ROOT_HASH);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -299,11 +299,22 @@ public class BesuEventsImplTest {
|
||||
blockchain.appendBlock(block, gen.receipts(block));
|
||||
assertThat(result.get()).isNull();
|
||||
|
||||
final var reorgBlock =
|
||||
final var forkBlock =
|
||||
gen.block(
|
||||
new BlockDataGenerator.BlockOptions()
|
||||
.setParentHash(blockchain.getGenesisBlock().getHash())
|
||||
.setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 2));
|
||||
.setDifficulty(block.getHeader().getDifficulty().subtract(1))
|
||||
.setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1));
|
||||
blockchain.appendBlock(forkBlock, gen.receipts(forkBlock));
|
||||
assertThat(result.get()).isNull();
|
||||
|
||||
final var reorgBlock =
|
||||
gen.block(
|
||||
new BlockDataGenerator.BlockOptions()
|
||||
.setParentHash(forkBlock.getHash())
|
||||
.setDifficulty(Difficulty.of(10000000))
|
||||
.setBlockNumber(forkBlock.getHeader().getNumber() + 1));
|
||||
|
||||
List<TransactionReceipt> transactionReceipts = gen.receipts(reorgBlock);
|
||||
blockchain.appendBlock(reorgBlock, transactionReceipts);
|
||||
assertThat(result.get()).isNotNull();
|
||||
|
||||
@@ -400,7 +400,7 @@ public class BlockchainQueriesTest {
|
||||
|
||||
@Test
|
||||
public void getOmmerByBlockHashAndIndexShouldReturnExpectedOmmerHeader() {
|
||||
final BlockchainWithData data = setupBlockchain(3);
|
||||
final BlockchainWithData data = setupBlockchain(4);
|
||||
final BlockchainQueries queries = data.blockchainQueries;
|
||||
final Block targetBlock = data.blockData.get(data.blockData.size() - 1).block;
|
||||
final BlockHeader ommerBlockHeader = targetBlock.getBody().getOmmers().get(0);
|
||||
@@ -448,7 +448,7 @@ public class BlockchainQueriesTest {
|
||||
|
||||
@Test
|
||||
public void getOmmerByBlockNumberAndIndexShouldReturnExpectedOmmerHeader() {
|
||||
final BlockchainWithData data = setupBlockchain(3);
|
||||
final BlockchainWithData data = setupBlockchain(4);
|
||||
final BlockchainQueries queries = data.blockchainQueries;
|
||||
final Block targetBlock = data.blockData.get(data.blockData.size() - 1).block;
|
||||
final List<BlockHeader> ommers = targetBlock.getBody().getOmmers();
|
||||
@@ -485,7 +485,7 @@ public class BlockchainQueriesTest {
|
||||
|
||||
@Test
|
||||
public void getLatestBlockOmmerByIndexShouldReturnExpectedOmmerHeader() {
|
||||
final BlockchainWithData data = setupBlockchain(3);
|
||||
final BlockchainWithData data = setupBlockchain(4);
|
||||
final BlockchainQueries queries = data.blockchainQueries;
|
||||
final Block targetBlock = data.blockData.get(data.blockData.size() - 1).block;
|
||||
final BlockHeader ommerBlockHeader = targetBlock.getBody().getOmmers().get(0);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright ConsenSys AG.
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* * the License. You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* * specific language governing permissions and limitations under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.ethereum.core;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class AccessList extends ArrayList<Map.Entry<Address, List<Bytes32>>> {
|
||||
|
||||
public AccessList(final List<Map.Entry<Address, List<Bytes32>>> accessList) {
|
||||
super(accessList);
|
||||
}
|
||||
|
||||
public static final AccessList EMPTY = new AccessList(emptyList());
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.ethereum.core;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class GasAndAccessedState {
|
||||
final Gas gas;
|
||||
final Set<Address> accessListAddressSet;
|
||||
final Multimap<Address, Bytes32> accessListStorageByAddress;
|
||||
|
||||
public GasAndAccessedState(
|
||||
final Gas gas,
|
||||
final Set<Address> accessListAddressSet,
|
||||
final Multimap<Address, Bytes32> accessedStorage) {
|
||||
this.gas = gas;
|
||||
this.accessListAddressSet = accessListAddressSet;
|
||||
this.accessListStorageByAddress = accessedStorage;
|
||||
}
|
||||
|
||||
public GasAndAccessedState(final Gas gas) {
|
||||
this.gas = gas;
|
||||
this.accessListAddressSet = emptySet();
|
||||
this.accessListStorageByAddress = HashMultimap.create();
|
||||
}
|
||||
|
||||
public Gas getGas() {
|
||||
return gas;
|
||||
}
|
||||
|
||||
public Set<Address> getAccessListAddressSet() {
|
||||
return accessListAddressSet;
|
||||
}
|
||||
|
||||
public Multimap<Address, Bytes32> getAccessListStorageByAddress() {
|
||||
return accessListStorageByAddress;
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,10 @@ public class LogsBloomFilter extends DelegatingBytes {
|
||||
data.size());
|
||||
}
|
||||
|
||||
public LogsBloomFilter(final String logsBloomHexString) {
|
||||
this(Bytes.fromHexString(logsBloomHexString));
|
||||
}
|
||||
|
||||
public static LogsBloomFilter fromHexString(final String hexString) {
|
||||
return new LogsBloomFilter(Bytes.fromHexString(hexString));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.core;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
||||
|
||||
import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.crypto.SECP256K1.PublicKey;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionRLPDecoder;
|
||||
@@ -73,6 +72,8 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
|
||||
private final Bytes payload;
|
||||
|
||||
private final AccessList accessList;
|
||||
|
||||
private final Optional<BigInteger> chainId;
|
||||
|
||||
private final Optional<BigInteger> v;
|
||||
@@ -111,6 +112,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
* @param value the value being transferred to the recipient
|
||||
* @param signature the signature
|
||||
* @param payload the payload
|
||||
* @param accessList the list of addresses/storage slots this transaction intends to preload
|
||||
* @param sender the transaction sender
|
||||
* @param chainId the chain id to apply the transaction to
|
||||
* @param v the v value. This is only passed in directly for GoQuorum private transactions
|
||||
@@ -132,6 +134,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
final Wei value,
|
||||
final SECP256K1.Signature signature,
|
||||
final Bytes payload,
|
||||
final AccessList accessList,
|
||||
final Address sender,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<BigInteger> v) {
|
||||
@@ -149,6 +152,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
this.value = value;
|
||||
this.signature = signature;
|
||||
this.payload = payload;
|
||||
this.accessList = accessList;
|
||||
this.sender = sender;
|
||||
this.chainId = chainId;
|
||||
this.v = v;
|
||||
@@ -178,6 +182,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
value,
|
||||
signature,
|
||||
payload,
|
||||
AccessList.EMPTY,
|
||||
sender,
|
||||
chainId,
|
||||
v);
|
||||
@@ -368,6 +373,10 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
return getTo().isPresent() ? Optional.of(payload) : Optional.empty();
|
||||
}
|
||||
|
||||
public AccessList getAccessList() {
|
||||
return accessList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction chain id (if it exists)
|
||||
*
|
||||
@@ -414,14 +423,16 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
if (hashNoSignature == null) {
|
||||
hashNoSignature =
|
||||
computeSenderRecoveryHash(
|
||||
transactionType,
|
||||
nonce,
|
||||
gasPrice,
|
||||
gasPremium,
|
||||
feeCap,
|
||||
gasLimit,
|
||||
to.orElse(null),
|
||||
to,
|
||||
value,
|
||||
payload,
|
||||
accessList,
|
||||
chainId);
|
||||
}
|
||||
return hashNoSignature;
|
||||
@@ -536,36 +547,121 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
}
|
||||
|
||||
private static Bytes32 computeSenderRecoveryHash(
|
||||
final TransactionType transactionType,
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final Wei gasPremium,
|
||||
final Wei feeCap,
|
||||
final long gasLimit,
|
||||
final Address to,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final AccessList accessList,
|
||||
final Optional<BigInteger> chainId) {
|
||||
final Bytes preimage;
|
||||
switch (transactionType) {
|
||||
case FRONTIER:
|
||||
preimage = frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId);
|
||||
break;
|
||||
case EIP1559:
|
||||
preimage =
|
||||
eip1559Preimage(
|
||||
nonce, gasPrice, gasPremium, feeCap, gasLimit, to, value, payload, chainId);
|
||||
break;
|
||||
case ACCESS_LIST:
|
||||
preimage =
|
||||
accessListPreimage(
|
||||
transactionType,
|
||||
nonce,
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
to,
|
||||
value,
|
||||
payload,
|
||||
accessList,
|
||||
chainId);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"Developer error. Didn't specify signing hash preimage computation");
|
||||
}
|
||||
return keccak256(preimage);
|
||||
}
|
||||
|
||||
private static Bytes frontierPreimage(
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final long gasLimit,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final Optional<BigInteger> chainId) {
|
||||
return keccak256(
|
||||
RLP.encode(
|
||||
out -> {
|
||||
out.startList();
|
||||
out.writeLongScalar(nonce);
|
||||
out.writeUInt256Scalar(gasPrice);
|
||||
out.writeLongScalar(gasLimit);
|
||||
out.writeBytes(to == null ? Bytes.EMPTY : to);
|
||||
out.writeUInt256Scalar(value);
|
||||
out.writeBytes(payload);
|
||||
if (ExperimentalEIPs.eip1559Enabled && gasPremium != null && feeCap != null) {
|
||||
out.writeUInt256Scalar(gasPremium);
|
||||
out.writeUInt256Scalar(feeCap);
|
||||
}
|
||||
if (chainId.isPresent()) {
|
||||
out.writeBigIntegerScalar(chainId.get());
|
||||
out.writeUInt256Scalar(UInt256.ZERO);
|
||||
out.writeUInt256Scalar(UInt256.ZERO);
|
||||
}
|
||||
out.endList();
|
||||
}));
|
||||
return RLP.encode(
|
||||
rlpOutput -> {
|
||||
rlpOutput.startList();
|
||||
rlpOutput.writeLongScalar(nonce);
|
||||
rlpOutput.writeUInt256Scalar(gasPrice);
|
||||
rlpOutput.writeLongScalar(gasLimit);
|
||||
rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY));
|
||||
rlpOutput.writeUInt256Scalar(value);
|
||||
rlpOutput.writeBytes(payload);
|
||||
if (chainId.isPresent()) {
|
||||
rlpOutput.writeBigIntegerScalar(chainId.get());
|
||||
rlpOutput.writeUInt256Scalar(UInt256.ZERO);
|
||||
rlpOutput.writeUInt256Scalar(UInt256.ZERO);
|
||||
}
|
||||
rlpOutput.endList();
|
||||
});
|
||||
}
|
||||
|
||||
private static Bytes eip1559Preimage(
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final Wei gasPremium,
|
||||
final Wei feeCap,
|
||||
final long gasLimit,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final Optional<BigInteger> chainId) {
|
||||
return RLP.encode(
|
||||
rlpOutput -> {
|
||||
rlpOutput.startList();
|
||||
rlpOutput.writeLongScalar(nonce);
|
||||
rlpOutput.writeUInt256Scalar(gasPrice);
|
||||
rlpOutput.writeLongScalar(gasLimit);
|
||||
rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY));
|
||||
rlpOutput.writeUInt256Scalar(value);
|
||||
rlpOutput.writeBytes(payload);
|
||||
rlpOutput.writeUInt256Scalar(gasPremium);
|
||||
rlpOutput.writeUInt256Scalar(feeCap);
|
||||
if (chainId.isPresent()) {
|
||||
rlpOutput.writeBigIntegerScalar(chainId.get());
|
||||
rlpOutput.writeUInt256Scalar(UInt256.ZERO);
|
||||
rlpOutput.writeUInt256Scalar(UInt256.ZERO);
|
||||
}
|
||||
rlpOutput.endList();
|
||||
});
|
||||
}
|
||||
|
||||
private static Bytes accessListPreimage(
|
||||
final TransactionType transactionType,
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final long gasLimit,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final AccessList accessList,
|
||||
final Optional<BigInteger> chainId) {
|
||||
return RLP.encode(
|
||||
rlpOutput -> {
|
||||
rlpOutput.startList();
|
||||
rlpOutput.writeIntScalar(transactionType.getSerializedType());
|
||||
TransactionRLPEncoder.encodeAccessListInner(
|
||||
chainId, nonce, gasPrice, gasLimit, to, value, payload, accessList, rlpOutput);
|
||||
rlpOutput.endList();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -574,17 +670,17 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
return false;
|
||||
}
|
||||
final Transaction that = (Transaction) other;
|
||||
return this.chainId.equals(that.chainId)
|
||||
&& this.gasLimit == that.gasLimit
|
||||
return Objects.equals(this.chainId, that.chainId)
|
||||
&& Objects.equals(this.gasLimit, that.gasLimit)
|
||||
&& Objects.equals(this.gasPrice, that.gasPrice)
|
||||
&& Objects.equals(this.gasPremium, that.gasPremium)
|
||||
&& Objects.equals(this.feeCap, that.feeCap)
|
||||
&& this.nonce == that.nonce
|
||||
&& this.payload.equals(that.payload)
|
||||
&& this.signature.equals(that.signature)
|
||||
&& this.to.equals(that.to)
|
||||
&& this.value.equals(that.value)
|
||||
&& this.v.equals(that.v);
|
||||
&& Objects.equals(this.nonce, that.nonce)
|
||||
&& Objects.equals(this.payload, that.payload)
|
||||
&& Objects.equals(this.signature, that.signature)
|
||||
&& Objects.equals(this.to, that.to)
|
||||
&& Objects.equals(this.value, that.value)
|
||||
&& Objects.equals(this.getV(), that.getV());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -610,7 +706,10 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
sb.append("sig=").append(getSignature()).append(", ");
|
||||
if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", ");
|
||||
if (v.isPresent()) sb.append("v=").append(v.get()).append(", ");
|
||||
sb.append("payload=").append(getPayload());
|
||||
sb.append("payload=").append(getPayload()).append(", ");
|
||||
if (transactionType.equals(TransactionType.ACCESS_LIST)) {
|
||||
sb.append("accessList=").append(accessList);
|
||||
}
|
||||
return sb.append("}").toString();
|
||||
}
|
||||
|
||||
@@ -635,7 +734,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
|
||||
protected long gasLimit = -1L;
|
||||
|
||||
protected Address to;
|
||||
protected Optional<Address> to = Optional.empty();
|
||||
|
||||
protected Wei value;
|
||||
|
||||
@@ -643,12 +742,19 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
|
||||
protected Bytes payload;
|
||||
|
||||
protected AccessList accessList = AccessList.EMPTY;
|
||||
|
||||
protected Address sender;
|
||||
|
||||
protected Optional<BigInteger> chainId = Optional.empty();
|
||||
|
||||
protected Optional<BigInteger> v = Optional.empty();
|
||||
|
||||
public Builder type(final TransactionType transactionType) {
|
||||
this.transactionType = transactionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder chainId(final BigInteger chainId) {
|
||||
this.chainId = Optional.of(chainId);
|
||||
return this;
|
||||
@@ -690,7 +796,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
}
|
||||
|
||||
public Builder to(final Address to) {
|
||||
this.to = to;
|
||||
this.to = Optional.ofNullable(to);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -699,6 +805,11 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder accessList(final AccessList accessList) {
|
||||
this.accessList = accessList;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sender(final Address sender) {
|
||||
this.sender = sender;
|
||||
return this;
|
||||
@@ -709,8 +820,14 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(final TransactionType transactionType) {
|
||||
this.transactionType = transactionType;
|
||||
public Builder guessType() {
|
||||
if (!accessList.isEmpty()) {
|
||||
transactionType = TransactionType.ACCESS_LIST;
|
||||
} else if (gasPremium != null || feeCap != null) {
|
||||
transactionType = TransactionType.EIP1559;
|
||||
} else {
|
||||
transactionType = TransactionType.FRONTIER;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -722,10 +839,11 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
gasPremium,
|
||||
feeCap,
|
||||
gasLimit,
|
||||
Optional.ofNullable(to),
|
||||
to,
|
||||
value,
|
||||
signature,
|
||||
payload,
|
||||
accessList,
|
||||
sender,
|
||||
chainId,
|
||||
v);
|
||||
@@ -740,19 +858,20 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
|
||||
}
|
||||
|
||||
SECP256K1.Signature computeSignature(final SECP256K1.KeyPair keys) {
|
||||
final Bytes32 hash =
|
||||
return SECP256K1.sign(
|
||||
computeSenderRecoveryHash(
|
||||
nonce, gasPrice, gasPremium, feeCap, gasLimit, to, value, payload, chainId);
|
||||
return SECP256K1.sign(hash, keys);
|
||||
}
|
||||
|
||||
public Builder guessType() {
|
||||
if (gasPremium != null || feeCap != null) {
|
||||
transactionType = TransactionType.EIP1559;
|
||||
} else {
|
||||
transactionType = TransactionType.FRONTIER;
|
||||
}
|
||||
return this;
|
||||
transactionType,
|
||||
nonce,
|
||||
gasPrice,
|
||||
gasPremium,
|
||||
feeCap,
|
||||
gasLimit,
|
||||
to,
|
||||
value,
|
||||
payload,
|
||||
accessList,
|
||||
chainId),
|
||||
keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,38 +174,36 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
|
||||
}
|
||||
|
||||
private void writeTo(final RLPOutput rlpOutput, final boolean withRevertReason) {
|
||||
final Bytes receiptBytes =
|
||||
RLP.encode(
|
||||
out -> {
|
||||
out.startList();
|
||||
|
||||
// Determine whether it's a state root-encoded transaction receipt
|
||||
// or is a status code-encoded transaction receipt.
|
||||
if (stateRoot != null) {
|
||||
out.writeBytes(stateRoot);
|
||||
} else {
|
||||
out.writeLongScalar(status);
|
||||
}
|
||||
out.writeLongScalar(cumulativeGasUsed);
|
||||
out.writeBytes(bloomFilter);
|
||||
out.writeList(logs, Log::writeTo);
|
||||
if (withRevertReason && revertReason.isPresent()) {
|
||||
out.writeBytes(revertReason.get());
|
||||
}
|
||||
out.endList();
|
||||
});
|
||||
|
||||
switch (transactionType) {
|
||||
case FRONTIER:
|
||||
case EIP1559:
|
||||
rlpOutput.writeRaw(receiptBytes);
|
||||
break;
|
||||
default:
|
||||
rlpOutput.writeBytes(
|
||||
Bytes.concatenate(Bytes.of((byte) transactionType.getSerializedType()), receiptBytes));
|
||||
if (transactionType.equals(TransactionType.FRONTIER)) {
|
||||
writeToForReceiptTrie(rlpOutput, withRevertReason);
|
||||
} else {
|
||||
rlpOutput.writeBytes(RLP.encode(out -> writeToForReceiptTrie(out, withRevertReason)));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToForReceiptTrie(final RLPOutput rlpOutput, final boolean withRevertReason) {
|
||||
if (!transactionType.equals(TransactionType.FRONTIER)) {
|
||||
rlpOutput.writeIntScalar(transactionType.getSerializedType());
|
||||
}
|
||||
|
||||
rlpOutput.startList();
|
||||
|
||||
// Determine whether it's a state root-encoded transaction receipt
|
||||
// or is a status code-encoded transaction receipt.
|
||||
if (stateRoot != null) {
|
||||
rlpOutput.writeBytes(stateRoot);
|
||||
} else {
|
||||
rlpOutput.writeLongScalar(status);
|
||||
}
|
||||
rlpOutput.writeLongScalar(cumulativeGasUsed);
|
||||
rlpOutput.writeBytes(bloomFilter);
|
||||
rlpOutput.writeList(logs, Log::writeTo);
|
||||
if (withRevertReason && revertReason.isPresent()) {
|
||||
rlpOutput.writeBytes(revertReason.get());
|
||||
}
|
||||
rlpOutput.endList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transaction receipt for the given RLP
|
||||
*
|
||||
@@ -227,11 +225,10 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
|
||||
final RLPInput rlpInput, final boolean revertReasonAllowed) {
|
||||
RLPInput input = rlpInput;
|
||||
TransactionType transactionType = TransactionType.FRONTIER;
|
||||
if (!input.nextIsList()) {
|
||||
final Bytes bytes = input.readBytes();
|
||||
transactionType = TransactionType.of(bytes.get(0));
|
||||
input = new BytesValueRLPInput(bytes.slice(1), false);
|
||||
System.out.println(bytes.toHexString());
|
||||
if (!rlpInput.nextIsList()) {
|
||||
final Bytes typedTransactionReceiptBytes = input.readBytes();
|
||||
transactionType = TransactionType.of(typedTransactionReceiptBytes.get(0));
|
||||
input = new BytesValueRLPInput(typedTransactionReceiptBytes.slice(1), false);
|
||||
}
|
||||
|
||||
input.enterList();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.core.encoding;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.hyperledger.besu.ethereum.core.Transaction.GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MAX;
|
||||
import static org.hyperledger.besu.ethereum.core.Transaction.GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN;
|
||||
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_PROTECTED_V_BASE;
|
||||
@@ -24,6 +25,7 @@ import static org.hyperledger.besu.ethereum.core.Transaction.TWO;
|
||||
|
||||
import org.hyperledger.besu.config.GoQuorumOptions;
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.ethereum.core.AccessList;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
@@ -32,10 +34,14 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.plugin.data.TransactionType;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class TransactionRLPDecoder {
|
||||
|
||||
@@ -46,7 +52,11 @@ public class TransactionRLPDecoder {
|
||||
|
||||
private static final ImmutableMap<TransactionType, TransactionRLPDecoder.Decoder>
|
||||
TYPED_TRANSACTION_DECODERS =
|
||||
ImmutableMap.of(TransactionType.EIP1559, TransactionRLPDecoder::decodeEIP1559);
|
||||
ImmutableMap.of(
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionRLPDecoder::decodeAccessList,
|
||||
TransactionType.EIP1559,
|
||||
TransactionRLPDecoder::decodeEIP1559);
|
||||
|
||||
public static Transaction decode(final RLPInput rlpInput) {
|
||||
if (rlpInput.nextIsList()) {
|
||||
@@ -56,13 +66,10 @@ public class TransactionRLPDecoder {
|
||||
final TransactionType transactionType =
|
||||
TransactionType.of(typedTransactionBytes.get(0) & 0xff);
|
||||
final Decoder decoder =
|
||||
Optional.ofNullable(TYPED_TRANSACTION_DECODERS.get(transactionType))
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
String.format(
|
||||
"Developer Error. A supported transaction type %s has no associated decoding logic",
|
||||
transactionType)));
|
||||
checkNotNull(
|
||||
TYPED_TRANSACTION_DECODERS.get(transactionType),
|
||||
"Developer Error. A supported transaction type %s has no associated decoding logic",
|
||||
transactionType);
|
||||
return decoder.decode(RLP.input(typedTransactionBytes.slice(1)));
|
||||
}
|
||||
}
|
||||
@@ -104,6 +111,45 @@ public class TransactionRLPDecoder {
|
||||
return builder.signature(signature).build();
|
||||
}
|
||||
|
||||
private static Transaction decodeAccessList(final RLPInput rlpInput) {
|
||||
rlpInput.enterList();
|
||||
final Transaction.Builder preSignatureTransactionBuilder =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.ACCESS_LIST)
|
||||
.chainId(BigInteger.valueOf(rlpInput.readLongScalar()))
|
||||
.nonce(rlpInput.readLongScalar())
|
||||
.gasPrice(Wei.of(rlpInput.readUInt256Scalar()))
|
||||
.gasLimit(rlpInput.readLongScalar())
|
||||
.to(
|
||||
rlpInput.readBytes(
|
||||
addressBytes -> addressBytes.size() == 0 ? null : Address.wrap(addressBytes)))
|
||||
.value(Wei.of(rlpInput.readUInt256Scalar()))
|
||||
.payload(rlpInput.readBytes())
|
||||
.accessList(
|
||||
new AccessList(
|
||||
rlpInput.readList(
|
||||
accessListEntryRLPInput -> {
|
||||
accessListEntryRLPInput.enterList();
|
||||
final Map.Entry<Address, List<Bytes32>> accessListEntry =
|
||||
new AbstractMap.SimpleEntry<>(
|
||||
Address.wrap(accessListEntryRLPInput.readBytes()),
|
||||
accessListEntryRLPInput.readList(RLPInput::readBytes32));
|
||||
accessListEntryRLPInput.leaveList();
|
||||
return accessListEntry;
|
||||
})));
|
||||
final byte recId = (byte) rlpInput.readIntScalar();
|
||||
final Transaction transaction =
|
||||
preSignatureTransactionBuilder
|
||||
.signature(
|
||||
SECP256K1.Signature.create(
|
||||
rlpInput.readUInt256Scalar().toBytes().toUnsignedBigInteger(),
|
||||
rlpInput.readUInt256Scalar().toBytes().toUnsignedBigInteger(),
|
||||
recId))
|
||||
.build();
|
||||
rlpInput.leaveList();
|
||||
return transaction;
|
||||
}
|
||||
|
||||
static Transaction decodeEIP1559(final RLPInput input) {
|
||||
input.enterList();
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.core.encoding;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.AccessList;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
@@ -23,6 +25,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
import org.hyperledger.besu.plugin.data.Quantity;
|
||||
import org.hyperledger.besu.plugin.data.TransactionType;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -36,9 +39,25 @@ public class TransactionRLPEncoder {
|
||||
}
|
||||
|
||||
private static final ImmutableMap<TransactionType, Encoder> TYPED_TRANSACTION_ENCODERS =
|
||||
ImmutableMap.of(TransactionType.EIP1559, TransactionRLPEncoder::encodeEIP1559);
|
||||
ImmutableMap.of(
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionRLPEncoder::encodeAccessList,
|
||||
TransactionType.EIP1559,
|
||||
TransactionRLPEncoder::encodeEIP1559);
|
||||
|
||||
public static void encode(final Transaction transaction, final RLPOutput rlpOutput) {
|
||||
final TransactionType transactionType =
|
||||
checkNotNull(
|
||||
transaction.getType(), "Transaction type for %s was not specified.", transaction);
|
||||
if (TransactionType.FRONTIER.equals(transactionType)) {
|
||||
encodeFrontier(transaction, rlpOutput);
|
||||
} else {
|
||||
rlpOutput.writeBytes(RLP.encode(output -> encodeForTransactionTrie(transaction, output)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void encodeForTransactionTrie(
|
||||
final Transaction transaction, final RLPOutput rlpOutput) {
|
||||
final TransactionType transactionType =
|
||||
checkNotNull(
|
||||
transaction.getType(), "Transaction type for %s was not specified.", transaction);
|
||||
@@ -46,17 +65,12 @@ public class TransactionRLPEncoder {
|
||||
encodeFrontier(transaction, rlpOutput);
|
||||
} else {
|
||||
final Encoder encoder =
|
||||
Optional.ofNullable(TYPED_TRANSACTION_ENCODERS.get(transactionType))
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
String.format(
|
||||
"Developer Error. A supported transaction type %s has no associated encoding logic",
|
||||
transactionType)));
|
||||
rlpOutput.writeBytes(
|
||||
Bytes.concatenate(
|
||||
Bytes.of((byte) transactionType.getSerializedType()),
|
||||
RLP.encode(output -> encoder.encode(transaction, output))));
|
||||
checkNotNull(
|
||||
TYPED_TRANSACTION_ENCODERS.get(transactionType),
|
||||
"Developer Error. A supported transaction type %s has no associated encoding logic",
|
||||
transactionType);
|
||||
rlpOutput.writeIntScalar(transactionType.getSerializedType());
|
||||
encoder.encode(transaction, rlpOutput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +82,79 @@ public class TransactionRLPEncoder {
|
||||
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
|
||||
out.writeUInt256Scalar(transaction.getValue());
|
||||
out.writeBytes(transaction.getPayload());
|
||||
writeSignature(transaction, out);
|
||||
writeSignatureAndRecoveryId(transaction, out);
|
||||
out.endList();
|
||||
}
|
||||
|
||||
static void encodeAccessList(final Transaction transaction, final RLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
encodeAccessListInner(
|
||||
transaction.getChainId(),
|
||||
transaction.getNonce(),
|
||||
transaction.getGasPrice(),
|
||||
transaction.getGasLimit(),
|
||||
transaction.getTo(),
|
||||
transaction.getValue(),
|
||||
transaction.getPayload(),
|
||||
transaction.getAccessList(),
|
||||
rlpOutput);
|
||||
rlpOutput.writeIntScalar(transaction.getSignature().getRecId());
|
||||
writeSignature(transaction, rlpOutput);
|
||||
rlpOutput.endList();
|
||||
}
|
||||
|
||||
public static void encodeAccessListInner(
|
||||
final Optional<BigInteger> chainId,
|
||||
final long nonce,
|
||||
final Wei gasPrice,
|
||||
final long gasLimit,
|
||||
final Optional<Address> to,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final AccessList accessList,
|
||||
final RLPOutput rlpOutput) {
|
||||
rlpOutput.writeLongScalar(
|
||||
chainId
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
"chainId is required for access list transactions"))
|
||||
.longValue());
|
||||
rlpOutput.writeLongScalar(nonce);
|
||||
rlpOutput.writeUInt256Scalar(gasPrice);
|
||||
rlpOutput.writeLongScalar(gasLimit);
|
||||
rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY));
|
||||
rlpOutput.writeUInt256Scalar(value);
|
||||
rlpOutput.writeBytes(payload);
|
||||
/*
|
||||
Access List encoding should look like this
|
||||
where hex strings represent raw bytes
|
||||
[
|
||||
[
|
||||
"0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
|
||||
[
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000007"
|
||||
]
|
||||
],
|
||||
[
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
[]
|
||||
]
|
||||
] */
|
||||
rlpOutput.writeList(
|
||||
accessList,
|
||||
(accessListEntry, accessListEntryRLPOutput) -> {
|
||||
accessListEntryRLPOutput.startList();
|
||||
rlpOutput.writeBytes(accessListEntry.getKey());
|
||||
rlpOutput.writeList(
|
||||
accessListEntry.getValue(),
|
||||
(storageKeyBytes, storageKeyBytesRLPOutput) ->
|
||||
storageKeyBytesRLPOutput.writeBytes(storageKeyBytes));
|
||||
accessListEntryRLPOutput.endList();
|
||||
});
|
||||
}
|
||||
|
||||
static void encodeEIP1559(final Transaction transaction, final RLPOutput out) {
|
||||
out.startList();
|
||||
out.writeLongScalar(transaction.getNonce());
|
||||
@@ -84,12 +167,17 @@ public class TransactionRLPEncoder {
|
||||
transaction.getGasPremium().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
|
||||
out.writeUInt256Scalar(
|
||||
transaction.getFeeCap().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
|
||||
writeSignature(transaction, out);
|
||||
writeSignatureAndRecoveryId(transaction, out);
|
||||
out.endList();
|
||||
}
|
||||
|
||||
private static void writeSignature(final Transaction transaction, final RLPOutput out) {
|
||||
private static void writeSignatureAndRecoveryId(
|
||||
final Transaction transaction, final RLPOutput out) {
|
||||
out.writeBigIntegerScalar(transaction.getV());
|
||||
writeSignature(transaction, out);
|
||||
}
|
||||
|
||||
private static void writeSignature(final Transaction transaction, final RLPOutput out) {
|
||||
out.writeBigIntegerScalar(transaction.getSignature().getR());
|
||||
out.writeBigIntegerScalar(transaction.getSignature().getS());
|
||||
}
|
||||
|
||||
@@ -16,16 +16,26 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.core.Address.BLS12_MAP_FP2_TO_G2;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.AccessList;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.BigIntegerModularExponentiationPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.vm.MessageFrame;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class BerlinGasCalculator extends IstanbulGasCalculator {
|
||||
@@ -34,6 +44,8 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
|
||||
private static final Gas COLD_SLOAD_COST = Gas.of(2100);
|
||||
private static final Gas COLD_ACCOUNT_ACCESS_COST = Gas.of(2600);
|
||||
private static final Gas WARM_STORAGE_READ_COST = Gas.of(100);
|
||||
private static final Gas ACCESS_LIST_ADDRESS_COST = Gas.of(2400);
|
||||
private static final Gas ACCESS_LIST_STORAGE_COST = Gas.of(1900);
|
||||
|
||||
// redefinitions for EIP-2929
|
||||
private static final Gas SLOAD_GAS = WARM_STORAGE_READ_COST;
|
||||
@@ -60,6 +72,35 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
|
||||
this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GasAndAccessedState transactionIntrinsicGasCostAndAccessedState(
|
||||
final Transaction transaction) {
|
||||
// As per https://eips.ethereum.org/EIPS/eip-2930
|
||||
final AccessList accessList = transaction.getAccessList();
|
||||
|
||||
long accessedStorageCount = 0;
|
||||
final Set<Address> accessedAddresses = new HashSet<>();
|
||||
final Multimap<Address, Bytes32> accessedStorage = HashMultimap.create();
|
||||
|
||||
for (final Map.Entry<Address, List<Bytes32>> accessListEntry : accessList) {
|
||||
final Address address = accessListEntry.getKey();
|
||||
|
||||
accessedAddresses.add(address);
|
||||
for (final Bytes32 storageKeyBytes : accessListEntry.getValue()) {
|
||||
accessedStorage.put(address, storageKeyBytes);
|
||||
++accessedStorageCount;
|
||||
}
|
||||
}
|
||||
|
||||
return new GasAndAccessedState(
|
||||
super.transactionIntrinsicGasCostAndAccessedState(transaction)
|
||||
.getGas()
|
||||
.plus(ACCESS_LIST_ADDRESS_COST.times(accessList.size()))
|
||||
.plus(ACCESS_LIST_STORAGE_COST.times(accessedStorageCount)),
|
||||
accessedAddresses,
|
||||
accessedStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrecompile(final Address address) {
|
||||
final byte[] addressBytes = address.toArrayUnsafe();
|
||||
|
||||
@@ -21,11 +21,13 @@ import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.LogsBloomFilter;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionRLPEncoder;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie;
|
||||
import org.hyperledger.besu.ethereum.trie.SimpleMerklePatriciaTrie;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
@@ -54,9 +56,15 @@ public final class BodyValidation {
|
||||
public static Hash transactionsRoot(final List<Transaction> transactions) {
|
||||
final MerklePatriciaTrie<Bytes, Bytes> trie = trie();
|
||||
|
||||
for (int i = 0; i < transactions.size(); ++i) {
|
||||
trie.put(indexKey(i), RLP.encode(transactions.get(i)::writeTo));
|
||||
}
|
||||
IntStream.range(0, transactions.size())
|
||||
.forEach(
|
||||
i ->
|
||||
trie.put(
|
||||
indexKey(i),
|
||||
RLP.encode(
|
||||
rlpOutput ->
|
||||
TransactionRLPEncoder.encodeForTransactionTrie(
|
||||
transactions.get(i), rlpOutput))));
|
||||
|
||||
return Hash.wrap(trie.getRootHash());
|
||||
}
|
||||
@@ -70,9 +78,13 @@ public final class BodyValidation {
|
||||
public static Hash receiptsRoot(final List<TransactionReceipt> receipts) {
|
||||
final MerklePatriciaTrie<Bytes, Bytes> trie = trie();
|
||||
|
||||
for (int i = 0; i < receipts.size(); ++i) {
|
||||
trie.put(indexKey(i), RLP.encode(receipts.get(i)::writeTo));
|
||||
}
|
||||
IntStream.range(0, receipts.size())
|
||||
.forEach(
|
||||
i ->
|
||||
trie.put(
|
||||
indexKey(i),
|
||||
RLP.encode(
|
||||
rlpOutput -> receipts.get(i).writeToForReceiptTrie(rlpOutput, false))));
|
||||
|
||||
return Hash.wrap(trie.getRootHash());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.vm.GasCalculator;
|
||||
@@ -113,7 +114,8 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
private static final Gas SELF_DESTRUCT_REFUND_AMOUNT = Gas.of(24_000L);
|
||||
|
||||
@Override
|
||||
public Gas transactionIntrinsicGasCost(final Transaction transaction) {
|
||||
public GasAndAccessedState transactionIntrinsicGasCostAndAccessedState(
|
||||
final Transaction transaction) {
|
||||
final Bytes payload = transaction.getPayload();
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
@@ -132,7 +134,7 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
cost = cost.plus(txCreateExtraGasCost());
|
||||
}
|
||||
|
||||
return cost;
|
||||
return new GasAndAccessedState(cost);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -40,7 +41,8 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
private static final Gas NEGATIVE_SSTORE_CLEARS_SCHEDULE = Gas.ZERO.minus(SSTORE_CLEARS_SCHEDULE);
|
||||
|
||||
@Override
|
||||
public Gas transactionIntrinsicGasCost(final Transaction transaction) {
|
||||
public GasAndAccessedState transactionIntrinsicGasCostAndAccessedState(
|
||||
final Transaction transaction) {
|
||||
final Bytes payload = transaction.getPayload();
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
@@ -59,7 +61,7 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
cost = cost.plus(txCreateExtraGasCost());
|
||||
}
|
||||
|
||||
return cost;
|
||||
return new GasAndAccessedState(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -422,6 +422,14 @@ public abstract class MainnetProtocolSpecs {
|
||||
enableRevertReason,
|
||||
quorumCompatibilityMode)
|
||||
.gasCalculator(BerlinGasCalculator::new)
|
||||
.transactionValidatorBuilder(
|
||||
gasCalculator ->
|
||||
new MainnetTransactionValidator(
|
||||
gasCalculator,
|
||||
true,
|
||||
chainId,
|
||||
Set.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST),
|
||||
quorumCompatibilityMode))
|
||||
.evmBuilder(
|
||||
gasCalculator ->
|
||||
MainnetEvmRegistries.berlin(gasCalculator, chainId.orElse(BigInteger.ZERO)))
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.core.AccountState;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.EvmAccount;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.MutableAccount;
|
||||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -288,7 +289,9 @@ public class MainnetTransactionProcessor {
|
||||
previousBalance,
|
||||
sender.getBalance());
|
||||
|
||||
final Gas intrinsicGas = gasCalculator.transactionIntrinsicGasCost(transaction);
|
||||
final GasAndAccessedState gasAndAccessedState =
|
||||
gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction);
|
||||
final Gas intrinsicGas = gasAndAccessedState.getGas();
|
||||
final Gas gasAvailable = Gas.of(transaction.getGasLimit()).minus(intrinsicGas);
|
||||
LOG.trace(
|
||||
"Gas available for execution {} = {} - {} (limit - intrinsic)",
|
||||
@@ -318,6 +321,8 @@ public class MainnetTransactionProcessor {
|
||||
.blockHashLookup(blockHashLookup)
|
||||
.isPersistingPrivateState(isPersistingPrivateState)
|
||||
.transactionHash(transaction.getHash())
|
||||
.accessListWarmAddresses(gasAndAccessedState.getAccessListAddressSet())
|
||||
.accessListWarmStorage(gasAndAccessedState.getAccessListStorageByAddress())
|
||||
.privateMetadataUpdater(privateMetadataUpdater);
|
||||
|
||||
final MessageFrame initialFrame;
|
||||
|
||||
@@ -55,13 +55,27 @@ public class MainnetTransactionValidator {
|
||||
final boolean goQuorumCompatibilityMode) {
|
||||
this(
|
||||
gasCalculator,
|
||||
Optional.empty(),
|
||||
checkSignatureMalleability,
|
||||
chainId,
|
||||
Set.of(TransactionType.FRONTIER),
|
||||
goQuorumCompatibilityMode);
|
||||
}
|
||||
|
||||
public MainnetTransactionValidator(
|
||||
final GasCalculator gasCalculator,
|
||||
final boolean checkSignatureMalleability,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Set<TransactionType> acceptedTransactionTypes,
|
||||
final boolean quorumCompatibilityMode) {
|
||||
this(
|
||||
gasCalculator,
|
||||
Optional.empty(),
|
||||
checkSignatureMalleability,
|
||||
chainId,
|
||||
acceptedTransactionTypes,
|
||||
quorumCompatibilityMode);
|
||||
}
|
||||
|
||||
public MainnetTransactionValidator(
|
||||
final GasCalculator gasCalculator,
|
||||
final Optional<TransactionPriceCalculator> transactionPriceCalculator,
|
||||
@@ -118,7 +132,8 @@ public class MainnetTransactionValidator {
|
||||
}
|
||||
}
|
||||
|
||||
final Gas intrinsicGasCost = gasCalculator.transactionIntrinsicGasCost(transaction);
|
||||
final Gas intrinsicGasCost =
|
||||
gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction).getGas();
|
||||
if (intrinsicGasCost.compareTo(Gas.of(transaction.getGasLimit())) > 0) {
|
||||
return ValidationResult.invalid(
|
||||
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.vm;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractMessageProcessor;
|
||||
@@ -64,7 +65,7 @@ public interface GasCalculator {
|
||||
* @param transaction The transaction
|
||||
* @return the transaction's intrinsic gas cost
|
||||
*/
|
||||
Gas transactionIntrinsicGasCost(Transaction transaction);
|
||||
GasAndAccessedState transactionIntrinsicGasCostAndAccessedState(Transaction transaction);
|
||||
|
||||
// Contract Creation Gas Calculations
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.vm;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
@@ -281,7 +282,9 @@ public class MessageFrame {
|
||||
final PrivateMetadataUpdater privateMetadataUpdater,
|
||||
final Hash transactionHash,
|
||||
final Optional<Bytes> revertReason,
|
||||
final int maxStackSize) {
|
||||
final int maxStackSize,
|
||||
final Set<Address> accessListWarmAddresses,
|
||||
final Multimap<Address, Bytes32> accessListWarmStorage) {
|
||||
this.type = type;
|
||||
this.blockchain = blockchain;
|
||||
this.messageFrameStack = messageFrameStack;
|
||||
@@ -320,10 +323,27 @@ public class MessageFrame {
|
||||
this.transactionHash = transactionHash;
|
||||
this.revertReason = revertReason;
|
||||
|
||||
this.warmedUpAddresses = new HashSet<>();
|
||||
warmedUpAddresses.add(sender);
|
||||
warmedUpAddresses.add(contract);
|
||||
this.warmedUpStorage = HashMultimap.create();
|
||||
this.warmedUpAddresses = new HashSet<>(accessListWarmAddresses);
|
||||
this.warmedUpAddresses.add(sender);
|
||||
this.warmedUpAddresses.add(contract);
|
||||
this.warmedUpStorage = HashMultimap.create(accessListWarmStorage);
|
||||
|
||||
// the warmed up addresses will always be a superset of the address keys in the warmed up
|
||||
// storage so we can do both warm ups in one pass
|
||||
accessListWarmAddresses
|
||||
.parallelStream()
|
||||
.forEach(
|
||||
address ->
|
||||
Optional.ofNullable(worldState.get(address))
|
||||
.ifPresent(
|
||||
account ->
|
||||
warmedUpStorage
|
||||
.get(address)
|
||||
.parallelStream()
|
||||
.forEach(
|
||||
storageKeyBytes ->
|
||||
account.getStorageValue(
|
||||
UInt256.fromBytes(storageKeyBytes)))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1143,6 +1163,8 @@ public class MessageFrame {
|
||||
private Hash transactionHash;
|
||||
private Optional<Bytes> reason = Optional.empty();
|
||||
private ReturnStack returnStack = new ReturnStack();
|
||||
private Set<Address> accessListWarmAddresses = emptySet();
|
||||
private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create();
|
||||
|
||||
public Builder type(final Type type) {
|
||||
this.type = type;
|
||||
@@ -1280,6 +1302,16 @@ public class MessageFrame {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder accessListWarmAddresses(final Set<Address> accessListWarmAddresses) {
|
||||
this.accessListWarmAddresses = accessListWarmAddresses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder accessListWarmStorage(final Multimap<Address, Bytes32> accessListWarmStorage) {
|
||||
this.accessListWarmStorage = accessListWarmStorage;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
checkState(type != null, "Missing message frame type");
|
||||
checkState(blockchain != null, "Missing message frame blockchain");
|
||||
@@ -1335,7 +1367,9 @@ public class MessageFrame {
|
||||
privateMetadataUpdater,
|
||||
transactionHash,
|
||||
reason,
|
||||
maxStackSize);
|
||||
maxStackSize,
|
||||
accessListWarmAddresses,
|
||||
accessListWarmStorage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.stream.Collectors.toUnmodifiableList;
|
||||
import static java.util.stream.Collectors.toUnmodifiableSet;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
@@ -32,11 +33,14 @@ import java.security.Security;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
@@ -349,6 +353,8 @@ public class BlockDataGenerator {
|
||||
return frontierTransaction(payload, to);
|
||||
case EIP1559:
|
||||
return eip1559Transaction(payload, to);
|
||||
case ACCESS_LIST:
|
||||
return accessListTransaction(payload, to);
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
@@ -357,6 +363,33 @@ public class BlockDataGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private Transaction accessListTransaction(final Bytes payload, final Address to) {
|
||||
return Transaction.builder()
|
||||
.type(TransactionType.ACCESS_LIST)
|
||||
.nonce(positiveLong())
|
||||
.gasPrice(Wei.wrap(bytes32()))
|
||||
.gasLimit(positiveLong())
|
||||
.to(to)
|
||||
.value(Wei.wrap(bytes32()))
|
||||
.payload(payload)
|
||||
.accessList(accessList())
|
||||
.chainId(BigInteger.ONE)
|
||||
.signAndBuild(generateKeyPair());
|
||||
}
|
||||
|
||||
private AccessList accessList() {
|
||||
final List<Address> accessedAddresses =
|
||||
Stream.generate(this::address).limit(1 + random.nextInt(3)).collect(toUnmodifiableList());
|
||||
final List<Map.Entry<Address, List<Bytes32>>> accessedStorage = new ArrayList<>();
|
||||
for (int i = 0; i < accessedAddresses.size(); ++i) {
|
||||
accessedStorage.add(
|
||||
new AbstractMap.SimpleEntry<>(
|
||||
accessedAddresses.get(i),
|
||||
Stream.generate(this::bytes32).limit(2L * i).collect(toUnmodifiableList())));
|
||||
}
|
||||
return new AccessList(accessedStorage);
|
||||
}
|
||||
|
||||
private Transaction eip1559Transaction(final Bytes payload, final Address to) {
|
||||
return Transaction.builder()
|
||||
.type(TransactionType.EIP1559)
|
||||
@@ -396,6 +429,23 @@ public class BlockDataGenerator {
|
||||
return transactions(n, TransactionType.values());
|
||||
}
|
||||
|
||||
public Set<Transaction> transactionsWithAllTypes() {
|
||||
return transactionsWithAllTypes(0);
|
||||
}
|
||||
|
||||
public Set<Transaction> transactionsWithAllTypes(final int atLeast) {
|
||||
checkArgument(atLeast >= 0);
|
||||
final HashSet<TransactionType> remainingTransactionTypes =
|
||||
new HashSet<>(Set.of(TransactionType.values()));
|
||||
final HashSet<Transaction> transactions = new HashSet<>();
|
||||
while (transactions.size() < atLeast || !remainingTransactionTypes.isEmpty()) {
|
||||
final Transaction newTransaction = transaction();
|
||||
transactions.add(newTransaction);
|
||||
remainingTransactionTypes.remove(newTransaction.getType());
|
||||
}
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public TransactionReceipt receipt(final long cumulativeGasUsed) {
|
||||
return new TransactionReceipt(
|
||||
transactionType(),
|
||||
@@ -457,7 +507,7 @@ public class BlockDataGenerator {
|
||||
return LogTopic.wrap(bytesValue(Bytes32.SIZE));
|
||||
}
|
||||
|
||||
private Bytes32 bytes32() {
|
||||
public Bytes32 bytes32() {
|
||||
return Bytes32.wrap(bytes(Bytes32.SIZE));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.plugin.data.TransactionType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -29,11 +31,15 @@ public class TransactionBuilderTest {
|
||||
|
||||
@Test
|
||||
public void guessTypeCanGuessAllTypes() {
|
||||
final BlockDataGenerator gen = new BlockDataGenerator();
|
||||
final Transaction.Builder frontierBuilder = Transaction.builder();
|
||||
final Transaction.Builder eip1559Builder = Transaction.builder().feeCap(Wei.of(5));
|
||||
final Transaction.Builder accessListBuilder =
|
||||
Transaction.builder()
|
||||
.accessList(new AccessList(List.of(Map.entry(gen.address(), List.of(gen.bytes32())))));
|
||||
|
||||
final Set<TransactionType> guessedTypes =
|
||||
Stream.of(frontierBuilder, eip1559Builder)
|
||||
Stream.of(frontierBuilder, eip1559Builder, accessListBuilder)
|
||||
.map(transactionBuilder -> transactionBuilder.guessType().build().getType())
|
||||
.collect(toUnmodifiableSet());
|
||||
|
||||
|
||||
@@ -17,7 +17,10 @@ package org.hyperledger.besu.ethereum.core.encoding;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
|
||||
@@ -64,4 +67,19 @@ public class TransactionRLPEncoderTest {
|
||||
.isEqualTo(output.encoded().toHexString());
|
||||
ExperimentalEIPs.eip1559Enabled = ExperimentalEIPs.EIP1559_ENABLED_DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockWithLegacyAndEIP2930TransactionsRoundTrips() {
|
||||
final BlockDataGenerator gen = new BlockDataGenerator();
|
||||
|
||||
final Block block =
|
||||
gen.block(
|
||||
BlockDataGenerator.BlockOptions.create()
|
||||
.addTransaction(gen.transactionsWithAllTypes().toArray(new Transaction[] {})));
|
||||
|
||||
assertThat(
|
||||
Block.readFrom(
|
||||
RLP.input(RLP.encode(block::writeTo)), new MainnetBlockHeaderFunctions()))
|
||||
.isEqualTo(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ public class IntrinsicGasTest {
|
||||
@Test
|
||||
public void validateGasCost() {
|
||||
Transaction t = Transaction.readFrom(RLP.input(Bytes.fromHexString(txRlp)));
|
||||
Assertions.assertThat(gasCalculator.transactionIntrinsicGasCost(t)).isEqualTo(expectedGas);
|
||||
Assertions.assertThat(gasCalculator.transactionIntrinsicGasCostAndAccessedState(t).getGas())
|
||||
.isEqualTo(expectedGas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.crypto.SECP256K1.KeyPair;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.GasAndAccessedState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionFilter;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
@@ -79,7 +80,8 @@ public class MainnetTransactionValidatorTest {
|
||||
.gasLimit(10)
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(senderKeys);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(transaction)).thenReturn(Gas.of(50));
|
||||
when(gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction))
|
||||
.thenReturn(new GasAndAccessedState(Gas.of(50)));
|
||||
|
||||
assertThat(validator.validate(transaction, Optional.empty()))
|
||||
.isEqualTo(
|
||||
@@ -290,7 +292,8 @@ public class MainnetTransactionValidatorTest {
|
||||
assertThat(frontierValidator.validate(transaction, Optional.empty()))
|
||||
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.INVALID_TRANSACTION_FORMAT));
|
||||
|
||||
when(gasCalculator.transactionIntrinsicGasCost(transaction)).thenReturn(Gas.of(0));
|
||||
when(gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction))
|
||||
.thenReturn(new GasAndAccessedState(Gas.of(0)));
|
||||
|
||||
assertThat(eip1559Validator.validate(transaction, Optional.of(1L)))
|
||||
.isEqualTo(ValidationResult.valid());
|
||||
@@ -341,7 +344,8 @@ public class MainnetTransactionValidatorTest {
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(senderKeys);
|
||||
final Optional<Long> basefee = Optional.of(150000L);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(transaction)).thenReturn(Gas.of(50));
|
||||
when(gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction))
|
||||
.thenReturn(new GasAndAccessedState(Gas.of(50)));
|
||||
|
||||
assertThat(validator.validate(transaction, basefee)).isEqualTo(ValidationResult.valid());
|
||||
ExperimentalEIPs.eip1559Enabled = false;
|
||||
@@ -372,7 +376,8 @@ public class MainnetTransactionValidatorTest {
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(senderKeys);
|
||||
|
||||
when(gasCalculator.transactionIntrinsicGasCost(transaction)).thenReturn(Gas.of(50));
|
||||
when(gasCalculator.transactionIntrinsicGasCostAndAccessedState(transaction))
|
||||
.thenReturn(new GasAndAccessedState(Gas.of(50)));
|
||||
|
||||
assertThat(validator.validate(transaction, Optional.empty()).isValid()).isTrue();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.plugin.data.TransactionType;
|
||||
import org.hyperledger.orion.testutil.OrionKeyUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -106,6 +107,7 @@ public class DefaultPrivacyControllerTest {
|
||||
|
||||
private static final Transaction PUBLIC_TRANSACTION =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.FRONTIER)
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(1000))
|
||||
.gasLimit(3000000)
|
||||
|
||||
@@ -289,7 +289,10 @@ public class EvmToolCommand implements Runnable {
|
||||
Optional.empty());
|
||||
|
||||
final Gas intrinsicGasCost =
|
||||
protocolSpec.getGasCalculator().transactionIntrinsicGasCost(tx);
|
||||
protocolSpec
|
||||
.getGasCalculator()
|
||||
.transactionIntrinsicGasCostAndAccessedState(tx)
|
||||
.getGas();
|
||||
final Gas evmGas = gas.minus(messageFrame.getRemainingGas());
|
||||
out.println();
|
||||
out.println(
|
||||
|
||||
@@ -64,7 +64,7 @@ Calculated : ${currentHash}
|
||||
tasks.register('checkAPIChanges', FileStateChecker) {
|
||||
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
|
||||
files = sourceSets.main.allJava.files
|
||||
knownHash = 'FFJnnC23PD/IYNsYnHUT7qF8qHnUucnJwCn4YYPVPE8='
|
||||
knownHash = 'iPuLs9OOxBC5ZJqJ5yWrkT6LgquIDEZztdRDbSe9E3U='
|
||||
}
|
||||
check.dependsOn('checkAPIChanges')
|
||||
|
||||
|
||||
@@ -14,8 +14,11 @@
|
||||
*/
|
||||
package org.hyperledger.besu.plugin.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum TransactionType {
|
||||
FRONTIER(0xf8 /* doesn't end up being used as we don't serialize legacy txs with their type */),
|
||||
ACCESS_LIST(0x1),
|
||||
EIP1559(0xf /* placeholder value until we know what the real type byte will be */);
|
||||
|
||||
private final int typeValue;
|
||||
@@ -29,15 +32,12 @@ public enum TransactionType {
|
||||
}
|
||||
|
||||
public static TransactionType of(final int serializedTypeValue) {
|
||||
if (serializedTypeValue >= 0xc0 && serializedTypeValue <= 0xfe) {
|
||||
return FRONTIER;
|
||||
}
|
||||
for (TransactionType transactionType : TransactionType.values()) {
|
||||
if (transactionType.typeValue == serializedTypeValue) {
|
||||
return transactionType;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unsupported transaction type %x", serializedTypeValue));
|
||||
return Arrays.stream(TransactionType.values())
|
||||
.filter(transactionType -> transactionType.typeValue == serializedTypeValue)
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Unsupported transaction type %x", serializedTypeValue)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user