Skip to content

Commit

Permalink
add column checked on state.block (#3543)
Browse files Browse the repository at this point in the history
* add column checked on state.block

* if no unchecked blocks  return ErrNotFound

* migration set to checked all but the block with number below max-1000
  • Loading branch information
joanestebanr authored Apr 8, 2024
1 parent 6ca0464 commit fed89ca
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 55 deletions.
11 changes: 11 additions & 0 deletions db/migrations/state/0018.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- +migrate Up
ALTER TABLE state.block
ADD COLUMN IF NOT EXISTS checked BOOL NOT NULL DEFAULT FALSE;

-- set block.checked to true for all blocks below max - 100
UPDATE state.block SET checked = true WHERE block_num <= (SELECT MAX(block_num) - 1000 FROM state.block);

-- +migrate Down
ALTER TABLE state.block
DROP COLUMN IF EXISTS checked;

69 changes: 69 additions & 0 deletions db/migrations/state/0018_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package migrations_test

import (
"database/sql"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

type migrationTest0018 struct{}

func (m migrationTest0018) InsertData(db *sql.DB) error {
const addBlock = "INSERT INTO state.block (block_num, received_at, block_hash) VALUES ($1, $2, $3)"
if _, err := db.Exec(addBlock, 1, time.Now(), "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1"); err != nil {
return err
}
if _, err := db.Exec(addBlock, 50, time.Now(), "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1"); err != nil {
return err
}
if _, err := db.Exec(addBlock, 1050, time.Now(), "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1"); err != nil {
return err
}
return nil
}

func (m migrationTest0018) RunAssertsAfterMigrationUp(t *testing.T, db *sql.DB) {
var checked bool
row := db.QueryRow("SELECT checked FROM state.block WHERE block_num = $1", 1)
assert.NoError(t, row.Scan(&checked))
assert.Equal(t, true, checked)
row = db.QueryRow("SELECT checked FROM state.block WHERE block_num = $1", 50)
assert.NoError(t, row.Scan(&checked))
assert.Equal(t, true, checked)
row = db.QueryRow("SELECT checked FROM state.block WHERE block_num = $1", 1050)
assert.NoError(t, row.Scan(&checked))
assert.Equal(t, false, checked)

const addBlock = "INSERT INTO state.block (block_num, received_at, block_hash, checked) VALUES ($1, $2, $3, $4)"
_, err := db.Exec(addBlock, 2, time.Now(), "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1", true)
assert.NoError(t, err)
_, err = db.Exec(addBlock, 3, time.Now(), "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1", false)
assert.NoError(t, err)
const sql = `SELECT count(*) FROM state.block WHERE checked = true`
row = db.QueryRow(sql)
var result int
assert.NoError(t, row.Scan(&result))
assert.Equal(t, 3, result, "must be 1,50 per migration and 2 by insert")

const sqlCheckedFalse = `SELECT count(*) FROM state.block WHERE checked = false`
row = db.QueryRow(sqlCheckedFalse)

assert.NoError(t, row.Scan(&result))
assert.Equal(t, 2, result, "must be 150 by migration, and 3 by insert")
}

func (m migrationTest0018) RunAssertsAfterMigrationDown(t *testing.T, db *sql.DB) {
var result int

// Check column wip doesn't exists in state.batch table
const sql = `SELECT count(*) FROM state.block`
row := db.QueryRow(sql)
assert.NoError(t, row.Scan(&result))
assert.Equal(t, 5, result)
}

func TestMigration0018(t *testing.T) {
runMigrationTest(t, 18, migrationTest0018{})
}
1 change: 1 addition & 0 deletions state/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Block struct {
BlockHash common.Hash
ParentHash common.Hash
ReceivedAt time.Time
Checked bool
}

// NewBlock creates a block with the given data.
Expand Down
2 changes: 2 additions & 0 deletions state/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type storage interface {
GetTxsOlderThanNL1BlocksUntilTxHash(ctx context.Context, nL1Blocks uint64, earliestTxHash common.Hash, dbTx pgx.Tx) ([]common.Hash, error)
GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*Block, error)
GetPreviousBlock(ctx context.Context, offset uint64, dbTx pgx.Tx) (*Block, error)
GetFirstUncheckedBlock(ctx context.Context, fromBlockNumber uint64, dbTx pgx.Tx) (*Block, error)
UpdateCheckedBlockByNumber(ctx context.Context, blockNumber uint64, newCheckedStatus bool, dbTx pgx.Tx) error
AddGlobalExitRoot(ctx context.Context, exitRoot *GlobalExitRoot, dbTx pgx.Tx) error
GetLatestGlobalExitRoot(ctx context.Context, maxBlockNumber uint64, dbTx pgx.Tx) (GlobalExitRoot, time.Time, error)
GetNumberOfBlocksSinceLastGERUpdate(ctx context.Context, dbTx pgx.Tx) (uint64, error)
Expand Down
109 changes: 109 additions & 0 deletions state/mocks/mock_storage.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 39 additions & 8 deletions state/pgstatestorage/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ const (

// AddBlock adds a new block to the State Store
func (p *PostgresStorage) AddBlock(ctx context.Context, block *state.Block, dbTx pgx.Tx) error {
const addBlockSQL = "INSERT INTO state.block (block_num, block_hash, parent_hash, received_at) VALUES ($1, $2, $3, $4)"
const addBlockSQL = "INSERT INTO state.block (block_num, block_hash, parent_hash, received_at, checked) VALUES ($1, $2, $3, $4, $5)"

e := p.getExecQuerier(dbTx)
_, err := e.Exec(ctx, addBlockSQL, block.BlockNumber, block.BlockHash.String(), block.ParentHash.String(), block.ReceivedAt)
_, err := e.Exec(ctx, addBlockSQL, block.BlockNumber, block.BlockHash.String(), block.ParentHash.String(), block.ReceivedAt, block.Checked)
return err
}

Expand All @@ -30,11 +30,11 @@ func (p *PostgresStorage) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*state
parentHash string
block state.Block
)
const getLastBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at FROM state.block ORDER BY block_num DESC LIMIT 1"
const getLastBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at, checked FROM state.block ORDER BY block_num DESC LIMIT 1"

q := p.getExecQuerier(dbTx)

err := q.QueryRow(ctx, getLastBlockSQL).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt)
err := q.QueryRow(ctx, getLastBlockSQL).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt, &block.Checked)
if errors.Is(err, pgx.ErrNoRows) {
return nil, state.ErrStateNotSynchronized
}
Expand All @@ -43,18 +43,38 @@ func (p *PostgresStorage) GetLastBlock(ctx context.Context, dbTx pgx.Tx) (*state
return &block, err
}

