Skip to content
This repository has been archived by the owner on May 11, 2024. It is now read-only.

feat: update some logic and add more tests based on #216 #228

Merged
merged 4 commits into from
May 16, 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
114 changes: 31 additions & 83 deletions driver/chain_syncer/calldata/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,24 +254,34 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB
)

// rewind chain by 1 until we find a block that is still in the chain
var lastKnownGoodBlockId *big.Int
var blockId *big.Int = s.lastInsertedBlockID
var block *types.Block
var err error
var (
lastKnownGoodBlockId *big.Int
blockId *big.Int
block *types.Block
err error
)

l2Head, err := s.rpc.L2.BlockByNumber(ctx, nil)
if err != nil {
return err
}
blockId = l2Head.Number()

stateVars, err := s.rpc.GetProtocolStateVariables(nil)
if err != nil {
return fmt.Errorf("failed to get state variables: %w", err)
}

for {
if blockId.Cmp(big.NewInt(0)) == 0 {
lastKnownGoodBlockId = new(big.Int).SetUint64(0)
if blockId.Cmp(common.Big0) == 0 {
if block, err = s.rpc.L2.BlockByNumber(ctx, common.Big0); err != nil {
return err
}
lastKnownGoodBlockId = common.Big0
break
}

block, err = s.rpc.L2.BlockByNumber(ctx, blockId)
if err != nil && !errors.Is(err, ethereum.NotFound) {
if block, err = s.rpc.L2.BlockByNumber(ctx, blockId); err != nil && !errors.Is(err, ethereum.NotFound) {
return err
}

Expand All @@ -281,7 +291,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB
break
} else {
// otherwise, sub 1 from blockId and try again
blockId = new(big.Int).Sub(s.lastInsertedBlockID, big.NewInt(1))
blockId = new(big.Int).Sub(blockId, common.Big1)
}
}

Expand All @@ -296,91 +306,29 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB
"blockID", lastKnownGoodBlockId,
)

var parentBlockInfo *ParentBlockInfo

if lastKnownGoodBlockId.Cmp(common.Big0) == 0 {
parentBlockInfo = &ParentBlockInfo{
Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
Number: big.NewInt(0),
GasUsed: 0,
}
} else {
parent, err := s.rpc.L2ParentByBlockId(ctx, lastKnownGoodBlockId)
if err != nil {
return fmt.Errorf("error getting l2 parent by block id: %w", err)
}

parentBlockInfo = &ParentBlockInfo{
Hash: parent.Hash(),
Number: parent.Number,
GasUsed: parent.GasUsed,
}
}

// reset l1 current to when the last known good block was inserted, and return the event.
blockProposedEvent, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{Height: block.Number()})
if err != nil {
return fmt.Errorf("failed to reset l1 current: %w", err)
}

tx, err := s.rpc.L1.TransactionInBlock(
ctx,
block.Hash(),
blockProposedEvent.Raw.TxIndex,
)
fcRes, err := s.rpc.L2Engine.ForkchoiceUpdate(ctx, &engine.ForkchoiceStateV1{HeadBlockHash: block.Hash()}, nil)
if err != nil {
return fmt.Errorf("failed to fetch original TaikoL1.proposeBlock transaction: %w", err)
}

txListBytes, hint, _, err := s.txListValidator.ValidateTxList(block.Number(), tx.Data())
if err != nil {
return fmt.Errorf("failed to validate transactions list: %w", err)
}

if hint != txListValidator.HintOK {
log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", block.NumberU64())
txListBytes = []byte{}
}

l1Origin := &rawdb.L1Origin{
BlockID: block.Number(),
L2BlockHash: common.Hash{}, // Will be set by taiko-geth.
L1BlockHeight: new(big.Int).SetUint64(blockProposedEvent.Raw.BlockNumber),
L1BlockHash: blockProposedEvent.Raw.BlockHash,
return err
}

payloadData, rpcError, payloadError := s.insertNewHead(
ctx,
blockProposedEvent,
parentBlockInfo,
s.state.GetHeadBlockID(),
txListBytes,
l1Origin,
)

