10 "github.com/vapor/protocol/vm"
12 "github.com/vapor/common"
14 log "github.com/sirupsen/logrus"
16 "github.com/vapor/account"
17 "github.com/vapor/blockchain/txbuilder"
18 "github.com/vapor/config"
19 "github.com/vapor/consensus"
20 engine "github.com/vapor/consensus/consensus"
21 dpos "github.com/vapor/consensus/consensus/dpos"
22 "github.com/vapor/crypto/ed25519/chainkd"
23 "github.com/vapor/errors"
24 "github.com/vapor/protocol"
25 "github.com/vapor/protocol/bc"
26 "github.com/vapor/protocol/bc/types"
27 "github.com/vapor/protocol/state"
28 "github.com/vapor/protocol/validation"
29 "github.com/vapor/protocol/vm/vmutil"
32 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
33 // based on the passed block height to the provided address. When the address
34 // is nil, the coinbase transaction will instead be redeemable by anyone.
35 func createCoinbaseTx(accountManager *account.Manager, amount uint64, blockHeight uint64, delegateInfo interface{}, timestamp uint64) (tx *types.Tx, err error) {
36 //amount += consensus.BlockSubsidy(blockHeight)
37 arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(blockHeight, 10))...)
40 address, _ := common.DecodeAddress(config.CommonConfig.Consensus.Coinbase, &consensus.ActiveNetParams)
41 redeemContract := address.ScriptAddress()
42 script, _ = vmutil.P2WPKHProgram(redeemContract)
48 if len(arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
49 return nil, validation.ErrCoinbaseArbitraryOversize
52 builder := txbuilder.NewBuilder(time.Now())
53 if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
57 if err = builder.AddOutput(types.NewTxOutput(*consensus.BTMAssetID, amount, script)); err != nil {
60 _, txData, err := builder.Build()
65 byteData, err := txData.MarshalText()
69 txData.SerializedSize = uint64(len(byteData))
70 delegates := dpos.DelegateInfoList{}
71 if delegateInfo != nil {
72 tmp := delegateInfo.(*dpos.DelegateInfo)
73 delegates.Delegate = *tmp
77 if config.CommonConfig.Consensus.XPrv == "" {
78 return nil, errors.New("Signer is empty")
80 xPrv.UnmarshalText([]byte(config.CommonConfig.Consensus.XPrv))
83 binary.LittleEndian.PutUint64(buf[:], timestamp)
84 delegates.SigTime = xPrv.Sign(buf[:])
85 delegates.Xpub = xPrv.XPub()
87 data, err := json.Marshal(&delegates)
97 data, err = json.Marshal(&msg)
101 txData.ReferenceData = data
105 Tx: types.MapTx(txData),
110 // NewBlockTemplate returns a new block template that is ready to be solved
111 func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager *account.Manager, engine engine.Engine, delegateInfo interface{}) (b *types.Block, err error) {
112 view := state.NewUtxoViewpoint()
113 txStatus := bc.NewTransactionStatus()
114 if err := txStatus.SetStatus(0, false); err != nil {
117 txEntries := []*bc.Tx{nil}
121 // get preblock info for generate next block
122 preBlockHeader := c.BestBlockHeader()
123 preBlockHash := preBlockHeader.Hash()
124 nextBlockHeight := preBlockHeader.Height + 1
126 header := types.BlockHeader{
128 Height: nextBlockHeight,
129 PreviousBlockHash: preBlockHash,
130 Timestamp: uint64(time.Now().Unix()),
131 BlockCommitment: types.BlockCommitment{},
135 bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: nextBlockHeight}}
136 b.Transactions = []*types.Tx{nil}
138 txs := txPool.GetTransactions()
139 sort.Sort(byTime(txs))
140 for _, txDesc := range txs {
144 if err := c.GetTransactionsUtxo(view, []*bc.Tx{tx}); err != nil {
145 blkGenSkipTxForErr(txPool, &tx.ID, err)
148 gasStatus, err := validation.ValidateTx(tx, bcBlock)
150 if !gasStatus.GasValid {
151 blkGenSkipTxForErr(txPool, &tx.ID, err)
157 if gasUsed+uint64(gasStatus.GasUsed) > consensus.MaxBlockGas {
161 if err := view.ApplyTransaction(bcBlock, tx, gasOnlyTx); err != nil {
162 blkGenSkipTxForErr(txPool, &tx.ID, err)
166 if err := txStatus.SetStatus(len(b.Transactions), gasOnlyTx); err != nil {
170 b.Transactions = append(b.Transactions, txDesc.Tx)
171 txEntries = append(txEntries, tx)
172 gasUsed += uint64(gasStatus.GasUsed)
174 if gasUsed == consensus.MaxBlockGas {
179 b.BlockHeader = header
180 // creater coinbase transaction
181 b.Transactions[0], err = createCoinbaseTx(accountManager, txFee, nextBlockHeight, delegateInfo, b.Timestamp)
183 return nil, errors.Wrap(err, "fail on createCoinbaseTx")
185 txEntries[0] = b.Transactions[0].Tx
187 b.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
192 b.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(txStatus.VerifyStatus)
196 func blkGenSkipTxForErr(txPool *protocol.TxPool, txHash *bc.Hash, err error) {
197 log.WithField("error", err).Error("mining block generation: skip tx due to")
198 txPool.RemoveTransaction(txHash)