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 }