Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add span-batch #82

Merged
merged 2 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions hildr-batcher/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ repositories {
maven {
url "https://artifacts.consensys.net/public/maven/maven/"
}
maven { url "https://artifacts.consensys.net/public/maven/maven/" }
maven { url "https://jitpack.io" }
google()
}

application {
Expand Down
13 changes: 13 additions & 0 deletions hildr-utilities/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ repositories {
maven {
url "https://artifacts.consensys.net/public/maven/maven/"
}
maven { url "https://artifacts.consensys.net/public/maven/maven/" }
maven { url "https://jitpack.io" }
google()
}

java {
Expand Down Expand Up @@ -75,11 +78,21 @@ dependencies {

implementation 'ch.qos.logback:logback-core:1.4.7'
implementation 'ch.qos.logback:logback-classic:1.4.7'

implementation 'io.tmio:tuweni-rlp:2.4.2'
implementation 'org.bouncycastle:bcprov-jdk18on:1.76'
implementation 'org.slf4j:slf4j-api:2.0.7'
implementation 'io.libp2p:jvm-libp2p:1.0.1-RELEASE'

testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'

testImplementation 'io.tmio:tuweni-ssz:2.4.2'
testImplementation 'io.tmio:tuweni-units:2.4.2'
testImplementation('io.tmio:tuweni-crypto:2.4.2'){
exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
}

errorprone("com.google.errorprone:error_prone_core:2.18.0")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.optimism.utilities.derive.stages;

public enum BatchType {
SINGULAR_BATCH_TYPE(0, "SingularBatchType"),
SPAN_BATCH_TYPE(1, "SpanBatchType");
private final int code;
private final String name;

BatchType(int code, String name) {
this.code = code;
this.name = name;
}

public int getCode() {
return code;
}

public String getName() {
return name;
}

public static BatchType from(int code) {
for (BatchType batchType : BatchType.values()) {
if (batchType.getCode() == code) {
return batchType;
}
}
throw new IllegalArgumentException("Invalid BatchType code: " + code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.optimism.utilities.derive.stages;

import java.math.BigInteger;

/**
* Batch contains information to build one or multiple L2 blocks.
* Batcher converts L2 blocks into Batch and writes encoded bytes to Channel.
* Derivation pipeline decodes Batch from Channel, and converts to one or multiple payload attributes.
*
* @author zhouop0
* @since 0.1.0
*/
public interface IBatch {

int getBatchType();

BigInteger getTimestamp();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.optimism.utilities.derive.stages;

public record RawSpanBatch(SpanBatchPrefix spanbatchPrefix, SpanBatchPayload spanbatchPayload) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.optimism.utilities.derive.stages;

import io.optimism.type.BlockId;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
import org.web3j.rlp.RlpType;

public record SingularBatch(
String parentHash, BigInteger epochNum, String epochHash, BigInteger timestamp, List<String> transactions)
implements IBatch {

public BlockId epoch() {
return new BlockId(epochHash(), epochNum());
}

public byte[] encode() {
List<RlpType> collect = transactions().stream()
.map(tx -> (RlpType) RlpString.create(tx))
.collect(Collectors.toList());
return RlpEncoder.encode(new RlpList(
RlpString.create(parentHash()),
RlpString.create(epochNum()),
RlpString.create(epochHash()),
RlpString.create(timestamp()),
new RlpList(collect)));
}

/**
* Decode batch.
*
* @param rlp the rlp
* @return the batch
*/
public static SingularBatch decode(RlpList rlp) {
String parentHash = ((RlpString) rlp.getValues().get(0)).asString();
BigInteger epochNum = ((RlpString) rlp.getValues().get(1)).asPositiveBigInteger();
String epochHash = ((RlpString) rlp.getValues().get(2)).asString();
BigInteger timestamp = ((RlpString) rlp.getValues().get(3)).asPositiveBigInteger();
List<String> transactions = ((RlpList) rlp.getValues().get(4))
.getValues().stream()
.map(rlpString -> ((RlpString) rlpString).asString())
.collect(Collectors.toList());
return new SingularBatch(parentHash, epochNum, epochHash, timestamp, transactions);
}

@Override
public int getBatchType() {
return BatchType.SINGULAR_BATCH_TYPE.getCode();
}

@Override
public BigInteger getTimestamp() {
return timestamp();
}

public BigInteger getEpochNum() {
return epochNum();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package io.optimism.utilities.derive.stages;

import java.math.BigInteger;
import java.util.List;

/**
* SpanBatch is an implementation of Batch interface,
* containing the input to build a span of L2 blocks in derived form (SpanBatchElement)
*
* @author zhouop0
* @since 0.1.0
*/
public class SpanBatch implements IBatch {

// First 20 bytes of the first block's parent hash
private String parentCheck;
// First 20 bytes of the last block's L1 origin hash
private String l1OriginCheck;
// List of block input in derived form
private List<SpanBatchElement> batches;

public String getParentCheck() {
return parentCheck;
}

public String getL1OriginCheck() {
return l1OriginCheck;
}

public List<SpanBatchElement> getBatches() {
return batches;
}

@Override
public int getBatchType() {
return BatchType.SPAN_BATCH_TYPE.getCode();
}

@Override
public BigInteger getTimestamp() {
return batches.getFirst().timestamp();
}

/**
* GetStartEpochNum returns epoch number(L1 origin block number) of the first block in the span.
*/
public BigInteger getStartEpochNum() {
return this.batches.getFirst().epochNum();
}

/**
* checks if the parentCheck matches the first 20 bytes of given hash, probably the current L2 safe head.
* @param hash the first 20 bytes of given hash.
* @return boolean.
*/
public boolean checkOriginHash(String hash) {
return this.l1OriginCheck.equals(hash);
}

/**
* checks if the parentCheck matches the first 20 bytes of given hash, probably the current L2 safe head.
* @param hash the first 20 bytes of given hash.
* @return boolean.
*/
public boolean checkParentHash(String hash) {
return this.parentCheck.equals(hash);
}

/**
* GetBlockEpochNum
* @param index batches index.
* @return the epoch number(L1 origin block number) of the block at the given index in the span.
*/
public BigInteger getBlockEpochNum(int index) {
return this.batches.get(index).epochNum();
}

/**
* GetBlockTimestamp
* @param index batches index.
* @return the timestamp of the block at the given index in the span.
*/
public BigInteger getBlockTimestamp(int index) {
return this.batches.get(index).timestamp();
}

/**
* GetBlockCount
* @return the number of blocks in the span.
*/
public int getBlockCount() {
return this.batches.size();
}

/**
* AppendSingularBatch appends a SingularBatch into the span batch
* updates l1OriginCheck or parentCheck if needed.
*
* @param singularBatch SingularBatch
*/
public void AppendSingularBatch(SingularBatch singularBatch) {
if (batches.size() == 0) {
this.parentCheck = singularBatch.parentHash().substring(0, 20);
}
this.batches.add(SpanBatchElement.singularBatchToElement(singularBatch)); // add the batch to the list
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.optimism.utilities.derive.stages;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.web3j.crypto.AccessListObject;
import org.web3j.crypto.transaction.type.TransactionType;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
import org.web3j.rlp.RlpType;

/**
* EIP-2930.
*
*/
public record SpanBatchAccessListTxData(
BigInteger value, BigInteger gasPrice, String data, List<AccessListObject> accessList)
implements SpanBatchTxData {

@Override
public byte txType() {
return TransactionType.EIP2930.getRlpType();
}

public byte[] encode() {
List<RlpType> rlpList = new ArrayList<>();
for (AccessListObject access : accessList) {
rlpList.add(RlpString.create(access.getAddress()));
rlpList.add(new RlpList(
access.getStorageKeys().stream().map(RlpString::create).collect(Collectors.toList())));
}
return RlpEncoder.encode(new RlpList(
RlpString.create(value()),
RlpString.create(gasPrice()),
RlpString.create(data()),
new RlpList(rlpList)));
}

public static SpanBatchAccessListTxData decode(RlpList rlp) {
BigInteger value = ((RlpString) rlp.getValues().get(0)).asPositiveBigInteger();
BigInteger gasPrice = ((RlpString) rlp.getValues().get(1)).asPositiveBigInteger();
String data = ((RlpString) rlp.getValues().get(2)).asString();
List<AccessListObject> accessObjList = new ArrayList<>();
((RlpList) rlp.getValues().get(3)).getValues().forEach(rlpType -> {
RlpList rlpList = (RlpList) rlpType;
String address = ((RlpString) rlpList.getValues().get(0)).asString();
List<String> storageKeys = ((RlpList) rlpList.getValues().get(1))
.getValues().stream()
.map(stKey -> ((RlpString) stKey).asString())
.collect(Collectors.toList());
accessObjList.add(new AccessListObject(address, storageKeys));
});
return new SpanBatchAccessListTxData(value, gasPrice, data, accessObjList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.optimism.utilities.derive.stages;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.web3j.crypto.AccessListObject;
import org.web3j.crypto.transaction.type.TransactionType;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
import org.web3j.rlp.RlpType;

public record SpanBatchDynamicFeeTxData(
BigInteger value, BigInteger gasTipCap, BigInteger gasFeeCap, String data, List<AccessListObject> accessList)
implements SpanBatchTxData {
@Override
public byte txType() {
return TransactionType.EIP1559.getRlpType();
}

public byte[] encode() {
List<RlpType> rlpList = new ArrayList<>();
for (AccessListObject access : accessList) {
rlpList.add(RlpString.create(access.getAddress()));
rlpList.add(new RlpList(
access.getStorageKeys().stream().map(RlpString::create).collect(Collectors.toList())));
}
return RlpEncoder.encode(new RlpList(
RlpString.create(value()),
RlpString.create(gasTipCap()),
RlpString.create(gasFeeCap()),
RlpString.create(data()),
new RlpList(rlpList)));
}

public static SpanBatchDynamicFeeTxData decode(RlpList rlp) {
BigInteger value = ((RlpString) rlp.getValues().get(0)).asPositiveBigInteger();
BigInteger gasTipCap = ((RlpString) rlp.getValues().get(1)).asPositiveBigInteger();
BigInteger gasFeeCap = ((RlpString) rlp.getValues().get(2)).asPositiveBigInteger();
String data = ((RlpString) rlp.getValues().get(2)).asString();
List<AccessListObject> accessObjList = new ArrayList<>();
((RlpList) rlp.getValues().get(3)).getValues().forEach(rlpType -> {
RlpList rlpList = (RlpList) rlpType;
String address = ((RlpString) rlpList.getValues().get(0)).asString();
List<String> storageKeys = ((RlpList) rlpList.getValues().get(1))
.getValues().stream()
.map(stKey -> ((RlpString) stKey).asString())
.collect(Collectors.toList());
accessObjList.add(new AccessListObject(address, storageKeys));
});
return new SpanBatchDynamicFeeTxData(value, gasTipCap, gasFeeCap, data, accessObjList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.optimism.utilities.derive.stages;

import java.math.BigInteger;
import java.util.List;

public record SpanBatchElement(BigInteger epochNum, BigInteger timestamp, List<String> transactions) {

public static SpanBatchElement singularBatchToElement(SingularBatch singularBatch) {
return new SpanBatchElement(singularBatch.epochNum(), singularBatch.timestamp(), singularBatch.transactions());
}
}
Loading
Loading