OSDN Git Service

Mov merge (#430)
[bytom/vapor.git] / proposal / proposal.go
1 package proposal
2
3 import (
4         "sort"
5         "strconv"
6         "time"
7
8         log "github.com/sirupsen/logrus"
9
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"
20 )
21
22 const (
23         logModule = "mining"
24 )
25
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)
32         if err != nil {
33                 return nil, err
34         }
35
36         rewards, err := consensusResult.GetCoinbaseRewards(preBlockHeader.Height)
37         if err != nil {
38                 return nil, err
39         }
40
41         return createCoinbaseTxByReward(accountManager, preBlockHeader.Height + 1, rewards)
42 }
43
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))...)
46         var script []byte
47         if accountManager == nil {
48                 script, err = vmutil.DefaultCoinbaseProgram()
49         } else {
50                 script, err = accountManager.GetCoinbaseControlProgram()
51                 arbitrary = append(arbitrary, accountManager.GetCoinbaseArbitrary()...)
52         }
53         if err != nil {
54                 return nil, err
55         }
56
57         if len(arbitrary) > consensus.ActiveNetParams.CoinbaseArbitrarySizeLimit {
58                 return nil, validation.ErrCoinbaseArbitraryOversize
59         }
60
61         builder := txbuilder.NewBuilder(time.Now())
62         if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
63                 return nil, err
64         }
65         if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, 0, script)); err != nil {
66                 return nil, err
67         }
68
69         for _, r := range rewards {
70                 if err = builder.AddOutput(types.NewIntraChainOutput(*consensus.BTMAssetID, r.Amount, r.ControlProgram)); err != nil {
71                         return nil, err
72                 }
73         }
74
75         _, txData, err := builder.Build()
76         if err != nil {
77                 return nil, err
78         }
79
80         byteData, err := txData.MarshalText()
81         if err != nil {
82                 return nil, err
83         }
84
85         txData.SerializedSize = uint64(len(byteData))
86         tx = &types.Tx{
87                 TxData: *txData,
88                 Tx:     types.MapTx(txData),
89         }
90         return tx, nil
91 }
92
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)
96
97         view := state.NewUtxoViewpoint()
98         txStatus := bc.NewTransactionStatus()
99
100         gasLeft, err := applyCoinbaseTransaction(chain, block, txStatus, accountManager, int64(consensus.ActiveNetParams.MaxBlockGas))
101         if err != nil {
102                 return nil, err
103         }
104
105         gasLeft, err = applyTransactionFromPool(chain, view, block, txStatus, gasLeft)
106         if err != nil {
107                 return nil, err
108         }
109         
110         if err := applyTransactionFromSubProtocol(chain, view, block, txStatus, accountManager, gasLeft); err != nil {
111                 return nil, err
112         }
113
114         var txEntries []*bc.Tx
115         for _, tx := range block.Transactions {
116                 txEntries = append(txEntries, tx.Tx)
117         }
118
119         block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
120         if err != nil {
121                 return nil, err
122         }
123
124         block.BlockHeader.BlockCommitment.TransactionStatusHash, err = types.TxStatusMerkleRoot(txStatus.VerifyStatus)
125
126         _, err = chain.SignBlock(block)
127         return block, err
128 }
129
130 func createBasicBlock(chain *protocol.Chain, timestamp uint64) *types.Block {
131         preBlockHeader := chain.BestBlockHeader()
132         return &types.Block{
133                 BlockHeader: types.BlockHeader{
134                         Version:           1,
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)},
140                 },
141         }
142 }
143
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())
146         if err != nil {
147                 return 0, errors.Wrap(err, "fail on create coinbase tx")
148         }
149
150         gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}, Transactions: []*bc.Tx{coinbaseTx.Tx}})
151         if err != nil {
152                 return 0, err
153         }
154
155         block.Transactions = append(block.Transactions, coinbaseTx)
156         if err := txStatus.SetStatus(0, false); err != nil {
157                 return 0, err
158         }
159
160         return gasLeft - gasState.GasUsed, nil
161 }
162
163
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)
170                         continue
171                 }
172
173                 if err := txStatus.SetStatus(len(block.Transactions), result.gasOnly); err != nil {
174                         return 0, err
175                 }
176
177                 block.Transactions = append(block.Transactions, result.tx)
178         }
179         return gasLeft, nil
180 }
181
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)
184         if err != nil {
185                 return err
186         }
187
188         results, gasLeft := preValidateTxs(txs, chain, view, gasLeft)
189         for _, result := range results {
190                 if result.err != nil {
191                         return err
192                 }
193
194                 if err := txStatus.SetStatus(len(block.Transactions), result.gasOnly); err != nil {
195                         return err
196                 }
197
198                 block.Transactions = append(block.Transactions, result.tx)
199         }
200         return nil
201 }
202
203 type validateTxResult struct {
204         tx      *types.Tx
205         gasOnly bool
206         err     error
207 }
208
209 func preValidateTxs(txs []*types.Tx, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
210         var results []*validateTxResult
211
212         bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
213         bcTxs := make([]*bc.Tx, len(txs))
214         for i, tx := range txs {
215                 bcTxs[i] = tx.Tx
216         }
217
218         validateResults := validation.ValidateTxs(bcTxs, bcBlock)
219         for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
220                 gasOnlyTx := false
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})
225                                 continue
226                         }
227                         gasOnlyTx = true
228                 }
229
230                 if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
231                         results = append(results, &validateTxResult{tx: txs[i], err: err})
232                         continue
233                 }
234
235                 if gasLeft-gasStatus.GasUsed < 0 {
236                         break
237                 }
238
239                 if err := view.ApplyTransaction(bcBlock, bcTxs[i], gasOnlyTx); err != nil {
240                         results = append(results, &validateTxResult{tx: txs[i], err: err})
241                         continue
242                 }
243
244                 if err := validateBySubProtocols(txs[i], validateResults[i].GetError() != nil, chain.SubProtocols()); err != nil {
245                         results = append(results, &validateTxResult{tx: txs[i], err: err})
246                         continue
247                 }
248
249                 results = append(results, &validateTxResult{tx: txs[i], gasOnly: gasOnlyTx, err: validateResults[i].GetError()})
250                 gasLeft -= gasStatus.GasUsed
251         }
252         return results, gasLeft
253 }
254
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 {
259                         return err
260                 }
261         }
262         return nil
263 }
264
265 func getAllTxsFromPool(txPool *protocol.TxPool) []*types.Tx {
266         txDescList := txPool.GetTransactions()
267         sort.Sort(byTime(txDescList))
268
269         poolTxs := make([]*types.Tx, len(txDescList))
270         for i, txDesc := range txDescList {
271                 poolTxs[i] = txDesc.Tx
272         }
273         return poolTxs
274 }
275
276 func getTxsFromSubProtocols(chain *protocol.Chain, accountManager *account.Manager, gasLeft int64) ([]*types.Tx, error) {
277         cp, err := accountManager.GetCoinbaseControlProgram()
278         if err != nil {
279                 return nil, err
280         }
281
282         var result []*types.Tx
283         var subTxs []*types.Tx
284         for i, p := range chain.SubProtocols() {
285                 if gasLeft <= 0 {
286                         break
287                 }
288
289                 subTxs, gasLeft, err = p.BeforeProposalBlock(cp, chain.BestBlockHeight() + 1, gasLeft)
290                 if err != nil {
291                         log.WithFields(log.Fields{"module": logModule, "index": i, "error": err}).Error("failed on sub protocol txs package")
292                         continue
293                 }
294
295                 result = append(result, subTxs...)
296         }
297         return result, nil
298 }
299
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)
303 }