SnapProtocolManager.java
/*
* 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.ProtocolContext;
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.sync.snapsync.SnapSyncConfiguration;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
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.WorldStateStorageCoordinator;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SnapProtocolManager implements ProtocolManager {
private static final Logger LOG = LoggerFactory.getLogger(SnapProtocolManager.class);
private final List<Capability> supportedCapabilities;
private final EthPeers ethPeers;
private final EthMessages snapMessages;
public SnapProtocolManager(
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final SnapSyncConfiguration snapConfig,
final EthPeers ethPeers,
final EthMessages snapMessages,
final ProtocolContext protocolContext) {
this.ethPeers = ethPeers;
this.snapMessages = snapMessages;
this.supportedCapabilities = calculateCapabilities();
new SnapServer(snapConfig, snapMessages, worldStateStorageCoordinator, protocolContext);
}
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: {}",
ethMessage.getData().getCode(),
ethPeer);
ethPeer.disconnect(DisconnectReason.BREACH_OF_PROTOCOL_UNSOLICITED_MESSAGE_RECEIVED);
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_MALFORMED_MESSAGE_RECEIVED);
}
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) {}
@Override
public boolean shouldConnect(final Peer peer, final boolean incoming) {
return false; // EthManager is taking care of this for now
}
@Override
public void handleDisconnect(
final PeerConnection connection,
final DisconnectReason reason,
final boolean initiatedByPeer) {}
@Override
public int getHighestProtocolVersion() {
return getSupportedCapabilities().stream()
.max(Comparator.comparing(Capability::getVersion))
.map(Capability::getVersion)
.orElse(0);
}
}