Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/acceptance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
- name: List all acceptance tests
run: ./gradlew acceptanceTest --test-dry-run -Dorg.gradle.parallel=true -Dorg.gradle.caching=true
- name: Extract current test list
run: mkdir tmp; find . -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v '/testsuite/@name' '{}'; echo ' acceptanceTest'" | tee tmp/currentTests.list
run: mkdir tmp; find . -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v '/testsuite/testcase[1]/@classname' '{}'; echo ' acceptanceTest'" | tee tmp/currentTests.list
- name: Get acceptance test reports
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d
continue-on-error: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pre-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
- name: List unit tests
run: ./gradlew test --test-dry-run -Dorg.gradle.parallel=true -Dorg.gradle.caching=true
- name: Extract current test list
run: mkdir tmp; find . -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v '/testsuite/@name' '{}'; echo '{}' | sed 's#\./\(.*\)/build/test-results/.*# \1#'" | tee tmp/currentTests.list
run: mkdir tmp; find . -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v '/testsuite/testcase[1]/@classname' '{}'; echo '{}' | sed 's#\./\(.*\)/build/test-results/.*# \1#'" | tee tmp/currentTests.list
- name: Get unit test reports
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d
continue-on-error: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/splitTestsByTime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ SPLIT_COUNT=$4
SPLIT_INDEX=$5

# extract tests time from Junit XML reports
find "$REPORTS_DIR" -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v 'concat(sum(//testcase/@time), \" \", //testsuite/@name)' '{}'; echo '{}' | sed \"s#${REPORT_STRIP_PREFIX}/\(.*\)/${REPORT_STRIP_SUFFIX}.*# \1#\"" > tmp/timing.tsv
find "$REPORTS_DIR" -type f -name TEST-*.xml | xargs -I{} bash -c "xmlstarlet sel -t -v 'concat(sum(//testcase/@time), \" \", //testsuite/testcase[1]/@classname)' '{}'; echo '{}' | sed \"s#${REPORT_STRIP_PREFIX}/\(.*\)/${REPORT_STRIP_SUFFIX}.*# \1#\"" > tmp/timing.tsv

# Sort times in descending order
IFS=$'\n' sorted=($(sort -nr tmp/timing.tsv))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ public BlockProcessingResult processBlock(
block,
blockAccessList,
new ParallelTransactionPreprocessing(transactionProcessor, executor, balConfiguration));

if (blockProcessingResult.isFailed()) {
// Fallback to non-parallel processing if there is a block processing exception .
LOG.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,18 +216,21 @@ public Optional<TransactionProcessingResult> getProcessingResult(
miningBeneficiaryAccount.incrementBalance(reward);
}

final Wei miningBeneficiaryPostBalance = miningBeneficiaryAccount.getBalance();
transactionProcessingResult
.getPartialBlockAccessView()
.ifPresent(
partialBlockAccessView ->
partialBlockAccessView.accountChanges().stream()
.filter(
accountChanges -> accountChanges.getAddress().equals(miningBeneficiary))
.findFirst()
.ifPresent(
accountChanges ->
accountChanges.setPostBalance(miningBeneficiaryPostBalance)));
if (!reward.isZero()) {
final Wei miningBeneficiaryPostBalance = miningBeneficiaryAccount.getBalance();
transactionProcessingResult
.getPartialBlockAccessView()
.ifPresent(
partialBlockAccessView ->
partialBlockAccessView.accountChanges().stream()
.filter(
accountChanges ->
accountChanges.getAddress().equals(miningBeneficiary))
.findFirst()
.ifPresent(
accountChanges ->
accountChanges.setPostBalance(miningBeneficiaryPostBalance)));
}

blockAccumulator.importStateChangesFromSource(transactionAccumulator);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet.parallelization;

import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_1;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_1_KEYPAIR;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_2;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_2_KEYPAIR;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.CONTRACT_ADDRESS;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;

import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/** Integration tests for contract storage operations. */
public abstract class AbstractContractStorageTest
extends AbstractParallelBlockProcessorIntegrationTest {

private final Address contractAddr = Address.fromHexStringStrict(CONTRACT_ADDRESS);

@Test
@DisplayName("Writing different storage slots from the same sender produces matching state")
void writeMultipleSlotsSameSender() {
final Transaction txSetSlot1 =
createContractCallTransaction(
0, contractAddr, "setSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(100));
final Transaction txSetSlot2 =
createContractCallTransaction(
1, contractAddr, "setSlot2", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(200));
final Transaction txSetSlot3 =
createContractCallTransaction(
2, contractAddr, "setSlot3", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(300));

final ComparisonResult result =
executeAndCompare(Wei.of(5), txSetSlot1, txSetSlot2, txSetSlot3);

assertContractStorage(result.seqWorldState(), contractAddr, 0, 100);
assertContractStorage(result.seqWorldState(), contractAddr, 1, 200);
assertContractStorage(result.seqWorldState(), contractAddr, 2, 300);

assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 0);
assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 1);
assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 2);
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_1));
}

