2021-05-14 20:32:49 +00:00
|
|
|
package pool
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
|
2021-05-19 20:54:36 +00:00
|
|
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
2021-05-14 20:32:49 +00:00
|
|
|
"github.com/fxamacker/cbor/v2"
|
2021-05-19 20:54:36 +00:00
|
|
|
|
2021-05-14 20:32:49 +00:00
|
|
|
"github.com/ledgerwatch/lmdb-go/lmdb"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
DefaultBlockPrefix = "block_"
|
|
|
|
DefaultBlockHeaderPrefix = "header_"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrBlockNotFound = errors.New("block isn't found")
|
|
|
|
)
|
|
|
|
|
|
|
|
type BlockPool struct {
|
|
|
|
dbEnv *lmdb.Env
|
|
|
|
db lmdb.DBI
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewBlockPool(path string) (*BlockPool, error) {
|
|
|
|
pool := &BlockPool{}
|
|
|
|
|
|
|
|
// configure lmdb env
|
|
|
|
env, err := lmdb.NewEnv()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = env.SetMapSize(100 * 1024 * 1024 * 1024) // 100 GB
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = env.Open(path, 0, 0664)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pool.dbEnv = env
|
|
|
|
|
|
|
|
var dbi lmdb.DBI
|
|
|
|
err = env.Update(func(txn *lmdb.Txn) error {
|
|
|
|
dbi, err = txn.OpenDBI("blocks", lmdb.Create)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pool.db = dbi
|
|
|
|
|
|
|
|
return pool, nil
|
|
|
|
}
|
|
|
|
|
2021-05-19 20:54:36 +00:00
|
|
|
func (bp *BlockPool) StoreBlock(block *types2.Block) error {
|
2021-05-14 20:32:49 +00:00
|
|
|
return bp.dbEnv.Update(func(txn *lmdb.Txn) error {
|
|
|
|
data, err := cbor.Marshal(block)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
headerData, err := cbor.Marshal(block.Header)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
blockHash := hex.EncodeToString(block.Header.Hash)
|
|
|
|
err = txn.Put(bp.db, []byte(DefaultBlockPrefix+blockHash), data, 0)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = txn.Put(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bp *BlockPool) HasBlock(blockHash string) (bool, error) {
|
|
|
|
var blockExists bool
|
|
|
|
err := bp.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
|
|
_, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash)) // try to fetch block header
|
|
|
|
if err != nil {
|
|
|
|
if lmdb.IsNotFound(err) {
|
|
|
|
blockExists = false
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
blockExists = true
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return blockExists, nil
|
|
|
|
}
|
|
|
|
|
2021-05-19 20:54:36 +00:00
|
|
|
func (bp *BlockPool) FetchBlock(blockHash string) (*types2.Block, error) {
|
|
|
|
var block types2.Block
|
2021-05-14 20:32:49 +00:00
|
|
|
err := bp.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
|
|
data, err := txn.Get(bp.db, []byte(DefaultBlockPrefix+blockHash))
|
|
|
|
if err != nil {
|
|
|
|
if lmdb.IsNotFound(err) {
|
|
|
|
return ErrBlockNotFound
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = cbor.Unmarshal(data, &block)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &block, nil
|
|
|
|
}
|
|
|
|
|
2021-05-19 20:54:36 +00:00
|
|
|
func (bp *BlockPool) FetchBlockHeader(blockHash string) (*types2.BlockHeader, error) {
|
|
|
|
var blockHeader types2.BlockHeader
|
2021-05-14 20:32:49 +00:00
|
|
|
err := bp.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
|
|
data, err := txn.Get(bp.db, []byte(DefaultBlockHeaderPrefix+blockHash))
|
|
|
|
if err != nil {
|
|
|
|
if lmdb.IsNotFound(err) {
|
|
|
|
return ErrBlockNotFound
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = cbor.Unmarshal(data, &blockHeader)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &blockHeader, nil
|
|
|
|
}
|