Merge branch 'main' into 7311-add-peertask-foundation-code

pull/7628/head
Matilda-Clerke 2 months ago committed by GitHub
commit f2ac53ecfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/acceptance-tests.yml
  2. 4
      .github/workflows/pre-review.yml
  3. 4
      .github/workflows/reference-tests.yml
  4. 3
      CHANGELOG.md
  5. 6
      besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java
  6. 6
      besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java
  7. 6
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  8. 14
      besu/src/main/scripts/besu-entry.sh
  9. 18
      besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java
  10. 7
      config/src/main/java/org/hyperledger/besu/config/BftConfigOptions.java
  11. 13
      config/src/main/java/org/hyperledger/besu/config/BftFork.java
  12. 10
      config/src/main/java/org/hyperledger/besu/config/JsonBftConfigOptions.java
  13. 21
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java
  14. 17
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/MutableBftConfigOptions.java
  15. 14
      consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java
  16. 3
      consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/RoundTimerTest.java
  17. 3
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  18. 20
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactory.java
  19. 6
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java
  20. 8
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidator.java
  21. 6
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java
  22. 5
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  23. 4
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java
  24. 3
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  25. 20
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java
  26. 1
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftForksSchedulesFactory.java
  27. 5
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java
  28. 6
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java
  29. 2
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java
  30. 15
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java
  31. 2
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java
  32. 27
      crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java
  33. 7
      docker/tests/02/goss_wait.yaml
  34. 2
      docs/tracing/README.md
  35. 215
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/EnclaveTest.java
  36. 52
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java
  37. 144
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java
  38. 109
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java
  39. 98
      enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java
  40. 192
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/PrivGetPrivateTransactionIntegrationTest.java
  41. 244
      ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java
  42. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockValidator.java
  43. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java
  44. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockImporter.java
  45. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java
  46. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BaseFeeBlockBodyValidator.java
  47. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockBodyValidator.java
  48. 26
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidationMode.java
  49. 20
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java
  50. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockImporter.java
  51. 44
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java
  52. 123
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidatorTest.java
  53. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueRequestsValidatorTest.java
  54. 8
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java
  55. 14
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/ImportBlocksStep.java
  56. 18
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/ImportBlocksStepTest.java
  57. 36
      gradle/verification-metadata.xml
  58. 2
      gradle/versions.gradle
  59. 1
      testutil/src/main/resources/enclave_key_0.key

