github.com/aergoio/aergo@v1.3.1/consensus/impl/sbp/sbp.go (about)

     1  package sbp
     2  
     3  import (
     4  	"runtime"
     5  	"time"
     6  
     7  	"github.com/aergoio/aergo-lib/log"
     8  	bc "github.com/aergoio/aergo/chain"
     9  	"github.com/aergoio/aergo/config"
    10  	"github.com/aergoio/aergo/consensus"
    11  	"github.com/aergoio/aergo/consensus/chain"
    12  	"github.com/aergoio/aergo/contract"
    13  	"github.com/aergoio/aergo/internal/enc"
    14  	"github.com/aergoio/aergo/pkg/component"
    15  	"github.com/aergoio/aergo/state"
    16  	"github.com/aergoio/aergo/types"
    17  )
    18  
    19  const (
    20  	slotQueueMax = 100
    21  )
    22  
    23  var logger *log.Logger
    24  
    25  func init() {
    26  	logger = log.NewLogger("sbp")
    27  }
    28  
    29  type txExec struct {
    30  	execTx bc.TxExecFn
    31  }
    32  
    33  func newTxExec(cdb consensus.ChainDB, blockNo types.BlockNo, ts int64, prevHash []byte, chainID []byte) chain.TxOp {
    34  	// Block hash not determined yet
    35  	return &txExec{
    36  		execTx: bc.NewTxExecutor(nil, contract.ChainAccessor(cdb), blockNo, ts, prevHash, contract.BlockFactory, chainID),
    37  	}
    38  }
    39  
    40  func (te *txExec) Apply(bState *state.BlockState, tx types.Transaction) error {
    41  	err := te.execTx(bState, tx)
    42  	return err
    43  }
    44  
    45  // SimpleBlockFactory implments a simple block factory which generate block each cfg.Consensus.BlockInterval.
    46  //
    47  // This can be used for testing purpose.
    48  type SimpleBlockFactory struct {
    49  	*component.ComponentHub
    50  	consensus.ChainDB
    51  	jobQueue         chan interface{}
    52  	blockInterval    time.Duration
    53  	maxBlockBodySize uint32
    54  	txOp             chain.TxOp
    55  	quit             chan interface{}
    56  	sdb              *state.ChainStateDB
    57  	prevBlock        *types.Block
    58  }
    59  
    60  // GetName returns the name of the consensus.
    61  func GetName() string {
    62  	return consensus.ConsensusName[consensus.ConsensusSBP]
    63  }
    64  
    65  // GetConstructor build and returns consensus.Constructor from New function.
    66  func GetConstructor(cfg *config.Config, hub *component.ComponentHub, cdb consensus.ChainDB,
    67  	sdb *state.ChainStateDB) consensus.Constructor {
    68  	return func() (consensus.Consensus, error) {
    69  		return New(cfg.Consensus, hub, cdb, sdb)
    70  	}
    71  }
    72  
    73  // New returns a SimpleBlockFactory.
    74  func New(cfg *config.ConsensusConfig, hub *component.ComponentHub, cdb consensus.ChainDB,
    75  	sdb *state.ChainStateDB) (*SimpleBlockFactory, error) {
    76  	s := &SimpleBlockFactory{
    77  		ComponentHub:     hub,
    78  		ChainDB:          cdb,
    79  		jobQueue:         make(chan interface{}, slotQueueMax),
    80  		blockInterval:    consensus.BlockInterval,
    81  		maxBlockBodySize: chain.MaxBlockBodySize(),
    82  		quit:             make(chan interface{}),
    83  		sdb:              sdb,
    84  	}
    85  
    86  	s.txOp = chain.NewCompTxOp(
    87  		chain.TxOpFn(func(bState *state.BlockState, txIn types.Transaction) error {
    88  			select {
    89  			case <-s.quit:
    90  				return chain.ErrQuit
    91  			default:
    92  				return nil
    93  			}
    94  		}),
    95  	)
    96  
    97  	return s, nil
    98  }
    99  
   100  // Ticker returns a time.Ticker for the main consensus loop.
   101  func (s *SimpleBlockFactory) Ticker() *time.Ticker {
   102  	return time.NewTicker(s.blockInterval)
   103  }
   104  
   105  // QueueJob send a block triggering information to jq.
   106  func (s *SimpleBlockFactory) QueueJob(now time.Time, jq chan<- interface{}) {
   107  	if b, _ := s.GetBestBlock(); b != nil {
   108  		if s.prevBlock != nil && s.prevBlock.BlockNo() == b.BlockNo() {
   109  			logger.Debug().Msg("previous block not connected. skip to generate block")
   110  			return
   111  		}
   112  		s.prevBlock = b
   113  		jq <- b
   114  	}
   115  }
   116  
   117  func (s *SimpleBlockFactory) GetType() consensus.ConsensusType {
   118  	return consensus.ConsensusSBP
   119  }
   120  
   121  // IsTransactionValid checks the onsensus level validity of a transaction
   122  func (s *SimpleBlockFactory) IsTransactionValid(tx *types.Tx) bool {
   123  	// SimpleBlockFactory has no tx valid check.
   124  	return true
   125  }
   126  
   127  // VerifyTimestamp checks the validity of the block timestamp.
   128  func (s *SimpleBlockFactory) VerifyTimestamp(*types.Block) bool {
   129  	// SimpleBlockFactory don't need to check timestamp.
   130  	return true
   131  }
   132  
   133  // VerifySign checks the consensus level validity of a block.
   134  func (s *SimpleBlockFactory) VerifySign(*types.Block) error {
   135  	// SimpleBlockFactory has no block signature.
   136  	return nil
   137  }
   138  
   139  // IsBlockValid checks the consensus level validity of a block.
   140  func (s *SimpleBlockFactory) IsBlockValid(*types.Block, *types.Block) error {
   141  	// SimpleBlockFactory has no block valid check.
   142  	return nil
   143  }
   144  
   145  // QuitChan returns the channel from which consensus-related goroutines check
   146  // when shutdown is initiated.
   147  func (s *SimpleBlockFactory) QuitChan() chan interface{} {
   148  	return s.quit
   149  }
   150  
   151  // Update has nothging to do.
   152  func (s *SimpleBlockFactory) Update(block *types.Block) {
   153  }
   154  
   155  // Save has nothging to do.
   156  func (s *SimpleBlockFactory) Save(tx consensus.TxWriter) error {
   157  	return nil
   158  }
   159  
   160  // BlockFactory returns s itself.
   161  func (s *SimpleBlockFactory) BlockFactory() consensus.BlockFactory {
   162  	return s
   163  }
   164  
   165  // NeedReorganization has nothing to do.
   166  func (s *SimpleBlockFactory) NeedReorganization(rootNo types.BlockNo) bool {
   167  	return true
   168  }
   169  
   170  // Start run a simple block factory service.
   171  func (s *SimpleBlockFactory) Start() {
   172  	defer logger.Info().Msg("shutdown initiated. stop the service")
   173  
   174  	runtime.LockOSThread()
   175  
   176  	for {
   177  		select {
   178  		case e := <-s.jobQueue:
   179  			if prevBlock, ok := e.(*types.Block); ok {
   180  				blockState := s.sdb.NewBlockState(prevBlock.GetHeader().GetBlocksRootHash())
   181  
   182  				ts := time.Now().UnixNano()
   183  
   184  				txOp := chain.NewCompTxOp(
   185  					s.txOp,
   186  					newTxExec(s.ChainDB, prevBlock.GetHeader().GetBlockNo()+1, ts, prevBlock.GetHash(), prevBlock.GetHeader().GetChainID()),
   187  				)
   188  
   189  				block, err := chain.GenerateBlock(s, prevBlock, blockState, txOp, ts, false)
   190  				if err == chain.ErrQuit {
   191  					return
   192  				} else if err != nil {
   193  					logger.Info().Err(err).Msg("failed to produce block")
   194  					continue
   195  				}
   196  				logger.Info().Uint64("no", block.GetHeader().GetBlockNo()).Str("hash", block.ID()).
   197  					Str("TrieRoot", enc.ToString(block.GetHeader().GetBlocksRootHash())).
   198  					Err(err).Msg("block produced")
   199  
   200  				chain.ConnectBlock(s, block, blockState, time.Second)
   201  			}
   202  		case <-s.quit:
   203  			return
   204  		}
   205  	}
   206  }
   207  
   208  // JobQueue returns the queue for block production triggering.
   209  func (s *SimpleBlockFactory) JobQueue() chan<- interface{} {
   210  	return s.jobQueue
   211  }
   212  
   213  // Info retuns an empty string since SBP has no valuable consensus-related
   214  // information.
   215  func (s *SimpleBlockFactory) Info() string {
   216  	return consensus.NewInfo(GetName()).AsJSON()
   217  }
   218  
   219  func (s *SimpleBlockFactory) ConsensusInfo() *types.ConsensusInfo {
   220  	return &types.ConsensusInfo{Type: GetName()}
   221  }
   222  
   223  var dummyRaft consensus.DummyRaftAccessor
   224  
   225  func (s *SimpleBlockFactory) RaftAccessor() consensus.AergoRaftAccessor {
   226  	return &dummyRaft
   227  }
   228  
   229  func (s *SimpleBlockFactory) NeedNotify() bool {
   230  	return true
   231  }
   232  
   233  func (s *SimpleBlockFactory) HasWAL() bool {
   234  	return false
   235  }
   236  
   237  func (s *SimpleBlockFactory) IsForkEnable() bool {
   238  	return true
   239  }
   240  
   241  func (s *SimpleBlockFactory) IsConnectedBlock(block *types.Block) bool {
   242  	_, err := s.ChainDB.GetBlock(block.BlockHash())
   243  	if err == nil {
   244  		return true
   245  	}
   246  
   247  	return false
   248  }
   249  
   250  func (s *SimpleBlockFactory) ConfChange(req *types.MembershipChange) (*consensus.Member, error) {
   251  	return nil, consensus.ErrNotSupportedMethod
   252  }
   253  
   254  func (s *SimpleBlockFactory) ConfChangeInfo(requestID uint64) (*types.ConfChangeProgress, error) {
   255  	return nil, consensus.ErrNotSupportedMethod
   256  }
   257  
   258  func (s *SimpleBlockFactory) MakeConfChangeProposal(req *types.MembershipChange) (*consensus.ConfChangePropose, error) {
   259  	return nil, consensus.ErrNotSupportedMethod
   260  }
   261  
   262  func (s *SimpleBlockFactory) ClusterInfo(bestBlockHash []byte) *types.GetClusterInfoResponse {
   263  	return &types.GetClusterInfoResponse{ChainID: nil, Error: consensus.ErrNotSupportedMethod.Error(), MbrAttrs: nil, HardStateInfo: nil}
   264  }
   265  
   266  func ValidateGenesis(genesis *types.Genesis) error {
   267  	return nil
   268  }