diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/RawSpanBatch.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/RawSpanBatch.java index 8dcc2c91..1fa7cf80 100644 --- a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/RawSpanBatch.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/RawSpanBatch.java @@ -54,6 +54,24 @@ public SpanBatchPayload spanbatchPayload() { return spanbatchPayload; } + /** + * Sets spanbatch prefix. + * + * @param spanbatchPrefix the spanbatch prefix + */ + public void setSpanbatchPrefix(SpanBatchPrefix spanbatchPrefix) { + this.spanbatchPrefix = spanbatchPrefix; + } + + /** + * Sets spanbatch payload. + * + * @param spanbatchPayload the spanbatch payload + */ + public void setSpanbatchPayload(SpanBatchPayload spanbatchPayload) { + this.spanbatchPayload = spanbatchPayload; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchPayload.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchPayload.java index 8dcc95cb..e9c0890c 100644 --- a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchPayload.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchPayload.java @@ -229,10 +229,10 @@ public void decodeTxs(ByteBuf source) { } long totalBlockTxCount = 0; - for (int i = 0; i < this.blockCount; i++) { + for (Long blockTxCount : this.blockTxCounts) { long total; try { - total = Math.addExact(totalBlockTxCount, this.blockTxCounts.get(i)); + total = Math.addExact(totalBlockTxCount, blockTxCount); } catch (ArithmeticException e) { throw new RuntimeException("totalBlockTxCount overflow"); } diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTx.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTx.java index 6278eaef..a073c2e0 100644 --- a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTx.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTx.java @@ -46,8 +46,12 @@ */ public class SpanBatchTx { - private static final Supplier SIGNATURE_ALGORITHM = + /** + * The constant SIGNATURE_ALGORITHM. + */ + public static final Supplier SIGNATURE_ALGORITHM = Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + private final SpanBatchTxData spanBatchTxData; /** diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTxs.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTxs.java index 4ffc270e..77709222 100644 --- a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTxs.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/SpanBatchTxs.java @@ -732,10 +732,16 @@ public static Pair readTxData(RLPInput input, boolean is if (isTypedTransaction(input)) { final Bytes typedTransactionBytes = input.readBytes(); Bytes txBytes = isLast ? lastCurrentListAsBytes(input) : input.currentListAsBytes(); + if (txBytes.size() > SpanBatchUtils.MaxSpanBatchSize) { + throw new RuntimeException("tx size too large"); + } var transactionType = getTransactionType(typedTransactionBytes).orElseThrow(); return Pair.of(Bytes.concatenate(typedTransactionBytes, txBytes), transactionType); } else { Bytes bytes = isLast ? lastCurrentListAsBytes(input) : input.currentListAsBytes(); + if (bytes.size() > SpanBatchUtils.MaxSpanBatchSize) { + throw new RuntimeException("tx size too large"); + } return Pair.of(bytes, TransactionType.FRONTIER); } } diff --git a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTest.java b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTest.java index aba7e85b..d3229b99 100644 --- a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTest.java +++ b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTest.java @@ -102,4 +102,101 @@ void testSpanBatchOriginBits() throws IOException { rawSpanBatch.spanbatchPayload().originBits(), rawSpanBatch1.spanbatchPayload().originBits()); } + + @Test + void testSpanBatchPrefix() throws IOException { + URL url = Resources.getResource("spanbatchoriginbits.txt"); + String origin = Resources.toString(url, Charsets.UTF_8); + + RawSpanBatch rawSpanBatch = new RawSpanBatch(); + rawSpanBatch.decode(Unpooled.wrappedBuffer(Numeric.hexStringToByteArray(origin))); + + rawSpanBatch.setSpanbatchPayload(new SpanBatchPayload()); + + byte[] prefix = rawSpanBatch.spanbatchPrefix().encode(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPrefix().decode(Unpooled.wrappedBuffer(prefix)); + + assertEquals(rawSpanBatch, rawSpanBatch1); + } + + @Test + void testSpanBatchMaxOriginBitsLength() { + RawSpanBatch rawSpanBatch = new RawSpanBatch(); + rawSpanBatch.spanbatchPayload().setBlockCount(Long.MAX_VALUE); + + assertThrows( + RuntimeException.class, + () -> rawSpanBatch.spanbatchPayload().decodeOriginBits(Unpooled.wrappedBuffer(new byte[] {}))); + } + + @Test + void testSpanBatchMaxBlockCount() throws IOException { + URL url = Resources.getResource("spanbatchoriginbits.txt"); + String origin = Resources.toString(url, Charsets.UTF_8); + + RawSpanBatch rawSpanBatch = new RawSpanBatch(); + rawSpanBatch.decode(Unpooled.wrappedBuffer(Numeric.hexStringToByteArray(origin))); + + rawSpanBatch.spanbatchPayload().setBlockCount(Long.MAX_VALUE); + + byte[] encodedBlockCount = rawSpanBatch.spanbatchPayload().encodeBlockCount(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + + assertThrows( + RuntimeException.class, + () -> rawSpanBatch1.spanbatchPayload().decodeBlockCount(Unpooled.wrappedBuffer(encodedBlockCount)), + "span batch size limit reached"); + } + + @Test + void testSpanBatchMaxBlockTxCount() throws IOException { + URL url = Resources.getResource("spanbatchoriginbits.txt"); + String origin = Resources.toString(url, Charsets.UTF_8); + + RawSpanBatch rawSpanBatch = new RawSpanBatch(); + rawSpanBatch.decode(Unpooled.wrappedBuffer(Numeric.hexStringToByteArray(origin))); + + rawSpanBatch.spanbatchPayload().blockTxCounts().set(0, Long.MAX_VALUE); + + byte[] encodedBlockTxCounts = rawSpanBatch.spanbatchPayload().encodeBlockTxCounts(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1 + .spanbatchPayload() + .setBlockCount(rawSpanBatch.spanbatchPayload().blockCount()); + + assertThrows( + RuntimeException.class, + () -> rawSpanBatch1 + .spanbatchPayload() + .decodeBlockTxCounts(Unpooled.wrappedBuffer(encodedBlockTxCounts)), + "span batch size limit reached"); + } + + @Test + void testSpanBatchTotalBlockTxCountNotOverflow() throws IOException { + URL url = Resources.getResource("spanbatchoriginbits.txt"); + String origin = Resources.toString(url, Charsets.UTF_8); + + RawSpanBatch rawSpanBatch = new RawSpanBatch(); + rawSpanBatch.decode(Unpooled.wrappedBuffer(Numeric.hexStringToByteArray(origin))); + + rawSpanBatch.spanbatchPayload().blockTxCounts().set(0, SpanBatchUtils.MaxSpanBatchSize - 1); + rawSpanBatch.spanbatchPayload().blockTxCounts().set(1, SpanBatchUtils.MaxSpanBatchSize - 1); + + byte[] encodedBlockTxCounts = rawSpanBatch.spanbatchPayload().encodeBlockTxCounts(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1 + .spanbatchPayload() + .setBlockTxCounts(rawSpanBatch.spanbatchPayload().blockTxCounts()); + + assertThrows( + RuntimeException.class, + () -> rawSpanBatch1.spanbatchPayload().decodeTxs(Unpooled.wrappedBuffer(encodedBlockTxCounts)), + "span batch size limit reached"); + } } diff --git a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTxsTest.java b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTxsTest.java index 0dd6125e..029732da 100644 --- a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTxsTest.java +++ b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/SpanBatchTxsTest.java @@ -1,7 +1,9 @@ package io.optimism.utilities.derive.stages; +import static io.optimism.utilities.derive.stages.SpanBatchTx.SIGNATURE_ALGORITHM; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.base.Charsets; import com.google.common.io.Resources; @@ -9,11 +11,19 @@ import java.io.IOException; import java.math.BigInteger; import java.net.URL; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.encoding.EncodingContext; +import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.junit.jupiter.api.Test; import org.web3j.utils.Numeric; @@ -337,6 +347,28 @@ void fullTxDynamic() throws IOException { assertEquals(txs, txs1); } + @Test + void testSpanBatchMaxTxData() { + Transaction.Builder builder = Transaction.builder(); + SecureRandom rng = new SecureRandom(); + byte[] randomBytes = new byte[(int) (SpanBatchUtils.MaxSpanBatchSize + 1)]; + rng.nextBytes(randomBytes); + builder.type(TransactionType.EIP1559) + .chainId(BigInteger.valueOf(108L)) + .maxFeePerGas(Wei.of(5)) + .maxPriorityFeePerGas(Wei.of(5)) + .value(Wei.ZERO) + .payload(Bytes.wrap(randomBytes)); + final SECPSignature signature = + SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 1); + builder.signature(signature); + + Bytes txEncoded = TransactionEncoder.encodeOpaqueBytes(builder.build(), EncodingContext.BLOCK_BODY); + + RLPInput rlpInput = new BytesValueRLPInput(txEncoded, false); + assertThrows(RuntimeException.class, () -> SpanBatchTxs.readTxData(rlpInput, true)); + } + // // @Test // void TestSpanBatchTxsContractCreationBits()