Refactor blockchain database for modularity
This commit is contained in:
parent
b9047797cc
commit
f074007c75
@ -3,13 +3,11 @@ package blockchain
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database"
|
||||||
|
|
||||||
drand2 "github.com/Secured-Finance/dione/beacon/drand"
|
drand2 "github.com/Secured-Finance/dione/beacon/drand"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/beacon"
|
"github.com/Secured-Finance/dione/beacon"
|
||||||
@ -26,105 +24,30 @@ import (
|
|||||||
|
|
||||||
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
|
||||||
"github.com/ledgerwatch/lmdb-go/lmdb"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DefaultBlockDataPrefix = "blockdata_"
|
|
||||||
DefaultBlockHeaderPrefix = "header_"
|
|
||||||
DefaultMetadataIndexName = "metadata"
|
|
||||||
LatestBlockHeightKey = "latest_block_height"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrBlockNotFound = errors.New("block isn't found")
|
|
||||||
ErrLatestHeightNil = errors.New("latest block height is nil")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockChain struct {
|
type BlockChain struct {
|
||||||
// db-related
|
db database.Database
|
||||||
dbEnv *lmdb.Env
|
|
||||||
db lmdb.DBI
|
|
||||||
metadataIndex *utils.Index
|
|
||||||
heightIndex *utils.Index
|
|
||||||
|
|
||||||
bus EventBus.Bus
|
bus EventBus.Bus
|
||||||
miner *Miner
|
miner *Miner
|
||||||
drandBeacon *drand2.DrandBeacon
|
drandBeacon *drand2.DrandBeacon
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockChain(path string, bus EventBus.Bus, miner *Miner, db *drand2.DrandBeacon) (*BlockChain, error) {
|
func NewBlockChain(db database.Database, bus EventBus.Bus, miner *Miner, drand *drand2.DrandBeacon) (*BlockChain, error) {
|
||||||
chain := &BlockChain{
|
chain := &BlockChain{
|
||||||
|
db: db,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
miner: miner,
|
miner: miner,
|
||||||
drandBeacon: db,
|
drandBeacon: drand,
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure lmdb env
|
logrus.Info("Blockchain has been successfully initialized!")
|
||||||
env, err := lmdb.NewEnv()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = env.SetMaxDBs(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = env.SetMapSize(100 * 1024 * 1024 * 1024) // 100 GB
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(path, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = env.Open(path, 0, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.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
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.db = dbi
|
|
||||||
|
|
||||||
// create index instances
|
|
||||||
metadataIndex := utils.NewIndex(DefaultMetadataIndexName, env, dbi)
|
|
||||||
heightIndex := utils.NewIndex("height", env, dbi)
|
|
||||||
chain.metadataIndex = metadataIndex
|
|
||||||
chain.heightIndex = heightIndex
|
|
||||||
|
|
||||||
return chain, nil
|
return chain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) setLatestBlockHeight(height uint64) error {
|
|
||||||
err := bc.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *BlockChain) GetLatestBlockHeight() (uint64, error) {
|
func (bc *BlockChain) GetLatestBlockHeight() (uint64, error) {
|
||||||
height, err := bc.metadataIndex.GetUint64([]byte(LatestBlockHeightKey))
|
return bc.db.GetLatestBlockHeight()
|
||||||
if err != nil {
|
|
||||||
if err == utils.ErrIndexKeyNotFound {
|
|
||||||
return 0, ErrLatestHeightNil
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return height, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) StoreBlock(block *types2.Block) error {
|
func (bc *BlockChain) StoreBlock(block *types2.Block) error {
|
||||||
@ -142,43 +65,18 @@ func (bc *BlockChain) StoreBlock(block *types2.Block) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := bc.dbEnv.Update(func(txn *lmdb.Txn) error {
|
if err := bc.db.StoreBlock(block); err != nil {
|
||||||
data, err := cbor.Marshal(block.Data)
|
|
||||||
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(bc.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = txn.Put(bc.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update index "height -> block hash"
|
|
||||||
heightBytes := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(heightBytes, block.Header.Height)
|
|
||||||
err = bc.heightIndex.PutBytes(heightBytes, block.Header.Hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update latest block height
|
// update latest block height
|
||||||
height, err := bc.GetLatestBlockHeight()
|
height, err := bc.GetLatestBlockHeight()
|
||||||
if err != nil && err != ErrLatestHeightNil {
|
if err != nil && err != database.ErrLatestHeightNil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == ErrLatestHeightNil || block.Header.Height > height {
|
if err == database.ErrLatestHeightNil || block.Header.Height > height {
|
||||||
if err = bc.setLatestBlockHeight(block.Header.Height); err != nil {
|
if err = bc.db.SetLatestBlockHeight(block.Header.Height); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bc.bus.Publish("blockchain:latestBlockHeightUpdated", block)
|
bc.bus.Publish("blockchain:latestBlockHeightUpdated", block)
|
||||||
@ -188,114 +86,27 @@ func (bc *BlockChain) StoreBlock(block *types2.Block) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) HasBlock(blockHash []byte) (bool, error) {
|
func (bc *BlockChain) HasBlock(blockHash []byte) (bool, error) {
|
||||||
var blockExists bool
|
return bc.db.HasBlock(blockHash)
|
||||||
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
||||||
h := hex.EncodeToString(blockHash)
|
|
||||||
_, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h)) // 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) {
|
func (bc *BlockChain) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) {
|
||||||
var data []*types2.Transaction
|
return bc.db.FetchBlockData(blockHash)
|
||||||
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
||||||
h := hex.EncodeToString(blockHash)
|
|
||||||
blockData, err := txn.Get(bc.db, []byte(DefaultBlockDataPrefix+h))
|
|
||||||
if err != nil {
|
|
||||||
if lmdb.IsNotFound(err) {
|
|
||||||
return ErrBlockNotFound
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = cbor.Unmarshal(blockData, &data)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) {
|
func (bc *BlockChain) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) {
|
||||||
var blockHeader types2.BlockHeader
|
return bc.db.FetchBlockHeader(blockHash)
|
||||||
err := bc.dbEnv.View(func(txn *lmdb.Txn) error {
|
|
||||||
h := hex.EncodeToString(blockHash)
|
|
||||||
data, err := txn.Get(bc.db, []byte(DefaultBlockHeaderPrefix+h))
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) {
|
func (bc *BlockChain) FetchBlock(blockHash []byte) (*types2.Block, error) {
|
||||||
var block types2.Block
|
return bc.db.FetchBlock(blockHash)
|
||||||
header, err := bc.FetchBlockHeader(blockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
block.Header = header
|
|
||||||
|
|
||||||
data, err := bc.FetchBlockData(blockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
block.Data = data
|
|
||||||
|
|
||||||
return &block, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) {
|
func (bc *BlockChain) FetchBlockByHeight(height uint64) (*types2.Block, error) {
|
||||||
var heightBytes = make([]byte, 8)
|
return bc.db.FetchBlockByHeight(height)
|
||||||
binary.LittleEndian.PutUint64(heightBytes, height)
|
|
||||||
blockHash, err := bc.heightIndex.GetBytes(heightBytes)
|
|
||||||
if err != nil {
|
|
||||||
if err == utils.ErrIndexKeyNotFound {
|
|
||||||
return nil, ErrBlockNotFound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block, err := bc.FetchBlock(blockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return block, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) {
|
func (bc *BlockChain) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) {
|
||||||
var heightBytes = make([]byte, 8)
|
return bc.db.FetchBlockHeaderByHeight(height)
|
||||||
binary.LittleEndian.PutUint64(heightBytes, height)
|
|
||||||
blockHash, err := bc.heightIndex.GetBytes(heightBytes)
|
|
||||||
if err != nil {
|
|
||||||
if err == utils.ErrIndexKeyNotFound {
|
|
||||||
return nil, ErrBlockNotFound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blockHeader, err := bc.FetchBlockHeader(blockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return blockHeader, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) ValidateBlock(block *types2.Block) error {
|
func (bc *BlockChain) ValidateBlock(block *types2.Block) error {
|
||||||
|
25
blockchain/database/database.go
Normal file
25
blockchain/database/database.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBlockNotFound = errors.New("block isn't found")
|
||||||
|
ErrLatestHeightNil = errors.New("latest block height is nil")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database interface {
|
||||||
|
StoreBlock(block *types.Block) error
|
||||||
|
HasBlock(blockhash []byte) (bool, error)
|
||||||
|
FetchBlockData(blockHash []byte) ([]*types2.Transaction, error)
|
||||||
|
FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error)
|
||||||
|
FetchBlock(blockHash []byte) (*types2.Block, error)
|
||||||
|
FetchBlockByHeight(height uint64) (*types2.Block, error)
|
||||||
|
FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error)
|
||||||
|
GetLatestBlockHeight() (uint64, error)
|
||||||
|
SetLatestBlockHeight(height uint64) error
|
||||||
|
}
|
239
blockchain/database/lmdb/database.go
Normal file
239
blockchain/database/lmdb/database.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package lmdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database"
|
||||||
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
"github.com/ledgerwatch/lmdb-go/lmdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultBlockDataPrefix = "blockdata_"
|
||||||
|
DefaultBlockHeaderPrefix = "header_"
|
||||||
|
DefaultMetadataIndexName = "metadata"
|
||||||
|
LatestBlockHeightKey = "latest_block_height"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
dbEnv *lmdb.Env
|
||||||
|
db lmdb.DBI
|
||||||
|
metadataIndex *Index
|
||||||
|
heightIndex *Index
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabase(path string) (*Database, error) {
|
||||||
|
db := &Database{}
|
||||||
|
|
||||||
|
// configure lmdb env
|
||||||
|
env, err := lmdb.NewEnv()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = env.SetMaxDBs(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = env.SetMapSize(100 * 1024 * 1024 * 1024) // 100 GB
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(path, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = env.Open(path, 0, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.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
|
||||||
|
}
|
||||||
|
|
||||||
|
db.db = dbi
|
||||||
|
|
||||||
|
// create index instances
|
||||||
|
metadataIndex := NewIndex(DefaultMetadataIndexName, env, dbi)
|
||||||
|
heightIndex := NewIndex("height", env, dbi)
|
||||||
|
db.metadataIndex = metadataIndex
|
||||||
|
db.heightIndex = heightIndex
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) StoreBlock(block *types2.Block) error {
|
||||||
|
err := d.dbEnv.Update(func(txn *lmdb.Txn) error {
|
||||||
|
data, err := cbor.Marshal(block.Data)
|
||||||
|
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(d.db, []byte(DefaultBlockDataPrefix+blockHash), data, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = txn.Put(d.db, []byte(DefaultBlockHeaderPrefix+blockHash), headerData, 0) // store header separately for easy fetching
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update index "height -> block hash"
|
||||||
|
heightBytes := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(heightBytes, block.Header.Height)
|
||||||
|
err = d.heightIndex.PutBytes(heightBytes, block.Header.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) HasBlock(blockHash []byte) (bool, error) {
|
||||||
|
var blockExists bool
|
||||||
|
err := d.dbEnv.View(func(txn *lmdb.Txn) error {
|
||||||
|
h := hex.EncodeToString(blockHash)
|
||||||
|
_, err := txn.Get(d.db, []byte(DefaultBlockHeaderPrefix+h)) // 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) {
|
||||||
|
var data []*types2.Transaction
|
||||||
|
err := d.dbEnv.View(func(txn *lmdb.Txn) error {
|
||||||
|
h := hex.EncodeToString(blockHash)
|
||||||
|
blockData, err := txn.Get(d.db, []byte(DefaultBlockDataPrefix+h))
|
||||||
|
if err != nil {
|
||||||
|
if lmdb.IsNotFound(err) {
|
||||||
|
return database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = cbor.Unmarshal(blockData, &data)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) {
|
||||||
|
var blockHeader types2.BlockHeader
|
||||||
|
err := d.dbEnv.View(func(txn *lmdb.Txn) error {
|
||||||
|
h := hex.EncodeToString(blockHash)
|
||||||
|
data, err := txn.Get(d.db, []byte(DefaultBlockHeaderPrefix+h))
|
||||||
|
if err != nil {
|
||||||
|
if lmdb.IsNotFound(err) {
|
||||||
|
return database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = cbor.Unmarshal(data, &blockHeader)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &blockHeader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlock(blockHash []byte) (*types2.Block, error) {
|
||||||
|
var block types2.Block
|
||||||
|
header, err := d.FetchBlockHeader(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
block.Header = header
|
||||||
|
|
||||||
|
data, err := d.FetchBlockData(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
block.Data = data
|
||||||
|
|
||||||
|
return &block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockByHeight(height uint64) (*types2.Block, error) {
|
||||||
|
var heightBytes = make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(heightBytes, height)
|
||||||
|
blockHash, err := d.heightIndex.GetBytes(heightBytes)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrIndexKeyNotFound {
|
||||||
|
return nil, database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block, err := d.FetchBlock(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) {
|
||||||
|
var heightBytes = make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(heightBytes, height)
|
||||||
|
blockHash, err := d.heightIndex.GetBytes(heightBytes)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrIndexKeyNotFound {
|
||||||
|
return nil, database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockHeader, err := d.FetchBlockHeader(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return blockHeader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) GetLatestBlockHeight() (uint64, error) {
|
||||||
|
height, err := d.metadataIndex.GetUint64([]byte(LatestBlockHeightKey))
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrIndexKeyNotFound {
|
||||||
|
return 0, database.ErrLatestHeightNil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) SetLatestBlockHeight(height uint64) error {
|
||||||
|
err := d.metadataIndex.PutUint64([]byte(LatestBlockHeightKey), height)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package utils
|
package lmdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
90
blockchain/database/memory/database.go
Normal file
90
blockchain/database/memory/database.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database"
|
||||||
|
|
||||||
|
types2 "github.com/Secured-Finance/dione/blockchain/types"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LatestBlockHeightKey = "latest_block_height"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
db *cache.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabase() *Database {
|
||||||
|
return &Database{
|
||||||
|
db: cache.New(cache.NoExpiration, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) StoreBlock(block *types2.Block) error {
|
||||||
|
h := hex.EncodeToString(block.Header.Hash)
|
||||||
|
d.db.SetDefault(h, block)
|
||||||
|
d.db.SetDefault(fmt.Sprintf("height/%d", block.Header.Height), block)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) HasBlock(blockHash []byte) (bool, error) {
|
||||||
|
_, ok := d.db.Get(hex.EncodeToString(blockHash))
|
||||||
|
return ok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockData(blockHash []byte) ([]*types2.Transaction, error) {
|
||||||
|
b, err := d.FetchBlock(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockHeader(blockHash []byte) (*types2.BlockHeader, error) {
|
||||||
|
b, err := d.FetchBlock(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlock(blockHash []byte) (*types2.Block, error) {
|
||||||
|
b, ok := d.db.Get(hex.EncodeToString(blockHash))
|
||||||
|
if !ok {
|
||||||
|
return nil, database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
return b.(*types2.Block), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockByHeight(height uint64) (*types2.Block, error) {
|
||||||
|
b, ok := d.db.Get(fmt.Sprintf("height/%d", height))
|
||||||
|
if !ok {
|
||||||
|
return nil, database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
return b.(*types2.Block), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) FetchBlockHeaderByHeight(height uint64) (*types2.BlockHeader, error) {
|
||||||
|
b, ok := d.db.Get(fmt.Sprintf("height/%d", height))
|
||||||
|
if !ok {
|
||||||
|
return nil, database.ErrBlockNotFound
|
||||||
|
}
|
||||||
|
return b.(*types2.Block).Header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) GetLatestBlockHeight() (uint64, error) {
|
||||||
|
height, ok := d.db.Get(LatestBlockHeightKey)
|
||||||
|
if !ok {
|
||||||
|
return 0, database.ErrLatestHeightNil
|
||||||
|
}
|
||||||
|
return height.(uint64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) SetLatestBlockHeight(height uint64) error {
|
||||||
|
d.db.SetDefault(LatestBlockHeightKey, height)
|
||||||
|
return nil
|
||||||
|
}
|
@ -7,8 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CacheTypeInMemory = "in-memory"
|
CacheTypeInMemory = "memory"
|
||||||
CacheTypeRedis = "redis"
|
CacheTypeRedis = "redis"
|
||||||
|
|
||||||
|
BlockchainDatabaseInMemory = "memory"
|
||||||
|
BlockChainDatabaseLMDB = "lmdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -60,7 +63,10 @@ type RedisConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BlockchainConfig struct {
|
type BlockchainConfig struct {
|
||||||
|
DatabaseType string `mapstructure:"database_type"`
|
||||||
|
LMDB struct {
|
||||||
DatabasePath string `mapstructure:"database_path"`
|
DatabasePath string `mapstructure:"database_path"`
|
||||||
|
} `mapstructure:"lmdb"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a new config based on default values or provided .env file
|
// NewConfig creates a new config based on default values or provided .env file
|
||||||
|
@ -202,8 +202,9 @@ func Start() {
|
|||||||
providePeerDiscovery,
|
providePeerDiscovery,
|
||||||
drand2.NewDrandBeacon,
|
drand2.NewDrandBeacon,
|
||||||
pool.NewMempool,
|
pool.NewMempool,
|
||||||
|
provideBlockchainDatabase,
|
||||||
blockchain.NewMiner,
|
blockchain.NewMiner,
|
||||||
provideBlockChain,
|
blockchain.NewBlockChain,
|
||||||
sync.NewSyncManager,
|
sync.NewSyncManager,
|
||||||
provideNetworkRPCHost,
|
provideNetworkRPCHost,
|
||||||
NewNetworkService,
|
NewNetworkService,
|
||||||
|
@ -11,6 +11,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database/memory"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database/lmdb"
|
||||||
|
|
||||||
|
"github.com/Secured-Finance/dione/blockchain/database"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/cache/inmemory"
|
"github.com/Secured-Finance/dione/cache/inmemory"
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/cache/redis"
|
"github.com/Secured-Finance/dione/cache/redis"
|
||||||
@ -24,12 +30,8 @@ import (
|
|||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/asaskevich/EventBus"
|
|
||||||
|
|
||||||
"github.com/Secured-Finance/dione/blockchain"
|
"github.com/Secured-Finance/dione/blockchain"
|
||||||
|
|
||||||
drand2 "github.com/Secured-Finance/dione/beacon/drand"
|
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p-core/protocol"
|
"github.com/libp2p/go-libp2p-core/protocol"
|
||||||
|
|
||||||
gorpc "github.com/libp2p/go-libp2p-gorpc"
|
gorpc "github.com/libp2p/go-libp2p-gorpc"
|
||||||
@ -64,6 +66,29 @@ func provideCacheManager(cfg *config.Config) cache.CacheManager {
|
|||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func provideBlockchainDatabase(cfg *config.Config) (database.Database, error) {
|
||||||
|
var db database.Database
|
||||||
|
switch cfg.Blockchain.DatabaseType {
|
||||||
|
case config.BlockchainDatabaseInMemory:
|
||||||
|
db = memory.NewDatabase()
|
||||||
|
case config.BlockChainDatabaseLMDB:
|
||||||
|
{
|
||||||
|
if cfg.Blockchain.LMDB.DatabasePath == "" {
|
||||||
|
return nil, fmt.Errorf("database path for lmdb database is empty")
|
||||||
|
}
|
||||||
|
l, err := lmdb.NewDatabase(cfg.Blockchain.LMDB.DatabasePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db = l
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
db = memory.NewDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: do we really need this?
|
// FIXME: do we really need this?
|
||||||
//func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) {
|
//func provideWallet(peerID peer.ID, privKey []byte) (*wallet.LocalWallet, error) {
|
||||||
// // TODO make persistent keystore
|
// // TODO make persistent keystore
|
||||||
@ -163,16 +188,6 @@ func providePeerDiscovery(baddrs []multiaddr.Multiaddr, h host.Host) discovery.D
|
|||||||
return pexDiscovery
|
return pexDiscovery
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideBlockChain(config *config.Config, bus EventBus.Bus, miner *blockchain.Miner, db *drand2.DrandBeacon) *blockchain.BlockChain {
|
|
||||||
bc, err := blockchain.NewBlockChain(config.Blockchain.DatabasePath, bus, miner, db)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("Failed to initialize blockchain storage: %s", err.Error())
|
|
||||||
}
|
|
||||||
logrus.Info("Blockchain storage has been successfully initialized!")
|
|
||||||
|
|
||||||
return bc
|
|
||||||
}
|
|
||||||
|
|
||||||
func provideDirectRPCClient(h host.Host) *gorpc.Client {
|
func provideDirectRPCClient(h host.Host) *gorpc.Client {
|
||||||
return gorpc.NewClient(h, DioneProtocolID)
|
return gorpc.NewClient(h, DioneProtocolID)
|
||||||
}
|
}
|
||||||
@ -303,7 +318,7 @@ func configureMiner(m *blockchain.Miner, b *blockchain.BlockChain) {
|
|||||||
|
|
||||||
func initializeBlockchain(bc *blockchain.BlockChain) {
|
func initializeBlockchain(bc *blockchain.BlockChain) {
|
||||||
_, err := bc.GetLatestBlockHeight()
|
_, err := bc.GetLatestBlockHeight()
|
||||||
if err == blockchain.ErrLatestHeightNil {
|
if err == database.ErrLatestHeightNil {
|
||||||
gBlock := types2.GenesisBlock()
|
gBlock := types2.GenesisBlock()
|
||||||
err = bc.StoreBlock(gBlock) // commit genesis block
|
err = bc.StoreBlock(gBlock) // commit genesis block
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user