erigon-pulse/kv/rawdbv3/txnum.go
2023-01-25 16:29:23 +07:00

187 lines
4.0 KiB
Go

package rawdbv3
import (
"encoding/binary"
"fmt"
"sort"
"github.com/ledgerwatch/erigon-lib/kv"
)
type txNums struct{}
var TxNums txNums
// Min - returns maxTxNum in given block. If block not found - return last available value (`latest`/`pending` state)
func (txNums) Max(tx kv.Tx, blockNum uint64) (maxTxNum uint64, err error) {
var k [8]byte
binary.BigEndian.PutUint64(k[:], blockNum)
c, err := tx.Cursor(kv.MaxTxNum)
if err != nil {
return 0, err
}
defer c.Close()
_, v, err := c.SeekExact(k[:])
if err != nil {
return 0, err
}
if len(v) == 0 {
_, v, err = c.Last()
if err != nil {
return 0, err
}
if len(v) == 0 {
return 0, nil
}
}
return binary.BigEndian.Uint64(v), nil
}
// Min = `max(blockNum-1)+1` returns minTxNum in given block. If block not found - return last available value (`latest`/`pending` state)
func (txNums) Min(tx kv.Tx, blockNum uint64) (maxTxNum uint64, err error) {
if blockNum == 0 {
return 0, nil
}
var k [8]byte
binary.BigEndian.PutUint64(k[:], blockNum-1)
c, err := tx.Cursor(kv.MaxTxNum)
if err != nil {
return 0, err
}
defer c.Close()
_, v, err := c.SeekExact(k[:])
if err != nil {
return 0, err
}
if len(v) == 0 {
_, v, err = c.Last()
if err != nil {
return 0, err
}
if len(v) == 0 {
return 0, nil
}
}
return binary.BigEndian.Uint64(v) + 1, nil
}
func (txNums) Append(tx kv.RwTx, blockNum, maxTxNum uint64) (err error) {
lastK, err := LastKey(tx, kv.MaxTxNum)
if err != nil {
return err
}
if len(lastK) != 0 {
lastBlockNum := binary.BigEndian.Uint64(lastK)
if lastBlockNum > 1 && lastBlockNum+1 != blockNum { //allow genesis
return fmt.Errorf("append with gap blockNum=%d, but current heigh=%d", blockNum, lastBlockNum)
}
}
var k, v [8]byte
binary.BigEndian.PutUint64(k[:], blockNum)
binary.BigEndian.PutUint64(v[:], maxTxNum)
if err := tx.Append(kv.MaxTxNum, k[:], v[:]); err != nil {
return err
}
return nil
}
func (txNums) WriteForGenesis(tx kv.RwTx, maxTxNum uint64) (err error) {
var k, v [8]byte
binary.BigEndian.PutUint64(k[:], 0)
binary.BigEndian.PutUint64(v[:], maxTxNum)
return tx.Put(kv.MaxTxNum, k[:], v[:])
}
func (txNums) Truncate(tx kv.RwTx, blockNum uint64) (err error) {
var seek [8]byte
binary.BigEndian.PutUint64(seek[:], blockNum)
c, err := tx.RwCursor(kv.MaxTxNum)
if err != nil {
return err
}
defer c.Close()
for k, _, err := c.Seek(seek[:]); k != nil; k, _, err = c.Next() {
if err != nil {
return err
}
if err = c.DeleteCurrent(); err != nil {
return err
}
}
return nil
}
func (txNums) FindBlockNum(tx kv.Tx, endTxNumMinimax uint64) (ok bool, blockNum uint64, err error) {
var seek [8]byte
c, err := tx.Cursor(kv.MaxTxNum)
if err != nil {
return false, 0, err
}
defer c.Close()
cnt, err := c.Count()
if err != nil {
return false, 0, err
}
blockNum = uint64(sort.Search(int(cnt), func(i int) bool {
binary.BigEndian.PutUint64(seek[:], uint64(i))
var v []byte
_, v, err = c.SeekExact(seek[:])
return binary.BigEndian.Uint64(v) >= endTxNumMinimax
}))
if err != nil {
return false, 0, err
}
if blockNum == cnt {
return false, 0, nil
}
return true, blockNum, nil
}
// LastKey
func LastKey(tx kv.Tx, table string) ([]byte, error) {
c, err := tx.Cursor(table)
if err != nil {
return nil, err
}
defer c.Close()
k, _, err := c.Last()
if err != nil {
return nil, err
}
return k, nil
}
// Last - candidate on move to kv.Tx interface
func Last(tx kv.Tx, table string) ([]byte, []byte, error) {
c, err := tx.Cursor(table)
if err != nil {
return nil, nil, err
}
defer c.Close()
k, v, err := c.Last()
if err != nil {
return nil, nil, err
}
return k, v, nil
}
// SecondKey - useful if table always has zero-key (for example genesis block)
func SecondKey(tx kv.Tx, table string) ([]byte, error) {
c, err := tx.Cursor(table)
if err != nil {
return nil, err
}
defer c.Close()
_, _, err = c.First()
if err != nil {
return nil, err
}
k, _, err := c.Next()
if err != nil {
return nil, err
}
return k, nil
}