Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 63 additions & 46 deletions sei-db/ledger_db/block/block_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,93 @@ package block
import (
"context"
"errors"
"time"
)

// ErrNoBlocks is returned by GetLowestBlockHeight and GetHighestBlockHeight
// when the database contains no blocks.
var ErrNoBlocks = errors.New("block db: no blocks")

// A binary transaction with its hash.
type BinaryTransaction struct {
// The hash of the transaction.
Hash []byte
// The binary transaction data.
Transaction []byte
// Transaction is the BlockDB's view of a transaction inside a block: its
// hash plus its raw bytes. BlockDB itself is block-storage-only — it does
// not index transactions by hash. Per the canonical-receipt-lookup design,
// tx-by-hash routing belongs in a separate Receipt Store; BlockDB exposes
// per-tx Hash() so a Receipt Store (or any other caller) can iterate
// `Block.Transactions()` and register its own (txHash → block, index)
// mapping at WriteBlock time.
type Transaction interface {
// Hash returns the canonical transaction hash.
Hash() []byte
// Bytes returns the raw, on-the-wire transaction bytes.
Bytes() []byte
}

// A binary block with its transactions and hash.
type BinaryBlock struct {
// The height of the block. Must be unique.
Height uint64
// The hash of the block. Must be unique.
Hash []byte
// The binary block data, not including transaction data (unless you are ok with wasting space)
BlockData []byte
// The transactions in the block and their hashes.
Transactions []*BinaryTransaction
// Block is the BlockDB's view of a finalized block. The interface intentionally
// exposes only what BlockDB itself needs to index and serve reads — backends
// must not assume any particular concrete implementation.
//
// Backends are permitted to call Transactions() multiple times across the
// block's lifetime in storage. Implementations that pay a non-trivial cost
// per call (allocation, hashing) should memoize the result at construction.
type Block interface {
// Hash returns the canonical block hash used for indexing.
Hash() []byte
// Height returns the block height (used as the key for the height index).
Height() uint64
// Time returns the block timestamp.
Time() time.Time
// Transactions returns the block's transactions in order. Must be cheap
// to call repeatedly — backends may call it more than once per block.
Transactions() []Transaction
}

// A database for storing binary block and transaction data.
// A database for storing finalized blocks. Block-only — the canonical
// "transaction by hash → execution result" lookup belongs in a separate
// Receipt Store (see the Giga Transaction Query proposal); a future
// Receipt Store reads tx bodies out of BlockDB by (blockHash, index)
// once it has resolved a hash.
//
// This store is fully threadsafe. All writes are atomic (that is, after a crash you will either see the write or
// you will not see it at all, i.e. partial writes are not possible). Multiple writes are not atomic with respect
// to each other, meaning if you write A then B and crash, you may observe B but not A (only possible when sharding
// is enabled). Within a single session, read-your-writes consistency is provided.
// This store is fully threadsafe. All writes are atomic (after a crash
// you will either see the write or you will not see it at all, i.e.
// partial writes are not possible). Multiple writes are not atomic with
// respect to each other, meaning if you write A then B and crash, you
// may observe B but not A. Within a single session, read-your-writes
// consistency is provided.
type BlockDB interface {

// Write a block to the database.
// WriteBlock writes a block to the database. Idempotent on duplicate
// block hash: a second WriteBlock for the same blockHash is a no-op,
// not an error.
//
// This method may return immediately and does not necessarily wait for the block to be written to disk.
// Call Flush() if you need to wait until the block is written to disk.
WriteBlock(ctx context.Context, block *BinaryBlock) error
// This method may return immediately and does not necessarily wait for
// the block to be written to disk. Call Flush() if you need to wait.
WriteBlock(ctx context.Context, block Block) error

// Blocks until all pending writes are flushed to disk. Any call to WriteBlock issued before calling Flush()
// will be crash-durable after Flush() returns. Calls to WriteBlock() made concurrently with Flush() may or
// may not be crash-durable after Flush() returns (but are otherwise eventually durable).
//
// It is not required to call Flush() in order to ensure data is written to disk. The database asyncronously
// pushes data down to disk even if Flush() is never called. Flush() just allows you to syncronize an external
// goroutine with the database's internal write loop.
// Flush blocks until all pending writes are durable. WriteBlocks issued
// before calling Flush() will be crash-durable after Flush() returns.
// Concurrent WriteBlocks may or may not be durable after Flush()
// returns (but are otherwise eventually durable).
Flush(ctx context.Context) error

// Retrieves a block by its hash.
GetBlockByHash(ctx context.Context, hash []byte) (block *BinaryBlock, ok bool, err error)

// Retrieves a block by its height.
GetBlockByHeight(ctx context.Context, height uint64) (block *BinaryBlock, ok bool, err error)
// GetBlockByHash retrieves a block by its hash.
GetBlockByHash(ctx context.Context, hash []byte) (block Block, ok bool, err error)

// Retrieves a transaction by its hash.
GetTransactionByHash(ctx context.Context, hash []byte) (transaction *BinaryTransaction, ok bool, err error)
// GetBlockByHeight retrieves a block by its height.
GetBlockByHeight(ctx context.Context, height uint64) (block Block, ok bool, err error)

// Schedules pruning for all blocks with a height less than the given height. Pruning is asynchronous,
// and so this method does not provide any guarantees about when the pruning will complete. It is possible
// that some data will not be pruned if the database is closed before the pruning is scheduled.
// Prune schedules pruning of all blocks with height < lowestHeightToKeep.
// Pruning is asynchronous; this method does not guarantee when it will
// complete. Some data may not be pruned if the database is closed before
// pruning is scheduled.
Prune(ctx context.Context, lowestHeightToKeep uint64) error

// Retrieves the lowest block height in the database.
// GetLowestBlockHeight returns the lowest block height in the database.
GetLowestBlockHeight(ctx context.Context) (uint64, error)

// Retrieves the highest block height in the database.
// GetHighestBlockHeight returns the highest block height in the database.
GetHighestBlockHeight(ctx context.Context) (uint64, error)

// Closes the database and releases any resources. Any in-flight writes are fully flushed to disk before this
// method returns.
// Close shuts the database down and releases any resources. Any in-flight
// writes are fully flushed to disk before this method returns.
Close(ctx context.Context) error
}
Loading
Loading