Skip to content

Commit 210eae6

Browse files
committed
Various TransactionType small optimizations
Signed-off-by: Fabio Di Fabio <[email protected]>
1 parent 5a46c18 commit 210eae6

File tree

9 files changed

+132
-69
lines changed

9 files changed

+132
-69
lines changed

datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
*/
1515
package org.hyperledger.besu.datatypes;
1616

17-
import java.util.Arrays;
1817
import java.util.EnumSet;
18+
import java.util.Optional;
1919
import java.util.Set;
2020

2121
/** The enum Transaction type. */
2222
public enum TransactionType {
2323
/** The Frontier. */
24-
FRONTIER(0xf8 /* this is serialized as 0x0 in TransactionCompleteResult */),
24+
FRONTIER(0xf8, 0x00),
2525
/** Access list transaction type. */
2626
ACCESS_LIST(0x01),
2727
/** Eip1559 transaction type. */
@@ -32,15 +32,44 @@ public enum TransactionType {
3232
DELEGATE_CODE(0x04);
3333

3434
private static final Set<TransactionType> ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES =
35-
Set.of(ACCESS_LIST, EIP1559, BLOB, DELEGATE_CODE);
36-
37-
private static final EnumSet<TransactionType> LEGACY_FEE_MARKET_TRANSACTION_TYPES =
38-
EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST);
35+
EnumSet.of(ACCESS_LIST, EIP1559, BLOB, DELEGATE_CODE);
36+
37+
private static final Set<TransactionType> LEGACY_FEE_MARKET_TRANSACTION_TYPES =
38+
EnumSet.of(FRONTIER, ACCESS_LIST);
39+
40+
private static final TransactionType[] transactionTypeBySerializedType =
41+
new TransactionType[values().length];
42+
43+
static {
44+
EnumSet.allOf(TransactionType.class).stream()
45+
.forEach(
46+
tt -> {
47+
tt.requireChainId = tt != FRONTIER;
48+
tt.supportAccessList = ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES.contains(tt);
49+
tt.supportBaseFeeMarket = !LEGACY_FEE_MARKET_TRANSACTION_TYPES.contains(tt);
50+
tt.supportBlob = tt == BLOB;
51+
tt.supportDelegatedCode = tt == DELEGATE_CODE;
52+
if (tt != FRONTIER) {
53+
transactionTypeBySerializedType[tt.getSerializedType()] = tt;
54+
}
55+
});
56+
}
3957

40-
private final int typeValue;
58+
private final byte typeValue;
59+
private final byte serializedType;
60+
boolean requireChainId;
61+
boolean supportAccessList;
62+
boolean supportBaseFeeMarket;
63+
boolean supportBlob;
64+
boolean supportDelegatedCode;
65+
66+
TransactionType(final int typeValue, final int serializedType) {
67+
this.typeValue = (byte) typeValue;
68+
this.serializedType = (byte) serializedType;
69+
}
4170

4271
TransactionType(final int typeValue) {
43-
this.typeValue = typeValue;
72+
this(typeValue, typeValue);
4473
}
4574

4675
/**
@@ -49,7 +78,7 @@ public enum TransactionType {
4978
* @return the serialized type
5079
*/
5180
public byte getSerializedType() {
52-
return (byte) this.typeValue;
81+
return typeValue;
5382
}
5483

5584
/**
@@ -60,40 +89,21 @@ public byte getSerializedType() {
6089
* @return the serialized type
6190
*/
6291
public byte getEthSerializedType() {
63-
return (this == FRONTIER ? 0x00 : this.getSerializedType());
64-
}
65-
66-
/**
67-
* Compare to serialized type.
68-
*
69-
* @param b the byte value
70-
* @return the int result of comparison
71-
*/
72-
public int compareTo(final Byte b) {
73-
return Byte.valueOf(getSerializedType()).compareTo(b);
92+
return serializedType;
7493
}
7594

7695
/**
77-
* Convert TransactionType from int serialized type value.
96+
* Convert TransactionType from byte serialized type value.
7897
*
7998
* @param serializedTypeValue the serialized type value
8099
* @return the transaction type
81100
*/
82-
public static TransactionType of(final int serializedTypeValue) {
83-
return Arrays.stream(
84-
new TransactionType[] {
85-
TransactionType.FRONTIER,
86-
TransactionType.ACCESS_LIST,
87-
TransactionType.EIP1559,
88-
TransactionType.BLOB,
89-
TransactionType.DELEGATE_CODE
90-
})
91-
.filter(transactionType -> transactionType.typeValue == serializedTypeValue)
92-
.findFirst()
93-
.orElseThrow(
94-
() ->
95-
new IllegalArgumentException(
96-
String.format("Unsupported transaction type %x", serializedTypeValue)));
101+
public static Optional<TransactionType> of(final byte serializedTypeValue) {
102+
try {
103+
return Optional.ofNullable(transactionTypeBySerializedType[serializedTypeValue]);
104+
} catch (final ArrayIndexOutOfBoundsException e) {
105+
return Optional.empty();
106+
}
97107
}
98108

99109
/**
@@ -102,7 +112,7 @@ public static TransactionType of(final int serializedTypeValue) {
102112
* @return the boolean
103113
*/
104114
public boolean supportsAccessList() {
105-
return ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES.contains(this);
115+
return supportAccessList;
106116
}
107117

108118
/**
@@ -111,7 +121,7 @@ public boolean supportsAccessList() {
111121
* @return the boolean
112122
*/
113123
public boolean supports1559FeeMarket() {
114-
return !LEGACY_FEE_MARKET_TRANSACTION_TYPES.contains(this);
124+
return supportBaseFeeMarket;
115125
}
116126

117127
/**
@@ -120,7 +130,7 @@ public boolean supports1559FeeMarket() {
120130
* @return the boolean
121131
*/
122132
public boolean requiresChainId() {
123-
return !this.equals(FRONTIER);
133+
return requireChainId;
124134
}
125135

126136
/**
@@ -129,7 +139,7 @@ public boolean requiresChainId() {
129139
* @return the boolean
130140
*/
131141
public boolean supportsBlob() {
132-
return this.equals(BLOB);
142+
return supportBlob;
133143
}
134144

135145
/**
@@ -138,15 +148,6 @@ public boolean supportsBlob() {
138148
* @return the boolean
139149
*/
140150
public boolean supportsDelegateCode() {
141-
return this.equals(DELEGATE_CODE);
142-
}
143-
144-
/**
145-
* Does transaction type require code.
146-
*
147-
* @return the boolean
148-
*/
149-
public boolean requiresCodeDelegation() {
150-
return this.equals(DELEGATE_CODE);
151+
return supportDelegatedCode;
151152
}
152153
}

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/SimpleTestTransactionBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static Transaction transaction(
7070
when(transaction.getR()).thenReturn(bigInteger(r));
7171
when(transaction.getS()).thenReturn(bigInteger(s));
7272
when(transaction.getTo()).thenReturn(Optional.ofNullable(address(toAddress)));
73-
when(transaction.getType()).thenReturn(TransactionType.of(Integer.decode(type)));
73+
when(transaction.getType()).thenReturn(TransactionType.of(Byte.decode(type)).get());
7474
when(transaction.getSender()).thenReturn(address(fromAddress));
7575
when(transaction.getPayload()).thenReturn(Bytes.fromHexString(input));
7676
when(transaction.getValue()).thenReturn(wei(value));
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.core;
16+
17+
import org.hyperledger.besu.datatypes.TransactionType;
18+
19+
import java.util.concurrent.TimeUnit;
20+
21+
import org.openjdk.jmh.annotations.Benchmark;
22+
import org.openjdk.jmh.annotations.BenchmarkMode;
23+
import org.openjdk.jmh.annotations.Measurement;
24+
import org.openjdk.jmh.annotations.Mode;
25+
import org.openjdk.jmh.annotations.OutputTimeUnit;
26+
import org.openjdk.jmh.annotations.Scope;
27+
import org.openjdk.jmh.annotations.Setup;
28+
import org.openjdk.jmh.annotations.State;
29+
import org.openjdk.jmh.annotations.Warmup;
30+
import org.openjdk.jmh.infra.Blackhole;
31+
32+
@State(Scope.Thread)
33+
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
34+
@OutputTimeUnit(value = TimeUnit.NANOSECONDS)
35+
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
36+
@BenchmarkMode(Mode.AverageTime)
37+
public class TransactionTypeBenchmark {
38+
39+
@Setup()
40+
public void setUp() {}
41+
42+
@Benchmark
43+
public void ofSerializedType(final Blackhole blackhole) {
44+
blackhole.consume(TransactionType.of((byte) 0xf8));
45+
blackhole.consume(TransactionType.of((byte) 0x01));
46+
blackhole.consume(TransactionType.of((byte) 0x02));
47+
blackhole.consume(TransactionType.of((byte) 0x03));
48+
blackhole.consume(TransactionType.of((byte) 0x04));
49+
}
50+
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ private Transaction(
246246
maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction");
247247
}
248248

249-
if (transactionType.requiresCodeDelegation()) {
249+
if (transactionType.supportsDelegateCode()) {
250250
checkArgument(
251251
maybeCodeDelegationList.isPresent(),
252252
"Must specify code delegation authorizations for code delegation transaction");

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public static Transaction decodeOpaqueBytes(
111111
if (opaqueBytes.isEmpty()) {
112112
throw new IllegalArgumentException("Empty opaque bytes");
113113
}
114-
var transactionType = getTransactionType(opaqueBytes);
114+
final var transactionType = getTransactionType(opaqueBytes);
115115
if (transactionType.isPresent()) {
116116
return decodeTypedTransaction(opaqueBytes, transactionType.get(), context);
117117
} else {
@@ -130,15 +130,8 @@ public static Transaction decodeOpaqueBytes(
130130
* to a valid transaction type, or an empty Optional if it does not
131131
*/
132132
private static Optional<TransactionType> getTransactionType(final Bytes opaqueBytes) {
133-
try {
134-
if (opaqueBytes.isEmpty()) {
135-
return Optional.empty();
136-
}
137-
byte transactionTypeByte = opaqueBytes.get(0);
138-
return Optional.of(TransactionType.of(transactionTypeByte));
139-
} catch (IllegalArgumentException ex) {
140-
return Optional.empty();
141-
}
133+
byte transactionTypeByte = opaqueBytes.get(0);
134+
return TransactionType.of(transactionTypeByte);
142135
}
143136

144137
/**

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/receipt/TransactionReceiptDecoder.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ private static TransactionReceipt decodeTypedReceipt(
6767
final RLPInput rlpInput, final boolean revertReasonAllowed) {
6868
RLPInput input = rlpInput;
6969
final Bytes typedTransactionReceiptBytes = input.readBytes();
70-
TransactionType transactionType = TransactionType.of(typedTransactionReceiptBytes.get(0));
70+
TransactionType transactionType =
71+
TransactionType.of(typedTransactionReceiptBytes.get(0))
72+
.orElseThrow(
73+
() ->
74+
new IllegalStateException(
75+
"Invalid transaction type %x"
76+
.formatted(typedTransactionReceiptBytes.get(0))));
7177
input = new BytesValueRLPInput(typedTransactionReceiptBytes.slice(1), false);
7278
input.enterList();
7379
final RLPInput statusOrStateRoot = input.readAsRlp();
@@ -118,9 +124,13 @@ private static TransactionReceipt decodeFlatReceipt(
118124

119125
private static TransactionReceipt decodeEth69Receipt(
120126
final RLPInput input, final RLPInput transactionByteRlp, final RLPInput statusOrStateRoot) {
121-
int transactionByte = transactionByteRlp.readIntScalar();
127+
byte transactionByte = transactionByteRlp.readByte();
122128
final TransactionType transactionType =
123-
transactionByte == 0x00 ? TransactionType.FRONTIER : TransactionType.of(transactionByte);
129+
TransactionType.of(transactionByte)
130+
.orElseThrow(
131+
() ->
132+
new IllegalStateException(
133+
"Invalid transaction type %x".formatted(transactionByte)));
124134
final long cumulativeGas = input.readLongScalar();
125135
final List<Log> logs = input.readList(logInput -> Log.readFrom(logInput, false));
126136
final LogsBloomFilter bloomFilter = LogsBloomFilter.builder().insertLogs(logs).build();

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/receipt/TransactionReceiptEncoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private static void writeLegacyReceipt(
7575
final RLPOutput rlpOutput,
7676
final TransactionReceiptEncodingConfiguration options) {
7777
if (!receipt.getTransactionType().equals(TransactionType.FRONTIER)) {
78-
rlpOutput.writeIntScalar(receipt.getTransactionType().getSerializedType());
78+
rlpOutput.writeByte(receipt.getTransactionType().getSerializedType());
7979
}
8080
rlpOutput.startList();
8181
writeStatusOrStateRoot(receipt, rlpOutput);
@@ -135,7 +135,7 @@ private static void writeEth69Receipt(
135135
final RLPOutput output,
136136
final TransactionReceiptEncodingConfiguration options) {
137137
output.startList();
138-
output.writeIntScalar(receipt.getTransactionType().getEthSerializedType());
138+
output.writeByte(receipt.getTransactionType().getEthSerializedType());
139139
writeStatusOrStateRoot(receipt, output);
140140
output.writeLongScalar(receipt.getCumulativeGasUsed());
141141
writeLogs(receipt, output, options);

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/encoding/TransactionAnnouncementDecoder.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ private static List<TransactionAnnouncement> decodeForEth68(final RLPInput input
7777
final List<TransactionType> types = new ArrayList<>();
7878
final byte[] bytes = input.readBytes().toArray();
7979
for (final byte b : bytes) {
80-
types.add(b == 0 ? TransactionType.FRONTIER : TransactionType.of(b));
80+
final var transactionType =
81+
TransactionType.of(b)
82+
.orElseThrow(
83+
() -> new IllegalStateException("Invalid transaction type %x".formatted(b)));
84+
types.add(transactionType);
8185
}
8286

8387
List<Long> sizes =

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,12 @@ protected static List<Transaction> extractTransactions(
158158
BigInteger chainId =
159159
Bytes.fromHexStringLenient(txNode.get("chainId").textValue())
160160
.toUnsignedBigInteger();
161-
TransactionType transactionType = TransactionType.of(type == 0 ? 0xf8 : type);
161+
TransactionType transactionType =
162+
TransactionType.of((byte) type)
163+
.orElseThrow(
164+
(() ->
165+
new IllegalArgumentException(
166+
"Unsupported transaction type: %x".formatted(type))));
162167
builder.type(transactionType);
163168
builder.nonce(Bytes.fromHexStringLenient(txNode.get("nonce").textValue()).toLong());
164169
builder.gasLimit(Bytes.fromHexStringLenient(txNode.get("gas").textValue()).toLong());

0 commit comments

Comments
 (0)