|
|
@ -52,12 +52,12 @@ public class TransactionRLPDecoder { |
|
|
|
if (GoQuorumOptions.goquorumCompatibilityMode) { |
|
|
|
if (GoQuorumOptions.goquorumCompatibilityMode) { |
|
|
|
return decodeGoQuorum(rlpInput); |
|
|
|
return decodeGoQuorum(rlpInput); |
|
|
|
} |
|
|
|
} |
|
|
|
final Bytes typedTransactionBytes = rlpInput.raw(); |
|
|
|
if (rlpInput.nextIsList()) { |
|
|
|
final int firstByte = typedTransactionBytes.get(0) & 0xff; |
|
|
|
return decodeFrontierOrEip1559(rlpInput); |
|
|
|
final TransactionType transactionType = TransactionType.of(firstByte); |
|
|
|
|
|
|
|
if (transactionType.equals(TransactionType.FRONTIER)) { |
|
|
|
|
|
|
|
return decodeFrontier(rlpInput); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
final Bytes typedTransactionBytes = rlpInput.raw(); |
|
|
|
|
|
|
|
final int firstByte = typedTransactionBytes.get(0) & 0xff; |
|
|
|
|
|
|
|
final TransactionType transactionType = TransactionType.of(firstByte); |
|
|
|
rlpInput.skipNext(); // throw away the type byte
|
|
|
|
rlpInput.skipNext(); // throw away the type byte
|
|
|
|
final Decoder decoder = |
|
|
|
final Decoder decoder = |
|
|
|
Optional.ofNullable(TYPED_TRANSACTION_DECODERS.get(transactionType)) |
|
|
|
Optional.ofNullable(TYPED_TRANSACTION_DECODERS.get(transactionType)) |
|
|
@ -65,13 +65,61 @@ public class TransactionRLPDecoder { |
|
|
|
() -> |
|
|
|
() -> |
|
|
|
new IllegalStateException( |
|
|
|
new IllegalStateException( |
|
|
|
String.format( |
|
|
|
String.format( |
|
|
|
"Developer Error. A supported transaction type %s has no associated" |
|
|
|
"Developer Error. A supported transaction type %s has no associated decoding logic", |
|
|
|
+ " decoding logic", |
|
|
|
|
|
|
|
transactionType))); |
|
|
|
transactionType))); |
|
|
|
return decoder.decode(rlpInput); |
|
|
|
return decoder.decode(rlpInput); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Transaction decodeFrontierOrEip1559(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 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()) { |
|
|
|
|
|
|
|
builder.type(TransactionType.FRONTIER); |
|
|
|
|
|
|
|
v = maybeGasPremiumOrV.toUnsignedBigInteger(); |
|
|
|
|
|
|
|
r = maybeFeeCapOrR.toUnsignedBigInteger(); |
|
|
|
|
|
|
|
s = maybeVOrS.toUnsignedBigInteger(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// otherwise this is an EIP-1559 transaction
|
|
|
|
|
|
|
|
builder.type(TransactionType.EIP1559); |
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Transaction decodeFrontier(final RLPInput input) { |
|
|
|
static Transaction decodeFrontier(final RLPInput input) { |
|
|
|
input.enterList(); |
|
|
|
input.enterList(); |
|
|
|
final Transaction.Builder builder = |
|
|
|
final Transaction.Builder builder = |
|
|
|