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 1 commit
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
108 changes: 25 additions & 83 deletions driver/chain_syncer/calldata/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,24 +254,28 @@ 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 = s.lastInsertedBlockID
block *types.Block
err error
)

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 == nil && blockId.Cmp(common.Big0) == 0 {
Copy link
Contributor

@cyberhorsey cyberhorsey May 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blockId cant be both nil, and compared to 0? it should be || perhaps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to init blockID with current L2 head, and also changed && to ||

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 +285,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 +300,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()})
fcRes, err := s.rpc.L2Engine.ForkchoiceUpdate(ctx, &engine.ForkchoiceStateV1{HeadBlockHash: block.Hash()}, nil)
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,
)
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