From 33085dd2331d137bb846d2d16833341b3dec53eb Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 18 Sep 2024 05:35:06 +0200 Subject: [PATCH 1/6] Fix flakiness ofTxPoolOptionsTest::txpoolForcePriceBumpToZeroWhenZeroBaseFeeMarket (#7610) Signed-off-by: Fabio Di Fabio --- .../hyperledger/besu/cli/CommandTestAbstract.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 3dd9641b48..7d11a4a8e9 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -24,7 +24,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,7 +42,6 @@ import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; -import org.hyperledger.besu.components.BesuComponent; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuControllerBuilder; @@ -256,10 +254,8 @@ public abstract class CommandTestAbstract { @BeforeEach public void initMocks() throws Exception { - // doReturn used because of generic BesuController - doReturn(mockControllerBuilder) - .when(mockControllerBuilderFactory) - .fromEthNetworkConfig(any(), any()); + when(mockControllerBuilderFactory.fromEthNetworkConfig(any(), any())) + .thenReturn(mockControllerBuilder); when(mockControllerBuilder.synchronizerConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.ethProtocolConfiguration(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.transactionPoolConfiguration(any())) @@ -288,14 +284,12 @@ public abstract class CommandTestAbstract { when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt())) .thenReturn(mockControllerBuilder); - when(mockControllerBuilder.besuComponent(any(BesuComponent.class))) - .thenReturn(mockControllerBuilder); + when(mockControllerBuilder.besuComponent(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.cacheLastBlocks(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.genesisStateHashCacheEnabled(any())) .thenReturn(mockControllerBuilder); - // doReturn used because of generic BesuController - doReturn(mockController).when(mockControllerBuilder).build(); + when(mockControllerBuilder.build()).thenReturn(mockController); lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager); lenient().when(mockController.getProtocolSchedule()).thenReturn(mockProtocolSchedule); lenient().when(mockController.getProtocolContext()).thenReturn(mockProtocolContext); From 578104e222619eedcf90740b8b1176b10c7ce165 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Wed, 18 Sep 2024 15:20:14 +1000 Subject: [PATCH 2/6] Fix Snap Server Account Range tests (#7552) Signed-off-by: Gabriel-Trintinalia --- .../ethereum/core/BlockchainSetupUtil.java | 21 +- .../ethereum/eth/manager/snap/SnapServer.java | 18 +- .../messages/snap/AccountRangeMessage.java | 25 +- .../messages/snap/GetAccountRangeMessage.java | 12 +- .../snap/SnapServerGetAccountRangeTest.java | 495 ++++++++++++++++++ .../eth/manager/snap/SnapServerTest.java | 2 +- .../snap/AccountRangeMessageTest.java | 83 ++- .../besu/testutil/BlockTestUtil.java | 20 + .../src/main/resources/snap/snapGenesis.json | 111 ++++ .../main/resources/snap/testBlockchain.blocks | Bin 0 -> 342468 bytes 10 files changed, 772 insertions(+), 15 deletions(-) create mode 100644 ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerGetAccountRangeTest.java create mode 100644 testutil/src/main/resources/snap/snapGenesis.json create mode 100644 testutil/src/main/resources/snap/testBlockchain.blocks diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java index 551f593e82..62d670c07a 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java @@ -85,6 +85,13 @@ public class BlockchainSetupUtil { return blockchain; } + public Blockchain importAllBlocks( + final HeaderValidationMode headerValidationMode, + final HeaderValidationMode ommerValidationMode) { + importBlocks(blocks, headerValidationMode, ommerValidationMode); + return blockchain; + } + public void importFirstBlocks(final int count) { importBlocks(blocks.subList(0, count)); } @@ -126,6 +133,10 @@ public class BlockchainSetupUtil { return createForEthashChain(BlockTestUtil.getUpgradedForkResources(), DataStorageFormat.FOREST); } + public static BlockchainSetupUtil forSnapTesting(final DataStorageFormat storageFormat) { + return createForEthashChain(BlockTestUtil.getSnapTestChainResources(), storageFormat); + } + public static BlockchainSetupUtil createForEthashChain( final ChainResources chainResources, final DataStorageFormat storageFormat) { return create( @@ -241,6 +252,13 @@ public class BlockchainSetupUtil { } private void importBlocks(final List blocks) { + importBlocks(blocks, HeaderValidationMode.FULL, HeaderValidationMode.FULL); + } + + private void importBlocks( + final List blocks, + final HeaderValidationMode headerValidationMode, + final HeaderValidationMode ommerValidationMode) { for (final Block block : blocks) { if (block.getHeader().getNumber() == BlockHeader.GENESIS_BLOCK_NUMBER) { continue; @@ -248,7 +266,8 @@ public class BlockchainSetupUtil { final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(block.getHeader()); final BlockImporter blockImporter = protocolSpec.getBlockImporter(); final BlockImportResult result = - blockImporter.importBlock(protocolContext, block, HeaderValidationMode.FULL); + blockImporter.importBlock( + protocolContext, block, headerValidationMode, ommerValidationMode); if (!result.isImported()) { throw new IllegalStateException("Unable to import block " + block.getHeader().getNumber()); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServer.java index c1b855f2d6..7de933e137 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServer.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.eth.sync.snapsync.SnapSyncConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.proof.WorldStateProofProvider; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.CompactEncoding; import org.hyperledger.besu.ethereum.trie.MerkleTrie; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider; @@ -240,12 +241,9 @@ class SnapServer implements BesuEvents.InitialSyncCompletionListener { stopWatch, maxResponseBytes, (pair) -> { - var rlpOutput = new BytesValueRLPOutput(); - rlpOutput.startList(); - rlpOutput.writeBytes(pair.getFirst()); - rlpOutput.writeRLPBytes(pair.getSecond()); - rlpOutput.endList(); - return rlpOutput.encodedSize(); + Bytes bytes = + AccountRangeMessage.toSlimAccount(RLP.input(pair.getSecond())); + return Hash.SIZE + bytes.size(); }); final Bytes32 endKeyBytes = range.endKeyHash(); @@ -257,11 +255,15 @@ class SnapServer implements BesuEvents.InitialSyncCompletionListener { storage.streamFlatAccounts(range.startKeyHash(), shouldContinuePredicate); if (accounts.isEmpty() && shouldContinuePredicate.shouldContinue.get()) { + var fromNextHash = + range.endKeyHash().compareTo(range.startKeyHash()) >= 0 + ? range.endKeyHash() + : range.startKeyHash(); // fetch next account after range, if it exists LOGGER.debug( "found no accounts in range, taking first value starting from {}", - asLogHash(range.endKeyHash())); - accounts = storage.streamFlatAccounts(range.endKeyHash(), UInt256.MAX_VALUE, 1L); + asLogHash(fromNextHash)); + accounts = storage.streamFlatAccounts(fromNextHash, UInt256.MAX_VALUE, 1L); } final var worldStateProof = diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessage.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessage.java index 8e1b1a31a5..71d0429592 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessage.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessage.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.eth.messages.snap; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; @@ -28,6 +29,7 @@ import java.util.NavigableMap; import java.util.Optional; import java.util.TreeMap; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import kotlin.collections.ArrayDeque; import org.apache.tuweni.bytes.Bytes; @@ -117,7 +119,8 @@ public final class AccountRangeMessage extends AbstractSnapMessageData { return ImmutableAccountRangeData.builder().accounts(accounts).proofs(proofs).build(); } - private Bytes toFullAccount(final RLPInput rlpInput) { + @VisibleForTesting + public static Bytes toFullAccount(final RLPInput rlpInput) { final StateTrieAccountValue accountValue = StateTrieAccountValue.readFrom(rlpInput); final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); @@ -131,6 +134,26 @@ public final class AccountRangeMessage extends AbstractSnapMessageData { return rlpOutput.encoded(); } + public static Bytes toSlimAccount(final RLPInput rlpInput) { + StateTrieAccountValue accountValue = StateTrieAccountValue.readFrom(rlpInput); + var rlpOutput = new BytesValueRLPOutput(); + rlpOutput.startList(); + rlpOutput.writeLongScalar(accountValue.getNonce()); + rlpOutput.writeUInt256Scalar(accountValue.getBalance()); + if (accountValue.getStorageRoot().equals(Hash.EMPTY_TRIE_HASH)) { + rlpOutput.writeNull(); + } else { + rlpOutput.writeBytes(accountValue.getStorageRoot()); + } + if (accountValue.getCodeHash().equals(Hash.EMPTY)) { + rlpOutput.writeNull(); + } else { + rlpOutput.writeBytes(accountValue.getCodeHash()); + } + rlpOutput.endList(); + return rlpOutput.encoded(); + } + @Value.Immutable public interface AccountRangeData { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessage.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessage.java index 8f5fcaf9a7..6d8c8c128c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessage.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessage.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.math.BigInteger; import java.util.Optional; +import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.immutables.value.Value; @@ -48,12 +49,21 @@ public final class GetAccountRangeMessage extends AbstractSnapMessageData { public static GetAccountRangeMessage create( final Hash worldStateRootHash, final Bytes32 startKeyHash, final Bytes32 endKeyHash) { + return create(worldStateRootHash, startKeyHash, endKeyHash, SIZE_REQUEST); + } + + @VisibleForTesting + public static GetAccountRangeMessage create( + final Hash worldStateRootHash, + final Bytes32 startKeyHash, + final Bytes32 endKeyHash, + final BigInteger sizeRequest) { final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); tmp.startList(); tmp.writeBytes(worldStateRootHash); tmp.writeBytes(startKeyHash); tmp.writeBytes(endKeyHash); - tmp.writeBigIntegerScalar(SIZE_REQUEST); + tmp.writeBigIntegerScalar(sizeRequest); tmp.endList(); return new GetAccountRangeMessage(tmp.encoded()); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerGetAccountRangeTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerGetAccountRangeTest.java new file mode 100644 index 0000000000..6d8180c8c0 --- /dev/null +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerGetAccountRangeTest.java @@ -0,0 +1,495 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.eth.manager.snap; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; +import org.hyperledger.besu.ethereum.eth.manager.EthMessages; +import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage; +import org.hyperledger.besu.ethereum.eth.messages.snap.GetAccountRangeMessage; +import org.hyperledger.besu.ethereum.eth.sync.snapsync.ImmutableSnapSyncConfiguration; +import org.hyperledger.besu.ethereum.eth.sync.snapsync.SnapSyncConfiguration; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedWorldStateProvider; +import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; +import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; + +import java.math.BigInteger; +import java.util.NavigableMap; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SnapServerGetAccountRangeTest { + private Hash rootHash; + public Bytes32 firstAccount; + public Bytes32 secondAccount; + private SnapServer snapServer; + private static ProtocolContext protocolContext; + + @BeforeAll + public static void setup() { + // Setup local blockchain for testing + BlockchainSetupUtil localBlockchainSetup = + BlockchainSetupUtil.forSnapTesting(DataStorageFormat.BONSAI); + localBlockchainSetup.importAllBlocks( + HeaderValidationMode.LIGHT_DETACHED_ONLY, HeaderValidationMode.LIGHT); + + protocolContext = localBlockchainSetup.getProtocolContext(); + } + + @BeforeEach + public void setupTest() { + WorldStateStorageCoordinator worldStateStorageCoordinator = + new WorldStateStorageCoordinator( + ((DiffBasedWorldStateProvider) protocolContext.getWorldStateArchive()) + .getWorldStateKeyValueStorage()); + + SnapSyncConfiguration snapSyncConfiguration = + ImmutableSnapSyncConfiguration.builder().isSnapServerEnabled(true).build(); + snapServer = + new SnapServer( + snapSyncConfiguration, + new EthMessages(), + worldStateStorageCoordinator, + protocolContext) + .start(); + initAccounts(); + } + + /** + * In this test, we request the entire state range, but limit the response to 4000 bytes. + * Expected: 86 accounts. + */ + @Test + public void test0_RequestEntireStateRangeWith4000BytesLimit() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .responseBytes(4000) + .expectedAccounts(86) + .expectedFirstAccount(firstAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x445cb5c1278fdce2f9cbdb681bdd76c52f8e50e41dbd9e220242a69ba99ac099")) + .build()); + } + + /** + * In this test, we request the entire state range, but limit the response to 3000 bytes. + * Expected: 65 accounts. + */ + @Test + public void test1_RequestEntireStateRangeWith3000BytesLimit() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .responseBytes(3000) + .expectedAccounts(65) + .expectedFirstAccount(firstAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x2e6fe1362b3e388184fd7bf08e99e74170b26361624ffd1c5f646da7067b58b6")) + .build()); + } + + /** + * In this test, we request the entire state range, but limit the response to 2000 bytes. + * Expected: 44 accounts. + */ + @Test + public void test2_RequestEntireStateRangeWith2000BytesLimit() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .responseBytes(2000) + .expectedAccounts(44) + .expectedFirstAccount(firstAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x1c3f74249a4892081ba0634a819aec9ed25f34c7653f5719b9098487e65ab595")) + .build()); + } + + /** + * In this test, we request the entire state range, but limit the response to 1 byte. The server + * should return the first account of the state. Expected: 1 account. + */ + @Test + public void test3_RequestEntireStateRangeWith1ByteLimit() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .responseBytes(1) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + /** + * Here we request with a responseBytes limit of zero. The server should return one account. + * Expected: 1 account. + */ + @Test + public void test4_RequestEntireStateRangeWithZeroBytesLimit() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .responseBytes(0) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + /** + * In this test, we request a range where startingHash is before the first available account key, + * and limitHash is after. The server should return the first and second account of the state + * (because the second account is the 'next available'). Expected: 2 accounts. + */ + @Test + public void test5_RequestRangeBeforeFirstAccountKey() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(hashAdd(firstAccount, -500)) + .limitHash(hashAdd(firstAccount, 1)) + .expectedAccounts(2) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(secondAccount) + .build()); + } + + /** + * Here we request range where both bounds are before the first available account key. This should + * return the first account (even though it's out of bounds). Expected: 1 account. + */ + @Test + public void test6_RequestRangeBothBoundsBeforeFirstAccountKey() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(hashAdd(firstAccount, -500)) + .limitHash(hashAdd(firstAccount, -400)) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + /** + * In this test, both startingHash and limitHash are zero. The server should return the first + * available account. Expected: 1 account. + */ + @Test + public void test7_RequestBothBoundsZero() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(Hash.ZERO) + .limitHash(Hash.ZERO) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + /** + * In this test, startingHash is exactly the first available account key. The server should return + * the first available account of the state as the first item. Expected: 86 accounts. + */ + @Test + public void test8_RequestStartingHashFirstAvailableAccountKey() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(firstAccount) + .responseBytes(4000) + .expectedAccounts(86) + .expectedFirstAccount(firstAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x445cb5c1278fdce2f9cbdb681bdd76c52f8e50e41dbd9e220242a69ba99ac099")) + .build()); + } + + /** + * In this test, startingHash is after the first available key. The server should return the + * second account of the state as the first item. Expected: 86 accounts. + */ + @Test + public void test9_RequestStartingHashAfterFirstAvailableKey() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(secondAccount) + .responseBytes(4000) + .expectedAccounts(86) + .expectedFirstAccount(secondAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x4615e5f5df5b25349a00ad313c6cd0436b6c08ee5826e33a018661997f85ebaa")) + .build()); + } + + /** This test requests a non-existent state root. Expected: 0 accounts. */ + @Test + public void test10_RequestNonExistentStateRoot() { + Hash rootHash = + Hash.fromHexString("1337000000000000000000000000000000000000000000000000000000000000"); + testAccountRangeRequest( + new AccountRangeRequestParams.Builder().rootHash(rootHash).expectedAccounts(0).build()); + } + + /** + * This test requests data at a state root that is 127 blocks old. We expect the server to have + * this state available. Expected: 84 accounts. + */ + @Test + public void test12_RequestStateRoot127BlocksOld() { + + Hash rootHash = + protocolContext + .getBlockchain() + .getBlockHeader((protocolContext.getBlockchain().getChainHeadBlockNumber() - 127)) + .orElseThrow() + .getStateRoot(); + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .expectedAccounts(84) + .responseBytes(4000) + .expectedFirstAccount(firstAccount) + .expectedLastAccount( + Bytes32.fromHexString( + "0x580aa878e2f92d113a12c0a3ce3c21972b03dbe80786858d49a72097e2c491a3")) + .build()); + } + + /** + * This test requests data at a state root that is actually the storage root of an existing + * account. The server is supposed to ignore this request. Expected: 0 accounts. + */ + @Test + public void test13_RequestStateRootIsStorageRoot() { + Hash rootHash = + Hash.fromHexString("df97f94bc47471870606f626fb7a0b42eed2d45fcc84dc1200ce62f7831da990"); + testAccountRangeRequest( + new AccountRangeRequestParams.Builder().rootHash(rootHash).expectedAccounts(0).build()); + } + + /** + * In this test, the startingHash is after limitHash (wrong order). The server should ignore this + * invalid request. Expected: 0 accounts. + */ + @Test + public void test14_RequestStartingHashAfterLimitHash() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(Hash.LAST) + .limitHash(Hash.ZERO) + .expectedAccounts(0) + .build()); + } + + /** + * In this test, the startingHash is the first available key, and limitHash is a key before + * startingHash (wrong order). The server should return the first available key. Expected: 1 + * account. + */ + @Test + public void test15_RequestStartingHashFirstAvailableKeyAndLimitHashBefore() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(firstAccount) + .limitHash(hashAdd(firstAccount, -1)) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + /** + * In this test, the startingHash is the first available key and limitHash is zero. (wrong order). + * The server should return the first available key. Expected: 1 account. + */ + @Test + public void test16_RequestStartingHashFirstAvailableKeyAndLimitHashZero() { + testAccountRangeRequest( + new AccountRangeRequestParams.Builder() + .rootHash(rootHash) + .startHash(firstAccount) + .limitHash(Hash.ZERO) + .expectedAccounts(1) + .expectedFirstAccount(firstAccount) + .expectedLastAccount(firstAccount) + .build()); + } + + private void testAccountRangeRequest(final AccountRangeRequestParams params) { + NavigableMap accounts = getAccountRange(params); + assertThat(accounts.size()).isEqualTo(params.getExpectedAccounts()); + + if (params.getExpectedAccounts() > 0) { + assertThat(accounts.firstKey()).isEqualTo(params.getExpectedFirstAccount()); + assertThat(accounts.lastKey()).isEqualTo(params.getExpectedLastAccount()); + } + } + + private NavigableMap getAccountRange(final AccountRangeRequestParams params) { + Hash rootHash = params.getRootHash(); + Bytes32 startHash = params.getStartHash(); + Bytes32 limitHash = params.getLimitHash(); + BigInteger sizeRequest = BigInteger.valueOf(params.getResponseBytes()); + + GetAccountRangeMessage requestMessage = + GetAccountRangeMessage.create(rootHash, startHash, limitHash, sizeRequest); + + AccountRangeMessage resultMessage = + AccountRangeMessage.readFrom( + snapServer.constructGetAccountRangeResponse( + requestMessage.wrapMessageData(BigInteger.ONE))); + NavigableMap accounts = resultMessage.accountData(false).accounts(); + return accounts; + } + + @SuppressWarnings("UnusedVariable") + private void initAccounts() { + rootHash = protocolContext.getWorldStateArchive().getMutable().rootHash(); + GetAccountRangeMessage requestMessage = + GetAccountRangeMessage.create(rootHash, Hash.ZERO, Hash.LAST, BigInteger.valueOf(4000)); + AccountRangeMessage resultMessage = + AccountRangeMessage.readFrom( + snapServer.constructGetAccountRangeResponse( + requestMessage.wrapMessageData(BigInteger.ONE))); + NavigableMap accounts = resultMessage.accountData(false).accounts(); + firstAccount = accounts.firstEntry().getKey(); + secondAccount = + accounts.entrySet().stream().skip(1).findFirst().orElse(accounts.firstEntry()).getKey(); + } + + private Bytes32 hashAdd(final Bytes32 hash, final int value) { + var result = Hash.wrap(hash).toBigInteger().add(BigInteger.valueOf(value)); + Bytes resultBytes = Bytes.wrap(result.toByteArray()); + return Bytes32.leftPad(resultBytes); + } + + public static class AccountRangeRequestParams { + private final Hash rootHash; + private final Bytes32 startHash; + private final Bytes32 limitHash; + private final int responseBytes; + private final int expectedAccounts; + private final Bytes32 expectedFirstAccount; + private final Bytes32 expectedLastAccount; + + private AccountRangeRequestParams(final Builder builder) { + this.rootHash = builder.rootHash; + this.startHash = builder.startHash; + this.limitHash = builder.limitHash; + this.responseBytes = builder.responseBytes; + this.expectedAccounts = builder.expectedAccounts; + this.expectedFirstAccount = builder.expectedFirstAccount; + this.expectedLastAccount = builder.expectedLastAccount; + } + + public static class Builder { + private Hash rootHash = null; + private Bytes32 startHash = Bytes32.ZERO; + private Bytes32 limitHash = Hash.LAST; + private int responseBytes = Integer.MAX_VALUE; + private int expectedAccounts = 0; + private Bytes32 expectedFirstAccount = null; + private Bytes32 expectedLastAccount = null; + + public Builder rootHash(final Hash rootHashHex) { + this.rootHash = rootHashHex; + return this; + } + + public Builder startHash(final Bytes32 startHashHex) { + this.startHash = startHashHex; + return this; + } + + public Builder limitHash(final Bytes32 limitHashHex) { + this.limitHash = limitHashHex; + return this; + } + + public Builder responseBytes(final int responseBytes) { + this.responseBytes = responseBytes; + return this; + } + + public Builder expectedAccounts(final int expectedAccounts) { + this.expectedAccounts = expectedAccounts; + return this; + } + + public Builder expectedFirstAccount(final Bytes32 expectedFirstAccount) { + this.expectedFirstAccount = expectedFirstAccount; + return this; + } + + public Builder expectedLastAccount(final Bytes32 expectedLastAccount) { + this.expectedLastAccount = expectedLastAccount; + return this; + } + + public AccountRangeRequestParams build() { + return new AccountRangeRequestParams(this); + } + } + + // Getters for each field + public Hash getRootHash() { + return rootHash; + } + + public Bytes32 getStartHash() { + return startHash; + } + + public Bytes32 getLimitHash() { + return limitHash; + } + + public int getResponseBytes() { + return responseBytes; + } + + public int getExpectedAccounts() { + return expectedAccounts; + } + + public Bytes32 getExpectedFirstAccount() { + return expectedFirstAccount; + } + + public Bytes32 getExpectedLastAccount() { + return expectedLastAccount; + } + } +} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java index a41da1ef6b..e168b7e2fe 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServerTest.java @@ -188,7 +188,7 @@ public class SnapServerTest { public void assertAccountLimitRangeResponse() { // assert we limit the range response according to size final int acctCount = 2000; - final long acctRLPSize = 105; + final long acctRLPSize = 37; List randomLoad = IntStream.range(1, 4096).boxed().collect(Collectors.toList()); Collections.shuffle(randomLoad); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessageTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessageTest.java index bd715b6459..c7e5bf74c4 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessageTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessageTest.java @@ -14,11 +14,15 @@ */ package org.hyperledger.besu.ethereum.eth.messages.snap; +import static org.assertj.core.api.Assertions.assertThat; + import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; import java.util.ArrayList; @@ -28,7 +32,6 @@ import java.util.Map; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; public final class AccountRangeMessageTest { @@ -51,7 +54,81 @@ public final class AccountRangeMessageTest { // check match originals. final AccountRangeMessage.AccountRangeData range = message.accountData(false); - Assertions.assertThat(range.accounts()).isEqualTo(keys); - Assertions.assertThat(range.proofs()).isEqualTo(proofs); + assertThat(range.accounts()).isEqualTo(keys); + assertThat(range.proofs()).isEqualTo(proofs); + } + + @Test + public void toSlimAccountTest() { + // Initialize nonce and balance + long nonce = 1L; + Wei balance = Wei.of(2L); + + // Create a StateTrieAccountValue with the given nonce and balance + final StateTrieAccountValue accountValue = + new StateTrieAccountValue(nonce, balance, Hash.EMPTY_TRIE_HASH, Hash.EMPTY); + + // Encode the account value to RLP + final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); + accountValue.writeTo(rlpOut); + + // Convert the encoded account value to a slim account representation + Bytes slimAccount = AccountRangeMessage.toSlimAccount(RLP.input(rlpOut.encoded())); + + // Read the slim account RLP input + RLPInput in = RLP.input(slimAccount); + in.enterList(); + + // Verify the nonce and balance + final long expectedNonce = in.readLongScalar(); + final Wei expectedWei = Wei.of(in.readUInt256Scalar()); + assertThat(expectedNonce).isEqualTo(nonce); + assertThat(expectedWei).isEqualTo(balance); + + // Check that the storageRoot is empty + assertThat(in.nextIsNull()).isTrue(); + in.skipNext(); + + // Check that the codeHash is empty + assertThat(in.nextIsNull()).isTrue(); + in.skipNext(); + + // Exit the list + in.leaveList(); + } + + @Test + public void toFullAccountTest() { + // Initialize nonce and balance + long nonce = 1L; + Wei balance = Wei.of(2L); + + // Create a StateTrieAccountValue with the given nonce and balance + final StateTrieAccountValue accountValue = + new StateTrieAccountValue(nonce, balance, Hash.EMPTY_TRIE_HASH, Hash.EMPTY); + + // Encode the account value to RLP + final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); + accountValue.writeTo(rlpOut); + + // Convert the encoded account value to a full account representation + Bytes fullAccount = AccountRangeMessage.toFullAccount(RLP.input(rlpOut.encoded())); + + // Read the full account RLP input + RLPInput in = RLP.input(fullAccount); + in.enterList(); + + // Verify the nonce and balance + final long expectedNonce = in.readLongScalar(); + final Wei expectedWei = Wei.of(in.readUInt256Scalar()); + assertThat(expectedNonce).isEqualTo(nonce); + assertThat(expectedWei).isEqualTo(balance); + + // Verify the storageRoot and codeHash + assertThat(in.readBytes32()).isEqualTo(Hash.EMPTY_TRIE_HASH); + assertThat(in.readBytes32()).isEqualTo(Hash.EMPTY); + + // Exit the list + in.leaveList(); } } diff --git a/testutil/src/main/java/org/hyperledger/besu/testutil/BlockTestUtil.java b/testutil/src/main/java/org/hyperledger/besu/testutil/BlockTestUtil.java index 9f31283bfc..4c45d3cf74 100644 --- a/testutil/src/main/java/org/hyperledger/besu/testutil/BlockTestUtil.java +++ b/testutil/src/main/java/org/hyperledger/besu/testutil/BlockTestUtil.java @@ -52,6 +52,8 @@ public final class BlockTestUtil { Suppliers.memoize(BlockTestUtil::supplyUpgradedForkResources); private static final Supplier testRpcCompactChainSupplier = Suppliers.memoize(BlockTestUtil::supplyTestRpcCompactResources); + private static final Supplier snapTestChainSupplier = + Suppliers.memoize(BlockTestUtil::supplySnapTestChainResources); /** * Gets test blockchain url. @@ -156,6 +158,15 @@ public final class BlockTestUtil { return testRpcCompactChainSupplier.get(); } + /** + * Gets test chain resources for Snap tests. + * + * @return the test chain resources + */ + public static ChainResources getSnapTestChainResources() { + return snapTestChainSupplier.get(); + } + private static ChainResources supplyTestChainResources() { final URL genesisURL = ensureFileUrl(BlockTestUtil.class.getClassLoader().getResource("testGenesis.json")); @@ -164,6 +175,15 @@ public final class BlockTestUtil { return new ChainResources(genesisURL, blocksURL); } + private static ChainResources supplySnapTestChainResources() { + final URL genesisURL = + ensureFileUrl(BlockTestUtil.class.getClassLoader().getResource("snap/snapGenesis.json")); + final URL blocksURL = + ensureFileUrl( + BlockTestUtil.class.getClassLoader().getResource("snap/testBlockchain.blocks")); + return new ChainResources(genesisURL, blocksURL); + } + private static ChainResources supplyHiveTestChainResources() { final URL genesisURL = ensureFileUrl(BlockTestUtil.class.getClassLoader().getResource("hive/testGenesis.json")); diff --git a/testutil/src/main/resources/snap/snapGenesis.json b/testutil/src/main/resources/snap/snapGenesis.json new file mode 100644 index 0000000000..20979e7f74 --- /dev/null +++ b/testutil/src/main/resources/snap/snapGenesis.json @@ -0,0 +1,111 @@ +{ + "config": { + "ethash": {}, + "chainID": 3503995874084926, + "homesteadBlock": 0, + "eip150Block": 6, + "eip155Block": 12, + "eip158Block": 12, + "byzantiumBlock": 18, + "constantinopleBlock": 24, + "constantinopleFixBlock": 30, + "istanbulBlock": 36, + "muirGlacierBlock": 42, + "berlinBlock": 48, + "londonBlock": 54, + "arrowGlacierBlock": 60, + "grayGlacierBlock": 66, + "mergeNetsplitBlock": 72, + "terminalTotalDifficulty": 9454784, + "shanghaiTime": 780, + "cancunTime": 840 + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x68697665636861696e", + "gasLimit": "0x23f3e20", + "difficulty": "0x20000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "balance": "0x2a" + }, + "0c2c51a0990aee1d73c1228de158688341557508": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "14e46043e63d0e3cdcf2530519f4cfaf35058cb2": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "1f5bde34b4afc686f136c7a3cb6ec376f7357759": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "2d389075be5be9f2246ad654ce152cf05990b209": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "4340ee1b812acb40a1eb561c019c327b243b92df": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "4a0f1452281bcec5bd90c3dce6162a5995bfe9df": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "4dde844b71bcdf95512fb4dc94e84fb67b512ed8": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "5f552da00dfb4d3749d9e62dcee3c918855a86a0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "654aa64f5fbefb84c270ec74211b81ca8c44a72e": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "717f8aa2b982bee0e29f573d31df288663e1ce16": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "83c7e323d189f18725ac510004fdc2941f8c4a78": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "84e75c28348fb86acea1a93a39426d7d60f4cc46": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "8bebc8ba651aee624937e7d897853ac30c95a067": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003" + }, + "balance": "0x1", + "nonce": "0x1" + }, + "c7b99a164efd027a93f147376cc7da7c67c6bbe0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "d803681e487e6ac18053afc5a6cd813c86ec3e4d": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "eda8645ba6948855e3b3cd596bbb07596d59c603": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": null, + "excessBlobGas": null, + "blobGasUsed": null +} \ No newline at end of file diff --git a/testutil/src/main/resources/snap/testBlockchain.blocks b/testutil/src/main/resources/snap/testBlockchain.blocks new file mode 100644 index 0000000000000000000000000000000000000000..0df8d2f63ad54f13682181181b114e78f0e4229f GIT binary patch literal 342468 zcmeF4c|278`~S@#B5TMNW68c{7cr>p+4m*;maK`aV~s+#qEwEulx#^t_R79wNo2_u zBH1FO{LTzh-QCN4xF7dFzpwXw&v|dxIWx`ma-HjW-e+dkpwKnQHvkJm0S>pQO%2tX z`aBGKVprVKP1{9&k}mP``GfOma^K+H2?~jSbpYVddi>?Y{)nt=H`p!Dr4%&qa<~y2 zX2(@er*R97VSm#C^rjdO(Y5GiZibwkh_7CCHdY*_yWx~u4YA~ZGGfs% ze`+iQiiL#;g~~|75Qs>7hYL1V_7)D#0H*gK0&P%Ty#|d~gM0ywiwqY>wszvd40&&= z5y7H|E%?fdoF&L)H5mMLUmMHqQUs$j*6z);gD>LB7uA}K8>KHZ&S)VnN=_I^2YB!N(3PgXcqzEe|P`tmDNZDBm{zsk3f`2nG=a) zBg~1+u_Vl~mdvr_gS5qoNf9MQ=(ZGzsVR0M#r)M= z2q;BjYEX*ie~_Z6Ih4YjS>Mge-No6C*UG}l$=c$)g@=QWjX9Qgka^GSX?|q}K`8eA1hRJ~|hP}MWE771_FCyg{@}y70 zhODtwhi%qRt-NoI;##6`)3d4wegP(8tFboSUCK3m?A<&0cbgJ?!!$KRRi|wQv$Wr} z9Qy*fDkpqG@-qPqkY61UMnH>)M@>_a?p5~vCPl*3m(EOJE}RlV0wFUMadO=TdwM^3_Fpl->MH3l&iWc0+A<= zloggaOv@Z@Yz{Zaf|VptZF4VVYw9va4ARKSb09;JFmk2A+_&yyKKIKz5oJ%--{-E>oOMVs{∨M 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�H&F!^bdyob?nq3cZroaYEZ$T@>N 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

4STe(;*%O0HVBDiN@v}+fy;}fnSs&TAt2I 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

