1 // Package txbuilder builds a Chain Protocol transaction from
9 log "github.com/sirupsen/logrus"
11 "github.com/bytom/errors"
12 "github.com/bytom/math/checked"
13 "github.com/bytom/protocol/bc"
14 "github.com/bytom/protocol/bc/types"
19 //ErrBadRefData means invalid reference data
20 ErrBadRefData = errors.New("transaction reference data does not match previous template's reference data")
21 //ErrBadTxInputIdx means unsigned tx input
22 ErrBadTxInputIdx = errors.New("unsigned tx missing input")
23 //ErrBadWitnessComponent means invalid witness component
24 ErrBadWitnessComponent = errors.New("invalid witness component")
25 //ErrBadAmount means invalid asset amount
26 ErrBadAmount = errors.New("bad asset amount")
27 //ErrBlankCheck means unsafe transaction
28 ErrBlankCheck = errors.New("unsafe transaction: leaves assets free to control")
29 //ErrAction means errors occurred in actions
30 ErrAction = errors.New("errors occurred in one or more actions")
31 //ErrMissingFields means missing required fields
32 ErrMissingFields = errors.New("required field is missing")
35 // Build builds or adds on to a transaction.
36 // Initially, inputs are left unconsumed, and destinations unsatisfied.
37 // Build partners then satisfy and consume inputs and destinations.
38 // The final party must ensure that the transaction is
39 // balanced before calling finalize.
40 func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time, timeRange uint64) (*Template, error) {
41 builder := TemplateBuilder{
47 // Build all of the actions, updating the builder.
49 for i, action := range actions {
50 err := action.Build(ctx, &builder)
52 log.WithFields(log.Fields{"action index": i, "error": err}).Error("Loop tx's action")
53 errs = append(errs, errors.WithDetailf(err, "action index %v", i))
57 // If there were any errors, rollback and return a composite error.
60 return nil, errors.WithData(ErrAction, "actions", errs)
63 // Build the transaction template.
64 tpl, tx, err := builder.Build()
70 /*TODO: This part is use for check the balance, but now we are using btm as gas fee
71 the rule need to be rewrite when we have time
72 err = checkBlankCheck(tx)
81 // Sign will try to sign all the witness
82 func Sign(ctx context.Context, tpl *Template, auth string, signFn SignFunc) error {
83 for i, sigInst := range tpl.SigningInstructions {
84 for j, wc := range sigInst.WitnessComponents {
85 switch sw := wc.(type) {
86 case *SignatureWitness:
87 err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
89 return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
91 case *RawTxSigWitness:
92 err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
94 return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
99 return materializeWitnesses(tpl)
102 func checkBlankCheck(tx *types.TxData) error {
103 assetMap := make(map[bc.AssetID]int64)
105 for _, in := range tx.Inputs {
106 asset := in.AssetID() // AssetID() is calculated for IssuanceInputs, so grab once
107 assetMap[asset], ok = checked.AddInt64(assetMap[asset], int64(in.Amount()))
109 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %s overflow the allowed asset amount 2^63", asset)
112 for _, out := range tx.Outputs {
113 assetMap[*out.AssetId], ok = checked.SubInt64(assetMap[*out.AssetId], int64(out.Amount))
115 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", out.AssetId.Bytes())
119 var requiresOutputs, requiresInputs bool
120 for _, amt := range assetMap {
122 requiresOutputs = true
125 requiresInputs = true
129 // 4 possible cases here:
130 // 1. requiresOutputs - false requiresInputs - false
131 // This is a balanced transaction with no free assets to consume.
132 // It could potentially be a complete transaction.
133 // 2. requiresOutputs - true requiresInputs - false
134 // This is an unbalanced transaction with free assets to consume
135 // 3. requiresOutputs - false requiresInputs - true
136 // This is an unbalanced transaction with a requiring assets to be spent
137 // 4. requiresOutputs - true requiresInputs - true
138 // This is an unbalanced transaction with free assets to consume
139 // and requiring assets to be spent.
140 // The only case that needs to be protected against is 2.
141 if requiresOutputs && !requiresInputs {
142 return errors.Wrap(ErrBlankCheck)
148 // MissingFieldsError returns a wrapped error ErrMissingFields
149 // with a data item containing the given field names.
150 func MissingFieldsError(name ...string) error {
151 return errors.WithData(ErrMissingFields, "missing_fields", name)