mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 22:07:59 -05:00
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:
@@ -257,7 +257,8 @@ public class BlockTransactionSelector {
|
||||
|
||||
transactionSelectionResult.update(
|
||||
transaction,
|
||||
transactionReceiptFactory.create(result, worldState, cumulativeGasUsed),
|
||||
transactionReceiptFactory.create(
|
||||
transaction.getType(), result, worldState, cumulativeGasUsed),
|
||||
gasUsedByTransaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,6 @@ public class EIP1559Test {
|
||||
Transaction.readFrom(
|
||||
RLP.input(
|
||||
Bytes.fromHexString(
|
||||
"0xf902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")));
|
||||
"0x03f902028032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b5682020f8201711ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user