github.com/aergoio/aergo@v1.3.1/consensus/consensus.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package consensus 7 8 import ( 9 "context" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "github.com/aergoio/etcd/raft" 14 "io" 15 "strings" 16 "time" 17 18 "github.com/aergoio/aergo-lib/db" 19 "github.com/aergoio/aergo-lib/log" 20 "github.com/aergoio/aergo/types" 21 "github.com/aergoio/etcd/raft/raftpb" 22 ) 23 24 // DefaultBlockIntervalSec is the default block generation interval in seconds. 25 const DefaultBlockIntervalSec = int64(1) 26 27 var ( 28 // BlockIntervalSec is the block genration interval in seconds. 29 BlockIntervalSec = DefaultBlockIntervalSec 30 31 // BlockInterval is the maximum block generation time limit. 32 BlockInterval = time.Second * time.Duration(DefaultBlockIntervalSec) 33 34 logger = log.NewLogger("consensus") 35 ) 36 37 var ( 38 ErrNotSupportedMethod = errors.New("not supported metehod in this consensus") 39 ) 40 41 // InitBlockInterval initializes block interval parameters. 42 func InitBlockInterval(blockIntervalSec int64) { 43 if blockIntervalSec > 0 { 44 BlockIntervalSec = blockIntervalSec 45 BlockInterval = time.Second * time.Duration(BlockIntervalSec) 46 } 47 } 48 49 // ErrorConsensus is a basic error struct for consensus modules. 50 type ErrorConsensus struct { 51 Msg string 52 Err error 53 } 54 55 func (e ErrorConsensus) Error() string { 56 errMsg := e.Msg 57 if e.Err != nil { 58 errMsg = fmt.Sprintf("%s: %s", errMsg, e.Err.Error()) 59 } 60 return errMsg 61 } 62 63 // Constructor represents a function returning the Consensus interfactor for 64 // each implementation. 65 type Constructor func() (Consensus, error) 66 67 // Consensus is an interface for a consensus implementation. 68 type Consensus interface { 69 ChainConsensus 70 ConsensusAccessor 71 Ticker() *time.Ticker 72 QueueJob(now time.Time, jq chan<- interface{}) 73 BlockFactory() BlockFactory 74 QuitChan() chan interface{} 75 } 76 77 type ConsensusAccessor interface { 78 ConsensusInfo() *types.ConsensusInfo 79 ClusterInfo([]byte) *types.GetClusterInfoResponse 80 ConfChange(req *types.MembershipChange) (*Member, error) 81 ConfChangeInfo(requestID uint64) (*types.ConfChangeProgress, error) 82 // RaftAccessor returns AergoRaftAccessor. It is only valid if chain is raft consensus 83 RaftAccessor() AergoRaftAccessor 84 } 85 86 // ChainDB is a reader interface for the ChainDB. 87 type ChainDB interface { 88 GetBestBlock() (*types.Block, error) 89 GetBlockByNo(blockNo types.BlockNo) (*types.Block, error) 90 GetHashByNo(blockNo types.BlockNo) ([]byte, error) 91 GetBlock(hash []byte) (*types.Block, error) 92 GetGenesisInfo() *types.Genesis 93 Get(key []byte) []byte 94 NewTx() db.Transaction 95 } 96 97 // AergoRaftAccessor is interface to access raft messaging. It is wrapping raft message with aergo internal types 98 type AergoRaftAccessor interface { 99 Process(ctx context.Context, peerID types.PeerID, m raftpb.Message) error 100 IsIDRemoved(peerID types.PeerID) bool 101 ReportUnreachable(peerID types.PeerID) 102 ReportSnapshot(peerID types.PeerID, status raft.SnapshotStatus) 103 104 SaveFromRemote(r io.Reader, id uint64, msg raftpb.Message) (int64, error) 105 106 GetMemberByID(id uint64) *Member 107 GetMemberByPeerID(peerID types.PeerID) *Member 108 } 109 110 type ConsensusType int 111 112 const ( 113 ConsensusDPOS ConsensusType = iota 114 ConsensusRAFT 115 ConsensusSBP 116 ) 117 118 var ConsensusName = []string{"dpos", "raft", "sbp"} 119 var ConsensusTypes = map[string]ConsensusType{"dpos": ConsensusDPOS, "raft": ConsensusRAFT, "sbp": ConsensusSBP} 120 121 var CurConsensusType ConsensusType 122 123 func IsRaftName(consensus string) bool { 124 return ConsensusName[ConsensusRAFT] == strings.ToLower(consensus) 125 } 126 func IsDposName(consensus string) bool { 127 return ConsensusName[ConsensusDPOS] == strings.ToLower(consensus) 128 } 129 130 func SetCurConsensus(consensus string) { 131 CurConsensusType = ConsensusTypes[consensus] 132 } 133 134 func UseRaft() bool { 135 return CurConsensusType == ConsensusRAFT 136 } 137 138 func UseDpos() bool { 139 return CurConsensusType == ConsensusDPOS 140 } 141 142 // ChainConsensus includes chainstatus and validation API. 143 type ChainConsensus interface { 144 ChainConsensusCluster 145 146 GetType() ConsensusType 147 IsTransactionValid(tx *types.Tx) bool 148 VerifyTimestamp(block *types.Block) bool 149 VerifySign(block *types.Block) error 150 IsBlockValid(block *types.Block, bestBlock *types.Block) error 151 Update(block *types.Block) 152 Save(tx TxWriter) error 153 NeedReorganization(rootNo types.BlockNo) bool 154 NeedNotify() bool 155 HasWAL() bool // if consensus has WAL, block has already written in db 156 IsConnectedBlock(block *types.Block) bool 157 IsForkEnable() bool 158 Info() string 159 } 160 161 type ChainConsensusCluster interface { 162 MakeConfChangeProposal(req *types.MembershipChange) (*ConfChangePropose, error) 163 } 164 165 type TxWriter interface { 166 Set(key, value []byte) 167 } 168 169 // Info represents an information for a consensus implementation. 170 type Info struct { 171 Type string 172 Status *json.RawMessage `json:",omitempty"` 173 } 174 175 // NewInfo returns a new Info with name. 176 func NewInfo(name string) *Info { 177 return &Info{Type: name} 178 } 179 180 // AsJSON() returns i as a JSON string 181 func (i *Info) AsJSON() string { 182 if m, err := json.Marshal(i); err == nil { 183 return string(m) 184 } 185 return "" 186 } 187 188 // BlockFactory is an interface for a block factory implementation. 189 type BlockFactory interface { 190 Start() 191 JobQueue() chan<- interface{} 192 } 193 194 // Start run a selected consesus service. 195 func Start(c Consensus) { 196 bf := c.BlockFactory() 197 if c == nil || bf == nil { 198 logger.Fatal().Msg("failed to start consensus service: no Consensus or BlockFactory") 199 } 200 201 go bf.Start() 202 203 go func() { 204 ticker := c.Ticker() 205 for now := range ticker.C { 206 c.QueueJob(now, bf.JobQueue()) 207 select { 208 case <-c.QuitChan(): 209 logger.Info().Msg("shutdown initiated. stop the consensus service") 210 return 211 default: 212 } 213 } 214 }() 215 } 216 217 // Stop shutdown consensus service. 218 func Stop(c Consensus) { 219 close(c.QuitChan()) 220 }