diff --git a/src/main/java/io/optimism/driver/Driver.java b/src/main/java/io/optimism/driver/Driver.java index 7f632150..767c8e65 100644 --- a/src/main/java/io/optimism/driver/Driver.java +++ b/src/main/java/io/optimism/driver/Driver.java @@ -16,7 +16,6 @@ import io.optimism.network.OpStackNetwork; import io.optimism.rpc.RpcMethod; import io.optimism.rpc.RpcServer; -import io.optimism.rpc.Web3jProvider; import io.optimism.rpc.internal.result.SyncStatusResult; import io.optimism.telemetry.InnerMetrics; import io.optimism.telemetry.TracerTaskWrapper; @@ -33,6 +32,7 @@ import io.optimism.types.RollupConfigResult; import io.optimism.types.SystemConfig; import io.optimism.utilities.encoding.TxDecoder; +import io.optimism.utilities.web3j.Web3jProvider; import java.math.BigInteger; import java.time.Duration; import java.util.HashMap; diff --git a/src/main/java/io/optimism/engine/EngineApi.java b/src/main/java/io/optimism/engine/EngineApi.java index 659b6ec4..4090b690 100644 --- a/src/main/java/io/optimism/engine/EngineApi.java +++ b/src/main/java/io/optimism/engine/EngineApi.java @@ -4,10 +4,10 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import io.optimism.config.Config; -import io.optimism.rpc.Web3jProvider; import io.optimism.types.ExecutionPayload; import io.optimism.types.ExecutionPayload.PayloadAttributes; import io.optimism.types.ForkChoiceUpdate.ForkchoiceState; +import io.optimism.utilities.web3j.Web3jProvider; import java.io.IOException; import java.math.BigInteger; import java.security.Key; diff --git a/src/main/java/io/optimism/l1/InnerWatcher.java b/src/main/java/io/optimism/l1/InnerWatcher.java index 84fc35f9..f7ce52b9 100644 --- a/src/main/java/io/optimism/l1/InnerWatcher.java +++ b/src/main/java/io/optimism/l1/InnerWatcher.java @@ -11,7 +11,6 @@ import io.optimism.exceptions.BlockNotIncludedException; import io.optimism.exceptions.DepositsNotFoundException; import io.optimism.exceptions.HildrServiceExecutionException; -import io.optimism.rpc.Web3jProvider; import io.optimism.telemetry.TracerTaskWrapper; import io.optimism.types.BeaconSignedBlockHeader; import io.optimism.types.BlobSidecar; @@ -22,6 +21,7 @@ import io.optimism.types.SystemConfigUpdate; import io.optimism.types.enums.Logging; import io.optimism.utilities.blob.BlobCodec; +import io.optimism.utilities.web3j.Web3jProvider; import io.reactivex.disposables.Disposable; import java.math.BigInteger; import java.time.Duration; diff --git a/src/main/java/io/optimism/rpc/methods/OutputAtBlock.java b/src/main/java/io/optimism/rpc/methods/OutputAtBlock.java index 5a8dc4c6..d9b9436f 100644 --- a/src/main/java/io/optimism/rpc/methods/OutputAtBlock.java +++ b/src/main/java/io/optimism/rpc/methods/OutputAtBlock.java @@ -2,13 +2,13 @@ import io.optimism.exceptions.HildrServiceExecutionException; import io.optimism.rpc.RpcMethod; -import io.optimism.rpc.Web3jProvider; import io.optimism.rpc.internal.JsonRpcRequestContext; import io.optimism.rpc.internal.response.JsonRpcResponse; import io.optimism.rpc.internal.response.JsonRpcSuccessResponse; import io.optimism.rpc.internal.result.EthGetProof; import io.optimism.rpc.internal.result.OutputRootResult; import io.optimism.telemetry.TracerTaskWrapper; +import io.optimism.utilities.web3j.Web3jProvider; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; diff --git a/src/main/java/io/optimism/runner/Runner.java b/src/main/java/io/optimism/runner/Runner.java index f5404a7f..d5c52469 100644 --- a/src/main/java/io/optimism/runner/Runner.java +++ b/src/main/java/io/optimism/runner/Runner.java @@ -15,12 +15,12 @@ import io.optimism.exceptions.SyncUrlMissingException; import io.optimism.exceptions.TransactionNotFoundException; import io.optimism.exceptions.TrustedPeerAddedException; -import io.optimism.rpc.Web3jProvider; import io.optimism.rpc.response.OpEthBlock; import io.optimism.telemetry.TracerTaskWrapper; import io.optimism.types.ExecutionPayload; import io.optimism.types.ExecutionPayload.Status; import io.optimism.types.ForkChoiceUpdate.ForkchoiceState; +import io.optimism.utilities.web3j.Web3jProvider; import java.math.BigInteger; import java.time.Duration; import java.util.Arrays; diff --git a/src/main/java/io/optimism/utilities/LruCacheProvider.java b/src/main/java/io/optimism/utilities/LruCacheProvider.java index 5182f9d9..b582468f 100644 --- a/src/main/java/io/optimism/utilities/LruCacheProvider.java +++ b/src/main/java/io/optimism/utilities/LruCacheProvider.java @@ -5,7 +5,13 @@ public class LruCacheProvider { - static Cache create() { - return CacheBuilder.newBuilder().maximumSize(1000L).build(); + public static final long DEFAULT_CACHE_SIZE = 300L; + + public static Cache create(long size) { + return CacheBuilder.newBuilder().maximumSize(size).build(); + } + + public static Cache create() { + return CacheBuilder.newBuilder().maximumSize(DEFAULT_CACHE_SIZE).build(); } } diff --git a/src/main/java/io/optimism/rpc/Web3jProvider.java b/src/main/java/io/optimism/utilities/web3j/Web3jProvider.java similarity index 96% rename from src/main/java/io/optimism/rpc/Web3jProvider.java rename to src/main/java/io/optimism/utilities/web3j/Web3jProvider.java index 75d0721f..f63419ef 100644 --- a/src/main/java/io/optimism/rpc/Web3jProvider.java +++ b/src/main/java/io/optimism/utilities/web3j/Web3jProvider.java @@ -1,6 +1,8 @@ -package io.optimism.rpc; +package io.optimism.utilities.web3j; import ch.qos.logback.classic.Level; +import io.optimism.rpc.HttpClientProvider; +import io.optimism.rpc.RetryRateLimitInterceptor; import java.net.ConnectException; import java.util.function.Consumer; import okhttp3.OkHttpClient; diff --git a/src/main/java/io/optimism/rpc/Web3jUtil.java b/src/main/java/io/optimism/utilities/web3j/Web3jUtil.java similarity index 70% rename from src/main/java/io/optimism/rpc/Web3jUtil.java rename to src/main/java/io/optimism/utilities/web3j/Web3jUtil.java index cc6548ed..35b3c3fb 100644 --- a/src/main/java/io/optimism/rpc/Web3jUtil.java +++ b/src/main/java/io/optimism/utilities/web3j/Web3jUtil.java @@ -1,8 +1,10 @@ -package io.optimism.rpc; +package io.optimism.utilities.web3j; import io.optimism.exceptions.Web3jCallException; +import io.optimism.rpc.response.OpEthBlock; import io.optimism.telemetry.TracerTaskWrapper; import java.math.BigInteger; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.StructuredTaskScope; @@ -15,10 +17,13 @@ import org.web3j.crypto.RawTransaction; import org.web3j.crypto.TransactionEncoder; import org.web3j.protocol.Web3j; +import org.web3j.protocol.Web3jService; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.Request; import org.web3j.protocol.core.methods.request.Transaction; import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.EthGetBlockReceipts; import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt; import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.utils.Numeric; @@ -74,6 +79,26 @@ public static EthGetTransactionReceipt getTxReceipt(final Web3j client, final St } } + /** + * Get the transaction receipts of the given block num. + * + * @param client the web3j client + * @param blockNum the block number + * @return the transaction receipts + */ + public static EthGetBlockReceipts getBlockReceipts(final Web3j client, final DefaultBlockParameter blockNum) { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + var receiptsFuture = + scope.fork(() -> client.ethGetBlockReceipts(blockNum).send()); + scope.join(); + scope.throwIfFailed(); + return receiptsFuture.get(); + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + throw new Web3jCallException("failed to get TxReceipt", e); + } + } + /** * Poll the block by the given parameter. * @param client the web3j client @@ -81,20 +106,55 @@ public static EthGetTransactionReceipt getTxReceipt(final Web3j client, final St * @param returnFullTransactionObjects whether to return full transaction objects * @return the block */ - public static EthBlock pollBlock( + public static EthBlock pollBlockByNum( final Web3j client, final DefaultBlockParameter parameter, final boolean returnFullTransactionObjects) { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { - var receiptFuture = scope.fork(() -> client.ethGetBlockByNumber(parameter, returnFullTransactionObjects) + var blockFuture = scope.fork(() -> client.ethGetBlockByNumber(parameter, returnFullTransactionObjects) .send()); scope.join(); scope.throwIfFailed(); - return receiptFuture.get(); + return blockFuture.get(); + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + throw new Web3jCallException("failed to get block by number", e); + } + } + + public static EthBlock pollBlockByHash( + final Web3j client, final String blockHash, final boolean returnFullTransactionObjects) { + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + var blockFuture = scope.fork(() -> client.ethGetBlockByHash(blockHash, returnFullTransactionObjects) + .send()); + scope.join(); + scope.throwIfFailed(); + return blockFuture.get(); } catch (InterruptedException | ExecutionException e) { Thread.currentThread().interrupt(); throw new Web3jCallException("failed to get block by number", e); } } + public static OpEthBlock pollOpBlockByNum( + final Web3jService client, + final DefaultBlockParameter parameter, + final boolean returnFullTransactionObjects) { + OpEthBlock block; + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + StructuredTaskScope.Subtask blockFuture = scope.fork(TracerTaskWrapper.wrap(() -> new Request<>( + "eth_getBlockByNumber", + Arrays.asList(parameter.getValue(), returnFullTransactionObjects), + client, + OpEthBlock.class) + .send())); + scope.join(); + scope.throwIfFailed(); + return blockFuture.get(); + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + throw new Web3jCallException("failed to get optimism block by number", e); + } + } + /** * Execute the contract. * @param client the web3j client diff --git a/src/main/java/io/optimism/v2/derive/datasource/BeaconBlobFetcher.java b/src/main/java/io/optimism/v2/derive/datasource/BeaconBlobFetcher.java new file mode 100644 index 00000000..68218089 --- /dev/null +++ b/src/main/java/io/optimism/v2/derive/datasource/BeaconBlobFetcher.java @@ -0,0 +1,222 @@ +package io.optimism.v2.derive.datasource; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import ethereum.ckzg4844.CKZG4844JNI; +import io.optimism.rpc.HttpClientProvider; +import io.optimism.rpc.response.BeaconApiResponse; +import io.optimism.types.BlobSidecar; +import io.optimism.types.SpecConfig; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.StructuredTaskScope; +import java.util.stream.Collectors; +import okhttp3.Call; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.web3j.utils.Numeric; + +/** + * The class of BeaconBlobFetcher. + * + * @author thinkAfCod + * @since 0.3.0 + */ +public class BeaconBlobFetcher { + + private static final Logger LOGGER = LoggerFactory.getLogger(BeaconBlobFetcher.class); + + private static final String GENESIS_METHOD_FORMAT = "%s/eth/v1/beacon/genesis"; + + private static final String SPEC_METHOD_FORMAT = "%s/eth/v1/config/spec"; + + private static final String SIDECARS_METHOD_PREFIX_FORMAT = "%s/eth/v1/beacon/blob_sidecars"; + + static { + CKZG4844JNI.loadNativeLibrary(); + CKZG4844JNI.loadTrustedSetupFromResource("/kzg-trusted-setups/mainnet.txt", BeaconBlobFetcher.class); + } + + private final String genesisMethod; + + private final String specMethod; + + private final String sidecarsMethod; + + private final String archiverSidecarsMethod; + + private final OkHttpClient httpClient; + + private final ObjectMapper mapper; + + private BigInteger genesisTimestamp; + + private BigInteger secondsPerSlot; + + /** + * Beacon blob info fetcher constructor. + * @param beaconUrl L1 beacon client url + */ + public BeaconBlobFetcher(String beaconUrl) { + this(beaconUrl, null); + } + + /** + * Beacon blob info fetcher constructor. + * + * @param beaconUrl L1 beacon client url + * @param beaconArchiverUrl L1 beacon archiver client url + */ + public BeaconBlobFetcher(String beaconUrl, String beaconArchiverUrl) { + beaconUrl = StringUtils.stripEnd(beaconUrl, "/"); + beaconArchiverUrl = StringUtils.stripEnd(beaconUrl, "/"); + this.genesisMethod = GENESIS_METHOD_FORMAT.formatted(beaconUrl); + this.specMethod = SPEC_METHOD_FORMAT.formatted(beaconUrl); + this.sidecarsMethod = SIDECARS_METHOD_PREFIX_FORMAT.formatted(beaconUrl); + this.archiverSidecarsMethod = StringUtils.isEmpty(beaconArchiverUrl) + ? null + : SIDECARS_METHOD_PREFIX_FORMAT.formatted(beaconArchiverUrl); + this.httpClient = HttpClientProvider.create(); + this.mapper = new ObjectMapper(); + this.mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + /** + * Get the genesis timestamp + * + * @return the genesis timestamp + */ + public BigInteger getGenesisTimestamp() { + var req = new Request.Builder().get().url(this.genesisMethod).build(); + var res = this.send(req, new TypeReference>>() {}); + return new BigInteger(res.getData().get("genesis_time")); + } + + /** + * Get the spec config + * + * @return the spec config + */ + public SpecConfig getSpecConfig() { + var req = new Request.Builder().get().url(this.specMethod).build(); + var res = this.send(req, new TypeReference>() {}); + return res.getData(); + } + + /** + * Get slot from time. + * @param time the block time + * @return the slot + */ + public BigInteger getSlotFromTime(BigInteger time) { + if (this.genesisTimestamp == null) { + this.genesisTimestamp = this.getGenesisTimestamp(); + this.secondsPerSlot = this.getSpecConfig().getSecondsPerSlot(); + } + return time.subtract(this.genesisTimestamp).divide(secondsPerSlot); + } + + /** + * Get the blob sidecars + * + * @param blockId the block id + * @param indices the blob indices + * @return the list of blob sidecars + */ + public List getBlobSidecards(String blockId, final List indices) { + var params = indices == null || indices.isEmpty() + ? null + : Map.of("indices", indices.stream().map(BigInteger::toString).collect(Collectors.joining(","))); + var postfix = "%s%s".formatted(blockId, prepareQueryParams(params)); + var res = getBlobSidecars("%s/%s".formatted(this.sidecarsMethod, postfix)); + if (res != null && res.getData() != null && !res.getData().isEmpty()) { + return res.getData(); + } + if (this.archiverSidecarsMethod != null) { + LOGGER.warn( + "blob sidecars may be pruned, try blob archiver sidecars method: blockId = {}, indices = {}", + blockId, + indices); + var archiverRes = getBlobSidecars("%s/%s".formatted(this.archiverSidecarsMethod, postfix)); + if (archiverRes.getData() != null && !archiverRes.getData().isEmpty()) { + return archiverRes.getData(); + } + } else { + LOGGER.info( + "blob archiver sidecars method is empty, skip retry: block Id = {}, indices = {}", + blockId, + indices); + } + + return res.getData(); + } + + private BeaconApiResponse> getBlobSidecars(String url) { + var req = new Request.Builder().get().url(HttpUrl.parse(url)).build(); + return this.send(req, new TypeReference>>() {}); + } + + static boolean verifyBlobSidecars(List blobSidecars, List versionedHashes) { + if (blobSidecars == null || versionedHashes == null) { + return false; + } + // check length + if (blobSidecars.size() != versionedHashes.size()) { + return false; + } + + for (int i = 0; i < blobSidecars.size(); i++) { + var blobSidecar = blobSidecars.get(i); + var versionedHash = versionedHashes.get(i); + if (!verifyBlobSidecar(blobSidecar, versionedHash)) { + return false; + } + } + + return true; + } + + static boolean verifyBlobSidecar(BlobSidecar blobSidecar, String versionedHash) { + if (!blobSidecar.getVersionedHash().equals(versionedHash)) { + return false; + } + return CKZG4844JNI.verifyBlobKzgProof( + Numeric.hexStringToByteArray(blobSidecar.getBlob()), + Numeric.hexStringToByteArray(blobSidecar.getKzgCommitment()), + Numeric.hexStringToByteArray(blobSidecar.getKzgProof())); + } + + private T send(final Request req, final TypeReference typeRef) { + Call call = this.httpClient.newCall(req); + try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { + var task = scope.fork(() -> { + Response execute = call.execute(); + return this.mapper.readValue(execute.body().byteStream(), typeRef); + }); + scope.join(); + scope.throwIfFailed(); + return task.get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.warn("request beacon client failed", e); + } + return null; + } + + private String prepareQueryParams(final Map params) { + if (params == null || params.isEmpty()) { + return ""; + } + return "?" + + params.entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining("&")); + } +} diff --git a/src/main/java/io/optimism/v2/derive/datasource/BlobProvider.java b/src/main/java/io/optimism/v2/derive/datasource/BlobProvider.java index 09a9b37d..adf4c012 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/BlobProvider.java +++ b/src/main/java/io/optimism/v2/derive/datasource/BlobProvider.java @@ -4,7 +4,13 @@ import io.optimism.types.BlockInfo; import java.util.List; +/** + * the blob data provider. + * + * @author thinkAfCod + * @since 0.4.5 + */ public interface BlobProvider { - BlobSidecar getBlobs(BlockInfo l1Info, List blobHashes); + BlobSidecar getBlobSidercars(BlockInfo l1Info, List blobHashes); } diff --git a/src/main/java/io/optimism/v2/derive/datasource/ChainProvider.java b/src/main/java/io/optimism/v2/derive/datasource/ChainProvider.java index d9257891..5ab6fa62 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/ChainProvider.java +++ b/src/main/java/io/optimism/v2/derive/datasource/ChainProvider.java @@ -6,6 +6,12 @@ import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.core.methods.response.TransactionReceipt; +/** + * the chain data provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface ChainProvider { EthBlock.Block headerByHash(String hash); diff --git a/src/main/java/io/optimism/v2/derive/datasource/DataAvailabilityProvider.java b/src/main/java/io/optimism/v2/derive/datasource/DataAvailabilityProvider.java index f6b59927..98c406da 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/DataAvailabilityProvider.java +++ b/src/main/java/io/optimism/v2/derive/datasource/DataAvailabilityProvider.java @@ -3,6 +3,12 @@ import io.optimism.types.BlockInfo; import io.optimism.v2.derive.stages.DataIter; +/** + * the data availability provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface DataAvailabilityProvider { DataIter openData(BlockInfo l1Ref, String batcherAddr); diff --git a/src/main/java/io/optimism/v2/derive/datasource/L2ChainProvider.java b/src/main/java/io/optimism/v2/derive/datasource/L2ChainProvider.java index f972b722..454b6f15 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/L2ChainProvider.java +++ b/src/main/java/io/optimism/v2/derive/datasource/L2ChainProvider.java @@ -2,10 +2,16 @@ import io.optimism.config.Config; import io.optimism.rpc.response.OpEthBlock; -import io.optimism.types.L2BlockRef; -import io.optimism.types.SystemConfig; +import io.optimism.v2.derive.types.L2BlockRef; +import io.optimism.v2.derive.types.SystemConfig; import java.math.BigInteger; +/** + * the l2 chain data provider. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface L2ChainProvider { L2BlockRef l2BlockInfoByNumber(BigInteger num); diff --git a/src/main/java/io/optimism/v2/derive/datasource/impl/L1BlobSource.java b/src/main/java/io/optimism/v2/derive/datasource/impl/L1BlobSource.java new file mode 100644 index 00000000..fbdaf31c --- /dev/null +++ b/src/main/java/io/optimism/v2/derive/datasource/impl/L1BlobSource.java @@ -0,0 +1,19 @@ +package io.optimism.v2.derive.datasource.impl; + +import io.optimism.types.BlobSidecar; +import io.optimism.types.BlockInfo; +import io.optimism.v2.derive.datasource.BlobProvider; +import java.util.List; + +/** + * the L1 blob data source + * + * @author thinkAfCod + * @since 0.4.6 + */ +public class L1BlobSource implements BlobProvider { + @Override + public BlobSidecar getBlobSidercars(BlockInfo l1Info, List blobHashes) { + return null; + } +} diff --git a/src/main/java/io/optimism/v2/derive/datasource/impl/L1ChainFetcher.java b/src/main/java/io/optimism/v2/derive/datasource/impl/L1ChainFetcher.java new file mode 100644 index 00000000..1dc1ef61 --- /dev/null +++ b/src/main/java/io/optimism/v2/derive/datasource/impl/L1ChainFetcher.java @@ -0,0 +1,144 @@ +package io.optimism.v2.derive.datasource.impl; + +import com.google.common.cache.Cache; +import io.optimism.exceptions.BlockNotIncludedException; +import io.optimism.utilities.LruCacheProvider; +import io.optimism.utilities.web3j.Web3jUtil; +import io.optimism.v2.derive.datasource.ChainProvider; +import io.optimism.v2.derive.types.BlockInfo; +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +/** + * the l1 chain data fetcher. + * + * @author thinkAfCod + * @since 0.4.6 + */ +public class L1ChainFetcher implements ChainProvider { + + private final Web3j l1Client; + + private Cache blockByHashCache; + + private Cache blockInfoByNumCache; + + private Cache> receiptsByHashCache; + + private final boolean devnet; + + public L1ChainFetcher(Web3j l1Client, boolean devnet) { + this.l1Client = l1Client; + this.blockByHashCache = LruCacheProvider.create(); + this.blockInfoByNumCache = LruCacheProvider.create(); + this.receiptsByHashCache = LruCacheProvider.create(); + this.devnet = devnet; + } + + @Override + public EthBlock.Block headerByHash(String hash) { + EthBlock block = this.blockByHashCache.getIfPresent(hash); + if (block != null) { + return block.getBlock(); + } + + EthBlock ethBlock = Web3jUtil.pollBlockByHash(this.l1Client, hash, true); + if (ethBlock == null || ethBlock.getBlock() == null) { + throw new BlockNotIncludedException("Block not found by hash: " + hash); + } + cacheBlock(ethBlock); + cacheBlockInfo(fromBlock(ethBlock)); + return null; + } + + @Override + public BlockInfo blockInfoByNumber(BigInteger num) { + final BlockInfo cachedBlockInfo = this.blockInfoByNumCache.getIfPresent(num); + if (cachedBlockInfo != null) { + return cachedBlockInfo; + } + EthBlock ethBlock = Web3jUtil.pollBlockByNum(this.l1Client, DefaultBlockParameter.valueOf(num), true); + cacheBlock(ethBlock); + BlockInfo info = fromBlock(ethBlock); + if (info == null) { + throw new BlockNotIncludedException("Block Info not found by number: " + num); + } + cacheBlockInfo(info); + return null; + } + + @Override + public List receiptsByHash(String hash) { + List cachedReceipts = this.receiptsByHashCache.getIfPresent(hash); + if (cachedReceipts != null) { + return cachedReceipts; + } + EthBlock.Block block = this.headerByHash(hash); + var receipts = Web3jUtil.getBlockReceipts(this.l1Client, DefaultBlockParameter.valueOf(block.getNumber())); + if (receipts == null) { + throw new BlockNotIncludedException("Block Receipts not found by hash:" + hash); + } + List inner; + if (receipts.getBlockReceipts().isPresent()) { + inner = receipts.getBlockReceipts().get(); + } else { + inner = List.of(); + } + this.receiptsByHashCache.put(hash, inner); + return inner; + } + + @Override + public EthBlock.Block blockInfoNTxsByHash(String hash) { + return this.headerByHash(hash); + } + + public EthBlock.Block getSafe() throws ExecutionException, InterruptedException { + var parameter = this.devnet ? DefaultBlockParameterName.LATEST : DefaultBlockParameterName.SAFE; + return getHeaderByTag(this.l1Client, parameter); + } + + public EthBlock.Block getFinalized() throws ExecutionException, InterruptedException { + var parameter = this.devnet ? DefaultBlockParameterName.LATEST : DefaultBlockParameterName.FINALIZED; + return getHeaderByTag(this.l1Client, parameter); + } + + public EthBlock.Block getHead() throws ExecutionException, InterruptedException { + return getHeaderByTag(this.l1Client, DefaultBlockParameterName.LATEST); + } + + private void cacheBlock(EthBlock block) { + if (block != null && block.getBlock() != null) { + this.blockByHashCache.put(block.getBlock().getHash(), block); + } + } + + private void cacheBlockInfo(BlockInfo blockInfo) { + if (blockInfo != null) { + this.blockInfoByNumCache.put(blockInfo.number(), blockInfo); + } + } + + private BlockInfo fromBlock(EthBlock block) { + if (block == null || block.getBlock() == null) { + return null; + } + var inner = block.getBlock(); + return new BlockInfo(inner.getHash(), inner.getNumber(), inner.getParentHash(), inner.getTimestamp()); + } + + private EthBlock.Block getHeaderByTag(Web3j client, DefaultBlockParameterName tag) + throws ExecutionException, InterruptedException { + var blockWrapper = Web3jUtil.pollBlockByNum(client, tag, false); + if (blockWrapper == null || blockWrapper.getBlock() == null) { + throw new BlockNotIncludedException(); + } + return blockWrapper.getBlock(); + } +} diff --git a/src/main/java/io/optimism/v2/derive/datasource/impl/L1Retrieval.java b/src/main/java/io/optimism/v2/derive/datasource/impl/L1Retrieval.java index 09968e29..58e20f23 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/impl/L1Retrieval.java +++ b/src/main/java/io/optimism/v2/derive/datasource/impl/L1Retrieval.java @@ -7,6 +7,12 @@ import io.optimism.v2.derive.types.BlockInfo; import io.optimism.v2.derive.types.SystemConfig; +/** + * the l1 chain data retrieval. + * + * @author thinkAfCod + * @since 0.4.6 + */ public class L1Retrieval implements FrameQueueProvider, OriginProvider, OriginAdvancer, ResettableStage { @Override diff --git a/src/main/java/io/optimism/v2/derive/datasource/impl/L1Traversal.java b/src/main/java/io/optimism/v2/derive/datasource/impl/L1Traversal.java index d480e5b2..1a196732 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/impl/L1Traversal.java +++ b/src/main/java/io/optimism/v2/derive/datasource/impl/L1Traversal.java @@ -15,6 +15,12 @@ import java.util.List; import org.web3j.protocol.core.methods.response.TransactionReceipt; +/** + * the L1 chain data traversal. + * + * @author thinkAfCod + * @since 0.4.6 + */ public class L1Traversal implements L1RetrievalProvider, OriginProvider, OriginAdvancer, ResettableStage { private final Config.ChainConfig rollupConfig; @@ -58,7 +64,7 @@ public void advanceOrigin() { } List txReceipts = this.dataSource.receiptsByHash(nextL1Origin.hash()); - updateWithReceipts( + curSysConfig = curSysConfig.updateByReceipts( txReceipts, this.rollupConfig.systemConfigContract(), this.rollupConfig.isEcotone(nextL1Origin.timestamp())); @@ -69,8 +75,6 @@ public void advanceOrigin() { this.rollupConfig.isHoloceneActivationBlock(nextL1Origin.timestamp()); } - private void updateWithReceipts(List txReceipts, String sysConfigAddr, boolean ecotone) {} - @Override public BlockInfo origin() { return this.block; diff --git a/src/main/java/io/optimism/v2/derive/datasource/impl/L2ChainFetcher.java b/src/main/java/io/optimism/v2/derive/datasource/impl/L2ChainFetcher.java index 0614ddd0..c6113ebd 100644 --- a/src/main/java/io/optimism/v2/derive/datasource/impl/L2ChainFetcher.java +++ b/src/main/java/io/optimism/v2/derive/datasource/impl/L2ChainFetcher.java @@ -1,25 +1,120 @@ package io.optimism.v2.derive.datasource.impl; +import com.google.common.cache.Cache; import io.optimism.config.Config; +import io.optimism.exceptions.BlockNotIncludedException; import io.optimism.rpc.response.OpEthBlock; -import io.optimism.types.L2BlockRef; -import io.optimism.types.SystemConfig; +import io.optimism.types.ParseBlockException; +import io.optimism.types.enums.TxType; +import io.optimism.utilities.LruCacheProvider; +import io.optimism.utilities.web3j.Web3jUtil; import io.optimism.v2.derive.datasource.L2ChainProvider; +import io.optimism.v2.derive.types.Epoch; +import io.optimism.v2.derive.types.L1BlockInfoTx; +import io.optimism.v2.derive.types.L2BlockRef; +import io.optimism.v2.derive.types.SystemConfig; import java.math.BigInteger; +import org.apache.commons.collections4.CollectionUtils; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.Web3jService; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.tuples.generated.Tuple2; +/** + * the L2 chain data fetcher + * + * @author thinkAfCod + * @since 0.4.6 + */ public class L2ChainFetcher implements L2ChainProvider { + + private final Web3j l2Client; + + private final Web3jService l2Service; + + private final Config.ChainConfig rollupConfig; + + private final Cache blockByNumCache; + + private final Cache l2InfoByNumCache; + + private final Cache sysConfigByNumCache; + + public L2ChainFetcher(Tuple2 tuple, Config.ChainConfig rollupConfig) { + this.l2Client = tuple.component1(); + this.l2Service = tuple.component2(); + this.rollupConfig = rollupConfig; + this.blockByNumCache = LruCacheProvider.create(); + this.l2InfoByNumCache = LruCacheProvider.create(); + this.sysConfigByNumCache = LruCacheProvider.create(); + } + @Override public L2BlockRef l2BlockInfoByNumber(BigInteger num) { - return null; + L2BlockRef cachedL2Ref = this.l2InfoByNumCache.getIfPresent(num); + if (cachedL2Ref != null) { + return cachedL2Ref; + } + OpEthBlock block = this.blockByNum(num); + BigInteger l1OriginNum; + String l1OriginHash; + BigInteger l1OriginTimestamp; + BigInteger seqNum; + if (rollupConfig.l2Genesis().number().equals(block.getBlock().getNumber())) { + l1OriginNum = rollupConfig.l1StartEpoch().number(); + l1OriginHash = rollupConfig.l1StartEpoch().hash(); + l1OriginTimestamp = rollupConfig.l1StartEpoch().timestamp(); + seqNum = BigInteger.ZERO; + } else { + if (CollectionUtils.isEmpty(block.getBlock().getTransactions())) { + throw new ParseBlockException("Missing L1 info deposit: " + num); + } + var tx = (OpEthBlock.TransactionObject) + block.getBlock().getTransactions().get(0); + if (!TxType.OPTIMISM_DEPOSIT.is(tx.getType())) { + throw new ParseBlockException("First tx Non Deposit"); + } + final L1BlockInfoTx l1BlockInfoTx = L1BlockInfoTx.decodeFrom(tx.getInput()); + l1OriginNum = l1BlockInfoTx.number(); + l1OriginHash = l1BlockInfoTx.blockHash(); + l1OriginTimestamp = l1BlockInfoTx.timestamp(); + seqNum = l1BlockInfoTx.sequenceNumber(); + } + final L2BlockRef ref = new L2BlockRef( + block.getBlock().getHash(), + block.getBlock().getNumber(), + block.getBlock().getParentHash(), + block.getBlock().getTimestamp(), + new Epoch(l1OriginNum, l1OriginHash, l1OriginTimestamp, null), + seqNum); + this.l2InfoByNumCache.put(num, ref); + return ref; } @Override public OpEthBlock blockByNum(BigInteger num) { + OpEthBlock cachedBlock = this.blockByNumCache.getIfPresent(num); + if (cachedBlock != null) { + return cachedBlock; + } + + OpEthBlock block = Web3jUtil.pollOpBlockByNum(this.l2Service, DefaultBlockParameter.valueOf(num), true); + if (block == null || block.getBlock() == null) { + throw new BlockNotIncludedException("Optimism block not found by number:" + num); + } + this.blockByNumCache.put(num, block); return null; } @Override public SystemConfig systemConfigByNumber(BigInteger num, Config.ChainConfig chainConfig) { - return null; + SystemConfig cachedConfig = this.sysConfigByNumCache.getIfPresent(num); + if (cachedConfig != null) { + return cachedConfig; + } + OpEthBlock opBlock = this.blockByNum(num); + var sysConfig = SystemConfig.fromOpBlock(opBlock.getBlock(), chainConfig); + this.sysConfigByNumCache.put(num, sysConfig); + return sysConfig; } } diff --git a/src/main/java/io/optimism/v2/derive/exception/PipelineEofException.java b/src/main/java/io/optimism/v2/derive/exception/PipelineEofException.java index b4304c4a..55759791 100644 --- a/src/main/java/io/optimism/v2/derive/exception/PipelineEofException.java +++ b/src/main/java/io/optimism/v2/derive/exception/PipelineEofException.java @@ -2,6 +2,9 @@ /** * Exception thrown when there is an error in the pipeline. + * + * @author thinkAfCod + * @since 0.4.6 */ public class PipelineEofException extends RuntimeException { /** Constructs a PipelineEofException. */ diff --git a/src/main/java/io/optimism/v2/derive/exception/PipelineProviderException.java b/src/main/java/io/optimism/v2/derive/exception/PipelineProviderException.java index 97d495af..eb1c5915 100644 --- a/src/main/java/io/optimism/v2/derive/exception/PipelineProviderException.java +++ b/src/main/java/io/optimism/v2/derive/exception/PipelineProviderException.java @@ -2,6 +2,9 @@ /** * Exception thrown when there is an error in the pipeline provider. + * + * @author thinkAfCod + * @since 0.4.6 */ public class PipelineProviderException extends RuntimeException { diff --git a/src/main/java/io/optimism/v2/derive/pipeline/DerivationPipeline.java b/src/main/java/io/optimism/v2/derive/pipeline/DerivationPipeline.java index 3075a9c6..cf2d6e8d 100644 --- a/src/main/java/io/optimism/v2/derive/pipeline/DerivationPipeline.java +++ b/src/main/java/io/optimism/v2/derive/pipeline/DerivationPipeline.java @@ -1,6 +1,15 @@ package io.optimism.v2.derive.pipeline; +/** + * the derivation pipeline of stages. + * + * @author thinkAfCod + * @since 0.4.6 + */ public class DerivationPipeline { - DerivationPipeline() {} + /** + * the DerivationPipeline constructor. + */ + public DerivationPipeline() {} } diff --git a/src/main/java/io/optimism/v2/derive/pipeline/PipelineBuilder.java b/src/main/java/io/optimism/v2/derive/pipeline/PipelineBuilder.java index cc5407b8..53aec92e 100644 --- a/src/main/java/io/optimism/v2/derive/pipeline/PipelineBuilder.java +++ b/src/main/java/io/optimism/v2/derive/pipeline/PipelineBuilder.java @@ -1,9 +1,23 @@ package io.optimism.v2.derive.pipeline; +/** + * the derivation pipeline builder. + * + * @author thinkAfCod + * @since 0.4.6 + */ public class PipelineBuilder { + /** + * the PipelineBuilder constructor. + */ public PipelineBuilder() {} + /** + * builds the derivation pipeline. + * + * @return the derivation pipeline + */ public DerivationPipeline build() { return new DerivationPipeline(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/AttributesBuilder.java b/src/main/java/io/optimism/v2/derive/stages/AttributesBuilder.java index 5e0ba09d..f873d9e6 100644 --- a/src/main/java/io/optimism/v2/derive/stages/AttributesBuilder.java +++ b/src/main/java/io/optimism/v2/derive/stages/AttributesBuilder.java @@ -1,10 +1,23 @@ package io.optimism.v2.derive.stages; -import io.optimism.v2.derive.types.Epoch; +import io.optimism.v2.derive.types.BlockInfo; import io.optimism.v2.derive.types.L2BlockRef; import io.optimism.v2.derive.types.OpPayloadAttributes; +/** + * the interface Attributes builder. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface AttributesBuilder { - OpPayloadAttributes preparePayloadAttr(L2BlockRef ref, Epoch epoch); + /** + * prepare next payload attributes. + * + * @param parent the parent block info of next payload + * @param l1Epoch the l1 inclusion block info of next payload + * @return the next payload attributes + */ + OpPayloadAttributes preparePayloadAttr(L2BlockRef parent, BlockInfo l1Epoch); } diff --git a/src/main/java/io/optimism/v2/derive/stages/AttributesProvider.java b/src/main/java/io/optimism/v2/derive/stages/AttributesProvider.java index 074a0e4b..18ee18ac 100644 --- a/src/main/java/io/optimism/v2/derive/stages/AttributesProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/AttributesProvider.java @@ -3,17 +3,22 @@ import io.optimism.types.L2BlockRef; import io.optimism.types.SingularBatch; +/** + * the attributes provider interface. + */ public interface AttributesProvider { /** * returns the next valid batch upon the given safe head. + * * @param parent - * @return + * @return the next Singular batch */ SingularBatch nextBatch(L2BlockRef parent); /** + * returns if the batch on the current index is last in the span. * - * @return + * @return true if the current batch is the last in the span, false otherwise */ boolean isLastInSpan(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/BatchQueueProvider.java b/src/main/java/io/optimism/v2/derive/stages/BatchQueueProvider.java index 2904c62e..fde286ef 100644 --- a/src/main/java/io/optimism/v2/derive/stages/BatchQueueProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/BatchQueueProvider.java @@ -2,9 +2,23 @@ import io.optimism.v2.derive.types.Batch; +/** + * the batch queue provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface BatchQueueProvider { + /** + * gets the next batch in the queue. + * + * @return the next batch in the queue + */ Batch nextBatch(); + /** + * flush the batch queue. + */ void flush(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/ChannelBankProvider.java b/src/main/java/io/optimism/v2/derive/stages/ChannelBankProvider.java index 9c3ea860..f702c04b 100644 --- a/src/main/java/io/optimism/v2/derive/stages/ChannelBankProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/ChannelBankProvider.java @@ -2,7 +2,17 @@ import io.optimism.v2.derive.types.Frame; +/** + * the channel bank provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface ChannelBankProvider { - + /** + * gets the next frame in the current channel + * + * @return the next frame in the current channel + */ Frame nextFrame(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/ChannelReaderProvider.java b/src/main/java/io/optimism/v2/derive/stages/ChannelReaderProvider.java index c8de15a3..1aab62d4 100644 --- a/src/main/java/io/optimism/v2/derive/stages/ChannelReaderProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/ChannelReaderProvider.java @@ -1,6 +1,17 @@ package io.optimism.v2.derive.stages; +/** + * The channel reader provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface ChannelReaderProvider { + /** + * get the bytes of the next raw batch. + * + * @return the bytes of the next raw batch + */ byte[] nextData(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/FrameQueueProvider.java b/src/main/java/io/optimism/v2/derive/stages/FrameQueueProvider.java index dc8008da..e8e42108 100644 --- a/src/main/java/io/optimism/v2/derive/stages/FrameQueueProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/FrameQueueProvider.java @@ -1,5 +1,16 @@ package io.optimism.v2.derive.stages; +/** + * The frame queue provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface FrameQueueProvider { + /** + * gets the bytes of the next raw frame. + * + * @return the bytes of the next raw frame + */ byte[] next(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/L1RetrievalProvider.java b/src/main/java/io/optimism/v2/derive/stages/L1RetrievalProvider.java index 89b98edc..b462a53d 100644 --- a/src/main/java/io/optimism/v2/derive/stages/L1RetrievalProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/L1RetrievalProvider.java @@ -2,9 +2,24 @@ import io.optimism.v2.derive.types.BlockInfo; +/** + * the l1 retrieval provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface L1RetrievalProvider { - + /** + * get the next L1 block info. + * + * @return the next L1 block info + */ BlockInfo nextL1Block(); + /** + * get the current batcher address. + * + * @return the batcher address + */ String batcherAddr(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/NextAttributes.java b/src/main/java/io/optimism/v2/derive/stages/NextAttributes.java index 9422ca3a..ac71d834 100644 --- a/src/main/java/io/optimism/v2/derive/stages/NextAttributes.java +++ b/src/main/java/io/optimism/v2/derive/stages/NextAttributes.java @@ -3,7 +3,19 @@ import io.optimism.v2.derive.types.L2BlockRef; import io.optimism.v2.derive.types.OpAttributesWithParent; +/** + * the next attributes interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface NextAttributes { + /** + * get the next payload attributes with the parent block info. + * + * @param parent the parent block info of next payload + * @return the next payload attributes with the parent block info + */ OpAttributesWithParent nextAttr(L2BlockRef parent); } diff --git a/src/main/java/io/optimism/v2/derive/stages/OriginAdvancer.java b/src/main/java/io/optimism/v2/derive/stages/OriginAdvancer.java index 56a3df6e..089af355 100644 --- a/src/main/java/io/optimism/v2/derive/stages/OriginAdvancer.java +++ b/src/main/java/io/optimism/v2/derive/stages/OriginAdvancer.java @@ -1,5 +1,15 @@ package io.optimism.v2.derive.stages; +/** + * The origin advancer interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface OriginAdvancer { + + /** + * advance the l1 origin info. + */ void advanceOrigin(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/OriginProvider.java b/src/main/java/io/optimism/v2/derive/stages/OriginProvider.java index 7a7851f5..c41707c5 100644 --- a/src/main/java/io/optimism/v2/derive/stages/OriginProvider.java +++ b/src/main/java/io/optimism/v2/derive/stages/OriginProvider.java @@ -2,7 +2,17 @@ import io.optimism.v2.derive.types.BlockInfo; +/** + * The origin provider interface. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface OriginProvider { + /** + * get the current l1 origin info. + * @return the current l1 origin info + */ BlockInfo origin(); } diff --git a/src/main/java/io/optimism/v2/derive/stages/ResettableStage.java b/src/main/java/io/optimism/v2/derive/stages/ResettableStage.java index b959af6d..a0bbdcc6 100644 --- a/src/main/java/io/optimism/v2/derive/stages/ResettableStage.java +++ b/src/main/java/io/optimism/v2/derive/stages/ResettableStage.java @@ -3,6 +3,12 @@ import io.optimism.v2.derive.types.BlockInfo; import io.optimism.v2.derive.types.SystemConfig; +/** + * the interface Resettable stage. + * + * @author thinkAfCod + * @since 0.4.6 + */ public interface ResettableStage { void reset(BlockInfo base, SystemConfig config); diff --git a/src/main/java/io/optimism/v2/derive/stages/impl/AttributesQueue.java b/src/main/java/io/optimism/v2/derive/stages/impl/AttributesQueue.java index 4097a9b9..16684587 100644 --- a/src/main/java/io/optimism/v2/derive/stages/impl/AttributesQueue.java +++ b/src/main/java/io/optimism/v2/derive/stages/impl/AttributesQueue.java @@ -6,7 +6,6 @@ import io.optimism.v2.derive.stages.OriginProvider; import io.optimism.v2.derive.stages.ResettableStage; import io.optimism.v2.derive.types.BlockInfo; -import io.optimism.v2.derive.types.Epoch; import io.optimism.v2.derive.types.L2BlockRef; import io.optimism.v2.derive.types.OpAttributesWithParent; import io.optimism.v2.derive.types.OpPayloadAttributes; @@ -15,7 +14,7 @@ public class AttributesQueue implements NextAttributes, OriginAdvancer, OriginProvider, ResettableStage, AttributesBuilder { @Override - public OpPayloadAttributes preparePayloadAttr(L2BlockRef ref, Epoch epoch) { + public OpPayloadAttributes preparePayloadAttr(L2BlockRef ref, BlockInfo epoch) { return null; } diff --git a/src/main/java/io/optimism/v2/derive/types/Batch.java b/src/main/java/io/optimism/v2/derive/types/Batch.java index 1fa6501b..005382fd 100644 --- a/src/main/java/io/optimism/v2/derive/types/Batch.java +++ b/src/main/java/io/optimism/v2/derive/types/Batch.java @@ -3,6 +3,12 @@ import io.optimism.v2.derive.types.enums.BatchType; import java.math.BigInteger; +/** + * The Batch interface. + * + * @author thinkAfCod + * @since 0.4.5 + */ public interface Batch { BatchType type(); diff --git a/src/main/java/io/optimism/v2/derive/types/BatchWithInclusionBlock.java b/src/main/java/io/optimism/v2/derive/types/BatchWithInclusionBlock.java deleted file mode 100644 index 6179bee4..00000000 --- a/src/main/java/io/optimism/v2/derive/types/BatchWithInclusionBlock.java +++ /dev/null @@ -1,3 +0,0 @@ -package io.optimism.v2.derive.types; - -public class BatchWithInclusionBlock {} diff --git a/src/main/java/io/optimism/v2/derive/types/BlockId.java b/src/main/java/io/optimism/v2/derive/types/BlockId.java new file mode 100644 index 00000000..f898af42 --- /dev/null +++ b/src/main/java/io/optimism/v2/derive/types/BlockId.java @@ -0,0 +1,30 @@ +package io.optimism.v2.derive.types; + +import java.math.BigInteger; +import org.web3j.protocol.core.methods.response.EthBlock; + +/** + * Block id. + * + * @param hash Block hash + * @param number Block number + * @author thinkAfCod + * @since 0.1.1 + */ +public record BlockId(String hash, BigInteger number) { + + /** + * Create BlockId from EthBlock.Block. + * + * @param block block data + * @return BlockId object + */ + public static BlockId from(EthBlock.Block block) { + return new BlockId(block.getHash(), block.getNumber()); + } + + @Override + public String toString() { + return "BlockId{hash='%s', number=%s}".formatted(hash, number); + } +} diff --git a/src/main/java/io/optimism/v2/derive/types/BlockInfo.java b/src/main/java/io/optimism/v2/derive/types/BlockInfo.java index 721d45b7..aca74e70 100644 --- a/src/main/java/io/optimism/v2/derive/types/BlockInfo.java +++ b/src/main/java/io/optimism/v2/derive/types/BlockInfo.java @@ -1,10 +1,10 @@ package io.optimism.v2.derive.types; import io.optimism.exceptions.BlockNotIncludedException; -import io.optimism.types.ExecutionPayload; +import io.optimism.rpc.response.OpEthBlock; import java.math.BigInteger; import java.util.Objects; -import org.web3j.protocol.core.methods.response.EthBlock.Block; +import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.utils.Numeric; /** @@ -25,32 +25,28 @@ public record BlockInfo(String hash, BigInteger number, String parentHash, BigIn Numeric.toHexString(new byte[32]), BigInteger.ZERO, Numeric.toHexString(new byte[32]), BigInteger.ZERO); /** - * From block info. + * create block info From EthBlock.Block. * - * @param block the block + * @param block the op block * @return the block info */ - public static BlockInfo from(Block block) { - BigInteger number = block.getNumber(); - if (number == null) { + public static BlockInfo from(EthBlock.Block block) { + if (block == null) { throw new BlockNotIncludedException(); } - - String hash = block.getHash(); - if (hash == null) { - throw new BlockNotIncludedException(); - } - return new BlockInfo(hash, number, block.getParentHash(), block.getTimestamp()); + return new BlockInfo(block.getHash(), block.getNumber(), block.getParentHash(), block.getTimestamp()); } /** - * From block info. - * - * @param payload the payload + * create block info From OpEthBlock.Block. + * @param block the op block * @return the block info */ - public static BlockInfo from(ExecutionPayload payload) { - return new BlockInfo(payload.blockHash(), payload.blockNumber(), payload.parentHash(), payload.timestamp()); + public static BlockInfo from(OpEthBlock.Block block) { + if (block == null) { + throw new BlockNotIncludedException(); + } + return new BlockInfo(block.getHash(), block.getNumber(), block.getParentHash(), block.getTimestamp()); } @Override diff --git a/src/main/java/io/optimism/v2/derive/types/Epoch.java b/src/main/java/io/optimism/v2/derive/types/Epoch.java index 6f406c50..d783207e 100644 --- a/src/main/java/io/optimism/v2/derive/types/Epoch.java +++ b/src/main/java/io/optimism/v2/derive/types/Epoch.java @@ -1,6 +1,5 @@ package io.optimism.v2.derive.types; -import io.optimism.types.L1BlockInfo; import java.math.BigInteger; /** @@ -13,26 +12,4 @@ * @author grapebaba * @since 0.1.0 */ -public record Epoch(BigInteger number, String hash, BigInteger timestamp, BigInteger sequenceNumber) { - - /** - * Create epoch from L1BlockInfo. - * - * @param info the L1 block info - * @return the epoch - */ - public static Epoch from(L1BlockInfo info) { - return new Epoch(info.number(), info.blockHash(), info.time(), info.sequenceNumber()); - } - - /** - * Creates epoch from an another epoch and sets sequence number. - * - * @param epoch the epoch info - * @param sequenceNumber the sequence number - * @return a new epoch with sequence number - */ - public static Epoch from(Epoch epoch, BigInteger sequenceNumber) { - return new Epoch(epoch.number(), epoch.hash(), epoch.timestamp(), sequenceNumber); - } -} +public record Epoch(BigInteger number, String hash, BigInteger timestamp, BigInteger sequenceNumber) {} diff --git a/src/main/java/io/optimism/v2/derive/types/L1BlockInfoTx.java b/src/main/java/io/optimism/v2/derive/types/L1BlockInfoTx.java index 5199c3a3..493091ca 100644 --- a/src/main/java/io/optimism/v2/derive/types/L1BlockInfoTx.java +++ b/src/main/java/io/optimism/v2/derive/types/L1BlockInfoTx.java @@ -88,10 +88,10 @@ public byte[] encodeForBedrock() { System.arraycopy(Numeric.toBytesPadded(this.timestamp, 32), 0, data, 36, 32); System.arraycopy(Numeric.toBytesPadded(this.baseFee, 32), 0, data, 68, 32); System.arraycopy(Numeric.hexStringToByteArray(this.blockHash), 0, data, 100, 32); - System.arraycopy(Numeric.toBytesPadded(this.sequenceNumber, 32), 132, data, 4, 32); - System.arraycopy(Numeric.hexStringToByteArray(this.batcherAddress), 164, data, 4, 32); - System.arraycopy(Numeric.toBytesPadded(this.l1FeeOverhead, 32), 196, data, 4, 32); - System.arraycopy(Numeric.toBytesPadded(this.l1FeeScalar, 32), 228, data, 4, 32); + System.arraycopy(Numeric.toBytesPadded(this.sequenceNumber, 32), 0, data, 132, 32); + System.arraycopy(Numeric.hexStringToByteArray(this.batcherAddress), 0, data, 164, 32); + System.arraycopy(Numeric.toBytesPadded(this.l1FeeOverhead, 32), 0, data, 196, 32); + System.arraycopy(Numeric.toBytesPadded(this.l1FeeScalar, 32), 0, data, 228, 32); return data; } diff --git a/src/main/java/io/optimism/v2/derive/types/L2BlockRef.java b/src/main/java/io/optimism/v2/derive/types/L2BlockRef.java index 1b749abc..1bc62eb9 100644 --- a/src/main/java/io/optimism/v2/derive/types/L2BlockRef.java +++ b/src/main/java/io/optimism/v2/derive/types/L2BlockRef.java @@ -1,11 +1,7 @@ package io.optimism.v2.derive.types; -import io.optimism.types.BlockId; -import io.optimism.types.Epoch; -import io.optimism.types.L1BlockInfo; import java.math.BigInteger; import java.util.Objects; -import org.web3j.protocol.core.methods.response.EthBlock; /** * L2 block brief information. @@ -38,23 +34,6 @@ public BlockId toId() { return new BlockId(hash, number); } - /** - * Create a L2BlockRef instance from EthBlock.Block and L1BlockInfo - * - * @param block block info - * @param l1Info l1 block info - * @return L2BlockRef instance - */ - public static L2BlockRef fromBlockAndL1Info(EthBlock.Block block, L1BlockInfo l1Info) { - return new L2BlockRef( - block.getHash(), - block.getNumber(), - block.getParentHash(), - block.getTimestamp(), - l1Info.toEpoch(), - l1Info.sequenceNumber()); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/io/optimism/v2/derive/types/OpAttributesWithParent.java b/src/main/java/io/optimism/v2/derive/types/OpAttributesWithParent.java index 49581eb0..b63b65fd 100644 --- a/src/main/java/io/optimism/v2/derive/types/OpAttributesWithParent.java +++ b/src/main/java/io/optimism/v2/derive/types/OpAttributesWithParent.java @@ -1,3 +1,13 @@ package io.optimism.v2.derive.types; +/** + * The OpAttributesWithParent class. + * + * @param attributes the attributes instance. + * @param parent parent l2 block info. + * @param isLastInSpan last in span flag. + * + * @author thinkAfCod + * @since 0.4.5 + */ public record OpAttributesWithParent(OpPayloadAttributes attributes, L2BlockRef parent, Boolean isLastInSpan) {} diff --git a/src/main/java/io/optimism/v2/derive/types/OpPayloadAttributes.java b/src/main/java/io/optimism/v2/derive/types/OpPayloadAttributes.java index bc919043..9727a316 100644 --- a/src/main/java/io/optimism/v2/derive/types/OpPayloadAttributes.java +++ b/src/main/java/io/optimism/v2/derive/types/OpPayloadAttributes.java @@ -5,6 +5,31 @@ import java.util.List; import org.web3j.protocol.core.methods.response.EthBlock; +/** + * The OpPayloadAttributes type. + * + * @param timestamp 64 bit value for the timestamp field of the new payload. + * @param prevRandao 32 byte value for the prevRandao field of the new payload. + * @param suggestedFeeRecipient 20 bytes suggested value for the feeRecipient field of the new + * payload. + * @param transactions List of transactions to be included in the new payload. + * @param withdrawals List of withdrawals to be included in the new payload. + * @param noTxPool Boolean value indicating whether the payload should be built without including + * transactions from the txpool. + * @param gasLimit 64 bit value for the gasLimit field of the new payload.The gasLimit is optional + * w.r.t. compatibility with L1, but required when used as rollup.This field overrides the gas + * limit used during block-building.If not specified as rollup, a STATUS_INVALID is returned. + * @param epoch The batch epoch number from derivation. This value is not expected by the engine + * is skipped during serialization and deserialization. + * @param l1InclusionBlock The L1 block number when this batch was first fully derived. This value + * is not expected by the engine and is skipped during serialization and deserialization. + * @param seqNumber The L2 sequence number of the block. This value is not expected by the engine + * and is skipped during serialization and deserialization. + * @param parentBeaconBlockRoot The parent beacon block root. + * + * @author zhouop0 + * @since 0.1.0 + */ public record OpPayloadAttributes( BigInteger timestamp, String prevRandao, diff --git a/src/main/java/io/optimism/v2/derive/types/SingularBatch.java b/src/main/java/io/optimism/v2/derive/types/SingularBatch.java index b1bb0b42..11b0b817 100644 --- a/src/main/java/io/optimism/v2/derive/types/SingularBatch.java +++ b/src/main/java/io/optimism/v2/derive/types/SingularBatch.java @@ -3,6 +3,12 @@ import io.optimism.v2.derive.types.enums.BatchType; import java.math.BigInteger; +/** + * the SingularBatch class. + * + * @author thinkAfCod + * @since 0.4.5 + */ public class SingularBatch implements Batch { @Override public BatchType type() { diff --git a/src/main/java/io/optimism/v2/derive/types/SpanBatch.java b/src/main/java/io/optimism/v2/derive/types/SpanBatch.java index e5fd0e41..8d0c7c55 100644 --- a/src/main/java/io/optimism/v2/derive/types/SpanBatch.java +++ b/src/main/java/io/optimism/v2/derive/types/SpanBatch.java @@ -3,6 +3,12 @@ import io.optimism.v2.derive.types.enums.BatchType; import java.math.BigInteger; +/** + * the SpanBatch class. + * + * @author thinkAfCod + * @since 0.4.5 + */ public class SpanBatch implements Batch { @Override public BatchType type() { diff --git a/src/main/java/io/optimism/v2/derive/types/SystemConfig.java b/src/main/java/io/optimism/v2/derive/types/SystemConfig.java index 6a1122d4..52ac29aa 100644 --- a/src/main/java/io/optimism/v2/derive/types/SystemConfig.java +++ b/src/main/java/io/optimism/v2/derive/types/SystemConfig.java @@ -3,6 +3,7 @@ import com.google.common.primitives.Bytes; import io.optimism.config.Config; import io.optimism.exceptions.InvalidSystemConfigUpdateException; +import io.optimism.rpc.response.OpEthBlock; import io.optimism.v2.derive.types.enums.TxType; import java.math.BigInteger; import java.util.Arrays; @@ -70,7 +71,7 @@ public Tuple2 ecotoneScalars() { throw new IllegalStateException("invalid l1FeeScalar"); } - public static SystemConfig fromOpBlock(EthBlock.Block opBlock, Config.ChainConfig chainConfig) { + public static SystemConfig fromOpBlock(OpEthBlock.Block opBlock, Config.ChainConfig chainConfig) { if (chainConfig.l2Genesis().number().equals(opBlock.getNumber())) { if (!chainConfig.l2Genesis().hash().equals(opBlock.getHash())) { // todo throw OpBlockConversionException InvalidGenesisHash diff --git a/src/main/java/io/optimism/v2/derive/types/enums/TxType.java b/src/main/java/io/optimism/v2/derive/types/enums/TxType.java index de3e7e4b..9c9b1d12 100644 --- a/src/main/java/io/optimism/v2/derive/types/enums/TxType.java +++ b/src/main/java/io/optimism/v2/derive/types/enums/TxType.java @@ -4,6 +4,9 @@ /** * The enum Tx type. + * + * @author grapepapa + * @since 0.1.1 */ public enum TxType { /**