blocks.go 3.71 KB
Newer Older
1 2 3
package database

import (
4
	"context"
Hamdi Allam's avatar
Hamdi Allam committed
5
	"errors"
6 7 8 9 10 11 12 13 14 15 16 17
	"math/big"

	"github.com/ethereum/go-ethereum/common"

	"gorm.io/gorm"
)

/**
 * Types
 */

type BlockHeader struct {
Hamdi Allam's avatar
Hamdi Allam committed
18
	Hash       common.Hash `gorm:"primaryKey;serializer:json"`
19
	ParentHash common.Hash `gorm:"serializer:json"`
20
	Number     U256
21 22 23 24
	Timestamp  uint64
}

type L1BlockHeader struct {
25
	BlockHeader
26 27 28
}

type L2BlockHeader struct {
29
	BlockHeader
30 31

	// Marked when the proposed output is finalized on L1.
32
	// All bedrock blocks will have `LegacyStateBatchIndex ^== NULL`
Hamdi Allam's avatar
Hamdi Allam committed
33
	L1BlockHash           *common.Hash `gorm:"serializer:json"`
34
	LegacyStateBatchIndex *uint64
35 36 37
}

type LegacyStateBatch struct {
38 39 40 41
	// `default:0` is added since gorm would interepret 0 as NULL
	// violating the primary key constraint.
	Index uint64 `gorm:"primaryKey;default:0"`

Hamdi Allam's avatar
Hamdi Allam committed
42
	Root        common.Hash `gorm:"serializer:json"`
43 44
	Size        uint64
	PrevTotal   uint64
Hamdi Allam's avatar
Hamdi Allam committed
45
	L1BlockHash common.Hash `gorm:"serializer:json"`
46 47 48
}

type BlocksView interface {
49 50
	FinalizedL1BlockHeight() (*big.Int, error)
	FinalizedL2BlockHeight() (*big.Int, error)
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

type BlocksDB interface {
	BlocksView

	StoreL1BlockHeaders([]*L1BlockHeader) error
	StoreLegacyStateBatch(*LegacyStateBatch) error

	StoreL2BlockHeaders([]*L2BlockHeader) error
	MarkFinalizedL1RootForL2Block(common.Hash, common.Hash) error
}

/**
 * Implementation
 */

type blocksDB struct {
	gorm *gorm.DB
}

func newBlocksDB(db *gorm.DB) BlocksDB {
	return &blocksDB{gorm: db}
}

// L1

func (db *blocksDB) StoreL1BlockHeaders(headers []*L1BlockHeader) error {
	result := db.gorm.Create(&headers)
	return result.Error
}

func (db *blocksDB) StoreLegacyStateBatch(stateBatch *LegacyStateBatch) error {
Hamdi Allam's avatar
Hamdi Allam committed
83
	// Even though transaction control flow is managed, could we benefit
84 85 86 87 88 89 90
	// from a nested transaction here?

	result := db.gorm.Create(stateBatch)
	if result.Error != nil {
		return result.Error
	}

91 92
	// Mark this state batch index & l1 block hash for all applicable l2 blocks
	l2Headers := make([]*L2BlockHeader, stateBatch.Size)
93 94

	// [start, end] range is inclusive. Since `PrevTotal` is the index of the prior batch, no
Hamdi Allam's avatar
Hamdi Allam committed
95
	// need to subtract one when adding the size
96 97
	startHeight := U256{Int: big.NewInt(int64(stateBatch.PrevTotal + 1))}
	endHeight := U256{Int: big.NewInt(int64(stateBatch.PrevTotal + stateBatch.Size))}
98 99 100
	result = db.gorm.Where("number BETWEEN ? AND ?", &startHeight, &endHeight).Find(&l2Headers)
	if result.Error != nil {
		return result.Error
Hamdi Allam's avatar
Hamdi Allam committed
101 102
	} else if result.RowsAffected != int64(stateBatch.Size) {
		return errors.New("state batch size exceeds number of indexed l2 blocks")
103 104 105
	}

	for _, header := range l2Headers {
106
		header.LegacyStateBatchIndex = &stateBatch.Index
107 108 109 110 111 112 113
		header.L1BlockHash = &stateBatch.L1BlockHash
	}

	result = db.gorm.Save(&l2Headers)
	return result.Error
}

114 115 116
func (db *blocksDB) FinalizedL1BlockHeight() (*big.Int, error) {
	var l1Header L1BlockHeader
	result := db.gorm.Order("number DESC").Take(&l1Header)
117 118 119 120
	if result.Error != nil {
		return nil, result.Error
	}

121
	return l1Header.Number.Int, nil
122 123 124 125 126 127 128 129 130
}

// L2

func (db *blocksDB) StoreL2BlockHeaders(headers []*L2BlockHeader) error {
	result := db.gorm.Create(&headers)
	return result.Error
}

131 132 133
func (db *blocksDB) FinalizedL2BlockHeight() (*big.Int, error) {
	var l2Header L2BlockHeader
	result := db.gorm.Order("number DESC").Take(&l2Header)
134 135 136 137
	if result.Error != nil {
		return nil, result.Error
	}

138 139
	result.Logger.Info(context.Background(), "number ", l2Header.Number)
	return l2Header.Number.Int, nil
140 141 142 143
}

func (db *blocksDB) MarkFinalizedL1RootForL2Block(l2Root, l1Root common.Hash) error {
	var l2Header L2BlockHeader
Hamdi Allam's avatar
Hamdi Allam committed
144 145 146 147 148
	l2Header.Hash = l2Root // set the primary key

	result := db.gorm.First(&l2Header)
	if result.Error != nil {
		return result.Error
149 150
	}

Hamdi Allam's avatar
Hamdi Allam committed
151 152
	l2Header.L1BlockHash = &l1Root
	result = db.gorm.Save(&l2Header)
153 154
	return result.Error
}