7 "github.com/bytom/vapor/common/arithmetic"
8 cfg "github.com/bytom/vapor/config"
9 "github.com/bytom/vapor/errors"
10 "github.com/bytom/vapor/math/checked"
11 "github.com/bytom/vapor/protocol"
12 "github.com/bytom/vapor/protocol/bc/types"
13 "github.com/bytom/vapor/protocol/vm"
17 // ErrRejected means the network rejected a tx (as a double-spend)
18 ErrRejected = errors.New("transaction rejected")
19 // ErrMissingRawTx means missing transaction
20 ErrMissingRawTx = errors.New("missing raw tx")
21 // ErrBadInstructionCount means too many signing instructions compare with inputs
22 ErrBadInstructionCount = errors.New("too many signing instructions in template")
23 // ErrOrphanTx means submit transaction is orphan
24 ErrOrphanTx = errors.New("finalize can't find transaction input utxo")
25 // ErrExtTxFee means transaction fee exceed max limit
26 ErrExtTxFee = errors.New("transaction fee exceed max limit")
29 // FinalizeTx validates a transaction signature template,
30 // assembles a fully signed tx, and stores the effects of
31 // its changes on the UTXO set.
32 func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error {
33 if fee, err := arithmetic.CalculateTxFee(tx); err != nil {
34 return checked.ErrOverflow
35 } else if fee > cfg.CommonConfig.Wallet.MaxTxFee {
39 if err := checkTxSighashCommitment(tx); err != nil {
43 // This part is use for prevent tx size is 0
44 data, err := tx.TxData.MarshalText()
48 tx.TxData.SerializedSize = uint64(len(data) / 2)
49 tx.Tx.SerializedSize = uint64(len(data) / 2)
51 isOrphan, err := c.ValidateTx(tx)
53 if errors.Root(err) == err {
54 return errors.Sub(ErrRejected, err)
66 // ErrNoTxSighashCommitment is returned when no input commits to the
67 // complete transaction.
68 // To permit idempotence of transaction submission, we require at
69 // least one input to commit to the complete transaction (what you get
70 // when you build a transaction with allow_additional_actions=false).
71 ErrNoTxSighashCommitment = errors.New("no commitment to tx sighash")
73 // ErrNoTxSighashAttempt is returned when there was no attempt made to sign
75 ErrNoTxSighashAttempt = errors.New("no tx sighash attempted")
77 // ErrTxSignatureFailure is returned when there was an attempt to sign this
78 // transaction, but it failed.
79 ErrTxSignatureFailure = errors.New("tx signature was attempted but failed")
82 func checkTxSighashCommitment(tx *types.Tx) error {
83 // TODO: this is the local sender check rules, we might don't need it due to the rule is difference
87 for i, inp := range tx.Inputs {
89 switch t := inp.TypedInput.(type) {
90 case *types.SpendInput:
93 // Note: These numbers will need to change if more args are added such that the minimum length changes
95 // A conforming arguments list contains
96 // [... arg1 arg2 ... argN N sig1 sig2 ... sigM prog]
97 // The args are the opaque arguments to prog. In the case where
98 // N is 0 (prog takes no args), and assuming there must be at
99 // least one signature, args has a minimum length of 3.
101 lastError = ErrNoTxSighashAttempt
104 lastError = ErrTxSignatureFailure
107 lastError = ErrNoTxSighashCommitment
108 prog := args[len(args)-1]
112 if prog[0] != byte(vm.OP_DATA_32) {
115 if !bytes.Equal(prog[33:], []byte{byte(vm.OP_TXSIGHASH), byte(vm.OP_EQUAL)}) {
118 h := tx.SigHash(uint32(i))
119 if !bytes.Equal(h.Bytes(), prog[1:33]) {
122 // At least one input passes commitment checks