18
18
import static org .hyperledger .besu .ethereum .referencetests .ReferenceTestProtocolSchedules .shouldClearEmptyAccounts ;
19
19
20
20
import org .hyperledger .besu .config .StubGenesisConfigOptions ;
21
+ import org .hyperledger .besu .crypto .KeyPair ;
22
+ import org .hyperledger .besu .crypto .SignatureAlgorithm ;
23
+ import org .hyperledger .besu .crypto .SignatureAlgorithmFactory ;
21
24
import org .hyperledger .besu .datatypes .Address ;
22
25
import org .hyperledger .besu .datatypes .DataGas ;
23
26
import org .hyperledger .besu .datatypes .Hash ;
36
39
import org .hyperledger .besu .ethereum .referencetests .ReferenceTestBlockchain ;
37
40
import org .hyperledger .besu .ethereum .referencetests .ReferenceTestEnv ;
38
41
import org .hyperledger .besu .ethereum .referencetests .ReferenceTestProtocolSchedules ;
42
+ import org .hyperledger .besu .ethereum .rlp .BytesValueRLPInput ;
39
43
import org .hyperledger .besu .ethereum .rlp .BytesValueRLPOutput ;
40
44
import org .hyperledger .besu .ethereum .rlp .RLP ;
41
45
import org .hyperledger .besu .ethereum .worldstate .DefaultMutableWorldState ;
46
+ import org .hyperledger .besu .evm .AccessListEntry ;
42
47
import org .hyperledger .besu .evm .account .Account ;
43
48
import org .hyperledger .besu .evm .account .AccountStorageEntry ;
44
49
import org .hyperledger .besu .evm .log .Log ;
45
50
import org .hyperledger .besu .evm .tracing .OperationTracer ;
46
51
import org .hyperledger .besu .evm .worldstate .WorldUpdater ;
47
52
import org .hyperledger .besu .evmtool .exception .UnsupportedForkException ;
53
+ import org .hyperledger .besu .plugin .data .TransactionType ;
48
54
49
55
import java .io .IOException ;
56
+ import java .io .PrintWriter ;
50
57
import java .math .BigInteger ;
51
58
import java .util .ArrayList ;
52
59
import java .util .Comparator ;
60
+ import java .util .Iterator ;
53
61
import java .util .List ;
54
62
import java .util .NavigableMap ;
63
+ import java .util .Spliterator ;
64
+ import java .util .Spliterators ;
55
65
import java .util .concurrent .TimeUnit ;
66
+ import java .util .stream .StreamSupport ;
56
67
68
+ import com .fasterxml .jackson .databind .JsonNode ;
57
69
import com .fasterxml .jackson .databind .ObjectMapper ;
58
70
import com .fasterxml .jackson .databind .node .ArrayNode ;
59
71
import com .fasterxml .jackson .databind .node .ObjectNode ;
64
76
import org .apache .tuweni .units .bigints .UInt256 ;
65
77
66
78
public class T8nExecutor {
79
+
80
+ record RejectedTransaction (int index , String error ) {}
81
+
82
+ protected static List <Transaction > extractTransactions (
83
+ final PrintWriter out ,
84
+ final Iterator <JsonNode > it ,
85
+ final List <Transaction > transactions ,
86
+ final List <RejectedTransaction > rejections ) {
87
+ int i = 0 ;
88
+ while (it .hasNext ()) {
89
+ try {
90
+ JsonNode txNode = it .next ();
91
+ if (txNode .isTextual ()) {
92
+ BytesValueRLPInput rlpInput =
93
+ new BytesValueRLPInput (Bytes .fromHexString (txNode .asText ()), false );
94
+ rlpInput .enterList ();
95
+ while (!rlpInput .isEndOfCurrentList ()) {
96
+ Transaction tx = Transaction .readFrom (rlpInput );
97
+ transactions .add (tx );
98
+ }
99
+ } else if (txNode .isObject ()) {
100
+ if (txNode .has ("txBytes" )) {
101
+ Transaction tx =
102
+ Transaction .readFrom (Bytes .fromHexString (txNode .get ("txbytes" ).asText ()));
103
+ transactions .add (tx );
104
+ } else {
105
+ Transaction .Builder builder = Transaction .builder ();
106
+ int type = Bytes .fromHexStringLenient (txNode .get ("type" ).textValue ()).toInt ();
107
+ TransactionType transactionType = TransactionType .of (type == 0 ? 0xf8 : type );
108
+ builder .type (transactionType );
109
+ builder .nonce (Bytes .fromHexStringLenient (txNode .get ("nonce" ).textValue ()).toLong ());
110
+ builder .gasLimit (Bytes .fromHexStringLenient (txNode .get ("gas" ).textValue ()).toLong ());
111
+ builder .value (Wei .fromHexString (txNode .get ("value" ).textValue ()));
112
+ builder .payload (Bytes .fromHexString (txNode .get ("input" ).textValue ()));
113
+
114
+ if (txNode .has ("gasPrice" )) {
115
+ builder .gasPrice (Wei .fromHexString (txNode .get ("gasPrice" ).textValue ()));
116
+ }
117
+ if (txNode .has ("maxPriorityFeePerGas" )) {
118
+ builder .maxPriorityFeePerGas (
119
+ Wei .fromHexString (txNode .get ("maxPriorityFeePerGas" ).textValue ()));
120
+ }
121
+ if (txNode .has ("maxFeePerGas" )) {
122
+ builder .maxFeePerGas (Wei .fromHexString (txNode .get ("maxFeePerGas" ).textValue ()));
123
+ }
124
+ if (txNode .has ("maxFeePerDataGas" )) {
125
+ builder .maxFeePerDataGas (
126
+ Wei .fromHexString (txNode .get ("maxFeePerDataGas" ).textValue ()));
127
+ }
128
+
129
+ if (txNode .has ("to" )) {
130
+ builder .to (Address .fromHexString (txNode .get ("to" ).textValue ()));
131
+ }
132
+
133
+ if (transactionType .requiresChainId ()
134
+ || !txNode .has ("protected" )
135
+ || txNode .get ("protected" ).booleanValue ()) {
136
+ // chainid if protected
137
+ builder .chainId (
138
+ new BigInteger (
139
+ 1 ,
140
+ Bytes .fromHexStringLenient (txNode .get ("chainId" ).textValue ())
141
+ .toArrayUnsafe ()));
142
+ }
143
+
144
+ if (txNode .has ("accessList" )) {
145
+ JsonNode accessList = txNode .get ("accessList" );
146
+ if (!accessList .isArray ()) {
147
+ out .printf (
148
+ "TX json node unparseable: expected accessList to be an array - %s%n" , txNode );
149
+ continue ;
150
+ }
151
+ List <AccessListEntry > entries = new ArrayList <>(accessList .size ());
152
+ for (JsonNode entryAsJson : accessList ) {
153
+ Address address = Address .fromHexString (entryAsJson .get ("address" ).textValue ());
154
+ List <String > storageKeys =
155
+ StreamSupport .stream (
156
+ Spliterators .spliteratorUnknownSize (
157
+ entryAsJson .get ("storageKeys" ).elements (), Spliterator .ORDERED ),
158
+ false )
159
+ .map (JsonNode ::textValue )
160
+ .toList ();
161
+ var accessListEntry = AccessListEntry .createAccessListEntry (address , storageKeys );
162
+ entries .add (accessListEntry );
163
+ }
164
+ builder .accessList (entries );
165
+ }
166
+
167
+ if (txNode .has ("blobVersionedHashes" )) {
168
+ JsonNode blobVersionedHashes = txNode .get ("blobVersionedHashes" );
169
+ if (!blobVersionedHashes .isArray ()) {
170
+ out .printf (
171
+ "TX json node unparseable: expected blobVersionedHashes to be an array - %s%n" ,
172
+ txNode );
173
+ continue ;
174
+ }
175
+ // FUTURE: placeholder code until 4844 PR merges
176
+ // List<VersionedHash> entries = new ArrayList<>(blobVersionedHashes.size());
177
+ // for (JsonNode versionedHashNode : blobVersionedHashes) {
178
+ // entries.add(
179
+ // new VersionedHash(Bytes32.fromHexString(versionedHashNode.textValue())));
180
+ // }
181
+ // builder.versionedHashes(entries);
182
+ }
183
+
184
+ if (txNode .has ("secretKey" )) {
185
+ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory .getInstance ();
186
+ KeyPair keys =
187
+ signatureAlgorithm .createKeyPair (
188
+ signatureAlgorithm .createPrivateKey (
189
+ Bytes32 .fromHexString (txNode .get ("secretKey" ).textValue ())));
190
+
191
+ transactions .add (builder .signAndBuild (keys ));
192
+ } else {
193
+ BigInteger v =
194
+ Bytes .fromHexStringLenient (txNode .get ("v" ).textValue ()).toUnsignedBigInteger ();
195
+ if (v .compareTo (BigInteger .valueOf (35 )) >= 0 ) {
196
+ v = v .subtract (BigInteger .valueOf (35 )).mod (BigInteger .TWO );
197
+ } else if (v .compareTo (BigInteger .valueOf (27 )) >= 0 ) {
198
+ v = v .subtract (BigInteger .valueOf (27 )).mod (BigInteger .TWO );
199
+ }
200
+ builder .signature (
201
+ SignatureAlgorithmFactory .getInstance ()
202
+ .createSignature (
203
+ Bytes .fromHexStringLenient (txNode .get ("r" ).textValue ())
204
+ .toUnsignedBigInteger (),
205
+ Bytes .fromHexStringLenient (txNode .get ("s" ).textValue ())
206
+ .toUnsignedBigInteger (),
207
+ v .byteValueExact ()));
208
+ transactions .add (builder .build ());
209
+ }
210
+ }
211
+ } else {
212
+ out .printf ("TX json node unparseable: %s%n" , txNode );
213
+ }
214
+ } catch (IllegalArgumentException iae ) {
215
+ rejections .add (new RejectedTransaction (i , iae .getMessage ()));
216
+ }
217
+ i ++;
218
+ }
219
+ return transactions ;
220
+ }
221
+
67
222
static T8nResult runTest (
68
223
final Long chainId ,
69
224
final String fork ,
@@ -72,6 +227,7 @@ static T8nResult runTest(
72
227
final ReferenceTestEnv referenceTestEnv ,
73
228
final MutableWorldState initialWorldState ,
74
229
final List <Transaction > transactions ,
230
+ final List <RejectedTransaction > rejections ,
75
231
final TracerManager tracerManager ) {
76
232
77
233
final ReferenceTestProtocolSchedules referenceTestProtocolSchedules =
@@ -95,7 +251,7 @@ static T8nResult runTest(
95
251
final Wei dataGasPrice = protocolSpec .getFeeMarket ().dataPrice (DataGas .ZERO );
96
252
97
253
List <TransactionReceipt > receipts = new ArrayList <>();
98
- List <T8nSubCommand . RejectedTransaction > invalidTransactions = new ArrayList <>();
254
+ List <RejectedTransaction > invalidTransactions = new ArrayList <>(rejections );
99
255
List <Transaction > validTransactions = new ArrayList <>();
100
256
ArrayNode receiptsArray = objectMapper .createArrayNode ();
101
257
long gasUsed = 0 ;
@@ -138,8 +294,7 @@ static T8nResult runTest(
138
294
}
139
295
if (result .isInvalid ()) {
140
296
invalidTransactions .add (
141
- new T8nSubCommand .RejectedTransaction (
142
- i , result .getValidationResult ().getErrorMessage ()));
297
+ new RejectedTransaction (i , result .getValidationResult ().getErrorMessage ()));
143
298
} else {
144
299
validTransactions .add (transaction );
145
300
0 commit comments