// GetFirstUncheckedBlock returns the first L1 block that has not been checked from a given block number.
func (p *PostgresStorage) GetFirstUncheckedBlock(ctx context.Context, fromBlockNumber uint64, dbTx pgx.Tx) (*state.Block, error) {
var (
blockHash string
parentHash string
block state.Block
)
const getLastBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at, checked FROM state.block WHERE block_num>=$1 AND checked=false ORDER BY block_num LIMIT 1"

q := p.getExecQuerier(dbTx)

err := q.QueryRow(ctx, getLastBlockSQL, fromBlockNumber).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt, &block.Checked)
if errors.Is(err, pgx.ErrNoRows) {
return nil, state.ErrNotFound
}
block.BlockHash = common.HexToHash(blockHash)
block.ParentHash = common.HexToHash(parentHash)
return &block, err
}

// GetPreviousBlock gets the offset previous L1 block respect to latest.
func (p *PostgresStorage) GetPreviousBlock(ctx context.Context, offset uint64, dbTx pgx.Tx) (*state.Block, error) {
var (
blockHash string
parentHash string
block state.Block
)
const getPreviousBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at FROM state.block ORDER BY block_num DESC LIMIT 1 OFFSET $1"
const getPreviousBlockSQL = "SELECT block_num, block_hash, parent_hash, received_at,checked FROM state.block ORDER BY block_num DESC LIMIT 1 OFFSET $1"

q := p.getExecQuerier(dbTx)

err := q.QueryRow(ctx, getPreviousBlockSQL, offset).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt)
err := q.QueryRow(ctx, getPreviousBlockSQL, offset).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt, &block.Checked)
if errors.Is(err, pgx.ErrNoRows) {
return nil, state.ErrNotFound
}
Expand All @@ -70,15 +90,26 @@ func (p *PostgresStorage) GetBlockByNumber(ctx context.Context, blockNumber uint
parentHash string
block state.Block
)
const getBlockByNumberSQL = "SELECT block_num, block_hash, parent_hash, received_at FROM state.block WHERE block_num = $1"
const getBlockByNumberSQL = "SELECT block_num, block_hash, parent_hash, received_at,checked FROM state.block WHERE block_num = $1"

