Merge branch 'main' into 7311-add-GetReceiptsFromPeerTask

pull/7638/head
Matilda-Clerke 2 months ago committed by GitHub
commit 3de578dbc4
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. 29
      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. 58
      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. 50
      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. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java
  43. 36
      gradle/verification-metadata.xml
  44. 2
      gradle/versions.gradle
  45. 1
      testutil/src/main/resources/enclave_key_0.key

@ -12,7 +12,7 @@ concurrency:
env: env:
GRADLE_OPTS: "-Xmx7g" GRADLE_OPTS: "-Xmx7g"
total-runners: 12 total-runners: 14
jobs: jobs:
acceptanceTestEthereum: acceptanceTestEthereum:
@ -24,7 +24,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: 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: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11

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

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

@ -120,7 +120,11 @@ public class TomlConfigurationDefaultProvider implements IDefaultValueProvider {
} }
private boolean isNumericType(final Class<?> type) { 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(int.class)
|| type.equals(Long.class) || type.equals(Long.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.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -182,7 +183,10 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()), Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector, proposerSelector,
uniqueMessageMulticaster, uniqueMessageMulticaster,
new RoundTimer(bftEventQueue, bftConfig.getRequestTimeoutSeconds(), bftExecutors), new RoundTimer(
bftEventQueue,
Duration.ofSeconds(bftConfig.getRequestTimeoutSeconds()),
bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, clock), new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, clock),
blockCreatorFactory, blockCreatorFactory,
clock); clock);

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

