8 log "github.com/sirupsen/logrus"
10 "github.com/bytom/vapor/account"
11 "github.com/bytom/vapor/blockchain/txbuilder"
12 "github.com/bytom/vapor/consensus"
13 "github.com/bytom/vapor/errors"
14 "github.com/bytom/vapor/protocol"
15 "github.com/bytom/vapor/protocol/bc"
16 "github.com/bytom/vapor/protocol/bc/types"
17 "github.com/bytom/vapor/protocol/state"
18 "github.com/bytom/vapor/protocol/validation"
19 "github.com/bytom/vapor/protocol/vm/vmutil"
31 // NewBlockTemplate returns a new block template that is ready to be solved
32 func NewBlockTemplate(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) (*types.Block, error) {
33 builder := newBlockBuilder(chain, accountManager, timestamp, warnDuration, criticalDuration)
34 return builder.build()
37 type blockBuilder struct {
39 accountManager *account.Manager
42 txStatus *bc.TransactionStatus
43 utxoView *state.UtxoViewpoint
45 warnTimeoutCh <-chan time.Time
46 criticalTimeoutCh <-chan time.Time
51 func newBlockBuilder(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) *blockBuilder {
52 preBlockHeader := chain.BestBlockHeader()
53 block := &types.Block{
54 BlockHeader: types.BlockHeader{
56 Height: preBlockHeader.Height + 1,
57 PreviousBlockHash: preBlockHeader.Hash(),
59 BlockCommitment: types.BlockCommitment{},
60 BlockWitness: types.BlockWitness{Witness: make([][]byte, consensus.ActiveNetParams.NumOfConsensusNode)},
64 builder := &blockBuilder{
66 accountManager: accountManager,
68 txStatus: bc.NewTransactionStatus(),
69 utxoView: state.NewUtxoViewpoint(),
70 warnTimeoutCh: time.After(warnDuration),
71 criticalTimeoutCh: time.After(criticalDuration),
72 gasLeft: int64(consensus.ActiveNetParams.MaxBlockGas),
73 timeoutStatus: timeoutOk,
78 func (b *blockBuilder) applyCoinbaseTransaction() error {
79 coinbaseTx, err := b.createCoinbaseTx()
81 return errors.Wrap(err, "fail on create coinbase tx")
84 gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: b.block.Height}, Transactions: []*bc.Tx{coinbaseTx.Tx}})
89 b.block.Transactions = append(b.block.Transactions, coinbaseTx)
90 if err := b.txStatus.SetStatus(0, false); err != nil {
94 b.gasLeft -= gasState.GasUsed
97 func (b *blockBuilder) applyTransactions(txs []*types.Tx, timeoutStatus uint8) error {
98 tempTxs := []*types.Tx{}
99 for i := 0; i < len(txs); i++ {
100 if tempTxs = append(tempTxs, txs[i]); len(tempTxs) < batchApplyNum && i != len(txs)-1 {
104 results, gasLeft := preValidateTxs(tempTxs, b.chain, b.utxoView, b.gasLeft)
105 for _, result := range results {
106 if result.err != nil && !result.gasOnly {
107 log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("mining block generation: skip tx due to")
108 b.chain.GetTxPool().RemoveTransaction(&result.tx.ID)
112 if err := b.txStatus.SetStatus(len(b.block.Transactions), result.gasOnly); err != nil {
116 b.block.Transactions = append(b.block.Transactions, result.tx)
120 tempTxs = []*types.Tx{}
121 if b.getTimeoutStatus() >= timeoutStatus {
128 func (b *blockBuilder) applyTransactionFromPool() error {
129 txDescList := b.chain.GetTxPool().GetTransactions()
130 sort.Sort(byTime(txDescList))
132 poolTxs := make([]*types.Tx, len(txDescList))
133 for i, txDesc := range txDescList {
134 poolTxs[i] = txDesc.Tx
137 return b.applyTransactions(poolTxs, timeoutWarn)
140 func (b *blockBuilder) applyTransactionFromSubProtocol() error {
141 cp, err := b.accountManager.GetCoinbaseControlProgram()
146 isTimeout := func() bool {
147 return b.getTimeoutStatus() > timeoutOk
150 for i, p := range b.chain.SubProtocols() {
151 if b.gasLeft <= 0 || isTimeout() {
155 subTxs, err := p.BeforeProposalBlock(b.block.Transactions, cp, b.block.Height, b.gasLeft, isTimeout)
157 log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package")
161 if err := b.applyTransactions(subTxs, timeoutCritical); err != nil {
168 func (b *blockBuilder) build() (*types.Block, error) {
169 if err := b.applyCoinbaseTransaction(); err != nil {
173 if err := b.applyTransactionFromPool(); err != nil {
177 if err := b.applyTransactionFromSubProtocol(); err != nil {
181 if err := b.calcBlockCommitment(); err != nil {
185 if err := b.chain.SignBlockHeader(&b.block.BlockHeader); err != nil {
192 func (b *blockBuilder) calcBlockCommitment() (err error) {
193 var txEntries []*bc.Tx
194 for _, tx := range b.block.Transactions {
195 txEntries = append(txEntries, tx.Tx)
198 b.block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
203 b.block.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(b.txStatus.VerifyStatus)
207 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
208 // based on the passed block height to the provided address. When the address
209 // is nil, the coinbase transaction will instead be redeemable by anyone.
210 func (b *blockBuilder) createCoinbaseTx() (*types.Tx, error) {
211 consensusResult, err := b.chain.GetConsensusResultByHash(&b.block.PreviousBlockHash)
216 rewards, err := consensusResult.GetCoinbaseRewards(b.block.Height - 1)
221 return createCoinbaseTxByReward(b.accountManager, b.block.Height, rewards)
224 func (b *blockBuilder) getTimeoutStatus() uint8 {
225 if b.timeoutStatus == timeoutCritical {
226 return b.timeoutStatus
230 case <-b.criticalTimeoutCh:
231 b.timeoutStatus = timeoutCritical
232 case <-b.warnTimeoutCh:
233 b.timeoutStatus = timeoutWarn
237 return b.timeoutStatus
240 func createCoinbaseTxByReward(accountManager *account.Manager, blockHeight uint64, rewards []state.CoinbaseReward) (tx *types.Tx, err error) {
241 arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
243 if accountManager == nil {
244 script, err = vmutil.DefaultCoinbaseProgram()
246 script, err = accountManager.GetCoinbaseControlProgram()
247 arbitrary = append(arbitrary, accountManager.GetCoinbaseArbitrary()...)
253 if len(arbitrary) > consensus.ActiveNetParams.CoinbaseArbitrarySizeLimit {
254 return nil, validation.ErrCoinbaseArbitraryOversize
257 builder := txbuilder.NewBuilder(time.Now())
258 if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
261 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, 0, script)); err != nil {
265 for _, r := range rewards {
266 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, r.Amount, r.ControlProgram)); err != nil {
271 _, txData, err := builder.Build()
276 byteData, err := txData.MarshalText()
281 txData.SerializedSize = uint64(len(byteData))
284 Tx: types.MapTx(txData),
289 type validateTxResult struct {
295 func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
296 var results []*validateTxResult
298 bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
299 bcTxs := make([]*bc.Tx, len(txs))
300 for i, tx := range txs {
304 validateResults := validation.ValidateTxs(bcTxs, bcBlock)
305 for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
307 gasStatus := validateResults[i].GetGasState()
308 if err := validateResults[i].GetError(); err != nil {
309 if !gasStatus.GasValid {
310 results = append(results, &validateTxResult{tx: txs[i], err: err})
316 if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
317 results = append(results, &validateTxResult{tx: txs[i], err: err})
321 if gasLeft-gasStatus.GasUsed < 0 {
325 if err := view.ApplyTransaction(bcBlock, bcTxs[i], gasOnlyTx); err != nil {
326 results = append(results, &validateTxResult{tx: txs[i], err: err})
330 if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
331 results = append(results, &validateTxResult{tx: txs[i], err: err})
335 results = append(results, &validateTxResult{tx: txs[i], gasOnly: gasOnlyTx, err: validateResults[i].GetError()})
336 gasLeft -= gasStatus.GasUsed
338 return results, gasLeft
341 func validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
342 for _, subProtocol := range subProtocols {
343 verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
344 if err := subProtocol.ValidateTx(tx, verifyResult); err != nil {