OSDN Git Service

42604dfd739172032cf9b08cdfd4ce209bb29736
[bytom/bytom.git] / protocol / validation / validation.go
1 package validation
2
3 import (
4         "fmt"
5         "time"
6
7         "github.com/bytom/consensus"
8         "github.com/bytom/consensus/algorithm"
9         "github.com/bytom/consensus/difficulty"
10         "github.com/bytom/consensus/segwit"
11         "github.com/bytom/errors"
12         "github.com/bytom/math/checked"
13         "github.com/bytom/protocol/bc"
14         "github.com/bytom/protocol/seed"
15         "github.com/bytom/protocol/vm"
16 )
17
18 const (
19         defaultGasLimit = int64(80000)
20         muxGasCost      = int64(10)
21         // GasRate indicates the current gas rate
22         GasRate = int64(1000)
23 )
24
25 type gasState struct {
26         gasLeft  int64
27         gasUsed  int64
28         BTMValue int64
29 }
30
31 func (g *gasState) setGas(BTMValue int64) error {
32         if BTMValue < 0 {
33                 return errGasCalculate
34         }
35         g.BTMValue = BTMValue
36
37         if gasAmount, ok := checked.DivInt64(BTMValue, GasRate); ok {
38                 if gasAmount == 0 {
39                         g.gasLeft = muxGasCost
40                 } else if gasAmount < defaultGasLimit {
41                         g.gasLeft = gasAmount
42                 }
43         } else {
44                 return errGasCalculate
45         }
46         return nil
47 }
48
49 func (g *gasState) updateUsage(gasLeft int64) error {
50         if gasLeft < 0 {
51                 return errGasCalculate
52         }
53         if gasUsed, ok := checked.SubInt64(g.gasLeft, gasLeft); ok {
54                 g.gasUsed += gasUsed
55                 g.gasLeft = gasLeft
56         } else {
57                 return errGasCalculate
58         }
59         return nil
60 }
61
62 // validationState contains the context that must propagate through
63 // the transaction graph when validating entries.
64 type validationState struct {
65         // The ID of the blockchain
66         block *bc.Block
67
68         // The enclosing transaction object
69         tx *bc.Tx
70
71         // The ID of the nearest enclosing entry
72         entryID bc.Hash
73
74         // The source position, for validating ValueSources
75         sourcePos uint64
76
77         // The destination position, for validating ValueDestinations
78         destPos uint64
79
80         // Memoized per-entry validation results
81         cache map[bc.Hash]error
82
83         gas *gasState
84
85         gasVaild *int
86 }
87
88 var (
89         errBadTimestamp             = errors.New("block timestamp is great than limit")
90         errGasCalculate             = errors.New("gas usage calculate got a math error")
91         errEmptyResults             = errors.New("transaction has no results")
92         errMismatchedAssetID        = errors.New("mismatched asset id")
93         errMismatchedBlock          = errors.New("mismatched block")
94         errMismatchedMerkleRoot     = errors.New("mismatched merkle root")
95         errMismatchedPosition       = errors.New("mismatched value source/dest positions")
96         errMismatchedReference      = errors.New("mismatched reference")
97         errMismatchedValue          = errors.New("mismatched value")
98         errMisorderedBlockHeight    = errors.New("misordered block height")
99         errMisorderedBlockTime      = errors.New("misordered block time")
100         errMissingField             = errors.New("missing required field")
101         errNoGas                    = errors.New("no gas input")
102         errNoPrevBlock              = errors.New("no previous block")
103         errNoSource                 = errors.New("no source for value")
104         errNonemptyExtHash          = errors.New("non-empty extension hash")
105         errOverflow                 = errors.New("arithmetic overflow/underflow")
106         errPosition                 = errors.New("invalid source or destination position")
107         errWorkProof                = errors.New("invalid difficulty proof of work")
108         errTxVersion                = errors.New("invalid transaction version")
109         errUnbalanced               = errors.New("unbalanced")
110         errUntimelyTransaction      = errors.New("block timestamp outside transaction time range")
111         errVersionRegression        = errors.New("version regression")
112         errWrongBlockSize           = errors.New("block size is too big")
113         errWrongTransactionSize     = errors.New("transaction size is too big")
114         errWrongTransactionStatus   = errors.New("transaction status is wrong")
115         errWrongCoinbaseTransaction = errors.New("wrong coinbase transaction")
116         errWrongCoinbaseAsset       = errors.New("wrong coinbase asset id")
117         errNotStandardTx            = errors.New("gas transaction is not standard transaction")
118 )
119
120 func checkValid(vs *validationState, e bc.Entry) (err error) {
121         entryID := bc.EntryID(e)
122         var ok bool
123         if err, ok = vs.cache[entryID]; ok {
124                 return err
125         }
126
127         defer func() {
128                 vs.cache[entryID] = err
129         }()
130
131         switch e := e.(type) {
132         case *bc.TxHeader:
133
134                 for i, resID := range e.ResultIds {
135                         resultEntry := vs.tx.Entries[*resID]
136                         vs2 := *vs
137                         vs2.entryID = *resID
138                         err = checkValid(&vs2, resultEntry)
139                         if err != nil {
140                                 return errors.Wrapf(err, "checking result %d", i)
141                         }
142                 }
143
144                 if e.Version == 1 {
145                         if len(e.ResultIds) == 0 {
146                                 return errEmptyResults
147                         }
148
149                         if e.ExtHash != nil && !e.ExtHash.IsZero() {
150                                 return errNonemptyExtHash
151                         }
152                 }
153
154         case *bc.Coinbase:
155                 if vs.block == nil || len(vs.block.Transactions) == 0 || vs.block.Transactions[0] != vs.tx {
156                         return errWrongCoinbaseTransaction
157                 }
158
159                 if *e.WitnessDestination.Value.AssetId != *consensus.BTMAssetID {
160                         return errWrongCoinbaseAsset
161                 }
162
163                 vs2 := *vs
164                 vs2.destPos = 0
165                 err = checkValidDest(&vs2, e.WitnessDestination)
166                 if err != nil {
167                         return errors.Wrap(err, "checking coinbase destination")
168                 }
169
170         case *bc.Mux:
171                 parity := make(map[bc.AssetID]int64)
172                 for i, src := range e.Sources {
173                         sum, ok := checked.AddInt64(parity[*src.Value.AssetId], int64(src.Value.Amount))
174                         if !ok {
175                                 return errors.WithDetailf(errOverflow, "adding %d units of asset %x from mux source %d to total %d overflows int64", src.Value.Amount, src.Value.AssetId.Bytes(), i, parity[*src.Value.AssetId])
176                         }
177                         parity[*src.Value.AssetId] = sum
178                 }
179
180                 for i, dest := range e.WitnessDestinations {
181                         sum, ok := parity[*dest.Value.AssetId]
182                         if !ok {
183                                 return errors.WithDetailf(errNoSource, "mux destination %d, asset %x, has no corresponding source", i, dest.Value.AssetId.Bytes())
184                         }
185
186                         diff, ok := checked.SubInt64(sum, int64(dest.Value.Amount))
187                         if !ok {
188                                 return errors.WithDetailf(errOverflow, "subtracting %d units of asset %x from mux destination %d from total %d underflows int64", dest.Value.Amount, dest.Value.AssetId.Bytes(), i, sum)
189                         }
190                         parity[*dest.Value.AssetId] = diff
191                 }
192
193                 if amount, ok := parity[*consensus.BTMAssetID]; ok {
194                         if err = vs.gas.setGas(amount); err != nil {
195                                 return err
196                         }
197                 } else {
198                         vs.gas.setGas(0)
199                 }
200
201                 for assetID, amount := range parity {
202                         if amount != 0 && assetID != *consensus.BTMAssetID {
203                                 return errors.WithDetailf(errUnbalanced, "asset %x sources - destinations = %d (should be 0)", assetID.Bytes(), amount)
204                         }
205                 }
206
207                 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.Program, e.WitnessArguments), vs.gas.gasLeft)
208                 if err != nil {
209                         return errors.Wrap(err, "checking mux program")
210                 }
211                 if err = vs.gas.updateUsage(gasLeft); err != nil {
212                         return err
213                 }
214
215                 for _, BTMInputID := range vs.tx.GasInputIDs {
216                         e, ok := vs.tx.Entries[BTMInputID]
217                         if !ok {
218                                 return errors.Wrapf(bc.ErrMissingEntry, "entry for bytom input %x not found", BTMInputID)
219                         }
220
221                         vs2 := *vs
222                         vs2.entryID = BTMInputID
223                         if err := checkValid(&vs2, e); err != nil {
224                                 return errors.Wrap(err, "checking value source")
225                         }
226                 }
227
228                 for i, dest := range e.WitnessDestinations {
229                         vs2 := *vs
230                         vs2.destPos = uint64(i)
231                         err = checkValidDest(&vs2, dest)
232                         if err != nil {
233                                 return errors.Wrapf(err, "checking mux destination %d", i)
234                         }
235                 }
236
237                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
238                         return errNonemptyExtHash
239                 }
240                 *vs.gasVaild = 1
241
242                 for i, src := range e.Sources {
243                         vs2 := *vs
244                         vs2.sourcePos = uint64(i)
245                         err = checkValidSrc(&vs2, src)
246                         if err != nil {
247                                 return errors.Wrapf(err, "checking mux source %d", i)
248                         }
249                 }
250
251         case *bc.Nonce:
252                 //TODO: add block heigh range check on the control program
253                 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.Program, e.WitnessArguments), vs.gas.gasLeft)
254                 if err != nil {
255                         return errors.Wrap(err, "checking nonce program")
256                 }
257                 if err = vs.gas.updateUsage(gasLeft); err != nil {
258                         return err
259                 }
260
261                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
262                         return errNonemptyExtHash
263                 }
264
265         case *bc.Output:
266                 vs2 := *vs
267                 vs2.sourcePos = 0
268                 err = checkValidSrc(&vs2, e.Source)
269                 if err != nil {
270                         return errors.Wrap(err, "checking output source")
271                 }
272
273                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
274                         return errNonemptyExtHash
275                 }
276
277         case *bc.Retirement:
278                 vs2 := *vs
279                 vs2.sourcePos = 0
280                 err = checkValidSrc(&vs2, e.Source)
281                 if err != nil {
282                         return errors.Wrap(err, "checking retirement source")
283                 }
284
285                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
286                         return errNonemptyExtHash
287                 }
288
289         case *bc.Issuance:
290                 computedAssetID := e.WitnessAssetDefinition.ComputeAssetID()
291                 if computedAssetID != *e.Value.AssetId {
292                         return errors.WithDetailf(errMismatchedAssetID, "asset ID is %x, issuance wants %x", computedAssetID.Bytes(), e.Value.AssetId.Bytes())
293                 }
294
295                 anchor, ok := vs.tx.Entries[*e.AnchorId]
296                 if !ok {
297                         return errors.Wrapf(bc.ErrMissingEntry, "entry for issuance anchor %x not found", e.AnchorId.Bytes())
298                 }
299
300                 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), vs.gas.gasLeft)
301                 if err != nil {
302                         return errors.Wrap(err, "checking issuance program")
303                 }
304                 if err = vs.gas.updateUsage(gasLeft); err != nil {
305                         return err
306                 }
307
308                 var anchored *bc.Hash
309                 switch a := anchor.(type) {
310                 case *bc.Nonce:
311                         anchored = a.WitnessAnchoredId
312
313                 case *bc.Spend:
314                         anchored = a.WitnessAnchoredId
315
316                 case *bc.Issuance:
317                         anchored = a.WitnessAnchoredId
318
319                 default:
320                         return errors.WithDetailf(bc.ErrEntryType, "issuance anchor has type %T, should be nonce, spend, or issuance", anchor)
321                 }
322
323                 if *anchored != vs.entryID {
324                         return errors.WithDetailf(errMismatchedReference, "issuance %x anchor is for %x", vs.entryID.Bytes(), anchored.Bytes())
325                 }
326
327                 anchorVS := *vs
328                 anchorVS.entryID = *e.AnchorId
329                 err = checkValid(&anchorVS, anchor)
330                 if err != nil {
331                         return errors.Wrap(err, "checking issuance anchor")
332                 }
333
334                 destVS := *vs
335                 destVS.destPos = 0
336                 err = checkValidDest(&destVS, e.WitnessDestination)
337                 if err != nil {
338                         return errors.Wrap(err, "checking issuance destination")
339                 }
340
341                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
342                         return errNonemptyExtHash
343                 }
344
345         case *bc.Spend:
346                 if e.SpentOutputId == nil {
347                         return errors.Wrap(errMissingField, "spend without spent output ID")
348                 }
349                 spentOutput, err := vs.tx.Output(*e.SpentOutputId)
350                 if err != nil {
351                         return errors.Wrap(err, "getting spend prevout")
352                 }
353                 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gas.gasLeft)
354                 if err != nil {
355                         return errors.Wrap(err, "checking control program")
356                 }
357                 if err = vs.gas.updateUsage(gasLeft); err != nil {
358                         return err
359                 }
360
361                 eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value)
362                 if err != nil {
363                         return err
364                 }
365                 if !eq {
366                         return errors.WithDetailf(
367                                 errMismatchedValue,
368                                 "previous output is for %d unit(s) of %x, spend wants %d unit(s) of %x",
369                                 spentOutput.Source.Value.Amount,
370                                 spentOutput.Source.Value.AssetId.Bytes(),
371                                 e.WitnessDestination.Value.Amount,
372                                 e.WitnessDestination.Value.AssetId.Bytes(),
373                         )
374                 }
375
376                 vs2 := *vs
377                 vs2.destPos = 0
378                 err = checkValidDest(&vs2, e.WitnessDestination)
379                 if err != nil {
380                         return errors.Wrap(err, "checking spend destination")
381                 }
382
383                 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
384                         return errNonemptyExtHash
385                 }
386
387         default:
388                 return fmt.Errorf("entry has unexpected type %T", e)
389         }
390
391         return nil
392 }
393
394 func checkValidSrc(vstate *validationState, vs *bc.ValueSource) error {
395         if vs == nil {
396                 return errors.Wrap(errMissingField, "empty value source")
397         }
398         if vs.Ref == nil {
399                 return errors.Wrap(errMissingField, "missing ref on value source")
400         }
401         if vs.Value == nil || vs.Value.AssetId == nil {
402                 return errors.Wrap(errMissingField, "missing value on value source")
403         }
404
405         e, ok := vstate.tx.Entries[*vs.Ref]
406         if !ok {
407                 return errors.Wrapf(bc.ErrMissingEntry, "entry for value source %x not found", vs.Ref.Bytes())
408         }
409         vstate2 := *vstate
410         vstate2.entryID = *vs.Ref
411         err := checkValid(&vstate2, e)
412         if err != nil {
413                 return errors.Wrap(err, "checking value source")
414         }
415
416         var dest *bc.ValueDestination
417         switch ref := e.(type) {
418         case *bc.Coinbase:
419                 if vs.Position != 0 {
420                         return errors.Wrapf(errPosition, "invalid position %d for coinbase source", vs.Position)
421                 }
422                 dest = ref.WitnessDestination
423         case *bc.Issuance:
424                 if vs.Position != 0 {
425                         return errors.Wrapf(errPosition, "invalid position %d for issuance source", vs.Position)
426                 }
427                 dest = ref.WitnessDestination
428
429         case *bc.Spend:
430                 if vs.Position != 0 {
431                         return errors.Wrapf(errPosition, "invalid position %d for spend source", vs.Position)
432                 }
433                 dest = ref.WitnessDestination
434
435         case *bc.Mux:
436                 if vs.Position >= uint64(len(ref.WitnessDestinations)) {
437                         return errors.Wrapf(errPosition, "invalid position %d for %d-destination mux source", vs.Position, len(ref.WitnessDestinations))
438                 }
439                 dest = ref.WitnessDestinations[vs.Position]
440
441         default:
442                 return errors.Wrapf(bc.ErrEntryType, "value source is %T, should be coinbase, issuance, spend, or mux", e)
443         }
444
445         if dest.Ref == nil || *dest.Ref != vstate.entryID {
446                 return errors.Wrapf(errMismatchedReference, "value source for %x has disagreeing destination %x", vstate.entryID.Bytes(), dest.Ref.Bytes())
447         }
448
449         if dest.Position != vstate.sourcePos {
450                 return errors.Wrapf(errMismatchedPosition, "value source position %d disagrees with %d", dest.Position, vstate.sourcePos)
451         }
452
453         eq, err := dest.Value.Equal(vs.Value)
454         if err != nil {
455                 return errors.Sub(errMissingField, err)
456         }
457         if !eq {
458                 return errors.Wrapf(errMismatchedValue, "source value %v disagrees with %v", dest.Value, vs.Value)
459         }
460
461         return nil
462 }
463
464 func checkValidDest(vs *validationState, vd *bc.ValueDestination) error {
465         if vd == nil {
466                 return errors.Wrap(errMissingField, "empty value destination")
467         }
468         if vd.Ref == nil {
469                 return errors.Wrap(errMissingField, "missing ref on value destination")
470         }
471         if vd.Value == nil || vd.Value.AssetId == nil {
472                 return errors.Wrap(errMissingField, "missing value on value source")
473         }
474
475         e, ok := vs.tx.Entries[*vd.Ref]
476         if !ok {
477                 return errors.Wrapf(bc.ErrMissingEntry, "entry for value destination %x not found", vd.Ref.Bytes())
478         }
479         var src *bc.ValueSource
480         switch ref := e.(type) {
481         case *bc.Output:
482                 if vd.Position != 0 {
483                         return errors.Wrapf(errPosition, "invalid position %d for output destination", vd.Position)
484                 }
485                 src = ref.Source
486
487         case *bc.Retirement:
488                 if vd.Position != 0 {
489                         return errors.Wrapf(errPosition, "invalid position %d for retirement destination", vd.Position)
490                 }
491                 src = ref.Source
492
493         case *bc.Mux:
494                 if vd.Position >= uint64(len(ref.Sources)) {
495                         return errors.Wrapf(errPosition, "invalid position %d for %d-source mux destination", vd.Position, len(ref.Sources))
496                 }
497                 src = ref.Sources[vd.Position]
498
499         default:
500                 return errors.Wrapf(bc.ErrEntryType, "value destination is %T, should be output, retirement, or mux", e)
501         }
502
503         if src.Ref == nil || *src.Ref != vs.entryID {
504                 return errors.Wrapf(errMismatchedReference, "value destination for %x has disagreeing source %x", vs.entryID.Bytes(), src.Ref.Bytes())
505         }
506
507         if src.Position != vs.destPos {
508                 return errors.Wrapf(errMismatchedPosition, "value destination position %d disagrees with %d", src.Position, vs.destPos)
509         }
510
511         eq, err := src.Value.Equal(vd.Value)
512         if err != nil {
513                 return errors.Sub(errMissingField, err)
514         }
515         if !eq {
516                 return errors.Wrapf(errMismatchedValue, "destination value %v disagrees with %v", src.Value, vd.Value)
517         }
518
519         return nil
520 }
521
522 // ValidateBlock validates a block and the transactions within.
523 // It does not run the consensus program; for that, see ValidateBlockSig.
524 func ValidateBlock(b, prev *bc.Block, seedCaches *seed.SeedCaches) error {
525         if b.Height > 0 {
526                 if prev == nil {
527                         return errors.WithDetailf(errNoPrevBlock, "height %d", b.Height)
528                 }
529                 err := validateBlockAgainstPrev(b, prev)
530                 if err != nil {
531                         return err
532                 }
533         }
534         if b.Timestamp > uint64(time.Now().Unix())+consensus.MaxTimeOffsetSeconds {
535                 return errBadTimestamp
536         }
537
538         if b.BlockHeader.SerializedSize > consensus.MaxBlockSzie {
539                 return errWrongBlockSize
540         }
541
542         seedCache, err := seedCaches.Get(b.Seed)
543         if err != nil {
544                 return err
545         }
546         proofHash, err := algorithm.AIHash(b.Height, &b.ID, seedCache)
547         if err != nil {
548                 return err
549         }
550         if !difficulty.CheckProofOfWork(proofHash, b.BlockHeader.Bits) {
551                 return errWorkProof
552         }
553
554         coinbaseValue := consensus.BlockSubsidy(b.BlockHeader.Height)
555         for i, tx := range b.Transactions {
556                 if b.Version == 1 && tx.Version != 1 {
557                         return errors.WithDetailf(errTxVersion, "block version %d, transaction version %d", b.Version, tx.Version)
558                 }
559                 if tx.TimeRange > b.Timestamp {
560                         return errors.New("invalid transaction time range")
561                 }
562                 txBTMValue, gasVaild, err := ValidateTx(tx, b)
563                 gasOnlyTx := false
564                 if err != nil {
565                         if !gasVaild {
566                                 return errors.Wrapf(err, "validity of transaction %d of %d", i, len(b.Transactions))
567                         }
568                         gasOnlyTx = true
569                 }
570                 if status, err := b.TransactionStatus.GetStatus(i); err != nil || status != gasOnlyTx {
571                         return errWrongTransactionStatus
572                 }
573                 coinbaseValue += txBTMValue
574         }
575
576         // check the coinbase output entry value
577         if err := validateCoinbase(b.Transactions[0], coinbaseValue); err != nil {
578                 return err
579         }
580
581         txRoot, err := bc.MerkleRoot(b.Transactions)
582         if err != nil {
583                 return errors.Wrap(err, "computing transaction merkle root")
584         }
585
586         if txRoot != *b.TransactionsRoot {
587                 return errors.WithDetailf(errMismatchedMerkleRoot, "computed %x, current block wants %x", txRoot.Bytes(), b.TransactionsRoot.Bytes())
588         }
589
590         return nil
591 }
592
593 func validateCoinbase(tx *bc.Tx, value uint64) error {
594         resultEntry := tx.Entries[*tx.TxHeader.ResultIds[0]]
595         output, ok := resultEntry.(*bc.Output)
596         if !ok {
597                 return errors.Wrap(errWrongCoinbaseTransaction, "decode output")
598         }
599
600         if output.Source.Value.Amount != value {
601                 return errors.Wrap(errWrongCoinbaseTransaction, "dismatch output value")
602         }
603
604         inputEntry := tx.Entries[tx.InputIDs[0]]
605         input, ok := inputEntry.(*bc.Coinbase)
606         if !ok {
607                 return errors.Wrap(errWrongCoinbaseTransaction, "decode input")
608         }
609         if input.Arbitrary != nil && len(input.Arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
610                 return errors.Wrap(errWrongCoinbaseTransaction, "coinbase arbitrary is over size")
611         }
612         return nil
613 }
614
615 func validateBlockAgainstPrev(b, prev *bc.Block) error {
616         if b.Version < prev.Version {
617                 return errors.WithDetailf(errVersionRegression, "previous block verson %d, current block version %d", prev.Version, b.Version)
618         }
619         if b.Height != prev.Height+1 {
620                 return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", prev.Height, b.Height)
621         }
622
623         if prev.ID != *b.PreviousBlockId {
624                 return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", prev.ID.Bytes(), b.PreviousBlockId.Bytes())
625         }
626         if b.Timestamp <= prev.Timestamp {
627                 return errors.WithDetailf(errMisorderedBlockTime, "previous block time %d, current block time %d", prev.Timestamp, b.Timestamp)
628         }
629         if *b.Seed != *algorithm.CreateSeed(b.Height, prev.Seed, []*bc.Hash{&prev.ID}) {
630                 return errors.New("wrong block seed")
631         }
632         return nil
633 }
634
635 func validateStandardTx(tx *bc.Tx) error {
636         for _, id := range tx.InputIDs {
637                 e, ok := tx.Entries[id]
638                 if !ok {
639                         return errors.New("miss tx input entry")
640                 }
641                 if spend, ok := e.(*bc.Spend); ok {
642                         if *spend.WitnessDestination.Value.AssetId != *consensus.BTMAssetID {
643                                 continue
644                         }
645                         spentOutput, err := tx.Output(*spend.SpentOutputId)
646                         if err != nil {
647                                 return errors.Wrap(err, "getting spend prevout")
648                         }
649
650                         if !segwit.IsP2WScript(spentOutput.ControlProgram.Code) {
651                                 return errNotStandardTx
652                         }
653                 }
654         }
655
656         for _, id := range tx.ResultIds {
657                 e, ok := tx.Entries[*id]
658                 if !ok {
659                         return errors.New("miss tx output entry")
660                 }
661                 if output, ok := e.(*bc.Output); ok {
662                         if *output.Source.Value.AssetId != *consensus.BTMAssetID {
663                                 continue
664                         }
665                         if !segwit.IsP2WScript(output.ControlProgram.Code) {
666                                 return errNotStandardTx
667                         }
668                 }
669         }
670         return nil
671 }
672
673 // ValidateTx validates a transaction.
674 func ValidateTx(tx *bc.Tx, block *bc.Block) (uint64, bool, error) {
675         if tx.TxHeader.SerializedSize > consensus.MaxTxSize {
676                 return 0, false, errWrongTransactionSize
677         }
678         if len(tx.ResultIds) == 0 {
679                 return 0, false, errors.New("tx didn't have any output")
680         }
681
682         if err := validateStandardTx(tx); err != nil {
683                 return 0, false, err
684         }
685
686         //TODO: handle the gas limit
687         gasVaild := 0
688         vs := &validationState{
689                 block:   block,
690                 tx:      tx,
691                 entryID: tx.ID,
692                 gas: &gasState{
693                         gasLeft: defaultGasLimit,
694                 },
695                 gasVaild: &gasVaild,
696                 cache:    make(map[bc.Hash]error),
697         }
698
699         err := checkValid(vs, tx.TxHeader)
700         return uint64(vs.gas.BTMValue), *vs.gasVaild == 1, err
701 }