github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/txbuilder/txbuilder.go (about)

     1  // Package txbuilder builds a Chain Protocol transaction from
     2  // a list of actions.
     3  package txbuilder
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"time"
     9  
    10  	log "github.com/sirupsen/logrus"
    11  
    12  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
    13  	"github.com/bytom/bytom/errors"
    14  	"github.com/bytom/bytom/math/checked"
    15  	"github.com/bytom/bytom/protocol/bc"
    16  	"github.com/bytom/bytom/protocol/bc/types"
    17  	"github.com/bytom/bytom/protocol/vm"
    18  )
    19  
    20  const logModule = "txbuilder"
    21  
    22  // errors
    23  var (
    24  	//ErrBadRefData means invalid reference data
    25  	ErrBadRefData = errors.New("transaction reference data does not match previous template's reference data")
    26  	//ErrBadTxInputIdx means unsigned tx input
    27  	ErrBadTxInputIdx = errors.New("unsigned tx missing input")
    28  	//ErrBadWitnessComponent means invalid witness component
    29  	ErrBadWitnessComponent = errors.New("invalid witness component")
    30  	//ErrBadAmount means invalid asset amount
    31  	ErrBadAmount = errors.New("bad asset amount")
    32  	//ErrBlankCheck means unsafe transaction
    33  	ErrBlankCheck = errors.New("unsafe transaction: leaves assets free to control")
    34  	//ErrAction means errors occurred in actions
    35  	ErrAction = errors.New("errors occurred in one or more actions")
    36  	//ErrMissingFields means missing required fields
    37  	ErrMissingFields = errors.New("required field is missing")
    38  	//ErrBadContractArgType means invalid contract argument type
    39  	ErrBadContractArgType = errors.New("invalid contract argument type")
    40  )
    41  
    42  // Build builds or adds on to a transaction.
    43  // Initially, inputs are left unconsumed, and destinations unsatisfied.
    44  // Build partners then satisfy and consume inputs and destinations.
    45  // The final party must ensure that the transaction is
    46  // balanced before calling finalize.
    47  func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time, timeRange uint64) (*Template, error) {
    48  	builder := TemplateBuilder{
    49  		base:      tx,
    50  		maxTime:   maxTime,
    51  		timeRange: timeRange,
    52  	}
    53  
    54  	// Build all of the actions, updating the builder.
    55  	var errs []error
    56  	for i, action := range actions {
    57  		err := action.Build(ctx, &builder)
    58  		if err != nil {
    59  			log.WithFields(log.Fields{"module": logModule, "action index": i, "error": err}).Error("Loop tx's action")
    60  			errs = append(errs, errors.WithDetailf(err, "action index %v", i))
    61  		}
    62  	}
    63  
    64  	// If there were any errors, rollback and return a composite error.
    65  	if len(errs) > 0 {
    66  		builder.Rollback()
    67  		return nil, errors.WithData(ErrAction, "actions", errs)
    68  	}
    69  
    70  	// Build the transaction template.
    71  	tpl, tx, err := builder.Build()
    72  	if err != nil {
    73  		builder.Rollback()
    74  		return nil, err
    75  	}
    76  
    77  	/*TODO: This part is use for check the balance, but now we are using btm as gas fee
    78  	the rule need to be rewrite when we have time
    79  	err = checkBlankCheck(tx)
    80  	if err != nil {
    81  		builder.rollback()
    82  		return nil, err
    83  	}*/
    84  
    85  	return tpl, nil
    86  }
    87  
    88  // Sign will try to sign all the witness
    89  func Sign(ctx context.Context, tpl *Template, auth string, signFn SignFunc) error {
    90  	for i, sigInst := range tpl.SigningInstructions {
    91  		for j, wc := range sigInst.WitnessComponents {
    92  			switch sw := wc.(type) {
    93  			case *SignatureWitness:
    94  				err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
    95  				if err != nil {
    96  					return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
    97  				}
    98  			case *RawTxSigWitness:
    99  				err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
   100  				if err != nil {
   101  					return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
   102  				}
   103  			}
   104  		}
   105  	}
   106  	return materializeWitnesses(tpl)
   107  }
   108  
   109  func checkBlankCheck(tx *types.TxData) error {
   110  	assetMap := make(map[bc.AssetID]int64)
   111  	var ok bool
   112  	for _, in := range tx.Inputs {
   113  		asset := in.AssetID() // AssetID() is calculated for IssuanceInputs, so grab once
   114  		assetMap[asset], ok = checked.AddInt64(assetMap[asset], int64(in.Amount()))
   115  		if !ok {
   116  			return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", asset)
   117  		}
   118  	}
   119  	for _, out := range tx.Outputs {
   120  		assetMap[*out.AssetId], ok = checked.SubInt64(assetMap[*out.AssetId], int64(out.Amount))
   121  		if !ok {
   122  			return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", out.AssetId.Bytes())
   123  		}
   124  	}
   125  
   126  	var requiresOutputs, requiresInputs bool
   127  	for _, amt := range assetMap {
   128  		if amt > 0 {
   129  			requiresOutputs = true
   130  		}
   131  		if amt < 0 {
   132  			requiresInputs = true
   133  		}
   134  	}
   135  
   136  	// 4 possible cases here:
   137  	// 1. requiresOutputs - false requiresInputs - false
   138  	//    This is a balanced transaction with no free assets to consume.
   139  	//    It could potentially be a complete transaction.
   140  	// 2. requiresOutputs - true requiresInputs - false
   141  	//    This is an unbalanced transaction with free assets to consume
   142  	// 3. requiresOutputs - false requiresInputs - true
   143  	//    This is an unbalanced transaction with a requiring assets to be spent
   144  	// 4. requiresOutputs - true requiresInputs - true
   145  	//    This is an unbalanced transaction with free assets to consume
   146  	//    and requiring assets to be spent.
   147  	// The only case that needs to be protected against is 2.
   148  	if requiresOutputs && !requiresInputs {
   149  		return errors.Wrap(ErrBlankCheck)
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  // MissingFieldsError returns a wrapped error ErrMissingFields
   156  // with a data item containing the given field names.
   157  func MissingFieldsError(name ...string) error {
   158  	return errors.WithData(ErrMissingFields, "missing_fields", name)
   159  }
   160  
   161  // AddContractArgs add contract arguments
   162  func AddContractArgs(sigInst *SigningInstruction, arguments []ContractArgument) error {
   163  	for _, arg := range arguments {
   164  		switch arg.Type {
   165  		case "raw_tx_signature":
   166  			rawTxSig := &RawTxSigArgument{}
   167  			if err := json.Unmarshal(arg.RawData, rawTxSig); err != nil {
   168  				return err
   169  			}
   170  
   171  			// convert path form chainjson.HexBytes to byte
   172  			var path [][]byte
   173  			for _, p := range rawTxSig.Path {
   174  				path = append(path, []byte(p))
   175  			}
   176  			sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
   177  
   178  		case "data":
   179  			data := &DataArgument{}
   180  			if err := json.Unmarshal(arg.RawData, data); err != nil {
   181  				return err
   182  			}
   183  			sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(data.Value))
   184  
   185  		case "string":
   186  			data := &StrArgument{}
   187  			if err := json.Unmarshal(arg.RawData, data); err != nil {
   188  				return err
   189  			}
   190  			sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness([]byte(data.Value)))
   191  
   192  		case "integer":
   193  			data := &IntegerArgument{}
   194  			if err := json.Unmarshal(arg.RawData, data); err != nil {
   195  				return err
   196  			}
   197  			sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.Uint64Bytes(data.Value)))
   198  
   199  		case "boolean":
   200  			data := &BoolArgument{}
   201  			if err := json.Unmarshal(arg.RawData, data); err != nil {
   202  				return err
   203  			}
   204  			sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.BoolBytes(data.Value)))
   205  
   206  		default:
   207  			return ErrBadContractArgType
   208  		}
   209  	}
   210  
   211  	return nil
   212  }