@ -12,7 +12,7 @@ concurrency:
env:
GRADLE_OPTS: "-Xmx7g"
total-runners: 12
total-runners: 14
jobs:
acceptanceTestEthereum:
@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: true
matrix:
runner_index: [0,1,2,3,4,5,6,7,8,9,10,11]
runner_index: [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11

@ -12,7 +12,7 @@ concurrency:
env:
GRADLE_OPTS: "-Xmx6g -Dorg.gradle.parallel=true"
total-runners: 8
total-runners: 10
jobs:
repolint:
@ -83,7 +83,7 @@ jobs:
strategy:
fail-fast: true
matrix:
runner_index: [0,1,2,3,4,5,6,7]
runner_index: [0,1,2,3,4,5,6,7,8,9]
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11

@ -8,7 +8,7 @@ on:
env:
GRADLE_OPTS: "-Xmx6g -Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.caching=true"
total-runners: 10
total-runners: 8
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: true
matrix:
runner_index: [1,2,3,4,5,6,7,8,9,10]
runner_index: [1,2,3,4,5,6,7,8]
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11

@ -15,7 +15,8 @@
- Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575)
- Fix for `debug_traceCall` to handle transactions without specified gas price. [#7510](https://github.com/hyperledger/besu/pull/7510)
- Corrects a regression where custom plugin services are not initialized correctly. [#7625](https://github.com/hyperledger/besu/pull/7625)
- Fix for IBFT2 chains using the BONSAI DB format [#7631](https://github.com/hyperledger/besu/pull/7631)
- Fix reading `tx-pool-min-score` option from configuration file [#7623](https://github.com/hyperledger/besu/pull/7623)
## 24.9.1

@ -120,7 +120,11 @@ public class TomlConfigurationDefaultProvider implements IDefaultValueProvider {
}
private boolean isNumericType(final Class<?> type) {
return type.equals(Integer.class)
return type.equals(Byte.class)
|| type.equals(byte.class)
|| type.equals(Short.class)
|| type.equals(short.class)
|| type.equals(Integer.class)
|| type.equals(int.class)
|| type.equals(Long.class)
|| type.equals(long.class)

@ -74,6 +74,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -182,7 +183,10 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
uniqueMessageMulticaster,
new RoundTimer(bftEventQueue, bftConfig.getRequestTimeoutSeconds(), bftExecutors),
new RoundTimer(
bftEventQueue,
Duration.ofSeconds(bftConfig.getRequestTimeoutSeconds()),
bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, clock),
blockCreatorFactory,
clock);

@ -84,6 +84,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -222,7 +223,10 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
uniqueMessageMulticaster,
new RoundTimer(bftEventQueue, qbftConfig.getRequestTimeoutSeconds(), bftExecutors),
new RoundTimer(
bftEventQueue,
Duration.ofSeconds(qbftConfig.getRequestTimeoutSeconds()),
bftExecutors),
new BlockTimer(bftEventQueue, qbftForksSchedule, bftExecutors, clock),
blockCreatorFactory,
clock);

@ -14,6 +14,14 @@
## SPDX-License-Identifier: Apache-2.0
##
# Construct the command as a single string
COMMAND="/opt/besu/bin/besu $@"
# Check if current user is not root. If not, run the command as is.
if [ "$(id -u)" -ne 0 ]; then
exec /bin/bash -c "$COMMAND"
fi
# Run Besu first to get paths needing permission adjustment
output=$(/opt/besu/bin/besu --print-paths-and-exit $BESU_USER_NAME "$@")
@ -41,9 +49,5 @@ echo "$output" | while IFS=: read -r prefix path accessType; do
fi
done
# Finally, run Besu with the actual arguments passed to the container
# Construct the command as a single string
COMMAND="/opt/besu/bin/besu $@"
# Switch to the besu user and execute the command
exec su -s /bin/bash $BESU_USER_NAME -c "$COMMAND"
exec su -s /bin/bash "$BESU_USER_NAME" -c "$COMMAND"

@ -427,6 +427,24 @@ public class TransactionPoolOptionsTest
Byte.toString(minScore));
}
@Test
public void minScoreWorksConfigFile() throws IOException {
final byte minScore = -10;
final Path tempConfigFilePath =
createTempFile(
"config",
String.format(
"""
tx-pool-min-score=%s
""",
minScore));
internalTestSuccess(
config -> assertThat(config.getMinScore()).isEqualTo(minScore),
"--config-file",
tempConfigFilePath.toString());
}
@Test
public void minScoreNonByteValueReturnError() {
final var overflowMinScore = Integer.toString(-300);

@ -37,6 +37,13 @@ public interface BftConfigOptions {
*/
int getBlockPeriodSeconds();
/**
* Gets block period milliseconds. For TESTING only. If set then blockperiodseconds is ignored.
*
* @return the block period milliseconds
*/
long getBlockPeriodMilliseconds();
/**
* Gets request timeout seconds.
*

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ArrayNode;
@ -40,6 +41,9 @@ public class BftFork implements Fork {
/** The constant BLOCK_PERIOD_SECONDS_KEY. */
public static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds";
/** The constant BLOCK_PERIOD_MILLISECONDS_KEY. */
public static final String BLOCK_PERIOD_MILLISECONDS_KEY = "xblockperiodmilliseconds";
/** The constant BLOCK_REWARD_KEY. */
public static final String BLOCK_REWARD_KEY = "blockreward";
@ -82,6 +86,15 @@ public class BftFork implements Fork {
return JsonUtil.getPositiveInt(forkConfigRoot, BLOCK_PERIOD_SECONDS_KEY);
}
/**
* Gets block period milliseconds. Experimental for test scenarios only.
*
* @return the block period milliseconds
*/
public OptionalLong getBlockPeriodMilliseconds() {
return JsonUtil.getLong(forkConfigRoot, BLOCK_PERIOD_MILLISECONDS_KEY);
}
/**
* Gets block reward wei.
*

@ -34,6 +34,7 @@ public class JsonBftConfigOptions implements BftConfigOptions {
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_BLOCK_PERIOD_MILLISECONDS = 0; // Experimental for test only
private static final int DEFAULT_ROUND_EXPIRY_SECONDS = 1;
// In a healthy network this can be very small. This default limit will allow for suitable
// protection for on a typical 20 node validator network with multiple rounds
@ -66,6 +67,12 @@ public class JsonBftConfigOptions implements BftConfigOptions {
bftConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS);
}
@Override
public long getBlockPeriodMilliseconds() {
return JsonUtil.getLong(
bftConfigRoot, "xblockperiodmilliseconds", DEFAULT_BLOCK_PERIOD_MILLISECONDS);
}
@Override
public int getRequestTimeoutSeconds() {
return JsonUtil.getInt(bftConfigRoot, "requesttimeoutseconds", DEFAULT_ROUND_EXPIRY_SECONDS);
@ -133,6 +140,9 @@ public class JsonBftConfigOptions implements BftConfigOptions {
if (bftConfigRoot.has("blockperiodseconds")) {
builder.put("blockPeriodSeconds", getBlockPeriodSeconds());
}
if (bftConfigRoot.has("xblockperiodmilliseconds")) {
builder.put("xBlockPeriodMilliSeconds", getBlockPeriodMilliseconds());
}
if (bftConfigRoot.has("requesttimeoutseconds")) {
builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds());
}

@ -24,9 +24,14 @@ import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Class for starting and keeping organised block timers */
public class BlockTimer {
private static final Logger LOG = LoggerFactory.getLogger(BlockTimer.class);
private final ForksSchedule<? extends BftConfigOptions> forksSchedule;
private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask;
@ -79,12 +84,26 @@ public class BlockTimer {
cancelTimer();
final long now = clock.millis();
final long expiryTime;
// Experimental option for test scenarios only. Not for production use.
final long blockPeriodMilliseconds =
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodMilliseconds();
if (blockPeriodMilliseconds > 0) {
// Experimental mode for setting < 1 second block periods e.g. for CI/CD pipelines
// running tests against Besu
expiryTime = clock.millis() + blockPeriodMilliseconds;
LOG.warn(
"Test-mode only xblockperiodmilliseconds has been set to {} millisecond blocks. Do not use in a production system.",
blockPeriodMilliseconds);
} else {
// absolute time when the timer is supposed to expire
final int blockPeriodSeconds =
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodSeconds();
final long minimumTimeBetweenBlocksMillis = blockPeriodSeconds * 1000L;
final long expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis;
expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis;
}
if (expiryTime > now) {
final long delay = expiryTime - now;

@ -31,6 +31,7 @@ import java.util.Optional;
public class MutableBftConfigOptions implements BftConfigOptions {
private long epochLength;
private int blockPeriodSeconds;
private long blockPeriodMilliseconds;
private int requestTimeoutSeconds;
private int gossipedHistoryLimit;
private int messageQueueLimit;
@ -48,6 +49,7 @@ public class MutableBftConfigOptions implements BftConfigOptions {
public MutableBftConfigOptions(final BftConfigOptions bftConfigOptions) {
this.epochLength = bftConfigOptions.getEpochLength();
this.blockPeriodSeconds = bftConfigOptions.getBlockPeriodSeconds();
this.blockPeriodMilliseconds = bftConfigOptions.getBlockPeriodMilliseconds();
this.requestTimeoutSeconds = bftConfigOptions.getRequestTimeoutSeconds();
this.gossipedHistoryLimit = bftConfigOptions.getGossipedHistoryLimit();
this.messageQueueLimit = bftConfigOptions.getMessageQueueLimit();
@ -68,6 +70,11 @@ public class MutableBftConfigOptions implements BftConfigOptions {
return blockPeriodSeconds;
}
@Override
public long getBlockPeriodMilliseconds() {
return blockPeriodMilliseconds;
}
@Override
public int getRequestTimeoutSeconds() {
return requestTimeoutSeconds;
@ -131,6 +138,16 @@ public class MutableBftConfigOptions implements BftConfigOptions {
this.blockPeriodSeconds = blockPeriodSeconds;
}
/**
* Sets block period milliseconds. Experimental for test scenarios. Not for use on production
* systems.
*
* @param blockPeriodMilliseconds the block period milliseconds
*/
public void setBlockPeriodMilliseconds(final long blockPeriodMilliseconds) {
this.blockPeriodMilliseconds = blockPeriodMilliseconds;
}
/**
* Sets request timeout seconds.
*

@ -16,6 +16,7 @@ package org.hyperledger.besu.consensus.common.bft;
import org.hyperledger.besu.consensus.common.bft.events.RoundExpiry;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -31,21 +32,21 @@ public class RoundTimer {
private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask;
private final BftEventQueue queue;
private final long baseExpiryMillis;
private final Duration baseExpiryPeriod;
/**
* Construct a RoundTimer with primed executor service ready to start timers
*
* @param queue The queue in which to put round expiry events
* @param baseExpirySeconds The initial round length for round 0
* @param baseExpiryPeriod The initial round length for round 0
* @param bftExecutors executor service that timers can be scheduled with
*/
public RoundTimer(
final BftEventQueue queue, final long baseExpirySeconds, final BftExecutors bftExecutors) {
final BftEventQueue queue, final Duration baseExpiryPeriod, final BftExecutors bftExecutors) {
this.queue = queue;
this.bftExecutors = bftExecutors;
this.currentTimerTask = Optional.empty();
this.baseExpiryMillis = baseExpirySeconds * 1000;
this.baseExpiryPeriod = baseExpiryPeriod;
}
/** Cancels the current running round timer if there is one */
@ -71,7 +72,8 @@ public class RoundTimer {
public synchronized void startTimer(final ConsensusRoundIdentifier round) {
cancelTimer();
final long expiryTime = baseExpiryMillis * (long) Math.pow(2, round.getRoundNumber());
final long expiryTime =
baseExpiryPeriod.toMillis() * (long) Math.pow(2, round.getRoundNumber());
final Runnable newTimerRunnable = () -> queue.add(new RoundExpiry(round));
@ -81,7 +83,7 @@ public class RoundTimer {
// Once we are up to round 2 start logging round expiries
if (round.getRoundNumber() >= 2) {
LOG.info(
"QBFT round {} expired. Moved to round {} which will expire in {} seconds",
"BFT round {} expired. Moved to round {} which will expire in {} seconds",
round.getRoundNumber() - 1,
round.getRoundNumber(),
(expiryTime / 1000));

@ -25,6 +25,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.consensus.common.bft.events.RoundExpiry;
import java.time.Duration;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -46,7 +47,7 @@ public class RoundTimerTest {
bftExecutors = mock(BftExecutors.class);
queue = new BftEventQueue(1000);
queue.start();
timer = new RoundTimer(queue, 1, bftExecutors);
timer = new RoundTimer(queue, Duration.ofSeconds(1), bftExecutors);
}
@Test

@ -100,6 +100,7 @@ import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
@ -403,7 +404,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors),
new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory,
clock);

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValid
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -45,20 +46,20 @@ public class IbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form
* part of the BlockChain (i.e. not proposed blocks, which do not contain commit seals)
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @param minimumTimeBetweenBlocks the minimum time which must elapse between blocks.
* @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers
*/
public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder()
final Duration minimumTimeBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) {
final BlockHeaderValidator.Builder ruleBuilder =
new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
@ -72,5 +73,16 @@ public class IbftBlockHeaderValidationRulesetFactory {
.addRule(new BftValidatorsValidationRule())
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
// Currently the minimum acceptable time between blocks is 1 second. The timestamp of an
// Ethereum header is stored as seconds since Unix epoch so blocks being produced more
// frequently than once a second cannot pass this validator. For non-production scenarios
// (e.g. for testing block production much more frequently than once a second) Besu has
// an experimental 'xblockperiodmilliseconds' option for BFT chains. If this is enabled
// we cannot apply the TimestampMoreRecentThanParent validation rule so we do not add it
if (minimumTimeBetweenBlocks.compareTo(Duration.ofSeconds(1)) >= 0) {
ruleBuilder.addRule(new TimestampMoreRecentThanParent(minimumTimeBetweenBlocks.getSeconds()));
}
return ruleBuilder;
}
}

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a BFT consensus mechanism. */
@ -120,6 +121,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder
Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
config.getBlockPeriodSeconds(), baseFeeMarket);
config.getBlockPeriodMilliseconds() > 0
? Duration.ofMillis(config.getBlockPeriodMilliseconds())
: Duration.ofSeconds(config.getBlockPeriodSeconds()),
baseFeeMarket);
}
}

@ -78,7 +78,9 @@ public class MessageValidator {
return false;
}
if (!validateBlock(msg.getBlock())) {
// We want to validate the block but not persist it yet as it's just a proposal. If it turns
// out to be an accepted block it will be persisted at block import time
if (!validateBlockWithoutPersisting(msg.getBlock())) {
return false;
}
@ -93,14 +95,14 @@ public class MessageValidator {
msg.getSignedPayload(), msg.getBlock(), blockInterface);
}
private boolean validateBlock(final Block block) {
private boolean validateBlockWithoutPersisting(final Block block) {
final BlockValidator blockValidator =
protocolSchedule.getByBlockHeader(block.getHeader()).getBlockValidator();
final var validationResult =
blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL, false);
if (validationResult.isFailed()) {
LOG.info(

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@ -93,7 +94,7 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.of(FeeMarket.london(1)))
Duration.ofSeconds(5), Optional.of(FeeMarket.london(1)))
.build();
assertThat(
@ -372,7 +373,8 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
}
public BlockHeaderValidator getBlockHeaderValidator() {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, Optional.empty())
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), Optional.empty())
.build();
}
}

@ -64,6 +64,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock;
import java.time.Duration;
import java.time.ZoneId;
import java.util.Collections;
import java.util.List;
@ -105,7 +106,7 @@ public class BftBlockCreatorTest {
public BlockHeaderValidator.Builder createBlockHeaderRuleset(
final BftConfigOptions config, final FeeMarket feeMarket) {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.empty());
Duration.ofSeconds(5), Optional.empty());
}
};
final GenesisConfigOptions configOptions =
@ -200,7 +201,7 @@ public class BftBlockCreatorTest {
final BlockHeaderValidator rules =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
secondsBetweenBlocks, Optional.empty())
Duration.ofSeconds(secondsBetweenBlocks), Optional.empty())
.build();
// NOTE: The header will not contain commit seals, so can only do light validation on header.

@ -113,7 +113,7 @@ public class MessageValidatorTest {
when(protocolSpec.getBlockValidator()).thenReturn(blockValidator);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any(), eq(false)))
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate(
@ -168,7 +168,7 @@ public class MessageValidatorTest {
@Test
public void blockValidationFailureFailsValidation() {
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any(), eq(false)))
.thenReturn(new BlockProcessingResult("Failed"));
final Proposal proposalMsg =

@ -118,6 +118,7 @@ import org.hyperledger.besu.util.Subscribers;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
@ -512,7 +513,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector,
multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors),
new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory,
clock);

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValid
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
@ -44,23 +45,23 @@ public class QbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form
* part of the BlockChain (i.e. not proposed blocks, which do not contain commit seals)
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @param minimumTimeBetweenBlocks the minimum amount of time that must elapse between blocks.
* @param useValidatorContract whether validator selection is using a validator contract
* @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers
*/
public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks,
final Duration minimumTimeBetweenBlocks,
final boolean useValidatorContract,
final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder()
BlockHeaderValidator.Builder ruleBuilder =
new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
@ -70,5 +71,16 @@ public class QbftBlockHeaderValidationRulesetFactory {
.addRule(new QbftValidatorsValidationRule(useValidatorContract))
.addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule());
// Currently the minimum acceptable time between blocks is 1 second. The timestamp of an
// Ethereum header is stored as seconds since Unix epoch so blocks being produced more
// frequently than once a second cannot pass this validator. For non-production scenarios
// (e.g. for testing block production much more frequently than once a second) Besu has
// an experimental 'xblockperiodmilliseconds' option for BFT chains. If this is enabled
// we cannot apply the TimestampMoreRecentThanParent validation rule so we do not add it
if (minimumTimeBetweenBlocks.compareTo(Duration.ofSeconds(1)) >= 0) {
ruleBuilder.addRule(new TimestampMoreRecentThanParent(minimumTimeBetweenBlocks.getSeconds()));
}
return ruleBuilder;
}
}

