1414 */
1515package org .hyperledger .besu .datatypes ;
1616
17- import java .util .Arrays ;
1817import java .util .EnumSet ;
18+ import java .util .Optional ;
1919import java .util .Set ;
2020
2121/** The enum Transaction type. */
2222public 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,65 @@ 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 );
35+ EnumSet .of (ACCESS_LIST , EIP1559 , BLOB , DELEGATE_CODE );
3636
37- private static final EnumSet <TransactionType > LEGACY_FEE_MARKET_TRANSACTION_TYPES =
38- EnumSet .of (TransactionType . FRONTIER , TransactionType . ACCESS_LIST );
37+ private static final Set <TransactionType > LEGACY_FEE_MARKET_TRANSACTION_TYPES =
38+ EnumSet .of (FRONTIER , ACCESS_LIST );
3939
40- private final int typeValue ;
40+ // The theoretical boundaries of the first byte of the RLP of a Frontier transaction.
41+ // In practice Frontier transactions almost always start with 0xf8 or 0xf9.
42+ private static final byte MIN_LEGACY_TX_OPAQUE_BYTE = (byte ) 0xc0 ;
43+ private static final byte MAX_LEGACY_TX_OPAQUE_BYTE = (byte ) 0xfe ;
44+
45+ // This array helps to translate the first opaque byte of an RLP encoded transaction
46+ // to its type.
47+ // Note that Frontier type occupies more slots, since there are different first byte values,
48+ // that represent such type.
49+ // Holes in the array represents invalid first byte values, for which there is not (yet)
50+ // a defined transaction type
51+ private static final TransactionType [] transactionTypeByOpaqueByte =
52+ new TransactionType [Byte .toUnsignedInt (MAX_LEGACY_TX_OPAQUE_BYTE ) + 1 ];
53+
54+ private static final TransactionType [] transactionTypeByEthSerializedType =
55+ new TransactionType [values ().length ];
56+
57+ static {
58+ EnumSet .allOf (TransactionType .class ).stream ()
59+ .forEach (
60+ tt -> {
61+ tt .requireChainId = tt != FRONTIER ;
62+ tt .supportAccessList = ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES .contains (tt );
63+ tt .supportBaseFeeMarket = !LEGACY_FEE_MARKET_TRANSACTION_TYPES .contains (tt );
64+ tt .supportBlob = tt == BLOB ;
65+ tt .supportDelegatedCode = tt == DELEGATE_CODE ;
66+ if (tt == FRONTIER ) {
67+ for (int i = Byte .toUnsignedInt (MIN_LEGACY_TX_OPAQUE_BYTE );
68+ i < Byte .toUnsignedInt (MAX_LEGACY_TX_OPAQUE_BYTE );
69+ i ++) {
70+ transactionTypeByOpaqueByte [i ] = FRONTIER ;
71+ }
72+ } else {
73+ transactionTypeByOpaqueByte [tt .getSerializedType ()] = tt ;
74+ }
75+ transactionTypeByEthSerializedType [tt .getEthSerializedType ()] = tt ;
76+ });
77+ }
78+
79+ private final byte typeValue ;
80+ private final byte serializedType ;
81+ boolean requireChainId ;
82+ boolean supportAccessList ;
83+ boolean supportBaseFeeMarket ;
84+ boolean supportBlob ;
85+ boolean supportDelegatedCode ;
86+
87+ TransactionType (final int typeValue , final int serializedType ) {
88+ this .typeValue = (byte ) typeValue ;
89+ this .serializedType = (byte ) serializedType ;
90+ }
4191
4292 TransactionType (final int typeValue ) {
43- this . typeValue = typeValue ;
93+ this ( typeValue , typeValue ) ;
4494 }
4595
4696 /**
@@ -49,7 +99,7 @@ public enum TransactionType {
4999 * @return the serialized type
50100 */
51101 public byte getSerializedType () {
52- return ( byte ) this . typeValue ;
102+ return typeValue ;
53103 }
54104
55105 /**
@@ -60,30 +110,35 @@ public byte getSerializedType() {
60110 * @return the serialized type
61111 */
62112 public byte getEthSerializedType () {
63- return ( this == FRONTIER ? 0x00 : this . getSerializedType ()) ;
113+ return serializedType ;
64114 }
65115
66116 /**
67- * Convert TransactionType from int serialized type value.
117+ * Convert from opaque byte to type value.
68118 *
69- * @param serializedTypeValue the serialized type value
119+ * @param opaqueByte the opaque byte from serialized bytes
70120 * @return the transaction type
71121 */
72- public static TransactionType of (final int serializedTypeValue ) {
73- return Arrays .stream (
74- new TransactionType [] {
75- TransactionType .FRONTIER ,
76- TransactionType .ACCESS_LIST ,
77- TransactionType .EIP1559 ,
78- TransactionType .BLOB ,
79- TransactionType .DELEGATE_CODE
80- })
81- .filter (transactionType -> transactionType .typeValue == serializedTypeValue )
82- .findFirst ()
83- .orElseThrow (
84- () ->
85- new IllegalArgumentException (
86- String .format ("Unsupported transaction type %x" , serializedTypeValue )));
122+ public static Optional <TransactionType > fromOpaque (final byte opaqueByte ) {
123+ try {
124+ return Optional .ofNullable (transactionTypeByOpaqueByte [Byte .toUnsignedInt (opaqueByte )]);
125+ } catch (final ArrayIndexOutOfBoundsException e ) {
126+ return Optional .empty ();
127+ }
128+ }
129+
130+ /**
131+ * Convert from ETH serialized byte to type value.
132+ *
133+ * @param ethSerializedType the opaque byte from serialized bytes
134+ * @return the transaction type
135+ */
136+ public static Optional <TransactionType > fromEthSerializedType (final byte ethSerializedType ) {
137+ try {
138+ return Optional .ofNullable (transactionTypeByEthSerializedType [ethSerializedType ]);
139+ } catch (final ArrayIndexOutOfBoundsException e ) {
140+ return Optional .empty ();
141+ }
87142 }
88143
89144 /**
@@ -92,7 +147,7 @@ public static TransactionType of(final int serializedTypeValue) {
92147 * @return the boolean
93148 */
94149 public boolean supportsAccessList () {
95- return ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES . contains ( this ) ;
150+ return supportAccessList ;
96151 }
97152
98153 /**
@@ -101,7 +156,7 @@ public boolean supportsAccessList() {
101156 * @return the boolean
102157 */
103158 public boolean supports1559FeeMarket () {
104- return ! LEGACY_FEE_MARKET_TRANSACTION_TYPES . contains ( this ) ;
159+ return supportBaseFeeMarket ;
105160 }
106161
107162 /**
@@ -110,7 +165,7 @@ public boolean supports1559FeeMarket() {
110165 * @return the boolean
111166 */
112167 public boolean requiresChainId () {
113- return ! this . equals ( FRONTIER ) ;
168+ return requireChainId ;
114169 }
115170
116171 /**
@@ -119,7 +174,7 @@ public boolean requiresChainId() {
119174 * @return the boolean
120175 */
121176 public boolean supportsBlob () {
122- return this . equals ( BLOB ) ;
177+ return supportBlob ;
123178 }
124179
125180 /**
@@ -128,15 +183,6 @@ public boolean supportsBlob() {
128183 * @return the boolean
129184 */
130185 public boolean supportsDelegateCode () {
131- return this .equals (DELEGATE_CODE );
132- }
133-
134- /**
135- * Does transaction type require code.
136- *
137- * @return the boolean
138- */
139- public boolean requiresCodeDelegation () {
140- return this .equals (DELEGATE_CODE );
186+ return supportDelegatedCode ;
141187 }
142188}
0 commit comments