Implement consensus state change watching to fix stage race conditions

This commit is contained in:
ChronosX88 2021-07-27 00:18:16 +03:00
parent 430a994a76
commit d179ffcd76
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
13 changed files with 322 additions and 247 deletions

View File

@ -181,10 +181,6 @@ func (bc *BlockChain) StoreBlock(block *types2.Block) error {
if err = bc.setLatestBlockHeight(block.Header.Height); err != nil {
return err
}
} else if block.Header.Height > height {
if err = bc.setLatestBlockHeight(block.Header.Height); err != nil {
return err
}
bc.bus.Publish("blockchain:latestBlockHeightUpdated", block)
}
bc.bus.Publish("blockchain:blockCommitted", block)

View File

@ -4,7 +4,10 @@ import (
"errors"
"fmt"
"math/big"
"sync"
"github.com/libp2p/go-libp2p-core/host"
"github.com/asaskevich/EventBus"
"github.com/Secured-Finance/dione/beacon"
"github.com/Secured-Finance/dione/types"
@ -27,34 +30,54 @@ var (
)
type Miner struct {
address peer.ID
ethAddress common.Address
mutex sync.Mutex
ethClient *ethclient.EthereumClient
minerStake *big.Int
networkStake *big.Int
privateKey crypto.PrivKey
mempool *pool.Mempool
bus EventBus.Bus
address peer.ID
ethClient *ethclient.EthereumClient
minerStake *big.Int
networkStake *big.Int
privateKey crypto.PrivKey
mempool *pool.Mempool
latestBlockHeader *types2.BlockHeader
blockchain *BlockChain
}
func NewMiner(
address peer.ID,
ethAddress common.Address,
h host.Host,
ethClient *ethclient.EthereumClient,
privateKey crypto.PrivKey,
mempool *pool.Mempool,
bus EventBus.Bus,
) *Miner {
return &Miner{
address: address,
ethAddress: ethAddress,
m := &Miner{
address: h.ID(),
ethClient: ethClient,
privateKey: privateKey,
mempool: mempool,
bus: bus,
}
return m
}
func (m *Miner) SetBlockchainInstance(b *BlockChain) {
m.blockchain = b
m.bus.SubscribeAsync("blockchain:latestBlockHeightUpdated", func(block *types2.Block) {
m.latestBlockHeader = block.Header
}, true)
height, _ := m.blockchain.GetLatestBlockHeight()
header, err := m.blockchain.FetchBlockHeaderByHeight(height)
if err != nil {
logrus.WithField("err", err.Error()).Fatal("Failed to initialize miner subsystem")
}
m.latestBlockHeader = header
logrus.Info("Mining subsystem has been initialized!")
}
func (m *Miner) UpdateCurrentStakeInfo() error {
mStake, err := m.ethClient.GetMinerStake(m.ethAddress)
mStake, err := m.ethClient.GetMinerStake(*m.ethClient.GetEthAddress())
if err != nil {
logrus.Warn("Can't get miner stake", err)
@ -92,15 +115,19 @@ func (m *Miner) GetStakeInfo(miner common.Address) (*big.Int, *big.Int, error) {
return mStake, nStake, nil
}
func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHeader *types2.BlockHeader) (*types2.Block, error) {
logrus.WithField("height", lastBlockHeader.Height+1).Debug("Trying to mine new block...")
func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64) (*types2.Block, error) {
if m.latestBlockHeader == nil {
return nil, fmt.Errorf("latest block header is null")
}
logrus.WithField("height", m.latestBlockHeader.Height+1).Debug("Trying to mine new block...")
if err := m.UpdateCurrentStakeInfo(); err != nil {
return nil, fmt.Errorf("failed to update miner stake: %w", err)
}
winner, err := isRoundWinner(
lastBlockHeader.Height+1,
m.latestBlockHeader.Height+1,
m.address,
randomness,
randomnessRound,
@ -113,16 +140,18 @@ func (m *Miner) MineBlock(randomness []byte, randomnessRound uint64, lastBlockHe
}
if winner == nil {
logrus.WithField("height", lastBlockHeader.Height+1).Debug("Block is not mined because we are not leader in consensus round")
logrus.WithField("height", m.latestBlockHeader.Height+1).Debug("Block is not mined because we are not leader in consensus round")
return nil, nil
}
logrus.WithField("height", m.latestBlockHeader.Height+1).Infof("We have been elected in the current consensus round")
txs := m.mempool.GetTransactionsForNewBlock()
if txs == nil {
return nil, ErrNoTxForBlock // skip new consensus round because there is no transaction for processing
}
newBlock, err := types2.CreateBlock(lastBlockHeader, txs, m.ethAddress, m.privateKey, winner)
newBlock, err := types2.CreateBlock(m.latestBlockHeader, txs, *m.ethClient.GetEthAddress(), m.privateKey, winner)
if err != nil {
return nil, fmt.Errorf("failed to create new block: %w", err)
}

View File

@ -1,101 +0,0 @@
package pool
import (
"bytes"
"encoding/hex"
"fmt"
"time"
"github.com/asaskevich/EventBus"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/cache"
)
// BlockPool is pool for blocks that isn't not validated or committed yet
type BlockPool struct {
mempool *Mempool
knownBlocks cache.Cache
acceptedBlocks cache.Cache
bus EventBus.Bus
}
func NewBlockPool(mp *Mempool, bus EventBus.Bus) (*BlockPool, error) {
bp := &BlockPool{
acceptedBlocks: cache.NewInMemoryCache(), // here we need to use separate cache
knownBlocks: cache.NewInMemoryCache(),
mempool: mp,
bus: bus,
}
return bp, nil
}
func (bp *BlockPool) AddBlock(block *types.Block) error {
err := bp.knownBlocks.StoreWithTTL(hex.EncodeToString(block.Header.Hash), block, 10*time.Minute)
if err != nil {
return err
}
logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered")
bp.bus.Publish("blockpool:knownBlockAdded", block)
return nil
}
func (bp *BlockPool) GetBlock(blockhash []byte) (*types.Block, error) {
var block types.Block
err := bp.knownBlocks.Get(hex.EncodeToString(blockhash), &block)
return &block, err
}
// PruneBlocks cleans known blocks list. It is called when new consensus round starts.
func (bp *BlockPool) PruneBlocks() {
for k := range bp.knownBlocks.Items() {
bp.knownBlocks.Delete(k)
}
bp.bus.Publish("blockpool:pruned")
}
func (bp *BlockPool) AddAcceptedBlock(block *types.Block) error {
err := bp.acceptedBlocks.Store(hex.EncodeToString(block.Header.Hash), block)
if err != nil {
return err
}
bp.bus.Publish("blockpool:acceptedBlockAdded", block)
return nil
}
func (bp *BlockPool) GetAllAcceptedBlocks() []*types.Block {
var blocks []*types.Block
for _, v := range bp.acceptedBlocks.Items() {
blocks = append(blocks, v.(*types.Block))
}
return blocks
}
// PruneAcceptedBlocks cleans accepted blocks list. It is called when new consensus round starts.
func (bp *BlockPool) PruneAcceptedBlocks(committedBlock *types.Block) {
for k, v := range bp.acceptedBlocks.Items() {
block := v.(*types.Block)
for _, v := range block.Data {
if !containsTx(committedBlock.Data, v) {
v.MerkleProof = nil
err := bp.mempool.StoreTx(v) // return transactions back to mempool
if err != nil {
logrus.Error(err)
}
}
}
bp.acceptedBlocks.Delete(k)
}
}
func containsTx(s []*types.Transaction, e *types.Transaction) bool {
for _, a := range s {
if bytes.Equal(a.Hash, e.Hash) {
return true
}
}
return false
}

View File

@ -98,21 +98,14 @@ func (sm *syncManager) doInitialBlockPoolSync() error {
return nil
}
ourLastHeight, err := sm.blockpool.GetLatestBlockHeight()
if err == blockchain.ErrLatestHeightNil {
gBlock := types2.GenesisBlock()
err = sm.blockpool.StoreBlock(gBlock) // commit genesis block
if err != nil {
return err
}
}
ourLastHeight, _ := sm.blockpool.GetLatestBlockHeight()
if sm.bootstrapPeer == "" {
return nil // FIXME
}
var reply wire.LastBlockHeightReply
err = sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply)
err := sm.rpcClient.Call(sm.bootstrapPeer, "NetworkService", "LastBlockHeight", nil, &reply)
if err != nil {
return err
}

1
cache/cache.go vendored
View File

@ -13,4 +13,5 @@ type Cache interface {
Get(key string, value interface{}) error
Delete(key string)
Items() map[string]interface{}
Exists(key string) bool
}

View File

@ -62,3 +62,8 @@ func (imc *InMemoryCache) Items() map[string]interface{} {
}
return m
}
func (imc *InMemoryCache) Exists(key string) bool {
_, exists := imc.cache.Get(key)
return exists
}

11
cache/redis_cache.go vendored
View File

@ -85,3 +85,14 @@ func (rc *RedisCache) Delete(key string) {
func (rc *RedisCache) Items() map[string]interface{} {
return nil // TODO
}
func (rc *RedisCache) Exists(key string) bool {
res := rc.Client.Exists(context.TODO(), key)
if res.Err() != nil {
return false
}
if res.Val() == 0 {
return false
}
return true
}

View File

@ -2,4 +2,4 @@ package config
import "math/big"
var ExpectedLeadersPerEpoch = big.NewInt(1)
var ExpectedLeadersPerEpoch = big.NewInt(2)

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"math/big"
"sync"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
@ -38,39 +37,20 @@ var (
ErrNoAcceptedBlocks = errors.New("there is no accepted blocks")
)
type StateStatus uint8
const (
StateStatusUnknown = iota
StateStatusPrePrepared
StateStatusPrepared
StateStatusCommited
)
type PBFTConsensusManager struct {
bus EventBus.Bus
psb *pubsub.PubSubRouter
minApprovals int // FIXME
privKey crypto.PrivKey
msgLog *ConsensusMessageLog
validator *ConsensusValidator
ethereumClient *ethclient.EthereumClient
miner *blockchain.Miner
blockPool *pool.BlockPool
mempool *pool.Mempool
blockchain *blockchain.BlockChain
state *State
address peer.ID
}
type State struct {
mutex sync.Mutex
drandRound uint64
randomness []byte
blockHeight uint64
status StateStatus
ready bool
bus EventBus.Bus
psb *pubsub.PubSubRouter
minApprovals int // FIXME
privKey crypto.PrivKey
msgLog *ConsensusMessageLog
validator *ConsensusValidator
ethereumClient *ethclient.EthereumClient
miner *blockchain.Miner
consensusRoundPool *ConsensusRoundPool
mempool *pool.Mempool
blockchain *blockchain.BlockChain
address peer.ID
stateChangeChannels map[string]map[State][]chan bool
}
func NewPBFTConsensusManager(
@ -81,28 +61,25 @@ func NewPBFTConsensusManager(
ethereumClient *ethclient.EthereumClient,
miner *blockchain.Miner,
bc *blockchain.BlockChain,
bp *pool.BlockPool,
bp *ConsensusRoundPool,
db *drand2.DrandBeacon,
mempool *pool.Mempool,
address peer.ID,
) *PBFTConsensusManager {
pcm := &PBFTConsensusManager{
psb: psb,
miner: miner,
validator: NewConsensusValidator(miner, bc, db),
msgLog: NewConsensusMessageLog(),
minApprovals: minApprovals,
privKey: privKey,
ethereumClient: ethereumClient,
state: &State{
ready: false,
status: StateStatusUnknown,
},
bus: bus,
blockPool: bp,
mempool: mempool,
blockchain: bc,
address: address,
psb: psb,
miner: miner,
validator: NewConsensusValidator(miner, bc, db),
msgLog: NewConsensusMessageLog(),
minApprovals: minApprovals,
privKey: privKey,
ethereumClient: ethereumClient,
bus: bus,
consensusRoundPool: bp,
mempool: mempool,
blockchain: bc,
address: address,
stateChangeChannels: map[string]map[State][]chan bool{},
}
return pcm
@ -115,8 +92,6 @@ func (pcm *PBFTConsensusManager) Run() {
pcm.bus.SubscribeAsync("beacon:newEntry", func(entry types2.BeaconEntry) {
pcm.onNewBeaconEntry(entry)
}, true)
height, _ := pcm.blockchain.GetLatestBlockHeight()
pcm.state.blockHeight = height + 1
}
func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error {
@ -125,15 +100,12 @@ func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error {
return err
}
pcm.psb.BroadcastToServiceTopic(prePrepareMsg)
pcm.blockPool.AddBlock(blk)
pcm.consensusRoundPool.AddConsensusInfo(blk)
logrus.WithField("blockHash", fmt.Sprintf("%x", blk.Header.Hash)).Debugf("Entered into PREPREPARED state")
pcm.state.status = StateStatusPrePrepared
return nil
}
func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage) {
//pcm.state.mutex.Lock()
//defer pcm.state.mutex.Unlock()
var prePrepare types.PrePrepareMessage
err := cbor.Unmarshal(message.Payload, &prePrepare)
if err != nil {
@ -166,7 +138,18 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage)
"blockHash": fmt.Sprintf("%x", cmsg.Block.Header.Hash),
"from": message.From.String(),
}).Debug("Received PREPREPARE message")
pcm.blockPool.AddBlock(cmsg.Block)
pcm.consensusRoundPool.AddConsensusInfo(cmsg.Block)
encodedHash := hex.EncodeToString(cmsg.Blockhash)
if m, ok := pcm.stateChangeChannels[encodedHash]; ok {
if channels, ok := m[StateStatusPrePrepared]; ok {
for _, v := range channels {
v <- true
close(v)
delete(pcm.stateChangeChannels, encodedHash)
}
}
}
prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey)
if err != nil {
@ -176,12 +159,9 @@ func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.PubSubMessage)
logrus.WithField("blockHash", fmt.Sprintf("%x", prePrepare.Block.Header.Hash)).Debugf("Entered into PREPREPARED state")
pcm.psb.BroadcastToServiceTopic(prepareMsg)
pcm.state.status = StateStatusPrePrepared
}
func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) {
//pcm.state.mutex.Lock()
//defer pcm.state.mutex.Unlock()
var prepare types.PrepareMessage
err := cbor.Unmarshal(message.Payload, &prepare)
if err != nil {
@ -196,9 +176,18 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) {
Signature: prepare.Signature,
}
if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) {
logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received unknown block %x", cmsg.Blockhash)
return
if _, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) {
encodedHash := hex.EncodeToString(cmsg.Blockhash)
logrus.WithField("blockHash", encodedHash).Warn("received PREPARE for unknown block")
waitingCh := make(chan bool)
if _, ok := pcm.stateChangeChannels[encodedHash]; !ok {
pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{}
}
pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrePrepared], waitingCh)
result := <-waitingCh
if !result {
return
}
}
if pcm.msgLog.Exists(cmsg) {
@ -225,13 +214,23 @@ func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.PubSubMessage) {
}
pcm.psb.BroadcastToServiceTopic(commitMsg)
logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into PREPARED state")
pcm.state.status = StateStatusPrepared
pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusPrepared)
// pull watchers
encodedHash := hex.EncodeToString(cmsg.Blockhash)
if m, ok := pcm.stateChangeChannels[encodedHash]; ok {
if channels, ok := m[StateStatusPrepared]; ok {
for _, v := range channels {
v <- true
close(v)
delete(pcm.stateChangeChannels, encodedHash)
}
}
}
}
}
func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) {
//pcm.state.mutex.Lock()
//defer pcm.state.mutex.Unlock()
var commit types.CommitMessage
err := cbor.Unmarshal(message.Payload, &commit)
if err != nil {
@ -246,11 +245,27 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) {
Signature: commit.Signature,
}
if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) {
logrus.Warnf("received unknown block %x", cmsg.Blockhash)
ci, err := pcm.consensusRoundPool.GetConsensusInfo(cmsg.Blockhash)
if errors.Is(err, cache.ErrNotFound) {
logrus.WithField("blockHash", hex.EncodeToString(cmsg.Blockhash)).Warnf("received COMMIT for unknown block")
return
}
if ci.State < StateStatusPrepared {
encodedHash := hex.EncodeToString(cmsg.Blockhash)
logrus.WithField("blockHash", encodedHash).Warnf("incorrect state of block consensus")
waitingCh := make(chan bool)
if _, ok := pcm.stateChangeChannels[encodedHash]; !ok {
pcm.stateChangeChannels[encodedHash] = map[State][]chan bool{}
}
pcm.stateChangeChannels[encodedHash][StateStatusPrepared] = append(pcm.stateChangeChannels[encodedHash][StateStatusPrepared], waitingCh)
result := <-waitingCh
if !result {
return
}
}
if pcm.msgLog.Exists(cmsg) {
logrus.Tracef("received existing commit msg for block %x", cmsg.Blockhash)
return
@ -268,27 +283,22 @@ func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.PubSubMessage) {
}).Debug("Received COMMIT message")
if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals {
block, err := pcm.blockPool.GetBlock(cmsg.Blockhash)
if err != nil {
logrus.Error(err)
return
}
pcm.blockPool.AddAcceptedBlock(block)
logrus.WithField("blockHash", fmt.Sprintf("%x", cmsg.Blockhash)).Debugf("Entered into COMMIT state")
pcm.state.status = StateStatusCommited
pcm.consensusRoundPool.UpdateConsensusState(cmsg.Blockhash, StateStatusCommited)
}
}
func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) {
block, err := pcm.commitAcceptedBlocks()
height, _ := pcm.blockchain.GetLatestBlockHeight()
if err != nil {
if errors.Is(err, ErrNoAcceptedBlocks) {
logrus.WithFields(logrus.Fields{
"height": pcm.state.blockHeight,
"height": height + 1,
}).Infof("No accepted blocks in the current consensus round")
} else {
logrus.WithFields(logrus.Fields{
"height": pcm.state.blockHeight,
"height": height + 1,
"err": err.Error(),
}).Errorf("Failed to select the block in the current consensus round")
return
@ -312,26 +322,20 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) {
if block.Header.Proposer.String() == pcm.address.String() {
pcm.submitTasksFromBlock(block)
}
pcm.state.blockHeight = pcm.state.blockHeight + 1
}
// get latest block
height, err := pcm.blockchain.GetLatestBlockHeight()
if err != nil {
logrus.Error(err)
return
}
blockHeader, err := pcm.blockchain.FetchBlockHeaderByHeight(height)
if err != nil {
logrus.Error(err)
return
for k, v := range pcm.stateChangeChannels {
for k1, j := range v {
for _, ch := range j {
ch <- true
close(ch)
}
delete(v, k1)
}
delete(pcm.stateChangeChannels, k)
}
pcm.state.drandRound = entry.Round
pcm.state.randomness = entry.Data
minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round, blockHeader)
minedBlock, err := pcm.miner.MineBlock(entry.Data, entry.Round)
if err != nil {
if errors.Is(err, blockchain.ErrNoTxForBlock) {
logrus.Info("Sealing skipped, no transactions in mempool")
@ -343,7 +347,6 @@ func (pcm *PBFTConsensusManager) onNewBeaconEntry(entry types2.BeaconEntry) {
// if we are round winner
if minedBlock != nil {
logrus.WithField("height", pcm.state.blockHeight).Infof("We have been elected in the current consensus round")
err = pcm.propose(minedBlock)
if err != nil {
logrus.Errorf("Failed to propose the block: %s", err.Error())
@ -388,32 +391,34 @@ func (pcm *PBFTConsensusManager) submitTasksFromBlock(block *types3.Block) {
}
func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) {
blocks := pcm.blockPool.GetAllAcceptedBlocks()
blocks := pcm.consensusRoundPool.GetAllBlocksWithCommit()
if blocks == nil {
return nil, ErrNoAcceptedBlocks
}
var maxStake *big.Int
var maxWinCount int64 = -1
var selectedBlock *types3.Block
for _, v := range blocks {
stake, err := pcm.ethereumClient.GetMinerStake(v.Header.ProposerEth)
stake, err := pcm.ethereumClient.GetMinerStake(v.Block.Header.ProposerEth)
if err != nil {
return nil, err
}
if maxStake != nil {
if stake.Cmp(maxStake) == -1 {
if maxStake != nil && maxWinCount != -1 {
if stake.Cmp(maxStake) == -1 || v.Block.Header.ElectionProof.WinCount < maxWinCount {
continue
}
}
maxStake = stake
selectedBlock = v
maxWinCount = v.Block.Header.ElectionProof.WinCount
selectedBlock = v.Block
}
logrus.WithFields(logrus.Fields{
"hash": hex.EncodeToString(selectedBlock.Header.Hash),
"height": selectedBlock.Header.Height,
"miner": selectedBlock.Header.Proposer.String(),
}).Info("Committed new block")
pcm.blockPool.PruneAcceptedBlocks(selectedBlock)
pcm.consensusRoundPool.Prune()
for _, v := range selectedBlock.Data {
err := pcm.mempool.DeleteTx(v.Hash)
if err != nil {

View File

@ -0,0 +1,119 @@
package consensus
import (
"bytes"
"encoding/hex"
"fmt"
"time"
"github.com/Secured-Finance/dione/blockchain/pool"
"github.com/asaskevich/EventBus"
"github.com/sirupsen/logrus"
"github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/cache"
)
type State uint8
const (
StateStatusUnknown = iota
StateStatusPrePrepared
StateStatusPrepared
StateStatusCommited
)
// ConsensusRoundPool is pool for blocks that isn't not validated or committed yet
type ConsensusRoundPool struct {
mempool *pool.Mempool
consensusInfoStorage cache.Cache
bus EventBus.Bus
}
func NewConsensusRoundPool(mp *pool.Mempool, bus EventBus.Bus) (*ConsensusRoundPool, error) {
bp := &ConsensusRoundPool{
consensusInfoStorage: cache.NewInMemoryCache(),
mempool: mp,
bus: bus,
}
return bp, nil
}
type ConsensusInfo struct {
Block *types.Block
State State
}
func (crp *ConsensusRoundPool) AddConsensusInfo(block *types.Block) error {
encodedHash := hex.EncodeToString(block.Header.Hash)
if crp.consensusInfoStorage.Exists(encodedHash) {
return nil
}
err := crp.consensusInfoStorage.StoreWithTTL(encodedHash, &ConsensusInfo{
Block: block,
State: StateStatusPrePrepared,
}, 10*time.Minute)
if err != nil {
return err
}
logrus.WithField("hash", fmt.Sprintf("%x", block.Header.Hash)).Debug("New block discovered")
crp.bus.Publish("blockpool:knownBlockAdded", block)
return nil
}
func (crp *ConsensusRoundPool) UpdateConsensusState(blockhash []byte, newState State) error {
encodedHash := hex.EncodeToString(blockhash)
var consensusInfo ConsensusInfo
err := crp.consensusInfoStorage.Get(encodedHash, &consensusInfo)
if err != nil {
return err
}
if newState < consensusInfo.State {
return fmt.Errorf("attempt to set incorrect state")
}
consensusInfo.State = newState
crp.bus.Publish("blockpool:newConsensusState", blockhash, newState)
return crp.consensusInfoStorage.StoreWithTTL(encodedHash, &consensusInfo, 10*time.Minute)
}
func (crp *ConsensusRoundPool) GetConsensusInfo(blockhash []byte) (*ConsensusInfo, error) {
var consensusInfo ConsensusInfo
err := crp.consensusInfoStorage.Get(hex.EncodeToString(blockhash), &consensusInfo)
return &consensusInfo, err
}
// Prune cleans known blocks list. It is called when new consensus round starts.
func (crp *ConsensusRoundPool) Prune() {
for k := range crp.consensusInfoStorage.Items() {
crp.consensusInfoStorage.Delete(k)
}
crp.bus.Publish("blockpool:pruned")
}
func (crp *ConsensusRoundPool) GetAllBlocksWithCommit() []*ConsensusInfo {
var consensusInfos []*ConsensusInfo
for _, v := range crp.consensusInfoStorage.Items() {
ci := v.(*ConsensusInfo)
if ci.State == StateStatusCommited {
consensusInfos = append(consensusInfos, ci)
}
}
return consensusInfos
}
func containsTx(s []*types.Transaction, e *types.Transaction) bool {
for _, a := range s {
if bytes.Equal(a.Hash, e.Hash) {
return true
}
}
return false
}

View File

@ -4,6 +4,8 @@ import (
"context"
"time"
"github.com/Secured-Finance/dione/blockchain"
drand2 "github.com/Secured-Finance/dione/beacon/drand"
"github.com/Secured-Finance/dione/pubsub"
@ -50,6 +52,7 @@ func runNode(
pubSubRouter *pubsub.PubSubRouter,
disputeManager *consensus.DisputeManager,
db *drand2.DrandBeacon,
bc *blockchain.BlockChain,
) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
@ -200,7 +203,7 @@ func Start() {
providePeerDiscovery,
provideDrandBeacon,
provideMempool,
provideMiner,
blockchain.NewMiner,
provideBlockChain,
provideBlockPool,
provideSyncManager,
@ -214,8 +217,10 @@ func Start() {
configureLogger,
configureDirectRPC,
configureForeignBlockchainRPC,
initializeBlockchain,
configureMiner,
runNode,
),
fx.NopLogger,
//fx.NopLogger,
).Run()
}

View File

@ -10,6 +10,8 @@ import (
"path"
"runtime"
types2 "github.com/Secured-Finance/dione/blockchain/types"
"github.com/Secured-Finance/dione/rpc"
"github.com/Secured-Finance/dione/rpc/filecoin"
solana2 "github.com/Secured-Finance/dione/rpc/solana"
@ -76,12 +78,6 @@ func provideDisputeManager(ethClient *ethclient.EthereumClient, pcm *consensus.P
return dm
}
func provideMiner(h host.Host, ethClient *ethclient.EthereumClient, privateKey crypto.PrivKey, mempool *pool.Mempool) *blockchain.Miner {
miner := blockchain.NewMiner(h.ID(), *ethClient.GetEthAddress(), ethClient, privateKey, mempool)
logrus.Info("Mining subsystem has been initialized!")
return miner
}
func provideDrandBeacon(ps *pubsub.PubSubRouter, bus EventBus.Bus) *drand2.DrandBeacon {
db, err := drand2.NewDrandBeacon(ps.Pubsub, bus)
if err != nil {
@ -135,7 +131,7 @@ func provideConsensusManager(
bc *blockchain.BlockChain,
ethClient *ethclient.EthereumClient,
privateKey crypto.PrivKey,
bp *pool.BlockPool,
bp *consensus.ConsensusRoundPool,
db *drand2.DrandBeacon,
mp *pool.Mempool,
) *consensus.PBFTConsensusManager {
@ -268,8 +264,8 @@ func provideNetworkService(bp *blockchain.BlockChain, mp *pool.Mempool) *Network
return ns
}
func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *pool.BlockPool {
bp, err := pool.NewBlockPool(mp, bus)
func provideBlockPool(mp *pool.Mempool, bus EventBus.Bus) *consensus.ConsensusRoundPool {
bp, err := consensus.NewConsensusRoundPool(mp, bus)
if err != nil {
logrus.Fatalf("Failed to initialize blockpool: %s", err.Error())
}
@ -392,3 +388,19 @@ func configureForeignBlockchainRPC() {
logrus.Info("Foreign Blockchain RPC clients has been successfully configured!")
}
func configureMiner(m *blockchain.Miner, b *blockchain.BlockChain) {
m.SetBlockchainInstance(b)
}
func initializeBlockchain(bc *blockchain.BlockChain) {
_, err := bc.GetLatestBlockHeight()
if err == blockchain.ErrLatestHeightNil {
gBlock := types2.GenesisBlock()
err = bc.StoreBlock(gBlock) // commit genesis block
if err != nil {
logrus.Fatal(err)
}
logrus.Info("Committed genesis block")
}
}

View File

@ -106,7 +106,7 @@ func lambda(power, totalPower *big.Int) *big.Int {
return lam
}
var MaxWinCount = 3 * config.ExpectedLeadersPerEpoch.Int64()
var MaxWinCount = 10 * config.ExpectedLeadersPerEpoch.Int64()
type poiss struct {
lam *big.Int