@ -49,6 +49,7 @@ public class QbftForksSchedulesFactory {
new MutableQbftConfigOptions(lastSpec.getValue());
fork.getBlockPeriodSeconds().ifPresent(bftConfigOptions::setBlockPeriodSeconds);
fork.getBlockPeriodMilliseconds().ifPresent(bftConfigOptions::setBlockPeriodMilliseconds);
fork.getBlockRewardWei().ifPresent(bftConfigOptions::setBlockRewardWei);
if (fork.isMiningBeneficiaryConfigured()) {

@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a QBFT consensus mechanism. */
@ -164,7 +165,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder
Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
qbftConfigOptions.getBlockPeriodSeconds(),
qbftConfigOptions.getBlockPeriodMilliseconds() > 0
? Duration.ofMillis(qbftConfigOptions.getBlockPeriodMilliseconds())
: Duration.ofSeconds(qbftConfigOptions.getBlockPeriodSeconds()),
qbftConfigOptions.isValidatorContractMode(),
baseFeeMarket);
}

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@ -92,7 +93,7 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator =
QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, false, Optional.of(FeeMarket.london(1)))
Duration.ofSeconds(5), false, Optional.of(FeeMarket.london(1)))
.build();
assertThat(
@ -366,7 +367,8 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
}
public BlockHeaderValidator getBlockHeaderValidator() {
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, false, Optional.empty())
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), false, Optional.empty())
.build();
}
}

