diff --git a/hildr-batcher/build.gradle b/hildr-batcher/build.gradle index 18a87a34..f25f9d81 100644 --- a/hildr-batcher/build.gradle +++ b/hildr-batcher/build.gradle @@ -6,6 +6,7 @@ plugins { id "com.diffplug.spotless" version "6.19.0" id "net.ltgt.errorprone" version "3.1.0" id 'org.graalvm.buildtools.native' version '0.9.22' + id 'com.github.johnrengelman.shadow' version '8.1.1' } group = 'io.optimism' @@ -46,6 +47,11 @@ tasks.withType(JavaExec).configureEach { jvmArgs += ["--add-modules", "jdk.incubator.concurrent"] } +tasks.withType(Test).configureEach { + jvmArgs += "--enable-preview" + jvmArgs += ["--add-modules", "jdk.incubator.concurrent"] +} + dependencies { implementation project(':hildr-utilities') implementation 'com.github.gestalt-config:gestalt-core:0.20.4' @@ -93,12 +99,11 @@ dependencies { exclude group: 'org.apache.tuweni', module: 'tuweni-rlp' exclude group: 'org.apache.tuweni', module: 'tuweni-crypto' } + implementation 'org.bouncycastle:bcprov-jdk18on:1.76' implementation 'info.picocli:picocli:4.7.3' annotationProcessor 'info.picocli:picocli-codegen:4.7.3' - implementation 'io.tmio:tuweni-crypto:2.4.2' - // implementation fileTree(dir: '../lib', include: '*.jar') // Use JUnit Jupiter for testing. testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' @@ -238,7 +243,6 @@ tasks.withType(Test).configureEach { "-Djunit.platform.reporting.open.xml.enabled=true", "-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}", "--enable-preview", - "--add-modules jdk.incubator.concurrent" ] } as CommandLineArgumentProvider) } @@ -259,17 +263,16 @@ javadoc { } jar { - dependsOn(configurations.runtimeClasspath) + enabled = false manifest { - attributes "Main-Class": "io.optimism.HildrBatcher" + attributes "Main-Class": "io.optimism.batcher.HildrBatcher" + attributes "Multi-Release": "true" } + dependsOn(shadowJar) +} - duplicatesStrategy = DuplicatesStrategy.WARN - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } { - exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF' - } +shadowJar { + archiveFileName = "${project.name}-${project.version}.jar" } nativeCompile { 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 6b5f7a99..32e51ec3 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/BatcherSubmitter.java @@ -61,7 +61,7 @@ public BatcherSubmitter(Config config) { this.channelManager = new ChannelManager(ChannelConfig.from(config), CompressorConfig.from(config)); this.blockLoader = new BlockLoader(LoaderConfig.from(config), this.channelManager::addL2Block); - + this.blockLoader.init(); this.channelPublisher = new ChannelDataPublisher( PublisherConfig.from(config, this.blockLoader.getRollConfig()), diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelImpl.java b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelImpl.java index a492f6e6..898b7a85 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelImpl.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/channel/ChannelImpl.java @@ -17,6 +17,7 @@ package io.optimism.batcher.channel; import io.optimism.batcher.compressor.Compressor; +import io.optimism.batcher.compressor.exception.CompressorException; import io.optimism.batcher.exception.UnsupportedException; import io.optimism.batcher.telemetry.BatcherMetrics; import io.optimism.type.BlockId; @@ -128,9 +129,10 @@ public L1BlockInfo addBlock(final EthBlock.Block block) { try { this.addBatch(batch); this.blocks.add(block); - } catch (ChannelFullException e) { + } catch (ChannelFullException | CompressorException e) { this.isFull = true; } + this.splitToFrame(); this.updateSeqWindowTimeout(batch); return l1Info; } @@ -312,6 +314,7 @@ private Tuple2 blockToBatch(EthBlock.Block block) { if (!DEPOSIT_TX_TYPE.equalsIgnoreCase(depositTxObj.getType())) { throw new ChannelException("block txs not contains deposit tx"); } + final L1BlockInfo l1Info = L1BlockInfo.from(Numeric.hexStringToByteArray(depositTxObj.getInput())); @@ -345,8 +348,9 @@ private int addBatch(Batch batch) { "could not add %d bytes to channel of %d bytes, max is %d", encode.length, this.rlpLength.get(), MAX_RLP_BYTES_PER_CHANNEL)); } + int n = this.compressor.write(encode); this.rlpLength.addAndGet(encode.length); - return this.compressor.write(encode); + return n; } private void closeAndOutputAllFrames() { @@ -383,8 +387,9 @@ private Frame frame(final int maxSize) { } var lastFrameFlag = false; var dataSize = maxSize - Frame.FRAME_V0_OVER_HEAD_SIZE; - if (dataSize > this.compressor.length()) { - dataSize = this.compressor.length(); + var cprLength = this.compressor.length(); + if (dataSize > cprLength) { + dataSize = cprLength; lastFrameFlag = this.isClose; } 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 959b1943..8e4dcaa1 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 @@ -117,7 +117,13 @@ public Frame txData(final BlockId l1Head) { } // no channel if (!this.hasSpace(this.latestChannel)) { - this.latestChannel = this.openChannel(l1Head); + this.latestChannel = this.openChannel(); + LOGGER.info( + "Created a channel: id:{}, l1Head: {}, blocksPending:{}", + this.latestChannel, + l1Head, + this.blocks.size()); + this.metrics.recordChannelOpened(null, this.blocks.size()); } this.pushBlocks(this.latestChannel); this.updateChannelTimeout(l1Head); @@ -222,12 +228,8 @@ private boolean hasSpace(final Channel channel) { return channel != null && !channel.isFull(); } - private Channel openChannel(final BlockId l1Head) { - Channel ch = new ChannelImpl(this.chConfig, Compressors.create(this.compressorConfig)); - LOGGER.info( - "Created a channel: id:{}, l1Head: {}, blocksPending:{}", ch, l1Head, this.blocks.size()); - this.metrics.recordChannelOpened(null, this.blocks.size()); - return ch; + private Channel openChannel() { + return new ChannelImpl(this.chConfig, Compressors.create(this.compressorConfig)); } private void pushBlocks(final Channel lastChannel) { 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 aec4b9b1..f6287e12 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,7 +16,7 @@ package io.optimism.batcher.compressor; -import io.optimism.batcher.compressor.exception.CompressorFullException; +import io.optimism.batcher.compressor.exception.CompressorException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; @@ -37,8 +37,12 @@ public class RatioCompressor implements Compressor { private final int inputThreshold; + private final byte[] compressed; + private volatile ByteArrayOutputStream bos; + private boolean closed; + private int pos; private int inputLength; @@ -48,6 +52,7 @@ public class RatioCompressor implements Compressor { this.deflater = new Deflater(Deflater.BEST_COMPRESSION); this.inputThreshold = inputThreshold(); this.bos = new ByteArrayOutputStream(this.inputThreshold); + this.compressed = new byte[2048]; this.pos = 0; this.inputLength = 0; } @@ -55,17 +60,30 @@ public class RatioCompressor implements Compressor { @Override public int write(byte[] p) { if (this.isFull()) { - throw new CompressorFullException("the target amount of input data has been reached limit"); + throw new CompressorException("the target amount of input data has been reached limit"); + } + if (this.isClose()) { + throw new CompressorException("the compressor has been closed"); } 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); + int len; + // int compressedLength = 0; + do { + len = this.deflater.deflate(compressed, 0, compressed.length, Deflater.SYNC_FLUSH); + if (len > 0) { + // compressedLength += len; + this.bos.write(compressed, 0, len); + } + } while (len > 0); this.deflater.reset(); return p.length; } + private boolean isClose() { + return this.closed; + } + @Override public int read(byte[] p) { byte[] data = this.bos.toByteArray(); @@ -105,6 +123,7 @@ public boolean isFull() { @Override public void close() throws IOException { + closed = true; this.deflater.finish(); this.deflater.end(); } diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorException.java similarity index 88% rename from hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java rename to hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorException.java index de0e7056..13c7af03 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorFullException.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/compressor/exception/CompressorException.java @@ -23,14 +23,14 @@ * @author thinkAfCod * @since 0.1.1 */ -public class CompressorFullException extends RuntimeException { +public class CompressorException extends RuntimeException { /** * Constructor of CompressorFullException. * * @param message error message */ - public CompressorFullException(String message) { + public CompressorException(String message) { super(message); } } 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 bec49556..154b59f0 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 @@ -26,12 +26,14 @@ import io.optimism.type.L2BlockRef; import io.optimism.type.OpEthSyncStatusRes; import io.optimism.type.RollupConfigRes; +import io.optimism.type.RollupConfigResult; import io.optimism.utilities.rpc.Web3jProvider; import java.io.Closeable; import java.math.BigInteger; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; +import javax.annotation.Nonnull; import jdk.incubator.concurrent.StructuredTaskScope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +60,7 @@ public class BlockLoader implements Closeable { private static final String OP_ROLLUP_CONFIG = "optimism_rollupConfig"; - private static final String OP_SYNC_STATUS = "optimism_syncStatus"; + static final String OP_SYNC_STATUS = "optimism_syncStatus"; private final Web3j l2Client; @@ -70,9 +72,9 @@ public class BlockLoader implements Closeable { private final Consumer blockConsumer; - private BlockId latestLoadedBlock; + BlockId latestLoadedBlock; - private RollupConfigRes.RollupConfig rollupConfig; + private RollupConfigResult rollupConfig; /** * Constructor of BlockLoader. @@ -80,14 +82,21 @@ public class BlockLoader implements Closeable { * @param config LoaderConfig instance * @param blockConsumer Consumer block loaded from L2 */ - public BlockLoader(LoaderConfig config, Consumer blockConsumer) { + public BlockLoader(LoaderConfig config, @Nonnull Consumer blockConsumer) { this.l2Client = Web3jProvider.createClient(config.l2RpcUrl()); Tuple2 tuple = Web3jProvider.create(config.rollupUrl()); this.rollupClient = tuple.component1(); this.rollupService = tuple.component2(); this.metrics = config.metrics(); this.blockConsumer = blockConsumer; - this.rollupConfig = loadRollConfig(); + this.latestLoadedBlock = new BlockId("0x0", BigInteger.ZERO); + } + + /** Should be called before load block. */ + public void init() { + if (this.rollupConfig == null) { + this.rollupConfig = this.loadRollConfig(); + } } /** @@ -95,13 +104,17 @@ public BlockLoader(LoaderConfig config, Consumer blockConsumer) * * @return rollup config object */ - public RollupConfigRes.RollupConfig getRollConfig() { + public RollupConfigResult getRollConfig() { return this.rollupConfig; } /** Trigger load block from L2. */ public void loadBlock() { - this.loadBlocksIntoState(); + try { + this.loadBlocksIntoState(); + } catch (SyncStatusException e) { + LOGGER.warn("sync status info may not correct", e); + } } @Override @@ -110,28 +123,10 @@ public void close() { this.rollupClient.shutdown(); } - private RollupConfigRes.RollupConfig loadRollConfig() { - try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { - var future = - scope.fork( - () -> - new Request<>( - OP_ROLLUP_CONFIG, List.of(), this.rollupService, RollupConfigRes.class) - .send() - .getConfig()); - scope.join(); - scope.throwIfFailed(); - return future.resultNow(); - } catch (ExecutionException | InterruptedException e) { - Thread.currentThread().interrupt(); - throw new Web3jCallException("failed to get op-rollup config", e); - } - } - - private void loadBlocksIntoState() { - Tuple2 blockNumbers = this.calculateL2BlockRangeToStore(); + void loadBlocksIntoState() { + Tuple2 blockNumbers = this.calculateL2BlockRangeToStore(); final BigInteger start = blockNumbers.component1().number(); - final BigInteger end = blockNumbers.component2(); + final BigInteger end = blockNumbers.component2().number(); var stopBlock = end.add(BigInteger.ONE); EthBlock.Block lastBlock = null; for (BigInteger i = start.add(BigInteger.ONE); @@ -144,29 +139,16 @@ private void loadBlocksIntoState() { if (lastBlock == null) { throw new BlockLoaderException("get latest block failed"); } - var l2Ref = l2BlockToBlockRef(lastBlock, rollupConfig.genesis()); + var l2Ref = l2BlockToBlockRef(lastBlock, getRollConfig().getGenesis()); this.metrics.recordL2BlocksLoaded(l2Ref); } - private Tuple2 calculateL2BlockRangeToStore() { - final Request req = - new Request<>(OP_SYNC_STATUS, List.of(), this.rollupService, OpEthSyncStatusRes.class); - OpEthSyncStatusRes.OpEthSyncStatus syncStatus = null; - try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { - var future = scope.fork(req::send); - scope.join(); - scope.throwIfFailed(); - syncStatus = future.resultNow().getOpEthSyncStatus(); - } catch (ExecutionException e) { - throw new Web3jCallException("StructuredTaskScope execute syncStatus failed:", e); - } catch (InterruptedException e) { - throw new Web3jCallException( - "Thread has been interrupted while calling calculateL2BlockRangeToStore:", e); - } - if (syncStatus.headL1().equals(L1BlockRef.emptyBlock)) { + Tuple2 calculateL2BlockRangeToStore() { + var syncStatus = this.requestSyncStatus(); + if (syncStatus.headL1() == null || syncStatus.headL1().equals(L1BlockRef.emptyBlock)) { throw new SyncStatusException("empty sync status"); } - if (latestLoadedBlock == null || latestLoadedBlock.number().equals(BigInteger.ZERO)) { + if (latestLoadedBlock.number().equals(BigInteger.ZERO)) { LOGGER.info("Starting batch-submitter work at L2 safe-head: {}", syncStatus.safeL2()); latestLoadedBlock = syncStatus.safeL2().toId(); } else if (latestLoadedBlock.number().compareTo(syncStatus.safeL2().number()) <= 0) { @@ -177,12 +159,15 @@ private Tuple2 calculateL2BlockRangeToStore() { if (syncStatus.safeL2().number().compareTo(syncStatus.unsafeL2().number()) >= 0 || latestLoadedBlock.number().compareTo(syncStatus.unsafeL2().number()) >= 0) { - throw new SyncStatusException("L2 safe head ahead of L2 unsafe head"); + throw new SyncStatusException( + String.format( + "L2 safe head(%d) ahead of L2 unsafe head(%d)", + syncStatus.safeL2().number(), syncStatus.unsafeL2().number())); } - return new Tuple2<>(latestLoadedBlock, syncStatus.unsafeL2().number()); + return new Tuple2<>(latestLoadedBlock, syncStatus.unsafeL2().toId()); } - private L2BlockRef l2BlockToBlockRef(final EthBlock.Block block, Genesis genesis) { + L2BlockRef l2BlockToBlockRef(final EthBlock.Block block, Genesis genesis) { BlockId l1Origin = null; BigInteger sequenceNumber = null; if (block.getNumber().equals(genesis.l2().number())) { @@ -221,23 +206,65 @@ private L2BlockRef l2BlockToBlockRef(final EthBlock.Block block, Genesis genesis } private EthBlock.Block loadBlockToChannel(BigInteger number) { + try { + var block = this.getBlock(number); + blockConsumer.accept(block); + return block; + } catch (ReorgException e) { + this.latestLoadedBlock = null; + throw e; + } + } + + RollupConfigResult loadRollConfig() { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + var future = + scope.fork( + () -> + new Request<>( + OP_ROLLUP_CONFIG, List.of(), this.rollupService, RollupConfigRes.class) + .send() + .getConfig()); + scope.join(); + scope.throwIfFailed(); + return future.resultNow(); + } catch (ExecutionException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new Web3jCallException("failed to get op-rollup config", e); + } + } + + OpEthSyncStatusRes.OpEthSyncStatus requestSyncStatus() { + final Request req = + new Request<>(OP_SYNC_STATUS, List.of(), this.rollupService, OpEthSyncStatusRes.class); + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + var future = scope.fork(req::send); + scope.join(); + scope.throwIfFailed(); + return future.resultNow().getOpEthSyncStatus(); + } catch (ExecutionException e) { + throw new Web3jCallException("StructuredTaskScope execute syncStatus failed:", e); + } catch (InterruptedException e) { + throw new Web3jCallException( + "Thread has been interrupted while calling calculateL2BlockRangeToStore:", e); + } + } + + EthBlock.Block getBlock(BigInteger blockNumber) { final Request ethBlockRequest = - l2Client.ethGetBlockByNumber(DefaultBlockParameter.valueOf(number), true); + l2Client.ethGetBlockByNumber(DefaultBlockParameter.valueOf(blockNumber), true); EthBlock.Block block; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var blockFuture = scope.fork(ethBlockRequest::send); scope.join(); scope.throwIfFailed(); block = blockFuture.get().getBlock(); - if (block != null) { - blockConsumer.accept(block); + if (block == null) { + throw new Web3jCallException("failed to get block by number:" + blockNumber); } } catch (ExecutionException | InterruptedException e) { Thread.currentThread().interrupt(); - throw new SyncStatusException("failed to get block by number failed", e); - } catch (ReorgException e) { - this.latestLoadedBlock = null; - throw e; + throw new SyncStatusException("failed to get block by number", e); } return block; } 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 41c6fc80..53eb7efb 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 @@ -105,13 +105,18 @@ public ChannelDataPublisher( public boolean publishPendingBlock() { boolean hasData = false; boolean sendData = true; - while (sendData) { - sendData = this.publishTxToL1(); - if (!hasData) { - hasData = sendData; + try { + while (sendData) { + sendData = this.publishTxToL1(); + if (!hasData) { + hasData = sendData; + } } + return hasData; + } catch (NoDataPublishException e) { + LOGGER.info(e.getMessage()); + return hasData; } - return hasData; } @Override diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublisherConfig.java b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublisherConfig.java index 21083bee..6332affe 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublisherConfig.java +++ b/hildr-batcher/src/main/java/io/optimism/batcher/publisher/PublisherConfig.java @@ -18,7 +18,7 @@ import io.optimism.batcher.config.Config; import io.optimism.batcher.telemetry.BatcherMetrics; -import io.optimism.type.RollupConfigRes; +import io.optimism.type.RollupConfigResult; import java.math.BigInteger; /** @@ -46,12 +46,12 @@ public record PublisherConfig( * @param rollupConfig Rollup config, get from rollup node api * @return PublisherConfig instance */ - public static PublisherConfig from(Config config, RollupConfigRes.RollupConfig rollupConfig) { + public static PublisherConfig from(Config config, RollupConfigResult rollupConfig) { return new PublisherConfig( config.l1RpcUrl(), config.l1Signer(), - rollupConfig.l1ChainId(), - rollupConfig.batchInboxAddress(), + rollupConfig.getL1ChainId(), + rollupConfig.getBatchInboxAddress(), config.metrics()); } } diff --git a/hildr-batcher/src/main/java/io/optimism/type/RollupConfigRes.java b/hildr-batcher/src/main/java/io/optimism/type/RollupConfigRes.java index 6eba939f..18c8811f 100644 --- a/hildr-batcher/src/main/java/io/optimism/type/RollupConfigRes.java +++ b/hildr-batcher/src/main/java/io/optimism/type/RollupConfigRes.java @@ -16,7 +16,6 @@ package io.optimism.type; -import java.math.BigInteger; import org.web3j.protocol.core.Response; /** @@ -25,7 +24,7 @@ * @author thinkAfCod * @since 0.1.1 */ -public class RollupConfigRes extends Response { +public class RollupConfigRes extends Response { /** Constructor of RollupConfigRes. */ public RollupConfigRes() {} @@ -35,46 +34,12 @@ public RollupConfigRes() {} * * @return rollup config info */ - public RollupConfig getConfig() { + public RollupConfigResult getConfig() { return getResult(); } @Override - public void setResult(RollupConfig result) { + public void setResult(RollupConfigResult result) { super.setResult(result); } - - /** - * Rollup config. - * - * @param genesis Genesis anchor point of the rollup. - * @param blockTime Seconds per L2 block - * @param maxSequencerDrift Sequencer batches may not be more than MaxSequencerDrift seconds after - * the L1 timestamp of the sequencing window end. - * @param seqWindowSize Number of epochs (L1 blocks) per sequencing window, including the epoch L1 - * origin block itself - * @param channelTimeout Number of L1 blocks between when a channel can be opened and when it must - * be closed by. - * @param l1ChainId Required to verify L1 signatures - * @param l2ChainId Required to identify the L2 network and create p2p signatures unique for this - * chain. - * @param regolithTime RegolithTime sets the activation time of the Regolith network-upgrade: a - * pre-mainnet Bedrock change that addresses findings of the Sherlock contest related to - * deposit attributes. "Regolith" is the loose deposited rock that sits on top of Bedrock. - * @param batchInboxAddress L1 address that batches are sent to. - * @param depositContractAddress L1 Deposit Contract Address. - * @param l1SystemConfigAddress L1 System Config Address. - */ - public record RollupConfig( - Genesis genesis, - BigInteger blockTime, - BigInteger maxSequencerDrift, - BigInteger seqWindowSize, - BigInteger channelTimeout, - BigInteger l1ChainId, - BigInteger l2ChainId, - BigInteger regolithTime, - String batchInboxAddress, - String depositContractAddress, - String l1SystemConfigAddress) {} } diff --git a/hildr-batcher/src/test/java/io/optimism/batcher/TestConstants.java b/hildr-batcher/src/test/java/io/optimism/batcher/TestConstants.java new file mode 100644 index 00000000..ff004262 --- /dev/null +++ b/hildr-batcher/src/test/java/io/optimism/batcher/TestConstants.java @@ -0,0 +1,67 @@ +/* + * 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; + +import java.util.Map; +import org.apache.commons.lang3.StringUtils; + +/** + * The type Test constants. + * + * @author thinkAfCod + * @since 2023.06 + */ +public class TestConstants { + + /** The constant isConfiguredApiKeyEnv. */ + public static boolean isConfiguredApiKeyEnv = false; + + private static final String ETH_API_ENV = "ETH_API_KEY"; + private static final String OPT_API_ENV = "OPT_API_KEY"; + private static final String ROLLUP_API_KEY = "ROLLUP_API_KEY"; + + /** The L 1 rpc url format. */ + static String l1RpcUrlFormat = "https://eth-goerli.g.alchemy.com/v2/%s"; + + /** The L 2 rpc url format. */ + static String l2RpcUrlFormat = "https://opt-goerli.g.alchemy.com/v2/%s"; + + public static String l1RpcUrl; + + public static String l2RpcUrl; + + public static String rollupRpcUrl; + + static { + Map envs = System.getenv(); + String ethApiKey = envs.get(ETH_API_ENV); + String optApiKey = envs.get(OPT_API_ENV); + String rollUpApiUrl = envs.get(ROLLUP_API_KEY); + + if (!StringUtils.isEmpty(ethApiKey)) { + l1RpcUrl = l1RpcUrlFormat.formatted(ethApiKey); + } + if (!StringUtils.isEmpty(optApiKey)) { + l2RpcUrl = l2RpcUrlFormat.formatted(optApiKey); + } + if (!StringUtils.isEmpty(rollUpApiUrl)) { + rollupRpcUrl = rollUpApiUrl; + } + } + + private TestConstants() {} +} diff --git a/hildr-batcher/src/test/java/io/optimism/batcher/compressor/RatioCompressorTest.java b/hildr-batcher/src/test/java/io/optimism/batcher/compressor/RatioCompressorTest.java new file mode 100644 index 00000000..899d9573 --- /dev/null +++ b/hildr-batcher/src/test/java/io/optimism/batcher/compressor/RatioCompressorTest.java @@ -0,0 +1,42 @@ +package io.optimism.batcher.compressor; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; +import org.junit.jupiter.api.Test; + +/** + * Ratio compressor test case. + * + * @author thinkAfCod + * @since 0.1.1 + */ +class RatioCompressorTest { + + @Test + public void testCompressor() throws DataFormatException, IOException { + var cprsConfig = new CompressorConfig(10_000, 1, "0.4", Compressors.RatioKind); + var cprr = new RatioCompressor(cprsConfig); + + final String source = "test compressor data 123456789987654321"; + byte[] sourceBytes = source.getBytes(StandardCharsets.UTF_8); + cprr.write(sourceBytes); + byte[] tmp = new byte[100]; + int n = cprr.read(tmp); + cprr.close(); + + Inflater inflater = new Inflater(); + inflater.setInput(Arrays.copyOf(tmp, n)); + + byte[] uncompressed = new byte[sourceBytes.length]; + inflater.inflate(uncompressed); + inflater.finished(); + inflater.end(); + + assertEquals(source, new String(uncompressed)); + } +} diff --git a/hildr-batcher/src/test/java/io/optimism/batcher/loader/BlockLoaderTest.java b/hildr-batcher/src/test/java/io/optimism/batcher/loader/BlockLoaderTest.java new file mode 100644 index 00000000..7267da4d --- /dev/null +++ b/hildr-batcher/src/test/java/io/optimism/batcher/loader/BlockLoaderTest.java @@ -0,0 +1,145 @@ +/* + * 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; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import io.optimism.batcher.TestConstants; +import io.optimism.batcher.telemetry.BatcherMetrics; +import io.optimism.type.BlockId; +import io.optimism.type.L1BlockRef; +import io.optimism.type.L2BlockRef; +import io.optimism.type.OpEthSyncStatusRes; +import io.optimism.type.RollupConfigResult; +import io.optimism.utilities.rpc.Web3jProvider; +import java.io.IOException; +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.tuples.generated.Tuple2; + +/** + * test case of InnerWatcher. + * + * @author thinkAfCod + * @since 2023.05 + */ +class BlockLoaderTest { + + static LoaderConfig config; + static BigInteger blockNumber; + + @BeforeAll + static void setUp() throws IOException { + BlockLoaderTest.config = + new LoaderConfig( + TestConstants.l2RpcUrl, TestConstants.rollupRpcUrl, mock(BatcherMetrics.class)); + } + + @AfterAll + static void tearDown() throws IOException {} + + @Test + void calculateL2BlockRangeToStore() { + var config = new LoaderConfig("http://fakeurl", "http://fakeurl", mock(BatcherMetrics.class)); + BlockLoader loader = spy(new BlockLoader(config, (unused) -> {})); + doReturn( + new OpEthSyncStatusRes.OpEthSyncStatus( + null, + null, + new L1BlockRef("0xhead", BigInteger.valueOf(20L), null, null), // headl1 + null, + null, + new L2BlockRef( + "0xunsafel2", BigInteger.valueOf(21L), null, null, null, null), // unsafeL2 + new L2BlockRef( + "0xsafel2", BigInteger.valueOf(20L), null, null, null, null), // safeL2 + null, + null)) + .doReturn( + new OpEthSyncStatusRes.OpEthSyncStatus( + null, + null, + new L1BlockRef("0xhead", BigInteger.valueOf(20L), null, null), // headl1 + null, + null, + new L2BlockRef( + "0xunsafel2", BigInteger.valueOf(20L), null, null, null, null), // unsafeL2 + new L2BlockRef( + "0xsafel2", BigInteger.valueOf(21L), null, null, null, null), // safeL2 + null, + null)) + .when(loader) + .requestSyncStatus(); + Tuple2 res = loader.calculateL2BlockRangeToStore(); + + assertNotNull(res.component1(), "start block id should not be null"); + assertNotNull(res.component2(), "end block id should not be null"); + assertTrue(res.component1().number().compareTo(res.component2().number()) <= 0); + + assertThrows(SyncStatusException.class, loader::calculateL2BlockRangeToStore); + } + + @Test + public void testLoadBlock() throws IOException { + if (StringUtils.isEmpty(TestConstants.l2RpcUrl) + || StringUtils.isEmpty(TestConstants.rollupRpcUrl)) { + return; + } + + Web3j l2RpcClient = Web3jProvider.createClient(TestConstants.l2RpcUrl); + BlockLoaderTest.blockNumber = l2RpcClient.ethBlockNumber().send().getBlockNumber(); + l2RpcClient.shutdown(); + + var consumeCount = new AtomicInteger(); + var blockConsumer = + (Consumer) + block -> { + consumeCount.addAndGet(1); + }; + + BlockLoader loader = spy(new BlockLoader(config, blockConsumer)); + var mockedBlock = new EthBlock.Block(); + mockedBlock.setHash("testHash"); + mockedBlock.setNumber("0x123"); + doReturn(mockedBlock).when(loader).getBlock(any()); + doReturn( + new Tuple2<>( + new BlockId("", BlockLoaderTest.blockNumber.subtract(BigInteger.valueOf(100L))), + new BlockId("", BlockLoaderTest.blockNumber.subtract(BigInteger.valueOf(80L))))) + .when(loader) + .calculateL2BlockRangeToStore(); + doReturn(null).when(loader).l2BlockToBlockRef(any(), any()); + doReturn(new RollupConfigResult()).when(loader).getRollConfig(); + loader.loadBlocksIntoState(); + assertEquals(20, consumeCount.get()); + assertEquals(loader.latestLoadedBlock.number(), mockedBlock.getNumber()); + } +} diff --git a/hildr-batcher/src/test/java/io/optimism/batcher/publisher/ChannelDataPublisherTest.java b/hildr-batcher/src/test/java/io/optimism/batcher/publisher/ChannelDataPublisherTest.java new file mode 100644 index 00000000..183b8118 --- /dev/null +++ b/hildr-batcher/src/test/java/io/optimism/batcher/publisher/ChannelDataPublisherTest.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * ChannelDataPublisher test case. + * + * @author thinkAfCod + * @since 0.1.1 + */ +class ChannelDataPublisherTest {} diff --git a/hildr-batcher/src/test/resources/ch_blocks.json b/hildr-batcher/src/test/resources/ch_blocks.json new file mode 100644 index 00000000..b94df474 --- /dev/null +++ b/hildr-batcher/src/test/resources/ch_blocks.json @@ -0,0 +1,1297 @@ +[ + { + "number": "0xfed909", + "hash": "0xc7b2230e3da22619fe16011c508385da0b80a2ee9f86d4f5ff014a9c5945c512", + "transactions": [ + { + "blockHash": "0xc7b2230e3da22619fe16011c508385da0b80a2ee9f86d4f5ff014a9c5945c512", + "blockNumber": "0xfed909", + "hash": "0x9cc77a10a90fc10a0acdf04cb6b771f814102351717e5e4da5aa5496bab15afb", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000000000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e0", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x13722cdb0636e661c15021b7763e1a471c17a34c81150fa39768bf246677eaa6", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xba25", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x697fdae3863bd52085780a41cfc8d7b3abe23bf784308c19cc7c76473dad299f", + "receiptsRoot": "0x030b2afe4ffa211c38dc7bd40db30e3d64b2f8463e24ff7423a3bb84ba449a47", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x2caaf3159a2c1d8a07e1c290cbeea0d993cb09077cf36a8c35b29f26fca1cd68", + "timestamp": "0x65421c76", + "totalDifficulty": "0x0", + "transactionsRoot": "0x408b142b4d1078fda5c294ac88b1b1386ec3b1ec3f557cfbcffa93d96976ee8c", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90a", + "hash": "0x7057d89f6f6d1da401815eb2c736deb252dd1f9de404bc02f2373367af559e61", + "transactions": [ + { + "blockHash": "0x7057d89f6f6d1da401815eb2c736deb252dd1f9de404bc02f2373367af559e61", + "blockNumber": "0xfed90a", + "hash": "0xed96b592ff6d32d3aef85ba0c75313e78375225b019a11be61e37cb98bffe943", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000010000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e1", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xef1e4a151be7900e9c610d16d1524b24357c3919b80c93c5aa8d398ca939420d", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x7057d89f6f6d1da401815eb2c736deb252dd1f9de404bc02f2373367af559e61", + "blockNumber": "0xfed90a", + "hash": "0xfabb9749531c86379b531421980865eb895cdd9a3bf58f70135b335599a906f4", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0xe425e", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e0594196130fab5e6eb6cb6c936e20185243dc9756e285ee0ce269d8a5877ac917b35cabe1740f4f381598bd2680c0e10cab6fe26f32c53c8f2ee0f02c023eeb8900000000000000000000000000000000000000000000000000000000000ff2cd000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c69000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020e18f442de5d60407d0f5ff214a3ef3fb3289d28f7a395f7cf511041ccac953570000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000200fb000000000000000000000000e543df75c1283a2b023f42f49640c37f10c7c1cb0000000000000000000000004ae7298d7edad7f1b0dabda4407fbabf78e2d59b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000046f6d6e6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c69000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a492056d8500000000000000000000000094e2cffd3a5ae36180905aaa81b538a816e63949000000000000000000000000000000000000000000000001d7d843dc3b480000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000046f6d6e69000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041fafcf2627b2eedcc896b5eef7455097aa448e6532d049b1531c97781db7f1465358502d5990f93e977a7499c5a11ce1c0a92e8c8b8143770e71a77909b1ab95d1c00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef87", + "r": "0x5ddabc95dbdf204a5f472ff5202284f58ada1363d6426a6fbb8814ec150a198b", + "s": "0x35bd1a598af8535b597e49dd5018a8f7ed182f258e5abe88c4f8b458d9826caf", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xe6a2d", + "logsBloom": "0x0008000000000000000010200000000204000000000000000000400000000000000000000000000000080000000000000000000000000000010000008000000004000000000000000000000a101000000000000000000208000400000000000000080000020000000000000000000800000000000800000000000010000001000000000000000840000000000000000000000000000000000000000004000000000010000000000000000000800000040000020001000000000000000000000000000002000000000000000000000000000000000000001000000002400020000000000000000000000000002804000000080000080000000000000000000020", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0xc7b2230e3da22619fe16011c508385da0b80a2ee9f86d4f5ff014a9c5945c512", + "receiptsRoot": "0x8e44f3e9a11974769d66a9b001b96e861c1728af260fb828ce5feac44008f9ae", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x963", + "stateRoot": "0x7b1dcc908b67e58122c4eb89f0a25c1efd2767a24dc0416b8c3214d67d8fba18", + "timestamp": "0x65421c78", + "totalDifficulty": "0x0", + "transactionsRoot": "0x63aa6510d5abaf2d6dbe82655f5af0067e5d69a6f8950dc9c9d3af3c2bd99e97", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90b", + "hash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "transactions": [ + { + "blockHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "blockNumber": "0xfed90b", + "hash": "0xde8b0fdf8573e94433c0d8192f386e39e84abdc510f87470935cb9853a1a2a24", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000020000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e2", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x1bf4d0d4f1f7cd61fd3fa0a1449145b2d475b56cb6230079622251c5b36670a5", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "blockNumber": "0xfed90b", + "hash": "0xcf9c0827e21faf373ba10c5cf24a4a56eb6953fc9d65d72885cab51e3f6175d6", + "accessList": [], + "chainId": "0x1a4", + "from": "0xb0c0f73f84ce7355e755846c123f0a5848304b52", + "gas": "0xf4240", + "gasPrice": "0x9502f932", + "input": "0x1249c58b", + "maxFeePerGas": "0x9502f964", + "maxPriorityFeePerGas": "0x9502f900", + "nonce": "0x2", + "r": "0x949ec41c0879211f8887d5164041d6188edc2c794a5a1d246ecf0d0eb6d35ffb", + "s": "0x57e80d08a5ae1f41c52017d8c03e9f1b434ba8040e2f88ae63a2110081324d5", + "to": "0x222e406c60dcda91e688afadbad5742c858e57a0", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "blockNumber": "0xfed90b", + "hash": "0xa691797770acbb5f94c529118e95527a9d5c71ab0b06165051889b195294d1c0", + "accessList": [], + "chainId": "0x1a4", + "from": "0x784e83fc9aa0ebbbeb56985d557d5b433ada1713", + "gas": "0x5b8d80", + "gasPrice": "0x5f5e132", + "input": "0xb1dc65a4000168fe4aeb6129d7251e28b38e5f27d5b3cea6548afead1311cfdb9447870b00000000000000000000000000000000000000000000000000000000007a15031e10c4d40e18ac947a55da7035300b2585acf048a43764c44082c5f0d1c282db00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030001000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000005ca20000000000000000000000000000000000000000000000000000000000005ca20691e3b307ece8ad343c0c2c5a1dcc2aa1ec2b9a119ebb5abc75dbcdd0a8868700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006434c12200af19a933c2b39525e1e33639b98bc01adebeb02c9be073c28c53b1d881bd1c5940e5a978bff3b61df355779c55677a89d266394805684d412554361c8cfdead0404ad831be7e93a1c0baba1ad5ea60ba15b9a8b35d49d4d12caf895fd48549df3ace9dc4febd7782fdd84c99e9f0e2940699ee296fba17bd1fe52a36c07502dde9839d2cd661f7c8ea9a3d47419910e26541cc73bb21ddd81a65f3030de928d1ebb7d1005b224c41961bf89a095b823baf1e9c330924a221268597d00000000000000000000000000000000000000000000000000000000000000066a5206c67c00adb657b333410aadb1e7915f0bb28f5add721cf33090d71897e33544e99677de19b5c18c1bd71ba7672bb2f02f7512fc62fbf5237e23382b5c3155913c53063b161b5c0ef4499ed8463bdb6f273f30d5f056f22c94cf82c467fd04a41f901d5dc4f1ba7365d9ffbf021b4ca0f9259e30a71945e63bf2e58811df4c0c60881eb5d1a2e4aecd17acf6315d0464ba85020997d0aa92f0a6e2429e5b38203c79db88875af3d32d2e9bcc0a33f74d088a23e3ab1a2db328c3977c94b5", + "maxFeePerGas": "0x5f5e132", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x2f8a", + "r": "0x23a2b3ab7c3b230ce629c9e8858081aa7d0436ed2a03ae9c75a2b8a65433f85e", + "s": "0x15c6e06e724b141e4438459718615d10f81fc1271f10b8e836ede0ecf15ec9aa", + "to": "0x4f57b2d4b3b42f09cd7ef48254d2c31b6b525763", + "transactionIndex": "0x2", + "type": "0x2", + "v": "0x1", + "value": "0x0" + }, + { + "blockHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "blockNumber": "0xfed90b", + "hash": "0xbbd4f254ca0cbada309e275cd1911233e4f54154b3c939355fef8caf60f8362a", + "accessList": [], + "chainId": "0x1a4", + "from": "0x3b497482adc590834cae8a0c7eaa7a67994e522c", + "gas": "0x5b8d80", + "gasPrice": "0x5f5e132", + "input": "0xb1dc65a40001e50f36f6449d45e7765580d00582234b527630572a1380ac70383a66a46400000000000000000000000000000000000000000000000000000000006c14038ef7f88cf6cfe3d6d52cffa8584f12f9333bbb48801769a9bfc55ca502cd49d700000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000156600000000000000000000000000000000000000000000000000000000000015662f4bf7005c5a0435535ca3e2d5d1e2be046607f68d00dc47d6fea7b703d30832000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064bd1776c5cf283c2ca56eb2d63483c291dad641a4602fe785febe1b00c771f69abe6da82554797432b84e63a2b203ce66a45c9ac87776d9fdddf45a4847c591dd18a7fcec8add0ce57a994baf6aeb65e9fccdbd91177d6b75fc14805f2cd6b00f77a2c857bfd67785c3f89567d9501b142bc5b35970c4263ab4b2ee46b87c0c3f25819101a0e05bd0e8e3afbee19a418410396560fa939f983237e364214955345892d6c9187a6841789785c2d86011e496d31fef7191cbe5eae0818baff8713000000000000000000000000000000000000000000000000000000000000000668668939461cf52dfa6bc6331dcc9bd42797201bf9f9e89ce2d4398b80fd787b5805bcea94dc09d1dc99949a2bb6cde0b1a6758d3ac94626ceed4168777cc55e4e5ec877a52d89e9bee2f1d1822cfdb1f1d5cd46b54e4934e17edeb9f01c87376b3c472bffed2919853e34aada54fa31fb018f6a6e7a4c64c6019a2db14dff16368af9a43ce94c432c5a1bf587524234513b6f09e6e026ef31e16e48bf7ad7bf6bf1e47d52cd46207ddac30708ccecc33ccb1ffdad089216ea163b1d09492c39", + "maxFeePerGas": "0x5f5e132", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0xa280", + "r": "0x9f14c375a2efcff35fcdd9543113bf358b0bffca2ea6ab7794856bbcbdfd8e9a", + "s": "0x7f20b7ac61daf217337744999f5ce447056c7e70a9e6378a13fe2fa2b4a8c646", + "to": "0x2980de4ce178bc8bb6840abd2ef0e2a7c8e7272f", + "transactionIndex": "0x3", + "type": "0x2", + "v": "0x1", + "value": "0x0" + }, + { + "blockHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "blockNumber": "0xfed90b", + "hash": "0xf19719ce101491bba7deb7f5bdcc772355e2b156c9e9da6e56fc65d4134e6689", + "accessList": [], + "chainId": "0x1a4", + "from": "0x365252db0066fb7101994a8d04e4e25ca9ff2a5f", + "gas": "0xe4e1c0", + "gasPrice": "0x64", + "input": "0xd985f1e8000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000b47f1e0035f15998fdd0999646eac1bc5aa8bd90000000000000000000000002cfdb5d233d8029ccb07aad2ca7a558bfccf48120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002447189fd3fca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001cb504e41550100000000a00100000000010004172835280596ef1f0ad40b8b09992e82ac5d96b8cc6770dfff1f79db6a2f6433fcad1cc0e45f76572e6b38aff1e1d6e66446f9e0a6d1040590fb2012ead5cf0165421c7300000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71000000000203006d01415557560000000000066a86ad00002710d39a6088fed6117d6d7927cd8767a31af05e05c801005500ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a60000002a0fb422fc0000000002cb5af6fffffff80000000065421c730000000065421c710000002a09a48b880000000002bb683b0a0e509230bc887125c2e11101e392a111ba60982690fe64c2b70d1a28d86d96ff9748d0bd2cbac47f756f18562cc6506da404aca909d70ff20d09647f2214569c08524a88d2e7e376e761e736c4fd9a13e92bd9b5b48561a93384067f9eb1aad3f53617572311d145c5dfca5bc537eb4942142676c081adeb501a0a36c59c90ce6eed693b822afd66a129f6ead59c1db9fb16ed5e23deff812436de0f5e59a2024a9fc7b84db1145603710d0fda72a67944534c44716512e1c91af4a19e1c7e0346d677d291162b6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000648c34d6be00000000000000000000000000000000000000000000000000000000000000dd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e100", + "maxPriorityFeePerGas": "0x32", + "nonce": "0x3f0c", + "r": "0xb3bb26e245851a23a4d985aa9a03b615ddf62d47568e59e151b1ebb7d6ae7cd3", + "s": "0x3dbb22da256d7b6e8789fda4bff1d6791b5867699c1bfadc3da38e8edf1efe7c", + "to": "0x00000000002fd5aeb385d324b580fca7c83823a0", + "transactionIndex": "0x4", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x140c73", + "logsBloom": "0x000000000000000006000020001002000001000010000000000000000000000000000000000200000003000000200000000200000000008000000000000600000000010008000000000000080000000080000000000420005006010000020020c18000000300000000400000400009000004000000010000000000100100000001000100000003020100000080000010000000000000000000000000000000000100000000000000200000000000001000000000000000011000000000000000000005021000000000008084c0022000000000000000010400001100008064000081000004000080000000000000100000010000000000000000008280800000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x7057d89f6f6d1da401815eb2c736deb252dd1f9de404bc02f2373367af559e61", + "receiptsRoot": "0x9a9dd846e8b11fcd0d43e5e0c353f91c14294cdeafd7ed48d5a21e575f6ce230", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x11ac", + "stateRoot": "0xec94c0c78d07a9a81b5d2e8fb8ce653812b0c3f7d5252a1bc2ec3925dc592093", + "timestamp": "0x65421c7a", + "totalDifficulty": "0x0", + "transactionsRoot": "0xfc42c02840271baaf3342ebf55491dd1338a53d781207d887fdbe566349d3651", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90c", + "hash": "0xc3b4c03658a2d44fcb66a254395fefe31326a2cf90c56183425f90b51724908a", + "transactions": [ + { + "blockHash": "0xc3b4c03658a2d44fcb66a254395fefe31326a2cf90c56183425f90b51724908a", + "blockNumber": "0xfed90c", + "hash": "0x8c3cacd6aff46ba107216a6b03e92b84a197f074af2813373d63a63f20a39793", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000030000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e3", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xc425acac055a6da6b168e1ef788495ab26b7c6dac1d5950e41c0347135c59eab", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xc3b4c03658a2d44fcb66a254395fefe31326a2cf90c56183425f90b51724908a", + "blockNumber": "0xfed90c", + "hash": "0x1a653d109a277ec42814fb520e84349c2ab089df02ce64c6e6fca511e911c59f", + "accessList": [], + "chainId": "0x1a4", + "from": "0xbbe9daf8010fecbea38a39f64dac16ea6b1cdcbe", + "gas": "0x2e89b", + "gasPrice": "0x59682f32", + "input": "0x110410f700000000000000000000000000000000000000000000000000000000013bd8cf", + "maxFeePerGas": "0x59682f3c", + "maxPriorityFeePerGas": "0x59682f00", + "nonce": "0x2a", + "r": "0xf6a25b71ecd861d59228fb5b088c0618406d05d145a4eac27c42382b9bb04248", + "s": "0x30d5d5725351ac39fd1c77efa2cd7cc19e868a1ffd151fb49ee3e05c674cced3", + "to": "0xd6047245e0fc9baa6231cd8d013b1118b5042aa9", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0xb5e620f48000" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x39fac", + "logsBloom": "0x00000000000000000000000000008000000000000000000000000000000000000000000001000000000000000000000000000080000000000000000000000000000000001000000001000008002000000000000000000000000000000000000000000000020040001000040000000800000000000000000000000010000000000000000000000000000000000000000010000000000000000000000000000000000000000010002000001000000000000000000000000000000000000800000000000002000000000000000000000000000000000000000000000000000020000000000000000000000000000020000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0xa45929fae4af65d69b4a942b67e33a8cfd079603a1e5b8a7fcea1bc116f90879", + "receiptsRoot": "0x6a8fb2ab22ec1231706342803f5a0f2661295dd98ee9d503de02d2ee9279db37", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x402", + "stateRoot": "0xf9cb7230cd8f6c78878e60a13edd42a2b247547a6528d793566c1e9f63948040", + "timestamp": "0x65421c7c", + "totalDifficulty": "0x0", + "transactionsRoot": "0x47119266cd1a22f0dbe3b536da55c5752eb687703fa08d781f10f731e219ea19", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90d", + "hash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "transactions": [ + { + "blockHash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "blockNumber": "0xfed90d", + "hash": "0xcaf761c36e4b92c8fd928457f6bdb271c86c6ac8c249609682b13bf8a0e9bf12", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000040000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e4", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xd70b9335dd6c98a3a268466673ad34b3f934c1f984b0c5e1e4c918ef6bfd0c30", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "blockNumber": "0xfed90d", + "hash": "0xee06bfaa5a219ab12a94b3bf1ea09753264b5dbe8198881408d1e40b30f9b1f3", + "accessList": [], + "chainId": "0x1a4", + "from": "0x3540e1f097a7d6bfce706224dcc965a26c5baa7c", + "gas": "0x11170", + "gasPrice": "0x5d21dba32", + "input": "0x97998611000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004f57b2d4b3b42f09cd7ef48254d2c31b6b5257630691e3b307ece8ad343c0c2c5a1dcc2aa1ec2b9a119ebb5abc75dbcdd0a88687", + "maxFeePerGas": "0xba43b7400", + "maxPriorityFeePerGas": "0x5d21dba00", + "nonce": "0x102b", + "r": "0xe45cafb3673736026cac65017fcfc7ce56cd95bcd6f78bbe1bbd6260ef00683b", + "s": "0x5a7e696f25429bbeb035a60f8dcb7d3af2420f0fa339c38b2b01d4e916116fb5", + "to": "0xeaf6968fab9c54ac31c3679f120705b5019d3546", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "blockNumber": "0xfed90d", + "hash": "0x8e74f3b7bb111162929b7318ccb143ca669fa2273ebdd8a09e9fd578edda5a85", + "accessList": [], + "chainId": "0x1a4", + "from": "0x4d9987720ec678aa1271621ffe617771288e436f", + "gas": "0x11170", + "gasPrice": "0x5d21dba32", + "input": "0x97998611000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004f57b2d4b3b42f09cd7ef48254d2c31b6b5257630691e3b307ece8ad343c0c2c5a1dcc2aa1ec2b9a119ebb5abc75dbcdd0a88687", + "maxFeePerGas": "0xba43b7400", + "maxPriorityFeePerGas": "0x5d21dba00", + "nonce": "0x19b1", + "r": "0x616bce30ef5741f3dd525b97547f14c035aa0637b065fc36c94f1a475076f3c7", + "s": "0xaafe09ae83e4ccd42a3c44e203369fa46f2921b230ff9c7ed525a4abfd515eb", + "to": "0xeaf6968fab9c54ac31c3679f120705b5019d3546", + "transactionIndex": "0x2", + "type": "0x2", + "v": "0x1", + "value": "0x0" + }, + { + "blockHash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "blockNumber": "0xfed90d", + "hash": "0xf4a4b5d7cc789fd61b37d20ea75e26d615ebb155a5ad25866f4ab3ccb222f4f6", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x405ac", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e0b35cabe1740f4f381598bd2680c0e10cab6fe26f32c53c8f2ee0f02c023eeb890911e63c580e836787004eb8dd2f9ed91a1f1e5a0e967d86e895a2b3ddf339cc00000000000000000000000000000000000000000000000000000000000ff2ce000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041dfcb74b61aca7b7ef90e9e5221ffa2d54f12551b1750cb5fc87e3e3b9dab7ffb7daa0f2dce7e9058e183c558af051bada0e802ff5c311199cb27159686c90fdc1b00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef88", + "r": "0xe88d3e7ad186ebbf03bbedba92ab96fba2ebbf25b8e22141673ada6ea42c3e87", + "s": "0xb60ef654e913b8b5b02fadcfad516a61c58159368b24b234135860366c5c546", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x3", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x56cbb", + "logsBloom": "0x00000000000000000000100000000000000000000000000000000000000000020000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000000000000010000000000000000080001000000000000000000000000000000000000000000000000000001004000000000000004000000000000000000120000000000008000000004000000000010000000000000000000000004004000000000800000000000000000400000000101000000004000000000000000000000004000000000000002000000000000000000020001000000000000000000000000000000000000000100000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0xc3b4c03658a2d44fcb66a254395fefe31326a2cf90c56183425f90b51724908a", + "receiptsRoot": "0xc6deb0e17d071eab845c4c3556721aa96a920ae7b9670166f9fc2574b1a95e9f", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x899", + "stateRoot": "0xebaf3a68b7cbc81eeb7a48de6f03c4f30b798bf37efdf43e52fd6d78b45463e9", + "timestamp": "0x65421c7e", + "totalDifficulty": "0x0", + "transactionsRoot": "0xd07f6cad26c1cba71c40f0ffb8592549e550c4e4bf7a4823eab2c2128fd84d31", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90e", + "hash": "0x3d1212b2a39a8b9066dd168a0a90f2e596a7652fb3df2ed2559df741f5caaa8b", + "transactions": [ + { + "blockHash": "0x3d1212b2a39a8b9066dd168a0a90f2e596a7652fb3df2ed2559df741f5caaa8b", + "blockNumber": "0xfed90e", + "hash": "0x18ffab593442e214ddf7eb10acf41ff84f34f483e8738b88b6c6c5ac3b9a7eef", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000050000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e5", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x84fadd5918311710d87353233066b621bcb369dbc0a82b82021469de3a1a2252", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xb711", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x14fbcf40d43e38c135d03c6b6df429c1f7ab190f334ec7e52eb1b4634a1de757", + "receiptsRoot": "0x29079b696c12a19999f3bb303fddb6fc12fb701f427678cca24954b91080ada3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x837f2da981198bd4ac3a300180917214238b28b3a48a6a2ef9c59d6b6f216509", + "timestamp": "0x65421c80", + "totalDifficulty": "0x0", + "transactionsRoot": "0xfe24abf581a58419f6a3f9af17ebe12cb38f06b30668751b8e1ed52957b73523", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed90f", + "hash": "0x9ec60db2a10a95df14532962ddd614ede91d1e7e0050dd35cd1b80c32c35bd74", + "transactions": [ + { + "blockHash": "0x9ec60db2a10a95df14532962ddd614ede91d1e7e0050dd35cd1b80c32c35bd74", + "blockNumber": "0xfed90f", + "hash": "0xa70de9ed96bdb201a7bb26072e078a5516b96741f9ea7b1314cc3585876d05c4", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000060000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e6", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xfa3a0d15d3bc4a30ddfedcb6f7df081e88e9eb700a289a16d9d89c9f83ba263c", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x9ec60db2a10a95df14532962ddd614ede91d1e7e0050dd35cd1b80c32c35bd74", + "blockNumber": "0xfed90f", + "hash": "0x42a20020d307d1fd18e9b4834daaba50b421ad0a675293557dccc3eb8e6da0d9", + "accessList": [], + "chainId": "0x1a4", + "from": "0x0b2dfb982114d57033c66a25a5d46957e93cb22f", + "gas": "0x7a120", + "gasPrice": "0x7270e3c", + "input": "0xc9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000020001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000038cfbf2a1a6b566666843f13294431710009423306030002010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000053c219328000000000000000000000000000000000000000000000000000000053c5cca66000000000000000000000000000000000000000000000000000000053c5cca66000000000000000000000000000000000000000000000000000000053c64f5f000000000000000000000000000000000000000000000000000000000000000024a61aa0cb5f60bd5ff7ae8f5d6f8c81cb741022255e8b70d0bf8c86209f01e118a2454b0f732f2737caf08790f6630475094e0893e965171b2ae5614a113ae6800000000000000000000000000000000000000000000000000000000000000024d3a08476f0041778f0589a3f1be7a22d82264663e659c8e1167a046bffe7db4292770a1e807b7fe993adb6b560b2aad3309f46d4db76e40c2dcd9c3caeecf38", + "maxFeePerGas": "0x7270e5a", + "maxPriorityFeePerGas": "0x7270e0a", + "nonce": "0x78ad9", + "r": "0x2fbf875de340747f7a71e9f07d29cd1029a85956413085a040cc5f0318c0e6dd", + "s": "0x7b179d1601d543c053cf3fec0f576213e0293ba2050b9681729bd61e1bbc8274", + "to": "0x40a8b760a86aa0ea51bb6c2831082ab70145b9fc", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x2afdb", + "logsBloom": "0x00000000000000000000000800000000000000000000000000000000000000000000100000000000000000000000000000000000000200000000000000000000000000000000000000001021002000000000000001000000000000000000001000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000040000480000000010000000000400000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000020000000000000000000000000000000080000010000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x3d1212b2a39a8b9066dd168a0a90f2e596a7652fb3df2ed2559df741f5caaa8b", + "receiptsRoot": "0xda248b89fd5a8d0f7a17ac45acd9d9c5ea52a1e617074a8831e08f3e9eb70d70", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x643", + "stateRoot": "0x917571f029fbe4bbe03199e1a05a609d2e3eef7bdc9c6248f9a6757b8d659b34", + "timestamp": "0x65421c82", + "totalDifficulty": "0x0", + "transactionsRoot": "0x1418b59412f84bad5365026990791aac796eb4f51fb7b258cda61a4223a9298b", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed910", + "hash": "0xd9616b463fc5c504c11591a240a33fa7ab0333fa583d8e61a3cfe74a7275c81e", + "transactions": [ + { + "blockHash": "0xd9616b463fc5c504c11591a240a33fa7ab0333fa583d8e61a3cfe74a7275c81e", + "blockNumber": "0xfed910", + "hash": "0x5144d13c65a18ada65d2b3e5bab4bb3a6075efc1ef9dfdcef9bbc8624c987c78", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000070000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e7", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x28fddacc9458f7f7a38f9c640e176934b691a3894d5ef5b0a5e358f4c57c5abb", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xd9616b463fc5c504c11591a240a33fa7ab0333fa583d8e61a3cfe74a7275c81e", + "blockNumber": "0xfed910", + "hash": "0xd77b6c3c5d31ccd7926aedc1c8a14f6b959f742399fe8bfd2382cd6440e25b30", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x50c5d", + "gasPrice": "0x5f5e132", + "input": "0xb630d4320000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000e00911e63c580e836787004eb8dd2f9ed91a1f1e5a0e967d86e895a2b3ddf339cc25b0456d3aa978072457cd416b58c8824bdd9ee8ed8852dfbb799b962325b69200000000000000000000000000000000000000000000000000000000000ff2cf000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e025b0456d3aa978072457cd416b58c8824bdd9ee8ed8852dfbb799b962325b6925818ed0aa3671b7ea18623aa1e7e4093186e08063ddb2b873c0878d6bacdaa9500000000000000000000000000000000000000000000000000000000000ff2d0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041a0b45fdada067eef2162f37208a5bc1a80c4673a59e701687d4b9a8ce8fb184f7c880ed0a9293be30c01b7351aebb222fa44ac5bead5e3f07370c9c31dcbcb171b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041fd9d15522aa97beccf2e247e65b566f7d1f9c48ee4a66b763209e12d287d33616d7923732d616450358d423798fa04358700cf4b27175683e120a11653da03dd1c00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef89", + "r": "0x3114f4c94a70d9ca807b52fcfb1468b14fa939fd698e68f1600296ed0559ab47", + "s": "0x2e7c8bcb0f07dedad5d7894e95ef95bf3dabd648dcdc8e2c583a4e9d80e678fe", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x5b1aa", + "logsBloom": "0x00000000100000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000081000000000000000000000010000000000000000000000000000000000004000000000010000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000020002000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x9ec60db2a10a95df14532962ddd614ede91d1e7e0050dd35cd1b80c32c35bd74", + "receiptsRoot": "0xa159a1cc9a597b22e46260e498c984948363ba0cbe1d2f99033543f1382dfdff", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x8e3", + "stateRoot": "0xf96c68b3db6ef664a94f1e79cceabfa27a8ed788965cf7e2bb346cd90fc74f73", + "timestamp": "0x65421c84", + "totalDifficulty": "0x0", + "transactionsRoot": "0x3b19486c1a59a65cdd817b060b1d1bc2edc8b3102f36c747bbf466eedbd67ab4", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed911", + "hash": "0x99b274f47e55c04dee02571d87a82e514f66228019d6c1b9986da0330fdc21bf", + "transactions": [ + { + "blockHash": "0x99b274f47e55c04dee02571d87a82e514f66228019d6c1b9986da0330fdc21bf", + "blockNumber": "0xfed911", + "hash": "0xa633bcaae71bec4e74d6654faf7d832fa17342dc3eee9137868df95dd5a40b2d", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000080000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e8", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x641a1442a989615a9b07958bdaaf226d64389d8527b4e0f64df90446b52c37b0", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xb711", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0xd9616b463fc5c504c11591a240a33fa7ab0333fa583d8e61a3cfe74a7275c81e", + "receiptsRoot": "0x29079b696c12a19999f3bb303fddb6fc12fb701f427678cca24954b91080ada3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x8e50fe5b6cbdc91c5acbb658c2919969bc557190079a0ad3d7eaaac0bce2dae1", + "timestamp": "0x65421c86", + "totalDifficulty": "0x0", + "transactionsRoot": "0x73f3b92b28dd4a74dfda16b7231e213236a4a5aae2ee1f8a9cef22c5d0b1edf6", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed912", + "hash": "0x7c6bc4bdd2b2b627f1abce0b056a8b2865be7a78135c402247991c74352cb523", + "transactions": [ + { + "blockHash": "0x7c6bc4bdd2b2b627f1abce0b056a8b2865be7a78135c402247991c74352cb523", + "blockNumber": "0xfed912", + "hash": "0xb09e05155f69426a2a83f6058bc0f8bcdec13a3b17f4941f1995993958bae0d8", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd100000000000000000000000000000000000000000000000000000000000000090000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0e9", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x40359ca320e94c644c2d3c70931e00bb8392c185bcbcf8aa4dc5ada7d8a23b1b", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x7c6bc4bdd2b2b627f1abce0b056a8b2865be7a78135c402247991c74352cb523", + "blockNumber": "0xfed912", + "hash": "0xdc70fb04e1c7122055e6d929d56710a7ea5fea4518b348ebff1b6e4787bf6b2a", + "accessList": [], + "chainId": "0x1a4", + "from": "0xbbe9daf8010fecbea38a39f64dac16ea6b1cdcbe", + "gas": "0x493e0", + "gasPrice": "0x59682f32", + "input": "0x2d3e085100000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000002", + "maxFeePerGas": "0x59682f3c", + "maxPriorityFeePerGas": "0x59682f00", + "nonce": "0x2b", + "r": "0xe8c2d8a6138c3b6e8f0146170bb137a86896583a2daf0ce3b3679d07a700006d", + "s": "0x6e407a68dd8b1f51c2e8354a2f2bcc84b5623410ca9c573ef593f3c197bfac9", + "to": "0xd6047245e0fc9baa6231cd8d013b1118b5042aa9", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + }, + { + "blockHash": "0x7c6bc4bdd2b2b627f1abce0b056a8b2865be7a78135c402247991c74352cb523", + "blockNumber": "0xfed912", + "hash": "0x18b3ea19fb3749bdc8d666f6af0cdfb22f7f0e61bade2505dd0a1b7e6710e75f", + "chainId": "0x1a4", + "from": "0x1f907becd9db9abdcb5a2e9cf8806127f2d8f207", + "gas": "0x21999", + "gasPrice": "0x5f5e132", + "input": "0x04e45aaf000000000000000000000000a2c5a9a5119189d79841e409033acbfb80fd5f680000000000000000000000006abdecfac3e45dd47ec9feffdf8c5d25429bd4ed00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000001f907becd9db9abdcb5a2e9cf8806127f2d8f207000000000000000000000000000000000000000000000000000b1e0f8b94b9ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x2746", + "r": "0x6ed46b0339dbecfd7ff19969cca6440a64bd591f0a6f9a259c3539d812b563e0", + "s": "0x2181d166681d9784690fb24be98522b69882facc4e428f30045e4f07af6a4a3a", + "to": "0x19461aab4c4a5afcbf0f4e3055648cb8a0196927", + "transactionIndex": "0x2", + "type": "0x0", + "v": "0x36b", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x4829a", + "logsBloom": "0x0c000000000000000000000000008800000000100000000000001000200000000000000001000000200000000010000000000000000020000000000000200000000000001000020801000008002000000000001000000002000000000000400000000000020040001044400000000800000000440800000000000010000800000000000000000000000000000001000010000000000000000000000000800000020000000000000000000100000000000800000000008000000000000000000000000002000000000000000000000000000000080000000000000000000020000010000000000000000000000020000020000000008000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x99b274f47e55c04dee02571d87a82e514f66228019d6c1b9986da0330fdc21bf", + "receiptsRoot": "0x6accf4b9e6f39afeebd9a8c4ce3b163b43031e8c793a4dcf8b1168810e017e29", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x56d", + "stateRoot": "0x8d3e078068fa62624e2a5098c67a0ad0a9cbae7f8adcb948b35b245d4a4f2f00", + "timestamp": "0x65421c88", + "totalDifficulty": "0x0", + "transactionsRoot": "0x56d229bcf7f1a6055b4c6187731aabbed3aa5ff722d198a94a53ddb56576e435", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed913", + "hash": "0xb2ac49c88c5705c09eec32f6d63177e2e764af8123820e33f38e54e9e8eacb0f", + "transactions": [ + { + "blockHash": "0xb2ac49c88c5705c09eec32f6d63177e2e764af8123820e33f38e54e9e8eacb0f", + "blockNumber": "0xfed913", + "hash": "0x248752c4646ea9114b67556639a27e483cf0b01e5c6173ac66a34726be6b2ab9", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0ea", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xba15d5caa9ab97e79ef07ba18ba9b363a16c3375a7cf06a1e958edebbb8f6d86", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xb2ac49c88c5705c09eec32f6d63177e2e764af8123820e33f38e54e9e8eacb0f", + "blockNumber": "0xfed913", + "hash": "0xae313e7a0b71e44c35437c60fe032eab51384c9c1cae3c921d22a631990abed5", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x2ecd4", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e05818ed0aa3671b7ea18623aa1e7e4093186e08063ddb2b873c0878d6bacdaa95ff6e3d3466c2e9309600ba08f977fd9f09e64687618949d68e0cd535b73eafc800000000000000000000000000000000000000000000000000000000000ff2d1000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041a3a841b7d7ef9bf0ccdc451d13ed306fc98001ad4d06b3d978a3d7a01fe68c1c4783b8627c56483b311be0ba4eb37239c313348087ca8de5878d6d6849aea7ec1c00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef8a", + "r": "0xda0d4bef1c0b573a1e49d1e3a1ac47c6592c5bd6c6824c3ffac10d21f356c8c0", + "s": "0x6a8eb64f095d749ca3da2502f5d7eb978824142f9dd455258b775630f1e6ef39", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x39a5c", + "logsBloom": "0x00000000000000000000100000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000001000000000000000000008000000000000000000000000000000000000004000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000008000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0x7c6bc4bdd2b2b627f1abce0b056a8b2865be7a78135c402247991c74352cb523", + "receiptsRoot": "0x694678e3ead8507c5a04c1384287673b28d2c4b47caa6dbb1db2932c69d4eba6", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6a3", + "stateRoot": "0x9d96898acaa04128721f0b186824efd293939a7769a68d259513e071dabb5d20", + "timestamp": "0x65421c8a", + "totalDifficulty": "0x0", + "transactionsRoot": "0xa2429aee09f470ea3e73196f3faeec5f528996893af132a9cd18ed0dfd15a167", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed914", + "hash": "0xe2fcad9b10fd668b0a3a05e1ba1437e4730b4b4c123474db58824cbce0473f19", + "transactions": [ + { + "blockHash": "0xe2fcad9b10fd668b0a3a05e1ba1437e4730b4b4c123474db58824cbce0473f19", + "blockNumber": "0xfed914", + "hash": "0x77fcd6bfe922737d5d18b673490cf42fe7e6d8e1d0b25f24accaf7b69d0a3b38", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181d0000000000000000000000000000000000000000000000000000000065421c30000000000000000000000000000000000000000000000000000000000000000aec8a872f5ca572df4c7e1989cc99627e0d086fb95f937b6c38ae303affff5cd1000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0eb", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x6ad54a0f7c8ec4e730a1c76339c13a30f1183f942d190013f652469177af34b3", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xb711", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xe6e5700ce7550f1bd6e1f415db1561a03c1add9f52403933a28034ab4c3779d7", + "nonce": "0x0000000000000000", + "parentHash": "0xb2ac49c88c5705c09eec32f6d63177e2e764af8123820e33f38e54e9e8eacb0f", + "receiptsRoot": "0x29079b696c12a19999f3bb303fddb6fc12fb701f427678cca24954b91080ada3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x9c89eda07d01df4cd74e68c93249e8c535923b9f62db92a2eb65eec134b472d9", + "timestamp": "0x65421c8c", + "totalDifficulty": "0x0", + "transactionsRoot": "0x9087ac2ebd3f514d129a61ec1dc59f9d53d008f567635ea6dcc6b4d17a38521c", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed915", + "hash": "0x9b1591a52a7ad4a27b67db3b58c9e85242eb2903f9009087e95d27916517addf", + "transactions": [ + { + "blockHash": "0x9b1591a52a7ad4a27b67db3b58c9e85242eb2903f9009087e95d27916517addf", + "blockNumber": "0xfed915", + "hash": "0xefdd5e3cb0c42cd6490c46e4df90a42aa39d1accaa201b1ff3ebfdd0041f8878", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000000000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0ec", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x569fdc44663d5ff3e34f4bb1362b745e12baa9947afcf6d9685cb19e4a328932", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xba25", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0xe2fcad9b10fd668b0a3a05e1ba1437e4730b4b4c123474db58824cbce0473f19", + "receiptsRoot": "0x030b2afe4ffa211c38dc7bd40db30e3d64b2f8463e24ff7423a3bb84ba449a47", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0xcb5dff8eab1c744c5ee14a21e3ed884576422d157234fc0e9e5fdae97ef1e20d", + "timestamp": "0x65421c8e", + "totalDifficulty": "0x0", + "transactionsRoot": "0xf69f6e20b6133fca5c01bccd9cc50408323316bb4518ccc3caaf1ee8b8c6d399", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed916", + "hash": "0x1fe58ee6c851316735107797621fcc3f942c70d82d1941dea4aa8d5ff2df8c06", + "transactions": [ + { + "blockHash": "0x1fe58ee6c851316735107797621fcc3f942c70d82d1941dea4aa8d5ff2df8c06", + "blockNumber": "0xfed916", + "hash": "0xae840243a0d83b5c65a8a86ead3add0bff61f6be83a5f6f2e030b8415438e611", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000010000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0ed", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x796fb268a12038af3422b4f479d14ba9de39e9615fc312a70f2c6f72ed40bef2", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x1fe58ee6c851316735107797621fcc3f942c70d82d1941dea4aa8d5ff2df8c06", + "blockNumber": "0xfed916", + "hash": "0xa7d6059a2f4b1315ea78b3130e6d017e161dca675bbf604cacc8ac566297bb27", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x2ecd4", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e0ff6e3d3466c2e9309600ba08f977fd9f09e64687618949d68e0cd535b73eafc81ae24f324b0592e76de47f8c55800c25ee4415ee6a518adf0ad07c243582b94e00000000000000000000000000000000000000000000000000000000000ff2d2000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000417dbc074ecf060a633a12249bfa92b97350f1e8e6dcbe2a3654edc7ed08da3ca436d29cb831469eb85874679611e41af5c62ab61a300a400ca82fa07def0d2df81c00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef8b", + "r": "0xe47ed7ece99e49cd04581cbacf95639eea6bf5742ccc253730e6b7c5d103a069", + "s": "0x2665dad7ccb36e22b33ef69560ba7fc03b6444607f524163721298be4fad2cd1", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x3dd28", + "logsBloom": "0x00000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000401000000000000000000000000000000000000000000000000000000000004000000000010000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000800000002000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0x9b1591a52a7ad4a27b67db3b58c9e85242eb2903f9009087e95d27916517addf", + "receiptsRoot": "0xe06c4e45b93e20b7d9a49c424b0847b86aa72a21bb63c51e6d8ff7adef255edb", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6a3", + "stateRoot": "0xffae2b813a5569a870ad07110e0e441ba496fbcea05ecb391f912c9b2dcf152e", + "timestamp": "0x65421c90", + "totalDifficulty": "0x0", + "transactionsRoot": "0xc425731c403af080ea2c24a7ed00788e4a5100535f1bcf2e00180e5b583b9377", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed917", + "hash": "0xd72d6a9c21ffc5571367a2985d0196869546fd2a9cb654ccc5c306de94d7227e", + "transactions": [ + { + "blockHash": "0xd72d6a9c21ffc5571367a2985d0196869546fd2a9cb654ccc5c306de94d7227e", + "blockNumber": "0xfed917", + "hash": "0xeef7d113480059eb79a2a300e6000214be56c7fdbd03446221137c55a96d86ea", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000020000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0ee", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xa3dfd244a67e69fee01d3ced68368314efcb2323fc5c4632b50d40eda11ca0ef", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xb711", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0x1fe58ee6c851316735107797621fcc3f942c70d82d1941dea4aa8d5ff2df8c06", + "receiptsRoot": "0x29079b696c12a19999f3bb303fddb6fc12fb701f427678cca24954b91080ada3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x159d678d0baf9cce807b46f9c1c9f1a0740d08dad6441852f04cabcbfb3415b0", + "timestamp": "0x65421c92", + "totalDifficulty": "0x0", + "transactionsRoot": "0xc1ec6ee4c4d144bea0937dbf26b2b97a3e94e9b885a74fd1f48cce3d987a5d3e", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed918", + "hash": "0x9c488b92e950d555f0647fb12763b9fabe8216018ee23417ac011f80ced40e1b", + "transactions": [ + { + "blockHash": "0x9c488b92e950d555f0647fb12763b9fabe8216018ee23417ac011f80ced40e1b", + "blockNumber": "0xfed918", + "hash": "0x7ea703c53e548e01eb103a4aaa2e249d74dcbc20d7645d0f3a1337a7a1ab6d12", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000030000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0ef", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xe55bccba45b06e900b30fd409cdacf4e5d0dded19020160afbb083db3afa55e6", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x9c488b92e950d555f0647fb12763b9fabe8216018ee23417ac011f80ced40e1b", + "blockNumber": "0xfed918", + "hash": "0xca5b9eb4c9a284a8a84396f4dc0b3326955f5c73ed62a9daebbb4b38f0c2716d", + "accessList": [], + "chainId": "0x1a4", + "from": "0xb0c0f73f84ce7355e755846c123f0a5848304b52", + "gas": "0xf4240", + "gasPrice": "0x9502f932", + "input": "0x1249c58b", + "maxFeePerGas": "0x9502f964", + "maxPriorityFeePerGas": "0x9502f900", + "nonce": "0x3", + "r": "0xd4ba36ff1a40f547cde39e29dab6fb6e0390eab475500e489da0ac611346c762", + "s": "0x680a832bc607039b0d413d47ed88acdb0adce892bb84ffe9f802fcaeb0516c1d", + "to": "0x222e406c60dcda91e688afadbad5742c858e57a0", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x488e4", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000040000000030000000000000000000800000000000000000000000010000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000002000000000000000000000000000000000000000000000000000020000000000000000080000000000020000400010000000000000000000000800000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0xd72d6a9c21ffc5571367a2985d0196869546fd2a9cb654ccc5c306de94d7227e", + "receiptsRoot": "0x537ebf10286b3c4b59b10c4afc279793d83f0cd2a343a3d06f25cfe32e0f0968", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x3dc", + "stateRoot": "0x4071aba551a787714d201fb571f41bb50f6c9c781732b2941c0e217b7a3faa73", + "timestamp": "0x65421c94", + "totalDifficulty": "0x0", + "transactionsRoot": "0xa843d6badf95772204013b016d39ce174ff91ae3607d089d3e165e195572d4ac", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed919", + "hash": "0xd1716b8667d686feaa8ffcb0c923dc56f6447148d38981e63d3b40ac7a470ea4", + "transactions": [ + { + "blockHash": "0xd1716b8667d686feaa8ffcb0c923dc56f6447148d38981e63d3b40ac7a470ea4", + "blockNumber": "0xfed919", + "hash": "0xe90fe348493e03702246824a1b6a03bf75fa208c1a6270dba25c5d2d22b06d78", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000040000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0f0", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x9e8b5939cdaf21d9d5ad318d9efeaa92f4c986807a9a8dc084d40beba403040b", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xd1716b8667d686feaa8ffcb0c923dc56f6447148d38981e63d3b40ac7a470ea4", + "blockNumber": "0xfed919", + "hash": "0xe6b258034c09860a5db34235fadb0001274a7185e2622e96ac24561599917531", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x2ece0", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e01ae24f324b0592e76de47f8c55800c25ee4415ee6a518adf0ad07c243582b94ea350f21aaefb7c3df254e3576b1bdfdd3def52dd4c9531d90bd5185cf8ecd70f00000000000000000000000000000000000000000000000000000000000ff2d3000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041ebd74b75f22eb0fe52f60dc86ff614a6b432a41769b5b73b5996070aee15fb921b4e6d9d0295fbea680dce7b984ce7c558559e2b4b7a460a9b7734b539fa5df61b00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef8c", + "r": "0x685b5ea55e71d595eb71db6df7a6a8c9b923da8990e101fefaf96ea8bc845afc", + "s": "0x7bef0a51440770c2d50d465c7e5a636c69075525c424fb128c2c76929523df4f", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x39a68", + "logsBloom": "0x00000000000000000000100004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000004000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000002000000000000000800000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0x9c488b92e950d555f0647fb12763b9fabe8216018ee23417ac011f80ced40e1b", + "receiptsRoot": "0x41591a6b43215d57424783f0e7e8243015b2f0daf24871d362d5d2f409430773", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x6a3", + "stateRoot": "0x387d78e93b05129115d723b444e16dc0f248d1d537b4714a3a3a9cabd7f43d2f", + "timestamp": "0x65421c96", + "totalDifficulty": "0x0", + "transactionsRoot": "0x4d11910e0a7c63816ae047c748b80d2c5f30d0cf9cf47265a797b1e1e1d53ebe", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed91a", + "hash": "0x996594979c173d1bd6828fd1e90d3e00579e71bc71de6ad236977dae87953c2f", + "transactions": [ + { + "blockHash": "0x996594979c173d1bd6828fd1e90d3e00579e71bc71de6ad236977dae87953c2f", + "blockNumber": "0xfed91a", + "hash": "0xc9cd1bedc970df025de79bfa8085a37dd269c0f88a26a00df1ef836e36a87995", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181e0000000000000000000000000000000000000000000000000000000065421c3c000000000000000000000000000000000000000000000000000000000000000a0442372046dcf86bdb03203ea1a9c8f62053f3cb9e7c488c0228a49a393387d900000000000000000000000000000000000000000000000000000000000000050000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0f1", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x7233d5ed50fea38f56f840e94adb6e429b020b8b6da71cbbfc25bec3d0356d1a", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x996594979c173d1bd6828fd1e90d3e00579e71bc71de6ad236977dae87953c2f", + "blockNumber": "0xfed91a", + "hash": "0x6d2166d3ada90c91433740c3d373c1d9657271a31e132768855b618a17a7ed46", + "accessList": [], + "chainId": "0x1a4", + "from": "0x1307ab404e65818318f9590ffd63ea8ff1720c8a", + "gas": "0x14fba", + "gasPrice": "0x59682f32", + "input": "0xde2996cb0000000000000000000000000000000000000000000000000000000000003175000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000f4126322c4226322c4426322c4326320000000000000000000000000000000000", + "maxFeePerGas": "0x59682f3c", + "maxPriorityFeePerGas": "0x59682f00", + "nonce": "0x48", + "r": "0xaa597cc03c45f83a68a3f0e439be15be319a99085daa7a062c1b91b0d2664852", + "s": "0x3588f498d983d1ddb716ff098904365401481c702674057d06ab836cd2395078", + "to": "0xfaea5cd610bd946cfdaf83d023280eca35d4fed9", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0x996594979c173d1bd6828fd1e90d3e00579e71bc71de6ad236977dae87953c2f", + "blockNumber": "0xfed91a", + "hash": "0xf0855b0dbcd30793683551f2fd679c1bb800f64b317b9b9eac96f7f0a3ac3475", + "chainId": "0x1a4", + "from": "0x1f907becd9db9abdcb5a2e9cf8806127f2d8f207", + "gas": "0x2196d", + "gasPrice": "0x5f5e132", + "input": "0x04e45aaf000000000000000000000000d82378800bb914f25bd7e438e205ac066ce22b000000000000000000000000006abdecfac3e45dd47ec9feffdf8c5d25429bd4ed00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000001f907becd9db9abdcb5a2e9cf8806127f2d8f20700000000000000000000000000000000000000000000000000017df8bb1016ae00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x2747", + "r": "0x57ab0acfd21be659b595f0cb75dfa8b9c9dd972431e95938d3b3b4c59a111555", + "s": "0x363fa14f861bab64b28eab81ec71eb5d5dd31e38251a5fb9c0c19d6b929e6b2a", + "to": "0x19461aab4c4a5afcbf0f4e3055648cb8a0196927", + "transactionIndex": "0x2", + "type": "0x0", + "v": "0x36c", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x3b253", + "logsBloom": "0x08000010000000000000800000000000000000100000000800000000000008000000000000000000000000000000000000000000000020000080000000200000000000400200000800000408000000000000000000002002000000000000000000000000000000000044400000000080010000400000000000000010000800000000000000000000000000000001000000000000000000000000000000004000020000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000010000000080000000000000000000000002010002000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0xbc2f59d9f90644e2b29cd5b49fa4f15b99d4b4254ca1250a67e9922c69e3071b", + "nonce": "0x0000000000000000", + "parentHash": "0xd1716b8667d686feaa8ffcb0c923dc56f6447148d38981e63d3b40ac7a470ea4", + "receiptsRoot": "0xa5cf1ccd212328bb2b60a0bc6a1de6f4263535c5665e3c3dd620b91a658e77e3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x5ef", + "stateRoot": "0x09668a27a6d3054f2f29343bae20dd0fd5c4b957773ca2273d694cf09c93067a", + "timestamp": "0x65421c98", + "totalDifficulty": "0x0", + "transactionsRoot": "0x660784e7e1b133c8132173c5065b88d62a2442eb737f0b9827b15bb1dcb62ed6", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed91b", + "hash": "0xc667c769f4ab49d08b7ed565e5df1367028d1553e3584f0c7daf510dc3e4d67e", + "transactions": [ + { + "blockHash": "0xc667c769f4ab49d08b7ed565e5df1367028d1553e3584f0c7daf510dc3e4d67e", + "blockNumber": "0xfed91b", + "hash": "0x9d138a866cb3a47e176fdeb5382b26fb22a1e52c60d8e6c6124cc4779821da09", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181f0000000000000000000000000000000000000000000000000000000065421c48000000000000000000000000000000000000000000000000000000000000000a0a2cf2dc6b0e57d45fbcb949511d0fa42cd13b361d55b8cf46ffc2b05e1b186400000000000000000000000000000000000000000000000000000000000000000000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0f2", + "r": "0x0", + "s": "0x0", + "sourceHash": "0x0faea38d53b0a34261344968a4ada77d2edf44169a63c821bf0e3a5a180f5be9", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + }, + { + "blockHash": "0xc667c769f4ab49d08b7ed565e5df1367028d1553e3584f0c7daf510dc3e4d67e", + "blockNumber": "0xfed91b", + "hash": "0x4ebb88f593e4fb546ded00cf2d59b95cac2571b7ff75ba66520d39c593a83ee7", + "accessList": [], + "chainId": "0x1a4", + "from": "0xb3311bf816cc744a86645e52a0b9d41521d0ab5c", + "gas": "0x7a120", + "gasPrice": "0x7270e3c", + "input": "0xc9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000020001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000025e5cfbea6f68c0eb25a0cf643facd9e00092441030300010200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000007f216900000000000000000000000000000000000000000000000000000000007f4f7480000000000000000000000000000000000000000000000000000000007f4f7480000000000000000000000000000000000000000000000000000000007f4f74800000000000000000000000000000000000000000000000000000000000000027be52e3ae6f5d06f3c366664e1d3ecdf52c6c5d556d1ac8b6fd8debc84101f5a6175f6fbcdd7ac0319b1d6894e1a20a40e59ed30a8f94d8c67e301cff4ff3906000000000000000000000000000000000000000000000000000000000000000225460ca4e9545f97e9a8d7298a77dcef503c346973ba652f81b0946ac9a249f36ae40a70f603075cc043f7143a679edfea3e39a6e8c1333a3f4731ec3221b255", + "maxFeePerGas": "0x7270e5a", + "maxPriorityFeePerGas": "0x7270e0a", + "nonce": "0x758a7", + "r": "0x72bc3984839f2a07d35932f6c1c404b008e61ededd608adf9cb1f9743040bd1e", + "s": "0x6beca1f146cc4bb73ad4b3aaa5d0cfe3c54c10f81ac95eb4e5a64abbe9f89989", + "to": "0xd4cc45b63283ec8e80cdce3632cb8ffa36d52894", + "transactionIndex": "0x1", + "type": "0x2", + "v": "0x1", + "value": "0x0" + }, + { + "blockHash": "0xc667c769f4ab49d08b7ed565e5df1367028d1553e3584f0c7daf510dc3e4d67e", + "blockNumber": "0xfed91b", + "hash": "0x4e5a5289f3313a4f243c4f317247ea50fa41545da86b9fd06808b001dd210e0a", + "accessList": [], + "chainId": "0x1a4", + "from": "0x9cc971e84fe5d09d0967f15ae05dfd553c5a1fa6", + "gas": "0x2ece0", + "gasPrice": "0x5f5e132", + "input": "0xb630d432000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e0a350f21aaefb7c3df254e3576b1bdfdd3def52dd4c9531d90bd5185cf8ecd70f4c79171ea93af9cef995c9330c5cadff2c0bafc145f95c4be9b427ccc4f8a27e00000000000000000000000000000000000000000000000000000000000ff2d4000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f7074696d69736d2d676f65726c69000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004148d182eb4232d8bd0f5560fd62e47502ab22d29de4d26d30226689cbcf8714f419594d06c45f412b8cf6e0116be70db22226635bc20ecdf98bd2da49f64501f11c00000000000000000000000000000000000000000000000000000000000000", + "maxFeePerGas": "0x5f5e164", + "maxPriorityFeePerGas": "0x5f5e100", + "nonce": "0x9ef8d", + "r": "0x9f63feaa1f048fbe5cbe432c0e4a52e4d0ab760e58ae8a13477018b3d949bdb6", + "s": "0x288093ef80611952b5b72cee620b418e5f1d73e862aed63100da7e837b066d45", + "to": "0xcbbc5da52ea2728279560dca8f4ec08d5f829985", + "transactionIndex": "0x2", + "type": "0x2", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0x58af2", + "logsBloom": "0x00008000002000000000100000000000000000000000000000000000004000000000300000000000000000000000000000000000000200000000000080000000000000000000000000000001002000000000000001001000000000000000000000080000020000000000000000000800000000200000000000002000000001000000000008000000000000000001000000000480000000000000000004000000000010001000001000000000000000000000000000000000000000000000000000000000000000000010000000000000004000000000000000000002000020000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0x6c3f2a849b90f4902a13f0a1cb7aabdbfd94754d9fcc1c3e006f3198c78a62b7", + "nonce": "0x0000000000000000", + "parentHash": "0x996594979c173d1bd6828fd1e90d3e00579e71bc71de6ad236977dae87953c2f", + "receiptsRoot": "0xe1e140c9522914486c0338f4022999fd1940ef448d8b709d8431fd6c5729a4bb", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x980", + "stateRoot": "0xd3b3b007c6f83779866dc76162ad899c0a283a823d12f53572a0cfad49f03c2e", + "timestamp": "0x65421c9a", + "totalDifficulty": "0x0", + "transactionsRoot": "0x367c99573f2dabf29aae707b0a334e768e0167f463e12ea0e6a699b5a9fbcb29", + "uncles": [], + "baseFeePerGas": "0x32" + }, + { + "number": "0xfed91c", + "hash": "0x677035e9924cf523dea9a5b5bdf58bbb5e670670c9d4c34d1104b8f51145b138", + "transactions": [ + { + "blockHash": "0x677035e9924cf523dea9a5b5bdf58bbb5e670670c9d4c34d1104b8f51145b138", + "blockNumber": "0xfed91c", + "hash": "0x43f734f9a9cc309f305eb5c9e98a8645bb271255c74fc91b141e1edb7987b2cc", + "from": "0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001", + "gas": "0xf4240", + "gasPrice": "0x0", + "input": "0x015d8eb9000000000000000000000000000000000000000000000000000000000098181f0000000000000000000000000000000000000000000000000000000065421c48000000000000000000000000000000000000000000000000000000000000000a0a2cf2dc6b0e57d45fbcb949511d0fa42cd13b361d55b8cf46ffc2b05e1b186400000000000000000000000000000000000000000000000000000000000000010000000000000000000000007431310e026b69bfc676c0013e12a1a11411eec9000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240", + "mint": "0x0", + "nonce": "0xc0e0f3", + "r": "0x0", + "s": "0x0", + "sourceHash": "0xd2e9ac342752449811c4e4cff763603c3045d91149a4a448ebb1bba0f0427cea", + "to": "0x4200000000000000000000000000000000000015", + "transactionIndex": "0x0", + "type": "0x7e", + "v": "0x0", + "value": "0x0" + } + ], + "difficulty": "0x0", + "extraData": "0x", + "gasLimit": "0x2faf080", + "gasUsed": "0xf9dd", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x4200000000000000000000000000000000000011", + "mixHash": "0x6c3f2a849b90f4902a13f0a1cb7aabdbfd94754d9fcc1c3e006f3198c78a62b7", + "nonce": "0x0000000000000000", + "parentHash": "0xc667c769f4ab49d08b7ed565e5df1367028d1553e3584f0c7daf510dc3e4d67e", + "receiptsRoot": "0x77ddca50217671904c60e97a070a82ede75581f477f7c49fadd91214e7091ae6", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x365", + "stateRoot": "0x6f72537a7a8ff9d4b3d747902924015e36dc947ac10b402f9d5c8ffe15393b2c", + "timestamp": "0x65421c9c", + "totalDifficulty": "0x0", + "transactionsRoot": "0x62c96bb38b6084a0eaf88121d99fa1610cec92c7fe4a027567e5407dc4b18935", + "uncles": [], + "baseFeePerGas": "0x32" + } +] \ No newline at end of file diff --git a/hildr-node/build.gradle b/hildr-node/build.gradle index 09da983e..d58d4e00 100644 --- a/hildr-node/build.gradle +++ b/hildr-node/build.gradle @@ -15,6 +15,7 @@ plugins { id "com.diffplug.spotless" version "6.19.0" id "net.ltgt.errorprone" version "3.1.0" id 'org.graalvm.buildtools.native' version '0.9.22' + id 'com.github.johnrengelman.shadow' version '8.1.1' // id 'org.unbroken-dome.test-sets' version '4.0.0' // id 'maven-publish' // id "io.github.gradle-nexus.publish-plugin" version "1.1.0" @@ -378,17 +379,16 @@ javadoc { } jar { - dependsOn(configurations.runtimeClasspath) + enabled = false manifest { attributes "Main-Class": "io.optimism.Hildr" + attributes "Multi-Release": "true" } + dependsOn(shadowJar) +} - duplicatesStrategy = DuplicatesStrategy.WARN - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } { - exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF' - } +shadowJar { + archiveFileName = "${project.name}-${project.version}.jar" } task copyJarToBinaryDir { diff --git a/hildr-node/src/main/java/io/optimism/cli/Cli.java b/hildr-node/src/main/java/io/optimism/cli/Cli.java index 002a78a6..60a4b6d5 100644 --- a/hildr-node/src/main/java/io/optimism/cli/Cli.java +++ b/hildr-node/src/main/java/io/optimism/cli/Cli.java @@ -100,6 +100,16 @@ public class Cli implements Runnable { description = "A trusted L2 RPC URL to use for fast/checkpoint syncing") String checkpointSyncUrl; + @Option( + names = {"--metrics-enable"}, + description = "The flag of enabled metrics") + Boolean metricsEnable; + + @Option( + names = {"--metrics-port"}, + description = "The port of metrics server") + Integer metricsPort; + @Option(names = "--devnet", description = "Dev net flag") private Boolean devnet; @@ -109,7 +119,14 @@ public Cli() {} @Override public void run() { TracerTaskWrapper.setTracerSupplier(Logging.INSTANCE::getTracer); - InnerMetrics.start(9200); + if (Boolean.TRUE.equals(metricsEnable)) { + var metricsPort = this.metricsPort; + if (metricsPort == null || metricsPort > 65535) { + metricsPort = 9200; + } + InnerMetrics.start(metricsPort); + } + Signal.handle(new Signal("INT"), sig -> System.exit(0)); Signal.handle(new Signal("TERM"), sig -> System.exit(0)); diff --git a/hildr-node/src/main/java/io/optimism/common/BlockInfo.java b/hildr-node/src/main/java/io/optimism/common/BlockInfo.java index 3e36f739..60b3eb6c 100644 --- a/hildr-node/src/main/java/io/optimism/common/BlockInfo.java +++ b/hildr-node/src/main/java/io/optimism/common/BlockInfo.java @@ -18,6 +18,7 @@ import io.optimism.engine.ExecutionPayload; import java.math.BigInteger; +import java.util.Objects; import org.web3j.protocol.core.methods.response.EthBlock.Block; /** @@ -63,4 +64,23 @@ public static BlockInfo from(ExecutionPayload payload) { return new BlockInfo( payload.blockHash(), payload.blockNumber(), payload.parentHash(), payload.timestamp()); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BlockInfo blockInfo)) { + return false; + } + return Objects.equals(hash, blockInfo.hash) + && Objects.equals(number, blockInfo.number) + && Objects.equals(parentHash, blockInfo.parentHash) + && Objects.equals(timestamp, blockInfo.timestamp); + } + + @Override + public int hashCode() { + return Objects.hash(hash, number, parentHash, timestamp); + } } diff --git a/hildr-node/src/main/java/io/optimism/config/Config.java b/hildr-node/src/main/java/io/optimism/config/Config.java index 9a356598..787daf23 100644 --- a/hildr-node/src/main/java/io/optimism/config/Config.java +++ b/hildr-node/src/main/java/io/optimism/config/Config.java @@ -52,6 +52,7 @@ * @param jwtSecret L2 engine API jwt secret. * @param chainConfig The chain config. * @param rpcPort The rpc port. + * @param devnet The flag of devnet. * @param checkpointSyncUrl The checkpoint sync url. * @author grapebaba * @since 0.1.0 @@ -63,6 +64,7 @@ public record Config( String jwtSecret, String checkpointSyncUrl, Integer rpcPort, + Boolean devnet, ChainConfig chainConfig) { /** @@ -175,6 +177,7 @@ public Map toConfigMap() { if (rpcPort != null) { map.put("config.rpcPort", rpcPort.toString()); } + map.put("config.devnet", String.valueOf(devnet != null && devnet)); return map; } } @@ -256,43 +259,6 @@ public static ChainConfig optimism() { "0x4200000000000000000000000000000000000016"); } - /** - * Optimism sepolia ChainConfig. - * - * @return the chain config - */ - public static ChainConfig optimismSepolia() { - return new ChainConfig( - "base-goerli", - BigInteger.valueOf(11155111L), - BigInteger.valueOf(11155420L), - new Epoch( - BigInteger.valueOf(4071408L), - "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", - BigInteger.valueOf(1691802540L)), - new BlockInfo( - "0x102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d", - BigInteger.valueOf(0L), - Numeric.toHexString(new byte[32]), - BigInteger.valueOf(1691802540L)), - new SystemConfig( - "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", - BigInteger.valueOf(30_000_000L), - BigInteger.valueOf(188), - BigInteger.valueOf(684000), - "0x0000000000000000000000000000000000000000"), - "0xff00000000000000000000000000000011155420", - "0x16fc5058f25648194471939df75cf27a2fdc48bc", - "0x034edd2a225f7f429a63e0f1d2084b9e0a93b538", - BigInteger.valueOf(100_000_000L), - BigInteger.valueOf(300L), - BigInteger.valueOf(3600L), - BigInteger.valueOf(600L), - BigInteger.ZERO, - BigInteger.valueOf(2L), - "0x4200000000000000000000000000000000000016"); - } - /** * Base mainnet chain config. * @@ -367,6 +333,43 @@ public static ChainConfig optimismGoerli() { "0xEF2ec5A5465f075E010BE70966a8667c94BCe15a"); } + /** + * Optimism sepolia ChainConfig. + * + * @return the chain config + */ + public static ChainConfig optimismSepolia() { + return new ChainConfig( + "base-goerli", + BigInteger.valueOf(11155111L), + BigInteger.valueOf(11155420L), + new Epoch( + BigInteger.valueOf(4071408L), + "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", + BigInteger.valueOf(1691802540L)), + new BlockInfo( + "0x102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d", + BigInteger.valueOf(0L), + Numeric.toHexString(new byte[32]), + BigInteger.valueOf(1691802540L)), + new SystemConfig( + "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", + BigInteger.valueOf(30_000_000L), + BigInteger.valueOf(188), + BigInteger.valueOf(684000), + "0x0000000000000000000000000000000000000000"), + "0xff00000000000000000000000000000011155420", + "0x16fc5058f25648194471939df75cf27a2fdc48bc", + "0x034edd2a225f7f429a63e0f1d2084b9e0a93b538", + BigInteger.valueOf(100_000_000L), + BigInteger.valueOf(300L), + BigInteger.valueOf(3600L), + BigInteger.valueOf(600L), + BigInteger.ZERO, + BigInteger.valueOf(2L), + "0x4200000000000000000000000000000000000016"); + } + /** * Base goerli ChainConfig. * diff --git a/hildr-node/src/main/java/io/optimism/driver/Driver.java b/hildr-node/src/main/java/io/optimism/driver/Driver.java index 001bd195..228f4917 100644 --- a/hildr-node/src/main/java/io/optimism/driver/Driver.java +++ b/hildr-node/src/main/java/io/optimism/driver/Driver.java @@ -33,11 +33,23 @@ import io.optimism.engine.ExecutionPayload.PayloadAttributes; import io.optimism.l1.BlockUpdate; import io.optimism.l1.ChainWatcher; +import io.optimism.rpc.RpcMethod; import io.optimism.rpc.RpcServer; +import io.optimism.rpc.internal.result.SyncStatusResult; import io.optimism.telemetry.InnerMetrics; +import io.optimism.type.BlockId; +import io.optimism.type.DepositTransaction; +import io.optimism.type.Genesis; +import io.optimism.type.L1BlockInfo; +import io.optimism.type.L2BlockRef; +import io.optimism.type.RollupConfigResult; +import io.optimism.type.SystemConfig; +import io.optimism.utilities.TxDecoder; +import io.optimism.utilities.rpc.Web3jProvider; import io.optimism.utilities.telemetry.TracerTaskWrapper; import java.math.BigInteger; import java.time.Duration; +import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; @@ -48,6 +60,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import java.util.stream.Collectors; import jdk.incubator.concurrent.StructuredTaskScope; import org.jctools.queues.MessagePassingQueue; @@ -56,7 +69,6 @@ import org.slf4j.LoggerFactory; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.response.EthBlock; -import org.web3j.protocol.http.HttpService; import org.web3j.utils.Numeric; /** @@ -95,6 +107,10 @@ public class Driver extends AbstractExecutionThreadService { private CountDownLatch latch; + private final Config config; + + private RollupConfigResult cachedRollConfig; + /** * Instantiates a new Driver. * @@ -105,6 +121,7 @@ public class Driver extends AbstractExecutionThreadService { * @param unsafeBlockQueue the unsafe block queue * @param rpcServer the rpc server * @param latch the close notifier + * @param config the chain config */ @SuppressWarnings("preview") public Driver( @@ -114,7 +131,8 @@ public Driver( ChainWatcher chainWatcher, MessagePassingQueue unsafeBlockQueue, RpcServer rpcServer, - CountDownLatch latch) { + CountDownLatch latch, + Config config) { this.engineDriver = engineDriver; this.rpcServer = rpcServer; this.pipeline = pipeline; @@ -125,6 +143,11 @@ public Driver( this.unfinalizedBlocks = Lists.newArrayList(); this.executor = Executors.newVirtualThreadPerTaskExecutor(); this.latch = latch; + this.config = config; + HashMap rpcHandler = HashMap.newHashMap(1); + rpcHandler.put(RpcMethod.OP_SYNC_STATUS.getRpcMethodName(), unused -> this.getSyncStatus()); + rpcHandler.put(RpcMethod.OP_ROLLUP_CONFIG.getRpcMethodName(), unused -> this.getRollupConfig()); + this.rpcServer.register(rpcHandler); } /** @@ -147,7 +170,7 @@ public EngineDriver getEngineDriver() { */ public static Driver from(Config config, CountDownLatch latch) throws InterruptedException, ExecutionException { - Web3j provider = Web3j.build(new HttpService(config.l2RpcUrl())); + Web3j provider = Web3jProvider.createClient(config.l2RpcUrl()); EthBlock finalizedBlock; try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { @@ -208,7 +231,82 @@ public static Driver from(Config config, CountDownLatch latch) MpscUnboundedXaddArrayQueue unsafeBlockQueue = new MpscUnboundedXaddArrayQueue<>(1024 * 64); provider.shutdown(); - return new Driver<>(engineDriver, pipeline, state, watcher, unsafeBlockQueue, rpcServer, latch); + return new Driver<>( + engineDriver, pipeline, state, watcher, unsafeBlockQueue, rpcServer, latch, config); + } + + /** + * Get rollupConfig. + * + * @return The rollup config + */ + public RollupConfigResult getRollupConfig() { + var chainConfig = this.config.chainConfig(); + if (this.cachedRollConfig == null) { + var rollupConfig = new RollupConfigResult(); + rollupConfig.setBlockTime(chainConfig.blockTime()); + rollupConfig.setMaxSequencerDrift(chainConfig.maxSeqDrift()); + rollupConfig.setSeqWindowSize(chainConfig.seqWindowSize()); + rollupConfig.setChannelTimeout(chainConfig.channelTimeout()); + rollupConfig.setL1ChainId(chainConfig.l1ChainId()); + rollupConfig.setL2ChainId(chainConfig.l2ChainId()); + rollupConfig.setRegolithTime(chainConfig.regolithTime()); + rollupConfig.setBatchInboxAddress(chainConfig.batchInbox()); + rollupConfig.setDepositContractAddress(chainConfig.depositContract()); + rollupConfig.setL1SystemConfigAddress(chainConfig.systemConfigContract()); + var curSysConfig = chainConfig.systemConfig(); + var sc = + new SystemConfig( + curSysConfig.batchSender(), + curSysConfig.l1FeeOverhead(), + curSysConfig.l1FeeScalar(), + curSysConfig.gasLimit()); + var latestGenesis = + new Genesis( + new BlockId(chainConfig.l1StartEpoch().hash(), chainConfig.l1StartEpoch().number()), + new BlockId(chainConfig.l2Genesis().hash(), chainConfig.l2Genesis().number()), + chainConfig.l2Genesis().timestamp(), + sc); + this.cachedRollConfig.setGenesis(latestGenesis); + this.cachedRollConfig = rollupConfig; + } + return this.cachedRollConfig; + } + + /** + * Get sync status. + * + * @return result of sync status. + */ + public SyncStatusResult getSyncStatus() { + // CurrentL1 + final var currentL1 = this.chainWatcher.getCurrentL1(); + // CurrentL1Finalized + final var currentL1Finalized = this.chainWatcher.getCurrentL1Finalized(); + + final var l1HeadBlock = this.chainWatcher.getL1HeadBlock(); + final var l1SafeBlock = this.chainWatcher.getL1SafeBlock(); + final var l1FinalizedBlock = this.chainWatcher.getL1FinalizedBlock(); + + // FinalizedL2 + final var finalizedHead = this.engineDriver.getFinalizedHead(); + // SafeL2 + final var safeHead = this.engineDriver.getSafeHead(); + // UnsafeL2 + final var unsafeHead = this.engineDriver.getUnsafeHead(); + + final var unsafeL2SyncTarget = this.unsafeL2SyncTarget(); + return new SyncStatusResult( + currentL1, + currentL1Finalized, + l1HeadBlock, + l1SafeBlock, + l1FinalizedBlock, + unsafeHead, + safeHead, + finalizedHead, + unsafeL2SyncTarget, + unsafeHead); } @Override @@ -476,6 +574,59 @@ private boolean synced() { return !this.unfinalizedBlocks.isEmpty(); } + /** + * Retrieves the first queued-up L2 unsafe payload, or a zeroed reference if there is none. + * + * @return the L2BlockRef instance + */ + private L2BlockRef unsafeL2SyncTarget() { + ExecutionPayload unsafePayload = this.unsafeBlockQueue.peek(); + if (unsafePayload == null) { + return L2BlockRef.EMPTY; + } + return Driver.payloadToRef(unsafePayload, this.config.chainConfig()); + } + + /** + * Read L2BlockRef info from Execution payload. + * + * @param payload l2 execution payload info + * @param genesis L2 genesis info + * @return L2BlockRef instance + */ + public static L2BlockRef payloadToRef(ExecutionPayload payload, Config.ChainConfig genesis) { + BlockId l1Origin; + BigInteger sequenceNumber; + if (payload.blockNumber().compareTo(genesis.l2Genesis().number()) == 0) { + if (!payload.blockHash().equals(genesis.l2Genesis().hash())) { + throw new RuntimeException( + String.format( + "expected L2 genesis hash to match L2 block at genesis block number %d: %s <> %s", + genesis.l2Genesis().number(), payload.blockHash(), genesis.l2Genesis().hash())); + } + l1Origin = new BlockId(genesis.l1StartEpoch().hash(), genesis.l1StartEpoch().number()); + sequenceNumber = BigInteger.ZERO; + } else { + if (payload.transactions().size() == 0) { + throw new RuntimeException( + String.format( + "l2 block is missing L1 info deposit tx, block hash: %s", payload.blockHash())); + } + DepositTransaction depositTx = TxDecoder.decodeToDeposit(payload.transactions().get(0)); + L1BlockInfo info = L1BlockInfo.from(Numeric.hexStringToByteArray(depositTx.getData())); + l1Origin = new BlockId(info.blockHash(), info.number()); + sequenceNumber = info.sequenceNumber(); + } + + return new L2BlockRef( + payload.blockHash(), + payload.blockNumber(), + payload.parentHash(), + payload.timestamp(), + new BlockId(l1Origin.hash(), l1Origin.number()), + sequenceNumber); + } + /** * The type Unfinalized block. * diff --git a/hildr-node/src/main/java/io/optimism/l1/ChainWatcher.java b/hildr-node/src/main/java/io/optimism/l1/ChainWatcher.java index 03659244..b86e0552 100644 --- a/hildr-node/src/main/java/io/optimism/l1/ChainWatcher.java +++ b/hildr-node/src/main/java/io/optimism/l1/ChainWatcher.java @@ -16,6 +16,7 @@ package io.optimism.l1; +import io.optimism.common.BlockInfo; import io.optimism.config.Config; import java.math.BigInteger; import java.util.concurrent.Executors; @@ -91,4 +92,58 @@ public void restart(BigInteger l1StartBlock, BigInteger l2StartBlock) { Executors.newVirtualThreadPerTaskExecutor()); this.start(); } + + /** + * Gets current L1 block info. + * + * @return the current L1 block info. + */ + public BlockInfo getCurrentL1() { + return this.innerWatcher.getCurrentL1(); + } + + /** + * Gets current L1 finalized block info. + * + * @return the current L1 finalized block info. + */ + public BlockInfo getCurrentL1Finalized() { + return this.innerWatcher.getCurrentL1Finalized(); + } + + /** + * Gets L1 head block info. + * + * @return L1 head block info. + */ + public BlockInfo getL1HeadBlock() { + return this.innerWatcher.getL1HeadBlock(); + } + + /** + * Gets L1 safe block info. + * + * @return L1 safe block info. + */ + public BlockInfo getL1SafeBlock() { + return this.innerWatcher.getL1SafeBlock(); + } + + /** + * Gets L1 finalized block info. + * + * @return L1 finalized block info. + */ + public BlockInfo getL1FinalizedBlock() { + return this.innerWatcher.getL1FinalizedBlock(); + } + + /** + * Gets current rollup system config info. + * + * @return rollup system config info. + */ + public Config.SystemConfig getSystemConfig() { + return this.innerWatcher.getSystemConfig(); + } } diff --git a/hildr-node/src/main/java/io/optimism/l1/DepositsNotFoundException.java b/hildr-node/src/main/java/io/optimism/l1/DepositsNotFoundException.java index 2d02ea43..2bf414f8 100644 --- a/hildr-node/src/main/java/io/optimism/l1/DepositsNotFoundException.java +++ b/hildr-node/src/main/java/io/optimism/l1/DepositsNotFoundException.java @@ -28,4 +28,13 @@ public class DepositsNotFoundException extends RuntimeException { public DepositsNotFoundException() { super("deposits not found"); } + + /** + * Instantiates a new Deposits not found exception. + * + * @param message exception message info + */ + public DepositsNotFoundException(String message) { + super(message); + } } diff --git a/hildr-node/src/main/java/io/optimism/l1/InnerWatcher.java b/hildr-node/src/main/java/io/optimism/l1/InnerWatcher.java index c21d20e9..b4ece710 100644 --- a/hildr-node/src/main/java/io/optimism/l1/InnerWatcher.java +++ b/hildr-node/src/main/java/io/optimism/l1/InnerWatcher.java @@ -30,6 +30,7 @@ import io.optimism.l1.BlockUpdate.FinalityUpdate; import io.optimism.utilities.rpc.Web3jProvider; import io.optimism.utilities.telemetry.Logging; +import io.reactivex.disposables.Disposable; import java.math.BigInteger; import java.time.Duration; import java.util.ArrayList; @@ -58,6 +59,7 @@ import org.web3j.protocol.core.methods.response.EthLog; import org.web3j.protocol.core.methods.response.EthLog.LogObject; import org.web3j.protocol.core.methods.response.EthLog.LogResult; +import org.web3j.protocol.websocket.events.NewHead; import org.web3j.tuples.generated.Tuple2; import org.web3j.utils.Numeric; @@ -99,11 +101,18 @@ public class InnerWatcher extends AbstractExecutionThreadService { /** Ethers http provider for L1. */ private final Web3j provider; + private BigInteger l1StartBlock; + + private BigInteger l2StartBlock; + /** Channel to send block updates. */ private final MessagePassingQueue blockUpdateQueue; /** Most recent ingested block. */ - BigInteger currentBlock; + private BigInteger currentBlock; + + /** Most recent ingested block info. */ + private volatile BlockInfo currentBlockInfo; /** Most recent block. */ private BigInteger headBlock; @@ -111,6 +120,13 @@ public class InnerWatcher extends AbstractExecutionThreadService { /** Most recent finalized block. */ private BigInteger finalizedBlock; + /** Most recent finalized block info. */ + private volatile BlockInfo l1Finalized; + + private volatile BlockInfo l1Safe; + + private volatile BlockInfo l1Head; + /** List of blocks that have not been finalized yet. */ private List unfinalizedBlocks; @@ -131,6 +147,10 @@ public class InnerWatcher extends AbstractExecutionThreadService { private boolean isShutdownTriggered = false; + private Disposable l1HeadListener; + + private boolean devnet = false; + /** * create a InnerWatcher instance. * @@ -147,14 +167,11 @@ public InnerWatcher( BigInteger l2StartBlock, ExecutorService executor) { this.executor = executor; - this.provider = Web3jProvider.createClient(config.l1RpcUrl()); this.config = config; - - if (l2StartBlock.equals(config.chainConfig().l2Genesis().number())) { - this.systemConfig = config.chainConfig().systemConfig(); - } else { - this.getMetadataFromL2(l2StartBlock); - } + this.provider = Web3jProvider.createClient(config.l1RpcUrl()); + this.l1StartBlock = l1StartBlock; + this.l2StartBlock = l2StartBlock; + this.devnet = config.devnet() != null && config.devnet(); this.blockUpdateQueue = queue; this.currentBlock = l1StartBlock; @@ -167,9 +184,9 @@ public InnerWatcher( private void getMetadataFromL2(BigInteger l2StartBlock) { Web3j l2Client = Web3jProvider.createClient(config.l2RpcUrl()); - EthBlock block; + EthBlock.Block block; try { - block = this.getBlock(l2Client, l2StartBlock.subtract(BigInteger.ONE)); + block = this.pollBlockByNumber(l2Client, l2StartBlock.subtract(BigInteger.ONE)); } catch (InterruptedException | ExecutionException e) { if (e instanceof InterruptedException) { Thread.currentThread().interrupt(); @@ -177,17 +194,17 @@ private void getMetadataFromL2(BigInteger l2StartBlock) { l2Client.shutdown(); throw new HildrServiceExecutionException(e); } - if (block.getBlock().getTransactions().isEmpty()) { + if (block.getTransactions().isEmpty()) { throw new L1AttributesDepositedTxNotFoundException(); } EthBlock.TransactionObject tx = - (EthBlock.TransactionObject) block.getBlock().getTransactions().get(0).get(); + (EthBlock.TransactionObject) block.getTransactions().get(0).get(); final byte[] input = Numeric.hexStringToByteArray(tx.getInput()); final String batchSender = Numeric.toHexString(Arrays.copyOfRange(input, 176, 196)); var l1FeeOverhead = Numeric.toBigInt(Arrays.copyOfRange(input, 196, 228)); var l1FeeScalar = Numeric.toBigInt(Arrays.copyOfRange(input, 228, 260)); - var gasLimit = block.getBlock().getGasLimit(); + var gasLimit = block.getGasLimit(); this.systemConfig = new Config.SystemConfig( batchSender, @@ -198,6 +215,20 @@ private void getMetadataFromL2(BigInteger l2StartBlock) { l2Client.shutdown(); } + private Disposable subscribeL1NewHeads() { + return this.provider + .newHeadsNotifications() + .subscribe( + notification -> { + NewHead header = notification.getParams().getResult(); + String hash = header.getHash(); + BigInteger number = Numeric.toBigInt(header.getNumber()); + String parentHash = header.getParentHash(); + BigInteger time = Numeric.toBigInt(header.getTimestamp()); + l1Head = new BlockInfo(hash, number, parentHash, time); + }); + } + /** * try ingest block. * @@ -205,12 +236,17 @@ private void getMetadataFromL2(BigInteger l2StartBlock) { * @throws InterruptedException thrown if executor has been shutdown */ public void tryIngestBlock() throws ExecutionException, InterruptedException { + final EthBlock.Block l1SafeBlock = this.getSafe(); + this.l1Safe = BlockInfo.from(l1SafeBlock); + if (this.currentBlock.compareTo(this.finalizedBlock) > 0) { LOGGER.debug( "will get finalized block: currentBlock({}) > finalizedBlock({})", this.currentBlock, this.finalizedBlock); - this.finalizedBlock = this.getFinalized(); + var finalizedBlockDetail = this.getFinalized(); + this.finalizedBlock = finalizedBlockDetail.getNumber(); + this.l1Finalized = BlockInfo.from(finalizedBlockDetail); this.putBlockUpdate(new FinalityUpdate(finalizedBlock)); this.unfinalizedBlocks = this.unfinalizedBlocks.stream() @@ -224,7 +260,7 @@ public void tryIngestBlock() throws ExecutionException, InterruptedException { "will get head block: currentBlock({}) > headBlock({})", this.currentBlock, this.headBlock); - this.headBlock = this.getHead(); + this.headBlock = this.getHead().getNumber(); } if (this.currentBlock.compareTo(this.headBlock) <= 0) { @@ -234,7 +270,10 @@ public void tryIngestBlock() throws ExecutionException, InterruptedException { this.headBlock); updateSystemConfigWithNewestLog(); } else { - LOGGER.debug("will sleep 250 milliseconds", this.currentBlock, this.headBlock); + LOGGER.debug( + "will sleep 250 milliseconds: currentBlock({}) > headBlock({})", + this.currentBlock, + this.headBlock); try { Thread.sleep(Duration.ofMillis(250L)); } catch (InterruptedException e) { @@ -246,26 +285,22 @@ public void tryIngestBlock() throws ExecutionException, InterruptedException { private void updateSystemConfigWithNewestLog() throws ExecutionException, InterruptedException { this.updateSystemConfig(); - EthBlock blockResp = this.getBlock(this.provider, this.currentBlock); + EthBlock.Block blockResp = this.pollBlockByNumber(this.provider, this.currentBlock); List userDeposits = this.getDeposits(this.currentBlock); boolean finalized = this.currentBlock.compareTo(this.finalizedBlock) >= 0; L1Info l1Info = L1Info.create( - blockResp.getBlock(), + blockResp, userDeposits, config.chainConfig().batchInbox(), finalized, this.systemConfig); if (l1Info.blockInfo().number().compareTo(this.finalizedBlock) >= 0) { - BlockInfo blockInfo = - new BlockInfo( - l1Info.blockInfo().hash(), - l1Info.blockInfo().number(), - blockResp.getBlock().getParentHash(), - l1Info.blockInfo().timestamp()); + BlockInfo blockInfo = BlockInfo.from(blockResp); this.unfinalizedBlocks.add(blockInfo); + this.currentBlockInfo = blockInfo; } BlockUpdate update = @@ -367,63 +402,39 @@ private boolean checkReorg() { return false; } - private BigInteger getFinalized() throws ExecutionException, InterruptedException { - EthBlock.Block finalizedBlock = - this.executor - .submit( - () -> { - EthBlock block = - this.provider - .ethGetBlockByNumber(DefaultBlockParameterName.FINALIZED, false) - .send(); - return block.getBlock(); - }) - .get(); - if (finalizedBlock == null) { - throw new BlockNotIncludedException(); - } - if (finalizedBlock.getNumber() == null) { - throw new BlockNotIncludedException(); - } + private EthBlock.Block getSafe() throws ExecutionException, InterruptedException { + return this.pollBlock(this.provider, DefaultBlockParameterName.SAFE, false); + } - return finalizedBlock.getNumber(); + private EthBlock.Block getFinalized() throws ExecutionException, InterruptedException { + var parameter = + this.devnet ? DefaultBlockParameterName.LATEST : DefaultBlockParameterName.FINALIZED; + return this.pollBlock(this.provider, parameter, false); } - private BigInteger getHead() throws ExecutionException, InterruptedException { - EthBlock.Block headBlock = - this.executor - .submit( - () -> { - EthBlock block = - this.provider - .ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false) - .send(); - return block.getBlock(); - }) - .get(); - if (headBlock == null) { - throw new BlockNotIncludedException(); - } - if (headBlock.getNumber() == null) { - throw new BlockNotIncludedException(); - } + private EthBlock.Block getHead() throws ExecutionException, InterruptedException { + return this.pollBlock(this.provider, DefaultBlockParameterName.LATEST, false); + } - return headBlock.getNumber(); + private EthBlock.Block pollBlockByNumber(final Web3j client, final BigInteger blockNum) + throws ExecutionException, InterruptedException { + return pollBlock(client, DefaultBlockParameter.valueOf(blockNum), true); } - private EthBlock getBlock(final Web3j client, final BigInteger blockNum) + private EthBlock.Block pollBlock( + final Web3j client, final DefaultBlockParameter parameter, final boolean fullTxObjectFlag) throws ExecutionException, InterruptedException { - EthBlock block = + EthBlock.Block block = this.executor - .submit( - () -> - client - .ethGetBlockByNumber(DefaultBlockParameter.valueOf(blockNum), true) - .send()) - .get(); + .submit(() -> client.ethGetBlockByNumber(parameter, fullTxObjectFlag).send()) + .get() + .getBlock(); if (block == null) { throw new BlockNotIncludedException(); } + if (block.getNumber() == null) { + throw new BlockNotIncludedException(); + } return block; } @@ -454,26 +465,25 @@ private List getDeposits(BigInteger blockNum) this.config.chainConfig().depositContract(), TRANSACTION_DEPOSITED_TOPIC); - List depositLogs = - result.getLogs().stream() - .map( - log -> { - if (log instanceof LogObject) { - return UserDeposited.fromLog((LogObject) log); - } else { - throw new IllegalStateException( - "Unexpected result type: " + log.get() + " required LogObject"); - } - }) - .collect(Collectors.toList()); - - for (BigInteger i = blockNum; i.compareTo(endBlock) <= 0; i = i.add(BigInteger.ONE)) { - final BigInteger num = i; - final List collect = - depositLogs.stream() - .filter(log -> log.l1BlockNum().compareTo(num) == 0) - .collect(Collectors.toList()); - InnerWatcher.this.deposits.put(num, collect); + result + .getLogs() + .forEach( + log -> { + if (log instanceof LogObject) { + var userDeposited = UserDeposited.fromLog((LogObject) log); + final var num = userDeposited.l1BlockNum(); + var userDepositeds = + InnerWatcher.this.deposits.computeIfAbsent(num, k -> new ArrayList<>()); + userDepositeds.add(userDeposited); + } else { + throw new IllegalStateException( + "Unexpected result type: " + log.get() + " required LogObject"); + } + }); + var max = (int) endBlock.subtract(blockNum).longValue(); + for (int i = 0; i < max; i++) { + InnerWatcher.this.deposits.putIfAbsent( + blockNum.add(BigInteger.valueOf(i)), new ArrayList<>()); } var remv = InnerWatcher.this.deposits.remove(blockNum); if (remv == null) { @@ -482,6 +492,16 @@ private List getDeposits(BigInteger blockNum) return remv; } + @Override + protected void startUp() throws Exception { + if (this.l2StartBlock.equals(config.chainConfig().l2Genesis().number())) { + this.systemConfig = config.chainConfig().systemConfig(); + } else { + this.getMetadataFromL2(this.l2StartBlock); + } + this.l1HeadListener = this.subscribeL1NewHeads(); + } + @Override protected void run() { while (isRunning() && !this.isShutdownTriggered) { @@ -511,10 +531,67 @@ protected Executor executor() { protected void shutDown() { this.executor.shutdown(); this.provider.shutdown(); + if (!this.l1HeadListener.isDisposed()) { + this.l1HeadListener.dispose(); + } } @Override protected void triggerShutdown() { this.isShutdownTriggered = true; } + + /** + * Gets Current L1 block. + * + * @return Current L1 BlockInfo instance + */ + public BlockInfo getCurrentL1() { + return this.currentBlockInfo; + } + + /** + * Gets Current L1 finalized block. + * + * @return Current L1 finalized BlockInfo instance + */ + public BlockInfo getCurrentL1Finalized() { + return this.l1Finalized; + } + + /** + * Gets L1 head block. + * + * @return L1 head BlockInfo instance + */ + public BlockInfo getL1HeadBlock() { + return this.l1Head; + } + + /** + * Gets L1 safe block. + * + * @return L1 safe BlockInfo instance + */ + public BlockInfo getL1SafeBlock() { + return this.l1Safe; + } + + /** + * Gets L1 finalized block. + * + * @return L1 finalized BlockInfo instance + */ + public BlockInfo getL1FinalizedBlock() { + return this.l1Finalized; + } + + /** + * Get system config info. + * + * @return SystemConfig instance + */ + public SystemConfig getSystemConfig() { + return this.systemConfig; + } } diff --git a/hildr-node/src/main/java/io/optimism/rpc/RpcMethod.java b/hildr-node/src/main/java/io/optimism/rpc/RpcMethod.java index 5072ab25..ec18ab14 100644 --- a/hildr-node/src/main/java/io/optimism/rpc/RpcMethod.java +++ b/hildr-node/src/main/java/io/optimism/rpc/RpcMethod.java @@ -27,7 +27,11 @@ public enum RpcMethod { /** optimism_outputAtBlock api. */ - OP_OUTPUT_AT_BLOCK("optimism_outputAtBlock"); + OP_OUTPUT_AT_BLOCK("optimism_outputAtBlock"), + /** optimism_syncStatus api. */ + OP_SYNC_STATUS("optimism_syncStatus"), + /** optimism_rollupConfig api. */ + OP_ROLLUP_CONFIG("optimism_rollupConfig"); private final String rpcMethodName; diff --git a/hildr-node/src/main/java/io/optimism/rpc/RpcServer.java b/hildr-node/src/main/java/io/optimism/rpc/RpcServer.java index 30497bd5..75e896ee 100644 --- a/hildr-node/src/main/java/io/optimism/rpc/RpcServer.java +++ b/hildr-node/src/main/java/io/optimism/rpc/RpcServer.java @@ -25,6 +25,7 @@ import io.optimism.rpc.handler.JsonRpcParseHandler; import io.optimism.rpc.handler.TimeoutHandler; import io.optimism.rpc.methods.JsonRpcMethod; +import io.optimism.rpc.methods.JsonRpcMethodAdapter; import io.optimism.rpc.methods.JsonRpcMethodsFactory; import io.optimism.utilities.telemetry.Logging; import io.vertx.core.Handler; @@ -41,6 +42,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -193,4 +195,20 @@ public void stop() { throw new RuntimeException(e); } } + + /** + * register method of handler to rpc server after start. + * + * @param methods map of json rpc handler + */ + public void register(Map methods) { + if (methods == null || methods.size() == 0) { + return; + } + methods.forEach( + (name, fn) -> { + logger.info("put json rpc method into processor: name({})", name); + this.methods.putIfAbsent(name, new JsonRpcMethodAdapter(name, fn)); + }); + } } diff --git a/hildr-node/src/main/java/io/optimism/rpc/internal/result/SyncStatusResult.java b/hildr-node/src/main/java/io/optimism/rpc/internal/result/SyncStatusResult.java new file mode 100644 index 00000000..4927378e --- /dev/null +++ b/hildr-node/src/main/java/io/optimism/rpc/internal/result/SyncStatusResult.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.rpc.internal.result; + +import io.optimism.common.BlockInfo; +import io.optimism.type.L2BlockRef; + +/** + * The SyncStatusResult type. A snapshot of the driver. Values may be zeroed if not yet initialized. + * + * @param currentL1 The L1 block that the driver process is currently at in the inner-most stage. + * @param currentL1Finalized The L1 block that the driver process is currently accepting as + * finalized in the inner-most stage. + * @param headL1 The perceived head of the L1 chain, no confirmation distance. + * @param safeL1 The stored L1 safe block or an empty block reference if the L1 safe block has not + * been initialized yet. + * @param finalizedL1 The stored L1 finalized block or an empty block reference if the L1 finalized + * block has not been initialized yet. + * @param unsafeL2 The absolute tip of the L2 chain, + * @param safeL2 Points to the L2 block that was derived from the L1 chain. + * @param finalizedL2 Points to the L2 block that was derived fully from finalized L1 information, + * thus irreversible. + * @param unsafeL2SyncTarget Points to the first unprocessed unsafe L2 block. It may be zeroed if + * there is no targeted block. + * @param engineSyncTarget Points to the L2 block that the execution engine is syncing to. If it is + * ahead from UnsafeL2, the engine is in progress of P2P sync. + * @author thinkAfCod + * @since 0.1.1 + */ +public record SyncStatusResult( + BlockInfo currentL1, + BlockInfo currentL1Finalized, + BlockInfo headL1, + BlockInfo safeL1, + BlockInfo finalizedL1, + BlockInfo unsafeL2, + BlockInfo safeL2, + BlockInfo finalizedL2, + L2BlockRef unsafeL2SyncTarget, + BlockInfo engineSyncTarget) {} diff --git a/hildr-node/src/main/java/io/optimism/rpc/methods/JsonRpcMethodAdapter.java b/hildr-node/src/main/java/io/optimism/rpc/methods/JsonRpcMethodAdapter.java new file mode 100644 index 00000000..b2669b52 --- /dev/null +++ b/hildr-node/src/main/java/io/optimism/rpc/methods/JsonRpcMethodAdapter.java @@ -0,0 +1,66 @@ +/* + * 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.rpc.methods; + +import io.optimism.common.HildrServiceExecutionException; +import io.optimism.rpc.internal.JsonRpcRequestContext; +import io.optimism.rpc.internal.response.JsonRpcResponse; +import io.optimism.rpc.internal.response.JsonRpcSuccessResponse; +import java.util.function.Function; + +/** + * The JsonRpcMethodAdapter type. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class JsonRpcMethodAdapter implements JsonRpcMethod { + + private final String name; + + private final Function fn; + + /** + * JsonRpcMethodAdapter type constructor. + * + * @param name json rpc method name + * @param responseSupplier response result supplier + */ + public JsonRpcMethodAdapter(String name, Function responseSupplier) { + this.name = name; + this.fn = responseSupplier; + } + + @Override + public String getName() { + return name; + } + + @Override + public JsonRpcResponse response(JsonRpcRequestContext ctx) { + try { + var result = fn.apply(ctx.getRequest().getParams()); + if (!(result instanceof JsonRpcResponse)) { + return new JsonRpcSuccessResponse(ctx.getRequest().getId(), result); + } + return (JsonRpcResponse) result; + } catch (Exception e) { + Thread.currentThread().interrupt(); + throw new HildrServiceExecutionException(e); + } + } +} 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 46c7ca63..c756f1eb 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 @@ -109,7 +109,7 @@ void testReadyChannelStillPending() { private Tuple2, MessagePassingQueue> createStage() { - Config config = new Config("", "", "", "", null, 9545, ChainConfig.optimismGoerli()); + Config config = new Config("", "", "", "", null, 9545, false, ChainConfig.optimismGoerli()); MessagePassingQueue transactionMessageMessagePassingQueue = new MpscGrowableArrayQueue<>(4096); Channels channels = diff --git a/hildr-node/src/test/java/io/optimism/rpc/RpcServerTest.java b/hildr-node/src/test/java/io/optimism/rpc/RpcServerTest.java index ee3cecd3..6cd6ddb8 100644 --- a/hildr-node/src/test/java/io/optimism/rpc/RpcServerTest.java +++ b/hildr-node/src/test/java/io/optimism/rpc/RpcServerTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.optimism.TestConstants; import io.optimism.config.Config; @@ -29,9 +30,13 @@ import io.optimism.rpc.internal.result.OutputRootResult; import io.optimism.utilities.telemetry.Logging; import io.optimism.utilities.telemetry.TracerTaskWrapper; +import java.io.IOException; import java.time.Duration; +import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.function.Function; import jdk.incubator.concurrent.StructuredTaskScope; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -55,10 +60,13 @@ public class RpcServerTest { private static Config config; + private static ObjectMapper mapper; + @BeforeAll static void setUp() { TracerTaskWrapper.setTracerSupplier(Logging.INSTANCE::getTracer); config = TestConstants.createConfig(); + mapper = new ObjectMapper(); } RpcServer createRpcServer(Config config) { @@ -115,4 +123,60 @@ void testRpcServerStart() throws Exception { rpcServer.stop(); } } + + @Test + void testRpcServerRegister() throws IOException, InterruptedException, ExecutionException { + RpcServer rpcServer = + createRpcServer( + new Config( + null, + "http://fakeurl", + null, + null, + null, + 9545, + false, + Config.ChainConfig.optimism())); + rpcServer.start(); + HashMap rpcHandler = HashMap.newHashMap(1); + rpcHandler.put("test_url", unused -> "response data"); + rpcServer.register(rpcHandler); + + OkHttpClient okHttpClient = + new OkHttpClient.Builder() + .readTimeout(Duration.ofMinutes(5)) + .callTimeout(Duration.ofMinutes(5)) + .build(); + JsonRpcRequest jsonRpcRequest = new JsonRpcRequest("2.0", "test_url", new Object[] {"7900000"}); + jsonRpcRequest.setId(new JsonRpcRequestId("1")); + try { + Response response = sendRequest(okHttpClient, jsonRpcRequest); + assertEquals(200, response.code()); + assertNotNull(response.body()); + Map jsonRpcResp = mapper.readValue(response.body().string(), Map.class); + System.out.println(jsonRpcResp); + } finally { + rpcServer.stop(); + } + } + + private Response sendRequest(OkHttpClient okHttpClient, JsonRpcRequest jsonRpcRequest) + throws JsonProcessingException, InterruptedException, ExecutionException { + var postBody = mapper.writeValueAsBytes(jsonRpcRequest); + RequestBody requestBody = RequestBody.create(postBody, MediaType.get("application/json")); + + final Request request = + new Request.Builder().url("http://127.0.0.1:9545").post(requestBody).build(); + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + Future fork = + scope.fork( + TracerTaskWrapper.wrap( + () -> { + logger.info("test: {}", Thread.currentThread().getName()); + return okHttpClient.newCall(request).execute(); + })); + scope.join(); + return fork.get(); + } + } } diff --git a/hildr-batcher/src/main/java/io/optimism/type/BlockId.java b/hildr-utilities/src/main/java/io/optimism/type/BlockId.java similarity index 100% rename from hildr-batcher/src/main/java/io/optimism/type/BlockId.java rename to hildr-utilities/src/main/java/io/optimism/type/BlockId.java diff --git a/hildr-utilities/src/main/java/io/optimism/type/DepositTransaction.java b/hildr-utilities/src/main/java/io/optimism/type/DepositTransaction.java new file mode 100644 index 00000000..f4b3e55c --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/type/DepositTransaction.java @@ -0,0 +1,139 @@ +/* + * 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.type; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpType; + +/** + * Class of DepositTransaction. + * Only declared in Optimism. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class DepositTransaction { + + /** + * Uniquely identifies the source of the deposit + */ + private String sourceHash; + + /** + * Exposed through the types.Signer, not through TxData + */ + private String from; + + /** + * Means contract creation + */ + private String to; + + /** + * Minted on L2, locked on L1, null if no minting. + */ + private BigInteger mint; + + /** + * Transferred from L2 balance, executed after Mint (if any) + */ + private BigInteger value; + + /** + * Gas limit + */ + private BigInteger gas; + + /** + * Field indicating if this transaction is exempt from the L2 gas limit. + */ + private boolean isSystemTransaction; + + /** + * Normal Tx data + */ + private String data; + + public DepositTransaction() {} + + public DepositTransaction( + String sourceHash, + String from, + String to, + BigInteger mint, + BigInteger value, + BigInteger gas, + boolean isSystemTransaction, + String data) { + this.sourceHash = sourceHash; + this.from = from; + this.to = to; + this.mint = mint; + this.value = value; + this.gas = gas; + this.isSystemTransaction = isSystemTransaction; + this.data = data; + } + + public String getSourceHash() { + return sourceHash; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public BigInteger getMint() { + return mint; + } + + public BigInteger getValue() { + return value; + } + + public BigInteger getGas() { + return gas; + } + + public boolean isSystemTransaction() { + return isSystemTransaction; + } + + public String getData() { + return data; + } + + public List asRlpValues() { + List result = new ArrayList<>(); + result.add(RlpString.create(getSourceHash())); + result.add(RlpString.create(getFrom())); + result.add(RlpString.create(getTo())); + result.add(RlpString.create(getMint())); + result.add(RlpString.create(getValue())); + result.add(RlpString.create(getGas())); + result.add(RlpString.create(isSystemTransaction() ? 1 : 0)); + result.add(RlpString.create(getData())); + return result; + } +} diff --git a/hildr-batcher/src/main/java/io/optimism/type/Genesis.java b/hildr-utilities/src/main/java/io/optimism/type/Genesis.java similarity index 100% rename from hildr-batcher/src/main/java/io/optimism/type/Genesis.java rename to hildr-utilities/src/main/java/io/optimism/type/Genesis.java diff --git a/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java b/hildr-utilities/src/main/java/io/optimism/type/L1BlockInfo.java similarity index 94% rename from hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java rename to hildr-utilities/src/main/java/io/optimism/type/L1BlockInfo.java index 642d5615..a38bede2 100644 --- a/hildr-batcher/src/main/java/io/optimism/type/L1BlockInfo.java +++ b/hildr-utilities/src/main/java/io/optimism/type/L1BlockInfo.java @@ -16,12 +16,11 @@ package io.optimism.type; -import io.optimism.batcher.loader.ParseBlockException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.Objects; import org.apache.commons.lang3.ArrayUtils; -import org.apache.tuweni.crypto.Hash; +import org.web3j.crypto.Hash; import org.web3j.utils.Numeric; /** @@ -55,7 +54,7 @@ public record L1BlockInfo( private static final byte[] SIGNATURE_BYTES = ArrayUtils.subarray( - Hash.keccak256(L1_INFO_FUNC_SIGNATURE.getBytes(StandardCharsets.UTF_8)), 0, 4); + Hash.sha3(L1_INFO_FUNC_SIGNATURE.getBytes(StandardCharsets.UTF_8)), 0, 4); /** * Parse tx data to L1BlockInfo. diff --git a/hildr-batcher/src/main/java/io/optimism/type/L1BlockRef.java b/hildr-utilities/src/main/java/io/optimism/type/L1BlockRef.java similarity index 100% rename from hildr-batcher/src/main/java/io/optimism/type/L1BlockRef.java rename to hildr-utilities/src/main/java/io/optimism/type/L1BlockRef.java diff --git a/hildr-batcher/src/main/java/io/optimism/type/L2BlockRef.java b/hildr-utilities/src/main/java/io/optimism/type/L2BlockRef.java similarity index 97% rename from hildr-batcher/src/main/java/io/optimism/type/L2BlockRef.java rename to hildr-utilities/src/main/java/io/optimism/type/L2BlockRef.java index cfd7f475..9ab73951 100644 --- a/hildr-batcher/src/main/java/io/optimism/type/L2BlockRef.java +++ b/hildr-utilities/src/main/java/io/optimism/type/L2BlockRef.java @@ -40,6 +40,8 @@ public record L2BlockRef( BlockId l1origin, BigInteger sequenceNumber) { + public final static L2BlockRef EMPTY = new L2BlockRef(null, null, null, null, null, null); + /** * L2BlockRef instance converts to BlockId instance. * diff --git a/hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java b/hildr-utilities/src/main/java/io/optimism/type/ParseBlockException.java similarity index 97% rename from hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java rename to hildr-utilities/src/main/java/io/optimism/type/ParseBlockException.java index 05ab0af7..b6e0a584 100644 --- a/hildr-batcher/src/main/java/io/optimism/batcher/loader/ParseBlockException.java +++ b/hildr-utilities/src/main/java/io/optimism/type/ParseBlockException.java @@ -14,7 +14,7 @@ * specific language governing permissions and limitations under the License. */ -package io.optimism.batcher.loader; +package io.optimism.type; /** * ParseBlockException class. diff --git a/hildr-utilities/src/main/java/io/optimism/type/RollupConfigResult.java b/hildr-utilities/src/main/java/io/optimism/type/RollupConfigResult.java new file mode 100644 index 00000000..bfd1fd4f --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/type/RollupConfigResult.java @@ -0,0 +1,212 @@ +/* + * 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.type; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigInteger; +import java.util.Objects; + +/** + * Rollup config. + * + * @author thinkAfCod + * @since 0.1.1 + */ +public class RollupConfigResult { + /** + * Genesis anchor point of the rollup. + */ + @JsonProperty("genesis") + private Genesis genesis; + + /** + * Seconds per L2 block + */ + @JsonProperty("blockTime") + private BigInteger blockTime; + + /** + * Sequencer batches may not be more than MaxSequencerDrift seconds after + * the L1 timestamp of the sequencing window end. + */ + @JsonProperty("max_sequencer_drift") + private BigInteger maxSequencerDrift; + + /** + * Number of epochs (L1 blocks) per sequencing window, including the epoch L1 + * origin block itself + */ + @JsonProperty("seq_window_size") + private BigInteger seqWindowSize; + + /** + * Number of L1 blocks between when a channel can be opened and when it must + * be closed by. + */ + @JsonProperty("channel_timeout") + private BigInteger channelTimeout; + + /** + * Required to verify L1 signatures. + */ + @JsonProperty("l1_chain_id") + private BigInteger l1ChainId; + + /** + * Required to identify the L2 network and create p2p signatures unique for this chain. + */ + @JsonProperty("l2_chain_id") + private BigInteger l2ChainId; + + /** + * RegolithTime sets the activation time of the Regolith network-upgrade: a + * pre-mainnet Bedrock change that addresses findings of the Sherlock contest related to + * deposit attributes. "Regolith" is the loose deposited rock that sits on top of Bedrock. + * + */ + @JsonProperty("regolith_time") + private BigInteger regolithTime; + + /** + * L1 address that batches are sent to. + */ + @JsonProperty("batch_inbox_address") + private String batchInboxAddress; + + /** + * L1 deposit contract address. + */ + @JsonProperty("deposit_contract_address") + private String depositContractAddress; + + /** + * L1 system config address. + */ + @JsonProperty("l1_system_config_address") + private String l1SystemConfigAddress; + + public Genesis getGenesis() { + return genesis; + } + + public void setGenesis(Genesis genesis) { + this.genesis = genesis; + } + + public BigInteger getBlockTime() { + return blockTime; + } + + public void setBlockTime(BigInteger blockTime) { + this.blockTime = blockTime; + } + + public BigInteger getMaxSequencerDrift() { + return maxSequencerDrift; + } + + public void setMaxSequencerDrift(BigInteger maxSequencerDrift) { + this.maxSequencerDrift = maxSequencerDrift; + } + + public BigInteger getSeqWindowSize() { + return seqWindowSize; + } + + public void setSeqWindowSize(BigInteger seqWindowSize) { + this.seqWindowSize = seqWindowSize; + } + + public BigInteger getChannelTimeout() { + return channelTimeout; + } + + public void setChannelTimeout(BigInteger channelTimeout) { + this.channelTimeout = channelTimeout; + } + + public BigInteger getL1ChainId() { + return l1ChainId; + } + + public void setL1ChainId(BigInteger l1ChainId) { + this.l1ChainId = l1ChainId; + } + + public BigInteger getL2ChainId() { + return l2ChainId; + } + + public void setL2ChainId(BigInteger l2ChainId) { + this.l2ChainId = l2ChainId; + } + + public BigInteger getRegolithTime() { + return regolithTime; + } + + public void setRegolithTime(BigInteger regolithTime) { + this.regolithTime = regolithTime; + } + + public String getBatchInboxAddress() { + return batchInboxAddress; + } + + public void setBatchInboxAddress(String batchInboxAddress) { + this.batchInboxAddress = batchInboxAddress; + } + + public String getDepositContractAddress() { + return depositContractAddress; + } + + public void setDepositContractAddress(String depositContractAddress) { + this.depositContractAddress = depositContractAddress; + } + + public String getL1SystemConfigAddress() { + return l1SystemConfigAddress; + } + + public void setL1SystemConfigAddress(String l1SystemConfigAddress) { + this.l1SystemConfigAddress = l1SystemConfigAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RollupConfigResult that)) return false; + return Objects.equals(genesis, that.genesis) + && Objects.equals(blockTime, that.blockTime) + && Objects.equals(maxSequencerDrift, that.maxSequencerDrift) + && Objects.equals(seqWindowSize, that.seqWindowSize) + && Objects.equals(channelTimeout, that.channelTimeout) + && Objects.equals(l1ChainId, that.l1ChainId) + && Objects.equals(l2ChainId, that.l2ChainId) + && Objects.equals(regolithTime, that.regolithTime) + && Objects.equals(batchInboxAddress, that.batchInboxAddress) + && Objects.equals(depositContractAddress, that.depositContractAddress) + && Objects.equals(l1SystemConfigAddress, that.l1SystemConfigAddress); + } + + @Override + public int hashCode() { + return Objects.hash(genesis, blockTime, maxSequencerDrift, seqWindowSize, channelTimeout, + l1ChainId, l2ChainId, regolithTime, batchInboxAddress, l1SystemConfigAddress); + } +} \ No newline at end of file diff --git a/hildr-batcher/src/main/java/io/optimism/type/SystemConfig.java b/hildr-utilities/src/main/java/io/optimism/type/SystemConfig.java similarity index 90% rename from hildr-batcher/src/main/java/io/optimism/type/SystemConfig.java rename to hildr-utilities/src/main/java/io/optimism/type/SystemConfig.java index 4e9da7f4..c5815655 100644 --- a/hildr-batcher/src/main/java/io/optimism/type/SystemConfig.java +++ b/hildr-utilities/src/main/java/io/optimism/type/SystemConfig.java @@ -29,4 +29,4 @@ * @since 0.1.1 */ public record SystemConfig( - String batcherAddr, String overhead, String scalar, BigInteger gasLimit) {} + String batcherAddr, BigInteger overhead, BigInteger scalar, BigInteger gasLimit) {} diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/TxDecoder.java b/hildr-utilities/src/main/java/io/optimism/utilities/TxDecoder.java new file mode 100644 index 00000000..26ab244a --- /dev/null +++ b/hildr-utilities/src/main/java/io/optimism/utilities/TxDecoder.java @@ -0,0 +1,51 @@ +/* + * 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; + +import io.optimism.type.DepositTransaction; +import java.math.BigInteger; +import java.util.Arrays; +import org.web3j.rlp.RlpDecoder; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpString; +import org.web3j.utils.Numeric; + +/** + * @author thinkAfCod + * @since 0.1.1 + */ +public class TxDecoder { + + public static DepositTransaction decodeToDeposit(final String hexTransaction) { + final byte[] transaction = Numeric.hexStringToByteArray(hexTransaction); + if (transaction.length > 0 && transaction[0] != ((byte) 0x01)) { + throw new RuntimeException("tx is not type of deposit tx"); + } + final byte[] encodedTx = Arrays.copyOfRange(transaction, 1, transaction.length); + final RlpList rlpList = RlpDecoder.decode(encodedTx); + var values = ((RlpList) rlpList.getValues().get(0)).getValues(); + final String sourceHash = ((RlpString) values.get(0)).asString(); + final String from = ((RlpString) values.get(0)).asString(); + final String to = ((RlpString) values.get(0)).asString(); + final BigInteger mint = ((RlpString) values.get(0)).asPositiveBigInteger(); + final BigInteger value = ((RlpString) values.get(0)).asPositiveBigInteger(); + final BigInteger gas = ((RlpString) values.get(0)).asPositiveBigInteger(); + final boolean isSystemTx = ((RlpString) values.get(0)).asPositiveBigInteger().compareTo(BigInteger.ONE) == 0; + final String data = ((RlpString) values.get(0)).asString(); + return new DepositTransaction(sourceHash, from, to, mint, value, gas, isSystemTx, data); + } +} diff --git a/hildr-utilities/src/main/java/io/optimism/utilities/rpc/Web3jProvider.java b/hildr-utilities/src/main/java/io/optimism/utilities/rpc/Web3jProvider.java index 3ebd4311..6db245dd 100644 --- a/hildr-utilities/src/main/java/io/optimism/utilities/rpc/Web3jProvider.java +++ b/hildr-utilities/src/main/java/io/optimism/utilities/rpc/Web3jProvider.java @@ -16,10 +16,13 @@ package io.optimism.utilities.rpc; +import java.net.ConnectException; import okhttp3.OkHttpClient; +import org.apache.commons.lang3.StringUtils; import org.web3j.protocol.Web3j; import org.web3j.protocol.Web3jService; import org.web3j.protocol.http.HttpService; +import org.web3j.protocol.websocket.WebSocketService; import org.web3j.tuples.generated.Tuple2; /** @@ -39,9 +42,7 @@ private Web3jProvider() {} * @return web3j client */ public static Web3j createClient(String url) { - OkHttpClient okHttpClient = - new OkHttpClient.Builder().addInterceptor(new RetryRateLimitInterceptor()).build(); - return Web3j.build(new HttpService(url, okHttpClient)); + return create(url).component1(); } /** @@ -52,9 +53,31 @@ public static Web3j createClient(String url) { * @return web3j client and web3j service */ public static Tuple2 create(String url) { - OkHttpClient okHttpClient = - new OkHttpClient.Builder().addInterceptor(new RetryRateLimitInterceptor()).build(); - Web3jService web3jService = new HttpService(url, okHttpClient); - return new Tuple2<>(Web3j.build(web3jService), web3jService); + Web3jService web3Srv = null; + if (Web3jProvider.isHttp(url)) { + OkHttpClient okHttpClient = + new OkHttpClient.Builder().addInterceptor(new RetryRateLimitInterceptor()).build(); + web3Srv = new HttpService(url, okHttpClient); + } else if (Web3jProvider.isWs(url)) { + web3Srv = new WebSocketService(url, true); + try { + ((WebSocketService) web3Srv).connect(); + } catch (ConnectException e) { + throw new IllegalStateException(e); + } + } else { + throw new IllegalArgumentException("not supported scheme:" + url); + } + return new Tuple2<>(Web3j.build(web3Srv), web3Srv); + } + + + + private static boolean isHttp(final String url) { + return !StringUtils.isEmpty(url) && url.startsWith("http"); + } + + private static boolean isWs(final String url) { + return !StringUtils.isEmpty(url) && url.startsWith("ws"); } }