Skip to content

Commit

Permalink
feat:spanbatch fulltx
Browse files Browse the repository at this point in the history
Signed-off-by: Chen Kai <281165273grape@gmail.com>
  • Loading branch information
GrapeBaBa committed Jan 17, 2024
1 parent 564a267 commit 63ee26d
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 21 deletions.
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

0 comments on commit 63ee26d

Please sign in to comment.