if rpcError != nil {
return fmt.Errorf("failed to insert new head to L2 execution engine: %w", rpcError)
if fcRes.PayloadStatus.Status != engine.VALID {
return fmt.Errorf("unexpected ForkchoiceUpdate response status: %s", fcRes.PayloadStatus.Status)
}

if payloadError != nil {
log.Warn(
"Ignore invalid block context", "blockID", event.Id, "payloadError", payloadError, "payloadData", payloadData,
)
return nil
// reset l1 current to when the last known good block was inserted, and return the event.
if _, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{ID: lastKnownGoodBlockId}); err != nil {
return fmt.Errorf("failed to reset L1 current: %w", err)
}

log.Debug("Payload data", "hash", payloadData.BlockHash, "txs", len(payloadData.Transactions))

log.Info(
"🔗 Rewound chain and inserted last known good block as new head",
"blockID", event.Id,
"height", payloadData.Number,
"hash", payloadData.BlockHash,
"height", block.Number(),
"hash", block.Hash(),
"latestVerifiedBlockHeight", s.state.GetLatestVerifiedBlock().Height,
"latestVerifiedBlockHash", s.state.GetLatestVerifiedBlock().Hash,
"transactions", len(payloadData.Transactions),
"baseFee", payloadData.BaseFeePerGas,
"withdrawals", len(payloadData.Withdrawals),
"transactions", len(block.Transactions()),
"baseFee", block.BaseFee(),
"withdrawals", len(block.Withdrawals()),
)

metrics.DriverL1CurrentHeightGauge.Update(int64(event.Raw.BlockNumber))
Expand Down
64 changes: 64 additions & 0 deletions driver/chain_syncer/calldata/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/suite"
"github.com/taikoxyz/taiko-client/bindings"
"github.com/taikoxyz/taiko-client/driver/chain_syncer/beaconsync"
"github.com/taikoxyz/taiko-client/driver/state"
"github.com/taikoxyz/taiko-client/proposer"
"github.com/taikoxyz/taiko-client/testutils"
)

type CalldataSyncerTestSuite struct {
testutils.ClientTestSuite
s *Syncer
p testutils.Proposer
}

func (s *CalldataSyncerTestSuite) SetupTest() {
Expand All @@ -36,6 +40,22 @@ func (s *CalldataSyncerTestSuite) SetupTest() {
)
s.Nil(err)
s.s = syncer

prop := new(proposer.Proposer)
l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY")))
s.Nil(err)
proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests
s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{
L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"),
L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"),
TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")),
TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")),
L1ProposerPrivKey: l1ProposerPrivKey,
L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")),
ProposeInterval: &proposeInterval,
})))

s.p = prop
}

func (s *CalldataSyncerTestSuite) TestProcessL1Blocks() {
Expand Down Expand Up @@ -86,6 +106,50 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() {
s.Nil(payloadErr)
}

func (s *CalldataSyncerTestSuite) TestHandleReorgToGenesis() {
testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s)

l2Head1, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Greater(l2Head1.NumberU64(), uint64(0))
s.NotZero(s.s.lastInsertedBlockID.Uint64())
s.s.lastInsertedBlockID = common.Big0 // let the chain reorg to genesis

s.Nil(s.s.handleReorg(context.Background(), &bindings.TaikoL1ClientBlockProposed{
Id: l2Head1.Number(),
Raw: types.Log{Removed: true},
}))

l2Head2, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Equal(uint64(0), l2Head2.NumberU64())
}

func (s *CalldataSyncerTestSuite) TestHandleReorgToNoneGenesis() {
testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s)

l2Head1, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Greater(l2Head1.NumberU64(), uint64(0))
s.NotZero(s.s.lastInsertedBlockID.Uint64())
s.s.lastInsertedBlockID = common.Big1 // let the chain reorg to height 1

s.Nil(s.s.handleReorg(context.Background(), &bindings.TaikoL1ClientBlockProposed{
Id: l2Head1.Number(),
Raw: types.Log{Removed: true},
}))

l2Head2, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Equal(uint64(1), l2Head2.NumberU64())

testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s)
l2Head3, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)
s.Greater(l2Head3.NumberU64(), l2Head2.NumberU64())
s.Greater(s.s.lastInsertedBlockID.Uint64(), uint64(1))
}

func TestCalldataSyncerTestSuite(t *testing.T) {
suite.Run(t, new(CalldataSyncerTestSuite))
}
2 changes: 1 addition & 1 deletion integration_test/nodes/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:
- "0.0.0.0"

l2_execution_engine:
image: gcr.io/evmchain/taiko-geth:taiko
image: gcr.io/evmchain/taiko-geth:sha-f8be24a # TODO: change back to taiko tag
restart: unless-stopped
pull_policy: always
volumes:
Expand Down