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

Commit

Permalink
feat: update some logic and add more tests based on #216
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha committed May 16, 2023
1 parent a9151c7 commit b6c6f82
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 84 deletions.
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 {
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

0 comments on commit b6c6f82

Please sign in to comment.