@ -214,7 +214,7 @@ public abstract class AbstractSECP256 implements SignatureAlgorithm {
@Override
public CodeDelegationSignature createCodeDelegationSignature(
final BigInteger r, final BigInteger s, final long yParity) {
final BigInteger r, final BigInteger s, final BigInteger yParity) {
return CodeDelegationSignature.create(r, s, yParity);
}

@ -42,18 +42,25 @@ public class CodeDelegationSignature extends SECPSignature {
* @return the new CodeDelegationSignature
*/
public static CodeDelegationSignature create(
final BigInteger r, final BigInteger s, final long yParity) {
final BigInteger r, final BigInteger s, final BigInteger yParity) {
checkNotNull(r);
checkNotNull(s);
if (r.compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException("Invalid 'r' value, should be < 2^256 but got " + r);
throw new IllegalArgumentException(
"Invalid 'r' value, should be < 2^256 but got " + r.toString(16));
}
if (s.compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException("Invalid 's' value, should be < 2^256 but got " + s);
throw new IllegalArgumentException(
"Invalid 's' value, should be < 2^256 but got " + s.toString(16));
}
return new CodeDelegationSignature(r, s, (byte) yParity);
if (yParity.compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException(
"Invalid 'yParity' value, should be < 2^256 but got " + yParity.toString(16));
}
return new CodeDelegationSignature(r, s, yParity.byteValue());
}
}

@ -224,7 +224,7 @@ public interface SignatureAlgorithm {
* @return the code delegation signature
*/
CodeDelegationSignature createCodeDelegationSignature(
final BigInteger r, final BigInteger s, final long yParity);
final BigInteger r, final BigInteger s, final BigInteger yParity);
/**
* Decode secp signature.

@ -29,19 +29,19 @@ class CodeDelegationSignatureTest {
void testValidInputs() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN;
long yParity = 1L;
BigInteger yParity = BigInteger.ONE;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS());
assertThat((byte) yParity).isEqualTo(result.getRecId());
assertThat(yParity.byteValue()).isEqualTo(result.getRecId());
}
@Test
void testNullRValue() {
BigInteger s = BigInteger.TEN;
long yParity = 0L;
BigInteger yParity = BigInteger.ZERO;
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(null, s, yParity));
@ -50,7 +50,7 @@ class CodeDelegationSignatureTest {
@Test
void testNullSValue() {
BigInteger r = BigInteger.ONE;
long yParity = 0L;
BigInteger yParity = BigInteger.ZERO;
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, null, yParity));
@ -60,7 +60,7 @@ class CodeDelegationSignatureTest {
void testRValueExceedsTwoPow256() {
BigInteger r = TWO_POW_256;
BigInteger s = BigInteger.TEN;
long yParity = 0L;
BigInteger yParity = BigInteger.ZERO;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
@ -71,23 +71,34 @@ class CodeDelegationSignatureTest {
void testSValueExceedsTwoPow256() {
BigInteger r = BigInteger.ONE;
BigInteger s = TWO_POW_256;
long yParity = 0L;
BigInteger yParity = BigInteger.ZERO;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
.withMessageContainingAll("Invalid 's' value, should be < 2^256");
}
@Test
void testYParityExceedsTwoPow256() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TWO;
BigInteger yParity = TWO_POW_256;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
.withMessageContainingAll("Invalid 'yParity' value, should be < 2^256");
}
@Test
void testValidYParityZero() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN;
long yParity = 0L;
BigInteger yParity = BigInteger.ZERO;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS());
assertThat((byte) yParity).isEqualTo(result.getRecId());
assertThat(yParity.byteValue()).isEqualTo(result.getRecId());
}
}

@ -0,0 +1,7 @@
---
# runtime docker tests for interfaces & ports
port:
tcp:30303:
listening: true
ip:
- 0.0.0.0

@ -1,6 +1,6 @@
# Tracing
Hyperledger Besu integrates with the [open-telemetry](https://open-telemetry.io) project to integrate tracing reporting.
Hyperledger Besu integrates with the [open-telemetry](https://opentelemetry.io/) project to integrate tracing reporting.
This allows to report all JSON-RPC traffic as traces.

@ -1,215 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.enclave;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.enclave.types.ReceiveResponse;
import org.hyperledger.besu.enclave.types.SendResponse;
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
import org.hyperledger.enclave.testutil.TesseraTestHarness;
import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Lists;
import io.vertx.core.Vertx;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class EnclaveTest {
@TempDir private static Path folder;
private static final String PAYLOAD = "a wonderful transaction";
private static final String MOCK_KEY = "iOCzoGo5kwtZU0J41Z9xnGXHN6ZNukIa9MspvHtu3Jk=";
private Enclave enclave;
private Vertx vertx;
private EnclaveFactory factory;
private TesseraTestHarness testHarness;
@BeforeEach
public void setUp() throws Exception {
vertx = Vertx.vertx();
factory = new EnclaveFactory(vertx);
testHarness =
TesseraTestHarnessFactory.create(
"enclave",
Files.createTempDirectory(folder, "enclave"),
new EnclaveKeyConfiguration(
new String[] {"enclave_key_0.pub"},
new String[] {"enclave_key_0.key"},
EnclaveEncryptorType.NOOP),
Optional.empty());
testHarness.start();
enclave = factory.createVertxEnclave(testHarness.clientUrl());
}
@AfterEach
public void tearDown() {
testHarness.close();
vertx.close();
}
@Test
public void testUpCheck() {
assertThat(enclave.upCheck()).isTrue();
}
@Test
public void testReceiveThrowsWhenPayloadDoesNotExist() {
final String publicKey = testHarness.getDefaultPublicKey();
final Throwable t = catchThrowable(() -> enclave.receive(MOCK_KEY, publicKey));
assertThat(t.getMessage()).isEqualTo("Message with hash was not found");
}
@Test
public void testSendAndReceive() {
final List<String> publicKeys = testHarness.getPublicKeys();
final SendResponse sr =
enclave.send(PAYLOAD, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0)));
final ReceiveResponse rr = enclave.receive(sr.getKey(), publicKeys.get(0));
assertThat(rr).isNotNull();
assertThat(new String(rr.getPayload(), UTF_8)).isEqualTo(PAYLOAD);
assertThat(rr.getPrivacyGroupId()).isNotNull();
}
@Test
public void testSendWithPrivacyGroupAndReceive() {
final List<String> publicKeys = testHarness.getPublicKeys();
final PrivacyGroup privacyGroupResponse =
enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), "", "");
final SendResponse sr =
enclave.send(PAYLOAD, publicKeys.get(0), privacyGroupResponse.getPrivacyGroupId());
final ReceiveResponse rr = enclave.receive(sr.getKey(), publicKeys.get(0));
assertThat(rr).isNotNull();
assertThat(new String(rr.getPayload(), UTF_8)).isEqualTo(PAYLOAD);
assertThat(rr.getPrivacyGroupId()).isNotNull();
}
@Test
public void testCreateAndDeletePrivacyGroup() {
final List<String> publicKeys = testHarness.getPublicKeys();
final String name = "testName";
final String description = "testDesc";
final PrivacyGroup privacyGroupResponse =
enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description);
assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull();
assertThat(privacyGroupResponse.getName()).isEqualTo(name);
assertThat(privacyGroupResponse.getDescription()).isEqualTo(description);
assertThat(privacyGroupResponse.getType()).isEqualByComparingTo(PrivacyGroup.Type.PANTHEON);
final String response =
enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0));
assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response);
}
@Test
public void testCreateFindDeleteFindPrivacyGroup() {
final List<String> publicKeys = testHarness.getPublicKeys();
final String name = "name";
final String description = "desc";
final PrivacyGroup privacyGroupResponse =
enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description);
assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull();
assertThat(privacyGroupResponse.getName()).isEqualTo(name);
assertThat(privacyGroupResponse.getDescription()).isEqualTo(description);
assertThat(privacyGroupResponse.getType()).isEqualTo(PrivacyGroup.Type.PANTHEON);
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
.untilAsserted(
() -> {
final PrivacyGroup[] findPrivacyGroupResponse = enclave.findPrivacyGroup(publicKeys);
assertThat(findPrivacyGroupResponse.length).isEqualTo(1);
assertThat(findPrivacyGroupResponse[0].getPrivacyGroupId())
.isEqualTo(privacyGroupResponse.getPrivacyGroupId());
});
final String response =
enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0));
assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response);
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
.untilAsserted(
() -> {
final PrivacyGroup[] findPrivacyGroupResponse = enclave.findPrivacyGroup(publicKeys);
assertThat(findPrivacyGroupResponse.length).isEqualTo(0);
});
}
@Test
public void testCreateDeleteRetrievePrivacyGroup() {
final List<String> publicKeys = testHarness.getPublicKeys();
final String name = "name";
final String description = "desc";
final PrivacyGroup privacyGroupResponse =
enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description);
assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull();
assertThat(privacyGroupResponse.getName()).isEqualTo(name);
assertThat(privacyGroupResponse.getDescription()).isEqualTo(description);
assertThat(privacyGroupResponse.getType()).isEqualTo(PrivacyGroup.Type.PANTHEON);
final PrivacyGroup retrievePrivacyGroup =
enclave.retrievePrivacyGroup(privacyGroupResponse.getPrivacyGroupId());
assertThat(retrievePrivacyGroup).usingRecursiveComparison().isEqualTo(privacyGroupResponse);
final String response =
enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0));
assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response);
}
@Test
public void upcheckReturnsFalseIfNoResponseReceived() throws URISyntaxException {
assertThat(factory.createVertxEnclave(new URI("http://8.8.8.8:65535")).upCheck()).isFalse();
}
}

@ -1,52 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.enclave;
import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import com.google.common.io.Resources;
public class TlsCertificateDefinition {
private final File pkcs12File;
private final String password;
public static TlsCertificateDefinition loadFromResource(
final String resourcePath, final String password) {
try {
final URL sslCertificate = Resources.getResource(resourcePath);
final Path keystorePath = Path.of(sslCertificate.getPath());
return new TlsCertificateDefinition(keystorePath.toFile(), password);
} catch (final Exception e) {
throw new RuntimeException("Failed to load TLS certificates", e);
}
}
public TlsCertificateDefinition(final File pkcs12File, final String password) {
this.pkcs12File = pkcs12File;
this.password = password;
}
public File getPkcs12File() {
return pkcs12File;
}
public String getPassword() {
return password;
}
}

@ -1,144 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.enclave;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Optional;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TlsEnabledEnclaveTest {
private TlsEnabledHttpServerFactory serverFactory;
private Vertx vertx;
final TlsCertificateDefinition httpServerCert =
TlsCertificateDefinition.loadFromResource("tls/cert1.pfx", "password");
final TlsCertificateDefinition besuCert =
TlsCertificateDefinition.loadFromResource("tls/cert2.pfx", "password2");
public void shutdown() {
vertx.close();
}
@BeforeEach
public void setup() {
serverFactory = new TlsEnabledHttpServerFactory();
this.vertx = Vertx.vertx();
}
@AfterEach
public void cleanup() {
serverFactory.shutdown();
this.shutdown();
}
private Enclave createEnclave(
final int httpServerPort, final Path workDir, final boolean tlsEnabled) throws IOException {
final Path serverFingerprintFile = workDir.resolve("server_known_clients");
final Path besuCertPasswordFile = workDir.resolve("password_file");
try {
populateFingerprintFile(serverFingerprintFile, httpServerCert, Optional.of(httpServerPort));
Files.write(besuCertPasswordFile, besuCert.getPassword().getBytes(Charset.defaultCharset()));
final EnclaveFactory factory = new EnclaveFactory(vertx);
if (tlsEnabled) {
final URI httpServerUri = new URI("https://localhost:" + httpServerPort);
return factory.createVertxEnclave(
httpServerUri,
besuCert.getPkcs12File().toPath(),
besuCertPasswordFile,
serverFingerprintFile);
} else {
return factory.createVertxEnclave(new URI("http://localhost:" + httpServerPort));
}
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
fail("unable to populate fingerprint file");
return null;
} catch (URISyntaxException e) {
fail("unable to create URI");
return null;
}
}
@Test
public void nonTlsEnclaveCannotConnectToTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false);
assertThat(enclave.upCheck()).isEqualTo(false);
}
@Test
public void nonTlsEnclaveCanConnectToNonTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false);
assertThat(enclave.upCheck()).isEqualTo(true);
}
@Test
public void tlsEnclaveCannotConnectToNonTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up!".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true);
assertThat(enclave.upCheck()).isEqualTo(false);
}
@Test
public void tlsEnclaveCanConnectToTlsServer() throws IOException {
Path workDir = Files.createTempDirectory("test-certs");
// Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up".
final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true);
final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true);
assertThat(enclave.upCheck()).isEqualTo(true);
}
}

@ -1,109 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.enclave;
import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile;
import java.io.IOException;
import java.nio.file.Path;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import com.google.common.collect.Lists;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.net.PfxOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import org.apache.tuweni.net.tls.VertxTrustOptions;
class TlsEnabledHttpServerFactory {
private final Vertx vertx;
private final List<HttpServer> serversCreated = Lists.newArrayList();
TlsEnabledHttpServerFactory() {
this.vertx = Vertx.vertx();
}
void shutdown() {
serversCreated.forEach(HttpServer::close);
vertx.close();
}
HttpServer create(
final TlsCertificateDefinition serverCert,
final TlsCertificateDefinition acceptedClientCerts,
final Path workDir,
final boolean tlsEnabled) {
try {
final Path serverFingerprintFile = workDir.resolve("server_known_clients");
populateFingerprintFile(serverFingerprintFile, acceptedClientCerts, Optional.empty());
final HttpServerOptions web3HttpServerOptions = new HttpServerOptions();
web3HttpServerOptions.setPort(0);
if (tlsEnabled) {
web3HttpServerOptions.setSsl(true);
web3HttpServerOptions.setClientAuth(ClientAuth.REQUIRED);
web3HttpServerOptions.setTrustOptions(
VertxTrustOptions.allowlistClients(serverFingerprintFile));
web3HttpServerOptions.setPfxKeyCertOptions(
new PfxOptions()
.setPath(serverCert.getPkcs12File().toString())
.setPassword(serverCert.getPassword()));
}
final Router router = Router.router(vertx);
router
.route(HttpMethod.GET, "/upcheck")
.produces(HttpHeaderValues.APPLICATION_JSON.toString())
.handler(TlsEnabledHttpServerFactory::handleRequest);
final HttpServer mockOrionHttpServer = vertx.createHttpServer(web3HttpServerOptions);
final CompletableFuture<Boolean> serverConfigured = new CompletableFuture<>();
mockOrionHttpServer.requestHandler(router).listen(result -> serverConfigured.complete(true));
serverConfigured.get();
serversCreated.add(mockOrionHttpServer);
return mockOrionHttpServer;
} catch (final KeyStoreException
| NoSuchAlgorithmException
| CertificateException
| IOException
| ExecutionException
| InterruptedException e) {
throw new RuntimeException("Failed to construct a TLS Enabled Server", e);
}
}
private static void handleRequest(final RoutingContext context) {
final HttpServerResponse response = context.response();
if (!response.closed()) {
response.end("I'm up!");
}
}
}

@ -1,98 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.enclave;
import org.hyperledger.besu.crypto.MessageDigestFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.StringJoiner;
import com.google.common.collect.Lists;
public class TlsHelpers {
private TlsHelpers() {}
private static KeyStore loadP12KeyStore(final File pkcsFile, final String password)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
final KeyStore store = KeyStore.getInstance("pkcs12");
try (final InputStream keystoreStream = new FileInputStream(pkcsFile)) {
store.load(keystoreStream, password.toCharArray());
} catch (IOException e) {
throw new RuntimeException("Unable to load keystore.", e);
}
return store;
}
public static void populateFingerprintFile(
final Path knownClientsPath,
final TlsCertificateDefinition certDef,
final Optional<Integer> serverPortToAppendToHostname)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
final List<X509Certificate> certs = getCertsFromPkcs12(certDef);
final StringBuilder fingerprintsToAdd = new StringBuilder();
final String portFragment = serverPortToAppendToHostname.map(port -> ":" + port).orElse("");
for (final X509Certificate cert : certs) {
final String fingerprint = generateFingerprint(cert);
fingerprintsToAdd.append(String.format("localhost%s %s%n", portFragment, fingerprint));
fingerprintsToAdd.append(String.format("127.0.0.1%s %s%n", portFragment, fingerprint));
}
Files.writeString(knownClientsPath, fingerprintsToAdd.toString());
}
@SuppressWarnings("JdkObsolete") // java.util.Enumeration is baked into the Keystore API
public static List<X509Certificate> getCertsFromPkcs12(final TlsCertificateDefinition certDef)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
final List<X509Certificate> results = Lists.newArrayList();
final KeyStore p12 = loadP12KeyStore(certDef.getPkcs12File(), certDef.getPassword());
final Enumeration<String> aliases = p12.aliases();
while (aliases.hasMoreElements()) {
results.add((X509Certificate) p12.getCertificate(aliases.nextElement()));
}
return results;
}
private static String generateFingerprint(final X509Certificate cert)
throws NoSuchAlgorithmException, CertificateEncodingException {
final MessageDigest md = MessageDigestFactory.create(MessageDigestFactory.SHA256_ALG);
md.update(cert.getEncoded());
final byte[] digest = md.digest();
final StringJoiner joiner = new StringJoiner(":");
for (final byte b : digest) {
joiner.add(String.format("%02X", b));
}
return joiner.toString().toLowerCase(Locale.ROOT);
}
}

@ -1,192 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.api.jsonrpc.methods.fork.frontier;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateMarkerTransaction;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.enclave.types.SendResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacyIdProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetPrivateTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.privacy.PrivateTransactionLegacyResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.RestrictedDefaultPrivacyController;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.plugin.data.Restriction;
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
import org.hyperledger.enclave.testutil.TesseraTestHarness;
import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Optional;
import com.google.common.collect.Lists;
import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class PrivGetPrivateTransactionIntegrationTest {
@TempDir private static Path folder;
private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
private final PrivacyIdProvider privacyIdProvider = (user) -> ENCLAVE_PUBLIC_KEY;
private final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class);
private final Blockchain blockchain = mock(Blockchain.class);
private final Address sender =
Address.fromHexString("0x0000000000000000000000000000000000000003");
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
private final KeyPair KEY_PAIR =
signatureAlgorithm.createKeyPair(
signatureAlgorithm.createPrivateKey(
new BigInteger(
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16)));
private final PrivateTransaction privateTransaction =
PrivateTransaction.builder()
.nonce(0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(null)
.value(Wei.ZERO)
.payload(
Bytes.fromHexString(
"0x608060405234801561001057600080fd5b5060d08061001f60003960"
+ "00f3fe60806040526004361060485763ffffffff7c01000000"
+ "00000000000000000000000000000000000000000000000000"
+ "60003504166360fe47b18114604d5780636d4ce63c14607557"
+ "5b600080fd5b348015605857600080fd5b5060736004803603"
+ "6020811015606d57600080fd5b50356099565b005b34801560"
+ "8057600080fd5b506087609e565b6040805191825251908190"
+ "0360200190f35b600055565b6000549056fea165627a7a7230"
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6"
+ "daa4f6b2f003d1b0180029"))
.sender(sender)
.chainId(BigInteger.valueOf(2018))
.privateFrom(Bytes.wrap(ENCLAVE_PUBLIC_KEY.getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
Bytes.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8))))
.restriction(Restriction.RESTRICTED)
.signAndBuild(KEY_PAIR);
private Vertx vertx = Vertx.vertx();
private TesseraTestHarness testHarness;
private Enclave enclave;
private PrivacyController privacyController;
@BeforeEach
public void setUp() throws Exception {
vertx = Vertx.vertx();
testHarness =
TesseraTestHarnessFactory.create(
"enclave",
Files.createTempDirectory(folder, "enclave"),
new EnclaveKeyConfiguration(
new String[] {"enclave_key_0.pub"},
new String[] {"enclave_key_0.key"},
EnclaveEncryptorType.NOOP),
Optional.empty());
testHarness.start();
final EnclaveFactory factory = new EnclaveFactory(vertx);
enclave = factory.createVertxEnclave(testHarness.clientUrl());
privacyController =
new RestrictedDefaultPrivacyController(
blockchain, privateStateStorage, enclave, null, null, null, null, null);
}
@AfterEach
public void tearDown() {
testHarness.close();
vertx.close();
}
@Test
public void returnsStoredPrivateTransaction() {
final PrivGetPrivateTransaction privGetPrivateTransaction =
new PrivGetPrivateTransaction(privacyController, privacyIdProvider);
final Hash blockHash = Hash.ZERO;
final Transaction pmt = spy(privateMarkerTransaction());
when(blockchain.getTransactionByHash(eq(pmt.getHash()))).thenReturn(Optional.of(pmt));
when(blockchain.getTransactionLocation(eq(pmt.getHash())))
.thenReturn(Optional.of(new TransactionLocation(blockHash, 0)));
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getHash()).thenReturn(blockHash);
when(blockchain.getBlockHeader(eq(blockHash))).thenReturn(Optional.of(blockHeader));
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput();
privateTransaction.writeTo(bvrlp);
final String payload = Base64.getEncoder().encodeToString(bvrlp.encoded().toArrayUnsafe());
final ArrayList<String> to = Lists.newArrayList("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=");
final SendResponse sendResponse = enclave.send(payload, ENCLAVE_PUBLIC_KEY, to);
final Bytes hexKey = Bytes.fromBase64String(sendResponse.getKey());
when(pmt.getPayload()).thenReturn(hexKey);
final Object[] params = new Object[] {pmt.getHash()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("1", "priv_getPrivateTransaction", params));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) privGetPrivateTransaction.response(request);
final PrivateTransactionLegacyResult result =
(PrivateTransactionLegacyResult) response.getResult();
assertThat(new PrivateTransactionLegacyResult(this.privateTransaction))
.usingRecursiveComparison()
.isEqualTo(result);
}
}

@ -1,244 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.precompiles.privacy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.enclave.types.SendResponse;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils;
import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
import org.hyperledger.enclave.testutil.TesseraTestHarness;
import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.Lists;
import io.vertx.core.Vertx;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class PrivacyPrecompiledContractIntegrationTest {
// this tempDir is deliberately static
@TempDir private static Path folder;
private static final Bytes VALID_PRIVATE_TRANSACTION_RLP =
Bytes.fromHexString(
"0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87"
+ "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d"
+ "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94"
+ "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267"
+ "5062572f776a3561784470573958386c393153476f3df85aac41316156744d"
+ "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838"
+ "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f"
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
+ "64");
private static final String DEFAULT_OUTPUT = "0x01";
private static Enclave enclave;
private static MessageFrame messageFrame;
private static TesseraTestHarness testHarness;
private static WorldStateArchive worldStateArchive;
private static PrivateStateStorage privateStateStorage;
private static final Vertx vertx = Vertx.vertx();
private PrivateTransactionProcessor mockPrivateTxProcessor() {
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
mock(PrivateTransactionProcessor.class);
final TransactionProcessingResult result =
TransactionProcessingResult.successful(
null, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null);
when(mockPrivateTransactionProcessor.processTransaction(
nullable(WorldUpdater.class),
nullable(WorldUpdater.class),
nullable(ProcessableBlockHeader.class),
nullable(Hash.class),
nullable(PrivateTransaction.class),
nullable(Address.class),
nullable(OperationTracer.class),
nullable(BlockHashLookup.class),
nullable(Bytes.class)))
.thenReturn(result);
return mockPrivateTransactionProcessor;
}
@BeforeAll
public static void setUpOnce() throws Exception {
testHarness =
TesseraTestHarnessFactory.create(
"enclave",
Files.createTempDirectory(folder, "enclave"),
new EnclaveKeyConfiguration(
new String[] {"enclave_key_0.pub"},
new String[] {"enclave_key_1.key"},
EnclaveEncryptorType.NOOP),
Optional.empty());
testHarness.start();
final EnclaveFactory factory = new EnclaveFactory(vertx);
enclave = factory.createVertxEnclave(testHarness.clientUrl());
messageFrame = mock(MessageFrame.class);
final BlockDataGenerator blockGenerator = new BlockDataGenerator();
final Block genesis = blockGenerator.genesisBlock();
final Block block =
blockGenerator.block(
new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash()));
when(messageFrame.getBlockValues()).thenReturn(block.getHeader());
final PrivateMetadataUpdater privateMetadataUpdater = mock(PrivateMetadataUpdater.class);
when(privateMetadataUpdater.getPrivateBlockMetadata(any())).thenReturn(null);
when(privateMetadataUpdater.getPrivacyGroupHeadBlockMap())
.thenReturn(PrivacyGroupHeadBlockMap.empty());
when(messageFrame.getContextVariable(
eq(PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE), anyBoolean()))
.thenReturn(false);
when(messageFrame.getContextVariable(eq(PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER)))
.thenReturn(privateMetadataUpdater);
when(messageFrame.hasContextVariable(eq(PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER)))
.thenReturn(true);
worldStateArchive = mock(WorldStateArchive.class);
final MutableWorldState mutableWorldState = mock(MutableWorldState.class);
when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class));
when(worldStateArchive.getMutable()).thenReturn(mutableWorldState);
when(worldStateArchive.getMutable(any(), any())).thenReturn(Optional.of(mutableWorldState));
privateStateStorage = mock(PrivateStateStorage.class);
final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class);
when(privateStateStorage.getPrivacyGroupHeadBlockMap(any()))
.thenReturn(Optional.of(PrivacyGroupHeadBlockMap.empty()));
when(storageUpdater.putPrivateBlockMetadata(
nullable(Bytes32.class), nullable(Bytes32.class), any()))
.thenReturn(storageUpdater);
when(storageUpdater.putTransactionReceipt(
nullable(Bytes32.class), nullable(Bytes32.class), any()))
.thenReturn(storageUpdater);
when(privateStateStorage.updater()).thenReturn(storageUpdater);
}
@AfterAll
public static void tearDownOnce() {
testHarness.stop();
vertx.close();
}
@Test
public void testUpCheck() {
assertThat(enclave.upCheck()).isTrue();
}
@Test
public void testSendAndReceive() {
final List<String> publicKeys = testHarness.getPublicKeys();
final PrivateTransaction privateTransaction =
PrivateTransactionDataFixture.privateContractDeploymentTransactionBesu(publicKeys.get(0));
final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput();
privateTransaction.writeTo(bytesValueRLPOutput);
final String s = bytesValueRLPOutput.encoded().toBase64String();
final SendResponse sr =
enclave.send(s, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0)));
final PrivacyPrecompiledContract privacyPrecompiledContract =
new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
new PrivateStateRootResolver(privateStateStorage),
new PrivateStateGenesisAllocator(
false, (privacyGroupId, blockNumber) -> Collections::emptyList),
false,
"IntegrationTest");
privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor());
final PrecompiledContract.PrecompileContractResult result =
privacyPrecompiledContract.computePrecompile(
Bytes.fromBase64String(sr.getKey()), messageFrame);
final Bytes actual = result.getOutput();
assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
@Test
public void testNoPrivateKeyError() throws RuntimeException {
final List<String> publicKeys = testHarness.getPublicKeys();
publicKeys.add("noPrivateKey");
final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String();
final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys));
assertThat(thrown).hasMessageContaining("Index 9 out of bounds for length 9");
}
@Test
public void testWrongPrivateKeyError() throws RuntimeException {
final List<String> publicKeys = testHarness.getPublicKeys();
publicKeys.add("noPrivateKenoPrivateKenoPrivateKenoPrivateK");
final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String();
final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys));
assertThat(thrown).hasMessageContaining("Recipient not found for key:");
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Request;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
@ -83,8 +84,8 @@ public interface BlockValidator {
final boolean shouldRecordBadBlock);
/**
* Performs fast block validation with the given context, block, transaction receipts, requests,
* header validation mode, and ommer validation mode.
* Performs fast block validation appropriate for use during syncing skipping transaction receipt
* roots and receipts roots as these are done during the download of the blocks.
*
* @param context the protocol context
* @param block the block to validate
@ -92,13 +93,15 @@ public interface BlockValidator {
* @param requests the requests
* @param headerValidationMode the header validation mode
* @param ommerValidationMode the ommer validation mode
* @param bodyValidationMode the body validation mode
* @return true if the block is valid, false otherwise
*/
boolean fastBlockValidation(
boolean validateBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode);
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode);
}

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.plugin.services.exception.StorageException;
@ -247,13 +248,14 @@ public class MainnetBlockValidator implements BlockValidator {
}
@Override
public boolean fastBlockValidation(
public boolean validateBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
final BlockHeader header = block.getHeader();
if (!blockHeaderValidator.validateHeader(header, context, headerValidationMode)) {
String description = String.format("Failed header validation (%s)", headerValidationMode);
@ -262,7 +264,7 @@ public class MainnetBlockValidator implements BlockValidator {
}
if (!blockBodyValidator.validateBodyLight(
context, block, receipts, requests, ommerValidationMode)) {
context, block, receipts, requests, ommerValidationMode, bodyValidationMode)) {
badBlockManager.addBadBlock(
block, BadBlockCause.fromValidationFailure("Failed body validation (light)"));
return false;

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
@ -73,10 +74,11 @@ public interface BlockImporter {
* @return {@code BlockImportResult}
* @see BlockImportResult
*/
BlockImportResult fastImportBlock(
BlockImportResult importBlockForSyncing(
ProtocolContext context,
Block block,
List<TransactionReceipt> receipts,
HeaderValidationMode headerValidationMode,
HeaderValidationMode ommerValidationMode);
HeaderValidationMode ommerValidationMode,
BodyValidationMode bodyValidationMode);
}

@ -81,7 +81,7 @@ public class CodeDelegationTransactionDecoder {
final Address address = Address.wrap(input.readBytes());
final long nonce = input.readLongScalar();
final long yParity = input.readUnsignedIntScalar();
final BigInteger yParity = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger();

@ -43,9 +43,11 @@ public class BaseFeeBlockBodyValidator extends MainnetBlockBodyValidator {
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
return super.validateBodyLight(context, block, receipts, requests, ommerValidationMode)
return super.validateBodyLight(
context, block, receipts, requests, ommerValidationMode, bodyValidationMode)
&& validateTransactionGasPrice(block);
}

@ -59,5 +59,6 @@ public interface BlockBodyValidator {
Block block,
List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode);
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode);
}

@ -0,0 +1,26 @@
/*
* 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;
public enum BodyValidationMode {
/** No Validation. data must be pre-validated */
NONE,
/** Skip receipts and transactions root validation */
LIGHT,
/** Fully validate the body */
FULL;
}

