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"
22 //ErrBadRefData means invalid reference data
23 ErrBadRefData = errors.New("transaction reference data does not match previous template's reference data")
24 //ErrBadTxInputIdx means unsigned tx input
25 ErrBadTxInputIdx = errors.New("unsigned tx missing input")
26 //ErrBadWitnessComponent means invalid witness component
27 ErrBadWitnessComponent = errors.New("invalid witness component")
28 //ErrBadAmount means invalid asset amount
29 ErrBadAmount = errors.New("bad asset amount")
30 //ErrBlankCheck means unsafe transaction
31 ErrBlankCheck = errors.New("unsafe transaction: leaves assets free to control")
32 //ErrAction means errors occurred in actions
33 ErrAction = errors.New("errors occurred in one or more actions")
34 //ErrMissingFields means missing required fields
35 ErrMissingFields = errors.New("required field is missing")
36 //ErrBadContractArgType means invalid contract argument type
37 ErrBadContractArgType = errors.New("invalid contract argument type")
40 // Build builds or adds on to a transaction.
41 // Initially, inputs are left unconsumed, and destinations unsatisfied.
42 // Build partners then satisfy and consume inputs and destinations.
43 // The final party must ensure that the transaction is
44 // balanced before calling finalize.
45 func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time, timeRange uint64) (*Template, error) {
46 builder := TemplateBuilder{
52 // Build all of the actions, updating the builder.
54 for i, action := range actions {
55 err := action.Build(ctx, &builder)
57 log.WithFields(log.Fields{"action index": i, "error": err}).Error("Loop tx's action")
58 errs = append(errs, errors.WithDetailf(err, "action index %v", i))
62 // If there were any errors, rollback and return a composite error.
65 return nil, errors.WithData(ErrAction, "actions", errs)
68 // Build the transaction template.
69 tpl, tx, err := builder.Build()
75 /*TODO: This part is use for check the balance, but now we are using btm as gas fee
76 the rule need to be rewrite when we have time
77 err = checkBlankCheck(tx)
86 // Sign will try to sign all the witness
87 func Sign(ctx context.Context, tpl *Template, auth string, signFn SignFunc) error {
88 for i, sigInst := range tpl.SigningInstructions {
89 for j, wc := range sigInst.WitnessComponents {
90 switch sw := wc.(type) {
91 case *SignatureWitness:
92 err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
94 return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
96 case *RawTxSigWitness:
97 err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
99 return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
104 return materializeWitnesses(tpl)
107 func checkBlankCheck(tx *types.TxData) error {
108 assetMap := make(map[bc.AssetID]int64)
110 for _, in := range tx.Inputs {
111 asset := in.AssetID() // AssetID() is calculated for IssuanceInputs, so grab once
112 assetMap[asset], ok = checked.AddInt64(assetMap[asset], int64(in.Amount()))
114 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", asset)
117 for _, out := range tx.Outputs {
118 assetMap[*out.AssetId], ok = checked.SubInt64(assetMap[*out.AssetId], int64(out.Amount))
120 return errors.WithDetailf(ErrBadAmount, "cumulative amounts for asset %x overflow the allowed asset amount 2^63", out.AssetId.Bytes())
124 var requiresOutputs, requiresInputs bool
125 for _, amt := range assetMap {
127 requiresOutputs = true
130 requiresInputs = true
134 // 4 possible cases here:
135 // 1. requiresOutputs - false requiresInputs - false
136 // This is a balanced transaction with no free assets to consume.
137 // It could potentially be a complete transaction.
138 // 2. requiresOutputs - true requiresInputs - false
139 // This is an unbalanced transaction with free assets to consume
140 // 3. requiresOutputs - false requiresInputs - true
141 // This is an unbalanced transaction with a requiring assets to be spent
142 // 4. requiresOutputs - true requiresInputs - true
143 // This is an unbalanced transaction with free assets to consume
144 // and requiring assets to be spent.
145 // The only case that needs to be protected against is 2.
146 if requiresOutputs && !requiresInputs {
147 return errors.Wrap(ErrBlankCheck)
153 // MissingFieldsError returns a wrapped error ErrMissingFields
154 // with a data item containing the given field names.
155 func MissingFieldsError(name ...string) error {
156 return errors.WithData(ErrMissingFields, "missing_fields", name)
159 // AddContractArgs add contract arguments
160 func AddContractArgs(sigInst *SigningInstruction, arguments []ContractArgument) error {
161 for _, arg := range arguments {
163 case "raw_tx_signature":
164 rawTxSig := &RawTxSigArgument{}
165 if err := json.Unmarshal(arg.RawData, rawTxSig); err != nil {
169 // convert path form chainjson.HexBytes to byte
171 for _, p := range rawTxSig.Path {
172 path = append(path, []byte(p))
174 sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
177 data := &DataArgument{}
178 if err := json.Unmarshal(arg.RawData, data); err != nil {
181 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(data.Value))
184 data := &StrArgument{}
185 if err := json.Unmarshal(arg.RawData, data); err != nil {
188 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness([]byte(data.Value)))
191 data := &IntegerArgument{}
192 if err := json.Unmarshal(arg.RawData, data); err != nil {
195 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.Int64Bytes(data.Value)))
198 data := &BoolArgument{}
199 if err := json.Unmarshal(arg.RawData, data); err != nil {
202 sigInst.WitnessComponents = append(sigInst.WitnessComponents, DataWitness(vm.BoolBytes(data.Value)))
205 return ErrBadContractArgType