Skip to content

Commit b6cc030

Browse files
authored
Fix BAL generation during parallel preprocessing (#9352)
Signed-off-by: Miroslav Kovar <[email protected]>
1 parent 1b9feec commit b6cc030

File tree

5 files changed

+132
-24
lines changed

5 files changed

+132
-24
lines changed

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ public BlockProcessingResult processBlock(
235235
transactions,
236236
miningBeneficiary,
237237
blockHashLookup,
238-
blobGasPrice);
238+
blobGasPrice,
239+
blockAccessListBuilder);
239240

240241
boolean parallelizedTxFound = false;
241242
int nbParallelTx = 0;
@@ -549,7 +550,8 @@ Optional<PreprocessingContext> run(
549550
final List<Transaction> transactions,
550551
final Address miningBeneficiary,
551552
final BlockHashLookup blockHashLookup,
552-
final Wei blobGasPrice);
553+
final Wei blobGasPrice,
554+
final Optional<BlockAccessListBuilder> blockAccessListBuilder);
553555

554556
class NoPreprocessing implements PreprocessingFunction {
555557

@@ -560,7 +562,8 @@ public Optional<PreprocessingContext> run(
560562
final List<Transaction> transactions,
561563
final Address miningBeneficiary,
562564
final BlockHashLookup blockHashLookup,
563-
final Wei blobGasPrice) {
565+
final Wei blobGasPrice,
566+
final Optional<BlockAccessListBuilder> blockAccessListBuilder) {
564567
return Optional.empty();
565568
}
566569
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/block/access/list/PartialBlockAccessView.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public String toString() {
9696

9797
public static final class AccountChanges {
9898
private final Address address;
99-
private final Optional<Wei> postBalance;
99+
private Optional<Wei> postBalance;
100100
private final Optional<Long> nonceChange;
101101
private final Optional<Bytes> newCode;
102102
private final List<StorageSlotKey> storageReads;
@@ -125,6 +125,10 @@ public Optional<Wei> getPostBalance() {
125125
return postBalance;
126126
}
127127

128+
public void setPostBalance(final Wei postBalance) {
129+
this.postBalance = Optional.ofNullable(postBalance);
130+
}
131+
128132
public Optional<Long> getNonceChange() {
129133
return nonceChange;
130134
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelTransactionPreprocessing.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.PreprocessingContext;
2323
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.PreprocessingFunction;
2424
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
25+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder;
2526
import org.hyperledger.besu.ethereum.mainnet.parallelization.MainnetParallelBlockProcessor.ParallelizedPreProcessingContext;
2627
import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.PathBasedWorldStateProvider;
2728
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
@@ -48,7 +49,8 @@ public Optional<PreprocessingContext> run(
4849
final List<Transaction> transactions,
4950
final Address miningBeneficiary,
5051
final BlockHashLookup blockHashLookup,
51-
final Wei blobGasPrice) {
52+
final Wei blobGasPrice,
53+
final Optional<BlockAccessListBuilder> blockAccessListBuilder) {
5254
if ((protocolContext.getWorldStateArchive() instanceof PathBasedWorldStateProvider)) {
5355
ParallelizedConcurrentTransactionProcessor parallelizedConcurrentTransactionProcessor =
5456
new ParallelizedConcurrentTransactionProcessor(transactionProcessor);
@@ -61,7 +63,8 @@ public Optional<PreprocessingContext> run(
6163
miningBeneficiary,
6264
blockHashLookup,
6365
blobGasPrice,
64-
executor);
66+
executor,
67+
blockAccessListBuilder);
6568
return Optional.of(
6669
new ParallelizedPreProcessingContext(parallelizedConcurrentTransactionProcessor));
6770
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
import org.hyperledger.besu.ethereum.core.Transaction;
2323
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
2424
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
25+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.AccessLocationTracker;
26+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder;
2527
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
2628
import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState;
2729
import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams;
2830
import org.hyperledger.besu.ethereum.trie.pathbased.common.worldview.PathBasedWorldState;
2931
import org.hyperledger.besu.ethereum.trie.pathbased.common.worldview.accumulator.PathBasedWorldStateUpdateAccumulator;
32+
import org.hyperledger.besu.evm.account.MutableAccount;
3033
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
3134
import org.hyperledger.besu.evm.tracing.OperationTracer;
35+
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
3236
import org.hyperledger.besu.evm.worldstate.WorldView;
3337
import org.hyperledger.besu.plugin.services.metrics.Counter;
3438

@@ -93,6 +97,7 @@ public ParallelizedConcurrentTransactionProcessor(
9397
* @param blockHashLookup Function for block hash lookup.
9498
* @param blobGasPrice Gas price for blob transactions.
9599
* @param executor The executor to use for asynchronous execution.
100+
* @param blockAccessListBuilder BAL builder.
96101
*/
97102
public void runAsyncBlock(
98103
final ProtocolContext protocolContext,
@@ -101,7 +106,8 @@ public void runAsyncBlock(
101106
final Address miningBeneficiary,
102107
final BlockHashLookup blockHashLookup,
103108
final Wei blobGasPrice,
104-
final Executor executor) {
109+
final Executor executor,
110+
final Optional<BlockAccessListBuilder> blockAccessListBuilder) {
105111

106112
completableFuturesForBackgroundTransactions = new CompletableFuture[transactions.size()];
107113
for (int i = 0; i < transactions.size(); i++) {
@@ -119,7 +125,8 @@ public void runAsyncBlock(
119125
transaction,
120126
miningBeneficiary,
121127
blockHashLookup,
122-
blobGasPrice),
128+
blobGasPrice,
129+
blockAccessListBuilder),
123130
executor);
124131
}
125132
}
@@ -132,7 +139,8 @@ public void runTransaction(
132139
final Transaction transaction,
133140
final Address miningBeneficiary,
134141
final BlockHashLookup blockHashLookup,
135-
final Wei blobGasPrice) {
142+
final Wei blobGasPrice,
143+
final Optional<BlockAccessListBuilder> blockAccessListBuilder) {
136144
final BlockHeader chainHeadHeader = protocolContext.getBlockchain().getChainHeadHeader();
137145
if (chainHeadHeader.getHash().equals(blockHeader.getParentHash())) {
138146
try (BonsaiWorldState ws =
@@ -148,9 +156,15 @@ public void runTransaction(
148156
new ParallelizedTransactionContext.Builder();
149157
final PathBasedWorldStateUpdateAccumulator<?> roundWorldStateUpdater =
150158
(PathBasedWorldStateUpdateAccumulator<?>) ws.updater();
159+
final WorldUpdater transactionUpdater = roundWorldStateUpdater.updater();
160+
final Optional<AccessLocationTracker> transactionLocationTracker =
161+
blockAccessListBuilder.map(
162+
b ->
163+
BlockAccessListBuilder.createTransactionAccessLocationTracker(
164+
transactionLocation));
151165
final TransactionProcessingResult result =
152166
transactionProcessor.processTransaction(
153-
roundWorldStateUpdater,
167+
transactionUpdater,
154168
blockHeader,
155169
transaction.detachedCopy(),
156170
miningBeneficiary,
@@ -177,10 +191,12 @@ public void traceBeforeRewardTransaction(
177191
},
178192
blockHashLookup,
179193
TransactionValidationParams.processingBlock(),
180-
blobGasPrice);
194+
blobGasPrice,
195+
transactionLocationTracker);
181196

182197
// commit the accumulator in order to apply all the modifications
183-
ws.getAccumulator().commit();
198+
transactionUpdater.commit();
199+
roundWorldStateUpdater.commit();
184200

185201
contextBuilder
186202
.transactionAccumulator(ws.getAccumulator())
@@ -252,11 +268,26 @@ public Optional<TransactionProcessingResult> applyParallelizedTransactionResult(
252268
transactionCollisionDetector.hasCollision(
253269
transaction, miningBeneficiary, parallelizedTransactionContext, blockAccumulator);
254270
if (transactionProcessingResult.isSuccessful() && !hasCollision) {
271+
final MutableAccount miningBeneficiaryAccount =
272+
blockAccumulator.getOrCreate(miningBeneficiary);
255273
Wei reward = parallelizedTransactionContext.miningBeneficiaryReward();
256274
if (!reward.isZero() || !transactionProcessor.getClearEmptyAccounts()) {
257-
blockAccumulator.getOrCreate(miningBeneficiary).incrementBalance(reward);
275+
miningBeneficiaryAccount.incrementBalance(reward);
258276
}
259277

278+
final Wei miningBeneficiaryPostBalance = miningBeneficiaryAccount.getBalance();
279+
transactionProcessingResult
280+
.getPartialBlockAccessView()
281+
.ifPresent(
282+
partialBlockAccessView ->
283+
partialBlockAccessView.accountChanges().stream()
284+
.filter(
285+
accountChanges -> accountChanges.getAddress().equals(miningBeneficiary))
286+
.findFirst()
287+
.ifPresent(
288+
accountChanges ->
289+
accountChanges.setPostBalance(miningBeneficiaryPostBalance)));
290+
260291
blockAccumulator.importStateChangesFromSource(transactionAccumulator);
261292

262293
if (confirmedParallelizedTransactionCounter.isPresent()) {

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static org.hyperledger.besu.ethereum.trie.pathbased.common.worldview.WorldStateConfig.createStatefulConfigWithTrie;
1818
import static org.junit.jupiter.api.Assertions.assertTrue;
1919
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.argThat;
2021
import static org.mockito.ArgumentMatchers.eq;
2122
import static org.mockito.Mockito.mock;
2223
import static org.mockito.Mockito.times;
@@ -34,6 +35,8 @@
3435
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
3536
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
3637
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
38+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder;
39+
import org.hyperledger.besu.ethereum.mainnet.block.access.list.PartialBlockAccessView;
3740
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
3841
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
3942
import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.cache.CodeCache;
@@ -42,12 +45,12 @@
4245
import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.storage.BonsaiWorldStateKeyValueStorage;
4346
import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState;
4447
import org.hyperledger.besu.ethereum.trie.pathbased.common.trielog.NoOpTrieLogManager;
45-
import org.hyperledger.besu.ethereum.trie.pathbased.common.worldview.accumulator.PathBasedWorldStateUpdateAccumulator;
4648
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
4749
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
4850
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
4951
import org.hyperledger.besu.evm.internal.EvmConfiguration;
5052
import org.hyperledger.besu.evm.tracing.OperationTracer;
53+
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
5154
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
5255

5356
import java.util.Collections;
@@ -118,7 +121,7 @@ void testRunTransaction() {
118121

119122
Mockito.when(
120123
transactionProcessor.processTransaction(
121-
any(), any(), any(), any(), any(), any(), any(), any()))
124+
any(), any(), any(), any(), any(), any(), any(), any(), any()))
122125
.thenReturn(
123126
TransactionProcessingResult.successful(
124127
Collections.emptyList(),
@@ -135,18 +138,20 @@ void testRunTransaction() {
135138
transaction,
136139
miningBeneficiary,
137140
(__, ___) -> Hash.EMPTY,
138-
blobGasPrice);
141+
blobGasPrice,
142+
Optional.empty());
139143

140144
verify(transactionProcessor, times(1))
141145
.processTransaction(
142-
any(PathBasedWorldStateUpdateAccumulator.class),
146+
any(WorldUpdater.class),
143147
eq(blockHeader),
144148
eq(transaction),
145149
eq(miningBeneficiary),
146150
any(OperationTracer.class),
147151
any(BlockHashLookup.class),
148152
eq(TransactionValidationParams.processingBlock()),
149-
eq(blobGasPrice));
153+
eq(blobGasPrice),
154+
eq(Optional.empty()));
150155

151156
assertTrue(
152157
processor
@@ -162,7 +167,7 @@ void testRunTransactionWithFailure() {
162167
Wei blobGasPrice = Wei.ZERO;
163168

164169
when(transactionProcessor.processTransaction(
165-
any(), any(), any(), any(), any(), any(), any(), any()))
170+
any(), any(), any(), any(), any(), any(), any(), any(), any()))
166171
.thenReturn(
167172
TransactionProcessingResult.failed(
168173
0,
@@ -180,7 +185,8 @@ void testRunTransactionWithFailure() {
180185
transaction,
181186
miningBeneficiary,
182187
(__, ___) -> Hash.EMPTY,
183-
blobGasPrice);
188+
blobGasPrice,
189+
Optional.empty());
184190

185191
Optional<TransactionProcessingResult> result =
186192
processor.applyParallelizedTransactionResult(
@@ -196,7 +202,7 @@ void testRunTransactionWithConflict() {
196202

197203
Mockito.when(
198204
transactionProcessor.processTransaction(
199-
any(), any(), any(), any(), any(), any(), any(), any()))
205+
any(), any(), any(), any(), any(), any(), any(), any(), any()))
200206
.thenReturn(
201207
TransactionProcessingResult.successful(
202208
Collections.emptyList(),
@@ -213,18 +219,20 @@ void testRunTransactionWithConflict() {
213219
transaction,
214220
miningBeneficiary,
215221
(__, ___) -> Hash.EMPTY,
216-
blobGasPrice);
222+
blobGasPrice,
223+
Optional.empty());
217224

218225
verify(transactionProcessor, times(1))
219226
.processTransaction(
220-
any(PathBasedWorldStateUpdateAccumulator.class),
227+
any(WorldUpdater.class),
221228
eq(blockHeader),
222229
eq(transaction),
223230
eq(miningBeneficiary),
224231
any(OperationTracer.class),
225232
any(BlockHashLookup.class),
226233
eq(TransactionValidationParams.processingBlock()),
227-
eq(blobGasPrice));
234+
eq(blobGasPrice),
235+
eq(Optional.empty()));
228236

229237
// simulate a conflict
230238
when(transactionCollisionDetector.hasCollision(any(), any(), any(), any())).thenReturn(true);
@@ -234,4 +242,63 @@ void testRunTransactionWithConflict() {
234242
worldState, miningBeneficiary, transaction, 0, Optional.empty(), Optional.empty());
235243
assertTrue(result.isEmpty(), "Expected no transaction result to be applied due to conflict");
236244
}
245+
246+
@Test
247+
void testApplyResultUsesAccessLocationTrackerAndUpdatesPartialBlockAccessView() {
248+
Address miningBeneficiary = Address.fromHexString("0x1");
249+
Wei blobGasPrice = Wei.ZERO;
250+
251+
PartialBlockAccessView partialView = mock(PartialBlockAccessView.class);
252+
PartialBlockAccessView.AccountChanges beneficiaryChanges =
253+
mock(PartialBlockAccessView.AccountChanges.class);
254+
when(beneficiaryChanges.getAddress()).thenReturn(miningBeneficiary);
255+
when(partialView.accountChanges()).thenReturn(Collections.singletonList(beneficiaryChanges));
256+
257+
when(transactionProcessor.processTransaction(
258+
any(), any(), any(), any(), any(), any(), any(), any(), any()))
259+
.thenReturn(
260+
TransactionProcessingResult.successful(
261+
Collections.emptyList(),
262+
0,
263+
0,
264+
Bytes.EMPTY,
265+
Optional.of(partialView),
266+
ValidationResult.valid()));
267+
268+
BlockAccessListBuilder balBuilder = mock(BlockAccessListBuilder.class);
269+
270+
processor.runTransaction(
271+
protocolContext,
272+
blockHeader,
273+
0,
274+
transaction,
275+
miningBeneficiary,
276+
(__, ___) -> Hash.EMPTY,
277+
blobGasPrice,
278+
Optional.of(balBuilder));
279+
280+
verify(transactionProcessor)
281+
.processTransaction(
282+
any(WorldUpdater.class),
283+
eq(blockHeader),
284+
eq(transaction),
285+
eq(miningBeneficiary),
286+
any(OperationTracer.class),
287+
any(BlockHashLookup.class),
288+
eq(TransactionValidationParams.processingBlock()),
289+
eq(blobGasPrice),
290+
argThat(Optional::isPresent));
291+
292+
when(transactionCollisionDetector.hasCollision(any(), any(), any(), any())).thenReturn(false);
293+
294+
Optional<TransactionProcessingResult> maybeResult =
295+
processor.applyParallelizedTransactionResult(
296+
worldState, miningBeneficiary, transaction, 0, Optional.empty(), Optional.empty());
297+
298+
assertTrue(
299+
maybeResult.isPresent(), "Expected the parallelized transaction result to be applied");
300+
TransactionProcessingResult result = maybeResult.get();
301+
assertTrue(result.getPartialBlockAccessView().isPresent(), "Expected BAL view to be present");
302+
verify(beneficiaryChanges).setPostBalance(any(Wei.class));
303+
}
237304
}

0 commit comments

Comments
 (0)