F zI=f(W4TL2%1rW7T$Mic?Cn{C6rc&OYGhozz#sY9p%GaKri5}GzoP^Q}%StJlH$}%A z%yZ%{3L~GCJQ>}Psje298Klx;kv6X6X)tlbcpDi|zJq25x~|{3Q=HNKx^6cpUeDT} zaU!GGHuqCO(GXx&q}8<;v6?4utD1*>&)bH1@KS2pn~qKa%YIKW^- z*01n*3-u_KKMku)>vRxlw;|h(3W62!;4Sb|N87nHgY*yQz_+YKLp8*I$x8Xdvr?u4 z^7KKpn<91fXF+Jf7VF-svq7+|RQQRwcc``cIHBRH9K^&X%1Htt_^#_7r4}!^6`gOo z2mp8RM7yg5A7@8_v~bT~A18XFALDTneg5=@4v^(UR}6bfD$J%-CH(A`Ep=29JSM}H zTsLViSuL2WqR9!V%w^JR7-VN3ky`fE?qVwQk;su)bpiheY`5-ybr#IQW1g8b9Uw`{ zr#pS9EFC&v|5L(G*YW>W1w!pSj9Q3IY5o?onKjcH?ytwpMYEanDX$vRXJLyzC(+b9 zZ%N!h$1%5z4za1bB%HO1-HNT^y^^`DHkdbqLu-~RHxR_aio4k?=N&bmHl>IfY+RoD z+=gNCZjE2yn@;4K=_~nLUHdjQM%mLuFlrq6C~#4`&5!uX>L#y}exje#R3Ao#yb40w z5!sT(L&_rIVLA1>I4tT!JgAx$tKmtb6Mv?N2Tinfo##*?8W@(?Q^HL;=B zAu=RiD=G>SY02Lm;l7-__Z`0{|IwyCc^JxC#D6XRU|~zVQirE(iy?n`B0JiYW*cp= zi#RVAP-!cdU+o=yK^{EJ8D0n`Sd+!OoWibv_GH}U>G`1%>I0kN)XhjY_6y^Sp~>%! zYttDQ&m|AkOe7vH$>~$iEatzt>^MI5@fQ?RzCK z8h8G>v?|lute{4M_EvhzYpP(R6vdMh@+d^s@F*ojFVZc_n=+7w<$1i5$v+5Nbk1vTH|4}{h?862{F*GZ7ylOaE1 zeJ%!=kYf^D`dBv8kSU`hilh3DUo)X$C(W~$TZCeJ9-|TVSAJMlDkO;&U6c=8GZ=Pf zD_g-K<+@4Q6j7j0JELBCnH$v%-w|n@l0GLBovbfSbyHf6CD_Nl`c0BzQ!$Cwmo2q3 zvE;XdftemYB#r7?%qBa3o4J-0#a#8Fkyajl&cg1SepP4HB#yIEzrguR1BBLPc)W$$ zl-HkzbyczFM&q|@91B8@cRAbxCLWf|OAZL(Uc|Hn>r92*F@|TQ0%2Jx!i@U}1dg7p zIZc8Txg0_qtFlEu-YfMutI}qIwGvrUTz@3N$wN4K>`UIemV4F)!>Hvv*)?5&!f=YO kW+?&l+=YeEH-oPpt`9ttw!?7i1`SlV774ugTwebF01?>9A^-pY literal 0 HcmV?d00001 From 0d9fa16a1f5ec11d22e4a03942a563940f7c35f4 Mon Sep 17 00:00:00 2001 From: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> Date: Wed, 18 Sep 2024 05:35:00 -0400 Subject: [PATCH 3/6] [#4292] Fix mounted data path directory permissions for besu user (#7575) * Fix mounted data path directory permissions for besu user Signed-off-by: Bhanu Pulluri * Add besu CLI option to output dirs needing permission update Signed-off-by: Bhanu Pulluri * run spotless apply to handle PR test failure Signed-off-by: Bhanu Pulluri * Remove newly added --print-paths-and-exit option from config file test This option doesn't have a corresponding config file entry as it's a standalone option to be used with docker containers Signed-off-by: Bhanu Pulluri * Add optional user argument to --print-paths-and-exit and fix directory permissions Signed-off-by: Bhanu Pulluri * Correct build.gradle changes, remove a duplicate line and extra whitespaces Signed-off-by: Bhanu Pulluri * Fix checking for user in path's group membership Signed-off-by: Bhanu Pulluri * Add platform check to restrict --print-paths-and-exit option usage to Linux and Mac Signed-off-by: Bhanu Pulluri * Apply suggestions from code review Co-authored-by: Fabio Di Fabio Signed-off-by: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> --------- Signed-off-by: Bhanu Pulluri Signed-off-by: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> Co-authored-by: Bhanu Pulluri Co-authored-by: Fabio Di Fabio --- CHANGELOG.md | 2 + .../org/hyperledger/besu/cli/BesuCommand.java | 137 ++++++++++++++++++ besu/src/main/scripts/besu-entry.sh | 49 +++++++ .../hyperledger/besu/cli/BesuCommandTest.java | 1 + build.gradle | 1 + docker/Dockerfile | 10 +- docker/test.sh | 8 + docker/tests/02/goss.yaml | 10 ++ docker/tests/dgoss | 2 +- 9 files changed, 217 insertions(+), 3 deletions(-) create mode 100755 besu/src/main/scripts/besu-entry.sh create mode 100644 docker/tests/02/goss.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index c241838637..d30ad08c98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,10 @@ - Remove privacy test classes support [#7569](https://github.com/hyperledger/besu/pull/7569) ### Bug fixes +- 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) + ## 24.9.1 ### Upcoming Breaking Changes diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index ecfc0eaadb..01ee6172f6 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -16,6 +16,7 @@ package org.hyperledger.besu.cli; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hyperledger.besu.cli.DefaultCommandValues.getDefaultBesuDataPath; @@ -203,16 +204,23 @@ import org.hyperledger.besu.util.number.Fraction; import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.PositiveNumber; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.math.BigInteger; import java.net.InetAddress; import java.net.SocketException; import java.net.URI; import java.net.URL; import java.net.UnknownHostException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; @@ -232,6 +240,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; @@ -243,6 +252,8 @@ import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; import org.slf4j.Logger; +import oshi.PlatformEnum; +import oshi.SystemInfo; import picocli.AutoComplete; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -382,6 +393,28 @@ public class BesuCommand implements DefaultCommandValues, Runnable { arity = "1") private final Optional identityString = Optional.empty(); + private Boolean printPathsAndExit = Boolean.FALSE; + private String besuUserName = "besu"; + + @Option( + names = "--print-paths-and-exit", + paramLabel = "", + description = "Print the configured paths and exit without starting the node.", + arity = "0..1") + void setUserName(final String userName) { + PlatformEnum currentPlatform = SystemInfo.getCurrentPlatform(); + // Only allow on Linux and macOS + if (currentPlatform == PlatformEnum.LINUX || currentPlatform == PlatformEnum.MACOS) { + if (userName != null) { + besuUserName = userName; + } + printPathsAndExit = Boolean.TRUE; + } else { + throw new UnsupportedOperationException( + "--print-paths-and-exit is only supported on Linux and macOS."); + } + } + // P2P Discovery Option Group @CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n") P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup(); @@ -1093,6 +1126,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable { try { configureLogging(true); + if (printPathsAndExit) { + // Print configured paths requiring read/write permissions to be adjusted + checkPermissionsAndPrintPaths(besuUserName); + System.exit(0); // Exit before any services are started + } + // set merge config on the basis of genesis config setMergeConfigOptions(); @@ -1138,6 +1177,104 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } } + private void checkPermissionsAndPrintPaths(final String userName) { + // Check permissions for the data path + checkPermissions(dataDir(), userName, false); + + // Check permissions for genesis file + try { + if (genesisFile != null) { + checkPermissions(genesisFile.toPath(), userName, true); + } + } catch (Exception e) { + commandLine + .getOut() + .println("Error: Failed checking genesis file: Reason: " + e.getMessage()); + } + } + + // Helper method to check permissions on a given path + private void checkPermissions(final Path path, final String besuUser, final boolean readOnly) { + try { + // Get the permissions of the file + // check if besu user is the owner - get owner permissions if yes + // else, check if besu user and owner are in the same group - if yes, check the group + // permission + // otherwise check permissions for others + + // Get the owner of the file or directory + UserPrincipal owner = Files.getOwner(path); + boolean hasReadPermission, hasWritePermission; + + // Get file permissions + Set permissions = Files.getPosixFilePermissions(path); + + // Check if besu is the owner + if (owner.getName().equals(besuUser)) { + // Owner permissions + hasReadPermission = permissions.contains(PosixFilePermission.OWNER_READ); + hasWritePermission = permissions.contains(PosixFilePermission.OWNER_WRITE); + } else { + // Get the group of the file + // Get POSIX file attributes and then group + PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class); + GroupPrincipal group = attrs.group(); + + // Check if besu user belongs to this group + boolean isMember = isGroupMember(besuUserName, group); + + if (isMember) { + // Group's permissions + hasReadPermission = permissions.contains(PosixFilePermission.GROUP_READ); + hasWritePermission = permissions.contains(PosixFilePermission.GROUP_WRITE); + } else { + // Others' permissions + hasReadPermission = permissions.contains(PosixFilePermission.OTHERS_READ); + hasWritePermission = permissions.contains(PosixFilePermission.OTHERS_WRITE); + } + } + + if (!hasReadPermission || (!readOnly && !hasWritePermission)) { + String accessType = readOnly ? "READ" : "READ_WRITE"; + commandLine.getOut().println("PERMISSION_CHECK_PATH:" + path + ":" + accessType); + } + } catch (Exception e) { + // Do nothing upon catching an error + commandLine + .getOut() + .println( + "Error: Failed to check permissions for path: '" + + path + + "'. Reason: " + + e.getMessage()); + } + } + + private static boolean isGroupMember(final String userName, final GroupPrincipal group) + throws IOException { + // Get the groups of the user by executing 'id -Gn username' + Process process = Runtime.getRuntime().exec(new String[] {"id", "-Gn", userName}); + BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8)); + + // Read the output of the command + String line = reader.readLine(); + boolean isMember = false; + if (line != null) { + // Split the groups + Iterable userGroups = Splitter.on(" ").split(line); + // Check if any of the user's groups match the file's group + + for (String grp : userGroups) { + if (grp.equals(group.getName())) { + isMember = true; + break; + } + } + } + return isMember; + } + @VisibleForTesting void setBesuConfiguration(final BesuConfigurationImpl pluginCommonConfiguration) { this.pluginCommonConfiguration = pluginCommonConfiguration; diff --git a/besu/src/main/scripts/besu-entry.sh b/besu/src/main/scripts/besu-entry.sh new file mode 100755 index 0000000000..ed3687b229 --- /dev/null +++ b/besu/src/main/scripts/besu-entry.sh @@ -0,0 +1,49 @@ +#!/bin/bash +## +## Copyright contributors to Hyperledger Besu. +## +## Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +## an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +## specific language governing permissions and limitations under the License. +## +## SPDX-License-Identifier: Apache-2.0 +## + +# Run Besu first to get paths needing permission adjustment +output=$(/opt/besu/bin/besu --print-paths-and-exit $BESU_USER_NAME "$@") + +# Parse the output to find the paths and their required access types +echo "$output" | while IFS=: read -r prefix path accessType; do + if [[ "$prefix" == "PERMISSION_CHECK_PATH" ]]; then + # Change ownership to besu user and group + chown -R $BESU_USER_NAME:$BESU_USER_NAME $path + + # Ensure read/write permissions for besu user + + echo "Setting permissions for: $path with access: $accessType" + + if [[ "$accessType" == "READ" ]]; then + # Set read-only permissions for besu user + # Add execute for directories to allow access + find $path -type d -exec chmod u+rx {} \; + find $path -type f -exec chmod u+r {} \; + elif [[ "$accessType" == "READ_WRITE" ]]; then + # Set read/write permissions for besu user + # Add execute for directories to allow access + find $path -type d -exec chmod u+rwx {} \; + find $path -type f -exec chmod u+rw {} \; + fi + fi +done + +# Finally, run Besu with the actual arguments passed to the container +# Construct the command as a single string +COMMAND="/opt/besu/bin/besu $@" + +# Switch to the besu user and execute the command +exec su -s /bin/bash $BESU_USER_NAME -c "$COMMAND" diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index e6e0e85951..99b45ada1b 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -390,6 +390,7 @@ public class BesuCommandTest extends CommandTestAbstract { options.remove(spec.optionsMap().get("--config-file")); options.remove(spec.optionsMap().get("--help")); options.remove(spec.optionsMap().get("--version")); + options.remove(spec.optionsMap().get("--print-paths-and-exit")); for (final String tomlKey : tomlResult.keySet()) { final CommandLine.Model.OptionSpec optionSpec = spec.optionsMap().get("--" + tomlKey); diff --git a/build.gradle b/build.gradle index 792355417f..ee4392c9fc 100644 --- a/build.gradle +++ b/build.gradle @@ -1097,6 +1097,7 @@ distributions { from("build/reports/license/license-dependency.html") { into "." } from("./docs/GettingStartedBinaries.md") { into "." } from("./docs/DocsArchive0.8.0.html") { into "." } + from("./besu/src/main/scripts/besu-entry.sh") { into "./bin/" } from(autocomplete) { into "." } } } diff --git a/docker/Dockerfile b/docker/Dockerfile index c16345a82b..a45a3ac73d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,7 +18,8 @@ RUN apt-get update $NO_PROXY_CACHE && \ chown besu:besu /opt/besu && \ chmod 0755 /opt/besu -USER besu +ARG BESU_USER=besu +USER ${BESU_USER} WORKDIR /opt/besu COPY --chown=besu:besu besu /opt/besu/ @@ -43,7 +44,12 @@ ENV OTEL_RESOURCE_ATTRIBUTES="service.name=besu,service.version=$VERSION" ENV OLDPATH="${PATH}" ENV PATH="/opt/besu/bin:${OLDPATH}" -ENTRYPOINT ["besu"] +USER root +RUN chmod +x /opt/besu/bin/besu-entry.sh + +ENV BESU_USER_NAME=${BESU_USER} + +ENTRYPOINT ["besu-entry.sh"] HEALTHCHECK --start-period=5s --interval=5s --timeout=1s --retries=10 CMD bash -c "[ -f /tmp/pid ]" # Build-time metadata as defined at http://label-schema.org diff --git a/docker/test.sh b/docker/test.sh index 6e08db13b5..e230f79ad1 100755 --- a/docker/test.sh +++ b/docker/test.sh @@ -41,4 +41,12 @@ bash $TEST_PATH/dgoss run --sysctl net.ipv6.conf.all.disable_ipv6=1 $DOCKER_IMAG --graphql-http-enabled \ > ./reports/01.xml || i=`expr $i + 1` +if [[ $i != 0 ]]; then exit $i; fi + +# Test for directory permissions +GOSS_FILES_PATH=$TEST_PATH/02 \ +bash $TEST_PATH/dgoss run --sysctl net.ipv6.conf.all.disable_ipv6=1 -v besu-data:/var/lib/besu $DOCKER_IMAGE --data-path=/var/lib/besu \ +--network=dev \ +> ./reports/02.xml || i=`expr $i + 1` + exit $i diff --git a/docker/tests/02/goss.yaml b/docker/tests/02/goss.yaml new file mode 100644 index 0000000000..d266cafa39 --- /dev/null +++ b/docker/tests/02/goss.yaml @@ -0,0 +1,10 @@ +--- +# runtime docker tests +file: + /var/lib/besu: + exists: true + owner: besu + mode: "0755" +process: + java: + running: true diff --git a/docker/tests/dgoss b/docker/tests/dgoss index 59bbc4683e..170270eff3 100755 --- a/docker/tests/dgoss +++ b/docker/tests/dgoss @@ -76,7 +76,7 @@ GOSS_PATH="${GOSS_PATH:-$(which goss 2> /dev/null || true)}" [[ $GOSS_PATH ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; } [[ ${GOSS_OPTS+x} ]] || GOSS_OPTS="--color --format documentation" [[ ${GOSS_WAIT_OPTS+x} ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null" -GOSS_SLEEP=${GOSS_SLEEP:-0.2} +GOSS_SLEEP=${GOSS_SLEEP:-1.0} case "$1" in run) From 6df4149f2a04555fb36702911b8f6142b843c5b3 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 18 Sep 2024 15:55:10 +0200 Subject: [PATCH 4/6] Fix logging the evaluation time when a tx is selected for block creation (#7636) Signed-off-by: Fabio Di Fabio --- .../blockcreation/txselection/BlockTransactionSelector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index 3d56c1fc07..e07b43b990 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -401,7 +401,7 @@ public class BlockTransactionSelector { LOG.atTrace() .setMessage("Selected {} for block creation, evaluated in {}") .addArgument(transaction::toTraceLog) - .addArgument(evaluationContext.getPendingTransaction()) + .addArgument(evaluationContext.getEvaluationTimer()) .log(); return SELECTED; } From 7d3e376771f99598e3730a0569793c7c1bd58834 Mon Sep 17 00:00:00 2001 From: Justin Florentine Date: Wed, 18 Sep 2024 10:31:03 -0400 Subject: [PATCH 5/6] shift creation of plugin context to BesuCommand for now (#7625) * shift creation of plugin context to BesuCommand for now * mock component will provide a no-op metrics sys --------- Signed-off-by: Justin Florentine --- CHANGELOG.md | 1 + .../dsl/node/ProcessBesuNodeRunner.java | 125 +++++++++--------- .../dsl/node/ThreadBesuNodeRunner.java | 23 ++-- .../main/java/org/hyperledger/besu/Besu.java | 5 +- .../org/hyperledger/besu/cli/BesuCommand.java | 38 ++++-- .../besu/components/BesuCommandModule.java | 12 +- .../besu/components/BesuComponent.java | 4 +- .../components/BesuPluginContextModule.java | 16 --- .../controller/BesuControllerBuilder.java | 2 +- .../besu/cli/CommandTestAbstract.java | 7 + .../components/MockBesuCommandModule.java | 16 +++ .../BonsaiCachedMerkleTrieLoaderModule.java | 5 +- .../besu/metrics/MetricsSystemModule.java | 6 - .../opentelemetry/OpenTelemetrySystem.java | 2 + 14 files changed, 148 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d30ad08c98..ff4d7cfbf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Bug fixes - Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575) - Fix for `debug_traceCall` to handle transactions without specified gas price. [#7510](https://github.com/hyperledger/besu/pull/7510) +- Corrects a regression where custom plugin services are not initialized correctly. [#7625](https://github.com/hyperledger/besu/pull/7625) ## 24.9.1 diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index c6696b15a2..6e00701ef2 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -75,6 +75,70 @@ public class ProcessBesuNodeRunner implements BesuNodeRunner { final Path dataDir = node.homeDirectory(); + final List params = commandlineArgs(node, dataDir); + + LOG.info("Creating besu process with params {}", params); + final ProcessBuilder processBuilder = + new ProcessBuilder(params) + .directory(new File(System.getProperty("user.dir")).getParentFile().getParentFile()) + .redirectErrorStream(true) + .redirectInput(Redirect.INHERIT); + if (!node.getPlugins().isEmpty()) { + processBuilder + .environment() + .put( + "BESU_OPTS", + "-Dbesu.plugins.dir=" + dataDir.resolve("plugins").toAbsolutePath().toString()); + } + // Use non-blocking randomness for acceptance tests + processBuilder + .environment() + .put( + "JAVA_OPTS", + "-Djava.security.properties=" + + "acceptance-tests/tests/build/resources/test/acceptanceTesting.security"); + // add additional environment variables + processBuilder.environment().putAll(node.getEnvironment()); + + try { + int debugPort = Integer.parseInt(System.getenv("BESU_DEBUG_CHILD_PROCESS_PORT")); + LOG.warn("Waiting for debugger to attach to SUSPENDED child process"); + String debugOpts = + " -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:" + debugPort; + String prevJavaOpts = processBuilder.environment().get("JAVA_OPTS"); + if (prevJavaOpts == null) { + processBuilder.environment().put("JAVA_OPTS", debugOpts); + } else { + processBuilder.environment().put("JAVA_OPTS", prevJavaOpts + debugOpts); + } + + } catch (NumberFormatException e) { + LOG.debug( + "Child process may be attached to by exporting BESU_DEBUG_CHILD_PROCESS_PORT= to env"); + } + + try { + checkState( + isNotAliveOrphan(node.getName()), + "A live process with name: %s, already exists. Cannot create another with the same name as it would orphan the first", + node.getName()); + + final Process process = processBuilder.start(); + process.onExit().thenRun(() -> node.setExitCode(process.exitValue())); + outputProcessorExecutor.execute(() -> printOutput(node, process)); + besuProcesses.put(node.getName(), process); + } catch (final IOException e) { + LOG.error("Error starting BesuNode process", e); + } + + if (node.getRunCommand().isEmpty()) { + waitForFile(dataDir, "besu.ports"); + waitForFile(dataDir, "besu.networks"); + } + MDC.remove("node"); + } + + private List commandlineArgs(final BesuNode node, final Path dataDir) { final List params = new ArrayList<>(); params.add("build/install/besu/bin/besu"); @@ -388,66 +452,7 @@ public class ProcessBesuNodeRunner implements BesuNodeRunner { } params.addAll(node.getRunCommand()); - - LOG.info("Creating besu process with params {}", params); - final ProcessBuilder processBuilder = - new ProcessBuilder(params) - .directory(new File(System.getProperty("user.dir")).getParentFile().getParentFile()) - .redirectErrorStream(true) - .redirectInput(Redirect.INHERIT); - if (!node.getPlugins().isEmpty()) { - processBuilder - .environment() - .put( - "BESU_OPTS", - "-Dbesu.plugins.dir=" + dataDir.resolve("plugins").toAbsolutePath().toString()); - } - // Use non-blocking randomness for acceptance tests - processBuilder - .environment() - .put( - "JAVA_OPTS", - "-Djava.security.properties=" - + "acceptance-tests/tests/build/resources/test/acceptanceTesting.security"); - // add additional environment variables - processBuilder.environment().putAll(node.getEnvironment()); - - try { - int debugPort = Integer.parseInt(System.getenv("BESU_DEBUG_CHILD_PROCESS_PORT")); - LOG.warn("Waiting for debugger to attach to SUSPENDED child process"); - String debugOpts = - " -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:" + debugPort; - String prevJavaOpts = processBuilder.environment().get("JAVA_OPTS"); - if (prevJavaOpts == null) { - processBuilder.environment().put("JAVA_OPTS", debugOpts); - } else { - processBuilder.environment().put("JAVA_OPTS", prevJavaOpts + debugOpts); - } - - } catch (NumberFormatException e) { - LOG.debug( - "Child process may be attached to by exporting BESU_DEBUG_CHILD_PROCESS_PORT= to env"); - } - - try { - checkState( - isNotAliveOrphan(node.getName()), - "A live process with name: %s, already exists. Cannot create another with the same name as it would orphan the first", - node.getName()); - - final Process process = processBuilder.start(); - process.onExit().thenRun(() -> node.setExitCode(process.exitValue())); - outputProcessorExecutor.execute(() -> printOutput(node, process)); - besuProcesses.put(node.getName(), process); - } catch (final IOException e) { - LOG.error("Error starting BesuNode process", e); - } - - if (node.getRunCommand().isEmpty()) { - waitForFile(dataDir, "besu.ports"); - waitForFile(dataDir, "besu.networks"); - } - MDC.remove("node"); + return params; } private boolean isNotAliveOrphan(final String name) { diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index d4ec54045d..5d78f1460c 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -138,7 +138,8 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { final PermissioningServiceImpl permissioningService = new PermissioningServiceImpl(); GlobalOpenTelemetry.resetForTest(); - final ObservableMetricsSystem metricsSystem = component.getObservableMetricsSystem(); + final ObservableMetricsSystem metricsSystem = + (ObservableMetricsSystem) component.getMetricsSystem(); final List bootnodes = node.getConfiguration().getBootnodes().stream().map(EnodeURLImpl::fromURI).toList(); @@ -280,6 +281,16 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { this.toProvide = toProvide; } + @Provides + @Singleton + MetricsConfiguration provideMetricsConfiguration() { + if (toProvide.getMetricsConfiguration() != null) { + return toProvide.getMetricsConfiguration(); + } else { + return MetricsConfiguration.builder().build(); + } + } + @Provides public BesuNode provideBesuNodeRunner() { return toProvide; @@ -410,13 +421,13 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { public BesuController provideBesuController( final SynchronizerConfiguration synchronizerConfiguration, final BesuControllerBuilder builder, - final ObservableMetricsSystem metricsSystem, + final MetricsSystem metricsSystem, final KeyValueStorageProvider storageProvider, final MiningParameters miningParameters) { builder .synchronizerConfiguration(synchronizerConfiguration) - .metricsSystem(metricsSystem) + .metricsSystem((ObservableMetricsSystem) metricsSystem) .dataStorageConfiguration(DataStorageConfiguration.DEFAULT_FOREST_CONFIG) .ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) .clock(Clock.systemUTC()) @@ -562,12 +573,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { return besuCommand; } - @Provides - @Singleton - MetricsConfiguration provideMetricsConfiguration() { - return MetricsConfiguration.builder().build(); - } - @Provides @Named("besuCommandLogger") @Singleton diff --git a/besu/src/main/java/org/hyperledger/besu/Besu.java b/besu/src/main/java/org/hyperledger/besu/Besu.java index 8d40a10eb4..3ba0af0a9b 100644 --- a/besu/src/main/java/org/hyperledger/besu/Besu.java +++ b/besu/src/main/java/org/hyperledger/besu/Besu.java @@ -16,6 +16,7 @@ package org.hyperledger.besu; import org.hyperledger.besu.cli.BesuCommand; import org.hyperledger.besu.cli.logging.BesuLoggingConfigurationFactory; +import org.hyperledger.besu.components.BesuComponent; import org.hyperledger.besu.components.DaggerBesuComponent; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -36,13 +37,15 @@ public final class Besu { */ public static void main(final String... args) { setupLogging(); - final BesuCommand besuCommand = DaggerBesuComponent.create().getBesuCommand(); + final BesuComponent besuComponent = DaggerBesuComponent.create(); + final BesuCommand besuCommand = besuComponent.getBesuCommand(); int exitCode = besuCommand.parse( new RunLast(), besuCommand.parameterExceptionHandler(), besuCommand.executionExceptionHandler(), System.in, + besuComponent, args); System.exit(exitCode); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 01ee6172f6..79879538bd 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -85,6 +85,7 @@ import org.hyperledger.besu.cli.util.BesuCommandCustomFactory; import org.hyperledger.besu.cli.util.CommandLineUtils; import org.hyperledger.besu.cli.util.ConfigDefaultValueProviderStrategy; import org.hyperledger.besu.cli.util.VersionProvider; +import org.hyperledger.besu.components.BesuComponent; import org.hyperledger.besu.config.CheckpointConfigOptions; import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigOptions; @@ -145,7 +146,6 @@ import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.metrics.MetricCategoryRegistryImpl; import org.hyperledger.besu.metrics.MetricsProtocol; -import org.hyperledger.besu.metrics.MetricsSystemFactory; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.metrics.StandardMetricCategory; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; @@ -423,6 +423,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl; private final TransactionSimulationServiceImpl transactionSimulationServiceImpl; private final BlockchainServiceImpl blockchainServiceImpl; + private BesuComponent besuComponent; static class P2PDiscoveryOptionGroup { @@ -897,9 +898,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private BesuController besuController; private BesuConfigurationImpl pluginCommonConfiguration; - private final Supplier metricsSystem = - Suppliers.memoize(() -> MetricsSystemFactory.create(metricsConfiguration())); - private Vertx vertx; private EnodeDnsConfiguration enodeDnsConfiguration; private KeyValueStorageProvider keyValueStorageProvider; @@ -1029,6 +1027,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { * @param parameterExceptionHandler Handler for exceptions related to command line parameters. * @param executionExceptionHandler Handler for exceptions during command execution. * @param in The input stream for commands. + * @param besuComponent The Besu component. * @param args The command line arguments. * @return The execution result status code. */ @@ -1037,8 +1036,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable { final BesuParameterExceptionHandler parameterExceptionHandler, final BesuExecutionExceptionHandler executionExceptionHandler, final InputStream in, + final BesuComponent besuComponent, final String... args) { - + if (besuComponent == null) { + throw new IllegalArgumentException("BesuComponent must be provided"); + } + this.besuComponent = besuComponent; initializeCommandLineSettings(in); // Create the execution strategy chain. @@ -1142,7 +1145,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { logger.info("Starting Besu"); // Need to create vertx after cmdline has been parsed, such that metricsSystem is configurable - vertx = createVertx(createVertxOptions(metricsSystem.get())); + vertx = createVertx(createVertxOptions(besuComponent.getMetricsSystem())); validateOptions(); @@ -1527,8 +1530,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } private void setReleaseMetrics() { - metricsSystem - .get() + besuComponent + .getMetricsSystem() .createLabelledGauge( StandardMetricCategory.PROCESS, "release", "Release information", "version") .labels(() -> 1, BesuInfo.version()); @@ -1992,7 +1995,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .miningParameters(miningParametersSupplier.get()) .transactionPoolConfiguration(buildTransactionPoolConfiguration()) .nodeKey(new NodeKey(securityModule())) - .metricsSystem(metricsSystem.get()) + .metricsSystem((ObservableMetricsSystem) besuComponent.getMetricsSystem()) .messagePermissioningProviders(permissioningService.getMessagePermissioningProviders()) .privacyParameters(privacyParameters()) .clock(Clock.systemUTC()) @@ -2012,7 +2015,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority) .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()) .cacheLastBlocks(numberOfblocksToCache) - .genesisStateHashCacheEnabled(genesisStateHashCacheEnabled); + .genesisStateHashCacheEnabled(genesisStateHashCacheEnabled) + .besuComponent(besuComponent); } private JsonRpcConfiguration createEngineJsonRpcConfiguration( @@ -2414,7 +2418,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable { p2pTLSConfiguration.ifPresent(runnerBuilder::p2pTLSConfiguration); - final ObservableMetricsSystem metricsSystem = this.metricsSystem.get(); final Runner runner = runnerBuilder .vertx(vertx) @@ -2441,7 +2444,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .pidPath(pidPath) .dataDir(dataDir()) .bannedNodeIds(p2PDiscoveryOptionGroup.bannedNodeIds) - .metricsSystem(metricsSystem) + .metricsSystem((ObservableMetricsSystem) besuComponent.getMetricsSystem()) .permissioningService(permissioningService) .metricsConfiguration(metricsConfiguration) .staticNodes(staticNodes) @@ -2608,7 +2611,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { * @return Instance of MetricsSystem */ public MetricsSystem getMetricsSystem() { - return metricsSystem.get(); + return besuComponent.getMetricsSystem(); } private Set loadStaticNodes() throws IOException { @@ -2946,4 +2949,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable { return builder.build(); } + + /** + * Returns the plugin context. + * + * @return the plugin context. + */ + public BesuPluginContextImpl getBesuPluginContext() { + return besuPluginContext; + } } diff --git a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java index 86aa8c7594..59e2d60bf3 100644 --- a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java +++ b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java @@ -42,9 +42,7 @@ public class BesuCommandModule { @Provides @Singleton - BesuCommand provideBesuCommand( - final BesuPluginContextImpl pluginContext, - final @Named("besuCommandLogger") Logger commandLogger) { + BesuCommand provideBesuCommand(final @Named("besuCommandLogger") Logger commandLogger) { final BesuCommand besuCommand = new BesuCommand( RlpBlockImporter::new, @@ -52,7 +50,7 @@ public class BesuCommandModule { RlpBlockExporter::new, new RunnerBuilder(), new BesuController.Builder(), - pluginContext, + new BesuPluginContextImpl(), System.getenv(), commandLogger); besuCommand.toCommandLine(); @@ -71,4 +69,10 @@ public class BesuCommandModule { Logger provideBesuCommandLogger() { return Besu.getFirstLogger(); } + + @Provides + @Singleton + BesuPluginContextImpl provideBesuPluginContextImpl(final BesuCommand provideFrom) { + return provideFrom.getBesuPluginContext(); + } } diff --git a/besu/src/main/java/org/hyperledger/besu/components/BesuComponent.java b/besu/src/main/java/org/hyperledger/besu/components/BesuComponent.java index 9f810a6dc6..b0d5c3da0f 100644 --- a/besu/src/main/java/org/hyperledger/besu/components/BesuComponent.java +++ b/besu/src/main/java/org/hyperledger/besu/components/BesuComponent.java @@ -20,7 +20,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.BlobCacheModule; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedMerkleTrieLoader; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedMerkleTrieLoaderModule; import org.hyperledger.besu.metrics.MetricsSystemModule; -import org.hyperledger.besu.metrics.ObservableMetricsSystem; +import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.services.BesuPluginContextImpl; import javax.inject.Named; @@ -60,7 +60,7 @@ public interface BesuComponent { * * @return ObservableMetricsSystem */ - ObservableMetricsSystem getObservableMetricsSystem(); + MetricsSystem getMetricsSystem(); /** * a Logger specifically configured to provide configuration feedback to users. diff --git a/besu/src/main/java/org/hyperledger/besu/components/BesuPluginContextModule.java b/besu/src/main/java/org/hyperledger/besu/components/BesuPluginContextModule.java index d62ab70224..702b63af19 100644 --- a/besu/src/main/java/org/hyperledger/besu/components/BesuPluginContextModule.java +++ b/besu/src/main/java/org/hyperledger/besu/components/BesuPluginContextModule.java @@ -14,9 +14,7 @@ */ package org.hyperledger.besu.components; -import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.services.BesuConfigurationImpl; -import org.hyperledger.besu.services.BesuPluginContextImpl; import javax.inject.Singleton; @@ -35,18 +33,4 @@ public class BesuPluginContextModule { BesuConfigurationImpl provideBesuPluginConfig() { return new BesuConfigurationImpl(); } - - /** - * Creates a BesuPluginContextImpl, used for plugin service discovery. - * - * @param pluginConfig the BesuConfigurationImpl - * @return the BesuPluginContext - */ - @Provides - @Singleton - public BesuPluginContextImpl provideBesuPluginContext(final BesuConfigurationImpl pluginConfig) { - BesuPluginContextImpl retval = new BesuPluginContextImpl(); - retval.addService(BesuConfiguration.class, pluginConfig); - return retval; - } } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 6bb3fb117c..bded2a38ac 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -552,7 +552,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides checkNotNull(evmConfiguration, "Missing evm config"); checkNotNull(networkingConfiguration, "Missing network configuration"); checkNotNull(dataStorageConfiguration, "Missing data storage configuration"); - + checkNotNull(besuComponent, "Must supply a BesuComponent"); prepForBuild(); final ProtocolSchedule protocolSchedule = createProtocolSchedule(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 7d11a4a8e9..5b1274389d 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -42,6 +42,7 @@ import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; +import org.hyperledger.besu.components.BesuComponent; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuControllerBuilder; @@ -69,6 +70,7 @@ import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.StorageService; @@ -204,6 +206,9 @@ public abstract class CommandTestAbstract { @Mock(lenient = true) protected BesuController mockController; + @Mock(lenient = true) + protected BesuComponent mockBesuComponent; + @Mock protected RlpBlockExporter rlpBlockExporter; @Mock protected JsonBlockImporter jsonBlockImporter; @Mock protected RlpBlockImporter rlpBlockImporter; @@ -344,6 +349,7 @@ public abstract class CommandTestAbstract { when(mockRunnerBuilder.allowedSubnets(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.poaDiscoveryRetryBootnodes(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); + when(mockBesuComponent.getMetricsSystem()).thenReturn(new NoOpMetricsSystem()); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); @@ -451,6 +457,7 @@ public abstract class CommandTestAbstract { besuCommand.parameterExceptionHandler(), besuCommand.executionExceptionHandler(), in, + mockBesuComponent, args); return besuCommand; } diff --git a/besu/src/test/java/org/hyperledger/besu/components/MockBesuCommandModule.java b/besu/src/test/java/org/hyperledger/besu/components/MockBesuCommandModule.java index 743b4ee8de..3695fe54f7 100644 --- a/besu/src/test/java/org/hyperledger/besu/components/MockBesuCommandModule.java +++ b/besu/src/test/java/org/hyperledger/besu/components/MockBesuCommandModule.java @@ -18,6 +18,9 @@ import static org.mockito.Mockito.mock; import org.hyperledger.besu.cli.BesuCommand; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.services.BesuConfigurationImpl; +import org.hyperledger.besu.services.BesuPluginContextImpl; import javax.inject.Named; import javax.inject.Singleton; @@ -47,4 +50,17 @@ public class MockBesuCommandModule { Logger provideBesuCommandLogger() { return LoggerFactory.getLogger(MockBesuCommandModule.class); } + + /** + * Creates a BesuPluginContextImpl, used for plugin service discovery. + * + * @return the BesuPluginContext + */ + @Provides + @Singleton + public BesuPluginContextImpl provideBesuPluginContext() { + BesuPluginContextImpl retval = new BesuPluginContextImpl(); + retval.addService(BesuConfiguration.class, new BesuConfigurationImpl()); + return retval; + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/BonsaiCachedMerkleTrieLoaderModule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/BonsaiCachedMerkleTrieLoaderModule.java index 8ed7daa35f..b506d5a5ff 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/BonsaiCachedMerkleTrieLoaderModule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/BonsaiCachedMerkleTrieLoaderModule.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache; import org.hyperledger.besu.metrics.ObservableMetricsSystem; +import org.hyperledger.besu.plugin.services.MetricsSystem; import dagger.Module; import dagger.Provides; @@ -24,7 +25,7 @@ public class BonsaiCachedMerkleTrieLoaderModule { @Provides BonsaiCachedMerkleTrieLoader provideCachedMerkleTrieLoaderModule( - final ObservableMetricsSystem metricsSystem) { - return new BonsaiCachedMerkleTrieLoader(metricsSystem); + final MetricsSystem metricsSystem) { + return new BonsaiCachedMerkleTrieLoader((ObservableMetricsSystem) metricsSystem); } } diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsSystemModule.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsSystemModule.java index 044085ef42..1347d6faaa 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsSystemModule.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsSystemModule.java @@ -36,10 +36,4 @@ public class MetricsSystemModule { MetricsSystem provideMetricsSystem(final MetricsConfiguration metricsConfig) { return MetricsSystemFactory.create(metricsConfig); } - - @Provides - @Singleton - ObservableMetricsSystem provideObservableMetricsSystem(final MetricsConfiguration metricsConfig) { - return MetricsSystemFactory.create(metricsConfig); - } } diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java index ca1dc5dd3a..a399b28373 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java @@ -41,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.DoubleSupplier; import java.util.stream.Stream; +import javax.inject.Singleton; import com.google.common.collect.ImmutableSet; import io.opentelemetry.api.common.AttributeKey; @@ -67,6 +68,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Metrics system relying on the native OpenTelemetry format. */ +@Singleton public class OpenTelemetrySystem implements ObservableMetricsSystem { private static final Logger LOG = LoggerFactory.getLogger(OpenTelemetrySystem.class); From 96e9873dd9624200f3a78cc6759a70b159c01d80 Mon Sep 17 00:00:00 2001 From: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:53:11 -0400 Subject: [PATCH 6/6] Handle hadolint check failure with proper risk assessment (#7637) Signed-off-by: Bhanu Pulluri Co-authored-by: Bhanu Pulluri Co-authored-by: Fabio Di Fabio --- docker/Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index a45a3ac73d..fe91c7026f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -44,6 +44,12 @@ ENV OTEL_RESOURCE_ATTRIBUTES="service.name=besu,service.version=$VERSION" ENV OLDPATH="${PATH}" ENV PATH="/opt/besu/bin:${OLDPATH}" + +# The entry script just sets permissions as needed based on besu config +# and is replaced by the besu process running as besu user. +# Suppressing this warning as there's no risk here because the root user +# only sets permissions and does not continue running the main process. +# hadolint ignore=DL3002 USER root RUN chmod +x /opt/besu/bin/besu-entry.sh