mirror of https://github.com/hyperledger/besu
added compatibility with the snap protocol (#3213)
Second PR to add compatibility with snap protocol messages. Signed-off-by: Karim TAAM <karim.t2am@gmail.com>pull/3232/head
parent
a890e427ca
commit
97bad4a7b7
@ -0,0 +1,99 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.eth; |
||||
|
||||
import org.hyperledger.besu.ethereum.eth.messages.snap.SnapV1; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Snap protocol messages as defined in https://github.com/ethereum/devp2p/blob/master/caps/snap.md}
|
||||
*/ |
||||
public class SnapProtocol implements SubProtocol { |
||||
public static final String NAME = "snap"; |
||||
public static final Capability SNAP1 = Capability.create(NAME, SnapVersion.V1); |
||||
|
||||
private static final SnapProtocol INSTANCE = new SnapProtocol(); |
||||
|
||||
private static final List<Integer> snap1Messages = |
||||
List.of( |
||||
SnapV1.GET_ACCOUNT_RANGE, |
||||
SnapV1.ACCOUNT_RANGE, |
||||
SnapV1.GET_STORAGE_RANGE, |
||||
SnapV1.STORAGE_RANGE, |
||||
SnapV1.GET_BYTECODES, |
||||
SnapV1.BYTECODES, |
||||
SnapV1.GET_TRIE_NODES, |
||||
SnapV1.TRIE_NODES); |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return NAME; |
||||
} |
||||
|
||||
@Override |
||||
public int messageSpace(final int protocolVersion) { |
||||
switch (protocolVersion) { |
||||
case SnapVersion.V1: |
||||
return 17; |
||||
default: |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValidMessageCode(final int protocolVersion, final int code) { |
||||
switch (protocolVersion) { |
||||
case SnapVersion.V1: |
||||
return snap1Messages.contains(code); |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String messageName(final int protocolVersion, final int code) { |
||||
switch (code) { |
||||
case SnapV1.GET_ACCOUNT_RANGE: |
||||
return "GetAccountRange"; |
||||
case SnapV1.ACCOUNT_RANGE: |
||||
return "AccountRange"; |
||||
case SnapV1.GET_STORAGE_RANGE: |
||||
return "GetStorageRange"; |
||||
case SnapV1.STORAGE_RANGE: |
||||
return "StorageRange"; |
||||
case SnapV1.GET_BYTECODES: |
||||
return "GetBytecodes"; |
||||
case SnapV1.BYTECODES: |
||||
return "Bytecodes"; |
||||
case SnapV1.GET_TRIE_NODES: |
||||
return "GetTrieNodes"; |
||||
case SnapV1.TRIE_NODES: |
||||
return "TrieNodes"; |
||||
default: |
||||
return INVALID_MESSAGE_NAME; |
||||
} |
||||
} |
||||
|
||||
public static SnapProtocol get() { |
||||
return INSTANCE; |
||||
} |
||||
|
||||
public static class SnapVersion { |
||||
public static final int V1 = 1; |
||||
} |
||||
} |
@ -1,48 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.eth.manager; |
||||
|
||||
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 java.math.BigInteger; |
||||
import java.util.AbstractMap; |
||||
import java.util.Map; |
||||
|
||||
public class RequestId { |
||||
public static MessageData wrapMessageData( |
||||
final BigInteger requestId, final MessageData messageData) { |
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); |
||||
rlpOutput.startList(); |
||||
rlpOutput.writeBigIntegerScalar(requestId); |
||||
rlpOutput.writeRaw(messageData.getData()); |
||||
rlpOutput.endList(); |
||||
return new RawMessage(messageData.getCode(), rlpOutput.encoded()); |
||||
} |
||||
|
||||
static Map.Entry<BigInteger, MessageData> unwrapMessageData(final MessageData messageData) { |
||||
final RLPInput messageDataRLP = RLP.input(messageData.getData()); |
||||
messageDataRLP.enterList(); |
||||
final BigInteger requestId = messageDataRLP.readBigIntegerScalar(); |
||||
final RLPInput unwrappedMessageRLP = messageDataRLP.readAsRlp(); |
||||
messageDataRLP.leaveList(); |
||||
|
||||
return new AbstractMap.SimpleImmutableEntry<>( |
||||
requestId, new RawMessage(messageData.getCode(), unwrappedMessageRLP.raw())); |
||||
} |
||||
} |
@ -0,0 +1,156 @@ |
||||
/* |
||||
* 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.eth.SnapProtocol; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessage; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; |
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers; |
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; |
||||
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractSnapMessageData; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Message; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; |
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPException; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import com.google.common.collect.ImmutableList; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class SnapProtocolManager implements ProtocolManager { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private final List<PeerValidator> peerValidators; |
||||
private final List<Capability> supportedCapabilities; |
||||
private final EthPeers ethPeers; |
||||
private final EthMessages snapMessages; |
||||
|
||||
public SnapProtocolManager( |
||||
final List<PeerValidator> peerValidators, |
||||
final EthPeers ethPeers, |
||||
final EthMessages snapMessages, |
||||
final WorldStateArchive worldStateArchive) { |
||||
this.peerValidators = peerValidators; |
||||
this.ethPeers = ethPeers; |
||||
this.snapMessages = snapMessages; |
||||
this.supportedCapabilities = calculateCapabilities(); |
||||
new SnapServer(snapMessages, worldStateArchive); |
||||
} |
||||
|
||||
private List<Capability> calculateCapabilities() { |
||||
final ImmutableList.Builder<Capability> capabilities = ImmutableList.builder(); |
||||
capabilities.add(SnapProtocol.SNAP1); |
||||
|
||||
return capabilities.build(); |
||||
} |
||||
|
||||
@Override |
||||
public String getSupportedProtocol() { |
||||
return SnapProtocol.NAME; |
||||
} |
||||
|
||||
@Override |
||||
public List<Capability> getSupportedCapabilities() { |
||||
return supportedCapabilities; |
||||
} |
||||
|
||||
@Override |
||||
public void stop() {} |
||||
|
||||
@Override |
||||
public void awaitStop() throws InterruptedException {} |
||||
|
||||
/** |
||||
* This function is called by the P2P framework when an "SNAP message has been received. |
||||
* |
||||
* @param cap The capability under which the message was transmitted. |
||||
* @param message The message to be decoded. |
||||
*/ |
||||
@Override |
||||
public void processMessage(final Capability cap, final Message message) { |
||||
final MessageData messageData = AbstractSnapMessageData.create(message); |
||||
final int code = messageData.getCode(); |
||||
LOG.trace("Process snap message {}, {}", cap, code); |
||||
final EthPeer ethPeer = ethPeers.peer(message.getConnection()); |
||||
if (ethPeer == null) { |
||||
LOG.debug( |
||||
"Ignoring message received from unknown peer connection: " + message.getConnection()); |
||||
return; |
||||
} |
||||
final EthMessage ethMessage = new EthMessage(ethPeer, messageData); |
||||
if (!ethPeer.validateReceivedMessage(ethMessage, getSupportedProtocol())) { |
||||
LOG.debug("Unsolicited message received from, disconnecting: {}", ethPeer); |
||||
ethPeer.disconnect(DisconnectReason.BREACH_OF_PROTOCOL); |
||||
return; |
||||
} |
||||
|
||||
// This will handle responses
|
||||
ethPeers.dispatchMessage(ethPeer, ethMessage, getSupportedProtocol()); |
||||
|
||||
// This will handle requests
|
||||
Optional<MessageData> maybeResponseData = Optional.empty(); |
||||
try { |
||||
final Map.Entry<BigInteger, MessageData> requestIdAndEthMessage = |
||||
ethMessage.getData().unwrapMessageData(); |
||||
maybeResponseData = |
||||
snapMessages |
||||
.dispatch(new EthMessage(ethPeer, requestIdAndEthMessage.getValue())) |
||||
.map(responseData -> responseData.wrapMessageData(requestIdAndEthMessage.getKey())); |
||||
} catch (final RLPException e) { |
||||
LOG.debug( |
||||
"Received malformed message {} , disconnecting: {}", messageData.getData(), ethPeer, e); |
||||
ethPeer.disconnect(DisconnectReason.BREACH_OF_PROTOCOL); |
||||
} |
||||
maybeResponseData.ifPresent( |
||||
responseData -> { |
||||
try { |
||||
ethPeer.send(responseData, getSupportedProtocol()); |
||||
} catch (final PeerConnection.PeerNotConnected error) { |
||||
// Peer disconnected before we could respond - nothing to do
|
||||
LOG.trace( |
||||
"Peer disconnected before we could respond - nothing to do " + error.getMessage()); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
public void handleNewConnection(final PeerConnection connection) { |
||||
ethPeers.registerConnection(connection, peerValidators); |
||||
} |
||||
|
||||
@Override |
||||
public void handleDisconnect( |
||||
final PeerConnection connection, |
||||
final DisconnectReason reason, |
||||
final boolean initiatedByPeer) { |
||||
ethPeers.registerDisconnect(connection); |
||||
LOG.debug( |
||||
"Disconnect - {} - {} - {} - {} peers left", |
||||
initiatedByPeer ? "Inbound" : "Outbound", |
||||
reason, |
||||
connection.getPeerInfo(), |
||||
ethPeers.peerCount()); |
||||
} |
||||
} |
@ -0,0 +1,79 @@ |
||||
/* |
||||
* 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.eth.manager.EthMessages; |
||||
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.p2p.rlpx.wire.MessageData; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.util.HashMap; |
||||
|
||||
import kotlin.collections.ArrayDeque; |
||||
|
||||
@SuppressWarnings("unused") |
||||
class SnapServer { |
||||
|
||||
private final EthMessages snapMessages; |
||||
private final WorldStateArchive worldStateArchive; |
||||
|
||||
SnapServer(final EthMessages snapMessages, final WorldStateArchive worldStateArchive) { |
||||
this.snapMessages = snapMessages; |
||||
this.worldStateArchive = worldStateArchive; |
||||
this.registerResponseConstructors(); |
||||
} |
||||
|
||||
private void registerResponseConstructors() { |
||||
snapMessages.registerResponseConstructor( |
||||
SnapV1.GET_ACCOUNT_RANGE, |
||||
messageData -> constructGetAccountRangeResponse(worldStateArchive, messageData)); |
||||
snapMessages.registerResponseConstructor( |
||||
SnapV1.GET_STORAGE_RANGE, |
||||
messageData -> constructGetStorageRangeResponse(worldStateArchive, messageData)); |
||||
snapMessages.registerResponseConstructor( |
||||
SnapV1.GET_BYTECODES, |
||||
messageData -> constructGetBytecodesResponse(worldStateArchive, messageData)); |
||||
snapMessages.registerResponseConstructor( |
||||
SnapV1.GET_TRIE_NODES, |
||||
messageData -> constructGetTrieNodesResponse(worldStateArchive, messageData)); |
||||
} |
||||
|
||||
private MessageData constructGetAccountRangeResponse( |
||||
final WorldStateArchive worldStateArchive, final MessageData message) { |
||||
// TODO implement
|
||||
return AccountRangeMessage.create(new HashMap<>(), new ArrayDeque<>()); |
||||
} |
||||
|
||||
private MessageData constructGetStorageRangeResponse( |
||||
final WorldStateArchive worldStateArchive, final MessageData message) { |
||||
// TODO implement
|
||||
return StorageRangeMessage.create(new ArrayDeque<>(), new ArrayDeque<>()); |
||||
} |
||||
|
||||
private MessageData constructGetBytecodesResponse( |
||||
final WorldStateArchive worldStateArchive, final MessageData message) { |
||||
// TODO implement
|
||||
return ByteCodesMessage.create(new ArrayDeque<>()); |
||||
} |
||||
|
||||
private MessageData constructGetTrieNodesResponse( |
||||
final WorldStateArchive worldStateArchive, final MessageData message) { |
||||
return TrieNodes.create(new ArrayDeque<>()); |
||||
} |
||||
} |
@ -0,0 +1,139 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
import java.util.TreeMap; |
||||
|
||||
import com.google.common.collect.Maps; |
||||
import kotlin.collections.ArrayDeque; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class AccountRangeMessage extends AbstractSnapMessageData { |
||||
|
||||
public static AccountRangeMessage readFrom(final MessageData message) { |
||||
if (message instanceof AccountRangeMessage) { |
||||
return (AccountRangeMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.ACCOUNT_RANGE) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a AccountRangeMessage.", code)); |
||||
} |
||||
return new AccountRangeMessage(message.getData()); |
||||
} |
||||
|
||||
public static AccountRangeMessage create( |
||||
final Map<Bytes32, Bytes> accounts, final ArrayDeque<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 BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeList( |
||||
accounts.entrySet(), |
||||
(entry, rlpOutput) -> { |
||||
rlpOutput.startList(); |
||||
rlpOutput.writeBytes(entry.getKey()); |
||||
rlpOutput.writeRLPBytes(entry.getValue()); |
||||
rlpOutput.endList(); |
||||
}); |
||||
tmp.writeList(proof, (bytes, rlpOutput) -> rlpOutput.writeBytes(bytes)); |
||||
tmp.endList(); |
||||
return new AccountRangeMessage(tmp.encoded()); |
||||
} |
||||
|
||||
public AccountRangeMessage(final Bytes data) { |
||||
super(data); |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final AccountRangeData accountData = accountData(false); |
||||
return create(Optional.of(requestId), accountData.accounts(), accountData.proofs()).getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.ACCOUNT_RANGE; |
||||
} |
||||
|
||||
public AccountRangeData accountData(final boolean withRequestId) { |
||||
final TreeMap<Bytes32, Bytes> accounts = new TreeMap<>(); |
||||
final ArrayDeque<Bytes> proofs = new ArrayDeque<>(); |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
|
||||
if (withRequestId) input.skipNext(); |
||||
|
||||
input |
||||
.readList( |
||||
rlpInput -> { |
||||
rlpInput.enterList(); |
||||
Map.Entry<Bytes32, Bytes> entry = |
||||
Maps.immutableEntry(rlpInput.readBytes32(), toFullAccount(rlpInput.readAsRlp())); |
||||
rlpInput.leaveList(); |
||||
return entry; |
||||
}) |
||||
.forEach(entry -> accounts.put(entry.getKey(), entry.getValue())); |
||||
|
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
proofs.add(input.readBytes()); |
||||
} |
||||
input.leaveList(); |
||||
|
||||
input.leaveList(); |
||||
return ImmutableAccountRangeData.builder().accounts(accounts).proofs(proofs).build(); |
||||
} |
||||
|
||||
private Bytes toFullAccount(final RLPInput rlpInput) { |
||||
final StateTrieAccountValue accountValue = StateTrieAccountValue.readFrom(rlpInput); |
||||
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); |
||||
rlpOutput.startList(); |
||||
rlpOutput.writeLongScalar(accountValue.getNonce()); // nonce
|
||||
rlpOutput.writeUInt256Scalar(accountValue.getBalance()); // balance
|
||||
rlpOutput.writeBytes(accountValue.getStorageRoot()); |
||||
rlpOutput.writeBytes(accountValue.getCodeHash()); |
||||
rlpOutput.endList(); |
||||
|
||||
return rlpOutput.encoded(); |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface AccountRangeData { |
||||
|
||||
TreeMap<Bytes32, Bytes> accounts(); |
||||
|
||||
ArrayDeque<Bytes> proofs(); |
||||
} |
||||
} |
@ -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.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.rlp.BytesValueRLPInput; |
||||
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 kotlin.collections.ArrayDeque; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class ByteCodesMessage extends AbstractSnapMessageData { |
||||
|
||||
public static ByteCodesMessage readFrom(final MessageData message) { |
||||
if (message instanceof ByteCodesMessage) { |
||||
return (ByteCodesMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.BYTECODES) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a ByteCodesMessage.", code)); |
||||
} |
||||
return new ByteCodesMessage(message.getData()); |
||||
} |
||||
|
||||
public static ByteCodesMessage create(final List<Bytes> codes) { |
||||
return create(Optional.empty(), codes); |
||||
} |
||||
|
||||
public static ByteCodesMessage create( |
||||
final Optional<BigInteger> requestId, final List<Bytes> codes) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeList(codes, (code, rlpOutput) -> rlpOutput.writeBytes(code)); |
||||
tmp.endList(); |
||||
return new ByteCodesMessage(tmp.encoded()); |
||||
} |
||||
|
||||
public ByteCodesMessage(final Bytes data) { |
||||
super(data); |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final ByteCodes bytecodes = bytecodes(false); |
||||
return create(Optional.of(requestId), bytecodes.codes()).getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.BYTECODES; |
||||
} |
||||
|
||||
public ByteCodes bytecodes(final boolean withRequestId) { |
||||
final ArrayDeque<Bytes> codes = new ArrayDeque<>(); |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
codes.add(input.readBytes()); |
||||
} |
||||
input.leaveList(); |
||||
input.leaveList(); |
||||
return ImmutableByteCodes.builder().codes(codes).build(); |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface ByteCodes { |
||||
|
||||
ArrayDeque<Bytes> codes(); |
||||
} |
||||
} |
@ -0,0 +1,114 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.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; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
|
||||
import java.math.BigInteger; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class GetAccountRangeMessage extends AbstractSnapMessageData { |
||||
|
||||
public static GetAccountRangeMessage readFrom(final MessageData message) { |
||||
if (message instanceof GetAccountRangeMessage) { |
||||
return (GetAccountRangeMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.GET_ACCOUNT_RANGE) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a GetAccountRangeMessage.", code)); |
||||
} |
||||
return new GetAccountRangeMessage(message.getData()); |
||||
} |
||||
|
||||
public static GetAccountRangeMessage create( |
||||
final Hash worldStateRootHash, |
||||
final Bytes32 startKeyHash, |
||||
final Bytes32 endKeyHash, |
||||
final BigInteger responseBytes) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
tmp.writeBytes(worldStateRootHash); |
||||
tmp.writeBytes(startKeyHash); |
||||
tmp.writeBytes(endKeyHash); |
||||
tmp.writeBigIntegerScalar(responseBytes); |
||||
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); |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
tmp.writeBigIntegerScalar(requestId); |
||||
tmp.writeBytes(range.worldStateRootHash()); |
||||
tmp.writeBytes(range.startKeyHash()); |
||||
tmp.writeBytes(range.endKeyHash()); |
||||
tmp.writeBigIntegerScalar(range.responseBytes()); |
||||
tmp.endList(); |
||||
return tmp.encoded(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.GET_ACCOUNT_RANGE; |
||||
} |
||||
|
||||
public Range range(final boolean withRequestId) { |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
final Hash worldStateRootHash = Hash.wrap(Bytes32.wrap(input.readBytes32())); |
||||
final ImmutableRange range = |
||||
ImmutableRange.builder() |
||||
.worldStateRootHash(getOverrideStateRoot().orElse(worldStateRootHash)) |
||||
.startKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32()))) |
||||
.endKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32()))) |
||||
.responseBytes(input.readBigIntegerScalar()) |
||||
.build(); |
||||
input.leaveList(); |
||||
return range; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "GetAccountRangeMessage{" + "data=" + data + '}'; |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface Range { |
||||
|
||||
Hash worldStateRootHash(); |
||||
|
||||
Hash startKeyHash(); |
||||
|
||||
Hash endKeyHash(); |
||||
|
||||
BigInteger responseBytes(); |
||||
} |
||||
} |
@ -0,0 +1,112 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
|
||||
import java.math.BigInteger; |
||||
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 static GetByteCodesMessage readFrom(final MessageData message) { |
||||
if (message instanceof GetByteCodesMessage) { |
||||
return (GetByteCodesMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.GET_BYTECODES) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a GetByteCodesMessage.", code)); |
||||
} |
||||
return new GetByteCodesMessage(Optional.empty(), 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 Optional<BigInteger> requestId, |
||||
final Optional<ArrayDeque<Bytes32>> accountHashes, |
||||
final ArrayDeque<Bytes32> codeHashes, |
||||
final BigInteger responseBytes) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeList(codeHashes, (hash, rlpOutput) -> rlpOutput.writeBytes(hash)); |
||||
tmp.writeBigIntegerScalar(responseBytes); |
||||
tmp.endList(); |
||||
return new GetByteCodesMessage(accountHashes, tmp.encoded()); |
||||
} |
||||
|
||||
public GetByteCodesMessage(final Optional<ArrayDeque<Bytes32>> accountHashes, final Bytes data) { |
||||
super(data); |
||||
this.accountHashes = accountHashes; |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final CodeHashes request = codeHashes(false); |
||||
return create(Optional.of(requestId), accountHashes, request.hashes(), request.responseBytes()) |
||||
.getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.GET_BYTECODES; |
||||
} |
||||
|
||||
public CodeHashes codeHashes(final boolean withRequestId) { |
||||
final ArrayDeque<Bytes32> hashes = new ArrayDeque<>(); |
||||
final BigInteger responseBytes; |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
hashes.add(input.readBytes32()); |
||||
} |
||||
input.leaveList(); |
||||
responseBytes = input.readBigIntegerScalar(); |
||||
input.leaveList(); |
||||
return ImmutableCodeHashes.builder().hashes(hashes).responseBytes(responseBytes).build(); |
||||
} |
||||
|
||||
public Optional<ArrayDeque<Bytes32>> getAccountHashes() { |
||||
return accountHashes; |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface CodeHashes { |
||||
|
||||
ArrayDeque<Bytes32> hashes(); |
||||
|
||||
BigInteger responseBytes(); |
||||
} |
||||
} |
@ -0,0 +1,171 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.Optional; |
||||
import javax.annotation.Nullable; |
||||
|
||||
import kotlin.collections.ArrayDeque; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class GetStorageRangeMessage extends AbstractSnapMessageData { |
||||
|
||||
private final Optional<ArrayDeque<Bytes32>> storageRoots; |
||||
|
||||
public static GetStorageRangeMessage readFrom(final MessageData message) { |
||||
if (message instanceof GetStorageRangeMessage) { |
||||
return (GetStorageRangeMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.GET_STORAGE_RANGE) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a GetStorageRangeMessage.", code)); |
||||
} |
||||
return new GetStorageRangeMessage(message.getData()); |
||||
} |
||||
|
||||
public static GetStorageRangeMessage create( |
||||
final Hash worldStateRootHash, |
||||
final ArrayDeque<Bytes32> accountHashes, |
||||
final Optional<ArrayDeque<Bytes32>> storageRoots, |
||||
final Bytes32 startKeyHash, |
||||
final Bytes32 endKeyHash, |
||||
final BigInteger responseBytes) { |
||||
return create( |
||||
Optional.empty(), |
||||
worldStateRootHash, |
||||
accountHashes, |
||||
storageRoots, |
||||
startKeyHash, |
||||
endKeyHash, |
||||
responseBytes); |
||||
} |
||||
|
||||
public static GetStorageRangeMessage create( |
||||
final Optional<BigInteger> requestId, |
||||
final Hash worldStateRootHash, |
||||
final ArrayDeque<Bytes32> accountHashes, |
||||
final Optional<ArrayDeque<Bytes32>> storageRoots, |
||||
final Bytes32 startKeyHash, |
||||
final Bytes32 endKeyHash, |
||||
final BigInteger responseBytes) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeBytes(worldStateRootHash); |
||||
tmp.writeList(accountHashes, (hash, rlpOutput) -> rlpOutput.writeBytes(hash)); |
||||
tmp.writeBytes(startKeyHash); |
||||
tmp.writeBytes(endKeyHash); |
||||
tmp.writeBigIntegerScalar(responseBytes); |
||||
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; |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final StorageRange range = range(false); |
||||
return create( |
||||
Optional.of(requestId), |
||||
range.worldStateRootHash(), |
||||
range.hashes(), |
||||
storageRoots, |
||||
range.startKeyHash(), |
||||
range.endKeyHash(), |
||||
range.responseBytes()) |
||||
.getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.GET_STORAGE_RANGE; |
||||
} |
||||
|
||||
public StorageRange range(final boolean withRequestId) { |
||||
final ArrayDeque<Bytes32> hashes = new ArrayDeque<>(); |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
final Hash worldStateRootHash = Hash.wrap(Bytes32.wrap(input.readBytes32())); |
||||
final ImmutableStorageRange.Builder range = |
||||
ImmutableStorageRange.builder() |
||||
.worldStateRootHash(getOverrideStateRoot().orElse(worldStateRootHash)); |
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
hashes.add(input.readBytes32()); |
||||
} |
||||
range.hashes(hashes); |
||||
input.leaveList(); |
||||
|
||||
if (input.nextIsNull()) { |
||||
input.skipNext(); |
||||
range.startKeyHash(Hash.ZERO); |
||||
} else { |
||||
range.startKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32()))); |
||||
} |
||||
if (input.nextIsNull()) { |
||||
input.skipNext(); |
||||
range.endKeyHash(Hash.ZERO); |
||||
} else { |
||||
range.endKeyHash(Hash.wrap(Bytes32.wrap(input.readBytes32()))); |
||||
} |
||||
range.responseBytes(input.readBigIntegerScalar()); |
||||
input.leaveList(); |
||||
return range.build(); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "GetStorageRangeMessage{" + "storageRoots=" + storageRoots + ", data=" + data + '}'; |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface StorageRange { |
||||
|
||||
Hash worldStateRootHash(); |
||||
|
||||
ArrayDeque<Bytes32> hashes(); |
||||
|
||||
Hash startKeyHash(); |
||||
|
||||
@Nullable |
||||
Hash endKeyHash(); |
||||
|
||||
BigInteger responseBytes(); |
||||
} |
||||
} |
@ -0,0 +1,120 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
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 org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class GetTrieNodes extends AbstractSnapMessageData { |
||||
|
||||
public static GetTrieNodes readFrom(final MessageData message) { |
||||
if (message instanceof GetTrieNodes) { |
||||
return (GetTrieNodes) 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()); |
||||
} |
||||
|
||||
/*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 GetTrieNodes create( |
||||
final Optional<BigInteger> requestId, |
||||
final Hash worldStateRootHash, |
||||
final List<List<Bytes>> paths, |
||||
final BigInteger responseBytes) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeBytes(worldStateRootHash); |
||||
tmp.writeList( |
||||
paths, |
||||
(path, rlpOutput) -> |
||||
rlpOutput.writeList(path, (b, subRlpOutput) -> subRlpOutput.writeBytes(b))); |
||||
tmp.writeBigIntegerScalar(responseBytes); |
||||
tmp.endList(); |
||||
return new GetTrieNodes(tmp.encoded()); |
||||
} |
||||
|
||||
public GetTrieNodes(final Bytes data) { |
||||
super(data); |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final TrieNodesPaths paths = paths(false); |
||||
return create( |
||||
Optional.of(requestId), |
||||
paths.worldStateRootHash(), |
||||
paths.paths(), |
||||
paths.responseBytes()) |
||||
.getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.GET_TRIE_NODES; |
||||
} |
||||
|
||||
public TrieNodesPaths paths(final boolean withRequestId) { |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
final ImmutableTrieNodesPaths.Builder paths = |
||||
ImmutableTrieNodesPaths.builder() |
||||
.worldStateRootHash(Hash.wrap(Bytes32.wrap(input.readBytes32()))) |
||||
.paths(input.readList(rlpInput -> rlpInput.readList(RLPInput::readBytes))) |
||||
.responseBytes(input.readBigIntegerScalar()); |
||||
input.leaveList(); |
||||
return paths.build(); |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface TrieNodesPaths { |
||||
|
||||
Hash worldStateRootHash(); |
||||
|
||||
List<List<Bytes>> paths(); |
||||
|
||||
BigInteger responseBytes(); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
/* |
||||
* 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; |
||||
|
||||
public final class SnapV1 { |
||||
|
||||
public static final int GET_ACCOUNT_RANGE = 0x00; |
||||
public static final int ACCOUNT_RANGE = 0x01; |
||||
public static final int GET_STORAGE_RANGE = 0x02; |
||||
public static final int STORAGE_RANGE = 0x03; |
||||
public static final int GET_BYTECODES = 0x04; |
||||
public static final int BYTECODES = 0x05; |
||||
public static final int GET_TRIE_NODES = 0x06; |
||||
public static final int TRIE_NODES = 0x07; |
||||
|
||||
private SnapV1() { |
||||
// Holder for constants only
|
||||
} |
||||
} |
@ -0,0 +1,127 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
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 java.util.TreeMap; |
||||
|
||||
import kotlin.collections.ArrayDeque; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.immutables.value.Value; |
||||
|
||||
public final class StorageRangeMessage extends AbstractSnapMessageData { |
||||
|
||||
public static StorageRangeMessage readFrom(final MessageData message) { |
||||
if (message instanceof StorageRangeMessage) { |
||||
return (StorageRangeMessage) message; |
||||
} |
||||
final int code = message.getCode(); |
||||
if (code != SnapV1.STORAGE_RANGE) { |
||||
throw new IllegalArgumentException( |
||||
String.format("Message has code %d and thus is not a StorageRangeMessage.", code)); |
||||
} |
||||
return new StorageRangeMessage(message.getData()); |
||||
} |
||||
|
||||
public static StorageRangeMessage create( |
||||
final ArrayDeque<TreeMap<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 List<Bytes> proof) { |
||||
final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); |
||||
tmp.startList(); |
||||
requestId.ifPresent(tmp::writeBigIntegerScalar); |
||||
tmp.writeList( |
||||
slots, |
||||
(accountList, accountRlpOutput) -> |
||||
accountRlpOutput.writeList( |
||||
accountList.entrySet(), |
||||
(entry, slotRlpOutput) -> { |
||||
slotRlpOutput.startList(); |
||||
slotRlpOutput.writeBytes(entry.getKey()); |
||||
slotRlpOutput.writeBytes(entry.getValue()); |
||||
slotRlpOutput.endList(); |
||||
})); |
||||
tmp.writeList(proof, (bytes, rlpOutput) -> rlpOutput.writeBytes(bytes)); |
||||
tmp.endList(); |
||||
return new StorageRangeMessage(tmp.encoded()); |
||||
} |
||||
|
||||
public StorageRangeMessage(final Bytes data) { |
||||
super(data); |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final SlotRangeData slotsData = slotsData(false); |
||||
return create(Optional.of(requestId), slotsData.slots(), slotsData.proofs()).getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.STORAGE_RANGE; |
||||
} |
||||
|
||||
public SlotRangeData slotsData(final boolean withRequestId) { |
||||
final ArrayDeque<TreeMap<Bytes32, Bytes>> slots = new ArrayDeque<>(); |
||||
final ArrayDeque<Bytes> proofs = new ArrayDeque<>(); |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
|
||||
if (withRequestId) input.skipNext(); |
||||
|
||||
input.readList( |
||||
accountRlpInput -> { |
||||
slots.add(new TreeMap<>()); |
||||
return accountRlpInput.readList( |
||||
slotRlpInput -> { |
||||
slotRlpInput.enterList(); |
||||
slots.last().put(slotRlpInput.readBytes32(), slotRlpInput.readBytes()); |
||||
slotRlpInput.leaveList(); |
||||
return Void.TYPE; // we don't need the response
|
||||
}); |
||||
}); |
||||
|
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
proofs.add(input.readBytes()); |
||||
} |
||||
input.leaveList(); |
||||
|
||||
input.leaveList(); |
||||
return ImmutableSlotRangeData.builder().slots(slots).proofs(proofs).build(); |
||||
} |
||||
|
||||
@Value.Immutable |
||||
public interface SlotRangeData { |
||||
|
||||
ArrayDeque<TreeMap<Bytes32, Bytes>> slots(); |
||||
|
||||
ArrayDeque<Bytes> proofs(); |
||||
} |
||||
} |
@ -0,0 +1,85 @@ |
||||
/* |
||||
* 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.rlp.BytesValueRLPInput; |
||||
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 kotlin.collections.ArrayDeque; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public final class TrieNodes extends AbstractSnapMessageData { |
||||
|
||||
public static TrieNodes readFrom(final MessageData message) { |
||||
if (message instanceof TrieNodes) { |
||||
return (TrieNodes) 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()); |
||||
} |
||||
|
||||
public static TrieNodes create(final List<Bytes> nodes) { |
||||
return create(Optional.empty(), nodes); |
||||
} |
||||
|
||||
public static TrieNodes 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); |
||||
} |
||||
|
||||
@Override |
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
final List<Bytes> nodes = nodes(false); |
||||
return create(Optional.of(requestId), nodes).getData(); |
||||
} |
||||
|
||||
@Override |
||||
public int getCode() { |
||||
return SnapV1.TRIE_NODES; |
||||
} |
||||
|
||||
public ArrayDeque<Bytes> nodes(final boolean withRequestId) { |
||||
final ArrayDeque<Bytes> trieNodes = new ArrayDeque<>(); |
||||
final RLPInput input = new BytesValueRLPInput(data, false); |
||||
input.enterList(); |
||||
if (withRequestId) input.skipNext(); |
||||
input.enterList(); |
||||
while (!input.isEndOfCurrentList()) { |
||||
trieNodes.add(input.readBytes()); |
||||
} |
||||
input.leaveList(); |
||||
input.leaveList(); |
||||
return trieNodes; |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.p2p.rlpx.wire; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.rlp.RLP; |
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.AbstractMap; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public abstract class AbstractSnapMessageData extends AbstractMessageData { |
||||
|
||||
private Optional<Hash> overrideStateRoot; |
||||
|
||||
public AbstractSnapMessageData(final Bytes data) { |
||||
super(data); |
||||
overrideStateRoot = Optional.empty(); |
||||
} |
||||
|
||||
public Optional<Hash> getOverrideStateRoot() { |
||||
return overrideStateRoot; |
||||
} |
||||
|
||||
public void setOverrideStateRoot(final Optional<Hash> overrideStateRoot) { |
||||
this.overrideStateRoot = overrideStateRoot; |
||||
} |
||||
|
||||
@Override |
||||
public MessageData wrapMessageData(final BigInteger requestId) { |
||||
return new RawMessage(getCode(), wrap(requestId)); |
||||
} |
||||
|
||||
@Override |
||||
public Map.Entry<BigInteger, MessageData> unwrapMessageData() { |
||||
final RLPInput messageDataRLP = RLP.input(getData()); |
||||
messageDataRLP.enterList(); |
||||
final BigInteger requestId = messageDataRLP.readBigIntegerScalar(); |
||||
messageDataRLP.leaveListLenient(); |
||||
return new AbstractMap.SimpleImmutableEntry<>(requestId, new RawMessage(getCode(), getData())); |
||||
} |
||||
|
||||
protected Bytes wrap(final BigInteger requestId) { |
||||
throw new UnsupportedOperationException("cannot wrap this message"); |
||||
} |
||||
|
||||
public static MessageData create(final Message message) { |
||||
return new AbstractSnapMessageData(message.getData().getData()) { |
||||
@Override |
||||
public int getCode() { |
||||
return message.getData().getCode(); |
||||
} |
||||
}; |
||||
} |
||||
} |
Loading…
Reference in new issue