8 log "github.com/sirupsen/logrus"
10 "github.com/vapor/account"
11 "github.com/vapor/blockchain/txbuilder"
12 "github.com/vapor/consensus"
13 "github.com/vapor/errors"
14 "github.com/vapor/protocol"
15 "github.com/vapor/protocol/bc"
16 "github.com/vapor/protocol/bc/types"
17 "github.com/vapor/protocol/state"
18 "github.com/vapor/protocol/validation"
19 "github.com/vapor/protocol/vm/vmutil"
26 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
27 // based on the passed block height to the provided address. When the address
28 // is nil, the coinbase transaction will instead be redeemable by anyone.
29 func createCoinbaseTx(accountManager *account.Manager, chain *protocol.Chain, preBlockHeader *types.BlockHeader) (tx *types.Tx, err error) {
30 preBlockHash := preBlockHeader.Hash()
31 consensusResult, err := chain.GetConsensusResultByHash(&preBlockHash)
36 rewards, err := consensusResult.GetCoinbaseRewards(preBlockHeader.Height)
41 return createCoinbaseTxByReward(accountManager, preBlockHeader.Height + 1, rewards)
44 func createCoinbaseTxByReward(accountManager *account.Manager, blockHeight uint64, rewards []state.CoinbaseReward) (tx *types.Tx, err error) {
45 arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
47 if accountManager == nil {
48 script, err = vmutil.DefaultCoinbaseProgram()
50 script, err = accountManager.GetCoinbaseControlProgram()
51 arbitrary = append(arbitrary, accountManager.GetCoinbaseArbitrary()...)
57 if len(arbitrary) > consensus.ActiveNetParams.CoinbaseArbitrarySizeLimit {
58 return nil, validation.ErrCoinbaseArbitraryOversize
61 builder := txbuilder.NewBuilder(time.Now())
62 if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
65 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, 0, script)); err != nil {
69 for _, r := range rewards {
70 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, r.Amount, r.ControlProgram)); err != nil {
75 _, txData, err := builder.Build()
80 byteData, err := txData.MarshalText()
85 txData.SerializedSize = uint64(len(byteData))
88 Tx: types.MapTx(txData),
93 // NewBlockTemplate returns a new block template that is ready to be solved
94 func NewBlockTemplate(chain *protocol.Chain, accountManager *account.Manager, timestamp uint64) (*types.Block, error) {
95 block := createBasicBlock(chain, timestamp)
97 view := state.NewUtxoViewpoint()
98 txStatus := bc.NewTransactionStatus()
100 gasLeft, err := applyCoinbaseTransaction(chain, block, txStatus, accountManager, int64(consensus.ActiveNetParams.MaxBlockGas))
105 gasLeft, err = applyTransactionFromPool(chain, view, block, txStatus, gasLeft)
110 if err := applyTransactionFromSubProtocol(chain, view, block, txStatus, accountManager, gasLeft); err != nil {
114 var txEntries []*bc.Tx
115 for _, tx := range block.Transactions {
116 txEntries = append(txEntries, tx.Tx)
119 block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
124 block.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(txStatus.VerifyStatus)
126 _, err = chain.SignBlock(block)
130 func createBasicBlock(chain *protocol.Chain, timestamp uint64) *types.Block {
131 preBlockHeader := chain.BestBlockHeader()
133 BlockHeader: types.BlockHeader{
135 Height: preBlockHeader.Height + 1,
136 PreviousBlockHash: preBlockHeader.Hash(),
137 Timestamp: timestamp,
138 BlockCommitment: types.BlockCommitment{},
139 BlockWitness: types.BlockWitness{Witness: make([][]byte, consensus.ActiveNetParams.NumOfConsensusNode)},
144 func applyCoinbaseTransaction(chain *protocol.Chain, block *types.Block, txStatus *bc.TransactionStatus, accountManager *account.Manager, gasLeft int64) (int64, error) {
145 coinbaseTx, err := createCoinbaseTx(accountManager, chain, chain.BestBlockHeader())
147 return 0, errors.Wrap(err, "fail on create coinbase tx")
150 gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}, Transactions: []*bc.Tx{coinbaseTx.Tx}})
155 block.Transactions = append(block.Transactions, coinbaseTx)
156 if err := txStatus.SetStatus(0, false); err != nil {
160 return gasLeft - gasState.GasUsed, nil
164 func applyTransactionFromPool(chain *protocol.Chain, view *state.UtxoViewpoint, block *types.Block, txStatus *bc.TransactionStatus, gasLeft int64) (int64, error) {
165 poolTxs := getAllTxsFromPool(chain.GetTxPool())
166 results, gasLeft := preValidateTxs(poolTxs, chain, view, gasLeft)
167 for _, result := range results {
168 if result.err != nil && !result.gasOnly {
169 blkGenSkipTxForErr(chain.GetTxPool(), &result.tx.ID, result.err)
173 if err := txStatus.SetStatus(len(block.Transactions), result.gasOnly); err != nil {
177 block.Transactions = append(block.Transactions, result.tx)
182 func applyTransactionFromSubProtocol(chain *protocol.Chain, view *state.UtxoViewpoint, block *types.Block, txStatus *bc.TransactionStatus, accountManager *account.Manager, gasLeft int64) error {
183 txs, err := getTxsFromSubProtocols(chain, accountManager, gasLeft)
188 results, gasLeft := preValidateTxs(txs, chain, view, gasLeft)
189 for _, result := range results {
190 if result.err != nil {
194 if err := txStatus.SetStatus(len(block.Transactions), result.gasOnly); err != nil {
198 block.Transactions = append(block.Transactions, result.tx)
203 type validateTxResult struct {
209 func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
210 var results []*validateTxResult
212 bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
213 bcTxs := make([]*bc.Tx, len(txs))
214 for i, tx := range txs {
218 validateResults := validation.ValidateTxs(bcTxs, bcBlock)
219 for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
221 gasStatus := validateResults[i].GetGasState()
222 if err := validateResults[i].GetError(); err != nil {
223 if !gasStatus.GasValid {
224 results = append(results, &validateTxResult{tx: txs[i], err: err})
230 if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
231 results = append(results, &validateTxResult{tx: txs[i], err: err})
235 if gasLeft-gasStatus.GasUsed < 0 {
239 if err := view.ApplyTransaction(bcBlock, bcTxs[i], gasOnlyTx); err != nil {
240 results = append(results, &validateTxResult{tx: txs[i], err: err})
244 if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
245 results = append(results, &validateTxResult{tx: txs[i], err: err})
249 results = append(results, &validateTxResult{tx: txs[i], gasOnly: gasOnlyTx, err: validateResults[i].GetError()})
250 gasLeft -= gasStatus.GasUsed
252 return results, gasLeft
255 func validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
256 for _, subProtocol := range subProtocols {
257 verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
258 if err := subProtocol.ValidateTx(tx, verifyResult); err != nil {
265 func getAllTxsFromPool(txPool *protocol.TxPool) []*types.Tx {
266 txDescList := txPool.GetTransactions()
267 sort.Sort(byTime(txDescList))
269 poolTxs := make([]*types.Tx, len(txDescList))
270 for i, txDesc := range txDescList {
271 poolTxs[i] = txDesc.Tx
276 func getTxsFromSubProtocols(chain *protocol.Chain, accountManager *account.Manager, gasLeft int64) ([]*types.Tx, error) {
277 cp, err := accountManager.GetCoinbaseControlProgram()
282 var result []*types.Tx
283 var subTxs []*types.Tx
284 for i, p := range chain.SubProtocols() {
289 subTxs, gasLeft, err = p.BeforeProposalBlock(cp, chain.BestBlockHeight() + 1, gasLeft)
291 log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package")
295 result = append(result, subTxs...)
300 func blkGenSkipTxForErr(txPool *protocol.TxPool, txHash *bc.Hash, err error) {
301 log.WithFields(log.Fields{"module": logModule, "error": err}).Error("mining block generation: skip tx due to")
302 txPool.RemoveTransaction(txHash)