1 // Package txbuilder builds a Chain Protocol transaction from
10 log "github.com/sirupsen/logrus"
12 "github.com/vapor/crypto/ed25519/chainkd"
13 "github.com/vapor/errors"
14 "github.com/vapor/math/checked"
15 "github.com/vapor/protocol/bc"
16 "github.com/vapor/protocol/bc/types"
17 "github.com/vapor/protocol/vm"
20 const logModule = "txbuilder"
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")
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{
54 // Build all of the actions, updating the builder.
56 for i, action := range actions {
57 err := action.Build(ctx, &builder)
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))
64 // If there were any errors, rollback and return a composite error.
67 return nil, errors.WithData(ErrAction, "actions", errs)
70 // Build the transaction template.
71 tpl, tx, err := builder.Build()
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)
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)
96 return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
98 case *RawTxSigWitness:
99 err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
101 return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
106 return materializeWitnesses(tpl)
109 func checkBlankCheck(tx *types.TxData) error {
110 assetMap := make(map[bc.AssetID]int64)
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()))
116 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", asset)
119 for _, out := range tx.Outputs {
120 assetMap[*out.AssetAmount().AssetId], ok = checked.SubInt64(assetMap[*out.AssetAmount().AssetId], int64(out.AssetAmount().Amount))
122 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", out.AssetAmount().AssetId.Bytes())
126 var requiresOutputs, requiresInputs bool
127 for _, amt := range assetMap {
129 requiresOutputs = true
132 requiresInputs = true
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)
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)
161 // AddContractArgs add contract arguments
162 func AddContractArgs(sigInst *SigningInstruction, arguments []ContractArgument) error {
163 for _, arg := range arguments {
165 case "raw_tx_signature":
166 rawTxSig := &RawTxSigArgument{}
167 if err := json.Unmarshal(arg.RawData, rawTxSig); err != nil {
171 // convert path form chainjson.HexBytes to byte
173 for _, p := range rawTxSig.Path {
174 path = append(path, []byte(p))
176 sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
179 data := &DataArgument{}
180 if err := json.Unmarshal(arg.RawData, data); err != nil {
183 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(data.Value))
186 data := &StrArgument{}
187 if err := json.Unmarshal(arg.RawData, data); err != nil {
190 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness([]byte(data.Value)))
193 data := &IntegerArgument{}
194 if err := json.Unmarshal(arg.RawData, data); err != nil {
197 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.Int64Bytes(data.Value)))
200 data := &BoolArgument{}
201 if err := json.Unmarshal(arg.RawData, data); err != nil {
204 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.BoolBytes(data.Value)))
207 return ErrBadContractArgType