2020-08-05 17:11:14 +00:00
|
|
|
package consensus
|
2020-10-21 19:54:40 +00:00
|
|
|
|
|
|
|
import (
|
2021-06-08 21:30:23 +00:00
|
|
|
"errors"
|
2020-11-15 13:46:58 +00:00
|
|
|
"math/big"
|
2020-11-20 21:29:30 +00:00
|
|
|
"sync"
|
|
|
|
|
2021-07-10 22:07:35 +00:00
|
|
|
"github.com/fxamacker/cbor/v2"
|
|
|
|
|
2021-06-14 21:45:35 +00:00
|
|
|
"github.com/Secured-Finance/dione/cache"
|
|
|
|
|
2021-06-11 11:40:32 +00:00
|
|
|
"github.com/asaskevich/EventBus"
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
"github.com/Secured-Finance/dione/blockchain"
|
|
|
|
|
|
|
|
"github.com/drand/drand/client"
|
|
|
|
|
|
|
|
"github.com/Arceliar/phony"
|
|
|
|
|
|
|
|
types3 "github.com/Secured-Finance/dione/blockchain/types"
|
|
|
|
|
|
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
|
|
|
|
|
|
|
"github.com/Secured-Finance/dione/blockchain/pool"
|
2020-12-02 13:42:02 +00:00
|
|
|
|
2020-11-18 19:53:52 +00:00
|
|
|
"github.com/Secured-Finance/dione/consensus/types"
|
2021-07-10 22:07:35 +00:00
|
|
|
types2 "github.com/Secured-Finance/dione/types"
|
2020-11-18 19:53:52 +00:00
|
|
|
|
2020-11-15 13:46:58 +00:00
|
|
|
"github.com/Secured-Finance/dione/ethclient"
|
|
|
|
"github.com/sirupsen/logrus"
|
2020-10-21 19:54:40 +00:00
|
|
|
|
2020-11-18 19:53:52 +00:00
|
|
|
"github.com/Secured-Finance/dione/pubsub"
|
2021-06-08 21:30:23 +00:00
|
|
|
)
|
|
|
|
|
2021-07-11 00:32:58 +00:00
|
|
|
var (
|
|
|
|
ErrNoAcceptedBlocks = errors.New("there is no accepted blocks")
|
|
|
|
)
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
type StateStatus uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
StateStatusUnknown = iota
|
|
|
|
|
|
|
|
StateStatusPrePrepared
|
|
|
|
StateStatusPrepared
|
|
|
|
StateStatusCommited
|
2020-10-21 19:54:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type PBFTConsensusManager struct {
|
2021-06-08 21:30:23 +00:00
|
|
|
phony.Inbox
|
2021-06-11 11:40:32 +00:00
|
|
|
bus EventBus.Bus
|
2020-11-20 21:29:30 +00:00
|
|
|
psb *pubsub.PubSubRouter
|
2021-06-08 21:30:23 +00:00
|
|
|
minApprovals int // FIXME
|
|
|
|
privKey crypto.PrivKey
|
|
|
|
msgLog *ConsensusMessageLog
|
2021-04-30 19:55:12 +00:00
|
|
|
validator *ConsensusValidator
|
2020-11-20 21:29:30 +00:00
|
|
|
ethereumClient *ethclient.EthereumClient
|
2020-11-27 16:16:08 +00:00
|
|
|
miner *Miner
|
2021-07-11 00:32:58 +00:00
|
|
|
blockPool *pool.BlockPool
|
2021-06-08 21:30:23 +00:00
|
|
|
blockchain blockchain.BlockChain
|
|
|
|
state *State
|
2020-10-21 19:54:40 +00:00
|
|
|
}
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
type State struct {
|
|
|
|
mutex sync.Mutex
|
|
|
|
drandRound uint64
|
|
|
|
randomness []byte
|
|
|
|
blockHeight uint64
|
|
|
|
status StateStatus
|
2021-06-11 11:40:32 +00:00
|
|
|
ready chan bool
|
2020-11-20 21:29:30 +00:00
|
|
|
}
|
2020-10-21 19:54:40 +00:00
|
|
|
|
2021-07-11 00:32:58 +00:00
|
|
|
func NewPBFTConsensusManager(
|
|
|
|
bus EventBus.Bus,
|
|
|
|
psb *pubsub.PubSubRouter,
|
|
|
|
minApprovals int,
|
|
|
|
privKey crypto.PrivKey,
|
|
|
|
ethereumClient *ethclient.EthereumClient,
|
|
|
|
miner *Miner,
|
|
|
|
bc *blockchain.BlockChain,
|
|
|
|
bp *pool.BlockPool,
|
|
|
|
) *PBFTConsensusManager {
|
2020-10-21 19:54:40 +00:00
|
|
|
pcm := &PBFTConsensusManager{}
|
|
|
|
pcm.psb = psb
|
2020-11-27 16:16:08 +00:00
|
|
|
pcm.miner = miner
|
2021-06-11 11:40:32 +00:00
|
|
|
pcm.validator = NewConsensusValidator(miner, bc)
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.msgLog = NewConsensusMessageLog()
|
2020-11-15 13:46:58 +00:00
|
|
|
pcm.minApprovals = minApprovals
|
|
|
|
pcm.privKey = privKey
|
|
|
|
pcm.ethereumClient = ethereumClient
|
2021-06-11 11:40:32 +00:00
|
|
|
pcm.state = &State{
|
|
|
|
ready: make(chan bool, 1),
|
|
|
|
status: StateStatusUnknown,
|
|
|
|
}
|
|
|
|
pcm.bus = bus
|
2021-07-11 00:32:58 +00:00
|
|
|
pcm.blockPool = bp
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.psb.Hook(pubsub.PrePrepareMessageType, pcm.handlePrePrepare, types.PrePrepareMessage{})
|
|
|
|
pcm.psb.Hook(pubsub.PrepareMessageType, pcm.handlePrepare, types.PrepareMessage{})
|
|
|
|
pcm.psb.Hook(pubsub.CommitMessageType, pcm.handleCommit, types.CommitMessage{})
|
2021-06-11 11:40:32 +00:00
|
|
|
bus.SubscribeOnce("sync:initialSyncCompleted", func() {
|
|
|
|
pcm.state.ready <- true
|
|
|
|
})
|
2020-10-21 19:54:40 +00:00
|
|
|
return pcm
|
|
|
|
}
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
func (pcm *PBFTConsensusManager) propose(blk *types3.Block) error {
|
|
|
|
pcm.state.mutex.Lock()
|
|
|
|
defer pcm.state.mutex.Unlock()
|
|
|
|
prePrepareMsg, err := NewMessage(types.ConsensusMessage{Block: blk}, types.ConsensusMessageTypePrePrepare, pcm.privKey)
|
2020-11-15 13:46:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pcm.psb.BroadcastToServiceTopic(prePrepareMsg)
|
2021-06-14 21:45:35 +00:00
|
|
|
pcm.blockPool.AddBlock(blk)
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.state.status = StateStatusPrePrepared
|
2020-11-15 13:46:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-11-12 14:18:30 +00:00
|
|
|
|
2021-06-03 21:15:32 +00:00
|
|
|
func (pcm *PBFTConsensusManager) handlePrePrepare(message *pubsub.GenericMessage) {
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.state.mutex.Lock()
|
|
|
|
defer pcm.state.mutex.Unlock()
|
|
|
|
prePrepare, ok := message.Payload.(types.PrePrepareMessage)
|
|
|
|
if !ok {
|
|
|
|
logrus.Warn("failed to convert payload to PrePrepare message")
|
2021-06-02 21:19:52 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
if prePrepare.Block.Header.Proposer == pcm.miner.address {
|
2021-03-15 20:39:52 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
|
|
|
|
cmsg := types.ConsensusMessage{
|
|
|
|
Type: types.ConsensusMessageTypePrePrepare,
|
|
|
|
From: message.From,
|
|
|
|
Block: prePrepare.Block,
|
|
|
|
}
|
|
|
|
|
2021-06-11 11:40:32 +00:00
|
|
|
<-pcm.state.ready
|
|
|
|
|
2021-06-02 21:19:52 +00:00
|
|
|
if pcm.msgLog.Exists(cmsg) {
|
2021-04-30 20:09:55 +00:00
|
|
|
logrus.Debugf("received existing pre_prepare msg, dropping...")
|
2020-11-15 13:46:58 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-11 11:40:32 +00:00
|
|
|
if !pcm.validator.Valid(cmsg, map[string]interface{}{"randomness": pcm.state.randomness}) {
|
2021-04-21 19:37:32 +00:00
|
|
|
logrus.Warn("received invalid pre_prepare msg, dropping...")
|
2020-11-15 13:46:58 +00:00
|
|
|
return
|
|
|
|
}
|
2020-10-21 19:54:40 +00:00
|
|
|
|
2021-06-02 21:19:52 +00:00
|
|
|
pcm.msgLog.AddMessage(cmsg)
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.blockPool.AddBlock(cmsg.Block)
|
2020-11-20 21:29:30 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
prepareMsg, err := NewMessage(cmsg, types.ConsensusMessageTypePrepare, pcm.privKey)
|
2020-11-15 13:46:58 +00:00
|
|
|
if err != nil {
|
2021-04-30 20:09:55 +00:00
|
|
|
logrus.Errorf("failed to create prepare message: %v", err)
|
2021-06-03 21:15:32 +00:00
|
|
|
return
|
2020-11-15 13:46:58 +00:00
|
|
|
}
|
2021-03-15 20:39:52 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.psb.BroadcastToServiceTopic(prepareMsg)
|
|
|
|
pcm.state.status = StateStatusPrePrepared
|
2020-10-21 19:54:40 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 21:15:32 +00:00
|
|
|
func (pcm *PBFTConsensusManager) handlePrepare(message *pubsub.GenericMessage) {
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.state.mutex.Lock()
|
|
|
|
defer pcm.state.mutex.Unlock()
|
|
|
|
prepare, ok := message.Payload.(types.PrepareMessage)
|
|
|
|
if !ok {
|
|
|
|
logrus.Warn("failed to convert payload to Prepare message")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cmsg := types.ConsensusMessage{
|
|
|
|
Type: types.ConsensusMessageTypePrepare,
|
|
|
|
From: message.From,
|
|
|
|
Blockhash: prepare.Blockhash,
|
2021-06-14 21:45:35 +00:00
|
|
|
Signature: prepare.Signature,
|
2021-06-08 21:30:23 +00:00
|
|
|
}
|
|
|
|
|
2021-06-14 21:45:35 +00:00
|
|
|
if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) {
|
|
|
|
logrus.Debugf("received unknown block")
|
2021-06-02 21:19:52 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if pcm.msgLog.Exists(cmsg) {
|
2021-04-30 20:09:55 +00:00
|
|
|
logrus.Debugf("received existing prepare msg, dropping...")
|
2020-11-15 13:46:58 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-14 21:45:35 +00:00
|
|
|
|
2021-06-11 11:40:32 +00:00
|
|
|
if !pcm.validator.Valid(cmsg, nil) {
|
2021-04-21 19:37:32 +00:00
|
|
|
logrus.Warn("received invalid prepare msg, dropping...")
|
2020-10-21 19:54:40 +00:00
|
|
|
return
|
|
|
|
}
|
2020-11-15 13:46:58 +00:00
|
|
|
|
2021-06-02 21:19:52 +00:00
|
|
|
pcm.msgLog.AddMessage(cmsg)
|
2020-11-15 13:46:58 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
if len(pcm.msgLog.Get(types.ConsensusMessageTypePrepare, cmsg.Blockhash)) >= pcm.minApprovals {
|
|
|
|
commitMsg, err := NewMessage(cmsg, types.ConsensusMessageTypeCommit, pcm.privKey)
|
2020-10-21 19:54:40 +00:00
|
|
|
if err != nil {
|
2021-06-08 21:30:23 +00:00
|
|
|
logrus.Errorf("failed to create commit message: %v", err)
|
2020-10-21 19:54:40 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.psb.BroadcastToServiceTopic(commitMsg)
|
|
|
|
pcm.state.status = StateStatusPrepared
|
2020-10-21 19:54:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-03 21:15:32 +00:00
|
|
|
func (pcm *PBFTConsensusManager) handleCommit(message *pubsub.GenericMessage) {
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.state.mutex.Lock()
|
|
|
|
defer pcm.state.mutex.Unlock()
|
|
|
|
commit, ok := message.Payload.(types.CommitMessage)
|
|
|
|
if !ok {
|
|
|
|
logrus.Warn("failed to convert payload to Prepare message")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cmsg := types.ConsensusMessage{
|
|
|
|
Type: types.ConsensusMessageTypeCommit,
|
|
|
|
From: message.From,
|
|
|
|
Blockhash: commit.Blockhash,
|
|
|
|
Signature: commit.Signature, // TODO check the signature
|
|
|
|
}
|
|
|
|
|
2021-06-14 21:45:35 +00:00
|
|
|
if _, err := pcm.blockPool.GetBlock(cmsg.Blockhash); errors.Is(err, cache.ErrNotFound) {
|
|
|
|
logrus.Debugf("received unknown block")
|
2021-06-02 21:19:52 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if pcm.msgLog.Exists(cmsg) {
|
2021-04-30 20:09:55 +00:00
|
|
|
logrus.Debugf("received existing commit msg, dropping...")
|
2020-10-21 19:54:40 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-11 11:40:32 +00:00
|
|
|
if !pcm.validator.Valid(cmsg, nil) {
|
2021-04-21 19:37:32 +00:00
|
|
|
logrus.Warn("received invalid commit msg, dropping...")
|
2020-10-22 14:37:31 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-02 21:19:52 +00:00
|
|
|
pcm.msgLog.AddMessage(cmsg)
|
2020-10-22 14:37:31 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
if len(pcm.msgLog.Get(types.ConsensusMessageTypeCommit, cmsg.Blockhash)) >= pcm.minApprovals {
|
|
|
|
block, err := pcm.blockPool.GetBlock(cmsg.Blockhash)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debug(err)
|
2021-04-21 19:37:32 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.blockPool.AddAcceptedBlock(block)
|
|
|
|
pcm.state.status = StateStatusCommited
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pcm *PBFTConsensusManager) NewDrandRound(from phony.Actor, res client.Result) {
|
|
|
|
pcm.Act(from, func() {
|
|
|
|
pcm.state.mutex.Lock()
|
|
|
|
defer pcm.state.mutex.Unlock()
|
|
|
|
block, err := pcm.commitAcceptedBlocks()
|
|
|
|
if err != nil {
|
2021-07-11 00:32:58 +00:00
|
|
|
if errors.Is(err, ErrNoAcceptedBlocks) {
|
|
|
|
logrus.Warnf("No accepted blocks for consensus round %d", pcm.state.blockHeight)
|
|
|
|
} else {
|
|
|
|
logrus.Errorf("Failed to select the block in consensus round %d: %s", pcm.state.blockHeight, err.Error())
|
|
|
|
}
|
2021-03-15 20:39:52 +00:00
|
|
|
return
|
|
|
|
}
|
2021-07-10 22:07:35 +00:00
|
|
|
|
|
|
|
// if we are miner for this block
|
|
|
|
// then post dione tasks to target chains (currently, only Ethereum)
|
|
|
|
if block.Header.Proposer == pcm.miner.address {
|
|
|
|
for _, v := range block.Data {
|
2021-07-11 00:32:58 +00:00
|
|
|
var task types2.DioneTask
|
2021-07-10 22:07:35 +00:00
|
|
|
err := cbor.Unmarshal(v.Data, &task)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to unmarshal transaction %x payload: %s", v.Hash, err.Error())
|
|
|
|
continue // FIXME
|
|
|
|
}
|
|
|
|
reqIDNumber, ok := big.NewInt(0).SetString(task.RequestID, 10)
|
|
|
|
if !ok {
|
|
|
|
logrus.Errorf("Failed to parse request id number in task of tx %x", v.Hash)
|
|
|
|
continue // FIXME
|
|
|
|
}
|
|
|
|
|
|
|
|
err = pcm.ethereumClient.SubmitRequestAnswer(reqIDNumber, task.Payload)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to submit task in tx %x: %s", v.Hash, err.Error())
|
|
|
|
continue // FIXME
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-11 11:43:48 +00:00
|
|
|
pcm.state.ready <- true
|
2021-03-05 19:29:09 +00:00
|
|
|
|
2021-06-11 11:40:32 +00:00
|
|
|
minedBlock, err := pcm.miner.MineBlock(res.Randomness(), block.Header)
|
2021-06-08 21:30:23 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to mine the block: %s", err.Error())
|
|
|
|
return
|
2020-11-15 13:46:58 +00:00
|
|
|
}
|
2021-03-15 20:39:52 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
pcm.state.drandRound = res.Round()
|
|
|
|
pcm.state.randomness = res.Randomness()
|
|
|
|
pcm.state.blockHeight = pcm.state.blockHeight + 1
|
2021-03-15 20:39:52 +00:00
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
// if we are round winner
|
|
|
|
if minedBlock != nil {
|
|
|
|
err = pcm.propose(minedBlock)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to propose the block: %s", err.Error())
|
|
|
|
return
|
|
|
|
}
|
2021-04-15 21:01:27 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
})
|
2021-03-15 20:39:52 +00:00
|
|
|
}
|
|
|
|
|
2021-06-08 21:30:23 +00:00
|
|
|
func (pcm *PBFTConsensusManager) commitAcceptedBlocks() (*types3.Block, error) {
|
|
|
|
blocks := pcm.blockPool.GetAllAcceptedBlocks()
|
|
|
|
if blocks == nil {
|
2021-07-11 00:32:58 +00:00
|
|
|
return nil, ErrNoAcceptedBlocks
|
2021-03-15 20:39:52 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
var maxStake *big.Int
|
|
|
|
var selectedBlock *types3.Block
|
|
|
|
for _, v := range blocks {
|
|
|
|
stake, err := pcm.ethereumClient.GetMinerStake(v.Header.ProposerEth)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-06-02 21:19:52 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
|
|
|
|
if maxStake != nil {
|
|
|
|
if stake.Cmp(maxStake) == -1 {
|
|
|
|
continue
|
|
|
|
}
|
2021-06-02 21:19:52 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
maxStake = stake
|
|
|
|
selectedBlock = v
|
2021-06-02 21:19:52 +00:00
|
|
|
}
|
2021-06-08 21:30:23 +00:00
|
|
|
logrus.Debugf("Selected block of miner %s", selectedBlock.Header.ProposerEth.Hex())
|
|
|
|
pcm.blockPool.PruneAcceptedBlocks()
|
|
|
|
return selectedBlock, pcm.blockchain.StoreBlock(selectedBlock)
|
2021-06-02 21:19:52 +00:00
|
|
|
}
|