OSDN Git Service

module claim
[bytom/vapor.git] / claim / rpc / bytom / bytom_main_tx.go
1 package bytom
2
3 import (
4         "bytes"
5         "crypto/hmac"
6         "crypto/sha256"
7         "encoding/json"
8         "time"
9
10         log "github.com/sirupsen/logrus"
11
12         "github.com/vapor/account"
13         "github.com/vapor/blockchain/txbuilder"
14         "github.com/vapor/claim/bytom/mainchain"
15         bytomtypes "github.com/vapor/claim/bytom/protocolbc/types"
16         "github.com/vapor/claim/rpc"
17         "github.com/vapor/common"
18         "github.com/vapor/consensus"
19         "github.com/vapor/crypto/ed25519/chainkd"
20         chainjson "github.com/vapor/encoding/json"
21         "github.com/vapor/equity/pegin_contract"
22         "github.com/vapor/errors"
23         "github.com/vapor/protocol/bc"
24         "github.com/vapor/protocol/bc/types"
25         "github.com/vapor/protocol/vm/vmutil"
26 )
27
28 type BytomMainTx struct {
29         rpc.MainTxParam
30 }
31
32 func (b *BytomMainTx) BuildMainChainTxForContract() ([]byte, error) {
33         var xpubs []chainkd.XPub
34         for _, pub := range b.Pubs {
35                 xpub := chainkd.XPub{}
36                 if err := xpub.UnmarshalText([]byte(pub)); err != nil {
37                         return nil, err
38                 }
39                 // pub + scriptPubKey 生成一个随机数A
40                 var tmp [32]byte
41                 h := hmac.New(sha256.New, xpub[:])
42                 h.Write(b.ClaimScript)
43                 tweak := h.Sum(tmp[:])
44                 // pub +  A 生成一个新的公钥pub_new
45                 chaildXPub := xpub.Child(tweak)
46                 xpubs = append(xpubs, chaildXPub)
47         }
48
49         utxo := &account.UTXO{}
50         if err := json.Unmarshal([]byte(b.Utxo), utxo); err != nil {
51                 return nil, err
52         }
53         txInput, sigInst, err := contractToInputs(utxo, xpubs, b.ClaimScript)
54         builder := mainchain.NewBuilder(time.Now())
55         builder.AddInput(txInput, sigInst)
56         changeAmount := uint64(0)
57         retire := false
58         tx := &types.Tx{}
59         if err := tx.UnmarshalText([]byte(b.Tx)); err != nil {
60                 return nil, err
61         }
62         for _, key := range tx.GetResultIds() {
63                 output, err := tx.Retire(*key)
64                 if err != nil {
65                         log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
66                         continue
67                 }
68                 retire = true
69                 var controlProgram []byte
70                 retBool := true
71                 if controlProgram, retBool = getInput(tx.Entries, *key, b.ControlProgram); !retBool {
72                         return nil, errors.New("The corresponding input cannot be found")
73                 }
74
75                 assetID := *output.Source.Value.AssetId
76                 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
77                 builder.AddOutput(out)
78                 changeAmount = utxo.Amount - output.Source.Value.Amount
79         }
80
81         if !retire {
82                 return nil, errors.New("It's not a transaction to retire assets")
83         }
84
85         if changeAmount > 100000000 {
86                 u := utxo
87                 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, utxo.ControlProgram)
88                 builder.AddOutput(out)
89         }
90
91         tmpl, txData, err := builder.Build()
92         if err != nil {
93                 return nil, err
94         }
95
96         for i, out := range tmpl.Transaction.Outputs {
97                 if bytes.Equal(out.ControlProgram, utxo.ControlProgram) {
98                         if changeAmount-100000000 > 0 {
99                                 txData.Outputs[i].Amount = changeAmount - 100000000
100                         }
101                 }
102         }
103         tmpl.Transaction = bytomtypes.NewTx(*txData)
104         resp, err := mainchain.MarshalText(tmpl)
105         if err != nil {
106                 return nil, err
107         }
108         return resp, nil
109 }
110
111 func (b *BytomMainTx) BuildMainChainTx() ([]byte, error) {
112         var xpubs []chainkd.XPub
113         for _, pub := range b.Pubs {
114
115                 xpub := chainkd.XPub{}
116                 if err := xpub.UnmarshalText([]byte(pub)); err != nil {
117                         return nil, err
118                 }
119                 // pub + scriptPubKey 生成一个随机数A
120                 var tmp [32]byte
121                 h := hmac.New(sha256.New, xpub[:])
122                 h.Write(b.ClaimScript)
123                 tweak := h.Sum(tmp[:])
124                 // pub +  A 生成一个新的公钥pub_new
125                 chaildXPub := xpub.Child(tweak)
126                 xpubs = append(xpubs, chaildXPub)
127         }
128
129         utxo := &account.UTXO{}
130         if err := json.Unmarshal([]byte(b.Utxo), utxo); err != nil {
131                 return nil, err
132         }
133
134         txInput, sigInst, err := utxoToInputs(xpubs, utxo)
135         if err != nil {
136                 return nil, err
137         }
138
139         builder := mainchain.NewBuilder(time.Now())
140         builder.AddInput(txInput, sigInst)
141         changeAmount := uint64(0)
142         retire := false
143         tx := &types.Tx{}
144         if err := tx.UnmarshalText([]byte(b.Tx)); err != nil {
145                 return nil, err
146         }
147         for _, key := range tx.GetResultIds() {
148                 output, err := tx.Retire(*key)
149                 if err != nil {
150                         log.WithFields(log.Fields{"moudle": "transact", "err": err}).Warn("buildMainChainTx error")
151                         continue
152                 }
153                 retire = true
154                 var controlProgram []byte
155                 retBool := true
156                 if controlProgram, retBool = getInput(tx.Entries, *key, b.ControlProgram); !retBool {
157                         return nil, errors.New("The corresponding input cannot be found")
158                 }
159
160                 assetID := *output.Source.Value.AssetId
161                 out := bytomtypes.NewTxOutput(assetID, output.Source.Value.Amount, controlProgram)
162                 builder.AddOutput(out)
163                 changeAmount = utxo.Amount - output.Source.Value.Amount
164
165         }
166
167         if !retire {
168                 return nil, errors.New("It's not a transaction to retire assets")
169         }
170
171         if changeAmount > 0 {
172                 u := utxo
173                 out := bytomtypes.NewTxOutput(u.AssetID, changeAmount, utxo.ControlProgram)
174                 builder.AddOutput(out)
175         }
176
177         tmpl, txData, err := builder.Build()
178         if err != nil {
179                 return nil, err
180         }
181         /*
182                 //交易费估算
183                 txGasResp, err := EstimateTxGasForMainchain(*tmpl)
184                 if err != nil {
185                         return nil, err
186                 }
187         */
188         for i, out := range tmpl.Transaction.Outputs {
189                 if bytes.Equal(out.ControlProgram, utxo.ControlProgram) {
190                         //tx.Outputs[i].Amount = changeAmount - uint64(txGasResp.TotalNeu)
191                         tx.Outputs[i].Amount = changeAmount - 100000000
192                 }
193         }
194         tmpl.Transaction = bytomtypes.NewTx(*txData)
195         resp, err := json.Marshal(tmpl)
196
197         if err != nil {
198                 return nil, err
199         }
200
201         return resp, nil
202 }
203
204 type BytomMainSign struct {
205         rpc.MainTxSignParam
206 }
207
208 func (b *BytomMainSign) SignWithKey() (interface{}, error) {
209         xprv := &chainkd.XPrv{}
210         if err := xprv.UnmarshalText([]byte(b.Xprv)); err != nil {
211                 return nil, err
212         }
213
214         xpub := &chainkd.XPub{}
215         if err := xpub.UnmarshalText([]byte(b.XPub)); err != nil {
216                 return nil, err
217         }
218
219         // pub + scriptPubKey 生成一个随机数A
220         var tmp [32]byte
221         h := hmac.New(sha256.New, xpub[:])
222         h.Write(b.ClaimScript)
223         tweak := h.Sum(tmp[:])
224         // pub +  A 生成一个新的公钥pub_new
225         privateKey := xprv.Child(tweak, false)
226
227         txs := &mainchain.Template{}
228         if err := mainchain.UnmarshalText([]byte(b.Txs), txs); err != nil {
229                 return nil, err
230         }
231
232         if err := sign(txs, privateKey); err != nil {
233                 return nil, err
234         }
235         log.Info("Sign Transaction complete.")
236         return struct {
237                 Tx           *mainchain.Template `json:"transaction"`
238                 SignComplete bool                `json:"sign_complete"`
239         }{Tx: txs, SignComplete: mainchain.SignProgress(txs)}, nil
240 }
241
242 func sign(tmpl *mainchain.Template, xprv chainkd.XPrv) error {
243         for i, sigInst := range tmpl.SigningInstructions {
244                 for j, wc := range sigInst.WitnessComponents {
245                         switch sw := wc.(type) {
246                         case *mainchain.SignatureWitness:
247                                 err := sw.Sign(tmpl, uint32(i), xprv)
248                                 if err != nil {
249                                         return errors.WithDetailf(err, "adding signature(s) to signature witness component %d of input %d", j, i)
250                                 }
251                         case *mainchain.RawTxSigWitness:
252                                 err := sw.Sign(tmpl, uint32(i), xprv)
253                                 if err != nil {
254                                         return errors.WithDetailf(err, "adding signature(s) to raw-signature witness component %d of input %d", j, i)
255                                 }
256                         }
257                 }
258         }
259         return materializeWitnesses(tmpl)
260 }
261
262 func materializeWitnesses(txTemplate *mainchain.Template) error {
263         msg := txTemplate.Transaction
264
265         if msg == nil {
266                 return errors.Wrap(txbuilder.ErrMissingRawTx)
267         }
268
269         if len(txTemplate.SigningInstructions) > len(msg.Inputs) {
270                 return errors.Wrap(txbuilder.ErrBadInstructionCount)
271         }
272
273         for i, sigInst := range txTemplate.SigningInstructions {
274                 if msg.Inputs[sigInst.Position] == nil {
275                         return errors.WithDetailf(txbuilder.ErrBadTxInputIdx, "signing instruction %d references missing tx input %d", i, sigInst.Position)
276                 }
277
278                 var witness [][]byte
279                 for j, wc := range sigInst.WitnessComponents {
280                         err := wc.Materialize(&witness)
281                         if err != nil {
282                                 return errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
283                         }
284                 }
285                 msg.SetInputArguments(sigInst.Position, witness)
286         }
287
288         return nil
289 }
290
291 //
292 func getInput(entry map[bc.Hash]bc.Entry, outputID bc.Hash, controlProgram string) ([]byte, bool) {
293         output := entry[outputID].(*bc.Retirement)
294         mux := entry[*output.Source.Ref].(*bc.Mux)
295
296         for _, valueSource := range mux.GetSources() {
297                 spend := entry[*valueSource.Ref].(*bc.Spend)
298                 prevout := entry[*spend.SpentOutputId].(*bc.Output)
299
300                 var ctrlProgram chainjson.HexBytes
301                 ctrlProgram = prevout.ControlProgram.Code
302                 tmp, _ := ctrlProgram.MarshalText()
303                 if string(tmp) == controlProgram {
304                         return ctrlProgram, true
305                 }
306         }
307         return nil, false
308 }
309
310 // UtxoToInputs convert an utxo to the txinput
311 func utxoToInputs(xpubs []chainkd.XPub, u *account.UTXO) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
312         txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
313         sigInst := &mainchain.SigningInstruction{}
314         quorum := len(xpubs)
315         if u.Address == "" {
316                 sigInst.AddWitnessKeys(xpubs, quorum)
317                 return txInput, sigInst, nil
318         }
319
320         address, err := common.DecodeBytomAddress(u.Address, &consensus.ActiveNetParams)
321         if err != nil {
322                 return nil, nil, err
323         }
324
325         sigInst.AddRawWitnessKeysWithoutPath(xpubs, quorum)
326
327         switch address.(type) {
328         case *common.AddressWitnessPubKeyHash:
329                 derivedPK := xpubs[0].PublicKey()
330                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness([]byte(derivedPK)))
331
332         case *common.AddressWitnessScriptHash:
333                 derivedXPubs := xpubs
334                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
335                 script, err := vmutil.P2SPMultiSigProgram(derivedPKs, quorum)
336                 if err != nil {
337                         return nil, nil, err
338                 }
339                 sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(script))
340
341         default:
342                 return nil, nil, errors.New("unsupport address type")
343         }
344
345         return txInput, sigInst, nil
346 }
347
348 func contractToInputs(u *account.UTXO, xpubs []chainkd.XPub, ClaimScript chainjson.HexBytes) (*bytomtypes.TxInput, *mainchain.SigningInstruction, error) {
349         txInput := bytomtypes.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
350         sigInst := &mainchain.SigningInstruction{}
351
352         // raw_tx_signature
353         for _, xpub := range xpubs {
354                 xpubsTmp := []chainkd.XPub{xpub}
355                 sigInst.AddRawWitnessKeysWithoutPath(xpubsTmp, 1)
356         }
357
358         // data
359         peginContractPrograms, err := pegin_contract.GetPeginContractPrograms(ClaimScript)
360         if err != nil {
361                 return nil, nil, err
362         }
363         sigInst.WitnessComponents = append(sigInst.WitnessComponents, mainchain.DataWitness(peginContractPrograms))
364
365         return txInput, sigInst, nil
366 }