@Test
@DisplayName("Writing then reading the same storage slot produces matching state")
void writeThenReadSameSlot() {
final Transaction txSetSlot1 =
createContractCallTransaction(
0, contractAddr, "setSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(999));
final Transaction txGetSlot1 =
createContractCallTransaction(
0, contractAddr, "getSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.empty());

final ComparisonResult result = executeAndCompare(Wei.of(5), txSetSlot1, txGetSlot1);

assertContractStorage(result.seqWorldState(), contractAddr, 0, 999);
assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 0);
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_1));
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_2));
}

@Test
@DisplayName("Reading then writing the same storage slot produces matching state")
void readThenWriteSameSlot() {
final Transaction txGetSlot1 =
createContractCallTransaction(
0, contractAddr, "getSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.empty());
final Transaction txSetSlot1 =
createContractCallTransaction(
0, contractAddr, "setSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.of(777));

final ComparisonResult result = executeAndCompare(Wei.of(5), txGetSlot1, txSetSlot1);

assertContractStorage(result.seqWorldState(), contractAddr, 0, 777);
assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 0);
}

@Test
@DisplayName("Write-read-write across two senders produces matching state")
void writeReadWriteAcrossSenders() {
final Transaction txSetSlot1 =
createContractCallTransaction(
0, contractAddr, "setSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(100));
final Transaction txGetSlot1 =
createContractCallTransaction(
0, contractAddr, "getSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.empty());
final Transaction txSetSlot2 =
createContractCallTransaction(
1, contractAddr, "setSlot2", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(200));
final Transaction txSetSlot3 =
createContractCallTransaction(
2, contractAddr, "setSlot3", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(300));

final ComparisonResult result =
executeAndCompare(Wei.of(5), txSetSlot1, txGetSlot1, txSetSlot2, txSetSlot3);

assertContractStorage(result.seqWorldState(), contractAddr, 0, 100);
assertContractStorage(result.seqWorldState(), contractAddr, 1, 200);
assertContractStorage(result.seqWorldState(), contractAddr, 2, 300);

for (int slot = 0; slot <= 2; slot++) {
assertContractStorageMatches(
result.seqWorldState(), result.parWorldState(), contractAddr, slot);
}
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_1));
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_2));
}

