From 197ca8666b019c39d72ac4e612ca39135714d814 Mon Sep 17 00:00:00 2001 From: Hoon <48665813+sh-cha@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:19:17 +0900 Subject: [PATCH] bug fix starting from 1 when initializing tree (#52) * bug fix starting from 1 when initializing tree * change workingtree start leaf count * change workingtree start leaf count in force * print outputroot * error logs --- challenger/child/withdraw.go | 7 +- cmd/opinitd/db.go | 59 ++++++++++++- cmd/opinitd/root.go | 2 +- executor/child/withdraw.go | 7 +- executor/db.go | 165 +++++++++++++++++++++++++++++++++++ provider/child/child.go | 40 +++++---- 6 files changed, 250 insertions(+), 30 deletions(-) diff --git a/challenger/child/withdraw.go b/challenger/child/withdraw.go index 8509178..4776af4 100644 --- a/challenger/child/withdraw.go +++ b/challenger/child/withdraw.go @@ -47,12 +47,11 @@ func (ch *Child) handleInitiateWithdrawal(l2Sequence uint64, from string, to str } func (ch *Child) prepareTree(blockHeight int64) error { - if ch.InitializeTree(blockHeight) { - return nil - } - err := ch.Merkle().LoadWorkingTree(types.MustInt64ToUint64(blockHeight - 1)) if err == dbtypes.ErrNotFound { + if ch.InitializeTree(blockHeight) { + return nil + } // must not happened panic(fmt.Errorf("working tree not found at height: %d, current: %d", blockHeight-1, blockHeight)) } else if err != nil { diff --git a/cmd/opinitd/db.go b/cmd/opinitd/db.go index e7c51ab..852bf9f 100644 --- a/cmd/opinitd/db.go +++ b/cmd/opinitd/db.go @@ -1,24 +1,32 @@ package main import ( + "context" "fmt" + "time" "github.com/initia-labs/opinit-bots/bot" bottypes "github.com/initia-labs/opinit-bots/bot/types" "github.com/initia-labs/opinit-bots/db" "github.com/initia-labs/opinit-bots/executor" + executortypes "github.com/initia-labs/opinit-bots/executor/types" + "github.com/initia-labs/opinit-bots/node/rpcclient" + "github.com/initia-labs/opinit-bots/provider/child" + "github.com/initia-labs/opinit-bots/types" "github.com/spf13/cobra" ) -// migration015Cmd handles the one-time migration of withdrawal data for v0.1.5 +// migrationCmd handles the one-time migration of withdrawal data for v0.1.5, v0.1.9 // TODO: Remove this command in the future -func migration015Cmd(ctx *cmdContext) *cobra.Command { +func migrationCmd(ctx *cmdContext) *cobra.Command { cmd := &cobra.Command{ Use: "migrate", Args: cobra.ExactArgs(1), Short: "Run database migrations", Long: `Run database migrations v0.1.5: Store the sequence number so that it can be accessed by address +v0.1.9-1: Delete finalized trees and create new finalized trees from working trees +v0.1.9-2: Fill block hash of finalized tree `, RunE: func(cmd *cobra.Command, args []string) error { version := args[0] @@ -30,10 +38,57 @@ v0.1.5: Store the sequence number so that it can be accessed by address return err } return executor.Migration015(db) + case "v0.1.9-1": + // Run migration for v0.1.9-1 + db, err := db.NewDB(bot.GetDBPath(ctx.homePath, bottypes.BotTypeExecutor)) + if err != nil { + return err + } + return executor.Migration0191(db) + case "v0.1.9-2": + // Run migration for v0.1.9-2 + db, err := db.NewDB(bot.GetDBPath(ctx.homePath, bottypes.BotTypeExecutor)) + if err != nil { + return err + } + cmdCtx, done := context.WithCancel(cmd.Context()) + gracefulShutdown(done) + interval, err := cmd.Flags().GetDuration(flagPollingInterval) + if err != nil { + return err + } + cmdCtx = types.WithPollingInterval(cmdCtx, interval) + + configPath, err := getConfigPath(cmd, ctx.homePath, string(bottypes.BotTypeExecutor)) + if err != nil { + return err + } + + cfg := &executortypes.Config{} + err = bot.LoadJsonConfig(configPath, cfg) + if err != nil { + return err + } + + l2Config := cfg.L2NodeConfig(ctx.homePath) + broadcasterConfig := l2Config.BroadcasterConfig + cdc, _, err := child.GetCodec(broadcasterConfig.Bech32Prefix) + if err != nil { + return err + } + + rpcClient, err := rpcclient.NewRPCClient(cdc, l2Config.RPC) + if err != nil { + return err + } + + return executor.Migration0192(cmdCtx, db, rpcClient) default: return fmt.Errorf("unknown migration version: %s", version) } }, } + cmd = configFlag(ctx.v, cmd) + cmd.Flags().Duration(flagPollingInterval, 100*time.Millisecond, "Polling interval in milliseconds") return cmd } diff --git a/cmd/opinitd/root.go b/cmd/opinitd/root.go index c47e6eb..b2919ce 100644 --- a/cmd/opinitd/root.go +++ b/cmd/opinitd/root.go @@ -48,7 +48,7 @@ func NewRootCmd() *cobra.Command { resetDBCmd(ctx), resetHeightsCmd(ctx), resetHeightCmd(ctx), - migration015Cmd(ctx), + migrationCmd(ctx), txCmd(ctx), version.NewVersionCommand(), ) diff --git a/executor/child/withdraw.go b/executor/child/withdraw.go index ebf118a..0d63282 100644 --- a/executor/child/withdraw.go +++ b/executor/child/withdraw.go @@ -63,12 +63,11 @@ func (ch *Child) handleInitiateWithdrawal(l2Sequence uint64, from string, to str } func (ch *Child) prepareTree(blockHeight int64) error { - if ch.InitializeTree(blockHeight) { - return nil - } - err := ch.Merkle().LoadWorkingTree(types.MustInt64ToUint64(blockHeight) - 1) if err == dbtypes.ErrNotFound { + if ch.InitializeTree(blockHeight) { + return nil + } // must not happened panic(fmt.Errorf("working tree not found at height: %d, current: %d", blockHeight-1, blockHeight)) } else if err != nil { diff --git a/executor/db.go b/executor/db.go index 8de0202..fbcb14c 100644 --- a/executor/db.go +++ b/executor/db.go @@ -1,12 +1,19 @@ package executor import ( + "context" + "encoding/base64" "encoding/json" "fmt" + "math/bits" + "time" + ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" dbtypes "github.com/initia-labs/opinit-bots/db/types" executortypes "github.com/initia-labs/opinit-bots/executor/types" + merkletypes "github.com/initia-labs/opinit-bots/merkle/types" "github.com/initia-labs/opinit-bots/node" + "github.com/initia-labs/opinit-bots/node/rpcclient" "github.com/initia-labs/opinit-bots/types" "github.com/pkg/errors" ) @@ -72,3 +79,161 @@ func Migration015(db types.DB) error { return false, nil }) } + +func Migration0191(db types.DB) error { + nodeDB := db.WithPrefix([]byte(types.ChildName)) + merkleDB := nodeDB.WithPrefix([]byte(types.MerkleName)) + + err := merkleDB.PrefixedIterate(merkletypes.FinalizedTreeKey, nil, func(key, value []byte) (bool, error) { + var tree merkletypes.FinalizedTreeInfo + err := json.Unmarshal(value, &tree) + if err != nil { + return true, err + } + + err = merkleDB.Delete(key) + if err != nil { + return true, err + } + fmt.Printf("delete finalized tree index: %d, start leaf index: %d, leaf count: %d\n", tree.TreeIndex, tree.StartLeafIndex, tree.LeafCount) + return false, nil + }) + if err != nil { + return err + } + + nextSequence := uint64(1) + changeWorkingTree := false + err = merkleDB.PrefixedIterate(merkletypes.WorkingTreeKey, nil, func(key, value []byte) (bool, error) { + if len(key) != len(merkletypes.WorkingTreeKey)+1+8 { + return true, fmt.Errorf("unexpected working tree key; expected: %d; got: %d", len(merkletypes.WorkingTreeKey)+1+8, len(key)) + } + + version := dbtypes.ToUint64Key(key[len(key)-8:]) + + var workingTree merkletypes.TreeInfo + err := json.Unmarshal(value, &workingTree) + if err != nil { + return true, err + } + + if nextSequence != workingTree.StartLeafIndex { + changeWorkingTree = true + } + + if changeWorkingTree { + workingTree.StartLeafIndex = nextSequence + workingTreeBz, err := json.Marshal(workingTree) + if err != nil { + return true, err + } + err = merkleDB.Set(key, workingTreeBz) + if err != nil { + return true, err + } + } + + if workingTree.Done && workingTree.LeafCount != 0 { + data, err := json.Marshal(executortypes.TreeExtraData{ + BlockNumber: types.MustUint64ToInt64(version), + }) + if err != nil { + return true, err + } + + treeHeight := types.MustIntToUint8(bits.Len64(workingTree.LeafCount - 1)) + if workingTree.LeafCount <= 1 { + treeHeight = uint8(workingTree.LeafCount) //nolint + } + + treeRootHash := workingTree.LastSiblings[treeHeight] + finalizedTreeInfo := merkletypes.FinalizedTreeInfo{ + TreeIndex: workingTree.Index, + TreeHeight: treeHeight, + Root: treeRootHash, + StartLeafIndex: workingTree.StartLeafIndex, + LeafCount: workingTree.LeafCount, + ExtraData: data, + } + + finalizedTreeBz, err := json.Marshal(finalizedTreeInfo) + if err != nil { + return true, err + } + + err = merkleDB.Set(finalizedTreeInfo.Key(), finalizedTreeBz) + if err != nil { + return true, err + } + + fmt.Printf("finalized tree index: %d, start leaf index: %d, leaf count: %d, block height: %d\n", finalizedTreeInfo.TreeIndex, finalizedTreeInfo.StartLeafIndex, finalizedTreeInfo.LeafCount, version) + nextSequence = workingTree.StartLeafIndex + workingTree.LeafCount + } + return false, nil + }) + if err != nil { + return err + } + return nil +} + +func Migration0192(ctx context.Context, db types.DB, rpcClient *rpcclient.RPCClient) error { + nodeDB := db.WithPrefix([]byte(types.ChildName)) + merkleDB := nodeDB.WithPrefix([]byte(types.MerkleName)) + + timer := time.NewTicker(types.PollingInterval(ctx)) + defer timer.Stop() + + return merkleDB.PrefixedIterate(merkletypes.FinalizedTreeKey, nil, func(key, value []byte) (bool, error) { + var tree merkletypes.FinalizedTreeInfo + err := json.Unmarshal(value, &tree) + if err != nil { + return true, err + } + + var extraData executortypes.TreeExtraData + err = json.Unmarshal(tree.ExtraData, &extraData) + if err != nil { + return true, err + } + + if extraData.BlockHash != nil { + return false, nil + } + + for { + select { + case <-ctx.Done(): + return true, ctx.Err() + case <-timer.C: + } + height := extraData.BlockNumber + 1 + header, err := rpcClient.Header(ctx, &height) + if err != nil { + fmt.Printf("failed to get header for block height: %d; %s\n", height, err.Error()) + continue + } + + extraData.BlockHash = header.Header.LastBlockID.Hash + break + } + + tree.ExtraData, err = json.Marshal(extraData) + if err != nil { + return true, err + } + treeBz, err := json.Marshal(tree) + if err != nil { + return true, err + } + err = merkleDB.Set(key, treeBz) + if err != nil { + return true, err + } + outputRoot := ophosttypes.GenerateOutputRoot(1, tree.Root, extraData.BlockHash) + outputRootStr := base64.StdEncoding.EncodeToString(outputRoot[:]) + + fmt.Printf("finalized tree index: %d, start leaf index: %d, leaf count: %d, block height: %d, block hash: %X, outputRoot: %s\n", tree.TreeIndex, tree.StartLeafIndex, tree.LeafCount, extraData.BlockNumber, extraData.BlockHash, outputRootStr) + return false, nil + }) +} diff --git a/provider/child/child.go b/provider/child/child.go index 1399ce0..8715516 100644 --- a/provider/child/child.go +++ b/provider/child/child.go @@ -119,14 +119,28 @@ func (b *BaseChild) Initialize( var l2Sequence uint64 if b.node.HeightInitialized() { if !disableDeleteFutureWithdrawals { - l2Sequence, err = b.QueryNextL2Sequence(ctx, processedHeight) - if err != nil { - return 0, err - } + l2Sequence = 1 + if processedHeight != 0 { + l2Sequence, err = b.QueryNextL2Sequence(ctx, processedHeight) + if err != nil { + return 0, err + } - err = b.mk.DeleteFutureFinalizedTrees(l2Sequence) - if err != nil { - return 0, err + err = b.mk.DeleteFutureFinalizedTrees(l2Sequence) + if err != nil { + return 0, err + } + } + b.initializeTreeFn = func(blockHeight int64) (bool, error) { + if processedHeight+1 == blockHeight { + b.logger.Info("initialize tree", zap.Uint64("index", startOutputIndex)) + err := b.mk.InitializeWorkingTree(startOutputIndex, l2Sequence) + if err != nil { + return false, err + } + return true, nil + } + return false, nil } } @@ -135,18 +149,6 @@ func (b *BaseChild) Initialize( if err != nil { return 0, err } - - b.initializeTreeFn = func(blockHeight int64) (bool, error) { - if processedHeight+1 == blockHeight { - b.logger.Info("initialize tree", zap.Uint64("index", startOutputIndex)) - err := b.mk.InitializeWorkingTree(startOutputIndex, 1) - if err != nil { - return false, err - } - return true, nil - } - return false, nil - } } if b.OracleEnabled() && oracleKeyringConfig != nil {