9 log "github.com/sirupsen/logrus"
11 "github.com/vapor/account"
12 "github.com/vapor/blockchain/txbuilder"
13 "github.com/vapor/blockchain/txbuilder/mainchain"
14 "github.com/vapor/common"
15 "github.com/vapor/consensus"
16 "github.com/vapor/crypto/ed25519/chainkd"
17 chainjson "github.com/vapor/encoding/json"
18 "github.com/vapor/equity/pegin_contract"
19 "github.com/vapor/errors"
20 "github.com/vapor/protocol/bc"
21 "github.com/vapor/protocol/bc/types"
22 bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
23 "github.com/vapor/protocol/vm/vmutil"
26 func (a *API) buildMainChainTxForContract(ins struct {
27 Utxo account.UTXO `json:"utxo"`
28 Tx types.Tx `json:"raw_transaction"`
29 RootXPubs []chainkd.XPub `json:"root_xpubs"`
30 ControlProgram string `json:"control_program"`
31 ClaimScript chainjson.HexBytes `json:"claim_script"`
34 var xpubs []chainkd.XPub
35 for _, xpub := range ins.RootXPubs {
36 // pub + scriptPubKey 生成一个随机数A
38 h := hmac.New(sha256.New, xpub[:])
39 h.Write(ins.ClaimScript)
40 tweak := h.Sum(tmp[:])
41 // pub + A 生成一个新的公钥pub_new
42 chaildXPub := xpub.Child(tweak)
43 xpubs = append(xpubs, chaildXPub)
46 txInput, sigInst, err := contractToInputs(a, &ins.Utxo, xpubs, ins.ClaimScript)
47 builder := mainchain.NewBuilder(time.Now())
48 builder.AddInput(txInput, sigInst)
49 changeAmount := uint64(0)
51 for _, key := range ins.Tx.GetResultIds() {
52 output, err := ins.Tx.Retire(*key)
54 log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
58 var controlProgram []byte
60 if controlProgram, retBool = getInput(ins.Tx.Entries, *key, ins.ControlProgram); !retBool {
61 return NewErrorResponse(errors.New("The corresponding input cannot be found"))
64 assetID := *output.Source.Value.AssetId
65 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
66 builder.AddOutput(out)
67 changeAmount = ins.Utxo.Amount - output.Source.Value.Amount
72 return NewErrorResponse(errors.New("It's not a transaction to retire assets"))
77 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, ins.Utxo.ControlProgram)
78 builder.AddOutput(out)
81 tmpl, tx, err := builder.Build()
83 return NewErrorResponse(err)
86 for i, out := range tmpl.Transaction.Outputs {
87 if bytes.Equal(out.ControlProgram, ins.Utxo.ControlProgram) {
88 //tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
89 tx.Outputs[i].Amount = changeAmount - 100000000
92 tmpl.Transaction = bytomtypes.NewTx(*tx)
93 return NewSuccessResponse(tmpl)
96 func (a *API) buildMainChainTx(ins struct {
97 Utxo account.UTXO `json:"utxo"`
98 Tx types.Tx `json:"raw_transaction"`
99 RootXPubs []chainkd.XPub `json:"root_xpubs"`
100 ControlProgram string `json:"control_program"`
101 ClaimScript chainjson.HexBytes `json:"claim_script"`
104 var xpubs []chainkd.XPub
105 for _, xpub := range ins.RootXPubs {
106 // pub + scriptPubKey 生成一个随机数A
108 h := hmac.New(sha256.New, xpub[:])
109 h.Write(ins.ClaimScript)
110 tweak := h.Sum(tmp[:])
111 // pub + A 生成一个新的公钥pub_new
112 chaildXPub := xpub.Child(tweak)
113 xpubs = append(xpubs, chaildXPub)
116 txInput, sigInst, err := utxoToInputs(xpubs, &ins.Utxo)
118 return NewErrorResponse(err)
121 builder := mainchain.NewBuilder(time.Now())
122 builder.AddInput(txInput, sigInst)
123 changeAmount := uint64(0)
125 for _, key := range ins.Tx.GetResultIds() {
126 output, err := ins.Tx.Retire(*key)
128 log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
132 var controlProgram []byte
134 if controlProgram, retBool = getInput(ins.Tx.Entries, *key, ins.ControlProgram); !retBool {
135 return NewErrorResponse(errors.New("The corresponding input cannot be found"))
138 assetID := *output.Source.Value.AssetId
139 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
140 builder.AddOutput(out)
141 changeAmount = ins.Utxo.Amount - output.Source.Value.Amount
146 return NewErrorResponse(errors.New("It's not a transaction to retire assets"))
149 if changeAmount > 0 {
151 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, ins.Utxo.ControlProgram)
152 builder.AddOutput(out)
155 tmpl, tx, err := builder.Build()
157 return NewErrorResponse(err)
160 txGasResp, err := EstimateTxGasForMainchain(*tmpl)
162 return NewErrorResponse(err)
164 for i, out := range tmpl.Transaction.Outputs {
165 if bytes.Equal(out.ControlProgram, ins.Utxo.ControlProgram) {
166 tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
169 tmpl.Transaction = bytomtypes.NewTx(*tx)
170 return NewSuccessResponse(tmpl)
174 func getInput(entry map[bc.Hash]bc.Entry, outputID bc.Hash, controlProgram string) ([]byte, bool) {
175 output := entry[outputID].(*bc.Retirement)
176 mux := entry[*output.Source.Ref].(*bc.Mux)
178 for _, valueSource := range mux.GetSources() {
179 spend := entry[*valueSource.Ref].(*bc.Spend)
180 prevout := entry[*spend.SpentOutputId].(*bc.Output)
182 var ctrlProgram chainjson.HexBytes
183 ctrlProgram = prevout.ControlProgram.Code
184 tmp, _ := ctrlProgram.MarshalText()
185 if string(tmp) == controlProgram {
186 return ctrlProgram, true
192 // UtxoToInputs convert an utxo to the txinput
193 func utxoToInputs(xpubs []chainkd.XPub, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
194 txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
195 sigInst := &mainchain.SigningInstruction{}
198 sigInst.AddWitnessKeys(xpubs, quorum)
199 return txInput, sigInst, nil
202 address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
207 sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
209 switch address.(type) {
210 case *common.AddressWitnessPubKeyHash:
211 derivedPK := xpubs[0].PublicKey()
212 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness([]byte(derivedPK)))
214 case *common.AddressWitnessScriptHash:
215 derivedXPubs := xpubs
216 derivedPKs := chainkd.XPubKeys(derivedXPubs)
217 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
221 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(script))
224 return nil, nil, errors.New("unsupport address type")
227 return txInput, sigInst, nil
230 func contractToInputs(a *API, u *account.UTXO, xpubs []chainkd.XPub, ClaimScript chainjson.HexBytes) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
231 txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
232 sigInst := &mainchain.SigningInstruction{}
235 for _, xpub := range xpubs {
236 xpubsTmp := []chainkd.XPub{xpub}
237 sigInst.AddRawWitnessKeysWithoutPath(xpubsTmp, 1)
241 peginContractPrograms, err := pegin_contract.GetPeginContractPrograms(ClaimScript)
245 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(peginContractPrograms))
247 return txInput, sigInst, nil
250 type signRespForMainchain struct {
251 Tx *mainchain.Template `json:"transaction"`
252 SignComplete bool `json:"sign_complete"`
255 func (a *API) signWithKey(ins struct {
256 Xprv string `json:"xprv"`
257 XPub chainkd.XPub `json:"xpub"`
258 Txs mainchain.Template `json:"transaction"`
259 ClaimScript chainjson.HexBytes `json:"claim_script"`
261 xprv := &chainkd.XPrv{}
262 if err := xprv.UnmarshalText([]byte(ins.Xprv)); err != nil {
263 return NewErrorResponse(err)
265 // pub + scriptPubKey 生成一个随机数A
267 h := hmac.New(sha256.New, ins.XPub[:])
268 h.Write(ins.ClaimScript)
269 tweak := h.Sum(tmp[:])
270 // pub + A 生成一个新的公钥pub_new
271 privateKey := xprv.Child(tweak, false)
273 if err := sign(&ins.Txs, privateKey); err != nil {
274 return NewErrorResponse(err)
276 log.Info("Sign Transaction complete.")
277 log.Info(mainchain.SignProgress(&ins.Txs))
278 return NewSuccessResponse(&signRespForMainchain{Tx: &ins.Txs, SignComplete: mainchain.SignProgress(&ins.Txs)})
281 func sign(tmpl *mainchain.Template, xprv chainkd.XPrv) error {
282 for i, sigInst := range tmpl.SigningInstructions {
283 for j, wc := range sigInst.WitnessComponents {
284 switch sw := wc.(type) {
285 case *mainchain.SignatureWitness:
286 err := sw.Sign(tmpl, uint32(i), xprv)
288 return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
290 case *mainchain.RawTxSigWitness:
291 err := sw.Sign(tmpl, uint32(i), xprv)
293 return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
298 return materializeWitnesses(tmpl)
301 func materializeWitnesses(txTemplate *mainchain.Template) error {
302 msg := txTemplate.Transaction
305 return errors.Wrap(txbuilder.ErrMissingRawTx)
308 if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
309 return errors.Wrap(txbuilder.ErrBadInstructionCount)
312 for i, sigInst := range txTemplate.SigningInstructions {
313 if msg.Inputs[sigInst.Position] == nil {
314 return errors.WithDetailf(txbuilder.ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
318 for j, wc := range sigInst.WitnessComponents {
319 err := wc.Materialize(&witness)
321 return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
324 msg.SetInputArguments(sigInst.Position, witness)