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  }