@ -29,6 +29,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,7 +56,8 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
final Hash worldStateRootHash,
final HeaderValidationMode ommerValidationMode) {
if (!validateBodyLight(context, block, receipts, requests, ommerValidationMode)) {
if (!validateBodyLight(
context, block, receipts, requests, ommerValidationMode, BodyValidationMode.FULL)) {
return false;
}
@ -77,10 +79,17 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
if (bodyValidationMode == BodyValidationMode.NONE) {
return true;
}
final BlockHeader header = block.getHeader();
final BlockBody body = block.getBody();
// these checks are only needed for full validation and can be skipped for light validation
if (bodyValidationMode == BodyValidationMode.FULL) {
final Bytes32 transactionsRoot = BodyValidation.transactionsRoot(body.getTransactions());
if (!validateTransactionsRoot(header, header.getTransactionsRoot(), transactionsRoot)) {
return false;
@ -90,6 +99,7 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
if (!validateReceiptsRoot(header, header.getReceiptsRoot(), receiptsRoot)) {
return false;
}
}
final long gasUsed =
receipts.isEmpty() ? 0 : receipts.get(receipts.size() - 1).getCumulativeGasUsed();
@ -115,7 +125,8 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
return true;
}
private static boolean validateTransactionsRoot(
@VisibleForTesting
protected boolean validateTransactionsRoot(
final BlockHeader header, final Bytes32 expected, final Bytes32 actual) {
if (!expected.equals(actual)) {
LOG.info(
@ -157,7 +168,8 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
return true;
}
private static boolean validateReceiptsRoot(
@VisibleForTesting
protected boolean validateReceiptsRoot(
final BlockHeader header, final Bytes32 expected, final Bytes32 actual) {
if (!expected.equals(actual)) {
LOG.warn(

@ -57,20 +57,22 @@ public class MainnetBlockImporter implements BlockImporter {
}
@Override
public BlockImportResult fastImportBlock(
public BlockImportResult importBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
if (blockValidator.fastBlockValidation(
if (blockValidator.validateBlockForSyncing(
context,
block,
receipts,
block.getBody().getRequests(),
headerValidationMode,
ommerValidationMode)) {
ommerValidationMode,
bodyValidationMode)) {
context.getBlockchain().appendBlock(block, receipts);
return new BlockImportResult(true);
}

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -98,7 +99,8 @@ public class MainnetBlockValidatorTest {
when(blockHeaderValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
when(blockBodyValidator.validateBody(any(), any(), any(), any(), any(), any()))
.thenReturn(true);
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any(), any())).thenReturn(true);
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any(), any(), any()))
.thenReturn(true);
when(blockProcessor.processBlock(any(), any(), any())).thenReturn(successfulProcessingResult);
when(blockProcessor.processBlock(any(), any(), any(), any()))
.thenReturn(successfulProcessingResult);
@ -343,55 +345,65 @@ public class MainnetBlockValidatorTest {
}
@Test
public void fastBlockValidation_onSuccess() {
public void validateBlockForSyncing_onSuccess() {
final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
HeaderValidationMode.FULL,
HeaderValidationMode.FULL);
HeaderValidationMode.FULL,
BodyValidationMode.FULL);
assertThat(isValid).isTrue();
assertNoBadBlocks();
}
@Test
public void fastBlockValidation_onFailedHeaderValidation() {
final HeaderValidationMode validationMode = HeaderValidationMode.FULL;
public void validateBlockValidation_onFailedHeaderForSyncing() {
final HeaderValidationMode headerValidationMode = HeaderValidationMode.FULL;
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class), eq(protocolContext), eq(validationMode)))
any(BlockHeader.class), eq(protocolContext), eq(headerValidationMode)))
.thenReturn(false);
final BodyValidationMode bodyValidationMode = BodyValidationMode.FULL;
final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
validationMode,
validationMode);
headerValidationMode,
headerValidationMode,
bodyValidationMode);
assertThat(isValid).isFalse();
assertBadBlockIsTracked(block);
}
@Test
public void fastBlockValidation_onFailedBodyValidation() {
final HeaderValidationMode validationMode = HeaderValidationMode.FULL;
public void validateBlockValidation_onFailedBodyForSyncing() {
final HeaderValidationMode headerValidationMode = HeaderValidationMode.FULL;
final BodyValidationMode bodyValidationMode = BodyValidationMode.FULL;
when(blockBodyValidator.validateBodyLight(
eq(protocolContext), eq(block), any(), any(), eq(validationMode)))
eq(protocolContext),
eq(block),
any(),
any(),
eq(headerValidationMode),
eq(bodyValidationMode)))
.thenReturn(false);
final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
validationMode,
validationMode);
headerValidationMode,
headerValidationMode,
bodyValidationMode);
assertThat(isValid).isFalse();
assertBadBlockIsTracked(block);

