mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-04-23 03:00:50 -04:00
250 lines
7.3 KiB
Go
250 lines
7.3 KiB
Go
package messageproof
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"gorm.io/gorm"
|
|
|
|
"bridge-history-api/orm"
|
|
)
|
|
|
|
// MsgProofUpdater is used to update message proof in db
|
|
type MsgProofUpdater struct {
|
|
ctx context.Context
|
|
db *gorm.DB
|
|
l2SentMsgOrm *orm.L2SentMsg
|
|
rollupOrm *orm.RollupBatch
|
|
withdrawTrie *WithdrawTrie
|
|
}
|
|
|
|
// NewMsgProofUpdater new MsgProofUpdater instance
|
|
func NewMsgProofUpdater(ctx context.Context, confirmations uint64, startBlock uint64, db *gorm.DB) *MsgProofUpdater {
|
|
return &MsgProofUpdater{
|
|
ctx: ctx,
|
|
db: db,
|
|
l2SentMsgOrm: orm.NewL2SentMsg(db),
|
|
rollupOrm: orm.NewRollupBatch(db),
|
|
withdrawTrie: NewWithdrawTrie(),
|
|
}
|
|
}
|
|
|
|
// Start the MsgProofUpdater
|
|
func (m *MsgProofUpdater) Start() {
|
|
log.Info("MsgProofUpdater Start")
|
|
m.initialize(m.ctx)
|
|
go func() {
|
|
tick := time.NewTicker(10 * time.Second)
|
|
for {
|
|
select {
|
|
case <-m.ctx.Done():
|
|
tick.Stop()
|
|
return
|
|
case <-tick.C:
|
|
latestBatch, err := m.rollupOrm.GetLatestRollupBatch(m.ctx)
|
|
if err != nil {
|
|
log.Warn("MsgProofUpdater: Can not get latest RollupBatch: ", "err", err)
|
|
continue
|
|
}
|
|
if latestBatch == nil {
|
|
continue
|
|
}
|
|
latestBatchIndexWithProof, err := m.l2SentMsgOrm.GetLatestL2SentMsgBatchIndex(m.ctx)
|
|
if err != nil {
|
|
log.Error("MsgProofUpdater: Can not get latest L2SentMsgBatchIndex: ", "err", err)
|
|
continue
|
|
}
|
|
log.Info("latest batch with proof", "batch_index", latestBatchIndexWithProof)
|
|
var start uint64
|
|
if latestBatchIndexWithProof < 0 {
|
|
start = 1
|
|
} else {
|
|
start = uint64(latestBatchIndexWithProof) + 1
|
|
}
|
|
for i := start; i <= latestBatch.BatchIndex; i++ {
|
|
batch, err := m.rollupOrm.GetRollupBatchByIndex(m.ctx, i)
|
|
if err != nil {
|
|
log.Error("MsgProofUpdater: Can not get RollupBatch: ", "err", err, "index", i)
|
|
break
|
|
}
|
|
// get all l2 messages in this batch
|
|
msgs, proofs, err := m.appendL2Messages(batch.StartBlockNumber, batch.EndBlockNumber)
|
|
if err != nil {
|
|
log.Error("MsgProofUpdater: can not append l2messages", "startBlockNumber", batch.StartBlockNumber, "endBlockNumber", batch.EndBlockNumber, "err", err)
|
|
break
|
|
}
|
|
// here we update batch withdraw root
|
|
err = m.rollupOrm.UpdateRollupBatchWithdrawRoot(m.ctx, batch.BatchIndex, m.withdrawTrie.MessageRoot().Hex())
|
|
if err != nil {
|
|
// if failed better restart the binary
|
|
log.Error("MsgProofUpdater: can not update batch withdraw root", "err", err)
|
|
break
|
|
}
|
|
err = m.updateMsgProof(msgs, proofs, batch.BatchIndex)
|
|
if err != nil {
|
|
// if failed better restart the binary
|
|
log.Error("MsgProofUpdater: can not update msg proof", "err", err)
|
|
break
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}()
|
|
|
|
}
|
|
|
|
// Stop the MsgProofUpdater
|
|
func (m *MsgProofUpdater) Stop() {
|
|
log.Info("MsgProofUpdater Stop")
|
|
}
|
|
|
|
func (m *MsgProofUpdater) initialize(ctx context.Context) {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
err := m.initializeWithdrawTrie()
|
|
if err != nil {
|
|
log.Error("can not initialize withdraw trie", "err", err)
|
|
// give it some time to retry
|
|
time.Sleep(10 * time.Second)
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *MsgProofUpdater) initializeWithdrawTrie() error {
|
|
var batch *orm.RollupBatch
|
|
firstMsg, err := m.l2SentMsgOrm.GetL2SentMessageByNonce(m.ctx, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get first l2 message: %v", err)
|
|
}
|
|
// no l2 message
|
|
// TO DO: check if we really dont have l2 sent message with nonce 0
|
|
if firstMsg == nil {
|
|
log.Info("No first l2sentmsg in db")
|
|
return nil
|
|
}
|
|
|
|
// if no batch, return and wait for next try round
|
|
batch, err = m.rollupOrm.GetLatestRollupBatch(m.ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get latest batch: %v", err)
|
|
}
|
|
if batch == nil {
|
|
return fmt.Errorf("no batch found")
|
|
}
|
|
|
|
var batches []*orm.RollupBatch
|
|
batchIndex := batch.BatchIndex
|
|
for {
|
|
var msg *orm.L2SentMsg
|
|
msg, err = m.l2SentMsgOrm.GetLatestL2SentMsgLEHeight(m.ctx, batch.EndBlockNumber)
|
|
if err != nil {
|
|
log.Warn("failed to get l2 sent message less than height", "endBlocknum", batch.EndBlockNumber, "err", err)
|
|
}
|
|
if msg != nil && msg.MsgProof != "" {
|
|
log.Info("Found latest l2 sent msg with proof: ", "msg_proof", msg.MsgProof, "height", msg.Height, "msg_hash", msg.MsgHash)
|
|
// initialize withdrawTrie
|
|
proofBytes := common.Hex2Bytes(msg.MsgProof)
|
|
m.withdrawTrie.Initialize(msg.Nonce, common.HexToHash(msg.MsgHash), proofBytes)
|
|
break
|
|
}
|
|
|
|
// append unprocessed batch
|
|
batches = append(batches, batch)
|
|
|
|
if batchIndex == 1 {
|
|
// otherwise overflow
|
|
// and batchIndex 0 is not in DB
|
|
// To Do: check if we dont have batch with index 0 in future
|
|
break
|
|
}
|
|
// iterate for next batch
|
|
batchIndex--
|
|
|
|
batch, err = m.rollupOrm.GetRollupBatchByIndex(m.ctx, batchIndex)
|
|
if err != nil || batch == nil {
|
|
return fmt.Errorf("failed to get block batch %v: %v", batchIndex, err)
|
|
}
|
|
}
|
|
|
|
log.Info("Build withdraw trie with pending messages")
|
|
for i := len(batches) - 1; i >= 0; i-- {
|
|
b := batches[i]
|
|
msgs, proofs, err := m.appendL2Messages(b.StartBlockNumber, b.EndBlockNumber)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = m.rollupOrm.UpdateRollupBatchWithdrawRoot(m.ctx, b.BatchIndex, m.withdrawTrie.MessageRoot().Hex())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = m.updateMsgProof(msgs, proofs, b.BatchIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
log.Info("Build withdraw trie finished")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *MsgProofUpdater) updateMsgProof(msgs []*orm.L2SentMsg, proofs [][]byte, batchIndex uint64) error {
|
|
if len(msgs) == 0 {
|
|
return nil
|
|
}
|
|
// this should not happen, but double check
|
|
if len(msgs) != len(proofs) {
|
|
return fmt.Errorf("illegal state: len(msgs) != len(proofs)")
|
|
}
|
|
err := m.db.Transaction(func(tx *gorm.DB) error {
|
|
for i, msg := range msgs {
|
|
proofHex := common.Bytes2Hex(proofs[i])
|
|
log.Debug("updateMsgProof", "msgHash", msg.MsgHash, "batchIndex", batchIndex, "proof", proofHex)
|
|
if err := m.l2SentMsgOrm.UpdateL2MessageProof(m.ctx, msg.MsgHash, proofHex, batchIndex, tx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// appendL2Messages will append all messages between firstBlock and lastBlock (both inclusive) to withdrawTrie and compute corresponding merkle proof of each message.
|
|
func (m *MsgProofUpdater) appendL2Messages(firstBlock, lastBlock uint64) ([]*orm.L2SentMsg, [][]byte, error) {
|
|
var msgProofs [][]byte
|
|
messages, err := m.l2SentMsgOrm.GetL2SentMsgMsgHashByHeightRange(m.ctx, firstBlock, lastBlock)
|
|
if err != nil {
|
|
log.Error("GetL2SentMsgMsgHashByHeightRange failed", "error", err, "firstBlock", firstBlock, "lastBlock", lastBlock)
|
|
return messages, msgProofs, err
|
|
}
|
|
if len(messages) == 0 {
|
|
return messages, msgProofs, nil
|
|
}
|
|
|
|
// double check whether nonce is matched
|
|
if messages[0].Nonce != m.withdrawTrie.NextMessageNonce {
|
|
log.Error("L2 message nonce mismatch", "expected", m.withdrawTrie.NextMessageNonce, "found", messages[0].Nonce)
|
|
return messages, msgProofs, fmt.Errorf("l2 message nonce mismatch, expected: %v, found: %v", m.withdrawTrie.NextMessageNonce, messages[0].Nonce)
|
|
}
|
|
|
|
var hashes []common.Hash
|
|
for _, msg := range messages {
|
|
hashes = append(hashes, common.HexToHash(msg.MsgHash))
|
|
}
|
|
msgProofs = m.withdrawTrie.AppendMessages(hashes)
|
|
|
|
return messages, msgProofs, nil
|
|
}
|