Skip to content

Commit

Permalink
feat: draft implementation of InsertSoftBlockFromTransactionsBatch
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha committed Oct 28, 2024
1 parent 5c58198 commit 6b41652
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 14 deletions.
13 changes: 13 additions & 0 deletions packages/taiko-client/bindings/encoding/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ var (
{Name: "TaikoData.Transition", Type: transitionComponentsType},
{Name: "TaikoData.TierProof", Type: tierProofComponentsType},
}
stringType, _ = abi.NewType("string", "TAIKO_DIFFICULTY", nil)
uint64Type, _ = abi.NewType("uint64", "local.b.numBlocks", nil)
difficultyCalculationInputArgs = abi.Arguments{{Type: stringType}, {Type: uint64Type}}
)

// Contract ABIs.
Expand Down Expand Up @@ -423,6 +426,16 @@ func EncodeProveBlockInput(
return b, nil
}

// EncodeDifficultCalcutionParams performs the solidity `abi.encode` for the
// `block.difficulty` hash payload.
func EncodeDifficultyCalcutionParams(numBlocks uint64) ([]byte, error) {
b, err := difficultyCalculationInputArgs.Pack("TAIKO_DIFFICULTY", numBlocks)
if err != nil {
return nil, fmt.Errorf("failed to abi.encode `block.difficulty` hash payload, %w", err)
}
return b, nil
}

