Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:spanbatch fulltx #90

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ public void setS(BigInteger s) {
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (SpanBatchSignature) obj;
return Objects.equals(this.v, that.v) && Objects.equals(this.r, that.r) && Objects.equals(this.s, that.s);
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SpanBatchSignature that)) return false;
return Objects.equals(v, that.v) && Objects.equals(r, that.r) && Objects.equals(s, that.s);
}

@Override
Expand All @@ -53,6 +52,6 @@ public int hashCode() {

@Override
public String toString() {
return "SpanBatchSignature[" + "v=" + v + ", " + "r=" + r + ", " + "s=" + s + ']';
return "SpanBatchSignature[v=%s, r=%s, s=%s]".formatted(v, r, s);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,23 @@

package io.optimism.utilities.derive.stages;

import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_PROTECTED_V_BASE;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_PROTECTED_V_MIN;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_UNPROTECTED_V_BASE;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_UNPROTECTED_V_BASE_PLUS_1;
import static org.hyperledger.besu.ethereum.core.Transaction.TWO;

import com.google.common.base.Suppliers;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.function.Supplier;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
Expand All @@ -31,6 +45,9 @@
* @since 0.2.4
*/
public class SpanBatchTx {

private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private final SpanBatchTxData spanBatchTxData;

/**
Expand Down Expand Up @@ -84,7 +101,7 @@ public static SpanBatchTx newSpanBatchTx(Transaction tx) {
* @return the span batch tx data
*/
public byte[] marshalBinary() {
if (spanBatchTxData.txType() == TransactionType.FRONTIER) {
if (TransactionType.FRONTIER == spanBatchTxData.txType()) {
SpanBatchLegacyTxData spanBatchLegacyTxData = (SpanBatchLegacyTxData) this.spanBatchTxData;
return spanBatchLegacyTxData.encode();
}
Expand All @@ -96,7 +113,7 @@ public byte[] marshalBinary() {
SpanBatchAccessListTxData spanBatchAccessListTxData = (SpanBatchAccessListTxData) this.spanBatchTxData;
return spanBatchAccessListTxData.encode();
}
return null;
throw new RuntimeException("invalid typed transaction type");
}

/**
Expand All @@ -105,23 +122,112 @@ public byte[] marshalBinary() {
* @param b the b
* @return the span batch tx data
*/
public static SpanBatchTxData unmarshalBinary(byte[] b) {
public static SpanBatchTx unmarshalBinary(byte[] b) {
if (b.length <= 1) {
throw new RuntimeException("typed transaction too short");
}
if ((b[0] & 0xFF) > 0x7F) {
RLPInput input = new BytesValueRLPInput(Bytes.wrap(b), false);
return SpanBatchLegacyTxData.decode(input);
return new SpanBatchTx(SpanBatchLegacyTxData.decode(input));
}

byte[] spanBatchData = ArrayUtils.subarray(b, 1, b.length);
RLPInput input = new BytesValueRLPInput(Bytes.wrap(spanBatchData), false);
if (TransactionType.ACCESS_LIST.getEthSerializedType() == b[0]) {
return SpanBatchAccessListTxData.decode(input);
return new SpanBatchTx(SpanBatchAccessListTxData.decode(input));
}
if (TransactionType.EIP1559.getEthSerializedType() == b[0]) {
return SpanBatchDynamicFeeTxData.decode(input);
return new SpanBatchTx(SpanBatchDynamicFeeTxData.decode(input));
}

throw new RuntimeException("invalid typed transaction type");
}

public SpanBatchTxData getSpanBatchTxData() {
return spanBatchTxData;
}

public Transaction convertToFullTx(
BigInteger nonce, BigInteger gas, String to, BigInteger chainId, BigInteger v, BigInteger r, BigInteger s) {

switch (txType()) {
case FRONTIER -> {
SpanBatchLegacyTxData spanBatchLegacyTxData = (SpanBatchLegacyTxData) this.spanBatchTxData;

var recIdAndProtectedTx = getRecId(chainId, v);
byte recId = recIdAndProtectedTx.getLeft();
boolean protectedTx = recIdAndProtectedTx.getRight();
final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, recId);

var builder = Transaction.builder()
.type(TransactionType.FRONTIER)
.nonce(nonce.longValue())
.gasPrice(spanBatchLegacyTxData.gasPrice())
.gasLimit(gas.longValue())
.to(to == null ? null : Address.fromHexString(to))
.value(spanBatchLegacyTxData.value())
.payload(spanBatchLegacyTxData.data())
.signature(signature);

if (protectedTx) {
builder.chainId(chainId);
}

return builder.build();
}
case EIP1559 -> {
SpanBatchDynamicFeeTxData spanBatchDynamicFeeTxData = (SpanBatchDynamicFeeTxData) this.spanBatchTxData;
final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, v.byteValueExact());

return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(nonce.longValue())
.maxPriorityFeePerGas(spanBatchDynamicFeeTxData.gasTipCap())
.maxFeePerGas(spanBatchDynamicFeeTxData.gasFeeCap())
.gasLimit(gas.longValue())
.to(to == null ? null : Address.fromHexString(to))
.value(spanBatchDynamicFeeTxData.value())
.payload(spanBatchDynamicFeeTxData.data())
.accessList(spanBatchDynamicFeeTxData.accessList())
.chainId(chainId)
.signature(signature)
.build();
}
case ACCESS_LIST -> {
SpanBatchAccessListTxData spanBatchAccessListTxData = (SpanBatchAccessListTxData) this.spanBatchTxData;
final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, v.byteValueExact());

return Transaction.builder()
.type(TransactionType.ACCESS_LIST)
.nonce(nonce.longValue())
.gasPrice(spanBatchAccessListTxData.gasPrice())
.gasLimit(gas.longValue())
.to(to == null ? null : Address.fromHexString(to))
.value(spanBatchAccessListTxData.value())
.payload(spanBatchAccessListTxData.data())
.accessList(spanBatchAccessListTxData.accessList())
.chainId(chainId)
.signature(signature)
.build();
}
case BLOB -> throw new RuntimeException("blob tx not supported");
default -> throw new IllegalStateException("unexpected value: %s".formatted(txType()));
}
}

private static Pair<Byte, Boolean> getRecId(BigInteger chainId, BigInteger v) {
byte recId;
boolean protectedTx;
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
protectedTx = false;
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
recId = v.subtract(TWO.multiply(chainId).add(REPLAY_PROTECTED_V_BASE))
.byteValueExact();
protectedTx = true;
} else {
throw new RuntimeException(String.format("An unsupported encoded `v` value of %s was found", v));
}
return null;
return Pair.of(recId, protectedTx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.web3j.utils.Numeric;
Expand Down Expand Up @@ -314,7 +315,33 @@ public void decode(ByteBuf buffer) {
decodeProtectedBits(buffer);
}

public static SpanBatchTxs newSpanBatchTxs(List<String> txs, BigInteger chainId) {
public List<byte[]> fullTxs(BigInteger chainId) {
List<byte[]> fullTxs = new ArrayList<>();
int toIdx = 0;
for (int i = 0; i < this.totalBlockTxCount; i++) {
SpanBatchTx spanBatchTx =
SpanBatchTx.unmarshalBinary(this.txDatas.get(i).toArrayUnsafe());
BigInteger nonce = this.txNonces.get(i);
BigInteger gas = this.txGases.get(i);
String to = null;
if (!this.contractCreationBits.testBit(i)) {
if (this.txTos.size() <= toIdx) {
throw new RuntimeException("tx to not enough");
}
to = this.txTos.get(toIdx);
toIdx++;
}
BigInteger v = this.txSigs.get(i).v();
BigInteger r = this.txSigs.get(i).r();
BigInteger s = this.txSigs.get(i).s();
Transaction tx = spanBatchTx.convertToFullTx(nonce, gas, to, chainId, v, r, s);
Bytes txBytes = TransactionEncoder.encodeOpaqueBytes(tx, EncodingContext.BLOCK_BODY);
fullTxs.add(txBytes.toArrayUnsafe());
}
return fullTxs;
}

public static SpanBatchTxs newSpanBatchTxs(List<byte[]> txs, BigInteger chainId) {
long totalBlockTxCount = txs.size();
BigInteger contractCreationBits = BigInteger.ZERO;
BigInteger yParityBits = BigInteger.ZERO;
Expand All @@ -327,8 +354,8 @@ public static SpanBatchTxs newSpanBatchTxs(List<String> txs, BigInteger chainId)
List<TransactionType> txTypes = new ArrayList<>();
long totalLegacyTxCount = 0;
for (int idx = 0; idx < totalBlockTxCount; idx++) {
String tx = txs.get(idx);
Bytes txnBytes = Bytes.fromHexString(tx);
byte[] tx = txs.get(idx);
Bytes txnBytes = Bytes.wrap(tx);
Transaction rawTransaction = TransactionDecoder.decodeOpaqueBytes(txnBytes, EncodingContext.BLOCK_BODY);
if (rawTransaction.getType() == TransactionType.FRONTIER) {
if (rawTransaction.getChainId().isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ void marshalBinaryInterOp() {
@Test
void unmarshalBinaryInterOp() {
SpanBatchTxData txData = SpanBatchTx.unmarshalBinary(
Numeric.hexStringToByteArray("0xda8853444835ec5800008502540be4008aba457aba24bbd63f5670"));
Numeric.hexStringToByteArray("0xda8853444835ec5800008502540be4008aba457aba24bbd63f5670"))
.getSpanBatchTxData();

assertNotNull(txData);
assertEquals(TransactionType.FRONTIER, txData.txType());
Expand All @@ -98,8 +99,9 @@ void unmarshalBinaryInterOp() {
assertEquals(Bytes.fromHexString("0xba457aba24bbd63f5670"), ((SpanBatchLegacyTxData) txData).data());

SpanBatchTxData txData1 = SpanBatchTx.unmarshalBinary(
Numeric.hexStringToByteArray(
"0x01f8548853444835ec5800008502540be4008aba457aba24bbd63f5670f838f794f1d2f39c58427be48c5ecda1e0cc7a930ae1ca50e1a00000000000000000000000000000000000000000000000000000000000000001"));
Numeric.hexStringToByteArray(
"0x01f8548853444835ec5800008502540be4008aba457aba24bbd63f5670f838f794f1d2f39c58427be48c5ecda1e0cc7a930ae1ca50e1a00000000000000000000000000000000000000000000000000000000000000001"))
.getSpanBatchTxData();
assertNotNull(txData1);
assertEquals(TransactionType.ACCESS_LIST, txData1.txType());
assertEquals(Wei.of(new BigInteger("6000000000000000000")), ((SpanBatchAccessListTxData) txData1).value());
Expand All @@ -112,8 +114,9 @@ void unmarshalBinaryInterOp() {
((SpanBatchAccessListTxData) txData1).accessList());

SpanBatchTxData txData2 = SpanBatchTx.unmarshalBinary(
Numeric.hexStringToByteArray(
"0x02f85a8853444835ec5800008502540be4008502540be4008aba457aba24bbd63f5670f838f794f1d2f39c58427be48c5ecda1e0cc7a930ae1ca50e1a00000000000000000000000000000000000000000000000000000000000000001"));
Numeric.hexStringToByteArray(
"0x02f85a8853444835ec5800008502540be4008502540be4008aba457aba24bbd63f5670f838f794f1d2f39c58427be48c5ecda1e0cc7a930ae1ca50e1a00000000000000000000000000000000000000000000000000000000000000001"))
.getSpanBatchTxData();
assertNotNull(txData2);
assertEquals(TransactionType.EIP1559, txData2.txType());
assertEquals(Wei.of(new BigInteger("6000000000000000000")), ((SpanBatchDynamicFeeTxData) txData2).value());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.TransactionType;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -276,6 +277,66 @@ void recoveryVE1559() throws IOException {
assertArrayEquals(vs, res);
}

@Test
void fullTxUnprotected() throws IOException {
URL url = Resources.getResource("fulltxunprotected.txt");
List<String> txs = Resources.readLines(url, Charsets.UTF_8);

SpanBatchTxs spanBatchTxs = SpanBatchTxs.newSpanBatchTxs(
txs.stream().map(Numeric::hexStringToByteArray).collect(Collectors.toList()), BigInteger.valueOf(697L));

List<String> txs1 = spanBatchTxs.fullTxs(BigInteger.valueOf(697L)).stream()
.map(Numeric::toHexString)
.collect(Collectors.toList());

assertEquals(txs, txs1);
}

@Test
void fullTxLegacy() throws IOException {
URL url = Resources.getResource("fulltxlegacy.txt");
List<String> txs = Resources.readLines(url, Charsets.UTF_8);

SpanBatchTxs spanBatchTxs = SpanBatchTxs.newSpanBatchTxs(
txs.stream().map(Numeric::hexStringToByteArray).collect(Collectors.toList()), BigInteger.valueOf(697L));

List<String> txs1 = spanBatchTxs.fullTxs(BigInteger.valueOf(697L)).stream()
.map(Numeric::toHexString)
.collect(Collectors.toList());

assertEquals(txs, txs1);
}

@Test
void fullTxAccessList() throws IOException {
URL url = Resources.getResource("fulltxacc.txt");
List<String> txs = Resources.readLines(url, Charsets.UTF_8);

SpanBatchTxs spanBatchTxs = SpanBatchTxs.newSpanBatchTxs(
txs.stream().map(Numeric::hexStringToByteArray).collect(Collectors.toList()), BigInteger.valueOf(697L));

List<String> txs1 = spanBatchTxs.fullTxs(BigInteger.valueOf(697L)).stream()
.map(Numeric::toHexString)
.collect(Collectors.toList());

assertEquals(txs, txs1);
}

@Test
void fullTxDynamic() throws IOException {
URL url = Resources.getResource("fulltxdyn.txt");
List<String> txs = Resources.readLines(url, Charsets.UTF_8);

SpanBatchTxs spanBatchTxs = SpanBatchTxs.newSpanBatchTxs(
txs.stream().map(Numeric::hexStringToByteArray).collect(Collectors.toList()), BigInteger.valueOf(697L));

List<String> txs1 = spanBatchTxs.fullTxs(BigInteger.valueOf(697L)).stream()
.map(Numeric::toHexString)
.collect(Collectors.toList());

assertEquals(txs, txs1);
}

//
// @Test
// void TestSpanBatchTxsContractCreationBits()
Expand Down
Loading
Loading