github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/mining/mining.go (about)

     1  package mining
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"time"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  
    10  	"github.com/bytom/bytom/account"
    11  	"github.com/bytom/bytom/blockchain/txbuilder"
    12  	"github.com/bytom/bytom/consensus"
    13  	"github.com/bytom/bytom/errors"
    14  	"github.com/bytom/bytom/protocol"
    15  	"github.com/bytom/bytom/protocol/bc"
    16  	"github.com/bytom/bytom/protocol/bc/types"
    17  	"github.com/bytom/bytom/protocol/state"
    18  	"github.com/bytom/bytom/protocol/validation"
    19  	"github.com/bytom/bytom/protocol/vm/vmutil"
    20  )
    21  
    22  const logModule = "mining"
    23  
    24  // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
    25  // based on the passed block height to the provided address.  When the address
    26  // is nil, the coinbase transaction will instead be redeemable by anyone.
    27  func createCoinbaseTx(accountManager *account.Manager, amount uint64, blockHeight uint64) (tx *types.Tx, err error) {
    28  	amount += consensus.BlockSubsidy(blockHeight)
    29  	arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
    30  
    31  	var script []byte
    32  	if accountManager == nil {
    33  		script, err = vmutil.DefaultCoinbaseProgram()
    34  	} else {
    35  		script, err = accountManager.GetCoinbaseControlProgram()
    36  		arbitrary = append(arbitrary, accountManager.GetCoinbaseArbitrary()...)
    37  	}
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	if len(arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
    43  		return nil, validation.ErrCoinbaseArbitraryOversize
    44  	}
    45  
    46  	builder := txbuilder.NewBuilder(time.Now())
    47  	if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
    48  		return nil, err
    49  	}
    50  	if err = builder.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, amount, script)); err != nil {
    51  		return nil, err
    52  	}
    53  	_, txData, err := builder.Build()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	byteData, err := txData.MarshalText()
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	txData.SerializedSize = uint64(len(byteData))
    63  
    64  	tx = &types.Tx{
    65  		TxData: *txData,
    66  		Tx:     types.MapTx(txData),
    67  	}
    68  	return tx, nil
    69  }
    70  
    71  // NewBlockTemplate returns a new block template that is ready to be solved
    72  func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager *account.Manager) (b *types.Block, err error) {
    73  	view := state.NewUtxoViewpoint()
    74  	txStatus := bc.NewTransactionStatus()
    75  	if err := txStatus.SetStatus(0, false); err != nil {
    76  		return nil, err
    77  	}
    78  	txEntries := []*bc.Tx{nil}
    79  	gasUsed := uint64(0)
    80  	txFee := uint64(0)
    81  
    82  	// get preblock info for generate next block
    83  	preBlockHeader := c.BestBlockHeader()
    84  	preBlockHash := preBlockHeader.Hash()
    85  	nextBlockHeight := preBlockHeader.Height + 1
    86  	nextBits, err := c.CalcNextBits(&preBlockHash)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	b = &types.Block{
    92  		BlockHeader: types.BlockHeader{
    93  			Version:           1,
    94  			Height:            nextBlockHeight,
    95  			PreviousBlockHash: preBlockHash,
    96  			Timestamp:         uint64(time.Now().Unix()),
    97  			BlockCommitment:   types.BlockCommitment{},
    98  			Bits:              nextBits,
    99  		},
   100  	}
   101  	bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: nextBlockHeight}}
   102  	b.Transactions = []*types.Tx{nil}
   103  
   104  	txs := txPool.GetTransactions()
   105  	sort.Sort(byTime(txs))
   106  	for _, txDesc := range txs {
   107  		tx := txDesc.Tx.Tx
   108  		gasOnlyTx := false
   109  
   110  		if err := c.GetTransactionsUtxo(view, []*bc.Tx{tx}); err != nil {
   111  			blkGenSkipTxForErr(txPool, &tx.ID, err)
   112  			continue
   113  		}
   114  
   115  		gasStatus, err := validation.ValidateTx(tx, bcBlock)
   116  		if err != nil {
   117  			if !gasStatus.GasValid {
   118  				blkGenSkipTxForErr(txPool, &tx.ID, err)
   119  				continue
   120  			}
   121  			gasOnlyTx = true
   122  		}
   123  
   124  		if gasUsed+uint64(gasStatus.GasUsed) > consensus.MaxBlockGas {
   125  			break
   126  		}
   127  
   128  		if err := view.ApplyTransaction(bcBlock, tx, gasOnlyTx); err != nil {
   129  			blkGenSkipTxForErr(txPool, &tx.ID, err)
   130  			continue
   131  		}
   132  
   133  		if err := txStatus.SetStatus(len(b.Transactions), gasOnlyTx); err != nil {
   134  			return nil, err
   135  		}
   136  
   137  		b.Transactions = append(b.Transactions, txDesc.Tx)
   138  		txEntries = append(txEntries, tx)
   139  		gasUsed += uint64(gasStatus.GasUsed)
   140  		txFee += txDesc.Fee
   141  
   142  		if gasUsed == consensus.MaxBlockGas {
   143  			break
   144  		}
   145  	}
   146  
   147  	// creater coinbase transaction
   148  	b.Transactions[0], err = createCoinbaseTx(accountManager, txFee, nextBlockHeight)
   149  	if err != nil {
   150  		return nil, errors.Wrap(err, "fail on createCoinbaseTx")
   151  	}
   152  	txEntries[0] = b.Transactions[0].Tx
   153  
   154  	b.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	b.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(txStatus.VerifyStatus)
   160  	return b, err
   161  }
   162  
   163  func blkGenSkipTxForErr(txPool *protocol.TxPool, txHash *bc.Hash, err error) {
   164  	log.WithFields(log.Fields{"module": logModule, "error": err}).Error("mining block generation: skip tx due to")
   165  	txPool.RemoveTransaction(txHash)
   166  }