diff --git a/bridge-history-api/go.mod b/bridge-history-api/go.mod index 7d7349824e..d4060f23c6 100644 --- a/bridge-history-api/go.mod +++ b/bridge-history-api/go.mod @@ -9,9 +9,11 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 github.com/modern-go/reflect2 v1.0.2 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.7.0 github.com/stretchr/testify v1.8.3 github.com/urfave/cli/v2 v2.25.7 + golang.org/x/sync v0.3.0 gorm.io/driver/postgres v1.5.0 gorm.io/gorm v1.25.2 ) @@ -117,7 +119,6 @@ require ( golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect golang.org/x/net v0.14.0 // indirect - golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/bridge-history-api/go.sum b/bridge-history-api/go.sum index 2bd3b8896d..142cd0e718 100644 --- a/bridge-history-api/go.sum +++ b/bridge-history-api/go.sum @@ -362,6 +362,8 @@ github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= diff --git a/bridge-history-api/internal/controller/history_controller.go b/bridge-history-api/internal/controller/history_controller.go index 02627962df..be6ccd61c7 100644 --- a/bridge-history-api/internal/controller/history_controller.go +++ b/bridge-history-api/internal/controller/history_controller.go @@ -1,23 +1,38 @@ package controller import ( + "errors" + "reflect" + "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" "github.com/gin-gonic/gin" + "github.com/patrickmn/go-cache" + "golang.org/x/sync/singleflight" "gorm.io/gorm" "bridge-history-api/internal/logic" "bridge-history-api/internal/types" ) +const ( + cacheKeyPrefixClaimableTxsByAddr = "claimableTxsByAddr:" + cacheKeyPrefixQueryTxsByHash = "queryTxsByHash:" +) + // HistoryController contains the query claimable txs service type HistoryController struct { historyLogic *logic.HistoryLogic + cache *cache.Cache + singleFlight singleflight.Group } // NewHistoryController return HistoryController instance func NewHistoryController(db *gorm.DB) *HistoryController { return &HistoryController{ historyLogic: logic.NewHistoryLogic(db), + cache: cache.New(30*time.Second, 10*time.Minute), } } @@ -28,32 +43,38 @@ func (c *HistoryController) GetAllClaimableTxsByAddr(ctx *gin.Context) { types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) return } - offset := (req.Page - 1) * req.PageSize - limit := req.PageSize - txs, total, err := c.historyLogic.GetClaimableTxsByAddress(ctx, common.HexToAddress(req.Address), offset, limit) - if err != nil { - types.RenderFailure(ctx, types.ErrGetClaimablesFailure, err) - return + + cacheKey := cacheKeyPrefixClaimableTxsByAddr + req.Address + if cachedData, found := c.cache.Get(cacheKey); found { + if resultData, ok := cachedData.(*types.ResultData); ok { + types.RenderSuccess(ctx, resultData) + return + } + // Log error for unexpected type, then fetch data from the database. + log.Error("unexpected type in cache", "expected", "*types.ResultData", "got", reflect.TypeOf(cachedData)) } - types.RenderSuccess(ctx, &types.ResultData{Result: txs, Total: total}) -} + result, err, _ := c.singleFlight.Do(cacheKey, func() (interface{}, error) { + txs, total, err := c.historyLogic.GetClaimableTxsByAddress(ctx, common.HexToAddress(req.Address)) + if err != nil { + return nil, err + } + resultData := &types.ResultData{Result: txs, Total: total} + c.cache.Set(cacheKey, resultData, cache.DefaultExpiration) + return resultData, nil + }) -// GetAllTxsByAddr defines the http get method behavior -func (c *HistoryController) GetAllTxsByAddr(ctx *gin.Context) { - var req types.QueryByAddressRequest - if err := ctx.ShouldBind(&req); err != nil { - types.RenderJSON(ctx, types.ErrParameterInvalidNo, err, nil) - return - } - offset := (req.Page - 1) * req.PageSize - limit := req.PageSize - message, total, err := c.historyLogic.GetTxsByAddress(ctx, common.HexToAddress(req.Address), offset, limit) if err != nil { - types.RenderFailure(ctx, types.ErrGetTxsByAddrFailure, err) + types.RenderFailure(ctx, types.ErrGetClaimablesFailure, err) return } - types.RenderSuccess(ctx, &types.ResultData{Result: message, Total: total}) + + if resultData, ok := result.(*types.ResultData); ok { + types.RenderSuccess(ctx, resultData) + } else { + log.Error("unexpected type from singleflight", "expected", "*types.ResultData", "got", reflect.TypeOf(result)) + types.RenderFailure(ctx, types.ErrGetClaimablesFailure, errors.New("unexpected error")) + } } // PostQueryTxsByHash defines the http post method behavior @@ -63,10 +84,48 @@ func (c *HistoryController) PostQueryTxsByHash(ctx *gin.Context) { types.RenderFailure(ctx, types.ErrParameterInvalidNo, err) return } - result, err := c.historyLogic.GetTxsByHashes(ctx, req.Txs) - if err != nil { - types.RenderFailure(ctx, types.ErrGetTxsByHashFailure, err) + + if len(req.Txs) > 10 { + types.RenderFailure(ctx, types.ErrParameterInvalidNo, errors.New("the number of hashes in the request exceeds the allowed maximum")) return } - types.RenderSuccess(ctx, &types.ResultData{Result: result, Total: 0}) + hashesMap := make(map[string]struct{}, len(req.Txs)) + results := make([]*types.TxHistoryInfo, 0, len(req.Txs)) + uncachedHashes := make([]string, 0, len(req.Txs)) + for _, hash := range req.Txs { + if _, exists := hashesMap[hash]; exists { + // Skip duplicate tx hash values. + continue + } + hashesMap[hash] = struct{}{} + + cacheKey := cacheKeyPrefixQueryTxsByHash + hash + if cachedData, found := c.cache.Get(cacheKey); found { + if txInfo, ok := cachedData.(*types.TxHistoryInfo); ok { + results = append(results, txInfo) + } else { + log.Error("unexpected type in cache", "expected", "*types.TxHistoryInfo", "got", reflect.TypeOf(cachedData)) + uncachedHashes = append(uncachedHashes, hash) + } + } else { + uncachedHashes = append(uncachedHashes, hash) + } + } + + if len(uncachedHashes) > 0 { + dbResults, err := c.historyLogic.GetTxsByHashes(ctx, uncachedHashes) + if err != nil { + types.RenderFailure(ctx, types.ErrGetTxsByHashFailure, err) + return + } + + for _, result := range dbResults { + results = append(results, result) + cacheKey := cacheKeyPrefixQueryTxsByHash + result.Hash + c.cache.Set(cacheKey, result, cache.DefaultExpiration) + } + } + + resultData := &types.ResultData{Result: results, Total: uint64(len(results))} + types.RenderSuccess(ctx, resultData) } diff --git a/bridge-history-api/internal/logic/history_logic.go b/bridge-history-api/internal/logic/history_logic.go index a326a5499b..6bbf48ab34 100644 --- a/bridge-history-api/internal/logic/history_logic.go +++ b/bridge-history-api/internal/logic/history_logic.go @@ -23,63 +23,102 @@ func NewHistoryLogic(db *gorm.DB) *HistoryLogic { return logic } -// getCrossTxClaimInfo get UserClaimInfos by address -func getCrossTxClaimInfo(ctx context.Context, msgHash string, db *gorm.DB) *types.UserClaimInfo { +// updateL2TxClaimInfo updates UserClaimInfos for each transaction history. +func updateL2TxClaimInfo(ctx context.Context, txHistories []*types.TxHistoryInfo, db *gorm.DB) { l2SentMsgOrm := orm.NewL2SentMsg(db) rollupOrm := orm.NewRollupBatch(db) - l2sentMsg, err := l2SentMsgOrm.GetL2SentMsgByHash(ctx, msgHash) - if err != nil || l2sentMsg == nil { - log.Debug("getCrossTxClaimInfo failed", "error", err) - return &types.UserClaimInfo{} + + var l2MsgHashes []string + for _, txHistory := range txHistories { + if !txHistory.IsL1 { + l2MsgHashes = append(l2MsgHashes, txHistory.MsgHash) + } + } + + l2sentMsgs, err := l2SentMsgOrm.GetL2SentMsgsByHashes(ctx, l2MsgHashes) + if err != nil || len(l2sentMsgs) == 0 { + log.Debug("GetL2SentMsgsByHashes failed", "l2 sent msgs", l2sentMsgs, "error", err) + return + } + + l2MsgMap := make(map[string]*orm.L2SentMsg, len(l2sentMsgs)) + var batchIndexes []uint64 + for _, l2sentMsg := range l2sentMsgs { + l2MsgMap[l2sentMsg.MsgHash] = l2sentMsg + batchIndexes = append(batchIndexes, l2sentMsg.BatchIndex) } - batch, err := rollupOrm.GetRollupBatchByIndex(ctx, l2sentMsg.BatchIndex) + + batches, err := rollupOrm.GetRollupBatchesByIndexes(ctx, batchIndexes) if err != nil { - log.Debug("getCrossTxClaimInfo failed", "error", err) - return &types.UserClaimInfo{} + log.Debug("GetRollupBatchesByIndexes failed", "error", err) + return } - return &types.UserClaimInfo{ - From: l2sentMsg.Sender, - To: l2sentMsg.Target, - Value: l2sentMsg.Value, - Nonce: strconv.FormatUint(l2sentMsg.Nonce, 10), - Message: l2sentMsg.MsgData, - Proof: "0x" + l2sentMsg.MsgProof, - BatchHash: batch.BatchHash, - BatchIndex: strconv.FormatUint(l2sentMsg.BatchIndex, 10), + + batchMap := make(map[uint64]*orm.RollupBatch, len(batches)) + for _, batch := range batches { + batchMap[batch.BatchIndex] = batch } + for _, txHistory := range txHistories { + if txHistory.IsL1 { + continue + } + + l2sentMsg, foundL2SentMsg := l2MsgMap[txHistory.MsgHash] + batch, foundBatch := batchMap[l2sentMsg.BatchIndex] + if foundL2SentMsg && foundBatch { + txHistory.ClaimInfo = &types.UserClaimInfo{ + From: l2sentMsg.Sender, + To: l2sentMsg.Target, + Value: l2sentMsg.Value, + Nonce: strconv.FormatUint(l2sentMsg.Nonce, 10), + Message: l2sentMsg.MsgData, + Proof: "0x" + l2sentMsg.MsgProof, + BatchHash: batch.BatchHash, + BatchIndex: strconv.FormatUint(l2sentMsg.BatchIndex, 10), + } + } + } } -func updateCrossTxHash(ctx context.Context, msgHash string, txInfo *types.TxHistoryInfo, db *gorm.DB) { - relayed := orm.NewRelayedMsg(db) - relayed, err := relayed.GetRelayedMsgByHash(ctx, msgHash) - if err != nil { - log.Debug("updateCrossTxHash failed", "error", err) - return +func updateCrossTxHashes(ctx context.Context, txHistories []*types.TxHistoryInfo, db *gorm.DB) { + msgHashes := make([]string, len(txHistories)) + for i, txHistory := range txHistories { + msgHashes[i] = txHistory.MsgHash } - if relayed == nil { + + relayed := orm.NewRelayedMsg(db) + relayedMsgs, err := relayed.GetRelayedMsgsByHashes(ctx, msgHashes) + if err != nil || len(relayedMsgs) == 0 { + log.Debug("GetRelayedMsgsByHashes failed", "msg hashes", msgHashes, "relayed msgs", relayedMsgs, "error", err) return } - if relayed.Layer1Hash != "" { - txInfo.FinalizeTx.Hash = relayed.Layer1Hash - txInfo.FinalizeTx.BlockNumber = relayed.Height - return + + relayedMsgMap := make(map[string]*orm.RelayedMsg, len(relayedMsgs)) + for _, relayedMsg := range relayedMsgs { + relayedMsgMap[relayedMsg.MsgHash] = relayedMsg } - if relayed.Layer2Hash != "" { - txInfo.FinalizeTx.Hash = relayed.Layer2Hash - txInfo.FinalizeTx.BlockNumber = relayed.Height - return + + for _, txHistory := range txHistories { + if relayedMsg, found := relayedMsgMap[txHistory.MsgHash]; found { + txHistory.FinalizeTx.Hash = relayedMsg.Layer1Hash + relayedMsg.Layer2Hash + txHistory.FinalizeTx.BlockNumber = relayedMsg.Height + } } +} +func updateCrossTxHashesAndL2TxClaimInfo(ctx context.Context, txHistories []*types.TxHistoryInfo, db *gorm.DB) { + updateCrossTxHashes(ctx, txHistories, db) + updateL2TxClaimInfo(ctx, txHistories, db) } // GetClaimableTxsByAddress get all claimable txs under given address -func (h *HistoryLogic) GetClaimableTxsByAddress(ctx context.Context, address common.Address, offset int, limit int) ([]*types.TxHistoryInfo, uint64, error) { +func (h *HistoryLogic) GetClaimableTxsByAddress(ctx context.Context, address common.Address) ([]*types.TxHistoryInfo, uint64, error) { var txHistories []*types.TxHistoryInfo l2SentMsgOrm := orm.NewL2SentMsg(h.db) l2CrossMsgOrm := orm.NewCrossMsg(h.db) - total, results, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddressWithOffset(ctx, address.Hex(), offset, limit) - if err != nil || total == 0 || len(results) == 0 { + results, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddress(ctx, address.Hex()) + if err != nil || len(results) == 0 { return txHistories, 0, err } var msgHashList []string @@ -98,10 +137,10 @@ func (h *HistoryLogic) GetClaimableTxsByAddress(ctx context.Context, address com for _, result := range results { txInfo := &types.TxHistoryInfo{ Hash: result.TxHash, + MsgHash: result.MsgHash, IsL1: false, BlockNumber: result.Height, FinalizeTx: &types.Finalized{}, - ClaimInfo: getCrossTxClaimInfo(ctx, result.MsgHash, h.db), } if crossMsg, exist := crossMsgMap[result.MsgHash]; exist { txInfo.Amount = crossMsg.Amount @@ -113,96 +152,36 @@ func (h *HistoryLogic) GetClaimableTxsByAddress(ctx context.Context, address com } txHistories = append(txHistories, txInfo) } - return txHistories, total, err + updateL2TxClaimInfo(ctx, txHistories, h.db) + return txHistories, uint64(len(results)), err } -// GetTxsByAddress get all txs under given address -func (h *HistoryLogic) GetTxsByAddress(ctx context.Context, address common.Address, offset int, limit int) ([]*types.TxHistoryInfo, uint64, error) { - var txHistories []*types.TxHistoryInfo - utilOrm := orm.NewCrossMsg(h.db) - total, err := utilOrm.GetTotalCrossMsgCountByAddress(ctx, address.String()) - if err != nil || total == 0 { - return txHistories, 0, err - } - result, err := utilOrm.GetCrossMsgsByAddressWithOffset(ctx, address.String(), offset, limit) - +// GetTxsByHashes get tx infos under given tx hashes +func (h *HistoryLogic) GetTxsByHashes(ctx context.Context, hashes []string) ([]*types.TxHistoryInfo, error) { + CrossMsgOrm := orm.NewCrossMsg(h.db) + results, err := CrossMsgOrm.GetCrossMsgsByHashes(ctx, hashes) if err != nil { - return nil, 0, err + return nil, err } - for _, msg := range result { + + var txHistories []*types.TxHistoryInfo + for _, result := range results { txHistory := &types.TxHistoryInfo{ - Hash: msg.Layer1Hash + msg.Layer2Hash, - Amount: msg.Amount, - To: msg.Target, - L1Token: msg.Layer1Token, - L2Token: msg.Layer2Token, - IsL1: msg.MsgType == int(orm.Layer1Msg), - BlockNumber: msg.Height, - BlockTimestamp: msg.Timestamp, - CreatedAt: msg.CreatedAt, - FinalizeTx: &types.Finalized{ - Hash: "", - }, - ClaimInfo: getCrossTxClaimInfo(ctx, msg.MsgHash, h.db), + Hash: result.Layer1Hash + result.Layer2Hash, + MsgHash: result.MsgHash, + Amount: result.Amount, + To: result.Target, + L1Token: result.Layer1Token, + L2Token: result.Layer2Token, + IsL1: orm.MsgType(result.MsgType) == orm.Layer1Msg, + BlockNumber: result.Height, + BlockTimestamp: result.Timestamp, + CreatedAt: result.CreatedAt, + FinalizeTx: &types.Finalized{Hash: ""}, } - updateCrossTxHash(ctx, msg.MsgHash, txHistory, h.db) txHistories = append(txHistories, txHistory) } - return txHistories, total, nil -} -// GetTxsByHashes get tx infos under given tx hashes -func (h *HistoryLogic) GetTxsByHashes(ctx context.Context, hashes []string) ([]*types.TxHistoryInfo, error) { - txHistories := make([]*types.TxHistoryInfo, 0) - CrossMsgOrm := orm.NewCrossMsg(h.db) - for _, hash := range hashes { - l1result, err := CrossMsgOrm.GetL1CrossMsgByHash(ctx, common.HexToHash(hash)) - if err != nil { - return nil, err - } - if l1result != nil { - txHistory := &types.TxHistoryInfo{ - Hash: l1result.Layer1Hash, - Amount: l1result.Amount, - To: l1result.Target, - IsL1: true, - L1Token: l1result.Layer1Token, - L2Token: l1result.Layer2Token, - BlockNumber: l1result.Height, - BlockTimestamp: l1result.Timestamp, - CreatedAt: l1result.CreatedAt, - FinalizeTx: &types.Finalized{ - Hash: "", - }, - } - updateCrossTxHash(ctx, l1result.MsgHash, txHistory, h.db) - txHistories = append(txHistories, txHistory) - continue - } - l2result, err := CrossMsgOrm.GetL2CrossMsgByHash(ctx, common.HexToHash(hash)) - if err != nil { - return nil, err - } - if l2result != nil { - txHistory := &types.TxHistoryInfo{ - Hash: l2result.Layer2Hash, - Amount: l2result.Amount, - To: l2result.Target, - IsL1: false, - L1Token: l2result.Layer1Token, - L2Token: l2result.Layer2Token, - BlockNumber: l2result.Height, - BlockTimestamp: l2result.Timestamp, - CreatedAt: l2result.CreatedAt, - FinalizeTx: &types.Finalized{ - Hash: "", - }, - ClaimInfo: getCrossTxClaimInfo(ctx, l2result.MsgHash, h.db), - } - updateCrossTxHash(ctx, l2result.MsgHash, txHistory, h.db) - txHistories = append(txHistories, txHistory) - continue - } - } + updateCrossTxHashesAndL2TxClaimInfo(ctx, txHistories, h.db) return txHistories, nil } diff --git a/bridge-history-api/internal/route/route.go b/bridge-history-api/internal/route/route.go index cadf0328e0..2dae42a81a 100644 --- a/bridge-history-api/internal/route/route.go +++ b/bridge-history-api/internal/route/route.go @@ -21,10 +21,8 @@ func Route(router *gin.Engine, conf *config.Config) { })) r := router.Group("api/") - r.GET("/txs", controller.HistoryCtrler.GetAllTxsByAddr) r.POST("/txsbyhashes", controller.HistoryCtrler.PostQueryTxsByHash) r.GET("/claimable", controller.HistoryCtrler.GetAllClaimableTxsByAddr) - r.GET("/withdraw_root", controller.BatchCtrler.GetWithdrawRootByBatchIndex) r.GET("/health", controller.HealthCheck.HealthCheck) r.GET("/ready", controller.Ready.Ready) } diff --git a/bridge-history-api/internal/types/history_types.go b/bridge-history-api/internal/types/history_types.go index 2f690dd123..38007a89af 100644 --- a/bridge-history-api/internal/types/history_types.go +++ b/bridge-history-api/internal/types/history_types.go @@ -27,8 +27,8 @@ const ( // QueryByAddressRequest the request parameter of address api type QueryByAddressRequest struct { Address string `form:"address" binding:"required"` - Page int `form:"page" binding:"required"` - PageSize int `form:"page_size" binding:"required"` + Page int `form:"page,default=1"` + PageSize int `form:"page_size,default=10"` } // QueryByHashRequest the request parameter of hash api @@ -80,6 +80,7 @@ type UserClaimInfo struct { // TxHistoryInfo the schema of tx history infos type TxHistoryInfo struct { Hash string `json:"hash"` + MsgHash string `json:"msgHash"` Amount string `json:"amount"` To string `json:"to"` // useless IsL1 bool `json:"isL1"` diff --git a/bridge-history-api/orm/batch.go b/bridge-history-api/orm/batch.go index b2a18628f4..4bee91db96 100644 --- a/bridge-history-api/orm/batch.go +++ b/bridge-history-api/orm/batch.go @@ -71,6 +71,16 @@ func (r *RollupBatch) GetRollupBatchByIndex(ctx context.Context, index uint64) ( return &result, nil } +// GetRollupBatchesByIndexes return the rollup batches by indexes +func (r *RollupBatch) GetRollupBatchesByIndexes(ctx context.Context, indexes []uint64) ([]*RollupBatch, error) { + var results []*RollupBatch + err := r.db.WithContext(ctx).Model(&RollupBatch{}).Where("batch_index IN (?)", indexes).Find(&results).Error + if err != nil { + return nil, fmt.Errorf("RollupBatch.GetRollupBatchesByIndexes error: %w", err) + } + return results, nil +} + // InsertRollupBatch batch insert rollup batch into db and return the transaction func (r *RollupBatch) InsertRollupBatch(ctx context.Context, batches []*RollupBatch, dbTx ...*gorm.DB) error { if len(batches) == 0 { diff --git a/bridge-history-api/orm/cross_msg.go b/bridge-history-api/orm/cross_msg.go index a89c4e6661..b989720d51 100644 --- a/bridge-history-api/orm/cross_msg.go +++ b/bridge-history-api/orm/cross_msg.go @@ -368,3 +368,14 @@ func (c *CrossMsg) GetCrossMsgsByAddressWithOffset(ctx context.Context, sender s } return messages, nil } + +// GetCrossMsgsByHashes retrieves a list of cross messages identified by their Layer 1 or Layer 2 hashes. +func (c *CrossMsg) GetCrossMsgsByHashes(ctx context.Context, hashes []string) ([]*CrossMsg, error) { + var results []*CrossMsg + err := c.db.WithContext(ctx).Model(&CrossMsg{}).Where("layer1_hash IN (?) OR layer2_hash IN (?)", hashes, hashes).Find(&results).Error + if err != nil { + return nil, fmt.Errorf("CrossMsg.GetCrossMsgsByHashes error: %w", err) + } + + return results, nil +} diff --git a/bridge-history-api/orm/l2_sent_msg.go b/bridge-history-api/orm/l2_sent_msg.go index be9b0d4c89..5717b67259 100644 --- a/bridge-history-api/orm/l2_sent_msg.go +++ b/bridge-history-api/orm/l2_sent_msg.go @@ -55,6 +55,19 @@ func (l *L2SentMsg) GetL2SentMsgByHash(ctx context.Context, msgHash string) (*L2 return &result, nil } +// GetL2SentMsgsByHashes get l2 sent msgs by hashes +func (l *L2SentMsg) GetL2SentMsgsByHashes(ctx context.Context, msgHashes []string) ([]*L2SentMsg, error) { + var results []*L2SentMsg + err := l.db.WithContext(ctx).Model(&L2SentMsg{}). + Where("msg_hash IN (?)", msgHashes). + Find(&results). + Error + if err != nil { + return nil, fmt.Errorf("L2SentMsg.GetL2SentMsgsByHashes error: %w", err) + } + return results, nil +} + // GetLatestSentMsgHeightOnL2 get latest sent msg height on l2 func (l *L2SentMsg) GetLatestSentMsgHeightOnL2(ctx context.Context) (uint64, error) { var result L2SentMsg @@ -73,9 +86,9 @@ func (l *L2SentMsg) GetLatestSentMsgHeightOnL2(ctx context.Context) (uint64, err return result.Height, nil } -// GetClaimableL2SentMsgByAddressWithOffset returns both the total number of unclaimed messages and a paginated list of those messages. +// GetClaimableL2SentMsgByAddress returns both the total number of unclaimed messages and a paginated list of those messages. // TODO: Add metrics about the result set sizes (total/claimed/unclaimed messages). -func (l *L2SentMsg) GetClaimableL2SentMsgByAddressWithOffset(ctx context.Context, address string, offset int, limit int) (uint64, []*L2SentMsg, error) { +func (l *L2SentMsg) GetClaimableL2SentMsgByAddress(ctx context.Context, address string) ([]*L2SentMsg, error) { var totalMsgs []*L2SentMsg db := l.db.WithContext(ctx) db = db.Table("l2_sent_msg") @@ -85,7 +98,7 @@ func (l *L2SentMsg) GetClaimableL2SentMsgByAddressWithOffset(ctx context.Context db = db.Order("id DESC") tx := db.Find(&totalMsgs) if tx.Error != nil || tx.RowsAffected == 0 { - return 0, nil, tx.Error + return nil, tx.Error } // Note on the use of IN vs VALUES in SQL Queries: @@ -113,7 +126,7 @@ func (l *L2SentMsg) GetClaimableL2SentMsgByAddressWithOffset(ctx context.Context db = db.Where(fmt.Sprintf("msg_hash IN (VALUES %s)", valuesStr)) db = db.Where("deleted_at IS NULL") if err := db.Pluck("msg_hash", &claimedMsgHashes).Error; err != nil { - return 0, nil, err + return nil, err } claimedMsgHashSet := make(map[string]struct{}) @@ -127,16 +140,7 @@ func (l *L2SentMsg) GetClaimableL2SentMsgByAddressWithOffset(ctx context.Context } } - // pagination - start := offset - end := offset + limit - if start > len(unclaimedL2Msgs) { - start = len(unclaimedL2Msgs) - } - if end > len(unclaimedL2Msgs) { - end = len(unclaimedL2Msgs) - } - return uint64(len(unclaimedL2Msgs)), unclaimedL2Msgs[start:end], nil + return unclaimedL2Msgs, nil } // GetLatestL2SentMsgBatchIndex get latest l2 sent msg batch index diff --git a/bridge-history-api/orm/l2_sent_msg_test.go b/bridge-history-api/orm/l2_sent_msg_test.go index dd89fa5d28..8cf1c67a36 100644 --- a/bridge-history-api/orm/l2_sent_msg_test.go +++ b/bridge-history-api/orm/l2_sent_msg_test.go @@ -7,11 +7,12 @@ import ( "github.com/stretchr/testify/assert" "bridge-history-api/orm/migrate" + "scroll-tech/common/database" "scroll-tech/common/docker" ) -func TestGetClaimableL2SentMsgByAddressWithOffset(t *testing.T) { +func TestGetClaimableL2SentMsgByAddress(t *testing.T) { base := docker.NewDockerApp() base.RunDBImage(t) @@ -32,9 +33,8 @@ func TestGetClaimableL2SentMsgByAddressWithOffset(t *testing.T) { l2SentMsgOrm := NewL2SentMsg(db) relayedMsgOrm := NewRelayedMsg(db) - count, msgs, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddressWithOffset(context.Background(), "sender1", 0, 10) + msgs, err := l2SentMsgOrm.GetClaimableL2SentMsgByAddress(context.Background(), "sender1") assert.NoError(t, err) - assert.Equal(t, uint64(0), count) assert.Len(t, msgs, 0) l2SentMsgs := []*L2SentMsg{ @@ -70,8 +70,8 @@ func TestGetClaimableL2SentMsgByAddressWithOffset(t *testing.T) { err = relayedMsgOrm.InsertRelayedMsg(context.Background(), relayedMsgs) assert.NoError(t, err) - count, msgs, err = l2SentMsgOrm.GetClaimableL2SentMsgByAddressWithOffset(context.Background(), "sender1", 0, 10) + msgs, err = l2SentMsgOrm.GetClaimableL2SentMsgByAddress(context.Background(), "sender1") assert.NoError(t, err) - assert.Equal(t, uint64(1), count) + assert.Len(t, msgs, 1) assert.Equal(t, "hash1", msgs[0].MsgHash) } diff --git a/bridge-history-api/orm/relayed_msg.go b/bridge-history-api/orm/relayed_msg.go index c70d916a58..43f2459d76 100644 --- a/bridge-history-api/orm/relayed_msg.go +++ b/bridge-history-api/orm/relayed_msg.go @@ -49,6 +49,19 @@ func (r *RelayedMsg) GetRelayedMsgByHash(ctx context.Context, msgHash string) (* return &result, nil } +// GetRelayedMsgsByHashes get relayed msg by hash array +func (r *RelayedMsg) GetRelayedMsgsByHashes(ctx context.Context, msgHashes []string) ([]*RelayedMsg, error) { + var results []*RelayedMsg + err := r.db.WithContext(ctx).Model(&RelayedMsg{}). + Where("msg_hash IN (?)", msgHashes). + Find(&results). + Error + if err != nil { + return nil, fmt.Errorf("RelayedMsg.GetRelayedMsgsByHashes error: %w", err) + } + return results, nil +} + // GetLatestRelayedHeightOnL1 get latest relayed height on l1 func (r *RelayedMsg) GetLatestRelayedHeightOnL1(ctx context.Context) (uint64, error) { var result RelayedMsg diff --git a/bridge-history-api/utils/parse_event.go b/bridge-history-api/utils/parse_event.go index b8eeb7b1f7..08b518932d 100644 --- a/bridge-history-api/utils/parse_event.go +++ b/bridge-history-api/utils/parse_event.go @@ -14,14 +14,6 @@ import ( "bridge-history-api/orm" ) -// CachedParsedTxCalldata store parsed batch infos -type CachedParsedTxCalldata struct { - CallDataIndex uint64 - BatchIndices []uint64 - StartBlocks []uint64 - EndBlocks []uint64 -} - // ParseBackendL1EventLogs parses L1 watched events func ParseBackendL1EventLogs(logs []types.Log) ([]*orm.CrossMsg, []*orm.RelayedMsg, error) { // Need use contract abi to parse event Log