github.com/aergoio/aergo@v1.3.1/consensus/chain/block.go (about)

     1  package chain
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/aergoio/aergo/internal/enc"
     7  	"github.com/aergoio/aergo/p2p/p2putil"
     8  	"time"
     9  
    10  	"github.com/aergoio/aergo/chain"
    11  	"github.com/aergoio/aergo/message"
    12  	"github.com/aergoio/aergo/pkg/component"
    13  	"github.com/aergoio/aergo/state"
    14  	"github.com/aergoio/aergo/types"
    15  )
    16  
    17  var (
    18  	// ErrQuit indicates that shutdown is initiated.
    19  	ErrQuit           = errors.New("shutdown initiated")
    20  	errBlockSizeLimit = errors.New("the transactions included exceeded the block size limit")
    21  	ErrBlockEmpty     = errors.New("no transactions in block")
    22  	ErrSyncChain      = errors.New("failed to sync request")
    23  )
    24  
    25  // ErrTimeout can be used to indicatefor any kind of timeout.
    26  type ErrTimeout struct {
    27  	Kind    string
    28  	Timeout int64
    29  }
    30  
    31  func (e ErrTimeout) Error() string {
    32  	if e.Timeout != 0 {
    33  		return fmt.Sprintf("%s timeout (%v)", e.Kind, e.Timeout)
    34  	}
    35  	return e.Kind + " timeout"
    36  }
    37  
    38  // ErrBlockConnect indicates a error indicating a failed block connected
    39  // request.
    40  type ErrBlockConnect struct {
    41  	id     string
    42  	prevID string
    43  	ec     error
    44  }
    45  
    46  func (e ErrBlockConnect) Error() string {
    47  	return fmt.Sprintf("failed to connect block (%s): id=%s, prev id=%s", e.ec.Error(), e.id, e.prevID)
    48  }
    49  
    50  // GetBestBlock returns the current best block from chainservice
    51  func GetBestBlock(hs component.ICompSyncRequester) *types.Block {
    52  	result, err := hs.RequestFuture(message.ChainSvc, &message.GetBestBlock{}, time.Second,
    53  		"consensus/util/info.GetBestBlock").Result()
    54  	if err != nil {
    55  		logger.Error().Err(err).Msg("failed to get best block info")
    56  		return nil
    57  	}
    58  	return result.(message.GetBestBlockRsp).Block
    59  }
    60  
    61  // MaxBlockBodySize returns the maximum block body size.
    62  func MaxBlockBodySize() uint32 {
    63  	return chain.MaxBlockBodySize()
    64  }
    65  
    66  // GenerateBlock generate & return a new block
    67  func GenerateBlock(hs component.ICompSyncRequester, prevBlock *types.Block, bState *state.BlockState, txOp TxOp, ts int64, skipEmpty bool) (*types.Block, error) {
    68  	transactions, err := GatherTXs(hs, bState, txOp, MaxBlockBodySize())
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	txs := make([]*types.Tx, 0)
    74  	for _, x := range transactions {
    75  		txs = append(txs, x.GetTx())
    76  	}
    77  
    78  	if len(txs) == 0 && skipEmpty {
    79  		logger.Debug().Msg("BF: empty block is skipped")
    80  		return nil, ErrBlockEmpty
    81  	}
    82  
    83  	block := types.NewBlock(prevBlock, bState.GetRoot(), bState.Receipts(), txs, chain.CoinbaseAccount, ts)
    84  	if len(txs) != 0 && logger.IsDebugEnabled() {
    85  		logger.Debug().
    86  			Str("txroothash", types.EncodeB64(block.GetHeader().GetTxsRootHash())).
    87  			Int("hashed", len(txs)).
    88  			Msg("BF: tx root hash")
    89  	}
    90  
    91  	return block, nil
    92  }
    93  
    94  // ConnectBlock send an AddBlock request to the chain service.
    95  func ConnectBlock(hs component.ICompSyncRequester, block *types.Block, blockState *state.BlockState, timeout time.Duration) error {
    96  	// blockState does not include a valid BlockHash since it is constructed
    97  	// from an incomplete block. So set it here.
    98  	_, err := hs.RequestFuture(message.ChainSvc, &message.AddBlock{PeerID: "", Block: block, Bstate: blockState},
    99  		timeout, "consensus/chain/info.ConnectBlock").Result()
   100  	if err != nil {
   101  		logger.Error().Err(err).Uint64("no", block.Header.BlockNo).
   102  			Str("hash", block.ID()).
   103  			Str("prev", block.PrevID()).
   104  			Msg("failed to connect block")
   105  
   106  		return &ErrBlockConnect{id: block.ID(), prevID: block.PrevID(), ec: err}
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func SyncChain(hs *component.ComponentHub, targetHash []byte, targetNo types.BlockNo, peerID types.PeerID) error {
   113  	logger.Info().Str("peer", p2putil.ShortForm(peerID)).Uint64("no", targetNo).
   114  		Str("hash", enc.ToString(targetHash)).Msg("request to sync for consensus")
   115  
   116  	notiC := make(chan error)
   117  	hs.Tell(message.SyncerSvc, &message.SyncStart{PeerID: peerID, TargetNo: targetNo, NotifyC: notiC})
   118  
   119  	// wait end of sync every 1sec
   120  	select {
   121  	case err := <-notiC:
   122  		if err != nil {
   123  			logger.Error().Err(err).Uint64("no", targetNo).
   124  				Str("hash", enc.ToString(targetHash)).
   125  				Msg("failed to sync")
   126  
   127  			return err
   128  		}
   129  	}
   130  
   131  	logger.Info().Str("peer", p2putil.ShortForm(peerID)).Msg("succeeded to sync for consensus")
   132  	// TODO check best block is equal to target Hash/no
   133  	return nil
   134  }