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  }