EIP-2718 Typed Transaction Envelopes (#1645)

This implements the encoding/decoding logic and because it's backwards 
compatible we can introduce it immediately. You can see that some of 
the typed-transaction specific encoding/decoding logic is tested where 
there are EIP-1559 encoding/decoding tests but there'll be more tests 
included in the EIP-2930 PR.

Signed-off-by: Ratan Rai Sur <ratan.r.sur@gmail.com>
This commit is contained in:
Ratan (Rai) Sur
2020-12-02 09:02:53 -05:00
committed by GitHub
parent 809b801add
commit 021ce8ee04
30 changed files with 374 additions and 247 deletions

View File

@@ -257,7 +257,8 @@ public class BlockTransactionSelector {
transactionSelectionResult.update(
transaction,
transactionReceiptFactory.create(result, worldState, cumulativeGasUsed),
transactionReceiptFactory.create(
transaction.getType(), result, worldState, cumulativeGasUsed),
gasUsedByTransaction);
}

View File

@@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
@@ -566,7 +567,10 @@ public class BlockTransactionSelectorTest {
// This is a duplicate of the MainnetProtocolSpec::frontierTransactionReceiptFactory
private TransactionReceipt createReceipt(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
worldState.rootHash(), gasUsed, Lists.newArrayList(), Optional.empty());
}

View File

@@ -57,8 +57,10 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
*/
public void writeTo(final RLPOutput output) {
output.startList();
output.writeList(getTransactions(), Transaction::writeTo);
output.writeList(getOmmers(), BlockHeader::writeTo);
output.endList();
}

View File

@@ -22,7 +22,6 @@ import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.ethereum.core.encoding.TransactionRLPDecoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionRLPEncoder;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.hyperledger.besu.plugin.data.Quantity;
@@ -92,8 +91,8 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
return new Builder();
}
public static Transaction readFrom(final RLPInput input) throws RLPException {
return TransactionRLPDecoder.decodeTransaction(input);
public static Transaction readFrom(final RLPInput rlpInput) {
return TransactionRLPDecoder.decode(rlpInput);
}
/**

View File

@@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionReceiptType;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.List;
import java.util.Objects;
@@ -41,6 +42,7 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
private static final int NONEXISTENT = -1;
private final TransactionType transactionType;
private final Hash stateRoot;
private final long cumulativeGasUsed;
private final List<Log> logs;
@@ -63,6 +65,7 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
final List<Log> logs,
final Optional<Bytes> revertReason) {
this(
TransactionType.FRONTIER,
stateRoot,
NONEXISTENT,
cumulativeGasUsed,
@@ -72,12 +75,20 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
}
private TransactionReceipt(
final TransactionType transactionType,
final Hash stateRoot,
final long cumulativeGasUsed,
final List<Log> logs,
final LogsBloomFilter bloomFilter,
final Optional<Bytes> revertReason) {
this(stateRoot, NONEXISTENT, cumulativeGasUsed, logs, bloomFilter, revertReason);
this(
transactionType,
stateRoot,
NONEXISTENT,
cumulativeGasUsed,
logs,
bloomFilter,
revertReason);
}
/**
@@ -94,6 +105,7 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
final List<Log> logs,
final Optional<Bytes> revertReason) {
this(
TransactionType.FRONTIER,
null,
status,
cumulativeGasUsed,
@@ -103,21 +115,39 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
}
private TransactionReceipt(
final TransactionType transactionType,
final int status,
final long cumulativeGasUsed,
final List<Log> logs,
final LogsBloomFilter bloomFilter,
final Optional<Bytes> revertReason) {
this(null, status, cumulativeGasUsed, logs, bloomFilter, revertReason);
this(transactionType, null, status, cumulativeGasUsed, logs, bloomFilter, revertReason);
}
public TransactionReceipt(
final TransactionType transactionType,
final int status,
final long cumulativeGasUsed,
final List<Log> logs,
final Optional<Bytes> maybeRevertReason) {
this(
transactionType,
status,
cumulativeGasUsed,
logs,
LogsBloomFilter.builder().insertLogs(logs).build(),
maybeRevertReason);
}
private TransactionReceipt(
final TransactionType transactionType,
final Hash stateRoot,
final int status,
final long cumulativeGasUsed,
final List<Log> logs,
final LogsBloomFilter bloomFilter,
final Optional<Bytes> revertReason) {
this.transactionType = transactionType;
this.stateRoot = stateRoot;
this.cumulativeGasUsed = cumulativeGasUsed;
this.status = status;
@@ -142,6 +172,9 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
}
private void writeTo(final RLPOutput out, final boolean withRevertReason) {
if (!transactionType.equals(TransactionType.FRONTIER)) {
out.writeRaw(Bytes.of((byte) transactionType.getSerializedType()));
}
out.startList();
// Determine whether it's a state root-encoded transaction receipt
@@ -179,7 +212,18 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
*/
public static TransactionReceipt readFrom(
final RLPInput input, final boolean revertReasonAllowed) {
input.enterList();
TransactionType transactionType = TransactionType.FRONTIER;
try {
input.enterList();
} catch (RLPException rlpe) {
if (rlpe.getMessage().contains("Expected current item to be a list")) {
// This is an EIP-2718 receipt
transactionType = TransactionType.of(input.readByte());
input.enterList();
} else {
throw rlpe;
}
}
try {
// Get the first element to check later to determine the
@@ -204,10 +248,12 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran
// byte for success (0x01) or failure (0x80).
if (firstElement.raw().size() == 1) {
final int status = firstElement.readIntScalar();
return new TransactionReceipt(status, cumulativeGas, logs, bloomFilter, revertReason);
return new TransactionReceipt(
transactionType, status, cumulativeGas, logs, bloomFilter, revertReason);
} else {
final Hash stateRoot = Hash.wrap(firstElement.readBytes32());
return new TransactionReceipt(stateRoot, cumulativeGas, logs, bloomFilter, revertReason);
return new TransactionReceipt(
transactionType, stateRoot, cumulativeGas, logs, bloomFilter, revertReason);
}
} finally {
input.leaveList();

View File

@@ -29,153 +29,152 @@ 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.RLPInput;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
@FunctionalInterface
public interface TransactionRLPDecoder {
public class TransactionRLPDecoder {
TransactionRLPDecoder FRONTIER = frontierDecoder();
TransactionRLPDecoder EIP1559 = eip1559Decoder();
TransactionRLPDecoder GOQUORUM_PRIVATE_TRANSACTION_DECODER = goQuorumPrivateTransactionDecoder();
@FunctionalInterface
interface Decoder {
Transaction decode(RLPInput input);
}
static Transaction decodeTransaction(final RLPInput input) {
private static final ImmutableMap<TransactionType, TransactionRLPDecoder.Decoder>
TYPED_TRANSACTION_DECODERS =
ImmutableMap.of(TransactionType.EIP1559, TransactionRLPDecoder::decodeEIP1559);
public static Transaction decode(final RLPInput rlpInput) {
if (GoQuorumOptions.goquorumCompatibilityMode) {
return GOQUORUM_PRIVATE_TRANSACTION_DECODER.decode(input);
return decodeGoQuorum(rlpInput);
}
final Bytes typedTransactionBytes = rlpInput.raw();
final int firstByte = typedTransactionBytes.get(0) & 0xff;
final TransactionType transactionType = TransactionType.of(firstByte);
if (transactionType.equals(TransactionType.FRONTIER)) {
return decodeFrontier(rlpInput);
} else {
rlpInput.skipNext(); // throw away the type byte
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)));
return decoder.decode(rlpInput);
}
return (ExperimentalEIPs.eip1559Enabled ? EIP1559 : FRONTIER).decode(input);
}
Transaction decode(RLPInput input);
static Transaction decodeFrontier(final RLPInput input) {
input.enterList();
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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());
static TransactionRLPDecoder frontierDecoder() {
return input -> {
input.enterList();
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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());
input.leaveList();
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId =
v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
};
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
}
static TransactionRLPDecoder eip1559Decoder() {
return input -> {
input.enterList();
static Transaction decodeEIP1559(final RLPInput input) {
ExperimentalEIPs.eip1559MustBeEnabled();
input.enterList();
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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());
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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())
.gasPremium(Wei.of(input.readBytes().toBigInteger()))
.feeCap(Wei.of(input.readBytes().toBigInteger()));
final Bytes maybeGasPremiumOrV = input.readBytes();
final Bytes maybeFeeCapOrR = input.readBytes();
final Bytes maybeVOrS = input.readBytes();
final BigInteger v, r, s;
// if this is the end of the list we are processing a legacy transaction
if (input.isEndOfCurrentList()) {
v = maybeGasPremiumOrV.toUnsignedBigInteger();
r = maybeFeeCapOrR.toUnsignedBigInteger();
s = maybeVOrS.toUnsignedBigInteger();
} else {
// otherwise this is an EIP-1559 transaction
builder
.gasPremium(Wei.of(maybeGasPremiumOrV.toBigInteger()))
.feeCap(Wei.of(maybeFeeCapOrR.toBigInteger()));
v = maybeVOrS.toBigInteger();
r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
}
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId =
v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
};
final BigInteger v = input.readBytes().toBigInteger();
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
}
static TransactionRLPDecoder goQuorumPrivateTransactionDecoder() {
return input -> {
input.enterList();
static Transaction decodeGoQuorum(final RLPInput input) {
input.enterList();
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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());
final Transaction.Builder builder =
Transaction.builder()
.nonce(input.readLongScalar())
.gasPrice(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());
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (isGoQuorumPrivateTransaction(v)) {
// GoQuorum private TX. No chain ID. Preserve the v value as provided.
builder.v(v);
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE)
|| v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId =
v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (isGoQuorumPrivateTransaction(v)) {
// GoQuorum private TX. No chain ID. Preserve the v value as provided.
builder.v(v);
recId = v.subtract(GO_QUORUM_PRIVATE_TRANSACTION_V_VALUE_MIN).byteValueExact();
} else if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toBytes().toUnsignedBigInteger();
final SECP256K1.Signature signature = SECP256K1.Signature.create(r, s, recId);
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
};
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();
}
private static boolean isGoQuorumPrivateTransaction(final BigInteger v) {

View File

@@ -17,60 +17,75 @@ package org.hyperledger.besu.ethereum.core.encoding;
import org.hyperledger.besu.config.experimental.ExperimentalEIPs;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.hyperledger.besu.plugin.data.Quantity;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
public class TransactionRLPEncoder {
private static final Encoder FRONTIER = frontierEncoder();
private static final Encoder EIP1559 = eip1559Encoder();
private static final ImmutableMap<TransactionType, Encoder> ENCODERS =
ImmutableMap.of(TransactionType.FRONTIER, FRONTIER, TransactionType.EIP1559, EIP1559);
public static void encode(final Transaction transaction, final RLPOutput output) {
ENCODERS.getOrDefault(transaction.getType(), FRONTIER).encode(transaction, output);
@FunctionalInterface
interface Encoder {
void encode(Transaction transaction, RLPOutput output);
}
static Encoder frontierEncoder() {
return (transaction, out) -> {
out.startList();
out.writeLongScalar(transaction.getNonce());
out.writeUInt256Scalar(transaction.getGasPrice());
out.writeLongScalar(transaction.getGasLimit());
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
out.writeUInt256Scalar(transaction.getValue());
out.writeBytes(transaction.getPayload());
writeSignature(transaction, out);
out.endList();
};
private static final ImmutableMap<TransactionType, Encoder> TYPED_TRANSACTION_ENCODERS =
ImmutableMap.of(TransactionType.EIP1559, TransactionRLPEncoder::encodeEIP1559);
public static void encode(final Transaction transaction, final RLPOutput rlpOutput) {
if (transaction.getType().equals(TransactionType.FRONTIER)) {
encodeFrontier(transaction, rlpOutput);
} else {
final TransactionType type = transaction.getType();
final Encoder encoder =
Optional.ofNullable(TYPED_TRANSACTION_ENCODERS.get(type))
.orElseThrow(
() ->
new IllegalStateException(
String.format(
"Developer Error. A supported transaction type %s has no associated"
+ " encoding logic",
type)));
rlpOutput.writeRaw(
Bytes.concatenate(
Bytes.of((byte) type.getSerializedType()),
RLP.encode(output -> encoder.encode(transaction, output))));
}
}
static Encoder eip1559Encoder() {
return (transaction, out) -> {
if (!ExperimentalEIPs.eip1559Enabled
|| !TransactionType.EIP1559.equals(transaction.getType())) {
throw new RuntimeException("Invalid transaction format");
}
static void encodeFrontier(final Transaction transaction, final RLPOutput out) {
out.startList();
out.writeLongScalar(transaction.getNonce());
out.writeUInt256Scalar(transaction.getGasPrice());
out.writeLongScalar(transaction.getGasLimit());
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
out.writeUInt256Scalar(transaction.getValue());
out.writeBytes(transaction.getPayload());
writeSignature(transaction, out);
out.endList();
}
out.startList();
out.writeLongScalar(transaction.getNonce());
out.writeNull();
out.writeLongScalar(transaction.getGasLimit());
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
out.writeUInt256Scalar(transaction.getValue());
out.writeBytes(transaction.getPayload());
out.writeUInt256Scalar(
transaction.getGasPremium().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
out.writeUInt256Scalar(
transaction.getFeeCap().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
writeSignature(transaction, out);
out.endList();
};
static void encodeEIP1559(final Transaction transaction, final RLPOutput out) {
ExperimentalEIPs.eip1559MustBeEnabled();
out.startList();
out.writeLongScalar(transaction.getNonce());
out.writeNull();
out.writeLongScalar(transaction.getGasLimit());
out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY));
out.writeUInt256Scalar(transaction.getValue());
out.writeBytes(transaction.getPayload());
out.writeUInt256Scalar(
transaction.getGasPremium().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
out.writeUInt256Scalar(
transaction.getFeeCap().map(Quantity::getValue).map(Wei::ofNumber).orElseThrow());
writeSignature(transaction, out);
out.endList();
}
private static void writeSignature(final Transaction transaction, final RLPOutput out) {
@@ -78,9 +93,4 @@ public class TransactionRLPEncoder {
out.writeBigIntegerScalar(transaction.getSignature().getR());
out.writeBigIntegerScalar(transaction.getSignature().getS());
}
@FunctionalInterface
interface Encoder {
void encode(Transaction transaction, RLPOutput output);
}
}

View File

@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +42,10 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
public interface TransactionReceiptFactory {
TransactionReceipt create(
TransactionProcessingResult result, WorldState worldState, long gasUsed);
TransactionType transactionType,
TransactionProcessingResult result,
WorldState worldState,
long gasUsed);
}
private static final Logger LOG = LogManager.getLogger();
@@ -165,7 +169,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
currentGasUsed += transaction.getGasLimit() - result.getGasRemaining();
final TransactionReceipt transactionReceipt =
transactionReceiptFactory.create(result, worldState, currentGasUsed);
transactionReceiptFactory.create(
transaction.getType(), result, worldState, currentGasUsed);
receipts.add(transactionReceipt);
}

View File

@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.fees.TransactionPriceCalculator;
import org.hyperledger.besu.ethereum.mainnet.contractvalidation.MaxCodeSizeRule;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.Collections;
@@ -238,13 +239,21 @@ public class ClassicProtocolSpecs {
}
private static TransactionReceipt byzantiumTransactionReceiptFactory(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
// ignored because it's always FRONTIER for byzantium
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
result.isSuccessful() ? 1 : 0, gasUsed, result.getLogs(), Optional.empty());
}
private static TransactionReceipt byzantiumTransactionReceiptFactoryWithReasonEnabled(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
// ignored because it's always FRONTIER for byzantium
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
result.isSuccessful() ? 1 : 0, gasUsed, result.getLogs(), result.getRevertReason());
}

View File

@@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.io.IOException;
import java.math.BigInteger;
@@ -403,6 +404,10 @@ public abstract class MainnetProtocolSpecs {
gasCalculator ->
MainnetEvmRegistries.berlin(gasCalculator, chainId.orElse(BigInteger.ZERO)))
.precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::berlin)
.transactionReceiptFactory(
enableRevertReason
? MainnetProtocolSpecs::berlinTransactionReceiptFactoryWithReasonEnabled
: MainnetProtocolSpecs::berlinTransactionReceiptFactory)
.name("Berlin");
}
@@ -459,7 +464,11 @@ public abstract class MainnetProtocolSpecs {
}
private static TransactionReceipt frontierTransactionReceiptFactory(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
// ignored because it's always FRONTIER
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
worldState.rootHash(),
gasUsed,
@@ -468,17 +477,51 @@ public abstract class MainnetProtocolSpecs {
}
private static TransactionReceipt byzantiumTransactionReceiptFactory(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
// ignored because it's always FRONTIER
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
result.isSuccessful() ? 1 : 0, gasUsed, result.getLogs(), Optional.empty());
}
private static TransactionReceipt byzantiumTransactionReceiptFactoryWithReasonEnabled(
final TransactionProcessingResult result, final WorldState worldState, final long gasUsed) {
// ignored because it's always FRONTIER
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
result.isSuccessful() ? 1 : 0, gasUsed, result.getLogs(), result.getRevertReason());
}
private static TransactionReceipt berlinTransactionReceiptFactory(
final TransactionType transactionType,
final TransactionProcessingResult transactionProcessingResult,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
transactionType,
transactionProcessingResult.isSuccessful() ? 1 : 0,
gasUsed,
transactionProcessingResult.getLogs(),
Optional.empty());
}
private static TransactionReceipt berlinTransactionReceiptFactoryWithReasonEnabled(
final TransactionType transactionType,
final TransactionProcessingResult transactionProcessingResult,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
transactionType,
transactionProcessingResult.isSuccessful() ? 1 : 0,
gasUsed,
transactionProcessingResult.getLogs(),
transactionProcessingResult.getRevertReason());
}
private static class DaoBlockProcessor implements BlockProcessor {
private final BlockProcessor wrapped;

View File

@@ -174,7 +174,7 @@ public class PrivateGroupRehydrationBlockProcessor {
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed;
final TransactionReceipt transactionReceipt =
transactionReceiptFactory.create(result, worldState, gasUsed);
transactionReceiptFactory.create(transaction.getType(), result, worldState, gasUsed);
receipts.add(transactionReceipt);
}

View File

@@ -114,7 +114,7 @@ public class PrivateMigrationBlockProcessor {
worldStateUpdater.commit();
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed;
final TransactionReceipt transactionReceipt =
transactionReceiptFactory.create(result, worldState, gasUsed);
transactionReceiptFactory.create(transaction.getType(), result, worldState, gasUsed);
receipts.add(transactionReceipt);
}

View File

@@ -23,7 +23,6 @@ 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;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.apache.tuweni.bytes.Bytes;
@@ -34,7 +33,7 @@ public class TransactionRLPDecoderTest {
private static final String FRONTIER_TX_RLP =
"0xf901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
private static final String EIP1559_TX_RLP =
"0xf902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
"0x03f902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
private static final String GOQUORUM_PRIVATE_TX_RLP =
"0xf88d0b808347b7608080b840290a80a37d198ff06abe189b638ff53ac8a8dc51a0aff07609d2aa75342783ae493b3e3c6b564c0eebe49284b05a0726fb33087b9e0231d349ea0c7b5661c8c526a07144db7045a395e608cda6ab051c86cc4fb42e319960b82087f3b26f0cbc3c2da00223ac129b22aec7a6c2ace3c3ef39c5eaaa54070fd82d8ee2140b0e70b1dca9";
@@ -43,7 +42,7 @@ public class TransactionRLPDecoderTest {
GoQuorumOptions.goquorumCompatibilityMode = true;
RLPInput input = RLP.input(Bytes.fromHexString(GOQUORUM_PRIVATE_TX_RLP));
final Transaction transaction = TransactionRLPDecoder.decodeTransaction(input);
final Transaction transaction = TransactionRLPDecoder.decode(input);
assertThat(transaction).isNotNull();
assertThat(transaction.getV()).isEqualTo(38);
assertThat(transaction.getSender())
@@ -55,7 +54,7 @@ public class TransactionRLPDecoderTest {
@Test
public void decodeFrontierNominalCase() {
final Transaction transaction =
TransactionRLPDecoder.decodeTransaction(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
assertThat(transaction).isNotNull();
assertThat(transaction.getGasPrice()).isEqualByComparingTo(Wei.of(50L));
assertThat(transaction.getGasPremium()).isEmpty();
@@ -66,7 +65,7 @@ public class TransactionRLPDecoderTest {
public void decodeEIP1559NominalCase() {
ExperimentalEIPs.eip1559Enabled = true;
final Transaction transaction =
TransactionRLPDecoder.decodeTransaction(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
assertThat(transaction).isNotNull();
assertThat(transaction.getGasPremium()).hasValue(Wei.of(527L));
assertThat(transaction.getFeeCap()).hasValue(Wei.of(369L));
@@ -77,11 +76,9 @@ public class TransactionRLPDecoderTest {
public void decodeEIP1559FailureWhenNotEnabled() {
ExperimentalEIPs.eip1559Enabled = false;
assertThatThrownBy(
() ->
TransactionRLPDecoder.decodeTransaction(
RLP.input(Bytes.fromHexString(EIP1559_TX_RLP))))
.isInstanceOf(RLPException.class)
.hasMessageContaining("Not at the end of the current list");
() -> TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP))))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("EIP-1559 feature flag");
ExperimentalEIPs.eip1559Enabled = ExperimentalEIPs.EIP1559_ENABLED_DEFAULT_VALUE;
}
}

View File

@@ -43,13 +43,12 @@ public class TransactionRLPEncoderTest {
private static final String FRONTIER_TX_RLP =
"0xf901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
private static final String EIP1559_TX_RLP =
"0xf902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
"0x03f902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
@Test
public void encodeFrontierTxNominalCase() {
final Transaction transaction =
TransactionRLPDecoder.frontierDecoder()
.decode(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
final BytesValueRLPOutput output = new BytesValueRLPOutput();
TransactionRLPEncoder.encode(transaction, output);
assertThat(FRONTIER_TX_RLP).isEqualTo(output.encoded().toHexString());
@@ -59,12 +58,11 @@ public class TransactionRLPEncoderTest {
public void encodeEIP1559TxNominalCase() {
ExperimentalEIPs.eip1559Enabled = true;
final Transaction transaction =
TransactionRLPDecoder.eip1559Decoder()
.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
final BytesValueRLPOutput output = new BytesValueRLPOutput();
TransactionRLPEncoder.encode(transaction, output);
assertThat(
"0xf902028080830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")
"0x03f902028080830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")
.isEqualTo(output.encoded().toHexString());
ExperimentalEIPs.eip1559Enabled = ExperimentalEIPs.EIP1559_ENABLED_DEFAULT_VALUE;
}
@@ -75,11 +73,10 @@ public class TransactionRLPEncoderTest {
assertThatThrownBy(
() ->
TransactionRLPEncoder.encode(
TransactionRLPDecoder.eip1559Decoder()
.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP))),
TransactionRLPDecoder.decode(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP))),
new BytesValueRLPOutput()))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("Invalid transaction format");
.hasMessageContaining("EIP-1559 feature flag");
ExperimentalEIPs.eip1559Enabled = ExperimentalEIPs.EIP1559_ENABLED_DEFAULT_VALUE;
}
}

View File

@@ -139,6 +139,6 @@ public class EIP1559Test {
Transaction.readFrom(
RLP.input(
Bytes.fromHexString(
"0xf902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")));
"0x03f902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")));
}
}