@ -19,14 +19,22 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode.NONE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestValidator;
import org.hyperledger.besu.ethereum.mainnet.requests.RequestsValidatorCoordinator;
@ -93,7 +101,12 @@ class MainnetBlockBodyValidatorTest {
assertThat(
new MainnetBlockBodyValidator(protocolSchedule)
.validateBodyLight(
blockchainSetupUtil.getProtocolContext(), block, emptyList(), any(), NONE))
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
any(),
NONE,
BodyValidationMode.FULL))
.isTrue();
}
@ -117,7 +130,12 @@ class MainnetBlockBodyValidatorTest {
assertThat(
new MainnetBlockBodyValidator(protocolSchedule)
.validateBodyLight(
blockchainSetupUtil.getProtocolContext(), block, emptyList(), any(), NONE))
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
any(),
NONE,
BodyValidationMode.FULL))
.isFalse();
}
@ -141,7 +159,12 @@ class MainnetBlockBodyValidatorTest {
assertThat(
new MainnetBlockBodyValidator(protocolSchedule)
.validateBodyLight(
blockchainSetupUtil.getProtocolContext(), block, emptyList(), any(), NONE))
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
any(),
NONE,
BodyValidationMode.FULL))
.isFalse();
}
@ -165,7 +188,99 @@ class MainnetBlockBodyValidatorTest {
assertThat(
new MainnetBlockBodyValidator(protocolSchedule)
.validateBodyLight(
blockchainSetupUtil.getProtocolContext(), block, emptyList(), any(), NONE))
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
any(),
NONE,
BodyValidationMode.FULL))
.isFalse();
}
@Test
@SuppressWarnings("unchecked")
public void noneValidationModeDoesNothing() {
final Block block = mock(Block.class);
final List<TransactionReceipt> receipts = mock(List.class);
final MainnetBlockBodyValidator bodyValidator = new MainnetBlockBodyValidator(protocolSchedule);
assertThat(
bodyValidator.validateBodyLight(
blockchainSetupUtil.getProtocolContext(),
block,
receipts,
Optional.empty(),
NONE,
BodyValidationMode.NONE))
.isTrue();
verifyNoInteractions(block);
verifyNoInteractions(receipts);
}
@Test
public void lightValidationDoesNotCheckTransactionRootOrReceiptRoot() {
final Block block =
blockDataGenerator.block(
new BlockOptions()
.setBlockNumber(1)
.setGasUsed(0)
.hasTransactions(false)
.hasOmmers(false)
.setReceiptsRoot(BodyValidation.receiptsRoot(emptyList()))
.setLogsBloom(LogsBloomFilter.empty())
.setParentHash(blockchainSetupUtil.getBlockchain().getChainHeadHash())
.setWithdrawals(Optional.of(withdrawals)));
blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList());
final MainnetBlockBodyValidator bodyValidator = new MainnetBlockBodyValidator(protocolSchedule);
final MainnetBlockBodyValidator bodyValidatorSpy = spy(bodyValidator);
assertThat(
bodyValidatorSpy.validateBodyLight(
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
Optional.empty(),
NONE,
BodyValidationMode.LIGHT))
.isTrue();
verify(bodyValidatorSpy, never()).validateReceiptsRoot(any(), any(), any());
verify(bodyValidatorSpy, never()).validateTransactionsRoot(any(), any(), any());
}
@Test
public void fullValidationChecksTransactionRootAndReceiptRoot() {
final Block block =
blockDataGenerator.block(
new BlockOptions()
.setBlockNumber(1)
.setGasUsed(0)
.hasTransactions(false)
.hasOmmers(false)
.setReceiptsRoot(BodyValidation.receiptsRoot(emptyList()))
.setLogsBloom(LogsBloomFilter.empty())
.setParentHash(blockchainSetupUtil.getBlockchain().getChainHeadHash())
.setWithdrawals(Optional.of(withdrawals)));
blockchainSetupUtil.getBlockchain().appendBlock(block, Collections.emptyList());
final MainnetBlockBodyValidator bodyValidator = new MainnetBlockBodyValidator(protocolSchedule);
final MainnetBlockBodyValidator bodyValidatorSpy = spy(bodyValidator);
assertThat(
bodyValidatorSpy.validateBodyLight(
blockchainSetupUtil.getProtocolContext(),
block,
emptyList(),
Optional.empty(),
NONE,
BodyValidationMode.FULL))
.isTrue();
final Hash receiptsRoot = BodyValidation.receiptsRoot(emptyList());
final Hash transactionsRoot = BodyValidation.transactionsRoot(emptyList());
verify(bodyValidatorSpy, times(1))
.validateReceiptsRoot(block.getHeader(), receiptsRoot, receiptsRoot);
verify(bodyValidatorSpy, times(1))
.validateTransactionsRoot(block.getHeader(), transactionsRoot, transactionsRoot);
}
}

