Skip to content

Commit

Permalink
SSZ - encoding (hyperledger#4979)
Browse files Browse the repository at this point in the history
* Adding preliminary SSZ encoding/decoding of Transaction Network Payload

* Adding ssz snapshot jar, delete it before moving to main

also removing licenses....

Signed-off-by: Jiri Peinlich <jiri.peinlich@gmail.com>
(cherry picked from commit bd147e0)
(cherry picked from commit 4e19926eb7b85fda7d40a0f565d46c0d9b27075d)
  • Loading branch information
gezero authored and jflo committed May 31, 2023
1 parent bcec730 commit 6ad7041
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ public class BlockBody implements org.hyperledger.besu.plugin.data.BlockBody {
new BlockBody(Collections.emptyList(), Collections.emptyList());
/**
* Adding a new field with a corresponding root hash in the block header will require a change in
* {@link org.hyperledger.besu.ethereum.eth.manager.task.GetBodiesFromPeerTask.BodyIdentifier}
* Also requires adding the new field to the constructor used in the {@link
* org.hyperledger.besu.ethereum.util.RawBlockIterator }
* {@link org.hyperledger.besu.ethereum.eth.manager.task.GetBodiesFromPeerTask.BodyIdentifier }
*/
private final List<Transaction> transactions;

Expand Down Expand Up @@ -102,35 +100,23 @@ public Optional<List<Deposit>> getDeposits() {
*
* @param output Output to write to
*/
public void writeWrappedBodyTo(final RLPOutput output) {
public void writeTo(final RLPOutput output) {
output.startList();
writeTo(output);
output.endList();
}

public void writeTo(final RLPOutput output) {
output.writeList(getTransactions(), TransactionEncoder::encodeOpaqueBytes);
output.writeList(getOmmers(), BlockHeader::writeTo);
withdrawals.ifPresent(withdrawals -> output.writeList(withdrawals, Withdrawal::writeTo));
deposits.ifPresent(deposits -> output.writeList(deposits, Deposit::writeTo));

output.endList();
}

public static BlockBody readWrappedBodyFrom(
public static BlockBody readFrom(
final RLPInput input, final BlockHeaderFunctions blockHeaderFunctions) {
return readWrappedBodyFrom(input, blockHeaderFunctions, false);
return readFrom(input, blockHeaderFunctions, false);
}

/**
* Read all fields from the block body expecting a list wrapping them An example of valid body
* structure that this method would be able to read is: [[txs],[ommers],[withdrawals]] This is
* used for decoding list of bodies
*
* @param input The RLP-encoded input
* @param blockHeaderFunctions The block header functions used for parsing block headers
* @param allowEmptyBody A flag indicating whether an empty body is allowed
* @return the decoded BlockBody from the RLP
*/
public static BlockBody readWrappedBodyFrom(
public static BlockBody readFrom(
final RLPInput input,
final BlockHeaderFunctions blockHeaderFunctions,
final boolean allowEmptyBody) {
Expand All @@ -140,33 +126,21 @@ public static BlockBody readWrappedBodyFrom(
input.leaveList();
return empty();
}
final BlockBody body = readFrom(input, blockHeaderFunctions);
// TODO: Support multiple hard fork transaction formats.
final BlockBody body =
new BlockBody(
input.readList(Transaction::readFrom),
input.readList(rlp -> BlockHeader.readFrom(rlp, blockHeaderFunctions)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(Withdrawal::readFrom)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(Deposit::readFrom)));
input.leaveList();
return body;
}

/**
* Read all fields from the block body expecting no list wrapping them. An example of a valid body
* would be: [txs],[ommers],[withdrawals],[deposits] this method is called directly when importing
* a single block
*
* @param input The RLP-encoded input
* @param blockHeaderFunctions The block header functions used for parsing block headers
* @return the BlockBody decoded from the RLP
*/
public static BlockBody readFrom(
final RLPInput input, final BlockHeaderFunctions blockHeaderFunctions) {
return new BlockBody(
input.readList(Transaction::readFrom),
input.readList(rlp -> BlockHeader.readFrom(rlp, blockHeaderFunctions)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(Withdrawal::readFrom)),
input.isEndOfCurrentList()
? Optional.empty()
: Optional.of(input.readList(Deposit::readFrom)));
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import com.google.common.primitives.Longs;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.ssz.SSZ;
import org.apache.tuweni.ssz.SSZFixedSizeTypeList;
import org.apache.tuweni.units.bigints.UInt256;
import org.apache.tuweni.units.bigints.UInt256s;
Expand Down Expand Up @@ -110,6 +109,7 @@ public class Transaction
private final TransactionType transactionType;

private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
private final Optional<Wei> maxFeePerData;
private final Optional<List<Hash>> versionedHashes;

private final Optional<BlobsWithCommitments> blobsWithCommitments;
Expand Down Expand Up @@ -164,8 +164,14 @@ public Transaction(
final Optional<List<AccessListEntry>> maybeAccessList,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<BigInteger> v,
final Optional<Wei> maxFeePerData,
final Optional<List<Hash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
if (v.isPresent() && chainId.isPresent()) {
throw new IllegalArgumentException(
String.format("chainId '%s' and v '%s' cannot both be provided", chainId.get(), v.get()));
}

if (transactionType.requiresChainId()) {
checkArgument(
Expand Down Expand Up @@ -212,6 +218,8 @@ public Transaction(
this.maybeAccessList = maybeAccessList;
this.sender = sender;
this.chainId = chainId;
this.v = v;
this.maxFeePerData = maxFeePerData;
this.versionedHashes = versionedHashes;
this.blobsWithCommitments = blobsWithCommitments;

Expand All @@ -233,6 +241,8 @@ public Transaction(
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<BigInteger> v,
final Optional<Wei> maxFeePerData,
final Optional<List<Hash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
Expand All @@ -250,6 +260,8 @@ public Transaction(
Optional.empty(),
sender,
chainId,
v,
maxFeePerData,
versionedHashes,
blobsWithCommitments);
}
Expand All @@ -263,6 +275,7 @@ public Transaction(
final SECPSignature signature,
final Bytes payload,
final Optional<BigInteger> chainId,
final Optional<BigInteger> v,
final Optional<List<Hash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
Expand All @@ -280,6 +293,8 @@ public Transaction(
Optional.empty(),
null,
chainId,
v,
Optional.empty(),
versionedHashes,
blobsWithCommitments);
}
Expand Down Expand Up @@ -310,22 +325,22 @@ public Transaction(
final SECPSignature signature,
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<List<Hash>> versionedHashes) {
final Optional<BigInteger> chainId) {
this(
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
to,
value,
signature,
payload,
sender,
chainId,
versionedHashes,
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty());
}

Expand All @@ -341,6 +356,7 @@ public Transaction(
* @param payload the payload
* @param sender the transaction sender
* @param chainId the chain id to apply the transaction to
* @param v the v value (only passed in directly for GoQuorum private transactions)
* <p>The {@code to} will be an {@code Optional.empty()} for a contract creation transaction;
* otherwise it should contain an address.
* <p>The {@code chainId} must be greater than 0 to be applied to a specific chain; otherwise
Expand All @@ -356,22 +372,25 @@ public Transaction(
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<Wei> maxFeePerDataGas,
final Optional<BigInteger> v,
final Optional<Wei> maxFeePerData,
final Optional<List<Hash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
this(
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
maxFeePerDataGas,
Optional.empty(),
gasLimit,
to,
value,
signature,
payload,
sender,
chainId,
v,
maxFeePerData,
versionedHashes,
blobsWithCommitments);
}
Expand Down Expand Up @@ -803,6 +822,10 @@ public TransactionType getType() {
return this.transactionType;
}

public Optional<Wei> getMaxFeePerData() {
return maxFeePerData;
}

public Optional<List<Hash>> getVersionedHashes() {
return versionedHashes;
}
Expand Down Expand Up @@ -1176,6 +1199,7 @@ public static class Builder {
protected Optional<BigInteger> chainId = Optional.empty();

protected List<Hash> versionedHashes = null;
private Wei maxFeePerData;
private BlobsWithCommitments blobsWithCommitments;

public Builder type(final TransactionType transactionType) {
Expand Down Expand Up @@ -1290,6 +1314,8 @@ public Transaction build() {
accessList,
sender,
chainId,
v,
Optional.ofNullable(maxFeePerData),
Optional.ofNullable(versionedHashes),
Optional.ofNullable(blobsWithCommitments));
}
Expand Down Expand Up @@ -1329,6 +1355,11 @@ public Builder kzgBlobs(
this.blobsWithCommitments = new BlobsWithCommitments(kzgCommitments, blobs, kzgProof);
return this;
}

public Builder maxFeePerData(final Wei maxFeePerData) {
this.maxFeePerData = maxFeePerData;
return this;
}
}

public static class BlobsWithCommitments {
Expand All @@ -1344,17 +1375,5 @@ public BlobsWithCommitments(
this.blobs = blobs;
this.kzgProof = kzgProof;
}

public List<Bytes> getBlobs() {
return blobs.getElements().stream()
.map(TransactionNetworkPayload.Blob::getBytes)
.collect(Collectors.toList());
}

public List<Bytes> getKzgCommitments() {
return kzgCommitments.getElements().stream()
.map(c -> c.getData())
.collect(Collectors.toList());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,11 @@
import org.apache.tuweni.ssz.SSZ;
import org.apache.tuweni.ssz.SSZReader;
import org.apache.tuweni.units.bigints.UInt32;
import org.slf4j.Logger;

public class TransactionDecoder {

private static final UInt32 BLOB_TRANSACTION_OFFSET = UInt32.fromHexString("0x3c000000");

private static final Logger LOG = getLogger(TransactionDecoder.class);

@FunctionalInterface
interface Decoder {
Transaction decode(final Bytes input);
Expand Down Expand Up @@ -91,15 +88,13 @@ public static Transaction decodeBlob(final SSZReader input, final UInt32 firstOf
TransactionNetworkPayload.SingedBlobTransaction signedBlobTransaction;

if (firstOffset.equals(BLOB_TRANSACTION_OFFSET)) {
LOG.trace("Decoding TransactionNetworkPayload");

TransactionNetworkPayload payload = new TransactionNetworkPayload();
payload.populateFromReader(input);
signedBlobTransaction = payload.getSignedBlobTransaction();

builder.kzgBlobs(payload.getKzgCommitments(), payload.getBlobs(), payload.getKzgProof());
} else {
LOG.trace("Decoding TransactionNetworkPayload.SingedBlobTransaction");
signedBlobTransaction = new TransactionNetworkPayload.SingedBlobTransaction();
signedBlobTransaction.populateFromReader(input);
}
Expand Down Expand Up @@ -130,7 +125,7 @@ public static Transaction decodeBlob(final SSZReader input, final UInt32 firstOf
signedBlobTransaction.getSignature().getR().toUnsignedBigInteger(),
signedBlobTransaction.getSignature().getS().toUnsignedBigInteger(),
signedBlobTransaction.getSignature().isParity() ? (byte) 1 : 0))
.maxFeePerDataGas(Wei.of(blobTransaction.getMaxFeePerDataGas()))
.maxFeePerData(Wei.of(blobTransaction.getMaxFeePerData()))
.versionedHashes(blobTransaction.getBlobVersionedHashes())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.apache.tuweni.ssz.SSZ;
import org.apache.tuweni.ssz.SSZWriter;
import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger;

public class TransactionEncoder {
private static final Logger LOG = getLogger(Encoder.class);
Expand Down Expand Up @@ -76,7 +75,6 @@ interface SSZEncoder {
TransactionType.BLOB, Encoder.sszEncoder(TransactionEncoder::encodeWithBlobs));

public static void encodeWithBlobs(final Transaction transaction, final SSZWriter rlpOutput) {
LOG.trace("Encoding transaction with blobs {}", transaction);
var payload = new TransactionNetworkPayload();
var blobsWithCommitments = transaction.getBlobsWithCommitments();
if (blobsWithCommitments.isPresent()) {
Expand All @@ -92,7 +90,6 @@ public static void encodeWithBlobs(final Transaction transaction, final SSZWrite
}

public static void encodeWithoutBlobs(final Transaction transaction, final SSZWriter rlpOutput) {
LOG.trace("Encoding transaction without blobs {}", transaction);
var signedBlobTransaction = new TransactionNetworkPayload.SingedBlobTransaction();
populatedSignedBlobTransaction(transaction, signedBlobTransaction);
signedBlobTransaction.writeTo(rlpOutput);
Expand Down Expand Up @@ -130,8 +127,7 @@ private static void populatedSignedBlobTransaction(
accessList.add(tuple);
});
});
blobTransaction.setMaxFeePerDataGas(
transaction.getMaxFeePerDataGas().orElseThrow().toUInt256());
blobTransaction.setMaxFeePerData(transaction.getMaxFeePerData().orElseThrow().toUInt256());
blobTransaction.setBlobVersionedHashes(transaction.getVersionedHashes().orElseThrow());
}

Expand Down
Loading

0 comments on commit 6ad7041

Please sign in to comment.