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

feat(taiko-client): check whether a submitted Pacaya batch proof is valid #18892

Merged
merged 16 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 12 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
15 changes: 14 additions & 1 deletion packages/protocol/contracts/layer1/based/TaikoInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,20 @@ abstract contract TaikoInbox is EssentialContract, ITaikoInbox, ITaiko {
Batch storage batch = state.batches[slot];
require(batch.batchId == _batchId, BatchNotFound());

uint24 tid = state.transitionIds[_batchId][_parentHash];
uint24 tid;
if (batch.nextTransitionId > 1) {
// This batch has at least one transition.
if (state.transitions[slot][1].parentHash == _parentHash) {
// Overwrite the first transition.
tid = 1;
} else if (batch.nextTransitionId > 2) {
// Retrieve the transition ID using the parent hash from the mapping. If the ID
// is 0, it indicates a new transition; otherwise, it's an overwrite of an
// existing transition.
tid = state.transitionIds[_batchId][_parentHash];
}
}

require(tid != 0 && tid < batch.nextTransitionId, TransitionNotFound());
return state.transitions[slot][tid];
}
Expand Down
2 changes: 1 addition & 1 deletion packages/taiko-client/bindings/pacaya/.githead
Original file line number Diff line number Diff line change
@@ -1 +1 @@
794397957a87aa0ffbbfac81e90907af54044a9a
11d3c6c0f73f2eb4b53446b4fb1a204b51741601
1 change: 1 addition & 0 deletions packages/taiko-client/driver/chain_syncer/blob/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func (s *Syncer) processL1Blocks(ctx context.Context) error {
Client: s.rpc.L1,
TaikoL1: s.rpc.OntakeClients.TaikoL1,
TaikoInbox: s.rpc.PacayaClients.TaikoInbox,
PacayaForkHeight: s.rpc.PacayaClients.ForkHeight,
StartHeight: s.state.GetL1Current().Number,
EndHeight: l1End.Number,
OnBlockProposedEvent: s.onBlockProposed,
Expand Down
1 change: 1 addition & 0 deletions packages/taiko-client/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func (d *Driver) InitFromConfig(ctx context.Context, cfg *Config) (err error) {
if d.protocolConfig, err = d.rpc.GetProtocolConfigs(&bind.CallOpts{Context: d.ctx}); err != nil {
return err
}
config.ReportProtocolConfigs(d.protocolConfig)

if d.PreconfBlockServerPort > 0 {
if d.preconfBlockServer, err = preconfBlocks.New(
Expand Down
8 changes: 2 additions & 6 deletions packages/taiko-client/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,7 @@ func (s *DriverTestSuite) TestInsertPreconfBlocks() {
s.Nil(s.d.ChainSyncer().BlobSyncer().ProcessL1Blocks(context.Background()))

// Propose valid L2 blocks to make the L2 fork into Pacaya fork.
for i := 0; i < int(s.RPCClient.PacayaClients.ForkHeight); i++ {
s.ProposeAndInsertValidBlock(s.p, s.d.ChainSyncer().BlobSyncer())
}
s.ForkIntoPacaya(s.p, s.d.ChainSyncer().BlobSyncer())

l2Head2, err := s.d.rpc.L2.HeaderByNumber(context.Background(), nil)
s.Nil(err)
Expand Down Expand Up @@ -497,9 +495,7 @@ func (s *DriverTestSuite) TestInsertPreconfBlocksNotReorg() {
s.Nil(s.d.ChainSyncer().BlobSyncer().ProcessL1Blocks(context.Background()))

// Propose valid L2 blocks to make the L2 fork into Pacaya fork.
for i := 0; i < int(s.RPCClient.PacayaClients.ForkHeight); i++ {
s.ProposeAndInsertValidBlock(s.p, s.d.ChainSyncer().BlobSyncer())
}
s.ForkIntoPacaya(s.p, s.d.ChainSyncer().BlobSyncer())

l2Head2, err := s.d.rpc.L2.HeaderByNumber(context.Background(), nil)
s.Nil(err)
Expand Down
39 changes: 30 additions & 9 deletions packages/taiko-client/internal/testutils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,30 @@ import (
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
)

func (s *ClientTestSuite) ForkIntoPacaya(proposer Proposer, syncer ChainSyncer) {
for i := 0; i < int(s.RPCClient.PacayaClients.ForkHeight); i++ {
s.ProposeAndInsertValidBlock(proposer, syncer)
}
head, err := s.RPCClient.L2.HeaderByNumber(context.Background(), nil)
s.Nil(err)
s.GreaterOrEqual(head.Number.Uint64(), s.RPCClient.PacayaClients.ForkHeight)
}

func (s *ClientTestSuite) proposeEmptyBlockOp(ctx context.Context, proposer Proposer) {
s.Nil(proposer.ProposeTxLists(ctx, []types.Transactions{{}}))
}

func (s *ClientTestSuite) ProposeAndInsertEmptyBlocks(
proposer Proposer,
blobSyncer BlobSyncer,
chainSyncer ChainSyncer,
) []metadata.TaikoProposalMetaData {
// Sync all pending L2 blocks at first.
s.NotPanics(func() {
if err := chainSyncer.ProcessL1Blocks(context.Background()); err != nil {
log.Warn("Failed to process L1 blocks", "error", err)
}
})

var metadataList []metadata.TaikoProposalMetaData

l1Head, err := s.RPCClient.L1.HeaderByNumber(context.Background(), nil)
Expand All @@ -52,15 +68,15 @@ func (s *ClientTestSuite) ProposeAndInsertEmptyBlocks(

// RLP encoded empty list
s.Nil(proposer.ProposeTxLists(context.Background(), []types.Transactions{{}}))
s.Nil(blobSyncer.ProcessL1Blocks(context.Background()))
s.Nil(chainSyncer.ProcessL1Blocks(context.Background()))

// Valid transactions lists.
s.ProposeValidBlock(proposer)
s.Nil(blobSyncer.ProcessL1Blocks(context.Background()))
s.Nil(chainSyncer.ProcessL1Blocks(context.Background()))

// Random bytes txList
s.proposeEmptyBlockOp(context.Background(), proposer)
s.Nil(blobSyncer.ProcessL1Blocks(context.Background()))
s.Nil(chainSyncer.ProcessL1Blocks(context.Background()))

var txHash common.Hash
for i := 0; i < 3; i++ {
Expand Down Expand Up @@ -91,8 +107,15 @@ func (s *ClientTestSuite) ProposeAndInsertEmptyBlocks(
// into L2 execution engine's local chain.
func (s *ClientTestSuite) ProposeAndInsertValidBlock(
proposer Proposer,
blobSyncer BlobSyncer,
chainSyncer ChainSyncer,
) metadata.TaikoProposalMetaData {
// Sync all pending L2 blocks at first.
s.NotPanics(func() {
if err := chainSyncer.ProcessL1Blocks(context.Background()); err != nil {
log.Warn("Failed to process L1 blocks", "error", err)
}
})

l1Head, err := s.RPCClient.L1.HeaderByNumber(context.Background(), nil)
s.Nil(err)

Expand Down Expand Up @@ -155,7 +178,7 @@ func (s *ClientTestSuite) ProposeAndInsertValidBlock(
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

s.Nil(backoff.Retry(func() error { return blobSyncer.ProcessL1Blocks(ctx) }, backoff.NewExponentialBackOff()))
s.Nil(backoff.Retry(func() error { return chainSyncer.ProcessL1Blocks(ctx) }, backoff.NewExponentialBackOff()))

s.Nil(s.RPCClient.WaitTillL2ExecutionEngineSynced(context.Background()))

Expand All @@ -165,9 +188,7 @@ func (s *ClientTestSuite) ProposeAndInsertValidBlock(
return meta
}

func (s *ClientTestSuite) ProposeValidBlock(
proposer Proposer,
) {
func (s *ClientTestSuite) ProposeValidBlock(proposer Proposer) {
l1Head, err := s.RPCClient.L1.HeaderByNumber(context.Background(), nil)
s.Nil(err)

Expand Down
2 changes: 1 addition & 1 deletion packages/taiko-client/internal/testutils/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/taikoxyz/taiko-mono/packages/taiko-client/cmd/utils"
)

type BlobSyncer interface {
type ChainSyncer interface {
ProcessL1Blocks(ctx context.Context) error
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type BlockProposedIteratorConfig struct {
Client *rpc.EthClient
TaikoL1 *ontakeBindings.TaikoL1Client
TaikoInbox *pacayaBindings.TaikoInboxClient
PacayaForkHeight uint64
MaxBlocksReadPerEpoch *uint64
StartHeight *big.Int
EndHeight *big.Int
Expand Down Expand Up @@ -73,6 +74,7 @@ func NewBlockProposedIterator(ctx context.Context, cfg *BlockProposedIteratorCon
cfg.Client,
cfg.TaikoL1,
cfg.TaikoInbox,
cfg.PacayaForkHeight,
cfg.OnBlockProposedEvent,
iterator,
),
Expand Down Expand Up @@ -103,6 +105,7 @@ func assembleBlockProposedIteratorCallback(
client *rpc.EthClient,
taikoL1 *ontakeBindings.TaikoL1Client,
taikoInbox *pacayaBindings.TaikoInboxClient,
pacayaForkHeight uint64,
callback OnBlockProposedEvent,
eventIter *BlockProposedIterator,
) chainIterator.OnBlocksFunc {
Expand Down Expand Up @@ -134,6 +137,19 @@ func assembleBlockProposedIteratorCallback(
event := iterOntake.Event
log.Debug("Processing BlockProposedV2 event", "block", event.BlockId, "l1BlockHeight", event.Raw.BlockNumber)

// In case some proposers calling the old contract after Pacaya fork, we should skip the event
// when the block ID is greater than or equal to the Pacaya fork height.
if event.BlockId.Uint64() >= pacayaForkHeight {
log.Warn(
"BlockProposedV2 event after Pacaya fork, skip this event",
"block", event.BlockId,
"pacayaForkHeight", pacayaForkHeight,
"proposer", event.Meta.Proposer,
"l1BlockHeight", event.Raw.BlockNumber,
)
break
}

if lastBlockID != 0 && event.BlockId.Uint64() != lastBlockID+1 {
log.Warn(
"BlockProposedV2 event is not continuous, rescan the L1 chain",
Expand Down
17 changes: 17 additions & 0 deletions packages/taiko-client/pkg/config/protocol_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"

ontakeBindings "github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/ontake"
pacayaBindings "github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/pacaya"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils"
)

// Configs is an interface that provides Taiko protocol specific configurations.
Expand All @@ -24,6 +26,21 @@ type ProtocolConfigs interface {
MaxBlocksPerBatch() int
}

// ReportProtocolConfigs logs the protocol configurations.
func ReportProtocolConfigs(configs ProtocolConfigs) {
log.Info(
"Protocol configs",
"BaseFeeConfig", configs.BaseFeeConfig(),
"BlockMaxGasLimit", configs.BlockMaxGasLimit(),
"ForkHeightsOntake", configs.ForkHeightsOntake(),
"ForkHeightsPacaya", configs.ForkHeightsPacaya(),
"LivenessBond", utils.WeiToEther(configs.LivenessBond()),
"LivenessBondPerBlock", utils.WeiToEther(configs.LivenessBondPerBlock()),
"MaxProposals", configs.MaxProposals(),
"MaxBlocksPerBatch", configs.MaxBlocksPerBatch(),
)
}

// OntakeProtocolConfigs is the configuration for the Ontake fork protocol.
type OntakeProtocolConfigs struct {
configs *ontakeBindings.TaikoDataConfig
Expand Down
4 changes: 0 additions & 4 deletions packages/taiko-client/pkg/rpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,6 @@ func (c *Client) initForkHeightConfigs(ctx context.Context) error {
return nil
}

log.Info("Got protocol configs",
"ontakeForkHeight", protocolConfigs.ForkHeights.Ontake,
"pacayaForkHeight", protocolConfigs.ForkHeights.Pacaya,
)
// Otherwise, chain is after the Pacaya fork, just use the fork height numbers from the protocol configs.
c.OntakeClients.ForkHeight = protocolConfigs.ForkHeights.Ontake
c.PacayaClients.ForkHeight = protocolConfigs.ForkHeights.Pacaya
Expand Down
2 changes: 1 addition & 1 deletion packages/taiko-client/pkg/rpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var (
)

// GetProtocolConfigs gets the protocol configs from TaikoInbox or TaikoL2 contract.
func (c *Client) GetProtocolConfigs(opts *bind.CallOpts) (config.ProtocolConfigs, error) {
func (c *Client) GetProtocolConfigs(opts *bind.CallOpts) (cfg config.ProtocolConfigs, err error) {
var cancel context.CancelFunc
if opts == nil {
opts = &bind.CallOpts{Context: context.Background()}
Expand Down
43 changes: 36 additions & 7 deletions packages/taiko-client/pkg/rpc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ func GetBlockProofStatus(
}

// GetBatchesProofStatus checks whether the L2 blocks batch still needs a new proof.
// Here are the possible status:
// 1. No proof on chain at all.
// 2. An invalid proof has been submitted.
// 3. A valid proof has been submitted.
func GetBatchProofStatus(
ctx context.Context,
cli *Client,
Expand All @@ -225,13 +229,18 @@ func GetBatchProofStatus(
batch *pacayaBindings.ITaikoInboxBatch
err error
)
if batch, err = cli.GetBatchByID(ctx, new(big.Int).Sub(batchID, common.Big1)); err != nil {
return nil, err
}
if batchID.Uint64() == cli.PacayaClients.ForkHeight {
parentID = new(big.Int).Sub(batchID, common.Big1)
} else {
parentID = new(big.Int).SetUint64(batch.LastBlockId)
lastBatch, err := cli.GetBatchByID(ctx, new(big.Int).Sub(batchID, common.Big1))
if err != nil {
return nil, err
}
parentID = new(big.Int).SetUint64(lastBatch.LastBlockId)
}

if batch, err = cli.GetBatchByID(ctx, batchID); err != nil {
return nil, err
}

// Get the local L2 parent header.
Expand All @@ -241,11 +250,12 @@ func GetBatchProofStatus(
}

// Get the transition state from TaikoInbox contract.
if _, err = cli.PacayaClients.TaikoInbox.GetTransitionByParentHash(
transition, err := cli.PacayaClients.TaikoInbox.GetTransitionByParentHash(
&bind.CallOpts{Context: ctxWithTimeout},
batchID.Uint64(),
parent.Hash(),
); err != nil {
)
if err != nil {
if !strings.Contains(encoding.TryParsingCustomError(err).Error(), "TransitionNotFound") {
return nil, encoding.TryParsingCustomError(err)
}
Expand All @@ -254,7 +264,26 @@ func GetBatchProofStatus(
return &BlockProofStatus{IsSubmitted: false, ParentHeader: parent}, nil
}

// Status 2, a valid proof has been submitted.
lastHeaderInBatch, err := cli.L2.HeaderByNumber(ctxWithTimeout, new(big.Int).SetUint64(batch.LastBlockId))
if err != nil {
return nil, err
}
if transition.BlockHash != lastHeaderInBatch.Hash() ||
(transition.StateRoot != (common.Hash{}) && transition.StateRoot != lastHeaderInBatch.Root) {
log.Warn(
"Different block hash or state root detected, try submitting another proof",
"batchID", batchID,
"parent", parent.Hash().Hex(),
"localBlockHash", lastHeaderInBatch.Hash(),
"protocolTransitionBlockHash", common.BytesToHash(transition.BlockHash[:]),
"localStateRoot", lastHeaderInBatch.Root,
"protocolTransitionStateRoot", common.BytesToHash(transition.StateRoot[:]),
)
// Status 2, an invalid proof has been submitted.
return &BlockProofStatus{IsSubmitted: true, Invalid: true, ParentHeader: parent}, nil
}

// Status 3, a valid proof has been submitted.
return &BlockProofStatus{IsSubmitted: true, ParentHeader: parent}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion packages/taiko-client/proposer/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (p *Proposer) InitFromConfig(
if p.protocolConfigs, err = p.rpc.GetProtocolConfigs(&bind.CallOpts{Context: p.ctx}); err != nil {
return fmt.Errorf("failed to get protocol configs: %w", err)
}
log.Info("Protocol configs", "configs", p.protocolConfigs)
config.ReportProtocolConfigs(p.protocolConfigs)

if txMgr == nil {
if txMgr, err = txmgr.NewSimpleTxManager(
Expand Down
5 changes: 2 additions & 3 deletions packages/taiko-client/proposer/proposer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,8 @@ func (s *ProposerTestSuite) TestUpdateProposingTicker() {

func (s *ProposerTestSuite) TestProposeMultiBlobsInOneBatch() {
// Propose valid L2 blocks to make the L2 fork into Pacaya fork.
for i := 0; i < int(s.RPCClient.PacayaClients.ForkHeight); i++ {
s.ProposeAndInsertValidBlock(s.p, s.s)
}
s.ForkIntoPacaya(s.p, s.s)

l2Head1, err := s.RPCClient.L2.HeaderByNumber(context.Background(), nil)
s.Nil(err)
s.NotZero(l2Head1.Number.Uint64())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ func (h *AssignmentExpiredEventHandler) Handle(

// If there is no contester, we submit a contest to protocol.
go func() {
if meta.IsPacaya() {
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{Meta: meta}
return
}
if proofStatus.CurrentTransitionState.Contester == rpc.ZeroAddress && !h.isGuardian {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: meta.Ontake().GetBlockID(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (h *BlockProposedEventHandler) checkL1Reorg(
blockID *big.Int,
meta metadata.TaikoProposalMetaData,
) error {
log.Info("Check L1 reorg", "blockID", blockID)
log.Debug("Check L1 reorg", "blockID", blockID)
// Check whether the L2 EE's anchored L1 info, to see if the L1 chain has been reorged.
reorgCheckResult, err := h.rpc.CheckL1Reorg(
ctx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func (s *EventHandlerTestSuite) TestBlockProposedHandle() {
opts := &NewBlockProposedEventHandlerOps{
handler := NewBlockProposedEventHandler(&NewBlockProposedEventHandlerOps{
SharedState: &state.SharedState{},
ProverAddress: common.Address{},
RPC: s.RPCClient,
Expand All @@ -24,7 +24,6 @@ func (s *EventHandlerTestSuite) TestBlockProposedHandle() {
BackOffMaxRetrys: 5,
ContesterMode: true,
ProveUnassignedBlocks: true,
}
handler := NewBlockProposedEventHandler(opts)
})
s.Nil(handler.Handle(context.Background(), s.ProposeAndInsertValidBlock(s.proposer, s.blobSyncer), func() {}))
}
Loading