Skip to content

Commit

Permalink
Implement eth/68 (EIP-5793) (#4730)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel-Trintinalia authored Dec 6, 2022
1 parent 64a5d74 commit c9fba12
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 45 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog
## 22.10.3

### Additions and Improvements
- Implement Eth/68 sub-protocol [#4715](https://github.com/hyperledger/besu/issues/4715)

### Breaking Changes
- Added `--rpc-max-logs-range` CLI option to allow limiting the number of blocks queried by `eth_getLogs` RPC API. Default value: 1000 [#4597](https://github.com/hyperledger/besu/pull/4597)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class EthProtocol implements SubProtocol {
public static final Capability ETH65 = Capability.create(NAME, EthProtocolVersion.V65);
public static final Capability ETH66 = Capability.create(NAME, EthProtocolVersion.V66);
public static final Capability ETH67 = Capability.create(NAME, EthProtocolVersion.V67);
public static final Capability ETH68 = Capability.create(NAME, EthProtocolVersion.V68);

public static boolean requestIdCompatible(final int code) {
return Set.of(
Expand Down Expand Up @@ -67,6 +68,7 @@ public int messageSpace(final int protocolVersion) {
case EthProtocolVersion.V65:
case EthProtocolVersion.V66:
case EthProtocolVersion.V67:
case EthProtocolVersion.V68:
// same number of messages in each range, eth65 defines messages in the middle of the
// range defined by eth63 and eth64 defines no new ranges.
return 17;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class EthProtocolVersion {
public static final int V65 = 65;
public static final int V66 = 66;
public static final int V67 = 67;
public static final int V68 = 68;

/** eth/62 (2015) */
private static final List<Integer> eth62Messages =
Expand Down Expand Up @@ -128,6 +129,7 @@ public static List<Integer> getSupportedMessages(final int protocolVersion) {
case EthProtocolVersion.V66:
return eth65Messages;
case EthProtocolVersion.V67:
case EthProtocolVersion.V68:
return eth67Messages;
default:
return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.encoding;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.eth.EthProtocolVersion;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAnnouncement;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.plugin.data.TransactionType;

import java.util.List;
import java.util.stream.Collectors;

public class TransactionAnnouncementDecoder {

@FunctionalInterface
public interface Decoder {
List<TransactionAnnouncement> decode(RLPInput input);
}

/**
* Returns the correct decoder given an Eth Capability
*
* <p>See <a href="https://eips.ethereum.org/EIPS/eip-5793">EIP-5793</a>
*
* @param capability the version of the eth protocol
* @return the correct decoder
*/
public static Decoder getDecoder(final Capability capability) {
if (capability.getVersion() >= EthProtocolVersion.V68) {
return TransactionAnnouncementDecoder::decodeForEth68;
} else {
return TransactionAnnouncementDecoder::decodeForEth66;
}
}
/**
* Decode the list of transactions in the NewPooledTransactionHashesMessage
*
* @param input input used to decode the NewPooledTransactionHashesMessage before Eth/68
* <p>format: [hash_0: B_32, hash_1: B_32, ...]
* @return the list of TransactionAnnouncement decoded from the message. Only hash is present.
* size and type will return an Optional.empty()
*/
private static List<TransactionAnnouncement> decodeForEth66(final RLPInput input) {
final List<Hash> hashes = input.readList(rlp -> Hash.wrap(rlp.readBytes32()));
return hashes.stream().map(TransactionAnnouncement::new).collect(Collectors.toList());
}

/**
* Decode the list of transactions in the NewPooledTransactionHashesMessage
*
* @param input input used to decode the NewPooledTransactionHashesMessage after Eth/68
* <p>format: [[type_0: B_1, type_1: B_1, ...], [size_0: B_4, size_1: B_4, ...], ...]
* @return the list of TransactionAnnouncement decoded from the message with size, type and hash
*/
private static List<TransactionAnnouncement> decodeForEth68(final RLPInput input) {
input.enterList();
final List<TransactionType> types =
input.readList(
rlp -> {
final int type = rlp.readByte() & 0xff;
return type == 0 ? TransactionType.FRONTIER : TransactionType.of(type);
});
final List<Long> sizes = input.readList(RLPInput::readUnsignedInt);
final List<Hash> hashes = input.readList(rlp -> Hash.wrap(rlp.readBytes32()));
input.leaveList();
if (!(types.size() == hashes.size() && hashes.size() == sizes.size())) {
throw new RLPException("Hashes, sizes and types must have the same number of elements");
}
return TransactionAnnouncement.create(types, sizes, hashes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.encoding;

import static org.hyperledger.besu.ethereum.core.Transaction.toHashList;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.EthProtocolVersion;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.plugin.data.TransactionType;

import java.util.ArrayList;
import java.util.List;

import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;

public class TransactionAnnouncementEncoder {

@FunctionalInterface
public interface Encoder {
Bytes encode(List<Transaction> transaction);
}

/**
* Returns the correct encoder given an Eth Capability
*
* <p>See <a href="https://eips.ethereum.org/EIPS/eip-5793">EIP-5793</a>
*
* @param capability the version of the eth protocol
* @return the correct encoder
*/
public static Encoder getEncoder(final Capability capability) {
if (capability.getVersion() >= EthProtocolVersion.V68) {
return TransactionAnnouncementEncoder::encodeForEth68;
} else {
return TransactionAnnouncementEncoder::encodeForEth66;
}
}

/**
* Encode a list of hashes for the NewPooledTransactionHashesMessage using the Eth/66
*
* <p>format: [hash_0: B_32, hash_1: B_32, ...]
*
* @param transactions the list to encode
* @return the encoded value. The message data will contain only the transaction hashes
*/
private static Bytes encodeForEth66(final List<Transaction> transactions) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeList(toHashList(transactions), (h, w) -> w.writeBytes(h));
return out.encoded();
}

/**
* Encode a list of transactions for the NewPooledTransactionHashesMessage using the Eth/68
*
* <p>format: [[type_0: B_1, type_1: B_1, ...], [size_0: B_4, size_1: B_4, ...], ...]
*
* @param transactions the list to encode
* @return the encoded value. The message data will contain hashes, types and sizes.
*/
private static Bytes encodeForEth68(final List<Transaction> transactions) {
final List<Integer> sizes = new ArrayList<>(transactions.size());
final List<TransactionType> types = new ArrayList<>(transactions.size());
final List<Hash> hashes = new ArrayList<>(transactions.size());
transactions.forEach(
transaction -> {
types.add(transaction.getType());
sizes.add(transaction.calculateSize());
hashes.add(transaction.getHash());
});

return encodeForEth68(types, sizes, hashes);
}

@VisibleForTesting
public static Bytes encodeForEth68(
final List<TransactionType> types, final List<Integer> sizes, final List<Hash> hashes) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
// Check if lists have the same size
if (!(types.size() == hashes.size() && hashes.size() == sizes.size())) {
throw new IllegalArgumentException(
"Hashes, sizes and types must have the same number of elements");
}
out.startList();
out.writeList(
types, (h, w) -> w.writeByte(h == TransactionType.FRONTIER ? 0x00 : h.getSerializedType()));
out.writeList(sizes, (h, w) -> w.writeInt(h));
out.writeList(hashes, (h, w) -> w.writeBytes(h));
out.endList();
return out.encoded();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,10 @@ private List<Capability> calculateCapabilities(

// Version 67 removes the GetNodeData and NodeData
// Fast sync depends on GetNodeData and NodeData
// Do not add eth/67 if fast sync is enabled
// see https://eips.ethereum.org/EIPS/eip-4938
if (!Objects.equals(SyncMode.FAST, synchronizerConfiguration.getSyncMode())) {
capabilities.add(EthProtocol.ETH67);
capabilities.add(EthProtocol.ETH68);
}

return capabilities.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,48 @@
*/
package org.hyperledger.besu.ethereum.eth.messages;

import static org.hyperledger.besu.ethereum.eth.encoding.TransactionAnnouncementDecoder.getDecoder;
import static org.hyperledger.besu.ethereum.eth.encoding.TransactionAnnouncementEncoder.getEncoder;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAnnouncement;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
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.RLP;

import java.util.List;
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;

public final class NewPooledTransactionHashesMessage extends AbstractMessageData {

public class NewPooledTransactionHashesMessage extends AbstractMessageData {
private static final int MESSAGE_CODE = EthPV65.NEW_POOLED_TRANSACTION_HASHES;
private List<Hash> pendingTransactions;
private List<TransactionAnnouncement> pendingTransactions;
private final Capability capability;

NewPooledTransactionHashesMessage(final Bytes rlp) {
@VisibleForTesting
public NewPooledTransactionHashesMessage(final Bytes rlp, final Capability capability) {
super(rlp);
this.capability = capability;
}

@Override
public int getCode() {
return MESSAGE_CODE;
}

public static NewPooledTransactionHashesMessage create(final List<Hash> pendingTransactions) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeList(pendingTransactions, (h, w) -> w.writeBytes(h));
return new NewPooledTransactionHashesMessage(out.encoded());
public static NewPooledTransactionHashesMessage create(
final List<Transaction> pendingTransactions, final Capability capability) {
return new NewPooledTransactionHashesMessage(
getEncoder(capability).encode(pendingTransactions), capability);
}

public static NewPooledTransactionHashesMessage readFrom(final MessageData message) {
public static NewPooledTransactionHashesMessage readFrom(
final MessageData message, final Capability capability) {

if (message instanceof NewPooledTransactionHashesMessage) {
return (NewPooledTransactionHashesMessage) message;
}
Expand All @@ -54,15 +65,19 @@ public static NewPooledTransactionHashesMessage readFrom(final MessageData messa
String.format(
"Message has code %d and thus is not a NewPooledTransactionHashesMessage.", code));
}

return new NewPooledTransactionHashesMessage(message.getData());
return new NewPooledTransactionHashesMessage(message.getData(), capability);
}

public List<Hash> pendingTransactions() {
public List<TransactionAnnouncement> pendingTransactions() {
if (pendingTransactions == null) {
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
pendingTransactions = in.readList(rlp -> Hash.wrap(rlp.readBytes32()));
pendingTransactions = getDecoder(capability).decode(RLP.input(data));
}
return pendingTransactions;
}

public List<Hash> pendingTransactionHashes() {
return pendingTransactions().stream()
.map(TransactionAnnouncement::getHash)
.collect(Collectors.toUnmodifiableList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

import static java.time.Instant.now;

import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.manager.EthMessage;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -41,8 +43,9 @@ public NewPooledTransactionHashesMessageHandler(

@Override
public void exec(final EthMessage message) {
final Capability capability = message.getPeer().getConnection().capability(EthProtocol.NAME);
final NewPooledTransactionHashesMessage transactionsMessage =
NewPooledTransactionHashesMessage.readFrom(message.getData());
NewPooledTransactionHashesMessage.readFrom(message.getData(), capability);
final Instant startedAt = now();
scheduler.scheduleTxWorkerTask(
() ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void processNewPooledTransactionHashesMessage(
private void processNewPooledTransactionHashesMessage(
final EthPeer peer, final NewPooledTransactionHashesMessage transactionsMessage) {
try {
final List<Hash> incomingTransactionHashes = transactionsMessage.pendingTransactions();
final List<Hash> incomingTransactionHashes = transactionsMessage.pendingTransactionHashes();

traceLambda(
LOG,
Expand Down
Loading

0 comments on commit c9fba12

Please sign in to comment.