The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
woop/consensus/consensus.go

153 lines
4.7 KiB

// Consensus package implements the Cosi PBFT consensus
package consensus // consensus
import (
"fmt"
7 years ago
"harmony-benchmark/blockchain"
"harmony-benchmark/common"
"harmony-benchmark/log"
7 years ago
"harmony-benchmark/p2p"
"regexp"
"strconv"
"sync"
)
// Consensus data containing all info related to one round of consensus process
type Consensus struct {
state ConsensusState
// Signatures collected from validators
commits map[string]string
// Signatures collected from validators
responses map[string]string
// List of validators
validators []p2p.Peer
// Leader
leader p2p.Peer
// private key of current node
priKey string
// Whether I am leader. False means I am validator
IsLeader bool
// Leader or validator Id - 2 byte
nodeId uint16
// Consensus Id (View Id) - 4 byte
consensusId uint32
// Blockhash - 32 byte
blockHash [32]byte
// BlockHeader to run consensus on
blockHeader []byte
// Shard Id which this node belongs to
7 years ago
ShardID uint32
7 years ago
// global consensus mutex
mutex sync.Mutex
// Validator specific fields
// Blocks received but not done with consensus yet
blocksReceived map[uint32]*BlockConsensusStatus
// Signal channel for starting a new consensus process
ReadySignal chan int
// The verifier func passed from Node object
7 years ago
BlockVerifier func(*blockchain.Block) bool
// The post-consensus processing func passed from Node object
// Called when consensus on a new block is done
OnConsensusDone func(*blockchain.Block)
//// Network related fields
msgCategory byte
msgType byte
Log log.Logger
}
// This used to keep track of the consensus status of multiple blocks received so far
// This is mainly used in the case that this node is lagging behind and needs to catch up.
// For example, the consensus moved to round N and this node received message(N).
// However, this node may still not finished with round N-1, so the newly received message(N)
// should be stored in this temporary structure. In case the round N-1 finishes, it can catch
// up to the latest state of round N by using this structure.
type BlockConsensusStatus struct {
blockHeader []byte // the block header of the block which the consensus is running on
state ConsensusState // the latest state of the consensus
}
// NewConsensus creates a new Consensus object
// TODO(minhdoan): Maybe convert it into just New
// FYI, see https://golang.org/doc/effective_go.html?#package-names
func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) Consensus {
consensus := Consensus{}
Peers := peers
leaderPeer := leader
selfPeer := p2p.Peer{Port: port, Ip: ip}
if leaderPeer == selfPeer {
consensus.IsLeader = true
} else {
consensus.IsLeader = false
}
consensus.commits = make(map[string]string)
consensus.responses = make(map[string]string)
consensus.leader = leaderPeer
consensus.validators = Peers
consensus.priKey = ip + ":" + port // use ip:port as unique key for now
consensus.consensusId = 0 // or view Id in the original pbft paper
7 years ago
myShardID, err := strconv.Atoi(ShardID)
if err != nil {
panic("Unparseable shard Id" + ShardID)
}
7 years ago
consensus.ShardID = uint32(myShardID)
// For validators to keep track of all blocks received but not yet committed, so as to catch up to latest consensus if lagged behind.
consensus.blocksReceived = make(map[uint32]*BlockConsensusStatus)
// For now use socket address as 16 byte Id
// TODO: populate with correct Id
reg, err := regexp.Compile("[^0-9]+")
if err != nil {
consensus.Log.Crit("Regex Compilation Failed", "err", err, "consensus", consensus)
}
socketId := reg.ReplaceAllString(consensus.priKey, "")
value, err := strconv.Atoi(socketId)
consensus.nodeId = uint16(value)
if consensus.IsLeader {
consensus.ReadySignal = make(chan int)
// send a signal to indicate it's ready to run consensus
// this signal is consumed by node object to create a new block and in turn trigger a new consensus on it
// this is a goroutine because go channel without buffer will block
go func() {
consensus.ReadySignal <- 1
}()
}
// The message category and type used for any messages sent for consensus
consensus.msgCategory = byte(common.COMMITTEE)
consensus.msgType = byte(CONSENSUS)
consensus.Log = log.New()
return consensus
7 years ago
}
// Reset the state of the consensus
func (consensus *Consensus) ResetState() {
consensus.state = FINISHED
consensus.commits = make(map[string]string)
consensus.responses = make(map[string]string)
}
// Returns a string representation of this consensus
7 years ago
func (consensus *Consensus) String() string {
var duty string
if consensus.IsLeader {
duty = "LDR" // leader
} else {
7 years ago
duty = "VLD" // validator
}
7 years ago
return fmt.Sprintf("[%s, %s, %v, %v, %s]", duty, consensus.priKey, consensus.ShardID, consensus.nodeId, consensus.state)
}