OSDN Git Service

remove unused config simd (#2037)
[bytom/bytom.git] / proposal / proposal.go
1 package proposal
2
3 import (
4         "encoding/hex"
5         "sort"
6         "strconv"
7         "time"
8
9         log "github.com/sirupsen/logrus"
10
11         "github.com/bytom/bytom/account"
12         "github.com/bytom/bytom/blockchain/txbuilder"
13         "github.com/bytom/bytom/consensus"
14         "github.com/bytom/bytom/errors"
15         "github.com/bytom/bytom/protocol"
16         "github.com/bytom/bytom/protocol/bc"
17         "github.com/bytom/bytom/protocol/bc/types"
18         "github.com/bytom/bytom/protocol/state"
19         "github.com/bytom/bytom/protocol/validation"
20         "github.com/bytom/bytom/protocol/vm/vmutil"
21 )
22
23 const (
24         logModule     = "proposal"
25         batchApplyNum = 16
26         softMaxTxNum  = 128
27
28         timeoutOk = iota + 1
29         timeoutWarn
30         timeoutCritical
31 )
32
33 // NewBlockTemplate returns a new block template that is ready to be solved
34 func NewBlockTemplate(chain *protocol.Chain, validator *state.Validator, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) (*types.Block, error) {
35         builder := newBlockBuilder(chain, validator, accountManager, timestamp, warnDuration, criticalDuration)
36         return builder.build()
37 }
38
39 type blockBuilder struct {
40         chain          *protocol.Chain
41         validator      *state.Validator
42         accountManager *account.Manager
43
44         block    *types.Block
45         utxoView *state.UtxoViewpoint
46
47         warnTimeoutCh     <-chan time.Time
48         criticalTimeoutCh <-chan time.Time
49         timeoutStatus     uint8
50         gasLeft           int64
51 }
52
53 func newBlockBuilder(chain *protocol.Chain, validator *state.Validator, accountManager *account.Manager, timestamp uint64, warnDuration, criticalDuration time.Duration) *blockBuilder {
54         preBlockHeader := chain.BestBlockHeader()
55         block := &types.Block{
56                 BlockHeader: types.BlockHeader{
57                         Version:           1,
58                         Height:            preBlockHeader.Height + 1,
59                         PreviousBlockHash: preBlockHeader.Hash(),
60                         Timestamp:         timestamp,
61                         BlockCommitment:   types.BlockCommitment{},
62                 },
63         }
64
65         builder := &blockBuilder{
66                 chain:             chain,
67                 validator:         validator,
68                 accountManager:    accountManager,
69                 block:             block,
70                 utxoView:          state.NewUtxoViewpoint(),
71                 warnTimeoutCh:     time.After(warnDuration),
72                 criticalTimeoutCh: time.After(criticalDuration),
73                 gasLeft:           int64(consensus.MaxBlockGas),
74                 timeoutStatus:     timeoutOk,
75         }
76         return builder
77 }
78
79 func (b *blockBuilder) build() (*types.Block, error) {
80         b.block.Transactions = []*types.Tx{nil}
81         if err := b.applyTransactionFromPool(); err != nil {
82                 return nil, err
83         }
84
85         if err := b.applyCoinbaseTransaction(); err != nil {
86                 return nil, err
87         }
88
89         if err := b.calculateBlockCommitment(); err != nil {
90                 return nil, err
91         }
92
93         blockHeader := &b.block.BlockHeader
94         b.chain.SignBlockHeader(blockHeader)
95         return b.block, nil
96 }
97
98 func (b *blockBuilder) applyCoinbaseTransaction() error {
99         coinbaseTx, err := b.createCoinbaseTx()
100         if err != nil {
101                 return errors.Wrap(err, "fail on create coinbase tx")
102         }
103
104         gasState, err := validation.ValidateTx(coinbaseTx.Tx, &bc.Block{BlockHeader: &bc.BlockHeader{Height: b.block.Height}, Transactions: []*bc.Tx{coinbaseTx.Tx}}, b.chain.ProgramConverter)
105         if err != nil {
106                 return err
107         }
108
109         b.block.Transactions[0] = coinbaseTx
110         b.gasLeft -= gasState.GasUsed
111         return nil
112 }
113
114 func (b *blockBuilder) applyTransactionFromPool() error {
115         txDescList := b.chain.GetTxPool().GetTransactions()
116         sort.Sort(byTime(txDescList))
117         return b.applyTransactions(txDescList, timeoutWarn)
118 }
119
120 func (b *blockBuilder) calculateBlockCommitment() (err error) {
121         var txEntries []*bc.Tx
122         for _, tx := range b.block.Transactions {
123                 txEntries = append(txEntries, tx.Tx)
124         }
125
126         b.block.BlockHeader.BlockCommitment.TransactionsMerkleRoot, err = types.TxMerkleRoot(txEntries)
127         if err != nil {
128                 return err
129         }
130
131         return nil
132 }
133
134 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
135 // based on the passed block height to the provided address.  When the address
136 // is nil, the coinbase transaction will instead be redeemable by anyone.
137 func (b *blockBuilder) createCoinbaseTx() (tx *types.Tx, err error) {
138         arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(b.block.Height, 10))...)
139         var script []byte
140         if b.accountManager == nil {
141                 script, err = vmutil.DefaultCoinbaseProgram()
142         } else {
143                 script, err = b.accountManager.GetCoinbaseControlProgram()
144                 arbitrary = append(arbitrary, b.accountManager.GetCoinbaseArbitrary()...)
145         }
146         if err != nil {
147                 return nil, err
148         }
149
150         if len(arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
151                 return nil, validation.ErrCoinbaseArbitraryOversize
152         }
153
154         builder := txbuilder.NewBuilder(time.Now())
155         if err = builder.AddInput(types.NewCoinbaseInput(arbitrary), &txbuilder.SigningInstruction{}); err != nil {
156                 return nil, err
157         }
158
159         checkpoint, err := b.getPrevCheckpoint()
160         if err != nil {
161                 return nil, err
162         }
163
164         if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, script, [][]byte{})); err != nil {
165                 return nil, err
166         }
167
168         if b.block.Height%consensus.ActiveNetParams.BlocksOfEpoch == 1 && b.block.Height != 1 {
169                 for controlProgram, amount := range checkpoint.Rewards {
170                         if controlProgram == hex.EncodeToString(script) {
171                                 builder.Outputs()[0].Amount = amount
172                                 continue
173                         }
174
175                         controlProgramBytes, err := hex.DecodeString(controlProgram)
176                         if err != nil {
177                                 return nil, err
178                         }
179
180                         if err := builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, amount, controlProgramBytes, [][]byte{})); err != nil {
181                                 return nil, err
182                         }
183                 }
184         }
185
186         _, txData, err := builder.Build()
187         if err != nil {
188                 return nil, err
189         }
190
191         byteData, err := txData.MarshalText()
192         if err != nil {
193                 return nil, err
194         }
195
196         txData.SerializedSize = uint64(len(byteData))
197         tx = &types.Tx{
198                 TxData: *txData,
199                 Tx:     types.MapTx(txData),
200         }
201         return tx, nil
202 }
203
204 func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus uint8) error {
205         batchTxs := []*protocol.TxDesc{}
206         for i := 0; i < len(txs); i++ {
207                 if batchTxs = append(batchTxs, txs[i]); len(batchTxs) < batchApplyNum && i != len(txs)-1 {
208                         continue
209                 }
210
211                 results, gasLeft := b.preValidateTxs(batchTxs, b.chain, b.utxoView, b.gasLeft)
212                 for _, result := range results {
213                         if result.err != nil {
214                                 log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("propose block generation: skip tx due to")
215                                 b.chain.GetTxPool().RemoveTransaction(&result.tx.ID)
216                                 continue
217                         }
218
219                         b.block.Transactions = append(b.block.Transactions, result.tx)
220                 }
221
222                 b.gasLeft = gasLeft
223                 batchTxs = batchTxs[:0]
224                 if b.getTimeoutStatus() >= timeoutStatus || len(b.block.Transactions) > softMaxTxNum {
225                         break
226                 }
227         }
228         return nil
229 }
230
231 type validateTxResult struct {
232         tx  *types.Tx
233         err error
234 }
235
236 func (b *blockBuilder) preValidateTxs(txs []*protocol.TxDesc, chain *protocol.Chain, view *state.UtxoViewpoint, gasLeft int64) ([]*validateTxResult, int64) {
237         var results []*validateTxResult
238         bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: chain.BestBlockHeight() + 1}}
239         bcTxs := make([]*bc.Tx, len(txs))
240         for i, tx := range txs {
241                 bcTxs[i] = tx.Tx.Tx
242         }
243
244         validateResults := validation.ValidateTxs(bcTxs, bcBlock, b.chain.ProgramConverter)
245         for i := 0; i < len(validateResults) && gasLeft > 0; i++ {
246                 tx := txs[i].Tx
247                 gasStatus := validateResults[i].GetGasState()
248                 if err := validateResults[i].GetError(); err != nil {
249                         results = append(results, &validateTxResult{tx: tx, err: err})
250                         continue
251                 }
252
253                 if err := chain.GetTransactionsUtxo(view, []*bc.Tx{bcTxs[i]}); err != nil {
254                         results = append(results, &validateTxResult{tx: tx, err: err})
255                         continue
256                 }
257
258                 if gasLeft-gasStatus.GasUsed < 0 {
259                         break
260                 }
261
262                 if err := view.ApplyTransaction(bcBlock, bcTxs[i]); err != nil {
263                         results = append(results, &validateTxResult{tx: tx, err: err})
264                         continue
265                 }
266
267                 results = append(results, &validateTxResult{tx: tx, err: validateResults[i].GetError()})
268                 gasLeft -= gasStatus.GasUsed
269         }
270         return results, gasLeft
271 }
272
273 func (b *blockBuilder) getTimeoutStatus() uint8 {
274         if b.timeoutStatus == timeoutCritical {
275                 return b.timeoutStatus
276         }
277
278         select {
279         case <-b.criticalTimeoutCh:
280                 b.timeoutStatus = timeoutCritical
281         case <-b.warnTimeoutCh:
282                 b.timeoutStatus = timeoutWarn
283         default:
284         }
285
286         return b.timeoutStatus
287 }
288
289 func (b *blockBuilder) prevBlockHash() *bc.Hash {
290         return &b.block.PreviousBlockHash
291 }
292
293 func (b *blockBuilder) getPrevCheckpoint() (*state.Checkpoint, error) {
294         return b.chain.PrevCheckpointByPrevHash(b.prevBlockHash())
295 }