View File

@@ -209,7 +209,7 @@ public class PrivacyBlockProcessorTest {
when(protocolSpec.getPrivateTransactionProcessor()).thenReturn(mockPrivateTransactionProcessor);
final AbstractBlockProcessor.TransactionReceiptFactory mockTransactionReceiptFactory =
mock(AbstractBlockProcessor.TransactionReceiptFactory.class);
when(mockTransactionReceiptFactory.create(any(), any(), anyLong()))
when(mockTransactionReceiptFactory.create(any(), any(), any(), anyLong()))
.thenReturn(new TransactionReceipt(0, 0, Collections.emptyList(), Optional.empty()));
when(protocolSpec.getTransactionReceiptFactory()).thenReturn(mockTransactionReceiptFactory);
when(protocolSpec.getBlockReward()).thenReturn(Wei.ZERO);

View File

@@ -43,9 +43,7 @@ public final class BlockBodiesMessage extends AbstractMessageData {
public static BlockBodiesMessage create(final Iterable<BlockBody> bodies) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
bodies.forEach(body -> body.writeTo(tmp));
tmp.endList();
tmp.writeList(bodies, BlockBody::writeTo);
return new BlockBodiesMessage(tmp.encoded());
}

View File

@@ -49,7 +49,7 @@ public final class LimitedTransactionsMessages {
if (encodedBytes.size() > LIMIT && (messageSize != 0)) {
break;
}
message.writeRLPUnsafe(encodedBytes);
message.writeRaw(encodedBytes);
includedTransactions.add(transaction);
// Check if last transaction to add to the message
messageSize += encodedBytes.size();

View File

@@ -19,10 +19,8 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.Iterator;
import java.util.function.Function;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
@@ -59,8 +57,7 @@ public class TransactionsMessage extends AbstractMessageData {
return EthPV62.TRANSACTIONS;
}
public Iterator<Transaction> transactions(
final Function<RLPInput, Transaction> transactionReader) {
return new BytesValueRLPInput(data, false).readList(transactionReader).iterator();
public List<Transaction> transactions() {
return new BytesValueRLPInput(data, false).readList(Transaction::readFrom);
}
}

View File

@@ -27,7 +27,7 @@ import org.hyperledger.besu.plugin.services.metrics.Counter;
import java.time.Duration;
import java.time.Instant;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Sets;
@@ -75,8 +75,7 @@ class TransactionsMessageProcessor {
try {
LOG.trace("Received transactions message from {}", peer);
final Iterator<Transaction> readTransactions =
transactionsMessage.transactions(Transaction::readFrom);
final List<Transaction> readTransactions = transactionsMessage.transactions();
final Set<Transaction> transactions = Sets.newHashSet(readTransactions);
transactionTracker.markTransactionsAsSeen(peer, transactions);
transactionPool.addRemoteTransactions(transactions);

View File

@@ -14,16 +14,16 @@
*/
package org.hyperledger.besu.ethereum.eth.messages;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public class TransactionsMessageTest {
@@ -46,10 +46,6 @@ public class TransactionsMessageTest {
final TransactionsMessage message = TransactionsMessage.readFrom(raw);
// Check that transactions match original inputs after transformations
final Iterator<Transaction> readTransactions = message.transactions(Transaction::readFrom);
for (int i = 0; i < txCount; ++i) {
Assertions.assertThat(readTransactions.next()).isEqualTo(transactions.get(i));
}
Assertions.assertThat(readTransactions.hasNext()).isFalse();
assertThat(message.transactions()).isEqualTo(transactions);
}
}

View File

@@ -104,6 +104,6 @@ public class TransactionsMessageSenderTest {
private Set<Transaction> getTransactionsFromMessage(final MessageData message) {
final TransactionsMessage transactionsMessage = TransactionsMessage.readFrom(message);
return newHashSet(transactionsMessage.transactions(Transaction::readFrom));
return newHashSet(transactionsMessage.transactions());
}
}

View File

@@ -86,7 +86,7 @@ abstract class AbstractRLPOutput implements RLPOutput {
}
@Override
public void writeRLPUnsafe(final Bytes v) {
public void writeRaw(final Bytes v) {
checkState(
stackSize > 1 || values.isEmpty(), "Terminated RLP output, cannot add more elements");
values.add(v);

View File

@@ -52,7 +52,11 @@ public abstract class RLP {
* read.
*/
public static RLPInput input(final Bytes encoded) {
return new BytesValueRLPInput(encoded, false);
return input(encoded, false);
}
public static RLPInput input(final Bytes encoded, final boolean lenient) {
return new BytesValueRLPInput(encoded, lenient);
}
/**

View File

@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.Collection;
import java.util.function.BiConsumer;
import org.apache.tuweni.bytes.Bytes;
@@ -243,8 +242,7 @@ public interface RLPOutput {
* writes this value to the output.
* @param <T> The type of values to write.
*/
default <T> void writeList(
final Collection<T> values, final BiConsumer<T, RLPOutput> valueWriter) {
default <T> void writeList(final Iterable<T> values, final BiConsumer<T, RLPOutput> valueWriter) {
startList();
for (final T v : values) {
valueWriter.accept(v, this);
@@ -260,23 +258,23 @@ public interface RLPOutput {
* more efficient in that it saves most of that decoding/re-encoding work. Please note however
* that this method <b>does</b> validate that the input is a valid RLP encoding. If you can
* guaranteed that the input is valid and do not want this validation step, please have a look at
* {@link #writeRLPUnsafe(Bytes)}.
* {@link #writeRaw(Bytes)}.
*
* @param rlpEncodedValue An already RLP encoded value to write as next item of this output.
*/
default void writeRLP(final Bytes rlpEncodedValue) {
default void writeRLPBytes(final Bytes rlpEncodedValue) {
RLP.validate(rlpEncodedValue);
writeRLPUnsafe(rlpEncodedValue);
writeRaw(rlpEncodedValue);
}
/**
* Writes an already RLP encoded item to the output.
*
* <p>This method is equivalent to {@link #writeRLP(Bytes)}, but is unsafe in that it does not do
* any validation of the its input. As such, it is faster but can silently yield invalid RLP
* output if misused.
* <p>This method is equivalent to {@link #writeRLPBytes(Bytes)}, but is unsafe in that it does
* not do any validation of the its input. As such, it is faster but can silently yield invalid
* RLP output if misused.
*
* @param rlpEncodedValue An already RLP encoded value to write as next item of this output.
* @param bytes An already RLP encoded value to write as next item of this output.
*/
void writeRLPUnsafe(Bytes rlpEncodedValue);
void writeRaw(Bytes bytes);
}

View File

@@ -98,7 +98,7 @@ class BranchNode<V> implements Node<V> {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
for (int i = 0; i < RADIX; ++i) {
out.writeRLPUnsafe(children.get(i).getRlpRef());
out.writeRaw(children.get(i).getRlpRef());
}
if (value.isPresent()) {
out.writeBytes(valueSerializer.apply(value.get()));

View File

@@ -85,7 +85,7 @@ class ExtensionNode<V> implements Node<V> {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(CompactEncoding.encode(path));
out.writeRLPUnsafe(child.getRlpRef());
out.writeRaw(child.getRlpRef());
out.endList();
final Bytes encoded = out.encoded();
rlp = new WeakReference<>(encoded);

View File

@@ -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 = 'gQ91kSJ/YS1eUWUrEbuei1qGi0uq5yGe/5pVmY5MwU4='
knownHash = 'Ev0Y22aT+tcysFVaGcdaJON9Mf+aYqk8+5h+aHKUzuk='
}
check.dependsOn('checkAPIChanges')

View File

@@ -15,6 +15,29 @@
package org.hyperledger.besu.plugin.data;
public enum TransactionType {
FRONTIER,
EIP1559
FRONTIER(0xf8 /* doesn't end up being used as we don't serialize legacy txs with their type */),
EIP1559(0x3 /* placeholder value until we know what the real type byte will be */);
private final int typeValue;
TransactionType(final int typeValue) {
this.typeValue = typeValue;
}
public int getSerializedType() {
return this.typeValue;
}
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));
}
}