@ -106,7 +106,8 @@ class PragueRequestsValidatorTest {
block,
emptyList(),
expectedRequests,
NONE))
NONE,
BodyValidationMode.FULL))
.isFalse();
}
}

@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.eth.sync.range.SyncTargetRange;
import org.hyperledger.besu.ethereum.eth.sync.range.SyncTargetRangeSource;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncTarget;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -117,6 +118,10 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
final int downloaderParallelism = syncConfig.getDownloaderParallelism();
final int headerRequestSize = syncConfig.getDownloaderHeaderRequestSize();
final int singleHeaderBufferSize = headerRequestSize * downloaderParallelism;
final BodyValidationMode bodyValidationMode =
protocolSchedule.anyMatch(scheduledProtocolSpec -> scheduledProtocolSpec.spec().isPoS())
? BodyValidationMode.NONE
: BodyValidationMode.LIGHT;
final SyncTargetRangeSource checkpointRangeSource =
new SyncTargetRangeSource(
new RangeHeadersFetcher(
@ -148,7 +153,8 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
attachedValidationPolicy,
ommerValidationPolicy,
ethContext,
fastSyncState.getPivotBlockHeader().get());
fastSyncState.getPivotBlockHeader().get(),
bodyValidationMode);
return PipelineBuilder.createPipelineFrom(
"fetchCheckpoints",

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.sync.ValidationPolicy;
import org.hyperledger.besu.ethereum.eth.sync.tasks.exceptions.InvalidBlockException;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import java.util.List;
@ -45,6 +46,7 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
private long accumulatedTime = 0L;
private OptionalLong logStartBlock = OptionalLong.empty();
private final BlockHeader pivotHeader;
private final BodyValidationMode bodyValidationMode;
public ImportBlocksStep(
final ProtocolSchedule protocolSchedule,
@ -52,13 +54,15 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
final ValidationPolicy headerValidationPolicy,
final ValidationPolicy ommerValidationPolicy,
final EthContext ethContext,
final BlockHeader pivotHeader) {
final BlockHeader pivotHeader,
final BodyValidationMode bodyValidationMode) {
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.headerValidationPolicy = headerValidationPolicy;
this.ommerValidationPolicy = ommerValidationPolicy;
this.ethContext = ethContext;
this.pivotHeader = pivotHeader;
this.bodyValidationMode = bodyValidationMode;
}
@Override
@ -106,20 +110,20 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
if (totalBlocks == 0) {
return 0;
}
final long blocksPercent = (100 * lastBlock / totalBlocks);
return blocksPercent;
return (100 * lastBlock / totalBlocks);
}
protected boolean importBlock(final BlockWithReceipts blockWithReceipts) {
final BlockImporter importer =
protocolSchedule.getByBlockHeader(blockWithReceipts.getHeader()).getBlockImporter();
final BlockImportResult blockImportResult =
importer.fastImportBlock(
importer.importBlockForSyncing(
protocolContext,
blockWithReceipts.getBlock(),
blockWithReceipts.getReceipts(),
headerValidationPolicy.getValidationModeForNextBlock(),
ommerValidationPolicy.getValidationModeForNextBlock());
ommerValidationPolicy.getValidationModeForNextBlock(),
bodyValidationMode);
return blockImportResult.isImported();
}
}