q := p.getExecQuerier(dbTx)

err := q.QueryRow(ctx, getBlockByNumberSQL, blockNumber).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt)
err := q.QueryRow(ctx, getBlockByNumberSQL, blockNumber).Scan(&block.BlockNumber, &blockHash, &parentHash, &block.ReceivedAt, &block.Checked)
if errors.Is(err, pgx.ErrNoRows) {
return nil, state.ErrNotFound
}
block.BlockHash = common.HexToHash(blockHash)
block.ParentHash = common.HexToHash(parentHash)
return &block, err
}

// UpdateCheckedBlockByNumber update checked flag for a block
func (p *PostgresStorage) UpdateCheckedBlockByNumber(ctx context.Context, blockNumber uint64, newCheckedStatus bool, dbTx pgx.Tx) error {
const query = `
UPDATE state.block
SET checked = $1 WHERE block_num = $2`

e := p.getExecQuerier(dbTx)
_, err := e.Exec(ctx, query, newCheckedStatus, blockNumber)
return err
}
37 changes: 37 additions & 0 deletions state/pgstatestorage/pgstatestorage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1650,3 +1650,40 @@ func TestGetLastGER(t *testing.T) {
require.Equal(t, common.HexToHash("0x2").String(), ger.String())

}

func TestGetFirstUncheckedBlock(t *testing.T) {
var err error
blockNumber := uint64(51001)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber, Checked: true}, nil)
require.NoError(t, err)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber + 1, Checked: false}, nil)
require.NoError(t, err)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber + 2, Checked: true}, nil)
require.NoError(t, err)

block, err := testState.GetFirstUncheckedBlock(context.Background(), blockNumber, nil)
require.NoError(t, err)
require.Equal(t, uint64(blockNumber+1), block.BlockNumber)
}

func TestUpdateCheckedBlockByNumber(t *testing.T) {
var err error
blockNumber := uint64(54001)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber, Checked: true}, nil)
require.NoError(t, err)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber + 1, Checked: false}, nil)
require.NoError(t, err)
err = testState.AddBlock(context.Background(), &state.Block{BlockNumber: blockNumber + 2, Checked: true}, nil)
require.NoError(t, err)

b1, err := testState.GetBlockByNumber(context.Background(), uint64(blockNumber), nil)
require.NoError(t, err)
require.True(t, b1.Checked)

err = testState.UpdateCheckedBlockByNumber(context.Background(), uint64(blockNumber), false, nil)
require.NoError(t, err)

b1, err = testState.GetBlockByNumber(context.Background(), uint64(blockNumber), nil)
require.NoError(t, err)
require.False(t, b1.Checked)
}
Loading

0 comments on commit fed89ca

Please sign in to comment.