diff --git a/hildr-batcher/build.gradle b/hildr-batcher/build.gradle index d8564f54..52bde3d4 100644 --- a/hildr-batcher/build.gradle +++ b/hildr-batcher/build.gradle @@ -48,7 +48,6 @@ tasks.withType(JavaExec).configureEach { dependencies { implementation project(':hildr-utilities') - implementation('net.osslabz.evm:evm-abi-decoder:0.0.6') implementation 'com.github.gestalt-config:gestalt-core:0.20.4' implementation 'com.github.gestalt-config:gestalt-toml:0.20.4' diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java b/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java index 9658fdf7..5ba933e5 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java @@ -19,7 +19,7 @@ import com.google.common.util.concurrent.AbstractExecutionThreadService; import io.optimism.batcher.channel.ChannelManager; import io.optimism.batcher.config.Config; -import io.optimism.batcher.ex.BatcherExecutionException; +import io.optimism.batcher.exception.BatcherExecutionException; import io.optimism.batcher.loader.BlockLoader; import io.optimism.batcher.loader.LoaderConfig; import io.optimism.batcher.publisher.ChannelDataPublisher; diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelException.java b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelException.java new file mode 100644 index 00000000..8fe91c08 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.channel; + +/** + * Batcher Channel Exception class. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class ChannelException extends RuntimeException { + + /** + * Instantiates a new ChannelException. + * + * @param message the message + */ + public ChannelException(String message) { + super(message); + } + + /** + * Instantiates a new ChannelException. + * + * @param message the message + * @param cause the cause + */ + public ChannelException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new ChannelException. + * + * @param cause the cause + */ + public ChannelException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelFullException.java b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelFullException.java new file mode 100644 index 00000000..d1e2d0a1 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelFullException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.channel; + +/** + * ChannelFullException class. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class ChannelFullException extends ChannelException { + + /** + * Instantiates a new ChannelFullException. + * + * @param message the message + */ + public ChannelFullException(String message) { + super(message); + } + + /** + * Instantiates a new ChannelFullException. + * + * @param message the message + * @param cause the cause + */ + public ChannelFullException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new ChannelFullException. + * + * @param cause the cause + */ + public ChannelFullException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelManager.java b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelManager.java index 2dca678d..2625fd6e 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelManager.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelManager.java @@ -16,7 +16,6 @@ package io.optimism.batcher.channel; -import io.optimism.batcher.ex.ReorgException; import io.optimism.type.BlockId; import java.util.ArrayList; import java.util.List; diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/ex/ReorgException.java b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ReorgException.java similarity index 97% rename from hildr-batcher/src/main/java/io/optimism/batcher/ex/ReorgException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/channel/ReorgException.java index a8d2fb9d..282f0dff 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/ex/ReorgException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ReorgException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.batcher.ex; +package io.optimism.batcher.channel; /** * ReorgException class. Throws this when chain occurs reorg. diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressor.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressor.java index 571c3122..e8c58dab 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressor.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressor.java @@ -17,7 +17,6 @@ package io.optimism.batcher.compressor; import java.io.Closeable; -import java.io.Flushable; /** * Tx data bytes compressor interface. @@ -25,7 +24,7 @@ * @author thinkAfCod * @since 0.1.1 */ -public interface Compressor extends Closeable, Readable, Flushable { +public interface Compressor extends Closeable { /** * write uncompressed data which will be compressed. Should return CompressorFullException if the @@ -59,6 +58,8 @@ public interface Compressor extends Closeable, Readable, Flushable { * returns CompressorFullException if the compressor is known to be full. Note that calls to Write * will fail if an error is returned from this method, but calls to Write can still return * CompressorFullErr even if this does not. + * + * @return return true if compressed data length reached the limit, otherwise return false */ - void fullErr(); + boolean isFull(); } diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Config.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorConfig.java similarity index 67% rename from hildr-batcher/src/main/java/io/optimism/batcher/compressor/Config.java rename to hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorConfig.java index 1501386d..5e6a4ef0 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Config.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorConfig.java @@ -16,20 +16,37 @@ package io.optimism.batcher.compressor; +import io.optimism.batcher.config.Config; + /** * Compressor Config. * - * @author thinkAfCod - * @since 0.1.1 * @param targetFrameSize To target when creating channel frames. Note that if the realized * compression ratio is worse than the approximate, more frames may actually be created. This * also depends on how close the target is to the max frame size. - * @param targetNumFrame To create in this channel. If the realized compression ratio is worse than + * @param targetNumFrames To create in this channel. If the realized compression ratio is worse than * approxComprRatio, additional leftover frame(s) might get created. * @param approxComprRatio ApproxComprRatio to assume. Should be slightly smaller than average from * experiments to avoid the chances of creating a small additional leftover frame. * @param kind Kind of compressor to use. Must be one of KindKeys. If unset, NewCompressor will * default to RatioKind. + * @author thinkAfCod + * @since 0.1.1 */ -public record Config( - long targetFrameSize, int targetNumFrame, double approxComprRatio, String kind) {} +public record CompressorConfig( + int targetFrameSize, int targetNumFrames, String approxComprRatio, String kind) { + + /** + * Create CompressorConfig instance from Config instance. + * + * @param config Config instance + * @return CompressorConfig instance + */ + public static CompressorConfig from(Config config) { + return new CompressorConfig( + config.targetFrameSize(), + config.targetNumFrames(), + config.approxComprRatio(), + Compressors.RatioKind); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorFactory.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorFactory.java deleted file mode 100644 index fc80e7f2..00000000 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/CompressorFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2023 q315xia@163.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.optimism.batcher.compressor; - -/** - * Compressor Factory. - * - * @author thinkAfCod - * @since 0.1.1 - */ -public class CompressorFactory { - - private CompressorFactory() {} -} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressors.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressors.java index 345b907b..9a25f89d 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressors.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/Compressors.java @@ -16,24 +16,33 @@ package io.optimism.batcher.compressor; +import io.optimism.batcher.exception.UnsupportedException; + /** * Compressor create tool. * * @author thinkAfCod * @since 0.1.1 */ -public interface Compressors { +public class Compressors { /** Kind type of ratio. */ - String RatioKind = "ratio"; + public static final String RatioKind = "ratio"; - /** Kind type of shadow. */ - String ShadowKind = "shadow"; + private Compressors() {} /** * Create Compressor by kind. * - * @param kind Type of Compressor + * @param config Config of compressor + * @return a compressor */ - default void create(String kind) {} + public static Compressor create(final CompressorConfig config) { + String kind = config.kind(); + if (kind.equalsIgnoreCase(RatioKind)) { + return new RatioCompressor(config); + } else { + throw new UnsupportedException(String.format("unsupported kind: %s", kind)); + } + } } diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/RatioCompressor.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/RatioCompressor.java index 0122d86a..aec4b9b1 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/RatioCompressor.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/RatioCompressor.java @@ -16,9 +16,12 @@ package io.optimism.batcher.compressor; +import io.optimism.batcher.compressor.exception.CompressorFullException; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.CharBuffer; -import org.jetbrains.annotations.NotNull; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.zip.Deflater; /** * RatioCompressor class. @@ -28,39 +31,88 @@ */ public class RatioCompressor implements Compressor { - RatioCompressor() { - // todo plan to use java.util.zip.Deflater + private final CompressorConfig config; + + private final Deflater deflater; + + private final int inputThreshold; + + private volatile ByteArrayOutputStream bos; + + private int pos; + + private int inputLength; + + RatioCompressor(final CompressorConfig config) { + this.config = config; + this.deflater = new Deflater(Deflater.BEST_COMPRESSION); + this.inputThreshold = inputThreshold(); + this.bos = new ByteArrayOutputStream(this.inputThreshold); + this.pos = 0; + this.inputLength = 0; } @Override public int write(byte[] p) { - return 0; + if (this.isFull()) { + throw new CompressorFullException("the target amount of input data has been reached limit"); + } + this.inputLength += p.length; + this.deflater.setInput(p); + byte[] compressed = new byte[p.length]; + int len = this.deflater.deflate(compressed); + this.bos.write(compressed, 0, len); + this.deflater.reset(); + return p.length; } @Override public int read(byte[] p) { - return 0; + byte[] data = this.bos.toByteArray(); + int len = this.bos.size(); + if (pos > len) { + return -1; + } + int readLen = p.length; + int avail = len - pos; + if (p.length > avail) { + readLen = avail; + } + System.arraycopy(data, pos, p, 0, readLen); + pos += readLen; + return readLen; } @Override - public int read(@NotNull CharBuffer cb) throws IOException { - return 0; + public void reset() { + if (this.bos != null) { + this.bos.reset(); + } + this.deflater.reset(); + this.pos = 0; + this.inputLength = 0; } - @Override - public void reset() {} - @Override public int length() { - return 0; + return this.bos.size() - this.pos; } @Override - public void fullErr() {} + public boolean isFull() { + return this.inputLength >= this.inputThreshold; + } @Override - public void close() throws IOException {} + public void close() throws IOException { + this.deflater.finish(); + this.deflater.end(); + } - @Override - public void flush() throws IOException {} + private int inputThreshold() { + return BigDecimal.valueOf(config.targetNumFrames()) + .multiply(BigDecimal.valueOf(config.targetFrameSize())) + .divide(new BigDecimal(config.approxComprRatio()), 2, RoundingMode.HALF_UP) + .intValue(); + } } diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/ShadowCompressor.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/ShadowCompressor.java deleted file mode 100644 index 9550fbf1..00000000 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/ShadowCompressor.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2023 q315xia@163.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package io.optimism.batcher.compressor; - -import java.io.IOException; -import java.nio.CharBuffer; -import org.jetbrains.annotations.NotNull; - -/** - * ShadowCompressor class. - * - * @author thinkAfCod - * @since 0.1.1 - */ -public class ShadowCompressor implements Compressor { - - /** Constructor of ShadowCompressor. */ - public ShadowCompressor() {} - - @Override - public int write(byte[] p) { - return 0; - } - - @Override - public int read(byte[] p) { - return 0; - } - - @Override - public int read(@NotNull CharBuffer cb) throws IOException { - return 0; - } - - @Override - public void reset() {} - - @Override - public int length() { - return 0; - } - - @Override - public void fullErr() {} - - @Override - public void close() throws IOException {} - - @Override - public void flush() throws IOException {} -} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/ex/CompressorFullException.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java similarity index 95% rename from hildr-batcher/src/main/java/io/optimism/batcher/compressor/ex/CompressorFullException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java index 5f321196..de0e7056 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/ex/CompressorFullException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.batcher.compressor.ex; +package io.optimism.batcher.compressor.exception; /** * If the compressor is full and no more data should be written or the compressor is known to be diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/config/Config.java b/hildr-batcher/src/main/java/io/optimism/batcher/config/Config.java index 5a1ed812..79e16b99 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/config/Config.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/config/Config.java @@ -27,6 +27,9 @@ * @param subSafetyMargin Sub-safety margin * @param pollInterval Milliseconds of poll interval * @param maxL1TxSize Max L1 Tx Size + * @param targetFrameSize Max L1 Tx Size + * @param targetNumFrames Max L1 Tx Size + * @param approxComprRatio Max L1 Tx Size * @author thinkAfCod * @since 0.1.1 */ @@ -38,4 +41,7 @@ public record Config( String batchInboxAddress, Long subSafetyMargin, Long pollInterval, - Long maxL1TxSize) {} + Long maxL1TxSize, + Integer targetFrameSize, + Integer targetNumFrames, + String approxComprRatio) {} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/ex/BatcherExecutionException.java b/hildr-batcher/src/main/java/io/optimism/batcher/exception/BatcherExecutionException.java similarity index 97% rename from hildr-batcher/src/main/java/io/optimism/batcher/ex/BatcherExecutionException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/exception/BatcherExecutionException.java index 7760adc2..778c1750 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/ex/BatcherExecutionException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/exception/BatcherExecutionException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.batcher.ex; +package io.optimism.batcher.exception; /** * The execution exception of Bathcer. diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/exception/UnsupportedException.java b/hildr-batcher/src/main/java/io/optimism/batcher/exception/UnsupportedException.java new file mode 100644 index 00000000..cff4e874 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/exception/UnsupportedException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.exception; + +/** + * UnsupportedException class. Throws This when field or operation not supported. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class UnsupportedException extends RuntimeException { + + /** + * Instantiates a new UnsupportedException. + * + * @param message the message + */ + public UnsupportedException(String message) { + super(message); + } + + /** + * Instantiates a new UnsupportedException. + * + * @param message the message + * @param cause the cause + */ + public UnsupportedException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new UnsupportedException. + * + * @param cause the cause + */ + public UnsupportedException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/ex/Web3jCallException.java b/hildr-batcher/src/main/java/io/optimism/batcher/exception/Web3jCallException.java similarity index 53% rename from hildr-batcher/src/main/java/io/optimism/batcher/ex/Web3jCallException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/exception/Web3jCallException.java index 5531dcb8..a4f1bb27 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/ex/Web3jCallException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/exception/Web3jCallException.java @@ -1,4 +1,20 @@ -package io.optimism.batcher.ex; +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.exception; /** * Web3jCallException class. Throws it when the call of web3j request task failed. diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoader.java b/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoader.java index c9d6c2bb..b4e967ac 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoader.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoader.java @@ -16,9 +16,8 @@ package io.optimism.batcher.loader; -import io.optimism.batcher.ex.ReorgException; -import io.optimism.batcher.ex.SyncStatusException; -import io.optimism.batcher.ex.Web3jCallException; +import io.optimism.batcher.channel.ReorgException; +import io.optimism.batcher.exception.Web3jCallException; import io.optimism.type.BlockId; import io.optimism.type.Genesis; import io.optimism.type.L1BlockInfo; @@ -121,7 +120,6 @@ private RollupConfigRes.RollupConfig loadRollConfig() { return future.resultNow(); } catch (ExecutionException | InterruptedException e) { Thread.currentThread().interrupt(); - // todo add description throw new Web3jCallException("failed to get op-rollup config", e); } } @@ -140,8 +138,7 @@ private void loadBlocksIntoState() { lastestBlock = block; } if (lastestBlock == null) { - // todo create BlockLoaderException - throw new RuntimeException(""); + throw new BlockLoaderException("get latest block failed"); } var ignore = l2BlockToBlockRef(lastestBlock, rollupConfig.genesis()); // todo metrics.RecordL2BlocksLoaded l2Ref @@ -169,8 +166,7 @@ private Tuple2 calculateL2BlockRangeToStore() { if (syncStatus.safeL2().number().compareTo(syncStatus.unsafeL2().number()) >= 0 || latestLoadedBlock.number().compareTo(syncStatus.unsafeL2().number()) >= 0) { - // todo create BlockLoaderException - throw new RuntimeException("L2 safe head ahead of L2 unsafe head"); + throw new SyncStatusException("L2 safe head ahead of L2 unsafe head"); } return new Tuple2<>(latestLoadedBlock, syncStatus.unsafeL2().number()); } catch (ExecutionException | InterruptedException e) { @@ -184,8 +180,7 @@ private L2BlockRef l2BlockToBlockRef(final EthBlock.Block block, Genesis genesis BigInteger sequenceNumber = null; if (block.getNumber().compareTo(genesis.l2().number()) == 0) { if (!block.getHash().equals(genesis.l2().hash())) { - // todo replace to BlockLoaderException - throw new RuntimeException( + throw new BlockLoaderException( String.format( "expected L2 genesis hash to match L2 block at genesis block number %d: %s <> %s", genesis.l2().number(), block.getHash(), genesis.l2().hash())); @@ -195,15 +190,13 @@ private L2BlockRef l2BlockToBlockRef(final EthBlock.Block block, Genesis genesis } else { var txs = block.getTransactions(); if (txs == null || txs.size() == 0) { - // todo replace BlockLoaderException - throw new RuntimeException( + throw new BlockLoaderException( String.format( "l2 block is missing L1 info deposit tx, block hash: %s", block.getHash())); } EthBlock.TransactionObject tx = (EthBlock.TransactionObject) txs.get(0).get(); if (!DEPOSIT_TX_TYPE.equalsIgnoreCase(tx.getType())) { - // todo replace to BlockLoaderException - throw new RuntimeException( + throw new BlockLoaderException( String.format("first payload tx has unexpected tx type: %s", tx.getType())); } final byte[] input = Numeric.hexStringToByteArray(tx.getInput()); diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoaderException.java b/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoaderException.java new file mode 100644 index 00000000..3d9137c5 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/loader/BlockLoaderException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.loader; + +/** + * BlockLoaderException class. Throws this when occurs error while executing BlockLoader. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class BlockLoaderException extends RuntimeException { + + /** + * Instantiates a new BlockLoaderException. + * + * @param message the message + */ + public BlockLoaderException(String message) { + super(message); + } + + /** + * Instantiates a new BlockLoaderException. + * + * @param message the message + * @param cause the cause + */ + public BlockLoaderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new BlockLoaderException. + * + * @param cause the cause + */ + public BlockLoaderException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java b/hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java new file mode 100644 index 00000000..05ab0af7 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.loader; + +/** + * ParseBlockException class. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class ParseBlockException extends RuntimeException { + + /** + * Instantiates a new ParseBlockException. + * + * @param message the message + */ + public ParseBlockException(String message) { + super(message); + } + + /** + * Instantiates a new ParseBlockException. + * + * @param message the message + * @param cause the cause + */ + public ParseBlockException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new ParseBlockException. + * + * @param cause the cause + */ + public ParseBlockException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/ex/SyncStatusException.java b/hildr-batcher/src/main/java/io/optimism/batcher/loader/SyncStatusException.java similarity index 97% rename from hildr-batcher/src/main/java/io/optimism/batcher/ex/SyncStatusException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/loader/SyncStatusException.java index d842adb2..e719b71d 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/ex/SyncStatusException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/loader/SyncStatusException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.batcher.ex; +package io.optimism.batcher.loader; /** * Sync status exception. Throws this when the call to the SyncStatus API fails. diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/ChannelDataPublisher.java b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/ChannelDataPublisher.java index 8f57b926..2a464dd4 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/ChannelDataPublisher.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/ChannelDataPublisher.java @@ -17,7 +17,7 @@ package io.optimism.batcher.publisher; import io.optimism.batcher.channel.Channel; -import io.optimism.batcher.ex.Web3jCallException; +import io.optimism.batcher.exception.Web3jCallException; import io.optimism.type.BlockId; import io.optimism.type.L1BlockRef; import io.optimism.type.TxCandidate; @@ -109,8 +109,7 @@ private boolean publishTxToL1() { Channel.TxData txData = dataSupplier.apply(l1HeadBlockRef.toId()); if (txData == null) { LOGGER.trace("no transaction data available"); - // todo create PublishException - throw new RuntimeException(""); + throw new NoDataPublishException("no transaction data available"); } this.sendTx(txData); return true; @@ -154,8 +153,7 @@ private L1BlockRef getL1HeadBlockRef() { scope.throwIfFailed(); var block = headBlockFuture.get(); if (block == null) { - // todo create PublishException - throw new RuntimeException(""); + throw new Web3jCallException("get l1 latest block failed"); } return L1BlockRef.from(block); } catch (ExecutionException | InterruptedException e) { diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/NoDataPublishException.java b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/NoDataPublishException.java new file mode 100644 index 00000000..0ae477a4 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/NoDataPublishException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.publisher; + +/** + * NoDataPublishException class. Throws this when no available data publish to L1. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class NoDataPublishException extends RuntimeException { + + /** + * Instantiates a new NoDataPublishException. + * + * @param message the message + */ + public NoDataPublishException(String message) { + super(message); + } + + /** + * Instantiates a new NoDataPublishException. + * + * @param message the message + * @param cause the cause + */ + public NoDataPublishException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new NoDataPublishException. + * + * @param cause the cause + */ + public NoDataPublishException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublishException.java b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublishException.java new file mode 100644 index 00000000..2781f129 --- /dev/null +++ b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublishException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.batcher.publisher; + +/** + * PublishException class. Throws this when publish data to L1. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class PublishException extends RuntimeException { + + /** + * Instantiates a new PublishException. + * + * @param message the message + */ + public PublishException(String message) { + super(message); + } + + /** + * Instantiates a new PublishException. + * + * @param message the message + * @param cause the cause + */ + public PublishException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new PublishException. + * + * @param cause the cause + */ + public PublishException(Throwable cause) { + super(cause); + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java b/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java index a13b4533..6ab01040 100644 --- a/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java +++ b/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java @@ -16,6 +16,7 @@ package io.optimism.type; +import io.optimism.batcher.loader.ParseBlockException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -64,13 +65,11 @@ public record L1BlockInfo( */ public static L1BlockInfo from(byte[] data) { if (data == null || data.length != L1_INFO_LENGTH) { - // todo create ParseBlockException - throw new RuntimeException( + throw new ParseBlockException( String.format("data is unexpected length: %d", data == null ? 0 : data.length)); } if (!Objects.deepEquals(ArrayUtils.subarray(data, 0, 4), SIGNATURE_BYTES)) { - // todo create ParseBlockException - throw new RuntimeException(""); + throw new ParseBlockException(""); } BigInteger number = Numeric.toBigInt(data, 4, 32); BigInteger time = Numeric.toBigInt(data, 36, 32); diff --git a/hildr-node/src/main/java/io/optimism/derive/stages/Attributes.java b/hildr-node/src/main/java/io/optimism/derive/stages/Attributes.java index 64fc1b95..f9a7c120 100644 --- a/hildr-node/src/main/java/io/optimism/derive/stages/Attributes.java +++ b/hildr-node/src/main/java/io/optimism/derive/stages/Attributes.java @@ -22,9 +22,9 @@ import io.optimism.config.Config.SystemAccounts; import io.optimism.derive.PurgeableIterator; import io.optimism.derive.State; -import io.optimism.derive.stages.Batches.Batch; import io.optimism.engine.ExecutionPayload.PayloadAttributes; import io.optimism.l1.L1Info; +import io.optimism.utilities.derive.stages.Batch; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/hildr-node/src/main/java/io/optimism/derive/stages/BatcherTransactions.java b/hildr-node/src/main/java/io/optimism/derive/stages/BatcherTransactions.java index 58b5e751..10c194d8 100644 --- a/hildr-node/src/main/java/io/optimism/derive/stages/BatcherTransactions.java +++ b/hildr-node/src/main/java/io/optimism/derive/stages/BatcherTransactions.java @@ -17,6 +17,7 @@ package io.optimism.derive.stages; import io.optimism.derive.PurgeableIterator; +import io.optimism.utilities.derive.stages.Frame; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; @@ -27,7 +28,6 @@ import org.jctools.queues.MessagePassingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.web3j.utils.Numeric; /** * The type BatcherTransactions. @@ -119,63 +119,4 @@ public static BatcherTransaction create(byte[] data, BigInteger l1Origin) { * @since 0.1.0 */ public record BatcherTransactionMessage(List txs, BigInteger l1Origin) {} - - /** - * The type Frame. - * - * @param channelId the channel id - * @param frameNumber the frame number - * @param frameDataLen the frame data len - * @param frameData the frame data - * @param isLastFrame the is last frame - * @param l1InclusionBlock the L1 inclusion block - * @author grapebaba - * @since 0.1.0 - */ - public record Frame( - BigInteger channelId, - Integer frameNumber, - Integer frameDataLen, - byte[] frameData, - Boolean isLastFrame, - BigInteger l1InclusionBlock) { - - /** - * From data immutable pair. - * - * @param data the data - * @param offset the offset - * @param l1InclusionBlock the L1 inclusion block - * @return the immutable pair - */ - public static ImmutablePair from( - byte[] data, int offset, BigInteger l1InclusionBlock) { - final byte[] frameDataMessage = ArrayUtils.subarray(data, offset, data.length); - if (frameDataMessage.length < 23) { - throw new InvalidFrameSizeException("invalid frame size"); - } - - final BigInteger channelId = Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 0, 16)); - final int frameNumber = - Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 16, 18)).intValue(); - final int frameDataLen = - Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 18, 22)).intValue(); - final int frameDataEnd = 22 + frameDataLen; - - if (frameDataMessage.length < frameDataEnd) { - throw new InvalidFrameSizeException("invalid frame size"); - } - - final byte[] frameData = ArrayUtils.subarray(frameDataMessage, 22, frameDataEnd); - final boolean isLastFrame = frameDataMessage[frameDataEnd] != 0; - final Frame frame = - new Frame(channelId, frameNumber, frameDataLen, frameData, isLastFrame, l1InclusionBlock); - LOGGER.debug( - String.format( - "saw batcher tx: block=%d, number=%d, is_last=%b", - l1InclusionBlock, frameNumber, isLastFrame)); - - return new ImmutablePair<>(frame, offset + frameDataMessage.length); - } - } } diff --git a/hildr-node/src/main/java/io/optimism/derive/stages/Batches.java b/hildr-node/src/main/java/io/optimism/derive/stages/Batches.java index acec7298..b54bc168 100644 --- a/hildr-node/src/main/java/io/optimism/derive/stages/Batches.java +++ b/hildr-node/src/main/java/io/optimism/derive/stages/Batches.java @@ -22,8 +22,8 @@ import io.optimism.config.Config; import io.optimism.derive.PurgeableIterator; import io.optimism.derive.State; -import io.optimism.derive.stages.Batches.Batch; import io.optimism.derive.stages.Channels.Channel; +import io.optimism.utilities.derive.stages.Batch; import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.util.List; @@ -33,14 +33,12 @@ import java.util.zip.DataFormatException; import java.util.zip.Inflater; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.web3j.rlp.RlpDecoder; import org.web3j.rlp.RlpList; import org.web3j.rlp.RlpString; import org.web3j.rlp.RlpType; -import org.web3j.utils.Numeric; /** * The type Batches. @@ -308,60 +306,4 @@ public enum BatchStatus { /** Future batch status. */ Future, } - - /** - * The type Batch. - * - * @param parentHash the parent hash - * @param epochNum the epoch num - * @param epochHash the epoch hash - * @param timestamp the timestamp - * @param transactions the transactions - * @param l1InclusionBlock L1 inclusion block - * @author grapebaba - * @since 0.1.0 - */ - public record Batch( - String parentHash, - BigInteger epochNum, - String epochHash, - BigInteger timestamp, - List transactions, - BigInteger l1InclusionBlock) { - - /** - * Decode batch. - * - * @param rlp the rlp - * @param l1InclusionBlock L1 inclusion block - * @return the batch - */ - public static Batch decode(RlpList rlp, BigInteger l1InclusionBlock) { - String parentHash = ((RlpString) rlp.getValues().get(0)).asString(); - BigInteger epochNum = ((RlpString) rlp.getValues().get(1)).asPositiveBigInteger(); - String epochHash = ((RlpString) rlp.getValues().get(2)).asString(); - BigInteger timestamp = ((RlpString) rlp.getValues().get(3)).asPositiveBigInteger(); - List transactions = - ((RlpList) rlp.getValues().get(4)) - .getValues().stream() - .map(rlpString -> ((RlpString) rlpString).asString()) - .collect(Collectors.toList()); - return new Batch(parentHash, epochNum, epochHash, timestamp, transactions, l1InclusionBlock); - } - - /** - * Has invalid transactions boolean. - * - * @return the boolean - */ - public boolean hasInvalidTransactions() { - return this.transactions.stream() - .anyMatch( - s -> - StringUtils.isEmpty(s) - || (Numeric.containsHexPrefix("0x") - ? StringUtils.startsWithIgnoreCase(s, "0x7E") - : StringUtils.startsWithIgnoreCase(s, "7E"))); - } - } } diff --git a/hildr-node/src/main/java/io/optimism/derive/stages/Channels.java b/hildr-node/src/main/java/io/optimism/derive/stages/Channels.java index 86789156..95c8b646 100644 --- a/hildr-node/src/main/java/io/optimism/derive/stages/Channels.java +++ b/hildr-node/src/main/java/io/optimism/derive/stages/Channels.java @@ -20,8 +20,8 @@ import io.optimism.config.Config; import io.optimism.derive.PurgeableIterator; import io.optimism.derive.stages.BatcherTransactions.BatcherTransaction; -import io.optimism.derive.stages.BatcherTransactions.Frame; import io.optimism.derive.stages.Channels.Channel; +import io.optimism.utilities.derive.stages.Frame; import java.math.BigInteger; import java.util.Comparator; import java.util.List; diff --git a/hildr-node/src/test/java/io/optimism/derive/stages/BatcherTransactionsTest.java b/hildr-node/src/test/java/io/optimism/derive/stages/BatcherTransactionsTest.java index 3e1fa975..bacf211e 100644 --- a/hildr-node/src/test/java/io/optimism/derive/stages/BatcherTransactionsTest.java +++ b/hildr-node/src/test/java/io/optimism/derive/stages/BatcherTransactionsTest.java @@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import io.optimism.derive.stages.BatcherTransactions.BatcherTransaction; -import io.optimism.derive.stages.BatcherTransactions.Frame; +import io.optimism.utilities.derive.stages.Frame; import java.math.BigInteger; import org.apache.commons.lang3.ArrayUtils; import org.junit.jupiter.api.DisplayName; diff --git a/hildr-node/src/test/java/io/optimism/derive/stages/BatchesTest.java b/hildr-node/src/test/java/io/optimism/derive/stages/BatchesTest.java index 28288f81..8793841d 100644 --- a/hildr-node/src/test/java/io/optimism/derive/stages/BatchesTest.java +++ b/hildr-node/src/test/java/io/optimism/derive/stages/BatchesTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import io.optimism.derive.stages.Channels.Channel; +import io.optimism.utilities.derive.stages.Batch; import java.math.BigInteger; import java.util.List; import org.bouncycastle.util.encoders.Hex; @@ -140,7 +141,7 @@ void decodeBatches() { + "4c665ca197cebff1c90e5484cc8a6cb2c5b1badab35aefa35c1384f0bb64" + "59061ad574c2f37f8bbbd2e8dff5f27f020000ffff8db46838"; Channel channel = new Channel(BigInteger.ONE, Hex.decode(data), BigInteger.ONE); - List batches = Batches.decodeBatches(channel); + List batches = Batches.decodeBatches(channel); System.out.println(batches); assertEquals(6, batches.size()); diff --git a/hildr-node/src/test/java/io/optimism/derive/stages/ChannelsTest.java b/hildr-node/src/test/java/io/optimism/derive/stages/ChannelsTest.java index 63ed848a..46c7ca63 100644 --- a/hildr-node/src/test/java/io/optimism/derive/stages/ChannelsTest.java +++ b/hildr-node/src/test/java/io/optimism/derive/stages/ChannelsTest.java @@ -23,8 +23,8 @@ import io.optimism.config.Config; import io.optimism.config.Config.ChainConfig; import io.optimism.derive.stages.BatcherTransactions.BatcherTransactionMessage; -import io.optimism.derive.stages.BatcherTransactions.Frame; import io.optimism.derive.stages.Channels.Channel; +import io.optimism.utilities.derive.stages.Frame; import java.math.BigInteger; import java.util.Optional; import org.jctools.queues.MessagePassingQueue; diff --git a/hildr-utilities/build.gradle b/hildr-utilities/build.gradle index 3aad0938..300eb388 100644 --- a/hildr-utilities/build.gradle +++ b/hildr-utilities/build.gradle @@ -28,6 +28,7 @@ repositories { dependencies { api 'com.google.guava:guava:31.1-jre' api 'com.github.rholder:guava-retrying:2.0.0' + api 'org.apache.commons:commons-lang3:3.12.0' api("com.squareup.okhttp3:okhttp:5.0.0-alpha.2") api("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2") diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Batch.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Batch.java new file mode 100644 index 00000000..6706c931 --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Batch.java @@ -0,0 +1,101 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.utilities.derive.stages; + +import java.math.BigInteger; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpType; +import org.web3j.utils.Numeric; + +/** + * The type Batch. + * + * @param parentHash the parent hash + * @param epochNum the epoch num + * @param epochHash the epoch hash + * @param timestamp the timestamp + * @param transactions the transactions + * @param l1InclusionBlock L1 inclusion block + * @author grapebaba + * @since 0.1.0 + */ +public record Batch( + String parentHash, + BigInteger epochNum, + String epochHash, + BigInteger timestamp, + List transactions, + BigInteger l1InclusionBlock) { + + /** + * Encode batch. + * + * @return encoded bytes by the batch + */ + public byte[] encode() { + List collect = transactions().stream() + .map(tx -> (RlpType) RlpString.create(tx)) + .collect(Collectors.toList()); + return RlpEncoder.encode(new RlpList( + RlpString.create(parentHash()), + RlpString.create(epochNum()), + RlpString.create(epochHash()), + RlpString.create(timestamp()), + new RlpList(collect))); + } + + /** + * Decode batch. + * + * @param rlp the rlp + * @param l1InclusionBlock L1 inclusion block + * @return the batch + */ + public static Batch decode(RlpList rlp, BigInteger l1InclusionBlock) { + String parentHash = ((RlpString) rlp.getValues().get(0)).asString(); + BigInteger epochNum = ((RlpString) rlp.getValues().get(1)).asPositiveBigInteger(); + String epochHash = ((RlpString) rlp.getValues().get(2)).asString(); + BigInteger timestamp = ((RlpString) rlp.getValues().get(3)).asPositiveBigInteger(); + List transactions = + ((RlpList) rlp.getValues().get(4)) + .getValues().stream() + .map(rlpString -> ((RlpString) rlpString).asString()) + .collect(Collectors.toList()); + return new Batch(parentHash, epochNum, epochHash, timestamp, transactions, l1InclusionBlock); + } + + /** + * Has invalid transactions boolean. + * + * @return the boolean + */ + public boolean hasInvalidTransactions() { + return this.transactions.stream() + .anyMatch( + s -> + StringUtils.isEmpty(s) + || (Numeric.containsHexPrefix("0x") + ? StringUtils.startsWithIgnoreCase(s, "0x7E") + : StringUtils.startsWithIgnoreCase(s, "7E"))); + } + +} diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Frame.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Frame.java new file mode 100644 index 00000000..80fd59bf --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/Frame.java @@ -0,0 +1,131 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.utilities.derive.stages; + +import com.google.common.base.Objects; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Shorts; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.web3j.utils.Numeric; + +/** + * The type Frame. + * + * @param channelId the channel id + * @param frameNumber the frame number + * @param frameDataLen the frame data len + * @param frameData the frame data + * @param isLastFrame the is last frame + * @param l1InclusionBlock the L1 inclusion block + * @author grapebaba + * @since 0.1.0 + */ +public record Frame( + BigInteger channelId, + Integer frameNumber, + Integer frameDataLen, + byte[] frameData, + Boolean isLastFrame, + BigInteger l1InclusionBlock) { + + private static final Logger LOGGER = LoggerFactory.getLogger(Frame.class); + + /** Derivation version. */ + public static final byte DERIVATION_VERSION_0 = 0; + + /** + * Frame over head size. + */ + public static final int FRAME_V0_OVER_HEAD_SIZE = 23; + + /** + * Get tx bytes. + * + * @return tx bytes + */ + public byte[] txBytes() { + return ArrayUtils.addAll(new byte[] {DERIVATION_VERSION_0}, frameData()); + } + + /** + * Get Tx data unique code. + * + * @return unique code. + */ + public String code() { + return String.valueOf(Objects.hashCode(channelId, frameNumber)); + } + + /** + * Encode this Frame to bytes. + * + * @return encoded bytes from frame + */ + public byte[] encode() { + var bos = new ByteArrayOutputStream(); + bos.writeBytes(Numeric.toBytesPadded(channelId, 16)); + bos.writeBytes(Shorts.toByteArray((short) frameNumber().intValue())); + bos.writeBytes(Ints.toByteArray(frameData().length)); + bos.writeBytes(frameData()); + bos.write(isLastFrame() ? 1 : 0); + return bos.toByteArray(); + } + + /** + * From data immutable pair. + * + * @param data the data + * @param offset the offset + * @param l1InclusionBlock the L1 inclusion block + * @return the immutable pair + */ + public static ImmutablePair from( + byte[] data, int offset, BigInteger l1InclusionBlock) { + final byte[] frameDataMessage = ArrayUtils.subarray(data, offset, data.length); + if (frameDataMessage.length < 23) { + throw new InvalidFrameSizeException("invalid frame size"); + } + + final BigInteger channelId = Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 0, 16)); + final int frameNumber = + Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 16, 18)).intValue(); + final int frameDataLen = + Numeric.toBigInt(ArrayUtils.subarray(frameDataMessage, 18, 22)).intValue(); + final int frameDataEnd = 22 + frameDataLen; + + if (frameDataMessage.length < frameDataEnd) { + throw new InvalidFrameSizeException("invalid frame size"); + } + + final byte[] frameData = ArrayUtils.subarray(frameDataMessage, 22, frameDataEnd); + final boolean isLastFrame = frameDataMessage[frameDataEnd] != 0; + final Frame frame = + new Frame(channelId, frameNumber, frameDataLen, frameData, isLastFrame, l1InclusionBlock); + LOGGER.debug( + String.format( + "saw batcher tx: block=%d, number=%d, is_last=%b", + l1InclusionBlock, frameNumber, isLastFrame)); + + return new ImmutablePair<>(frame, offset + frameDataMessage.length); + } + +} diff --git a/hildr-node/src/main/java/io/optimism/derive/stages/InvalidFrameSizeException.java b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/InvalidFrameSizeException.java similarity index 95% rename from hildr-node/src/main/java/io/optimism/derive/stages/InvalidFrameSizeException.java rename to hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/InvalidFrameSizeException.java index b6bb3eb8..3eb8f609 100644 --- a/hildr-node/src/main/java/io/optimism/derive/stages/InvalidFrameSizeException.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/derive/stages/InvalidFrameSizeException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.derive.stages; +package io.optimism.utilities.derive.stages; /** * The type InvalidFrameSizeException. diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/exception/GasOverflowException.java b/hildr-utilities/src/main/java/io/optimism/utilities/exception/GasOverflowException.java new file mode 100644 index 00000000..6597dc8c --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/utilities/exception/GasOverflowException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 q315xia@163.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package io.optimism.utilities.exception; + +/** + * GasOverflowException class. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class GasOverflowException extends RuntimeException { + + /** + * Instantiates a new GasOverflowException. + * + * @param message the message + */ + public GasOverflowException(String message) { + super(message); + } + + /** + * Instantiates a new GasOverflowException. + * + * @param message the message + * @param cause the cause + */ + public GasOverflowException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new GasOverflowException. + * + * @param cause the cause + */ + public GasOverflowException(Throwable cause) { + super(cause); + } +}