@ -14,6 +14,14 @@
## SPDX-License-Identifier: Apache-2.0 ## 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 # Run Besu first to get paths needing permission adjustment
output=$(/opt/besu/bin/besu --print-paths-and-exit $BESU_USER_NAME "$@") 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 fi
done 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 # 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)); 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 @Test
public void minScoreNonByteValueReturnError() { public void minScoreNonByteValueReturnError() {
final var overflowMinScore = Integer.toString(-300); final var overflowMinScore = Integer.toString(-300);

@ -37,6 +37,13 @@ public interface BftConfigOptions {
*/ */
int getBlockPeriodSeconds(); 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. * Gets request timeout seconds.
* *

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.OptionalLong;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
@ -40,6 +41,9 @@ public class BftFork implements Fork {
/** The constant BLOCK_PERIOD_SECONDS_KEY. */ /** The constant BLOCK_PERIOD_SECONDS_KEY. */
public static final String BLOCK_PERIOD_SECONDS_KEY = "blockperiodseconds"; 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. */ /** The constant BLOCK_REWARD_KEY. */
public static final String BLOCK_REWARD_KEY = "blockreward"; 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); 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. * 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 long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1; 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; 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 // 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 // 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); bftConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS);
} }
@Override
public long getBlockPeriodMilliseconds() {
return JsonUtil.getLong(
bftConfigRoot, "xblockperiodmilliseconds", DEFAULT_BLOCK_PERIOD_MILLISECONDS);
}
@Override @Override
public int getRequestTimeoutSeconds() { public int getRequestTimeoutSeconds() {
return JsonUtil.getInt(bftConfigRoot, "requesttimeoutseconds", DEFAULT_ROUND_EXPIRY_SECONDS); return JsonUtil.getInt(bftConfigRoot, "requesttimeoutseconds", DEFAULT_ROUND_EXPIRY_SECONDS);
@ -133,6 +140,9 @@ public class JsonBftConfigOptions implements BftConfigOptions {
if (bftConfigRoot.has("blockperiodseconds")) { if (bftConfigRoot.has("blockperiodseconds")) {
builder.put("blockPeriodSeconds", getBlockPeriodSeconds()); builder.put("blockPeriodSeconds", getBlockPeriodSeconds());
} }
if (bftConfigRoot.has("xblockperiodmilliseconds")) {
builder.put("xBlockPeriodMilliSeconds", getBlockPeriodMilliseconds());
}
if (bftConfigRoot.has("requesttimeoutseconds")) { if (bftConfigRoot.has("requesttimeoutseconds")) {
builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds()); builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds());
} }

@ -24,9 +24,14 @@ import java.util.Optional;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Class for starting and keeping organised block timers */ /** Class for starting and keeping organised block timers */
public class BlockTimer { public class BlockTimer {
private static final Logger LOG = LoggerFactory.getLogger(BlockTimer.class);
private final ForksSchedule<? extends BftConfigOptions> forksSchedule; private final ForksSchedule<? extends BftConfigOptions> forksSchedule;
private final BftExecutors bftExecutors; private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask; private Optional<ScheduledFuture<?>> currentTimerTask;
@ -79,12 +84,26 @@ public class BlockTimer {
cancelTimer(); cancelTimer();
final long now = clock.millis(); 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();
// absolute time when the timer is supposed to expire if (blockPeriodMilliseconds > 0) {
final int blockPeriodSeconds = // Experimental mode for setting < 1 second block periods e.g. for CI/CD pipelines
forksSchedule.getFork(round.getSequenceNumber()).getValue().getBlockPeriodSeconds(); // running tests against Besu
final long minimumTimeBetweenBlocksMillis = blockPeriodSeconds * 1000L; expiryTime = clock.millis() + blockPeriodMilliseconds;
final long expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis; 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;
expiryTime = chainHeadHeader.getTimestamp() * 1_000 + minimumTimeBetweenBlocksMillis;
}
if (expiryTime > now) { if (expiryTime > now) {
final long delay = expiryTime - now; final long delay = expiryTime - now;

@ -31,6 +31,7 @@ import java.util.Optional;
public class MutableBftConfigOptions implements BftConfigOptions { public class MutableBftConfigOptions implements BftConfigOptions {
private long epochLength; private long epochLength;
private int blockPeriodSeconds; private int blockPeriodSeconds;
private long blockPeriodMilliseconds;
private int requestTimeoutSeconds; private int requestTimeoutSeconds;
private int gossipedHistoryLimit; private int gossipedHistoryLimit;
private int messageQueueLimit; private int messageQueueLimit;
@ -48,6 +49,7 @@ public class MutableBftConfigOptions implements BftConfigOptions {
public MutableBftConfigOptions(final BftConfigOptions bftConfigOptions) { public MutableBftConfigOptions(final BftConfigOptions bftConfigOptions) {
this.epochLength = bftConfigOptions.getEpochLength(); this.epochLength = bftConfigOptions.getEpochLength();
this.blockPeriodSeconds = bftConfigOptions.getBlockPeriodSeconds(); this.blockPeriodSeconds = bftConfigOptions.getBlockPeriodSeconds();
this.blockPeriodMilliseconds = bftConfigOptions.getBlockPeriodMilliseconds();
this.requestTimeoutSeconds = bftConfigOptions.getRequestTimeoutSeconds(); this.requestTimeoutSeconds = bftConfigOptions.getRequestTimeoutSeconds();
this.gossipedHistoryLimit = bftConfigOptions.getGossipedHistoryLimit(); this.gossipedHistoryLimit = bftConfigOptions.getGossipedHistoryLimit();
this.messageQueueLimit = bftConfigOptions.getMessageQueueLimit(); this.messageQueueLimit = bftConfigOptions.getMessageQueueLimit();
@ -68,6 +70,11 @@ public class MutableBftConfigOptions implements BftConfigOptions {
return blockPeriodSeconds; return blockPeriodSeconds;
} }
@Override
public long getBlockPeriodMilliseconds() {
return blockPeriodMilliseconds;
}
@Override @Override
public int getRequestTimeoutSeconds() { public int getRequestTimeoutSeconds() {
return requestTimeoutSeconds; return requestTimeoutSeconds;
@ -131,6 +138,16 @@ public class MutableBftConfigOptions implements BftConfigOptions {
this.blockPeriodSeconds = blockPeriodSeconds; 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. * 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 org.hyperledger.besu.consensus.common.bft.events.RoundExpiry;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -31,21 +32,21 @@ public class RoundTimer {
private final BftExecutors bftExecutors; private final BftExecutors bftExecutors;
private Optional<ScheduledFuture<?>> currentTimerTask; private Optional<ScheduledFuture<?>> currentTimerTask;
private final BftEventQueue queue; private final BftEventQueue queue;
private final long baseExpiryMillis; private final Duration baseExpiryPeriod;
/** /**
* Construct a RoundTimer with primed executor service ready to start timers * Construct a RoundTimer with primed executor service ready to start timers
* *
* @param queue The queue in which to put round expiry events * @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 * @param bftExecutors executor service that timers can be scheduled with
*/ */
public RoundTimer( public RoundTimer(
final BftEventQueue queue, final long baseExpirySeconds, final BftExecutors bftExecutors) { final BftEventQueue queue, final Duration baseExpiryPeriod, final BftExecutors bftExecutors) {
this.queue = queue; this.queue = queue;
this.bftExecutors = bftExecutors; this.bftExecutors = bftExecutors;
this.currentTimerTask = Optional.empty(); this.currentTimerTask = Optional.empty();
this.baseExpiryMillis = baseExpirySeconds * 1000; this.baseExpiryPeriod = baseExpiryPeriod;
} }
/** Cancels the current running round timer if there is one */ /** Cancels the current running round timer if there is one */
@ -71,7 +72,8 @@ public class RoundTimer {
public synchronized void startTimer(final ConsensusRoundIdentifier round) { public synchronized void startTimer(final ConsensusRoundIdentifier round) {
cancelTimer(); 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)); 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 // Once we are up to round 2 start logging round expiries
if (round.getRoundNumber() >= 2) { if (round.getRoundNumber() >= 2) {
LOG.info( 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() - 1,
round.getRoundNumber(), round.getRoundNumber(),
(expiryTime / 1000)); (expiryTime / 1000));

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

@ -100,6 +100,7 @@ import org.hyperledger.besu.testutil.TestClock;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.time.Clock; import java.time.Clock;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
@ -403,7 +404,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()), Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector, proposerSelector,
multicaster, multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors), new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()), new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory, blockCreatorFactory,
clock); 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.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
@ -45,32 +46,43 @@ public class IbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form * 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) * 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. * @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers * @return BlockHeaderValidator configured for assessing bft block headers
*/ */
public static BlockHeaderValidator.Builder blockHeaderValidator( public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) { final Duration minimumTimeBetweenBlocks, final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder() final BlockHeaderValidator.Builder ruleBuilder =
.addRule(new AncestryValidationRule()) new BlockHeaderValidator.Builder()
.addRule(new GasUsageValidationRule()) .addRule(new AncestryValidationRule())
.addRule( .addRule(new GasUsageValidationRule())
new GasLimitRangeAndDeltaValidationRule( .addRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket)) new GasLimitRangeAndDeltaValidationRule(
.addRule(new TimestampBoundedByFutureParameter(1)) DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks)) .addRule(new TimestampBoundedByFutureParameter(1))
.addRule( .addRule(
new ConstantFieldValidationRule<>( new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH)) "MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule( .addRule(
new ConstantFieldValidationRule<>( new ConstantFieldValidationRule<>(
"OmmersHash", BlockHeader::getOmmersHash, Hash.EMPTY_LIST_HASH)) "OmmersHash", BlockHeader::getOmmersHash, Hash.EMPTY_LIST_HASH))
.addRule( .addRule(
new ConstantFieldValidationRule<>( new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE)) "Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new ConstantFieldValidationRule<>("Nonce", BlockHeader::getNonce, 0L)) .addRule(new ConstantFieldValidationRule<>("Nonce", BlockHeader::getNonce, 0L))
.addRule(new BftValidatorsValidationRule()) .addRule(new BftValidatorsValidationRule())
.addRule(new BftCoinbaseValidationRule()) .addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule()); .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.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a BFT consensus mechanism. */ /** 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); Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( 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; 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; return false;
} }
@ -93,14 +95,14 @@ public class MessageValidator {
msg.getSignedPayload(), msg.getBlock(), blockInterface); msg.getSignedPayload(), msg.getBlock(), blockInterface);
} }
private boolean validateBlock(final Block block) { private boolean validateBlockWithoutPersisting(final Block block) {
final BlockValidator blockValidator = final BlockValidator blockValidator =
protocolSchedule.getByBlockHeader(block.getHeader()).getBlockValidator(); protocolSchedule.getByBlockHeader(block.getHeader()).getBlockValidator();
final var validationResult = final var validationResult =
blockValidator.validateAndProcessBlock( blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL); protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL, false);
if (validationResult.isFailed()) { if (validationResult.isFailed()) {
LOG.info( 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.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -93,7 +94,7 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator = final BlockHeaderValidator validator =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.of(FeeMarket.london(1))) Duration.ofSeconds(5), Optional.of(FeeMarket.london(1)))
.build(); .build();
assertThat( assertThat(
@ -372,7 +373,8 @@ public class IbftBlockHeaderValidationRulesetFactoryTest {
} }
public BlockHeaderValidator getBlockHeaderValidator() { public BlockHeaderValidator getBlockHeaderValidator() {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, Optional.empty()) return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), Optional.empty())
.build(); .build();
} }
} }

@ -64,6 +64,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler; import org.hyperledger.besu.testutil.DeterministicEthScheduler;
import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.testutil.TestClock;
import java.time.Duration;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -105,7 +106,7 @@ public class BftBlockCreatorTest {
public BlockHeaderValidator.Builder createBlockHeaderRuleset( public BlockHeaderValidator.Builder createBlockHeaderRuleset(
final BftConfigOptions config, final FeeMarket feeMarket) { final BftConfigOptions config, final FeeMarket feeMarket) {
return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( return IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, Optional.empty()); Duration.ofSeconds(5), Optional.empty());
} }
}; };
final GenesisConfigOptions configOptions = final GenesisConfigOptions configOptions =
@ -200,7 +201,7 @@ public class BftBlockCreatorTest {
final BlockHeaderValidator rules = final BlockHeaderValidator rules =
IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( IbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
secondsBetweenBlocks, Optional.empty()) Duration.ofSeconds(secondsBetweenBlocks), Optional.empty())
.build(); .build();
// NOTE: The header will not contain commit seals, so can only do light validation on header. // 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(protocolSpec.getBlockValidator()).thenReturn(blockValidator);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); 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())); .thenReturn(new BlockProcessingResult(Optional.empty()));
when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate(
@ -168,7 +168,7 @@ public class MessageValidatorTest {
@Test @Test
public void blockValidationFailureFailsValidation() { public void blockValidationFailureFailsValidation() {
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) when(blockValidator.validateAndProcessBlock(any(), any(), any(), any(), eq(false)))
.thenReturn(new BlockProcessingResult("Failed")); .thenReturn(new BlockProcessingResult("Failed"));
final Proposal proposalMsg = final Proposal proposalMsg =

@ -118,6 +118,7 @@ import org.hyperledger.besu.util.Subscribers;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Clock; import java.time.Clock;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
@ -512,7 +513,7 @@ public class TestContextBuilder {
Util.publicKeyToAddress(nodeKey.getPublicKey()), Util.publicKeyToAddress(nodeKey.getPublicKey()),
proposerSelector, proposerSelector,
multicaster, multicaster,
new RoundTimer(bftEventQueue, ROUND_TIMER_SEC, bftExecutors), new RoundTimer(bftEventQueue, Duration.ofSeconds(ROUND_TIMER_SEC), bftExecutors),
new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()), new BlockTimer(bftEventQueue, forksSchedule, bftExecutors, TestClock.fixed()),
blockCreatorFactory, blockCreatorFactory,
clock); 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.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
@ -44,31 +45,42 @@ public class QbftBlockHeaderValidationRulesetFactory {
* Produces a BlockHeaderValidator configured for assessing bft block headers which are to form * 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) * 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 useValidatorContract whether validator selection is using a validator contract
* @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate. * @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate.
* @return BlockHeaderValidator configured for assessing bft block headers * @return BlockHeaderValidator configured for assessing bft block headers
*/ */
public static BlockHeaderValidator.Builder blockHeaderValidator( public static BlockHeaderValidator.Builder blockHeaderValidator(
final long secondsBetweenBlocks, final Duration minimumTimeBetweenBlocks,
final boolean useValidatorContract, final boolean useValidatorContract,
final Optional<BaseFeeMarket> baseFeeMarket) { final Optional<BaseFeeMarket> baseFeeMarket) {
return new BlockHeaderValidator.Builder() BlockHeaderValidator.Builder ruleBuilder =
.addRule(new AncestryValidationRule()) new BlockHeaderValidator.Builder()
.addRule(new GasUsageValidationRule()) .addRule(new AncestryValidationRule())
.addRule( .addRule(new GasUsageValidationRule())
new GasLimitRangeAndDeltaValidationRule( .addRule(
DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket)) new GasLimitRangeAndDeltaValidationRule(
.addRule(new TimestampBoundedByFutureParameter(1)) DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks)) .addRule(new TimestampBoundedByFutureParameter(1))
.addRule( .addRule(
new ConstantFieldValidationRule<>( new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH)) "MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH))
.addRule( .addRule(
new ConstantFieldValidationRule<>( new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE)) "Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new QbftValidatorsValidationRule(useValidatorContract)) .addRule(new QbftValidatorsValidationRule(useValidatorContract))
.addRule(new BftCoinbaseValidationRule()) .addRule(new BftCoinbaseValidationRule())
.addRule(new BftCommitSealsValidationRule()); .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()); new MutableQbftConfigOptions(lastSpec.getValue());
fork.getBlockPeriodSeconds().ifPresent(bftConfigOptions::setBlockPeriodSeconds); fork.getBlockPeriodSeconds().ifPresent(bftConfigOptions::setBlockPeriodSeconds);
fork.getBlockPeriodMilliseconds().ifPresent(bftConfigOptions::setBlockPeriodMilliseconds);
fork.getBlockRewardWei().ifPresent(bftConfigOptions::setBlockRewardWei); fork.getBlockRewardWei().ifPresent(bftConfigOptions::setBlockRewardWei);
if (fork.isMiningBeneficiaryConfigured()) { 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.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
/** Defines the protocol behaviours for a blockchain using a QBFT consensus mechanism. */ /** 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); Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast);
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
qbftConfigOptions.getBlockPeriodSeconds(), qbftConfigOptions.getBlockPeriodMilliseconds() > 0
? Duration.ofMillis(qbftConfigOptions.getBlockPeriodMilliseconds())
: Duration.ofSeconds(qbftConfigOptions.getBlockPeriodSeconds()),
qbftConfigOptions.isValidatorContractMode(), qbftConfigOptions.isValidatorContractMode(),
baseFeeMarket); 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.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -92,7 +93,7 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
final BlockHeaderValidator validator = final BlockHeaderValidator validator =
QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator( QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
5, false, Optional.of(FeeMarket.london(1))) Duration.ofSeconds(5), false, Optional.of(FeeMarket.london(1)))
.build(); .build();
assertThat( assertThat(
@ -366,7 +367,8 @@ public class QbftBlockHeaderValidationRulesetFactoryTest {
} }
public BlockHeaderValidator getBlockHeaderValidator() { public BlockHeaderValidator getBlockHeaderValidator() {
return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(5, false, Optional.empty()) return QbftBlockHeaderValidationRulesetFactory.blockHeaderValidator(
Duration.ofSeconds(5), false, Optional.empty())
.build(); .build();
} }
} }

@ -214,7 +214,7 @@ public abstract class AbstractSECP256 implements SignatureAlgorithm {
@Override @Override
public CodeDelegationSignature createCodeDelegationSignature( 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); return CodeDelegationSignature.create(r, s, yParity);
} }

@ -42,18 +42,25 @@ public class CodeDelegationSignature extends SECPSignature {
* @return the new CodeDelegationSignature * @return the new CodeDelegationSignature
*/ */
public static CodeDelegationSignature create( 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(r);
checkNotNull(s); checkNotNull(s);
if (r.compareTo(TWO_POW_256) >= 0) { 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) { 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 * @return the code delegation signature
*/ */
CodeDelegationSignature createCodeDelegationSignature( CodeDelegationSignature createCodeDelegationSignature(
final BigInteger r, final BigInteger s, final long yParity); final BigInteger r, final BigInteger s, final BigInteger yParity);
/** /**
* Decode secp signature. * Decode secp signature.

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

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

@ -1023,6 +1023,11 @@
<sha256 value="c19eba1f4897afe5d5926d68e19f2c2986752e459cfa9c5925e57290d9217add" origin="Generated by Gradle"/> <sha256 value="c19eba1f4897afe5d5926d68e19f2c2986752e459cfa9c5925e57290d9217add" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.protobuf" name="protobuf-bom" version="3.23.4">
<artifact name="protobuf-bom-3.23.4.pom"> <artifact name="protobuf-bom-3.23.4.pom">
<sha256 value="aba9410f5ed7278126b8c046fbc74e7d0b2ba3cd18ac7a59d3bf0ffab0774f78" origin="Generated by Gradle"/> <sha256 value="aba9410f5ed7278126b8c046fbc74e7d0b2ba3cd18ac7a59d3bf0ffab0774f78" origin="Generated by Gradle"/>
@ -1033,6 +1038,11 @@
<sha256 value="b46e3f26fe0f473ff33077ee1245f88d4b8db35cc79f554cd0fd937766a45e58" origin="Generated by Gradle"/> <sha256 value="b46e3f26fe0f473ff33077ee1245f88d4b8db35cc79f554cd0fd937766a45e58" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.protobuf" name="protobuf-java" version="3.19.6">
<artifact name="protobuf-java-3.19.6.jar"> <artifact name="protobuf-java-3.19.6.jar">
<sha256 value="6a9a2dff91dcf71f85be71ae971f6164b5a631dcd34bff08f0618535ca44ad02" origin="Generated by Gradle"/> <sha256 value="6a9a2dff91dcf71f85be71ae971f6164b5a631dcd34bff08f0618535ca44ad02" origin="Generated by Gradle"/>
@ -1049,6 +1059,14 @@
<sha256 value="7ef79a4915167d42f785c1b16b60e48af15df3dd017289fa1e10e494e9eee3e3" origin="Generated by Gradle"/> <sha256 value="7ef79a4915167d42f785c1b16b60e48af15df3dd017289fa1e10e494e9eee3e3" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.protobuf" name="protobuf-java" version="3.23.4">
<artifact name="protobuf-java-3.23.4.pom"> <artifact name="protobuf-java-3.23.4.pom">
<sha256 value="6deebe93fe6a35bab8fe926d6e617997a938ab0123175d0bd80edca6421ed20c" origin="Generated by Gradle"/> <sha256 value="6deebe93fe6a35bab8fe926d6e617997a938ab0123175d0bd80edca6421ed20c" origin="Generated by Gradle"/>
@ -1062,6 +1080,14 @@
<sha256 value="c1efac1ae00f0bbab656428e196b63698c914d0dd22be120710beffc481cb547" origin="Generated by Gradle"/> <sha256 value="c1efac1ae00f0bbab656428e196b63698c914d0dd22be120710beffc481cb547" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.protobuf" name="protobuf-java-util" version="3.25.3">
<artifact name="protobuf-java-util-3.25.3.jar"> <artifact name="protobuf-java-util-3.25.3.jar">
<sha256 value="b813c8d6d554cb71c1e82d171d7f80730ae74222a185c863cbedf05072c88155" origin="Generated by Gradle"/> <sha256 value="b813c8d6d554cb71c1e82d171d7f80730ae74222a185c863cbedf05072c88155" origin="Generated by Gradle"/>
@ -1080,6 +1106,11 @@
<sha256 value="1b0229a3bbfef37b025d0d8d7ab369393e7128dabb809023681d1ecccf789020" origin="Generated by Gradle"/> <sha256 value="1b0229a3bbfef37b025d0d8d7ab369393e7128dabb809023681d1ecccf789020" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.protobuf" name="protobuf-parent" version="3.23.4">
<artifact name="protobuf-parent-3.23.4.pom"> <artifact name="protobuf-parent-3.23.4.pom">
<sha256 value="43804e420ca2d5d9a4505707d5d301da0b28c28f946b12cc727378f3c8affb39" origin="Generated by Gradle"/> <sha256 value="43804e420ca2d5d9a4505707d5d301da0b28c28f946b12cc727378f3c8affb39" origin="Generated by Gradle"/>
@ -1090,6 +1121,11 @@
<sha256 value="bc2744608cea3a7a2d4cd0b74e1c3f88138c64ce5aa61b9d7f0afd846882be7a" origin="Generated by Gradle"/> <sha256 value="bc2744608cea3a7a2d4cd0b74e1c3f88138c64ce5aa61b9d7f0afd846882be7a" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </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"> <component group="com.google.re2j" name="re2j" version="1.7">
<artifact name="re2j-1.7.jar"> <artifact name="re2j-1.7.jar">
<sha256 value="4f657af51ab8bb0909bcc3eb40862d26125af8cbcf92aaaba595fed77f947bc0" origin="Generated by Gradle"/> <sha256 value="4f657af51ab8bb0909bcc3eb40862d26125af8cbcf92aaaba595fed77f947bc0" origin="Generated by Gradle"/>

@ -25,6 +25,8 @@ dependencyManagement {
dependency 'com.github.ben-manes.caffeine:caffeine:3.1.8' 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.github.oshi:oshi-core:6.6.3'
dependency 'com.google.auto.service:auto-service:1.1.1' dependency 'com.google.auto.service:auto-service:1.1.1'

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