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"
19 defaultGasLimit = int64(80000)
20 muxGasCost = int64(10)
21 // GasRate indicates the current gas rate
25 type gasState struct {
31 func (g *gasState) setGas(BTMValue int64) error {
33 return errGasCalculate
37 if gasAmount, ok := checked.DivInt64(BTMValue, GasRate); ok {
39 g.gasLeft = muxGasCost
40 } else if gasAmount < defaultGasLimit {
44 return errGasCalculate
49 func (g *gasState) updateUsage(gasLeft int64) error {
51 return errGasCalculate
53 if gasUsed, ok := checked.SubInt64(g.gasLeft, gasLeft); ok {
57 return errGasCalculate
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
68 // The enclosing transaction object
71 // The ID of the nearest enclosing entry
74 // The source position, for validating ValueSources
77 // The destination position, for validating ValueDestinations
80 // Memoized per-entry validation results
81 cache map[bc.Hash]error
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")
120 func checkValid(vs *validationState, e bc.Entry) (err error) {
121 entryID := bc.EntryID(e)
123 if err, ok = vs.cache[entryID]; ok {
128 vs.cache[entryID] = err
131 switch e := e.(type) {
134 for i, resID := range e.ResultIds {
135 resultEntry := vs.tx.Entries[*resID]
138 err = checkValid(&vs2, resultEntry)
140 return errors.Wrapf(err, "checking result %d", i)
145 if len(e.ResultIds) == 0 {
146 return errEmptyResults
149 if e.ExtHash != nil && !e.ExtHash.IsZero() {
150 return errNonemptyExtHash
155 if vs.block == nil || len(vs.block.Transactions) == 0 || vs.block.Transactions[0] != vs.tx {
156 return errWrongCoinbaseTransaction
159 if *e.WitnessDestination.Value.AssetId != *consensus.BTMAssetID {
160 return errWrongCoinbaseAsset
165 err = checkValidDest(&vs2, e.WitnessDestination)
167 return errors.Wrap(err, "checking coinbase destination")
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))
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])
177 parity[*src.Value.AssetId] = sum
180 for i, dest := range e.WitnessDestinations {
181 sum, ok := parity[*dest.Value.AssetId]
183 return errors.WithDetailf(errNoSource, "mux destination %d, asset %x, has no corresponding source", i, dest.Value.AssetId.Bytes())
186 diff, ok := checked.SubInt64(sum, int64(dest.Value.Amount))
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)
190 parity[*dest.Value.AssetId] = diff
193 if amount, ok := parity[*consensus.BTMAssetID]; ok {
194 if err = vs.gas.setGas(amount); err != nil {
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)
207 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.Program, e.WitnessArguments), vs.gas.gasLeft)
209 return errors.Wrap(err, "checking mux program")
211 if err = vs.gas.updateUsage(gasLeft); err != nil {
215 for _, BTMInputID := range vs.tx.GasInputIDs {
216 e, ok := vs.tx.Entries[BTMInputID]
218 return errors.Wrapf(bc.ErrMissingEntry, "entry for bytom input %x not found", BTMInputID)
222 vs2.entryID = BTMInputID
223 if err := checkValid(&vs2, e); err != nil {
224 return errors.Wrap(err, "checking value source")
228 for i, dest := range e.WitnessDestinations {
230 vs2.destPos = uint64(i)
231 err = checkValidDest(&vs2, dest)
233 return errors.Wrapf(err, "checking mux destination %d", i)
237 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
238 return errNonemptyExtHash
242 for i, src := range e.Sources {
244 vs2.sourcePos = uint64(i)
245 err = checkValidSrc(&vs2, src)
247 return errors.Wrapf(err, "checking mux source %d", i)
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)
255 return errors.Wrap(err, "checking nonce program")
257 if err = vs.gas.updateUsage(gasLeft); err != nil {
261 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
262 return errNonemptyExtHash
268 err = checkValidSrc(&vs2, e.Source)
270 return errors.Wrap(err, "checking output source")
273 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
274 return errNonemptyExtHash
280 err = checkValidSrc(&vs2, e.Source)
282 return errors.Wrap(err, "checking retirement source")
285 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
286 return errNonemptyExtHash
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())
295 anchor, ok := vs.tx.Entries[*e.AnchorId]
297 return errors.Wrapf(bc.ErrMissingEntry, "entry for issuance anchor %x not found", e.AnchorId.Bytes())
300 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), vs.gas.gasLeft)
302 return errors.Wrap(err, "checking issuance program")
304 if err = vs.gas.updateUsage(gasLeft); err != nil {
308 var anchored *bc.Hash
309 switch a := anchor.(type) {
311 anchored = a.WitnessAnchoredId
314 anchored = a.WitnessAnchoredId
317 anchored = a.WitnessAnchoredId
320 return errors.WithDetailf(bc.ErrEntryType, "issuance anchor has type %T, should be nonce, spend, or issuance", anchor)
323 if *anchored != vs.entryID {
324 return errors.WithDetailf(errMismatchedReference, "issuance %x anchor is for %x", vs.entryID.Bytes(), anchored.Bytes())
328 anchorVS.entryID = *e.AnchorId
329 err = checkValid(&anchorVS, anchor)
331 return errors.Wrap(err, "checking issuance anchor")
336 err = checkValidDest(&destVS, e.WitnessDestination)
338 return errors.Wrap(err, "checking issuance destination")
341 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
342 return errNonemptyExtHash
346 if e.SpentOutputId == nil {
347 return errors.Wrap(errMissingField, "spend without spent output ID")
349 spentOutput, err := vs.tx.Output(*e.SpentOutputId)
351 return errors.Wrap(err, "getting spend prevout")
353 gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gas.gasLeft)
355 return errors.Wrap(err, "checking control program")
357 if err = vs.gas.updateUsage(gasLeft); err != nil {
361 eq, err := spentOutput.Source.Value.Equal(e.WitnessDestination.Value)
366 return errors.WithDetailf(
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(),
378 err = checkValidDest(&vs2, e.WitnessDestination)
380 return errors.Wrap(err, "checking spend destination")
383 if vs.tx.Version == 1 && e.ExtHash != nil && !e.ExtHash.IsZero() {
384 return errNonemptyExtHash
388 return fmt.Errorf("entry has unexpected type %T", e)
394 func checkValidSrc(vstate *validationState, vs *bc.ValueSource) error {
396 return errors.Wrap(errMissingField, "empty value source")
399 return errors.Wrap(errMissingField, "missing ref on value source")
401 if vs.Value == nil || vs.Value.AssetId == nil {
402 return errors.Wrap(errMissingField, "missing value on value source")
405 e, ok := vstate.tx.Entries[*vs.Ref]
407 return errors.Wrapf(bc.ErrMissingEntry, "entry for value source %x not found", vs.Ref.Bytes())
410 vstate2.entryID = *vs.Ref
411 err := checkValid(&vstate2, e)
413 return errors.Wrap(err, "checking value source")
416 var dest *bc.ValueDestination
417 switch ref := e.(type) {
419 if vs.Position != 0 {
420 return errors.Wrapf(errPosition, "invalid position %d for coinbase source", vs.Position)
422 dest = ref.WitnessDestination
424 if vs.Position != 0 {
425 return errors.Wrapf(errPosition, "invalid position %d for issuance source", vs.Position)
427 dest = ref.WitnessDestination
430 if vs.Position != 0 {
431 return errors.Wrapf(errPosition, "invalid position %d for spend source", vs.Position)
433 dest = ref.WitnessDestination
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))
439 dest = ref.WitnessDestinations[vs.Position]
442 return errors.Wrapf(bc.ErrEntryType, "value source is %T, should be coinbase, issuance, spend, or mux", e)
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())
449 if dest.Position != vstate.sourcePos {
450 return errors.Wrapf(errMismatchedPosition, "value source position %d disagrees with %d", dest.Position, vstate.sourcePos)
453 eq, err := dest.Value.Equal(vs.Value)
455 return errors.Sub(errMissingField, err)
458 return errors.Wrapf(errMismatchedValue, "source value %v disagrees with %v", dest.Value, vs.Value)
464 func checkValidDest(vs *validationState, vd *bc.ValueDestination) error {
466 return errors.Wrap(errMissingField, "empty value destination")
469 return errors.Wrap(errMissingField, "missing ref on value destination")
471 if vd.Value == nil || vd.Value.AssetId == nil {
472 return errors.Wrap(errMissingField, "missing value on value source")
475 e, ok := vs.tx.Entries[*vd.Ref]
477 return errors.Wrapf(bc.ErrMissingEntry, "entry for value destination %x not found", vd.Ref.Bytes())
479 var src *bc.ValueSource
480 switch ref := e.(type) {
482 if vd.Position != 0 {
483 return errors.Wrapf(errPosition, "invalid position %d for output destination", vd.Position)
488 if vd.Position != 0 {
489 return errors.Wrapf(errPosition, "invalid position %d for retirement destination", vd.Position)
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))
497 src = ref.Sources[vd.Position]
500 return errors.Wrapf(bc.ErrEntryType, "value destination is %T, should be output, retirement, or mux", e)
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())
507 if src.Position != vs.destPos {
508 return errors.Wrapf(errMismatchedPosition, "value destination position %d disagrees with %d", src.Position, vs.destPos)
511 eq, err := src.Value.Equal(vd.Value)
513 return errors.Sub(errMissingField, err)
516 return errors.Wrapf(errMismatchedValue, "destination value %v disagrees with %v", src.Value, vd.Value)
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 {
527 return errors.WithDetailf(errNoPrevBlock, "height %d", b.Height)
529 err := validateBlockAgainstPrev(b, prev)
534 if b.Timestamp > uint64(time.Now().Unix())+consensus.MaxTimeOffsetSeconds {
535 return errBadTimestamp
538 if b.BlockHeader.SerializedSize > consensus.MaxBlockSzie {
539 return errWrongBlockSize
542 seedCache, err := seedCaches.Get(b.Seed)
546 proofHash, err := algorithm.AIHash(b.Height, &b.ID, seedCache)
550 if !difficulty.CheckProofOfWork(proofHash, b.BlockHeader.Bits) {
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)
559 if tx.TimeRange > b.Timestamp {
560 return errors.New("invalid transaction time range")
562 txBTMValue, gasVaild, err := ValidateTx(tx, b)
566 return errors.Wrapf(err, "validity of transaction %d of %d", i, len(b.Transactions))
570 if status, err := b.TransactionStatus.GetStatus(i); err != nil || status != gasOnlyTx {
571 return errWrongTransactionStatus
573 coinbaseValue += txBTMValue
576 // check the coinbase output entry value
577 if err := validateCoinbase(b.Transactions[0], coinbaseValue); err != nil {
581 txRoot, err := bc.MerkleRoot(b.Transactions)
583 return errors.Wrap(err, "computing transaction merkle root")
586 if txRoot != *b.TransactionsRoot {
587 return errors.WithDetailf(errMismatchedMerkleRoot, "computed %x, current block wants %x", txRoot.Bytes(), b.TransactionsRoot.Bytes())
593 func validateCoinbase(tx *bc.Tx, value uint64) error {
594 resultEntry := tx.Entries[*tx.TxHeader.ResultIds[0]]
595 output, ok := resultEntry.(*bc.Output)
597 return errors.Wrap(errWrongCoinbaseTransaction, "decode output")
600 if output.Source.Value.Amount != value {
601 return errors.Wrap(errWrongCoinbaseTransaction, "dismatch output value")
604 inputEntry := tx.Entries[tx.InputIDs[0]]
605 input, ok := inputEntry.(*bc.Coinbase)
607 return errors.Wrap(errWrongCoinbaseTransaction, "decode input")
609 if input.Arbitrary != nil && len(input.Arbitrary) > consensus.CoinbaseArbitrarySizeLimit {
610 return errors.Wrap(errWrongCoinbaseTransaction, "coinbase arbitrary is over size")
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)
619 if b.Height != prev.Height+1 {
620 return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", prev.Height, b.Height)
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())
626 if b.Timestamp <= prev.Timestamp {
627 return errors.WithDetailf(errMisorderedBlockTime, "previous block time %d, current block time %d", prev.Timestamp, b.Timestamp)
629 if *b.Seed != *algorithm.CreateSeed(b.Height, prev.Seed, []*bc.Hash{&prev.ID}) {
630 return errors.New("wrong block seed")
635 func validateStandardTx(tx *bc.Tx) error {
636 for _, id := range tx.InputIDs {
637 e, ok := tx.Entries[id]
639 return errors.New("miss tx input entry")
641 if spend, ok := e.(*bc.Spend); ok {
642 if *spend.WitnessDestination.Value.AssetId != *consensus.BTMAssetID {
645 spentOutput, err := tx.Output(*spend.SpentOutputId)
647 return errors.Wrap(err, "getting spend prevout")
650 if !segwit.IsP2WScript(spentOutput.ControlProgram.Code) {
651 return errNotStandardTx
656 for _, id := range tx.ResultIds {
657 e, ok := tx.Entries[*id]
659 return errors.New("miss tx output entry")
661 if output, ok := e.(*bc.Output); ok {
662 if *output.Source.Value.AssetId != *consensus.BTMAssetID {
665 if !segwit.IsP2WScript(output.ControlProgram.Code) {
666 return errNotStandardTx
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
678 if len(tx.ResultIds) == 0 {
679 return 0, false, errors.New("tx didn't have any output")
682 if err := validateStandardTx(tx); err != nil {
686 //TODO: handle the gas limit
688 vs := &validationState{
693 gasLeft: defaultGasLimit,
696 cache: make(map[bc.Hash]error),
699 err := checkValid(vs, tx.TxHeader)
700 return uint64(vs.gas.BTMValue), *vs.gasVaild == 1, err