SNAPSYNC add request task (#3601)

PR that adds the different request tasks necessary for the snapsync as well as a utility to manage the ranges of requests


Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
pull/3629/head
matkt 3 years ago committed by GitHub
parent 1aec566c35
commit 0d182b80ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 95
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TrieGenerator.java
  2. 78
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/proof/WorldStateRangeProofProviderTest.java
  3. 42
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java
  4. 93
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/GetAccountRangeFromPeerTask.java
  5. 104
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/GetBytecodeFromPeerTask.java
  6. 97
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/GetStorageRangeFromPeerTask.java
  7. 113
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/GetTrieNodeFromPeerTask.java
  8. 78
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetAccountRangeFromPeerTask.java
  9. 72
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetBytecodeFromPeerTask.java
  10. 82
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetStorageRangeFromPeerTask.java
  11. 71
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetTrieNodeFromPeerTask.java
  12. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapServer.java
  13. 13
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessage.java
  14. 8
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/ByteCodesMessage.java
  15. 17
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessage.java
  16. 42
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetByteCodesMessage.java
  17. 55
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetStorageRangeMessage.java
  18. 49
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetTrieNodesMessage.java
  19. 17
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/StorageRangeMessage.java
  20. 25
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/snap/TrieNodesMessage.java
  21. 142
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/RangeManager.java
  22. 57
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/AccountRangeMessageTest.java
  23. 49
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/BytecodeMessageTest.java
  24. 48
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetAccountRangeMessageTest.java
  25. 51
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetBytecodeMessageTest.java
  26. 52
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetStorageRangeMessageTest.java
  27. 53
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/GetTrieNodeMessageTest.java
  28. 56
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/StorageRangeMessageTest.java
  29. 49
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/snap/TrieNodeMessageTest.java
  30. 174
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/RangeManagerTest.java
  31. 16
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/wire/AbstractSnapMessageData.java
  32. 17
      ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/InnerNodeDiscoveryManager.java
  33. 20
      ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/MerkleTrieException.java
  34. 4
      ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/StoredNode.java

@ -0,0 +1,95 @@
/*
* 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.core;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie;
import org.hyperledger.besu.ethereum.trie.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
public class TrieGenerator {
public static MerklePatriciaTrie<Bytes32, Bytes> generateTrie(
final WorldStateStorage worldStateStorage, final int nbAccounts) {
final List<Hash> accountHash = new ArrayList<>();
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
emptyAccountStateTrie(worldStateStorage);
// Add some storage values
for (int i = 0; i < nbAccounts; i++) {
final WorldStateStorage.Updater updater = worldStateStorage.updater();
accountHash.add(Hash.wrap(Bytes32.leftPad(Bytes.of(i + 1))));
final MerklePatriciaTrie<Bytes32, Bytes> storageTrie =
emptyStorageTrie(worldStateStorage, accountHash.get(i));
writeStorageValue(storageTrie, UInt256.ONE, UInt256.valueOf(2L));
writeStorageValue(storageTrie, UInt256.valueOf(2L), UInt256.valueOf(4L));
writeStorageValue(storageTrie, UInt256.valueOf(3L), UInt256.valueOf(6L));
int accountIndex = i;
storageTrie.commit(
(location, hash, value) ->
updater.putAccountStorageTrieNode(
accountHash.get(accountIndex), location, hash, value));
final Hash codeHash = Hash.hash(Bytes32.leftPad(Bytes.of(i + 10)));
final StateTrieAccountValue accountValue =
new StateTrieAccountValue(1L, Wei.of(2L), Hash.wrap(storageTrie.getRootHash()), codeHash);
accountStateTrie.put(accountHash.get(i), RLP.encode(accountValue::writeTo));
accountStateTrie.commit(updater::putAccountStateTrieNode);
// Persist updates
updater.commit();
}
return accountStateTrie;
}
private static void writeStorageValue(
final MerklePatriciaTrie<Bytes32, Bytes> storageTrie,
final UInt256 key,
final UInt256 value) {
storageTrie.put(storageKeyHash(key), encodeStorageValue(value));
}
private static Bytes32 storageKeyHash(final UInt256 storageKey) {
return Hash.hash(storageKey);
}
private static Bytes encodeStorageValue(final UInt256 storageValue) {
return RLP.encode(out -> out.writeBytes(storageValue.toMinimalBytes()));
}
public static MerklePatriciaTrie<Bytes32, Bytes> emptyStorageTrie(
final WorldStateStorage worldStateStorage, final Hash accountHash) {
return new StoredMerklePatriciaTrie<>(
(location, hash) ->
worldStateStorage.getAccountStorageTrieNode(accountHash, location, hash),
b -> b,
b -> b);
}
public static MerklePatriciaTrie<Bytes32, Bytes> emptyAccountStateTrie(
final WorldStateStorage worldStateStorage) {
return new StoredMerklePatriciaTrie<>(
worldStateStorage::getAccountStateTrieNode, b -> b, b -> b);
}
}

@ -17,14 +17,11 @@ package org.hyperledger.besu.ethereum.proof;
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.rlp.RLP;
import org.hyperledger.besu.ethereum.core.TrieGenerator;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie;
import org.hyperledger.besu.ethereum.trie.RangeStorageEntriesCollector;
import org.hyperledger.besu.ethereum.trie.StoredMerklePatriciaTrie;
import org.hyperledger.besu.ethereum.trie.TrieIterator;
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@ -36,7 +33,6 @@ import java.util.TreeMap;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Before;
import org.junit.Test;
@ -57,7 +53,8 @@ public class WorldStateRangeProofProviderTest {
@Test
public void rangeProofValidationNominalCase() {
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = generateTrie();
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
// collect accounts in range
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 10, Integer.MAX_VALUE);
@ -85,7 +82,8 @@ public class WorldStateRangeProofProviderTest {
@Test
public void rangeProofValidationMissingAccount() {
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = generateTrie();
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
// collect accounts in range
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 10, Integer.MAX_VALUE);
@ -122,7 +120,8 @@ public class WorldStateRangeProofProviderTest {
@Test
public void rangeProofValidationNoMonotonicIncreasing() {
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = generateTrie();
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
// generate the invalid proof
final RangeStorageEntriesCollector collector =
@ -158,7 +157,8 @@ public class WorldStateRangeProofProviderTest {
@Test
public void rangeProofValidationEmptyProof() {
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = generateTrie();
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
// generate the invalid proof
final RangeStorageEntriesCollector collector =
@ -185,7 +185,8 @@ public class WorldStateRangeProofProviderTest {
@Test
public void rangeProofValidationInvalidEmptyProof() {
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = generateTrie();
MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
// generate the invalid proof
final RangeStorageEntriesCollector collector =
@ -209,61 +210,4 @@ public class WorldStateRangeProofProviderTest {
accounts);
assertThat(isValidRangeProof).isFalse();
}
private MerklePatriciaTrie<Bytes32, Bytes> generateTrie() {
final List<Hash> accountHash = new ArrayList<>();
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie = emptyAccountStateTrie();
// Add some storage values
for (int i = 0; i < 15; i++) {
final WorldStateStorage.Updater updater = worldStateStorage.updater();
accountHash.add(Hash.wrap(Bytes32.leftPad(Bytes.of(i + 1))));
final MerklePatriciaTrie<Bytes32, Bytes> storageTrie = emptyStorageTrie(accountHash.get(i));
writeStorageValue(storageTrie, UInt256.ONE, UInt256.valueOf(2L));
writeStorageValue(storageTrie, UInt256.valueOf(2L), UInt256.valueOf(4L));
writeStorageValue(storageTrie, UInt256.valueOf(3L), UInt256.valueOf(6L));
int accountIndex = i;
storageTrie.commit(
(location, hash, value) ->
updater.putAccountStorageTrieNode(
accountHash.get(accountIndex), location, hash, value));
final Hash codeHash = Hash.hash(Bytes32.leftPad(Bytes.of(i + 10)));
final StateTrieAccountValue accountValue =
new StateTrieAccountValue(1L, Wei.of(2L), Hash.wrap(storageTrie.getRootHash()), codeHash);
accountStateTrie.put(accountHash.get(i), RLP.encode(accountValue::writeTo));
accountStateTrie.commit(updater::putAccountStateTrieNode);
// Persist updates
updater.commit();
}
return accountStateTrie;
}
private void writeStorageValue(
final MerklePatriciaTrie<Bytes32, Bytes> storageTrie,
final UInt256 key,
final UInt256 value) {
storageTrie.put(storageKeyHash(key), encodeStorageValue(value));
}
private Bytes32 storageKeyHash(final UInt256 storageKey) {
return Hash.hash(storageKey);
}
private Bytes encodeStorageValue(final UInt256 storageValue) {
return RLP.encode(out -> out.writeBytes(storageValue.toMinimalBytes()));
}
private MerklePatriciaTrie<Bytes32, Bytes> emptyStorageTrie(final Hash accountHash) {
return new StoredMerklePatriciaTrie<>(
(location, hash) ->
worldStateStorage.getAccountStorageTrieNode(accountHash, location, hash),
b -> b,
b -> b);
}
private static MerklePatriciaTrie<Bytes32, Bytes> emptyAccountStateTrie() {
return new StoredMerklePatriciaTrie<>(
worldStateStorage::getAccountStateTrieNode, b -> b, b -> b);
}
}

@ -31,7 +31,7 @@ import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.GetAccountRangeMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.GetByteCodesMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.GetStorageRangeMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.GetTrieNodes;
import org.hyperledger.besu.ethereum.eth.messages.snap.GetTrieNodesMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
@ -57,6 +57,7 @@ import java.util.function.Consumer;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -291,26 +292,45 @@ public class EthPeer {
requestManagers.get(protocolName).get(EthPV65.GET_POOLED_TRANSACTIONS), message);
}
public RequestManager.ResponseStream getSnapAccountRange(final GetAccountRangeMessage message)
public RequestManager.ResponseStream getSnapAccountRange(
final Hash stateRoot, final Bytes32 startKeyHash, final Bytes32 endKeyHash)
throws PeerNotConnected {
final GetAccountRangeMessage getAccountRangeMessage =
GetAccountRangeMessage.create(stateRoot, startKeyHash, endKeyHash);
getAccountRangeMessage.setRootHash(Optional.of(stateRoot));
return sendRequest(
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_ACCOUNT_RANGE), message);
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_ACCOUNT_RANGE),
getAccountRangeMessage);
}
public RequestManager.ResponseStream getSnapStorageRange(final GetStorageRangeMessage message)
public RequestManager.ResponseStream getSnapStorageRange(
final Hash stateRoot,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash)
throws PeerNotConnected {
final GetStorageRangeMessage getStorageRangeMessage =
GetStorageRangeMessage.create(stateRoot, accountHashes, startKeyHash, endKeyHash);
getStorageRangeMessage.setRootHash(Optional.of(stateRoot));
return sendRequest(
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_STORAGE_RANGE), message);
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_STORAGE_RANGE),
getStorageRangeMessage);
}
public RequestManager.ResponseStream getSnapBytecode(final GetByteCodesMessage message)
throws PeerNotConnected {
return sendRequest(requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_BYTECODES), message);
public RequestManager.ResponseStream getSnapBytecode(
final Hash stateRoot, final List<Bytes32> codeHashes) throws PeerNotConnected {
final GetByteCodesMessage getByteCodes = GetByteCodesMessage.create(codeHashes);
getByteCodes.setRootHash(Optional.of(stateRoot));
return sendRequest(
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_BYTECODES), getByteCodes);
}
public RequestManager.ResponseStream getSnapTrieNode(final GetTrieNodes message)
throws PeerNotConnected {
return sendRequest(requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_TRIE_NODES), message);
public RequestManager.ResponseStream getSnapTrieNode(
final Hash stateRoot, final List<List<Bytes>> paths) throws PeerNotConnected {
final GetTrieNodesMessage getTrieNodes = GetTrieNodesMessage.create(stateRoot, paths);
getTrieNodes.setRootHash(Optional.of(stateRoot));
return sendRequest(
requestManagers.get(SnapProtocol.NAME).get(SnapV1.GET_TRIE_NODES), getTrieNodes);
}
private RequestManager.ResponseStream sendRequest(

@ -0,0 +1,93 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerRequestTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetAccountRangeFromPeerTask
extends AbstractPeerRequestTask<AccountRangeMessage.AccountRangeData> {
private static final Logger LOG = LoggerFactory.getLogger(GetAccountRangeFromPeerTask.class);
private final Bytes32 startKeyHash;
private final Bytes32 endKeyHash;
private final BlockHeader blockHeader;
private GetAccountRangeFromPeerTask(
final EthContext ethContext,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, SnapV1.ACCOUNT_RANGE, metricsSystem);
this.startKeyHash = startKeyHash;
this.endKeyHash = endKeyHash;
this.blockHeader = blockHeader;
}
public static GetAccountRangeFromPeerTask forAccountRange(
final EthContext ethContext,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new GetAccountRangeFromPeerTask(
ethContext, startKeyHash, endKeyHash, blockHeader, metricsSystem);
}
@Override
protected PendingPeerRequest sendRequest() {
return sendRequestToPeer(
peer -> {
LOG.trace(
"Requesting account range [{} ,{}] for state root {} from peer {} .",
startKeyHash,
endKeyHash,
blockHeader.getStateRoot(),
peer);
return peer.getSnapAccountRange(blockHeader.getStateRoot(), startKeyHash, endKeyHash);
},
blockHeader.getNumber());
}
@Override
protected Optional<AccountRangeMessage.AccountRangeData> processResponse(
final boolean streamClosed, final MessageData message, final EthPeer peer) {
if (streamClosed) {
// We don't record this as a useless response because it's impossible to know if a peer has
// the data we're requesting.
return Optional.empty();
}
final AccountRangeMessage accountRangeMessage = AccountRangeMessage.readFrom(message);
final AccountRangeMessage.AccountRangeData accountRangeData =
accountRangeMessage.accountData(true);
return Optional.of(accountRangeData);
}
}

@ -0,0 +1,104 @@
/*
* 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 java.util.Collections.emptyMap;
import static org.slf4j.LoggerFactory.getLogger;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerRequestTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.ByteCodesMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
public class GetBytecodeFromPeerTask extends AbstractPeerRequestTask<Map<Bytes32, Bytes>> {
private static final Logger LOG = getLogger(GetBytecodeFromPeerTask.class);
private final List<Bytes32> codeHashes;
private final BlockHeader blockHeader;
private GetBytecodeFromPeerTask(
final EthContext ethContext,
final List<Bytes32> codeHashes,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, SnapV1.STORAGE_RANGE, metricsSystem);
this.codeHashes = codeHashes;
this.blockHeader = blockHeader;
}
public static GetBytecodeFromPeerTask forBytecode(
final EthContext ethContext,
final List<Bytes32> codeHashes,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new GetBytecodeFromPeerTask(ethContext, codeHashes, blockHeader, metricsSystem);
}
@Override
protected PendingPeerRequest sendRequest() {
return sendRequestToPeer(
peer -> {
LOG.trace("Requesting {} Bytecodes from {} .", codeHashes.size(), peer);
return peer.getSnapBytecode(blockHeader.getStateRoot(), codeHashes);
},
blockHeader.getNumber());
}
@Override
protected Optional<Map<Bytes32, Bytes>> processResponse(
final boolean streamClosed, final MessageData message, final EthPeer peer) {
if (streamClosed) {
// We don't record this as a useless response because it's impossible to know if a peer has
// the data we're requesting.
return Optional.of(emptyMap());
}
final ByteCodesMessage byteCodesMessage = ByteCodesMessage.readFrom(message);
final ArrayDeque<Bytes> bytecodes = byteCodesMessage.bytecodes(true).codes();
if (bytecodes.size() > codeHashes.size()) {
// Can't be the response to our request
return Optional.empty();
}
return mapCodeByHash(bytecodes);
}
private Optional<Map<Bytes32, Bytes>> mapCodeByHash(final ArrayDeque<Bytes> bytecodes) {
final Map<Bytes32, Bytes> codeByHash = new HashMap<>();
for (int i = 0; i < bytecodes.size(); i++) {
final Hash hash = Hash.hash(bytecodes.get(i));
if (codeHashes.get(i).equals(hash)) {
codeByHash.put(hash, bytecodes.get(i));
}
}
return Optional.of(codeByHash);
}
}

@ -0,0 +1,97 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerRequestTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.eth.messages.snap.StorageRangeMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetStorageRangeFromPeerTask
extends AbstractPeerRequestTask<StorageRangeMessage.SlotRangeData> {
private static final Logger LOG = LoggerFactory.getLogger(GetStorageRangeFromPeerTask.class);
private final List<Bytes32> accountHashes;
private final Bytes32 startKeyHash;
private final Bytes32 endKeyHash;
private final BlockHeader blockHeader;
private GetStorageRangeFromPeerTask(
final EthContext ethContext,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, SnapV1.STORAGE_RANGE, metricsSystem);
this.accountHashes = accountHashes;
this.startKeyHash = startKeyHash;
this.endKeyHash = endKeyHash;
this.blockHeader = blockHeader;
}
public static GetStorageRangeFromPeerTask forStorageRange(
final EthContext ethContext,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new GetStorageRangeFromPeerTask(
ethContext, accountHashes, startKeyHash, endKeyHash, blockHeader, metricsSystem);
}
@Override
protected PendingPeerRequest sendRequest() {
return sendRequestToPeer(
peer -> {
LOG.trace(
"Requesting storage range [{} ,{}] for {} accounts from peer {} .",
startKeyHash,
endKeyHash,
accountHashes.size(),
peer);
return peer.getSnapStorageRange(
blockHeader.getStateRoot(), accountHashes, startKeyHash, endKeyHash);
},
blockHeader.getNumber());
}
@Override
protected Optional<StorageRangeMessage.SlotRangeData> processResponse(
final boolean streamClosed, final MessageData message, final EthPeer peer) {
if (streamClosed) {
// We don't record this as a useless response because it's impossible to know if a peer has
// the data we're requesting.
return Optional.empty();
}
return Optional.of(StorageRangeMessage.readFrom(message).slotsData(true));
}
}

@ -0,0 +1,113 @@
/*
* 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 java.util.Collections.emptyMap;
import static org.slf4j.LoggerFactory.getLogger;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerRequestTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.eth.messages.snap.TrieNodesMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import com.google.common.collect.Lists;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
public class GetTrieNodeFromPeerTask extends AbstractPeerRequestTask<Map<Bytes, Bytes>> {
private static final Logger LOG = getLogger(GetTrieNodeFromPeerTask.class);
private final List<List<Bytes>> paths;
private final BlockHeader blockHeader;
private GetTrieNodeFromPeerTask(
final EthContext ethContext,
final List<List<Bytes>> paths,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, SnapV1.TRIE_NODES, metricsSystem);
this.paths = paths;
this.blockHeader = blockHeader;
}
public static GetTrieNodeFromPeerTask forTrieNodes(
final EthContext ethContext,
final Map<Bytes, List<Bytes>> paths,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new GetTrieNodeFromPeerTask(
ethContext,
paths.entrySet().stream()
.map(entry -> Lists.asList(entry.getKey(), entry.getValue().toArray(new Bytes[0])))
.collect(Collectors.toList()),
blockHeader,
metricsSystem);
}
@Override
protected PendingPeerRequest sendRequest() {
return sendRequestToPeer(
peer -> {
LOG.trace("Requesting {} trie nodes from peer {}", paths.size(), peer);
return peer.getSnapTrieNode(blockHeader.getStateRoot(), paths);
},
blockHeader.getNumber());
}
@Override
protected Optional<Map<Bytes, Bytes>> processResponse(
final boolean streamClosed, final MessageData message, final EthPeer peer) {
if (streamClosed) {
// We don't record this as a useless response because it's impossible to know if a peer has
// the data we're requesting.
return Optional.of(emptyMap());
}
final TrieNodesMessage trieNodes = TrieNodesMessage.readFrom(message);
final ArrayDeque<Bytes> nodes = trieNodes.nodes(true);
return mapNodeDataByPath(nodes);
}
private Optional<Map<Bytes, Bytes>> mapNodeDataByPath(final ArrayDeque<Bytes> nodeData) {
final Map<Bytes, Bytes> nodeDataByPath = new HashMap<>();
paths.forEach(
list -> {
int i = 1;
if (!nodeData.isEmpty() && i == list.size()) {
Bytes bytes = nodeData.removeFirst();
nodeDataByPath.put(list.get(0), bytes);
} else {
while (!nodeData.isEmpty() && i < list.size()) {
Bytes bytes = nodeData.removeFirst();
nodeDataByPath.put(Bytes.concatenate(list.get(0), list.get(i)), bytes);
i++;
}
}
});
return Optional.of(nodeDataByPath);
}
}

@ -0,0 +1,78 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.apache.tuweni.bytes.Bytes32;
public class RetryingGetAccountRangeFromPeerTask
extends AbstractRetryingPeerTask<AccountRangeMessage.AccountRangeData> {
private final EthContext ethContext;
private final Bytes32 startKeyHash;
private final Bytes32 endKeyHash;
private final BlockHeader blockHeader;
private final MetricsSystem metricsSystem;
private RetryingGetAccountRangeFromPeerTask(
final EthContext ethContext,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(
ethContext, 3, data -> data.accounts().isEmpty() && data.proofs().isEmpty(), metricsSystem);
this.ethContext = ethContext;
this.startKeyHash = startKeyHash;
this.endKeyHash = endKeyHash;
this.blockHeader = blockHeader;
this.metricsSystem = metricsSystem;
}
public static EthTask<AccountRangeMessage.AccountRangeData> forAccountRange(
final EthContext ethContext,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new RetryingGetAccountRangeFromPeerTask(
ethContext, startKeyHash, endKeyHash, blockHeader, metricsSystem);
}
@Override
protected CompletableFuture<AccountRangeMessage.AccountRangeData> executePeerTask(
final Optional<EthPeer> assignedPeer) {
final GetAccountRangeFromPeerTask task =
GetAccountRangeFromPeerTask.forAccountRange(
ethContext, startKeyHash, endKeyHash, blockHeader, metricsSystem);
assignedPeer.ifPresent(task::assignPeer);
return executeSubTask(task::run)
.thenApply(
peerResult -> {
result.complete(peerResult.getResult());
return peerResult.getResult();
});
}
}

@ -0,0 +1,72 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class RetryingGetBytecodeFromPeerTask extends AbstractRetryingPeerTask<Map<Bytes32, Bytes>> {
private final EthContext ethContext;
private final List<Bytes32> codeHashes;
private final BlockHeader blockHeader;
private final MetricsSystem metricsSystem;
private RetryingGetBytecodeFromPeerTask(
final EthContext ethContext,
final List<Bytes32> codeHashes,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, 3, Map::isEmpty, metricsSystem);
this.ethContext = ethContext;
this.codeHashes = codeHashes;
this.blockHeader = blockHeader;
this.metricsSystem = metricsSystem;
}
public static EthTask<Map<Bytes32, Bytes>> forByteCode(
final EthContext ethContext,
final List<Bytes32> codeHashes,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new RetryingGetBytecodeFromPeerTask(ethContext, codeHashes, blockHeader, metricsSystem);
}
@Override
protected CompletableFuture<Map<Bytes32, Bytes>> executePeerTask(
final Optional<EthPeer> assignedPeer) {
final GetBytecodeFromPeerTask task =
GetBytecodeFromPeerTask.forBytecode(ethContext, codeHashes, blockHeader, metricsSystem);
assignedPeer.ifPresent(task::assignPeer);
return executeSubTask(task::run)
.thenApply(
peerResult -> {
result.complete(peerResult.getResult());
return peerResult.getResult();
});
}
}

@ -0,0 +1,82 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.messages.snap.StorageRangeMessage;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.apache.tuweni.bytes.Bytes32;
public class RetryingGetStorageRangeFromPeerTask
extends AbstractRetryingPeerTask<StorageRangeMessage.SlotRangeData> {
private final EthContext ethContext;
private final List<Bytes32> accountHashes;
private final Bytes32 startKeyHash;
private final Bytes32 endKeyHash;
private final BlockHeader blockHeader;
private final MetricsSystem metricsSystem;
private RetryingGetStorageRangeFromPeerTask(
final EthContext ethContext,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, 3, data -> data.proofs().isEmpty() && data.slots().isEmpty(), metricsSystem);
this.ethContext = ethContext;
this.accountHashes = accountHashes;
this.startKeyHash = startKeyHash;
this.endKeyHash = endKeyHash;
this.blockHeader = blockHeader;
this.metricsSystem = metricsSystem;
}
public static EthTask<StorageRangeMessage.SlotRangeData> forStorageRange(
final EthContext ethContext,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new RetryingGetStorageRangeFromPeerTask(
ethContext, accountHashes, startKeyHash, endKeyHash, blockHeader, metricsSystem);
}
@Override
protected CompletableFuture<StorageRangeMessage.SlotRangeData> executePeerTask(
final Optional<EthPeer> assignedPeer) {
final GetStorageRangeFromPeerTask task =
GetStorageRangeFromPeerTask.forStorageRange(
ethContext, accountHashes, startKeyHash, endKeyHash, blockHeader, metricsSystem);
assignedPeer.ifPresent(task::assignPeer);
return executeSubTask(task::run)
.thenApply(
peerResult -> {
result.complete(peerResult.getResult());
return peerResult.getResult();
});
}
}

@ -0,0 +1,71 @@
/*
* 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 org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.apache.tuweni.bytes.Bytes;
public class RetryingGetTrieNodeFromPeerTask extends AbstractRetryingPeerTask<Map<Bytes, Bytes>> {
private final EthContext ethContext;
private final Map<Bytes, List<Bytes>> paths;
private final BlockHeader blockHeader;
private final MetricsSystem metricsSystem;
private RetryingGetTrieNodeFromPeerTask(
final EthContext ethContext,
final Map<Bytes, List<Bytes>> paths,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
super(ethContext, 3, Map::isEmpty, metricsSystem);
this.ethContext = ethContext;
this.paths = paths;
this.blockHeader = blockHeader;
this.metricsSystem = metricsSystem;
}
public static EthTask<Map<Bytes, Bytes>> forTrieNodes(
final EthContext ethContext,
final Map<Bytes, List<Bytes>> paths,
final BlockHeader blockHeader,
final MetricsSystem metricsSystem) {
return new RetryingGetTrieNodeFromPeerTask(ethContext, paths, blockHeader, metricsSystem);
}
@Override
protected CompletableFuture<Map<Bytes, Bytes>> executePeerTask(
final Optional<EthPeer> assignedPeer) {
final GetTrieNodeFromPeerTask task =
GetTrieNodeFromPeerTask.forTrieNodes(ethContext, paths, blockHeader, metricsSystem);
assignedPeer.ifPresent(task::assignPeer);
return executeSubTask(task::run)
.thenApply(
peerResult -> {
result.complete(peerResult.getResult());
return peerResult.getResult();
});
}
}

@ -19,7 +19,7 @@ import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.ByteCodesMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1;
import org.hyperledger.besu.ethereum.eth.messages.snap.StorageRangeMessage;
import org.hyperledger.besu.ethereum.eth.messages.snap.TrieNodes;
import org.hyperledger.besu.ethereum.eth.messages.snap.TrieNodesMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
@ -74,6 +74,6 @@ class SnapServer {
private MessageData constructGetTrieNodesResponse(
final WorldStateArchive worldStateArchive, final MessageData message) {
return TrieNodes.create(new ArrayDeque<>());
return TrieNodesMessage.create(new ArrayDeque<>());
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
@ -34,6 +35,10 @@ import org.immutables.value.Value;
public final class AccountRangeMessage extends AbstractSnapMessageData {
public AccountRangeMessage(final Bytes data) {
super(data);
}
public static AccountRangeMessage readFrom(final MessageData message) {
if (message instanceof AccountRangeMessage) {
return (AccountRangeMessage) message;
@ -47,14 +52,14 @@ public final class AccountRangeMessage extends AbstractSnapMessageData {
}
public static AccountRangeMessage create(
final Map<Bytes32, Bytes> accounts, final ArrayDeque<Bytes> proof) {
final Map<Bytes32, Bytes> accounts, final List<Bytes> proof) {
return create(Optional.empty(), accounts, proof);
}
public static AccountRangeMessage create(
final Optional<BigInteger> requestId,
final Map<Bytes32, Bytes> accounts,
final ArrayDeque<Bytes> proof) {
final List<Bytes> proof) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
requestId.ifPresent(tmp::writeBigIntegerScalar);
@ -71,10 +76,6 @@ public final class AccountRangeMessage extends AbstractSnapMessageData {
return new AccountRangeMessage(tmp.encoded());
}
public AccountRangeMessage(final Bytes data) {
super(data);
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final AccountRangeData accountData = accountData(false);

@ -30,6 +30,10 @@ import org.immutables.value.Value;
public final class ByteCodesMessage extends AbstractSnapMessageData {
public ByteCodesMessage(final Bytes data) {
super(data);
}
public static ByteCodesMessage readFrom(final MessageData message) {
if (message instanceof ByteCodesMessage) {
return (ByteCodesMessage) message;
@ -56,10 +60,6 @@ public final class ByteCodesMessage extends AbstractSnapMessageData {
return new ByteCodesMessage(tmp.encoded());
}
public ByteCodesMessage(final Bytes data) {
super(data);
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final ByteCodes bytecodes = bytecodes(false);

@ -29,6 +29,10 @@ import org.immutables.value.Value;
public final class GetAccountRangeMessage extends AbstractSnapMessageData {
public GetAccountRangeMessage(final Bytes data) {
super(data);
}
public static GetAccountRangeMessage readFrom(final MessageData message) {
if (message instanceof GetAccountRangeMessage) {
return (GetAccountRangeMessage) message;
@ -42,24 +46,17 @@ public final class GetAccountRangeMessage extends AbstractSnapMessageData {
}
public static GetAccountRangeMessage create(
final Hash worldStateRootHash,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BigInteger responseBytes) {
final Hash worldStateRootHash, final Bytes32 startKeyHash, final Bytes32 endKeyHash) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
tmp.writeBytes(worldStateRootHash);
tmp.writeBytes(startKeyHash);
tmp.writeBytes(endKeyHash);
tmp.writeBigIntegerScalar(responseBytes);
tmp.writeBigIntegerScalar(SIZE_REQUEST);
tmp.endList();
return new GetAccountRangeMessage(tmp.encoded());
}
public GetAccountRangeMessage(final Bytes data) {
super(data);
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final Range range = range(false);
@ -86,7 +83,7 @@ public final class GetAccountRangeMessage extends AbstractSnapMessageData {
final Hash worldStateRootHash = Hash.wrap(Bytes32.wrap(input.readBytes32()));
final ImmutableRange range =
ImmutableRange.builder()
.worldStateRootHash(getOverrideStateRoot().orElse(worldStateRootHash))
.worldStateRootHash(getRootHash().orElse(worldStateRootHash))
.startKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32())))
.endKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32())))
.responseBytes(input.readBigIntegerScalar())

@ -21,16 +21,19 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.immutables.value.Value;
public final class GetByteCodesMessage extends AbstractSnapMessageData {
final Optional<ArrayDeque<Bytes32>> accountHashes;
public GetByteCodesMessage(final Bytes data) {
super(data);
}
public static GetByteCodesMessage readFrom(final MessageData message) {
if (message instanceof GetByteCodesMessage) {
@ -41,40 +44,27 @@ public final class GetByteCodesMessage extends AbstractSnapMessageData {
throw new IllegalArgumentException(
String.format("Message has code %d and thus is not a GetByteCodesMessage.", code));
}
return new GetByteCodesMessage(Optional.empty(), message.getData());
return new GetByteCodesMessage(message.getData());
}
public static GetByteCodesMessage create(
final Optional<ArrayDeque<Bytes32>> accountHashes,
final ArrayDeque<Bytes32> codeHashes,
final BigInteger responseBytes) {
return create(Optional.empty(), accountHashes, codeHashes, responseBytes);
public static GetByteCodesMessage create(final List<Bytes32> codeHashes) {
return create(Optional.empty(), codeHashes);
}
public static GetByteCodesMessage create(
final Optional<BigInteger> requestId,
final Optional<ArrayDeque<Bytes32>> accountHashes,
final ArrayDeque<Bytes32> codeHashes,
final BigInteger responseBytes) {
final Optional<BigInteger> requestId, final List<Bytes32> codeHashes) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
requestId.ifPresent(tmp::writeBigIntegerScalar);
tmp.writeList(codeHashes, (hash, rlpOutput) -> rlpOutput.writeBytes(hash));
tmp.writeBigIntegerScalar(responseBytes);
tmp.writeBigIntegerScalar(SIZE_REQUEST);
tmp.endList();
return new GetByteCodesMessage(accountHashes, tmp.encoded());
}
public GetByteCodesMessage(final Optional<ArrayDeque<Bytes32>> accountHashes, final Bytes data) {
super(data);
this.accountHashes = accountHashes;
return new GetByteCodesMessage(tmp.encoded());
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final CodeHashes request = codeHashes(false);
return create(Optional.of(requestId), accountHashes, request.hashes(), request.responseBytes())
.getData();
return create(Optional.of(requestId), codeHashes(false).hashes()).getData();
}
@Override
@ -83,7 +73,7 @@ public final class GetByteCodesMessage extends AbstractSnapMessageData {
}
public CodeHashes codeHashes(final boolean withRequestId) {
final ArrayDeque<Bytes32> hashes = new ArrayDeque<>();
final List<Bytes32> hashes = new ArrayList<>();
final BigInteger responseBytes;
final RLPInput input = new BytesValueRLPInput(data, false);
input.enterList();
@ -98,14 +88,10 @@ public final class GetByteCodesMessage extends AbstractSnapMessageData {
return ImmutableCodeHashes.builder().hashes(hashes).responseBytes(responseBytes).build();
}
public Optional<ArrayDeque<Bytes32>> getAccountHashes() {
return accountHashes;
}
@Value.Immutable
public interface CodeHashes {
ArrayDeque<Bytes32> hashes();
List<Bytes32> hashes();
BigInteger responseBytes();
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
@ -32,7 +33,9 @@ import org.immutables.value.Value;
public final class GetStorageRangeMessage extends AbstractSnapMessageData {
private final Optional<ArrayDeque<Bytes32>> storageRoots;
public GetStorageRangeMessage(final Bytes data) {
super(data);
}
public static GetStorageRangeMessage readFrom(final MessageData message) {
if (message instanceof GetStorageRangeMessage) {
@ -48,29 +51,18 @@ public final class GetStorageRangeMessage extends AbstractSnapMessageData {
public static GetStorageRangeMessage create(
final Hash worldStateRootHash,
final ArrayDeque<Bytes32> accountHashes,
final Optional<ArrayDeque<Bytes32>> storageRoots,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BigInteger responseBytes) {
return create(
Optional.empty(),
worldStateRootHash,
accountHashes,
storageRoots,
startKeyHash,
endKeyHash,
responseBytes);
final Bytes32 endKeyHash) {
return create(Optional.empty(), worldStateRootHash, accountHashes, startKeyHash, endKeyHash);
}
public static GetStorageRangeMessage create(
final Optional<BigInteger> requestId,
final Hash worldStateRootHash,
final ArrayDeque<Bytes32> accountHashes,
final Optional<ArrayDeque<Bytes32>> storageRoots,
final List<Bytes32> accountHashes,
final Bytes32 startKeyHash,
final Bytes32 endKeyHash,
final BigInteger responseBytes) {
final Bytes32 endKeyHash) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
requestId.ifPresent(tmp::writeBigIntegerScalar);
@ -78,23 +70,9 @@ public final class GetStorageRangeMessage extends AbstractSnapMessageData {
tmp.writeList(accountHashes, (hash, rlpOutput) -> rlpOutput.writeBytes(hash));
tmp.writeBytes(startKeyHash);
tmp.writeBytes(endKeyHash);
tmp.writeBigIntegerScalar(responseBytes);
tmp.writeBigIntegerScalar(SIZE_REQUEST);
tmp.endList();
return new GetStorageRangeMessage(tmp.encoded(), storageRoots);
}
public GetStorageRangeMessage(final Bytes data) {
this(data, Optional.empty());
}
public GetStorageRangeMessage(
final Bytes data, final Optional<ArrayDeque<Bytes32>> storageRoots) {
super(data);
this.storageRoots = storageRoots;
}
public Optional<ArrayDeque<Bytes32>> getStorageRoots() {
return storageRoots;
return new GetStorageRangeMessage(tmp.encoded());
}
@Override
@ -104,10 +82,8 @@ public final class GetStorageRangeMessage extends AbstractSnapMessageData {
Optional.of(requestId),
range.worldStateRootHash(),
range.hashes(),
storageRoots,
range.startKeyHash(),
range.endKeyHash(),
range.responseBytes())
range.endKeyHash())
.getData();
}
@ -124,7 +100,7 @@ public final class GetStorageRangeMessage extends AbstractSnapMessageData {
final Hash worldStateRootHash = Hash.wrap(Bytes32.wrap(input.readBytes32()));
final ImmutableStorageRange.Builder range =
ImmutableStorageRange.builder()
.worldStateRootHash(getOverrideStateRoot().orElse(worldStateRootHash));
.worldStateRootHash(getRootHash().orElse(worldStateRootHash));
input.enterList();
while (!input.isEndOfCurrentList()) {
hashes.add(input.readBytes32());
@ -149,11 +125,6 @@ public final class GetStorageRangeMessage extends AbstractSnapMessageData {
return range.build();
}
@Override
public String toString() {
return "GetStorageRangeMessage{" + "storageRoots=" + storageRoots + ", data=" + data + '}';
}
@Value.Immutable
public interface StorageRange {

@ -29,39 +29,33 @@ import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.immutables.value.Value;
public final class GetTrieNodes extends AbstractSnapMessageData {
public final class GetTrieNodesMessage extends AbstractSnapMessageData {
public static GetTrieNodes readFrom(final MessageData message) {
if (message instanceof GetTrieNodes) {
return (GetTrieNodes) message;
public GetTrieNodesMessage(final Bytes data) {
super(data);
}
public static GetTrieNodesMessage readFrom(final MessageData message) {
if (message instanceof GetTrieNodesMessage) {
return (GetTrieNodesMessage) message;
}
final int code = message.getCode();
if (code != SnapV1.GET_TRIE_NODES) {
throw new IllegalArgumentException(
String.format("Message has code %d and thus is not a GetTrieNodes.", code));
}
return new GetTrieNodes(message.getData());
return new GetTrieNodesMessage(message.getData());
}
/*public static GetTrieNodes createWithRequest(
final Hash worldStateRootHash,
final TrieNodeDataRequest request,
final BigInteger responseBytes) {
return create(Optional.empty(), worldStateRootHash, request.getPaths(), responseBytes);
}*/
public static GetTrieNodes create(
final Hash worldStateRootHash,
final List<List<Bytes>> requests,
final BigInteger responseBytes) {
return create(Optional.empty(), worldStateRootHash, requests, responseBytes);
public static GetTrieNodesMessage create(
final Hash worldStateRootHash, final List<List<Bytes>> requests) {
return create(Optional.empty(), worldStateRootHash, requests);
}
public static GetTrieNodes create(
public static GetTrieNodesMessage create(
final Optional<BigInteger> requestId,
final Hash worldStateRootHash,
final List<List<Bytes>> paths,
final BigInteger responseBytes) {
final List<List<Bytes>> paths) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
requestId.ifPresent(tmp::writeBigIntegerScalar);
@ -70,24 +64,15 @@ public final class GetTrieNodes extends AbstractSnapMessageData {
paths,
(path, rlpOutput) ->
rlpOutput.writeList(path, (b, subRlpOutput) -> subRlpOutput.writeBytes(b)));
tmp.writeBigIntegerScalar(responseBytes);
tmp.writeBigIntegerScalar(SIZE_REQUEST);
tmp.endList();
return new GetTrieNodes(tmp.encoded());
}
public GetTrieNodes(final Bytes data) {
super(data);
return new GetTrieNodesMessage(tmp.encoded());
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final TrieNodesPaths paths = paths(false);
return create(
Optional.of(requestId),
paths.worldStateRootHash(),
paths.paths(),
paths.responseBytes())
.getData();
return create(Optional.of(requestId), paths.worldStateRootHash(), paths.paths()).getData();
}
@Override

@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
@ -32,6 +33,10 @@ import org.immutables.value.Value;
public final class StorageRangeMessage extends AbstractSnapMessageData {
public StorageRangeMessage(final Bytes data) {
super(data);
}
public static StorageRangeMessage readFrom(final MessageData message) {
if (message instanceof StorageRangeMessage) {
return (StorageRangeMessage) message;
@ -45,13 +50,13 @@ public final class StorageRangeMessage extends AbstractSnapMessageData {
}
public static StorageRangeMessage create(
final ArrayDeque<TreeMap<Bytes32, Bytes>> slots, final List<Bytes> proof) {
final ArrayDeque<Map<Bytes32, Bytes>> slots, final List<Bytes> proof) {
return create(Optional.empty(), slots, proof);
}
public static StorageRangeMessage create(
final Optional<BigInteger> requestId,
final ArrayDeque<TreeMap<Bytes32, Bytes>> slots,
final ArrayDeque<Map<Bytes32, Bytes>> slots,
final List<Bytes> proof) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
@ -72,10 +77,6 @@ public final class StorageRangeMessage extends AbstractSnapMessageData {
return new StorageRangeMessage(tmp.encoded());
}
public StorageRangeMessage(final Bytes data) {
super(data);
}
@Override
protected Bytes wrap(final BigInteger requestId) {
final SlotRangeData slotsData = slotsData(false);
@ -88,7 +89,7 @@ public final class StorageRangeMessage extends AbstractSnapMessageData {
}
public SlotRangeData slotsData(final boolean withRequestId) {
final ArrayDeque<TreeMap<Bytes32, Bytes>> slots = new ArrayDeque<>();
final ArrayDeque<Map<Bytes32, Bytes>> slots = new ArrayDeque<>();
final ArrayDeque<Bytes> proofs = new ArrayDeque<>();
final RLPInput input = new BytesValueRLPInput(data, false);
input.enterList();
@ -120,7 +121,7 @@ public final class StorageRangeMessage extends AbstractSnapMessageData {
@Value.Immutable
public interface SlotRangeData {
ArrayDeque<TreeMap<Bytes32, Bytes>> slots();
ArrayDeque<Map<Bytes32, Bytes>> slots();
ArrayDeque<Bytes> proofs();
}

@ -27,35 +27,36 @@ import java.util.Optional;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
public final class TrieNodes extends AbstractSnapMessageData {
public final class TrieNodesMessage extends AbstractSnapMessageData {
public static TrieNodes readFrom(final MessageData message) {
if (message instanceof TrieNodes) {
return (TrieNodes) message;
public TrieNodesMessage(final Bytes data) {
super(data);
}
public static TrieNodesMessage readFrom(final MessageData message) {
if (message instanceof TrieNodesMessage) {
return (TrieNodesMessage) message;
}
final int code = message.getCode();
if (code != SnapV1.TRIE_NODES) {
throw new IllegalArgumentException(
String.format("Message has code %d and thus is not a TrieNodes.", code));
}
return new TrieNodes(message.getData());
return new TrieNodesMessage(message.getData());
}
public static TrieNodes create(final List<Bytes> nodes) {
public static TrieNodesMessage create(final List<Bytes> nodes) {
return create(Optional.empty(), nodes);
}
public static TrieNodes create(final Optional<BigInteger> requestId, final List<Bytes> nodes) {
public static TrieNodesMessage create(
final Optional<BigInteger> requestId, final List<Bytes> nodes) {
final BytesValueRLPOutput tmp = new BytesValueRLPOutput();
tmp.startList();
requestId.ifPresent(tmp::writeBigIntegerScalar);
tmp.writeList(nodes, (node, rlpOutput) -> rlpOutput.writeBytes(node));
tmp.endList();
return new TrieNodes(tmp.encoded());
}
public TrieNodes(final Bytes data) {
super(data);
return new TrieNodesMessage(tmp.encoded());
}
@Override

@ -0,0 +1,142 @@
/*
* 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.sync.snapsync;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.trie.InnerNodeDiscoveryManager;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.trie.StoredMerklePatriciaTrie;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/**
* This class helps to generate ranges according to several parameters (the start and the end of the
* range, its size)
*/
public class RangeManager {
public static final Hash MIN_RANGE =
Hash.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000");
public static final Hash MAX_RANGE =
Hash.fromHexString("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
private RangeManager() {}
public static Map<Bytes32, Bytes32> generateAllRanges(final int sizeRange) {
if (sizeRange == 1) {
return Map.ofEntries(Map.entry(MIN_RANGE, MAX_RANGE));
}
return generateRanges(
MIN_RANGE.toUnsignedBigInteger(), MAX_RANGE.toUnsignedBigInteger(), sizeRange);
}
/**
* Generate a range
*
* @param min start of the range in bytes
* @param max the max end of the range in bytes
* @param nbRange number of ranges
* @return the start and end of the generated range
*/
public static Map<Bytes32, Bytes32> generateRanges(
final Bytes32 min, final Bytes32 max, final int nbRange) {
return generateRanges(min.toUnsignedBigInteger(), max.toUnsignedBigInteger(), nbRange);
}
/**
* Generate a range
*
* @param min start of the range
* @param max the max end of the range
* @param nbRange number of ranges
* @return the start and end of the generated range
*/
public static Map<Bytes32, Bytes32> generateRanges(
final BigInteger min, final BigInteger max, final int nbRange) {
final BigInteger rangeSize = max.subtract(min).divide(BigInteger.valueOf(nbRange));
final TreeMap<Bytes32, Bytes32> ranges = new TreeMap<>();
if (min.equals(max) || nbRange == 1) {
ranges.put(format(min), format(max));
return ranges;
}
BigInteger currentStart = min;
BigInteger currentEnd = min;
while (max.subtract(currentEnd).compareTo(rangeSize) > 0) {
currentEnd = currentStart.add(rangeSize);
ranges.put(format(currentStart), format(currentEnd));
currentStart = currentStart.add(rangeSize).add(BigInteger.ONE);
}
if (max.subtract(currentEnd).compareTo(BigInteger.ZERO) > 0) {
currentEnd = currentStart.add(max.subtract(currentEnd)).subtract(BigInteger.ONE);
ranges.put(format(currentStart), format(currentEnd));
}
return ranges;
}
/**
* Helps to create a new range according to the last data obtained. This happens when a peer
* doesn't return all of the data in a range.
*
* @param worldstateRootHash the root hash
* @param proofs proof received
* @param endKeyHash the end of the range initially wanted
* @param receivedKeys the last key received
* @return begin of the new range
*/
public static Optional<Bytes32> findNewBeginElementInRange(
final Bytes32 worldstateRootHash,
final List<Bytes> proofs,
final TreeMap<Bytes32, Bytes> receivedKeys,
final Bytes32 endKeyHash) {
if (receivedKeys.isEmpty() || receivedKeys.lastKey().compareTo(endKeyHash) >= 0) {
return Optional.empty();
} else {
final Map<Bytes32, Bytes> proofsEntries = new HashMap<>();
for (Bytes proof : proofs) {
proofsEntries.put(Hash.hash(proof), proof);
}
final StoredMerklePatriciaTrie<Bytes, Bytes> storageTrie =
new StoredMerklePatriciaTrie<>(
new InnerNodeDiscoveryManager<>(
(location, key) -> Optional.ofNullable(proofsEntries.get(key)),
Function.identity(),
Function.identity(),
receivedKeys.lastKey(),
endKeyHash,
false),
worldstateRootHash);
try {
storageTrie.visitAll(bytesNode -> {});
} catch (MerkleTrieException e) {
return Optional.of(InnerNodeDiscoveryManager.decodePath(e.getLocation()));
}
return Optional.empty();
}
}
private static Bytes32 format(final BigInteger data) {
return Bytes32.leftPad(Bytes.of(data.toByteArray()).trimLeadingZeros());
}
}

@ -0,0 +1,57 @@
/*
* 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.messages.snap;
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.RLP;
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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.Test;
public final class AccountRangeMessageTest {
@Test
public void roundTripTest() {
final Map<Bytes32, Bytes> keys = new HashMap<>();
final StateTrieAccountValue accountValue =
new StateTrieAccountValue(1L, Wei.of(2L), Hash.EMPTY_TRIE_HASH, Hash.EMPTY);
keys.put(Hash.wrap(Bytes32.leftPad(Bytes.of(1))), RLP.encode(accountValue::writeTo));
final List<Bytes> proofs = new ArrayList<>();
proofs.add(Bytes32.random());
// Perform round-trip transformation
final MessageData initialMessage = AccountRangeMessage.create(keys, proofs);
final MessageData raw = new RawMessage(SnapV1.ACCOUNT_RANGE, initialMessage.getData());
final AccountRangeMessage message = AccountRangeMessage.readFrom(raw);
// check match originals.
final AccountRangeMessage.AccountRangeData range = message.accountData(false);
Assertions.assertThat(range.accounts()).isEqualTo(keys);
Assertions.assertThat(range.proofs()).isEqualTo(proofs);
}
}

@ -0,0 +1,49 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class BytecodeMessageTest {
@Test
public void roundTripTest() {
final List<Bytes> codes = new ArrayList<>();
final int hashCount = 20;
for (int i = 0; i < hashCount; ++i) {
codes.add(Bytes32.random());
}
// Perform round-trip transformation
final MessageData initialMessage = ByteCodesMessage.create(codes);
final MessageData raw = new RawMessage(SnapV1.BYTECODES, initialMessage.getData());
final ByteCodesMessage message = ByteCodesMessage.readFrom(raw);
// check match originals.
final ByteCodesMessage.ByteCodes response = message.bytecodes(false);
Assertions.assertThat(response.codes()).isEqualTo(codes);
}
}

@ -0,0 +1,48 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.eth.sync.snapsync.RangeManager;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class GetAccountRangeMessageTest {
@Test
public void roundTripTest() {
final Hash rootHash = Hash.wrap(Bytes32.random());
final Hash startKeyHash = RangeManager.MIN_RANGE;
final Hash endKeyHash = RangeManager.MAX_RANGE;
// Perform round-trip transformation
final MessageData initialMessage =
GetAccountRangeMessage.create(rootHash, startKeyHash, endKeyHash);
final MessageData raw = new RawMessage(SnapV1.GET_ACCOUNT_RANGE, initialMessage.getData());
final GetAccountRangeMessage message = GetAccountRangeMessage.readFrom(raw);
// check match originals.
final GetAccountRangeMessage.Range range = message.range(false);
Assertions.assertThat(range.worldStateRootHash()).isEqualTo(rootHash);
Assertions.assertThat(range.startKeyHash()).isEqualTo(startKeyHash);
Assertions.assertThat(range.responseBytes()).isEqualTo(AbstractSnapMessageData.SIZE_REQUEST);
}
}

@ -0,0 +1,51 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class GetBytecodeMessageTest {
@Test
public void roundTripTest() {
final List<Bytes32> hashes = new ArrayList<>();
final int hashCount = 20;
for (int i = 0; i < hashCount; ++i) {
hashes.add(Bytes32.random());
}
// Perform round-trip transformation
final MessageData initialMessage = GetByteCodesMessage.create(hashes);
final MessageData raw = new RawMessage(SnapV1.GET_BYTECODES, initialMessage.getData());
final GetByteCodesMessage message = GetByteCodesMessage.readFrom(raw);
// check match originals.
final GetByteCodesMessage.CodeHashes codeHashes = message.codeHashes(false);
Assertions.assertThat(codeHashes.hashes()).isEqualTo(hashes);
Assertions.assertThat(codeHashes.responseBytes())
.isEqualTo(AbstractSnapMessageData.SIZE_REQUEST);
}
}

@ -0,0 +1,52 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.eth.sync.snapsync.RangeManager;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.List;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class GetStorageRangeMessageTest {
@Test
public void roundTripTest() {
final Hash rootHash = Hash.wrap(Bytes32.random());
final List<Bytes32> accountKeys = List.of(Bytes32.random());
final Hash startKeyHash = RangeManager.MIN_RANGE;
final Hash endKeyHash = RangeManager.MAX_RANGE;
// Perform round-trip transformation
final MessageData initialMessage =
GetStorageRangeMessage.create(rootHash, accountKeys, startKeyHash, endKeyHash);
final MessageData raw = new RawMessage(SnapV1.GET_STORAGE_RANGE, initialMessage.getData());
final GetStorageRangeMessage message = GetStorageRangeMessage.readFrom(raw);
// check match originals.
final GetStorageRangeMessage.StorageRange range = message.range(false);
Assertions.assertThat(range.worldStateRootHash()).isEqualTo(rootHash);
Assertions.assertThat(range.hashes()).isEqualTo(accountKeys);
Assertions.assertThat(range.startKeyHash()).isEqualTo(startKeyHash);
Assertions.assertThat(range.responseBytes()).isEqualTo(AbstractSnapMessageData.SIZE_REQUEST);
}
}

@ -0,0 +1,53 @@
/*
* 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.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.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class GetTrieNodeMessageTest {
@Test
public void roundTripTest() {
final Hash rootHash = Hash.wrap(Bytes32.random());
final List<Bytes> paths = new ArrayList<>();
final int hashCount = 20;
for (int i = 0; i < hashCount; ++i) {
paths.add(Bytes32.random());
}
// Perform round-trip transformation
final MessageData initialMessage = GetTrieNodesMessage.create(rootHash, List.of(paths));
final MessageData raw = new RawMessage(SnapV1.GET_TRIE_NODES, initialMessage.getData());
final GetTrieNodesMessage message = GetTrieNodesMessage.readFrom(raw);
// check match originals.
final GetTrieNodesMessage.TrieNodesPaths response = message.paths(false);
Assertions.assertThat(response.worldStateRootHash()).isEqualTo(rootHash);
Assertions.assertThat(response.paths()).contains(paths);
Assertions.assertThat(response.responseBytes()).isEqualTo(AbstractSnapMessageData.SIZE_REQUEST);
}
}

@ -0,0 +1,56 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class StorageRangeMessageTest {
@Test
public void roundTripTest() {
final ArrayDeque<Map<Bytes32, Bytes>> keys = new ArrayDeque<>();
final Map<Bytes32, Bytes> storage = new HashMap<>();
storage.put(Hash.wrap(Bytes32.leftPad(Bytes.of(1))), Bytes32.random());
keys.add(storage);
final List<Bytes> proofs = new ArrayList<>();
proofs.add(Bytes32.random());
// Perform round-trip transformation
final MessageData initialMessage = StorageRangeMessage.create(keys, proofs);
final MessageData raw = new RawMessage(SnapV1.STORAGE_RANGE, initialMessage.getData());
final StorageRangeMessage message = StorageRangeMessage.readFrom(raw);
// check match originals.
final StorageRangeMessage.SlotRangeData range = message.slotsData(false);
Assertions.assertThat(range.slots()).isEqualTo(keys);
Assertions.assertThat(range.proofs()).isEqualTo(proofs);
}
}

@ -0,0 +1,49 @@
/*
* 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.messages.snap;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.ArrayList;
import java.util.List;
import kotlin.collections.ArrayDeque;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class TrieNodeMessageTest {
@Test
public void roundTripTest() {
final List<Bytes> nodes = new ArrayList<>();
final int hashCount = 20;
for (int i = 0; i < hashCount; ++i) {
nodes.add(Bytes32.random());
}
// Perform round-trip transformation
final MessageData initialMessage = TrieNodesMessage.create(nodes);
final MessageData raw = new RawMessage(SnapV1.TRIE_NODES, initialMessage.getData());
final TrieNodesMessage message = TrieNodesMessage.readFrom(raw);
// check match originals.
final ArrayDeque<Bytes> response = message.nodes(false);
Assertions.assertThat(response).isEqualTo(nodes);
}
}

@ -0,0 +1,174 @@
/*
* 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.sync.snapsync;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.TrieGenerator;
import org.hyperledger.besu.ethereum.proof.WorldStateProofProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie;
import org.hyperledger.besu.ethereum.trie.RangeStorageEntriesCollector;
import org.hyperledger.besu.ethereum.trie.TrieIterator;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public final class RangeManagerTest {
@Test
public void testGenerateAllRangesWithSize1() {
final Map<Bytes32, Bytes32> expectedResult = new HashMap<>();
expectedResult.put(
Bytes32.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"),
Bytes32.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
final Map<Bytes32, Bytes32> ranges = RangeManager.generateAllRanges(1);
Assertions.assertThat(ranges.size()).isEqualTo(1);
Assertions.assertThat(ranges).isEqualTo(expectedResult);
}
@Test
public void testGenerateAllRangesWithSize3() {
final Map<Bytes32, Bytes32> expectedResult = new HashMap<>();
expectedResult.put(
Bytes32.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"),
Bytes32.fromHexString(
"0x5555555555555555555555555555555555555555555555555555555555555555"));
expectedResult.put(
Bytes32.fromHexString("0x5555555555555555555555555555555555555555555555555555555555555556"),
Bytes32.fromHexString(
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"));
expectedResult.put(
Bytes32.fromHexString("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
Bytes32.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
final Map<Bytes32, Bytes32> ranges = RangeManager.generateAllRanges(3);
Assertions.assertThat(ranges.size()).isEqualTo(3);
Assertions.assertThat(ranges).isEqualTo(expectedResult);
}
@Test
public void testGenerateRangesWithSize3() {
final Map<Bytes32, Bytes32> expectedResult = new HashMap<>();
expectedResult.put(
Bytes32.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"),
Bytes32.fromHexString(
"0x5555555555555555555555555555555555555555555555555555555555555555"));
expectedResult.put(
Bytes32.fromHexString("0x5555555555555555555555555555555555555555555555555555555555555556"),
Bytes32.fromHexString(
"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"));
expectedResult.put(
Bytes32.fromHexString("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
Bytes32.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
final Map<Bytes32, Bytes32> ranges =
RangeManager.generateRanges(
Bytes32.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"),
Bytes32.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
3);
Assertions.assertThat(ranges.size()).isEqualTo(3);
Assertions.assertThat(ranges).isEqualTo(expectedResult);
}
@Test
public void testFindNewBeginElement() {
final WorldStateStorage worldStateStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(
Hash.ZERO, RangeManager.MAX_RANGE, 10, Integer.MAX_VALUE);
final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector);
final TreeMap<Bytes32, Bytes> accounts =
(TreeMap<Bytes32, Bytes>)
accountStateTrie.entriesFrom(
root ->
RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO));
final WorldStateProofProvider worldStateProofProvider =
new WorldStateProofProvider(worldStateStorage);
// generate the proof
final List<Bytes> proofs =
worldStateProofProvider.getAccountProofRelatedNodes(
Hash.wrap(accountStateTrie.getRootHash()), Hash.ZERO);
proofs.addAll(
worldStateProofProvider.getAccountProofRelatedNodes(
Hash.wrap(accountStateTrie.getRootHash()), accounts.lastKey()));
final Optional<Bytes32> newBeginElementInRange =
RangeManager.findNewBeginElementInRange(
accountStateTrie.getRootHash(), proofs, accounts, RangeManager.MAX_RANGE);
Assertions.assertThat(newBeginElementInRange)
.contains(Bytes32.leftPad(Bytes.wrap(Bytes.ofUnsignedShort(0x0b))));
}
@Test
public void testFindNewBeginElementWhenNothingIsMissing() {
final WorldStateStorage worldStateStorage =
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final MerklePatriciaTrie<Bytes32, Bytes> accountStateTrie =
TrieGenerator.generateTrie(worldStateStorage, 15);
final RangeStorageEntriesCollector collector =
RangeStorageEntriesCollector.createCollector(
Hash.ZERO, RangeManager.MAX_RANGE, 15, Integer.MAX_VALUE);
final TrieIterator<Bytes> visitor = RangeStorageEntriesCollector.createVisitor(collector);
final TreeMap<Bytes32, Bytes> accounts =
(TreeMap<Bytes32, Bytes>)
accountStateTrie.entriesFrom(
root ->
RangeStorageEntriesCollector.collectEntries(
collector, visitor, root, Hash.ZERO));
final WorldStateProofProvider worldStateProofProvider =
new WorldStateProofProvider(worldStateStorage);
// generate the proof
final List<Bytes> proofs =
worldStateProofProvider.getAccountProofRelatedNodes(
Hash.wrap(accountStateTrie.getRootHash()), Hash.ZERO);
proofs.addAll(
worldStateProofProvider.getAccountProofRelatedNodes(
Hash.wrap(accountStateTrie.getRootHash()), accounts.lastKey()));
final Optional<Bytes32> newBeginElementInRange =
RangeManager.findNewBeginElementInRange(
accountStateTrie.getRootHash(), proofs, accounts, RangeManager.MAX_RANGE);
Assertions.assertThat(newBeginElementInRange).isEmpty();
}
}

@ -23,23 +23,27 @@ import java.util.AbstractMap;
import java.util.Map;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;
public abstract class AbstractSnapMessageData extends AbstractMessageData {
private Optional<Hash> overrideStateRoot;
@VisibleForTesting
public static final BigInteger SIZE_REQUEST = BigInteger.valueOf(524288); // 512 * 1024
private Optional<Hash> rootHash;
public AbstractSnapMessageData(final Bytes data) {
super(data);
overrideStateRoot = Optional.empty();
rootHash = Optional.empty();
}
public Optional<Hash> getOverrideStateRoot() {
return overrideStateRoot;
public Optional<Hash> getRootHash() {
return rootHash;
}
public void setOverrideStateRoot(final Optional<Hash> overrideStateRoot) {
this.overrideStateRoot = overrideStateRoot;
public void setRootHash(final Optional<Hash> rootHash) {
this.rootHash = rootHash;
}
@Override

@ -26,6 +26,7 @@ import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.bytes.MutableBytes;
import org.apache.tuweni.bytes.MutableBytes32;
import org.immutables.value.Value;
public class InnerNodeDiscoveryManager<V> extends StoredNodeFactory<V> {
@ -132,6 +133,22 @@ public class InnerNodeDiscoveryManager<V> extends StoredNodeFactory<V> {
return path;
}
public static Bytes32 decodePath(final Bytes bytes) {
final MutableBytes32 decoded = MutableBytes32.create();
final MutableBytes path = MutableBytes.create(Bytes32.SIZE * 2);
path.set(0, bytes);
int decodedPos = 0;
for (int pathPos = 0; pathPos < path.size() - 1; pathPos += 2, decodedPos += 1) {
final byte high = path.get(pathPos);
final byte low = path.get(pathPos + 1);
if ((high & 0xf0) != 0 || (low & 0xf0) != 0) {
throw new IllegalArgumentException("Invalid path: contains elements larger than a nibble");
}
decoded.set(decodedPos, (byte) (high << 4 | (low & 0xff)));
}
return decoded;
}
@Value.Immutable
public interface InnerNode {
Bytes location();

@ -14,17 +14,37 @@
*/
package org.hyperledger.besu.ethereum.trie;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
/**
* This exception is thrown when there is an issue retrieving or decoding values from {@link
* MerkleStorage}.
*/
public class MerkleTrieException extends RuntimeException {
private Bytes32 hash;
private Bytes location;
public MerkleTrieException(final String message) {
super(message);
}
public MerkleTrieException(final String message, final Bytes32 hash, final Bytes location) {
super(message);
this.hash = hash;
this.location = location;
}
public MerkleTrieException(final String message, final Exception cause) {
super(message, cause);
}
public Bytes32 getHash() {
return hash;
}
public Bytes getLocation() {
return location;
}
}

@ -123,7 +123,9 @@ class StoredNode<V> implements Node<V> {
"Unable to load trie node value for hash "
+ hash
+ " location "
+ location));
+ location,
hash,
location));
}
return loaded;

Loading…
Cancel
Save