@Test
@DisplayName("Storage reads from different senders produce matching state")
void readFromDifferentSenders() {
final Transaction txGetSlot1A =
createContractCallTransaction(
0, contractAddr, "getSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.empty());
final Transaction txGetSlot1B =
createContractCallTransaction(
0, contractAddr, "getSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.empty());

final ComparisonResult result = executeAndCompare(Wei.of(5), txGetSlot1A, txGetSlot1B);

assertContractStorageMatches(result.seqWorldState(), result.parWorldState(), contractAddr, 0);
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_1));
assertAccountsMatch(
result.seqWorldState(),
result.parWorldState(),
Address.fromHexStringStrict(ACCOUNT_GENESIS_2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet.parallelization;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_2;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_3;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_4;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_1_KEYPAIR;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_2_KEYPAIR;
import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.MINING_BENEFICIARY;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList;
import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BalanceChange;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/** Integration tests for mining beneficiary BAL balance tracking. */
public abstract class AbstractMiningBeneficiaryBalTest
extends AbstractParallelBlockProcessorIntegrationTest {

@Test
@DisplayName(
"Parallel BAL must record correct cumulative mining beneficiary postBalance with priority fees")
void miningBeneficiaryPostBalanceWithPriorityFees() {
// Two independent transfers with non-zero priority fees.
// baseFee=1 so effectivePriorityFee = min(maxPriorityFeePerGas, maxFeePerGas - baseFee) > 0.
// The mining beneficiary accumulates the priority fee from each transaction.
final Transaction tx1 =
createTransferTransaction(
0, 1_000_000_000_000_000_000L, 300_000L, 2L, 10L, ACCOUNT_2, ACCOUNT_GENESIS_1_KEYPAIR);
final Transaction tx2 =
createTransferTransaction(
0, 2_000_000_000_000_000_000L, 300_000L, 3L, 10L, ACCOUNT_3, ACCOUNT_GENESIS_2_KEYPAIR);

final ComparisonResult result = executeAndCompare(Wei.of(1), tx1, tx2);

final Optional<BlockAccessList> seqBal = getBlockAccessList(result.seqResult());
final Optional<BlockAccessList> parBal = getBlockAccessList(result.parResult());
assertThat(seqBal).as("Sequential BAL should be present").isPresent();
assertThat(parBal).as("Parallel BAL should be present").isPresent();

final List<BalanceChange> seqBalChanges =
getBalanceChangesFor(seqBal.get(), MINING_BENEFICIARY);
final List<BalanceChange> parBalChanges =
getBalanceChangesFor(parBal.get(), MINING_BENEFICIARY);

assertThat(seqBalChanges)
.as("Sequential BAL should have balance changes for mining beneficiary")
.isNotEmpty();
assertThat(parBalChanges)
.as("Parallel BAL balance changes for mining beneficiary must match sequential")
.isEqualTo(seqBalChanges);
}

@Test
@DisplayName(
"Multiple transactions with varying priority fees produce correct cumulative beneficiary balances")
void multipleTxsWithVaryingPriorityFees() {
// Three transactions: two from sender A (nonces 0,1) and one from sender B (nonce 0),
// each with a different priority fee. baseFee=1 ensures positive effective priority fees.
final Transaction tx1 =
createTransferTransaction(
0, 100_000_000_000_000L, 300_000L, 1L, 10L, ACCOUNT_2, ACCOUNT_GENESIS_1_KEYPAIR);
final Transaction tx2 =
createTransferTransaction(
0, 200_000_000_000_000L, 300_000L, 4L, 10L, ACCOUNT_3, ACCOUNT_GENESIS_2_KEYPAIR);
final Transaction tx3 =
createTransferTransaction(
1, 300_000_000_000_000L, 300_000L, 2L, 10L, ACCOUNT_4, ACCOUNT_GENESIS_1_KEYPAIR);

final ComparisonResult result = executeAndCompare(Wei.of(1), tx1, tx2, tx3);

final Optional<BlockAccessList> seqBal = getBlockAccessList(result.seqResult());
final Optional<BlockAccessList> parBal = getBlockAccessList(result.parResult());
assertThat(seqBal).isPresent();
assertThat(parBal).isPresent();

final List<BalanceChange> seqBalChanges =
getBalanceChangesFor(seqBal.get(), MINING_BENEFICIARY);
final List<BalanceChange> parBalChanges =
getBalanceChangesFor(parBal.get(), MINING_BENEFICIARY);

assertThat(seqBalChanges).isNotEmpty();
assertThat(parBalChanges)
.as("Parallel BAL mining beneficiary balance changes must match sequential")
.isEqualTo(seqBalChanges);

// Each balance change should be strictly increasing (cumulative rewards)
for (int i = 1; i < seqBalChanges.size(); i++) {
assertThat(seqBalChanges.get(i).postBalance())
.as("Balance change at index %d must be >= previous", i)
.isGreaterThanOrEqualTo(seqBalChanges.get(i - 1).postBalance());
}
}
}
Loading
Loading