From 768a3afc0b318d0f07455282d6e67bfaeb98f8bb Mon Sep 17 00:00:00 2001 From: Chen Kai <281165273grape@gmail.com> Date: Sun, 28 Jan 2024 15:25:22 +0800 Subject: [PATCH] feat:fix recover unprotected Signed-off-by: Chen Kai <281165273grape@gmail.com> --- .../utilities/derive/stages/SpanBatchTx.java | 3 +- .../utilities/derive/stages/SpanBatchTxs.java | 39 +++- .../utilities/derive/stages/RandomUtils.java | 25 +- .../derive/stages/SpanBatchTest.java | 219 +++++++++++++++++- 4 files changed, 268 insertions(+), 18 deletions(-) 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 a073c2e0..f7a434b5 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 @@ -241,14 +241,13 @@ public Transaction convertToFullTx( private static Pair getRecId(BigInteger chainId, BigInteger v) { byte recId; - boolean protectedTx; + boolean protectedTx = true; 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)); } 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 77709222..0ce7c9c2 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 @@ -12,6 +12,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.tuple.Pair; import org.apache.tuweni.bytes.Bytes; @@ -341,12 +342,15 @@ public void recoverV(BigInteger chainId) { throw new RuntimeException("protected bits not set"); } + int protectedBitsIdx = 0; for (int i = 0; i < this.txTypes.size(); i++) { BigInteger bit = this.yParityBits.testBit(i) ? BigInteger.ONE : BigInteger.ZERO; BigInteger v; switch (this.txTypes.get(i)) { case FRONTIER: - if (this.protectedBits.testBit(i)) { + boolean isProtected = this.protectedBits.testBit(protectedBitsIdx); + protectedBitsIdx++; + if (isProtected) { v = chainId.multiply(BigInteger.TWO) .add(REPLAY_PROTECTED_V_BASE) .add(bit); @@ -799,4 +803,37 @@ private static Optional getTransactionType(final Bytes opaqueBy return Optional.empty(); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SpanBatchTxs txs)) return false; + return totalBlockTxCount == txs.totalBlockTxCount + && totalLegacyTxCount == txs.totalLegacyTxCount + && Objects.equals(contractCreationBits, txs.contractCreationBits) + && Objects.equals(yParityBits, txs.yParityBits) + && Objects.equals(txSigs, txs.txSigs) + && Objects.equals(txNonces, txs.txNonces) + && Objects.equals(txGases, txs.txGases) + && Objects.equals(txTos, txs.txTos) + && Objects.equals(txDatas, txs.txDatas) + && Objects.equals(txTypes, txs.txTypes) + && Objects.equals(protectedBits, txs.protectedBits); + } + + @Override + public int hashCode() { + return Objects.hash( + totalBlockTxCount, + contractCreationBits, + yParityBits, + txSigs, + txNonces, + txGases, + txTos, + txDatas, + txTypes, + totalLegacyTxCount, + protectedBits); + } } diff --git a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/RandomUtils.java b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/RandomUtils.java index d33e61bd..d2153a65 100644 --- a/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/RandomUtils.java +++ b/hildr-utilities/src/test/java/io/optimism/utilities/derive/stages/RandomUtils.java @@ -1,31 +1,28 @@ package io.optimism.utilities.derive.stages; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.math.BigInteger; import java.security.SecureRandom; import org.web3j.utils.Numeric; public class RandomUtils { - private static byte[] computeHash(byte[] input) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - return digest.digest(input); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - return null; - } + public static String randomHex() { + // Create a SecureRandom object + SecureRandom rng = new SecureRandom(); + // Generate a random byte array + byte[] randomBytes = new byte[32]; + rng.nextBytes(randomBytes); + // Convert the byte array to a hexadecimal string + return Numeric.toHexString(randomBytes); } - public static String randomHex() { + public static BigInteger randomBigInt() { // Create a SecureRandom object SecureRandom rng = new SecureRandom(); // Generate a random byte array byte[] randomBytes = new byte[32]; rng.nextBytes(randomBytes); - // Compute the hash of the random byte array - byte[] hash = computeHash(randomBytes); // Convert the byte array to a hexadecimal string - return Numeric.toHexString(hash); + return new BigInteger(randomBytes); } } 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 d3229b99..5b44db5b 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 @@ -1,10 +1,14 @@ package io.optimism.utilities.derive.stages; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.base.Charsets; import com.google.common.io.Resources; import io.netty.buffer.Unpooled; +import io.optimism.type.BlockId; +import io.optimism.type.L2BlockRef; import java.io.IOException; import java.math.BigInteger; import java.net.URL; @@ -121,6 +125,219 @@ void testSpanBatchPrefix() throws IOException { assertEquals(rawSpanBatch, rawSpanBatch1); } + @Test + void testSpanBatchRelTimestamp() 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))); + + byte[] relTimestamp = rawSpanBatch.spanbatchPrefix().encodeRelTimestamp(); + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPrefix().decodeRelTimestamp(Unpooled.wrappedBuffer(relTimestamp)); + + assertEquals( + rawSpanBatch.spanbatchPrefix().relTimestamp(), + rawSpanBatch1.spanbatchPrefix().relTimestamp()); + } + + @Test + void testSpanBatchL1OriginNum() 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))); + + byte[] l1OriginNum = rawSpanBatch.spanbatchPrefix().encodeL1OriginNum(); + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPrefix().decodeL1OriginNum(Unpooled.wrappedBuffer(l1OriginNum)); + + assertEquals( + rawSpanBatch.spanbatchPrefix().l1OriginNum(), + rawSpanBatch1.spanbatchPrefix().l1OriginNum()); + } + + @Test + void testSpanBatchParentCheck() 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))); + + byte[] parentCheck = rawSpanBatch.spanbatchPrefix().encodeParentCheck(); + assertEquals(20, parentCheck.length); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPrefix().decodeParentCheck(Unpooled.wrappedBuffer(parentCheck)); + + assertEquals( + rawSpanBatch.spanbatchPrefix().parentCheck(), + rawSpanBatch1.spanbatchPrefix().parentCheck()); + } + + @Test + void testSpanBatchL1OriginCheck() 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))); + + byte[] l1OriginCheck = rawSpanBatch.spanbatchPrefix().encodeL1OriginCheck(); + assertEquals(20, l1OriginCheck.length); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPrefix().decodeL1OriginCheck(Unpooled.wrappedBuffer(l1OriginCheck)); + + assertEquals( + rawSpanBatch.spanbatchPrefix().l1OriginCheck(), + rawSpanBatch1.spanbatchPrefix().l1OriginCheck()); + } + + @Test + void testSpanBatchPayload() 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().txs().recoverV(BigInteger.valueOf(28)); + + byte[] payload = rawSpanBatch.spanbatchPayload().encode(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPayload().decode(Unpooled.wrappedBuffer(payload)); + rawSpanBatch1.spanbatchPayload().txs().recoverV(BigInteger.valueOf(28)); + + assertEquals(rawSpanBatch.spanbatchPayload(), rawSpanBatch1.spanbatchPayload()); + } + + @Test + void testSpanBatchBlockCount() 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))); + + byte[] blockCount = rawSpanBatch.spanbatchPayload().encodeBlockCount(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1.spanbatchPayload().decodeBlockCount(Unpooled.wrappedBuffer(blockCount)); + + assertEquals( + rawSpanBatch.spanbatchPayload().blockCount(), + rawSpanBatch1.spanbatchPayload().blockCount()); + } + + @Test + void testSpanBatchBlockTxCounts() 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))); + + byte[] blockTxCounts = rawSpanBatch.spanbatchPayload().encodeBlockTxCounts(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1 + .spanbatchPayload() + .setBlockCount(rawSpanBatch.spanbatchPayload().blockCount()); + rawSpanBatch1.spanbatchPayload().decodeBlockTxCounts(Unpooled.wrappedBuffer(blockTxCounts)); + + assertEquals( + rawSpanBatch.spanbatchPayload().blockTxCounts(), + rawSpanBatch1.spanbatchPayload().blockTxCounts()); + } + + @Test + void testSpanBatchTxs() 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().txs().recoverV(BigInteger.valueOf(28)); + + byte[] txs = rawSpanBatch.spanbatchPayload().encodeTxs(); + + RawSpanBatch rawSpanBatch1 = new RawSpanBatch(); + rawSpanBatch1 + .spanbatchPayload() + .setBlockTxCounts(rawSpanBatch.spanbatchPayload().blockTxCounts()); + rawSpanBatch1.spanbatchPayload().decodeTxs(Unpooled.wrappedBuffer(txs)); + rawSpanBatch1.spanbatchPayload().txs().recoverV(BigInteger.valueOf(28)); + + assertEquals( + rawSpanBatch.spanbatchPayload().txs(), + rawSpanBatch1.spanbatchPayload().txs()); + } + + @Test + void testSpanBatchDerive() throws IOException { + BigInteger l2BlockTime = BigInteger.TWO; + for (int originChangedBit = 0; originChangedBit < 2; originChangedBit++) { + URL url = Resources.getResource("spanbatchfromsingular.txt"); + List singularBatches = Resources.readLines(url, Charsets.UTF_8); + + List singularBatches1 = singularBatches.stream() + .map(singularBatch -> { + RlpList rlpBatchData = (RlpList) RlpDecoder.decode(Numeric.hexStringToByteArray(singularBatch)) + .getValues() + .getFirst(); + return SingularBatch.decode(rlpBatchData); + }) + .toList(); + L2BlockRef l2BlockRef = new L2BlockRef( + singularBatches1.getFirst().parentHash(), + // random biginteger + RandomUtils.randomBigInt(), + // random string + RandomUtils.randomHex(), + // random biginteger + RandomUtils.randomBigInt(), + new BlockId(RandomUtils.randomHex(), RandomUtils.randomBigInt()), + RandomUtils.randomBigInt()); + + BigInteger genesisTimeStamp = + BigInteger.ONE.add(singularBatches1.getFirst().timestamp()).subtract(BigInteger.valueOf(128)); + + SpanBatch spanBatch = SpanBatch.newSpanBatch(singularBatches1); + RawSpanBatch rawSpanBatch = + spanBatch.toRawSpanBatch(originChangedBit, genesisTimeStamp, BigInteger.valueOf(589)); + + long blockCount = singularBatches1.size(); + SpanBatch spanBatchDerived = rawSpanBatch.derive(l2BlockTime, genesisTimeStamp, BigInteger.valueOf(589)); + + assertEquals( + l2BlockRef.hash().substring(0, 40), + spanBatchDerived.getParentCheck().toHexString()); + assertEquals( + singularBatches1.getLast().epoch().hash().substring(0, 40), + spanBatchDerived.getL1OriginCheck().toHexString()); + assertEquals(blockCount, spanBatchDerived.getBlockCount()); + + for (int i = 1; i < blockCount; i++) { + assertEquals( + spanBatchDerived.getBatches().get(i).timestamp(), + spanBatchDerived.getBatches().get(i - 1).timestamp().add(l2BlockTime)); + } + + for (int i = 0; i < blockCount; i++) { + assertEquals( + spanBatchDerived.getBatches().get(i).epochNum(), + singularBatches1.get(i).epochNum()); + assertEquals( + spanBatchDerived.getBatches().get(i).timestamp(), + singularBatches1.get(i).timestamp()); + assertEquals( + spanBatchDerived.getBatches().get(i).transactions(), + singularBatches1.get(i).transactions()); + } + } + } + @Test void testSpanBatchMaxOriginBitsLength() { RawSpanBatch rawSpanBatch = new RawSpanBatch();