github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/blockchain/txbuilder/finalize.go (about) 1 package txbuilder 2 3 import ( 4 "bytes" 5 "context" 6 7 cfg "github.com/bytom/bytom/config" 8 "github.com/bytom/bytom/consensus" 9 "github.com/bytom/bytom/errors" 10 "github.com/bytom/bytom/protocol" 11 "github.com/bytom/bytom/protocol/bc/types" 12 "github.com/bytom/bytom/protocol/vm" 13 ) 14 15 var ( 16 // ErrRejected means the network rejected a tx (as a double-spend) 17 ErrRejected = errors.New("transaction rejected") 18 // ErrMissingRawTx means missing transaction 19 ErrMissingRawTx = errors.New("missing raw tx") 20 // ErrBadInstructionCount means too many signing instructions compare with inputs 21 ErrBadInstructionCount = errors.New("too many signing instructions in template") 22 // ErrOrphanTx means submit transaction is orphan 23 ErrOrphanTx = errors.New("finalize can't find transaction input utxo") 24 // ErrExtTxFee means transaction fee exceed max limit 25 ErrExtTxFee = errors.New("transaction fee exceed max limit") 26 // ErrNoGasInput means transaction has no gas input 27 ErrNoGasInput = errors.New("transaction has no gas input") 28 ) 29 30 // FinalizeTx validates a transaction signature template, 31 // assembles a fully signed tx, and stores the effects of 32 // its changes on the UTXO set. 33 func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error { 34 if fee := CalculateTxFee(tx); fee > cfg.CommonConfig.Wallet.MaxTxFee { 35 return ErrExtTxFee 36 } 37 38 if err := checkTxSighashCommitment(tx); err != nil { 39 return err 40 } 41 42 if len(tx.GasInputIDs) == 0 { 43 return ErrNoGasInput 44 } 45 46 // This part is use for prevent tx size is 0 47 data, err := tx.TxData.MarshalText() 48 if err != nil { 49 return err 50 } 51 tx.TxData.SerializedSize = uint64(len(data) / 2) 52 tx.Tx.SerializedSize = uint64(len(data) / 2) 53 54 isOrphan, err := c.ValidateTx(tx) 55 if errors.Root(err) == protocol.ErrBadTx { 56 return errors.Sub(ErrRejected, err) 57 } 58 if err != nil { 59 return errors.WithDetail(err, "tx rejected: "+err.Error()) 60 } 61 if isOrphan { 62 return ErrOrphanTx 63 } 64 return nil 65 } 66 67 var ( 68 // ErrNoTxSighashCommitment is returned when no input commits to the 69 // complete transaction. 70 // To permit idempotence of transaction submission, we require at 71 // least one input to commit to the complete transaction (what you get 72 // when you build a transaction with allow_additional_actions=false). 73 ErrNoTxSighashCommitment = errors.New("no commitment to tx sighash") 74 75 // ErrNoTxSighashAttempt is returned when there was no attempt made to sign 76 // this transaction. 77 ErrNoTxSighashAttempt = errors.New("no tx sighash attempted") 78 79 // ErrTxSignatureFailure is returned when there was an attempt to sign this 80 // transaction, but it failed. 81 ErrTxSignatureFailure = errors.New("tx signature was attempted but failed") 82 ) 83 84 func checkTxSighashCommitment(tx *types.Tx) error { 85 // TODO: this is the local sender check rules, we might don't need it due to the rule is difference 86 return nil 87 var lastError error 88 89 for i, inp := range tx.Inputs { 90 var args [][]byte 91 switch t := inp.TypedInput.(type) { 92 case *types.SpendInput: 93 args = t.Arguments 94 case *types.IssuanceInput: 95 args = t.Arguments 96 } 97 // Note: These numbers will need to change if more args are added such that the minimum length changes 98 switch { 99 // A conforming arguments list contains 100 // [... arg1 arg2 ... argN N sig1 sig2 ... sigM prog] 101 // The args are the opaque arguments to prog. In the case where 102 // N is 0 (prog takes no args), and assuming there must be at 103 // least one signature, args has a minimum length of 3. 104 case len(args) == 0: 105 lastError = ErrNoTxSighashAttempt 106 continue 107 case len(args) < 3: 108 lastError = ErrTxSignatureFailure 109 continue 110 } 111 lastError = ErrNoTxSighashCommitment 112 prog := args[len(args)-1] 113 if len(prog) != 35 { 114 continue 115 } 116 if prog[0] != byte(vm.OP_DATA_32) { 117 continue 118 } 119 if !bytes.Equal(prog[33:], []byte{byte(vm.OP_TXSIGHASH), byte(vm.OP_EQUAL)}) { 120 continue 121 } 122 h := tx.SigHash(uint32(i)) 123 if !bytes.Equal(h.Bytes(), prog[1:33]) { 124 continue 125 } 126 // At least one input passes commitment checks 127 return nil 128 } 129 130 return lastError 131 } 132 133 // CalculateTxFee calculate transaction fee 134 func CalculateTxFee(tx *types.Tx) (fee uint64) { 135 totalInputBTM := uint64(0) 136 totalOutputBTM := uint64(0) 137 138 for _, input := range tx.Inputs { 139 if input.InputType() == types.CoinbaseInputType { 140 return 0 141 } 142 if input.AssetID() == *consensus.BTMAssetID { 143 totalInputBTM += input.Amount() 144 } 145 } 146 147 for _, output := range tx.Outputs { 148 if *output.AssetId == *consensus.BTMAssetID { 149 totalOutputBTM += output.Amount 150 } 151 } 152 153 fee = totalInputBTM - totalOutputBTM 154 return 155 }