// UnpackTxListBytes unpacks the input data of a TaikoL1.proposeBlock transaction, and returns the txList bytes.
func UnpackTxListBytes(txData []byte) ([]byte, error) {
method, err := TaikoL1ABI.MethodById(txData)
Expand Down
11 changes: 11 additions & 0 deletions packages/taiko-client/bindings/encoding/protocol_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ var (
OntakeForkHeight: 538_304,
BaseFeeConfig: bindings.LibSharedDataBaseFeeConfig{
AdjustmentQuotient: 8,
SharingPctg: 75,
GasIssuancePerSecond: 5_000_000,
MinGasExcess: 1_340_000_000,
MaxGasIssuancePerBlock: 600_000_000,
Expand All @@ -76,3 +77,13 @@ func GetProtocolConfig(chainID uint64) *bindings.TaikoDataConfig {
return InternlDevnetProtocolConfig
}
}

// EncodeBaseFeeConfig encodes the block.extraData field from the given base fee config.
func EncodeBaseFeeConfig(baseFeeConfig *bindings.LibSharedDataBaseFeeConfig) [32]byte {
var (
bytes32Value [32]byte
uintValue = new(big.Int).SetUint64(uint64(baseFeeConfig.SharingPctg))
)
copy(bytes32Value[32-len(uintValue.Bytes()):], uintValue.Bytes())
return bytes32Value
}
185 changes: 179 additions & 6 deletions packages/taiko-client/driver/chain_syncer/blob/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
consensus "github.com/ethereum/go-ethereum/consensus/taiko"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
Expand Down Expand Up @@ -106,11 +107,6 @@ func (s *Syncer) ProcessL1Blocks(ctx context.Context) error {
}
}

// InsertSoftBlock inserts a soft block into the L2 execution engine's blockchain.
func (s *Syncer) InsertSoftBlock(ctx context.Context) (*types.Header, error) {
return nil, nil
}

// processL1Blocks is the inner method which responsible for processing
// all new L1 blocks.
func (s *Syncer) processL1Blocks(ctx context.Context) error {
Expand Down Expand Up @@ -492,7 +488,7 @@ func (s *Syncer) createExecutionPayloads(
"timestamp", attributes.BlockMetadata.Timestamp,
"mixHash", attributes.BlockMetadata.MixHash,
"baseFee", utils.WeiToGWei(attributes.BaseFeePerGas),
"extraData", string(attributes.BlockMetadata.ExtraData),
"extraData", common.Bytes2Hex(attributes.BlockMetadata.ExtraData),
"l1OriginHeight", attributes.L1Origin.L1BlockHeight,
"l1OriginHash", attributes.L1Origin.L1BlockHash,
)
Expand Down Expand Up @@ -693,3 +689,180 @@ func (s *Syncer) checkReorg(

return reorgCheckResult, nil
}

// InsertSoftBlockFromTransactionsBatch inserts a soft block into the L2 execution engine's blockchain
// from the given transactions batch.
func (s *Syncer) InsertSoftBlockFromTransactionsBatch(
// Transactions batch parameters
ctx context.Context,
blockID uint64,
batchID uint64, // TODO(DavidCai): verify batch ID
txListBytes []byte,
batchMarker string, // TODO(DavidCai): handle batch marker
// Block parameters
timestamp uint64,
coinbase common.Address,
// Anchor parameters
anchorBlockID uint64,
anchorStateRoot common.Hash,
) (*types.Header, error) {
parent, err := s.rpc.L2.HeaderByNumber(ctx, new(big.Int).Sub(new(big.Int).SetUint64(blockID), common.Big1))
if err != nil {
return nil, err
}

if parent.Number.Uint64()+1 != blockID {
return nil, fmt.Errorf("parent block number (%d) is not equal to blockID - 1 (%d)", parent.Number.Uint64(), blockID)
}

// Calculate the other block parameters
difficultyHashPaylaod, err := encoding.EncodeDifficultyCalcutionParams(blockID)
if err != nil {
return nil, fmt.Errorf("failed to encode `block.difficulty` calculation parameters: %w", err)
}

var (
txList []*types.Transaction
fc = &engine.ForkchoiceStateV1{HeadBlockHash: parent.Hash()}
difficulty = crypto.Keccak256Hash(difficultyHashPaylaod)
protocolConfig = encoding.GetProtocolConfig(s.rpc.L2.ChainID.Uint64())
extraData = encoding.EncodeBaseFeeConfig(&protocolConfig.BaseFeeConfig)
)

if err := rlp.DecodeBytes(txListBytes, &txList); err != nil {
return nil, fmt.Errorf("failed to RLP decode txList bytes: %w", err)
}

baseFee, err := s.rpc.CalculateBaseFee(
ctx,
parent,
new(big.Int).SetUint64(anchorBlockID),
true,
&protocolConfig.BaseFeeConfig,
timestamp,
)
if err != nil {
return nil, fmt.Errorf("failed to calculate base fee: %w", err)
}

// Insert the anchor transaction at the head of the transactions list.
if batchID == 0 {
// Assemble a TaikoL2.anchorV2 transaction.
anchorTx, err := s.anchorConstructor.AssembleAnchorV2Tx(
ctx,
new(big.Int).SetUint64(anchorBlockID),
anchorStateRoot,
parent.GasUsed,
&protocolConfig.BaseFeeConfig,
new(big.Int).SetUint64(blockID),
baseFee,
)
if err != nil {
return nil, fmt.Errorf("failed to create TaikoL2.anchorV2 transaction: %w", err)
}

txList = append([]*types.Transaction{anchorTx}, txList...)
if txListBytes, err = rlp.EncodeToBytes(txList); err != nil {
log.Error("Encode txList error", "blockID", blockID, "error", err)
return nil, err
}
} else {
prevSoftBlock, err := s.rpc.L2.BlockByNumber(ctx, new(big.Int).SetUint64(blockID-1))
if err != nil {
return nil, fmt.Errorf("failed to fetch previous soft block (%d): %w", blockID, err)
}
txList = append(prevSoftBlock.Transactions(), txList...)
}

attributes := &engine.PayloadAttributes{
Timestamp: timestamp,
Random: difficulty,
SuggestedFeeRecipient: coinbase,
Withdrawals: []*types.Withdrawal{},
BlockMetadata: &engine.BlockMetadata{
Beneficiary: coinbase,
GasLimit: uint64(protocolConfig.BlockMaxGasLimit) + consensus.AnchorGasLimit,
Timestamp: timestamp,
TxList: txListBytes,
MixHash: difficulty,
ExtraData: extraData[:],
},
BaseFeePerGas: baseFee,
L1Origin: nil, // TODO(DavidCai): check L1 origin
}

log.Info(
"Soft block payloadAttributes",
"blockID", blockID,
"timestamp", attributes.Timestamp,
"random", attributes.Random,
"suggestedFeeRecipient", attributes.SuggestedFeeRecipient,
"withdrawals", len(attributes.Withdrawals),
"gasLimit", attributes.BlockMetadata.GasLimit,
"timestamp", attributes.BlockMetadata.Timestamp,
"mixHash", attributes.BlockMetadata.MixHash,
"baseFee", utils.WeiToGWei(attributes.BaseFeePerGas),
"extraData", common.Bytes2Hex(attributes.BlockMetadata.ExtraData),
)

// Step 1, prepare a payload
fcRes, err := s.rpc.L2Engine.ForkchoiceUpdate(ctx, fc, attributes)
if err != nil {
return nil, fmt.Errorf("failed to update fork choice: %w", err)
}
if fcRes.PayloadStatus.Status != engine.VALID {
return nil, fmt.Errorf("unexpected ForkchoiceUpdate response status: %s", fcRes.PayloadStatus.Status)
}
if fcRes.PayloadID == nil {
return nil, errors.New("empty payload ID")
}

// Step 2, get the payload
payload, err := s.rpc.L2Engine.GetPayload(ctx, fcRes.PayloadID)
if err != nil {
return nil, fmt.Errorf("failed to get payload: %w", err)
}

log.Info(
"Soft block payload",
"blockID", blockID,
"baseFee", utils.WeiToGWei(payload.BaseFeePerGas),
"number", payload.Number,
"hash", payload.BlockHash,
"gasLimit", payload.GasLimit,
"gasUsed", payload.GasUsed,
"timestamp", payload.Timestamp,
"withdrawalsHash", payload.WithdrawalsHash,
)

// Step 3, execute the payload
execStatus, err := s.rpc.L2Engine.NewPayload(ctx, payload)
if err != nil {
return nil, fmt.Errorf("failed to create a new payload: %w", err)
}
if execStatus.Status != engine.VALID {
return nil, fmt.Errorf("unexpected NewPayload response status: %s", execStatus.Status)
}

lastVerifiedBlockHash, err := s.rpc.GetLastVerifiedBlockHash(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch last verified block hash: %w", err)
}

fc = &engine.ForkchoiceStateV1{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.BlockHash, // TODO(DavidCai): use last canonical block hash.
FinalizedBlockHash: lastVerifiedBlockHash,
}

// Update the fork choice
fcRes, err = s.rpc.L2Engine.ForkchoiceUpdate(ctx, fc, nil)
if err != nil {
return nil, err
}
if fcRes.PayloadStatus.Status != engine.VALID {
return nil, fmt.Errorf("unexpected ForkchoiceUpdate response status: %s", fcRes.PayloadStatus.Status)
}

return s.rpc.L2.HeaderByHash(ctx, payload.BlockHash)
}
Loading

0 comments on commit 6b41652

Please sign in to comment.