@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
import org.hyperledger.besu.ethereum.eth.sync.ValidationPolicy;
import org.hyperledger.besu.ethereum.eth.sync.tasks.exceptions.InvalidBlockException;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
@ -72,7 +73,8 @@ public class ImportBlocksStepTest {
validationPolicy,
ommerValidationPolicy,
null,
pivotHeader);
pivotHeader,
BodyValidationMode.FULL);
}
@Test
@ -84,12 +86,13 @@ public class ImportBlocksStepTest {
.collect(toList());
for (final BlockWithReceipts blockWithReceipts : blocksWithReceipts) {
when(blockImporter.fastImportBlock(
when(blockImporter.importBlockForSyncing(
protocolContext,
blockWithReceipts.getBlock(),
blockWithReceipts.getReceipts(),
FULL,
LIGHT))
LIGHT,
BodyValidationMode.FULL))
.thenReturn(new BlockImportResult(true));
}
importBlocksStep.accept(blocksWithReceipts);
@ -105,8 +108,13 @@ public class ImportBlocksStepTest {
final Block block = gen.block();
final BlockWithReceipts blockWithReceipts = new BlockWithReceipts(block, gen.receipts(block));
when(blockImporter.fastImportBlock(
protocolContext, block, blockWithReceipts.getReceipts(), FULL, LIGHT))
when(blockImporter.importBlockForSyncing(
protocolContext,
block,
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.FULL))
.thenReturn(new BlockImportResult(false));
assertThatThrownBy(() -> importBlocksStep.accept(singletonList(blockWithReceipts)))
.isInstanceOf(InvalidBlockException.class);

@ -1023,6 +1023,11 @@
<sha256 value="c19eba1f4897afe5d5926d68e19f2c2986752e459cfa9c5925e57290d9217add" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-bom" version="3.23.0">
<artifact name="protobuf-bom-3.23.0.pom">
<sha256 value="4e4ce6aeeed9b97ce86a7a61b33a33cfaa818f25fd5c8b67093b90b52af5ea4f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-bom" version="3.23.4">
<artifact name="protobuf-bom-3.23.4.pom">
<sha256 value="aba9410f5ed7278126b8c046fbc74e7d0b2ba3cd18ac7a59d3bf0ffab0774f78" origin="Generated by Gradle"/>
@ -1033,6 +1038,11 @@
<sha256 value="b46e3f26fe0f473ff33077ee1245f88d4b8db35cc79f554cd0fd937766a45e58" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-bom" version="3.25.5">
<artifact name="protobuf-bom-3.25.5.pom">
<sha256 value="080e2984173238b50e064c226afffbb1b0233520295c790a7fd3d6ae4593f063" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java" version="3.19.6">
<artifact name="protobuf-java-3.19.6.jar">
<sha256 value="6a9a2dff91dcf71f85be71ae971f6164b5a631dcd34bff08f0618535ca44ad02" origin="Generated by Gradle"/>
@ -1049,6 +1059,14 @@
<sha256 value="7ef79a4915167d42f785c1b16b60e48af15df3dd017289fa1e10e494e9eee3e3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java" version="3.23.0">
<artifact name="protobuf-java-3.23.0.jar">
<sha256 value="5aef5807afe5da8201612e5577690057466cebea063f239cb95504c9df649991" origin="Generated by Gradle"/>
</artifact>
<artifact name="protobuf-java-3.23.0.pom">
<sha256 value="5ae12a87b64aa3abf5728bc956daec81d93de88fe8616c60f7f2b0eaea115208" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java" version="3.23.4">
<artifact name="protobuf-java-3.23.4.pom">
<sha256 value="6deebe93fe6a35bab8fe926d6e617997a938ab0123175d0bd80edca6421ed20c" origin="Generated by Gradle"/>
@ -1062,6 +1080,14 @@
<sha256 value="c1efac1ae00f0bbab656428e196b63698c914d0dd22be120710beffc481cb547" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java" version="3.25.5">
<artifact name="protobuf-java-3.25.5.jar">
<sha256 value="8540247fad9e06baefa8fb45eb313802d019f485f14300e0f9d6b556ed88e753" origin="Generated by Gradle"/>
</artifact>
<artifact name="protobuf-java-3.25.5.pom">
<sha256 value="e752032157a7a39be9be3786684075452a46cd586b2865abd33e707568a4c8af" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-java-util" version="3.25.3">
<artifact name="protobuf-java-util-3.25.3.jar">
<sha256 value="b813c8d6d554cb71c1e82d171d7f80730ae74222a185c863cbedf05072c88155" origin="Generated by Gradle"/>
@ -1080,6 +1106,11 @@
<sha256 value="1b0229a3bbfef37b025d0d8d7ab369393e7128dabb809023681d1ecccf789020" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-parent" version="3.23.0">
<artifact name="protobuf-parent-3.23.0.pom">
<sha256 value="e979a0c47b473bbcd6b3586f12a8892b7914b28ea06211db53c4a1a3ce6cf7d8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-parent" version="3.23.4">
<artifact name="protobuf-parent-3.23.4.pom">
<sha256 value="43804e420ca2d5d9a4505707d5d301da0b28c28f946b12cc727378f3c8affb39" origin="Generated by Gradle"/>
@ -1090,6 +1121,11 @@
<sha256 value="bc2744608cea3a7a2d4cd0b74e1c3f88138c64ce5aa61b9d7f0afd846882be7a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.protobuf" name="protobuf-parent" version="3.25.5">
<artifact name="protobuf-parent-3.25.5.pom">
<sha256 value="64cc0e3ad6e85f5aec8f9dcf9341d1379e9525364ff53e23e16d8d5824673ef7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.re2j" name="re2j" version="1.7">
<artifact name="re2j-1.7.jar">
<sha256 value="4f657af51ab8bb0909bcc3eb40862d26125af8cbcf92aaaba595fed77f947bc0" origin="Generated by Gradle"/>

@ -25,6 +25,8 @@ dependencyManagement {
dependency 'com.github.ben-manes.caffeine:caffeine:3.1.8'
dependency 'com.google.protobuf:protobuf-java:3.25.5'
dependency 'com.github.oshi:oshi-core:6.6.3'
dependency 'com.google.auto.service:auto-service:1.1.1'

@ -1 +0,0 @@
{"data":{"bytes":"hBsuQsGJzx4QHmFmBkNoI7YGnTmaZP4P+wBOdu56ljk="},"type":"unlocked"}
Loading…
Cancel
Save