OSDN Git Service

Hulk did something
[bytom/vapor.git] / blockchain / txbuilder / finalize.go
1 package txbuilder
2
3 import (
4         "bytes"
5         "context"
6
7         cfg "github.com/vapor/config"
8         "github.com/vapor/consensus"
9         "github.com/vapor/errors"
10         "github.com/vapor/protocol"
11         "github.com/vapor/protocol/bc/types"
12         "github.com/vapor/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 }