mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-10 06:28:04 -05:00
285 lines
9.5 KiB
Go
285 lines
9.5 KiB
Go
package orm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/da-codec/encoding"
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/crypto"
|
|
"gorm.io/gorm"
|
|
|
|
"scroll-tech/common/types"
|
|
"scroll-tech/common/types/message"
|
|
"scroll-tech/common/utils"
|
|
)
|
|
|
|
// Bundle represents a bundle of batches.
|
|
type Bundle struct {
|
|
db *gorm.DB `gorm:"column:-"`
|
|
|
|
// bundle
|
|
Index uint64 `json:"index" gorm:"column:index;primaryKey"`
|
|
Hash string `json:"hash" gorm:"column:hash"`
|
|
StartBatchIndex uint64 `json:"start_batch_index" gorm:"column:start_batch_index"`
|
|
EndBatchIndex uint64 `json:"end_batch_index" gorm:"column:end_batch_index"`
|
|
StartBatchHash string `json:"start_batch_hash" gorm:"column:start_batch_hash"`
|
|
EndBatchHash string `json:"end_batch_hash" gorm:"column:end_batch_hash"`
|
|
CodecVersion int16 `json:"codec_version" gorm:"column:codec_version"`
|
|
|
|
// proof
|
|
BatchProofsStatus int16 `json:"batch_proofs_status" gorm:"column:batch_proofs_status;default:1"`
|
|
ProvingStatus int16 `json:"proving_status" gorm:"column:proving_status;default:1"`
|
|
Proof []byte `json:"proof" gorm:"column:proof;default:NULL"`
|
|
ProvedAt *time.Time `json:"proved_at" gorm:"column:proved_at;default:NULL"`
|
|
ProofTimeSec int32 `json:"proof_time_sec" gorm:"column:proof_time_sec;default:NULL"`
|
|
|
|
// rollup
|
|
RollupStatus int16 `json:"rollup_status" gorm:"column:rollup_status;default:1"`
|
|
FinalizeTxHash string `json:"finalize_tx_hash" gorm:"column:finalize_tx_hash;default:NULL"`
|
|
FinalizedAt *time.Time `json:"finalized_at" gorm:"column:finalized_at;default:NULL"`
|
|
|
|
// metadata
|
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
|
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;default:NULL"`
|
|
}
|
|
|
|
// NewBundle creates a new Bundle database instance.
|
|
func NewBundle(db *gorm.DB) *Bundle {
|
|
return &Bundle{db: db}
|
|
}
|
|
|
|
// TableName returns the table name for the Bundle model.
|
|
func (*Bundle) TableName() string {
|
|
return "bundle"
|
|
}
|
|
|
|
// getLatestBundle retrieves the latest bundle from the database.
|
|
func (o *Bundle) getLatestBundle(ctx context.Context) (*Bundle, error) {
|
|
db := o.db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Order("index desc")
|
|
|
|
var latestBundle Bundle
|
|
if err := db.First(&latestBundle).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("getLatestBundle error: %w", err)
|
|
}
|
|
return &latestBundle, nil
|
|
}
|
|
|
|
// GetBundles retrieves selected bundles from the database.
|
|
// The returned bundles are sorted in ascending order by their index.
|
|
// only used in unit tests.
|
|
func (o *Bundle) GetBundles(ctx context.Context, fields map[string]interface{}, orderByList []string, limit int) ([]*Bundle, error) {
|
|
db := o.db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
|
|
for key, value := range fields {
|
|
db = db.Where(key, value)
|
|
}
|
|
|
|
for _, orderBy := range orderByList {
|
|
db = db.Order(orderBy)
|
|
}
|
|
|
|
if limit > 0 {
|
|
db = db.Limit(limit)
|
|
}
|
|
|
|
db = db.Order("index ASC")
|
|
|
|
var bundles []*Bundle
|
|
if err := db.Find(&bundles).Error; err != nil {
|
|
return nil, fmt.Errorf("Bundle.GetBundles error: %w, fields: %v, orderByList: %v", err, fields, orderByList)
|
|
}
|
|
return bundles, nil
|
|
}
|
|
|
|
// GetFirstUnbundledBatchIndex retrieves the first unbundled batch index.
|
|
func (o *Bundle) GetFirstUnbundledBatchIndex(ctx context.Context) (uint64, error) {
|
|
// Get the latest bundle
|
|
latestBundle, err := o.getLatestBundle(ctx)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("Bundle.GetFirstUnbundledBatchIndex error: %w", err)
|
|
}
|
|
if latestBundle == nil {
|
|
return 0, nil
|
|
}
|
|
return latestBundle.EndBatchIndex + 1, nil
|
|
}
|
|
|
|
// GetFirstPendingBundle retrieves the first pending bundle from the database.
|
|
func (o *Bundle) GetFirstPendingBundle(ctx context.Context) (*Bundle, error) {
|
|
db := o.db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Where("rollup_status = ?", types.RollupPending)
|
|
db = db.Order("index asc")
|
|
|
|
var pendingBundle Bundle
|
|
if err := db.First(&pendingBundle).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("Bundle.GetFirstPendingBundle error: %w", err)
|
|
}
|
|
return &pendingBundle, nil
|
|
}
|
|
|
|
// GetVerifiedProofByHash retrieves the verified aggregate proof for a bundle with the given hash.
|
|
func (o *Bundle) GetVerifiedProofByHash(ctx context.Context, hash string) (*message.OpenVMBundleProof, error) {
|
|
db := o.db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Select("proof")
|
|
db = db.Where("hash = ? AND proving_status = ?", hash, types.ProvingTaskVerified)
|
|
|
|
var bundle Bundle
|
|
if err := db.Find(&bundle).Error; err != nil {
|
|
return nil, fmt.Errorf("Bundle.GetVerifiedProofByHash error: %w, bundle hash: %v", err, hash)
|
|
}
|
|
|
|
var proof message.OpenVMBundleProof
|
|
if err := json.Unmarshal(bundle.Proof, &proof); err != nil {
|
|
return nil, fmt.Errorf("Bundle.GetVerifiedProofByHash error: %w, bundle hash: %v", err, hash)
|
|
}
|
|
return &proof, nil
|
|
}
|
|
|
|
// InsertBundle inserts a new bundle into the database.
|
|
// Assuming input batches are ordered by index.
|
|
func (o *Bundle) InsertBundle(ctx context.Context, batches []*Batch, codecVersion encoding.CodecVersion, dbTX ...*gorm.DB) (*Bundle, error) {
|
|
if len(batches) == 0 {
|
|
return nil, errors.New("Bundle.InsertBundle error: no batches provided")
|
|
}
|
|
|
|
db := o.db
|
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
|
db = dbTX[0]
|
|
}
|
|
db = db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
|
|
newBundle := Bundle{
|
|
StartBatchHash: batches[0].Hash,
|
|
StartBatchIndex: batches[0].Index,
|
|
EndBatchHash: batches[len(batches)-1].Hash,
|
|
EndBatchIndex: batches[len(batches)-1].Index,
|
|
BatchProofsStatus: int16(types.BatchProofsStatusPending),
|
|
ProvingStatus: int16(types.ProvingTaskUnassigned),
|
|
RollupStatus: int16(types.RollupPending),
|
|
CodecVersion: int16(codecVersion),
|
|
}
|
|
|
|
// Not part of DA hash, used for SQL query consistency and ease of use.
|
|
// Derived using keccak256(concat(start_batch_hash_bytes, end_batch_hash_bytes)).
|
|
newBundle.Hash = hex.EncodeToString(crypto.Keccak256(append(common.Hex2Bytes(newBundle.StartBatchHash[2:]), common.Hex2Bytes(newBundle.EndBatchHash[2:])...)))
|
|
|
|
if err := db.Create(&newBundle).Error; err != nil {
|
|
return nil, fmt.Errorf("Bundle.InsertBundle Create error: %w, bundle hash: %v", err, newBundle.Hash)
|
|
}
|
|
|
|
return &newBundle, nil
|
|
}
|
|
|
|
// UpdateFinalizeTxHashAndRollupStatus updates the finalize transaction hash and rollup status for a bundle.
|
|
func (o *Bundle) UpdateFinalizeTxHashAndRollupStatus(ctx context.Context, hash string, finalizeTxHash string, status types.RollupStatus, dbTX ...*gorm.DB) error {
|
|
updateFields := make(map[string]interface{})
|
|
updateFields["finalize_tx_hash"] = finalizeTxHash
|
|
updateFields["rollup_status"] = int(status)
|
|
if status == types.RollupFinalized {
|
|
updateFields["finalized_at"] = utils.NowUTC()
|
|
}
|
|
|
|
db := o.db
|
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
|
db = dbTX[0]
|
|
}
|
|
db = db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Where("hash", hash)
|
|
|
|
if err := db.Updates(updateFields).Error; err != nil {
|
|
return fmt.Errorf("Bundle.UpdateFinalizeTxHashAndRollupStatus error: %w, bundle hash: %v, status: %v, finalizeTxHash: %v", err, hash, status.String(), finalizeTxHash)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateProvingStatus updates the proving status of a bundle.
|
|
func (o *Bundle) UpdateProvingStatus(ctx context.Context, hash string, status types.ProvingStatus, dbTX ...*gorm.DB) error {
|
|
updateFields := make(map[string]interface{})
|
|
updateFields["proving_status"] = int(status)
|
|
|
|
switch status {
|
|
case types.ProvingTaskVerified:
|
|
updateFields["proved_at"] = utils.NowUTC()
|
|
}
|
|
|
|
db := o.db
|
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
|
db = dbTX[0]
|
|
}
|
|
db = db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Where("hash", hash)
|
|
|
|
if err := db.Updates(updateFields).Error; err != nil {
|
|
return fmt.Errorf("Bundle.UpdateProvingStatus error: %w, bundle hash: %v, status: %v", err, hash, status.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateRollupStatus updates the rollup status for a bundle.
|
|
// only used in unit tests.
|
|
func (o *Bundle) UpdateRollupStatus(ctx context.Context, hash string, status types.RollupStatus) error {
|
|
updateFields := make(map[string]interface{})
|
|
updateFields["rollup_status"] = int(status)
|
|
if status == types.RollupFinalized {
|
|
updateFields["finalized_at"] = utils.NowUTC()
|
|
}
|
|
|
|
db := o.db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Where("hash", hash)
|
|
|
|
if err := db.Updates(updateFields).Error; err != nil {
|
|
return fmt.Errorf("Bundle.UpdateRollupStatus error: %w, bundle hash: %v, status: %v", err, hash, status.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateProofAndProvingStatusByHash updates the bundle proof and proving status by hash.
|
|
// only used in unit tests.
|
|
func (o *Bundle) UpdateProofAndProvingStatusByHash(ctx context.Context, hash string, proof *message.OpenVMBundleProof, provingStatus types.ProvingStatus, proofTimeSec uint64, dbTX ...*gorm.DB) error {
|
|
db := o.db
|
|
if len(dbTX) > 0 && dbTX[0] != nil {
|
|
db = dbTX[0]
|
|
}
|
|
|
|
proofBytes, err := json.Marshal(proof)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
updateFields := make(map[string]interface{})
|
|
updateFields["proof"] = proofBytes
|
|
updateFields["proving_status"] = provingStatus
|
|
updateFields["proof_time_sec"] = proofTimeSec
|
|
updateFields["proved_at"] = utils.NowUTC()
|
|
|
|
db = db.WithContext(ctx)
|
|
db = db.Model(&Bundle{})
|
|
db = db.Where("hash", hash)
|
|
|
|
if err := db.Updates(updateFields).Error; err != nil {
|
|
return fmt.Errorf("Bundle.UpdateProofAndProvingStatusByHash error: %w, bundle hash: %v", err, hash)
|
|
}
|
|
return nil
|
|
}
|