OSDN Git Service

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