OSDN Git Service

fix bug and add address for contract
[bytom/vapor.git] / api / cliam_transact.go
1 package api
2
3 import (
4         "bytes"
5         "context"
6         "encoding/json"
7         "strconv"
8         "time"
9
10         log "github.com/sirupsen/logrus"
11         "github.com/vapor/account"
12         "github.com/vapor/blockchain/txbuilder"
13         "github.com/vapor/consensus"
14         "github.com/vapor/crypto/sha3pool"
15         chainjson "github.com/vapor/encoding/json"
16         "github.com/vapor/errors"
17         "github.com/vapor/protocol/bc"
18         "github.com/vapor/protocol/bc/types"
19         "github.com/vapor/protocol/bc/types/bytom"
20         bytomtypes "github.com/vapor/protocol/bc/types/bytom/types"
21         "github.com/vapor/protocol/validation"
22         "github.com/vapor/util"
23 )
24
25 func getPeginTxnOutputIndex(rawTx bytomtypes.Tx, controlProg []byte) int {
26         for index, output := range rawTx.Outputs {
27                 if bytes.Equal(output.ControlProgram, controlProg) {
28                         return index
29                 }
30         }
31         return -1
32 }
33
34 func toHash(hexBytes []chainjson.HexBytes) (hashs []*bytom.Hash) {
35         for _, data := range hexBytes {
36                 b32 := [32]byte{}
37                 copy(b32[:], data)
38                 res := bytom.NewHash(b32)
39                 hashs = append(hashs, &res)
40         }
41         return
42 }
43
44 func (a *API) claimPeginTx(ctx context.Context, ins struct {
45         Password     string                 `json:"password"`
46         RawTx        bytomtypes.Tx          `json:"raw_transaction"`
47         BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
48         TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
49         StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
50         Flags        []uint32               `json:"flags"`
51         MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
52         ClaimScript  chainjson.HexBytes     `json:"claim_script"`
53 }) Response {
54         tmpl, err := a.createRawPegin(ctx, ins)
55         if err != nil {
56                 log.WithField("build err", err).Error("fail on createrawpegin.")
57                 return NewErrorResponse(err)
58         }
59         // 交易签名
60         if err := txbuilder.Sign(ctx, tmpl, ins.Password, a.PseudohsmSignTemplate); err != nil {
61                 log.WithField("build err", err).Error("fail on sign transaction.")
62                 return NewErrorResponse(err)
63         }
64
65         // submit
66         if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
67                 return NewErrorResponse(err)
68         }
69
70         log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
71         return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
72 }
73
74 func (a *API) createRawPegin(ctx context.Context, ins struct {
75         Password     string                 `json:"password"`
76         RawTx        bytomtypes.Tx          `json:"raw_transaction"`
77         BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
78         TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
79         StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
80         Flags        []uint32               `json:"flags"`
81         MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
82         ClaimScript  chainjson.HexBytes     `json:"claim_script"`
83 }) (*txbuilder.Template, error) {
84         // proof验证
85         var flags []uint8
86         for flag := range ins.Flags {
87                 flags = append(flags, uint8(flag))
88         }
89         txHashes := toHash(ins.TxHashes)
90         matchedTxIDs := toHash(ins.MatchedTxIDs)
91         statusHashes := toHash(ins.StatusHashes)
92         if !bytomtypes.ValidateTxMerkleTreeProof(txHashes, flags, matchedTxIDs, ins.BlockHeader.BlockCommitment.TransactionsMerkleRoot) {
93                 return nil, errors.New("Merkleblock validation failed")
94         }
95         // CheckBytomProof
96         //difficulty.CheckBytomProofOfWork(ins.BlockHeader.Hash(), ins.BlockHeader)
97         // 增加spv验证以及连接主链api查询交易的确认数
98         if util.ValidatePegin {
99                 if err := util.IsConfirmedBytomBlock(ins.BlockHeader.Height, consensus.ActiveNetParams.PeginMinDepth); err != nil {
100                         return nil, err
101                 }
102         }
103         // 找出与claim script有关联的交易的输出
104         var claimScript []byte
105         nOut := len(ins.RawTx.Outputs)
106         if ins.ClaimScript == nil {
107                 // 遍历寻找与交易输出有关的claim script
108                 cps, err := a.wallet.AccountMgr.ListControlProgram()
109                 if err != nil {
110                         return nil, err
111                 }
112
113                 for _, cp := range cps {
114                         _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
115                         if controlProg == nil {
116                                 continue
117                         }
118                         // 获取交易的输出
119                         nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
120                         if nOut != len(ins.RawTx.Outputs) {
121                                 claimScript = cp.ControlProgram
122                         }
123                 }
124         } else {
125                 claimScript = ins.ClaimScript
126                 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
127                 // 获取交易的输出
128                 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
129         }
130         if nOut == len(ins.RawTx.Outputs) || nOut == -1 {
131                 return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
132         }
133
134         // 根据ClaimScript 获取account id
135         var hash [32]byte
136         sha3pool.Sum256(hash[:], claimScript)
137         data := a.wallet.DB.Get(account.ContractKey(hash))
138         if data == nil {
139                 return nil, errors.New("Failed to find control program through claim script")
140         }
141
142         cp := &account.CtrlProgram{}
143         if err := json.Unmarshal(data, cp); err != nil {
144                 return nil, errors.New("Failed on unmarshal control program")
145         }
146
147         // 构造交易
148         // 用输出作为交易输入 生成新的交易
149         builder := txbuilder.NewBuilder(time.Now())
150         // TODO 根据raw tx生成一个utxo
151         //txInput := types.NewClaimInputInput(nil, *ins.RawTx.Outputs[nOut].AssetId, ins.RawTx.Outputs[nOut].Amount, cp.ControlProgram)
152         assetId := bc.AssetID{}
153         assetId.V0 = ins.RawTx.Outputs[nOut].AssetId.GetV0()
154         assetId.V1 = ins.RawTx.Outputs[nOut].AssetId.GetV1()
155         assetId.V2 = ins.RawTx.Outputs[nOut].AssetId.GetV2()
156         assetId.V3 = ins.RawTx.Outputs[nOut].AssetId.GetV3()
157
158         sourceID := bc.Hash{}
159         sourceID.V0 = ins.RawTx.OutputID(nOut).GetV0()
160         sourceID.V1 = ins.RawTx.OutputID(nOut).GetV1()
161         sourceID.V2 = ins.RawTx.OutputID(nOut).GetV2()
162         sourceID.V3 = ins.RawTx.OutputID(nOut).GetV3()
163         outputAccount := ins.RawTx.Outputs[nOut].Amount
164
165         txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
166         if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
167                 return nil, err
168         }
169         program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
170         if err != nil {
171                 return nil, err
172         }
173
174         if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
175                 return nil, err
176         }
177
178         tmpl, txData, err := builder.Build()
179         if err != nil {
180                 return nil, err
181         }
182
183         // todo把一些主链的信息加到交易的stack中
184         var stack [][]byte
185
186         //amount
187         amount := strconv.FormatUint(ins.RawTx.Outputs[nOut].Amount, 10)
188         stack = append(stack, []byte(amount))
189         // 主链的gennesisBlockHash
190         stack = append(stack, []byte(consensus.ActiveNetParams.ParentGenesisBlockHash))
191         // claim script
192         stack = append(stack, claimScript)
193         // raw tx
194         tx, _ := json.Marshal(ins.RawTx)
195         stack = append(stack, tx)
196         // proof
197         blockHeader, err := ins.BlockHeader.MarshalText()
198         if err != nil {
199                 return nil, err
200         }
201         merkleBlock := validation.MerkleBlock{
202                 BlockHeader:  blockHeader,
203                 TxHashes:     txHashes,
204                 StatusHashes: statusHashes,
205                 Flags:        ins.Flags,
206                 MatchedTxIDs: matchedTxIDs,
207         }
208
209         txOutProof, _ := json.Marshal(merkleBlock)
210
211         stack = append(stack, txOutProof)
212
213         //      tmpl.Transaction.Inputs[0].Peginwitness = stack
214         txData.Inputs[0].Peginwitness = stack
215
216         //交易费估算
217         txGasResp, err := EstimateTxGas(*tmpl)
218         if err != nil {
219                 return nil, err
220         }
221         txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
222         //重设置Transaction
223         tmpl.Transaction = types.NewTx(*txData)
224         return tmpl, nil
225 }
226
227 func (a *API) claimContractPeginTx(ctx context.Context, ins struct {
228         Password     string                 `json:"password"`
229         RawTx        bytomtypes.Tx          `json:"raw_transaction"`
230         BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
231         TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
232         StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
233         Flags        []uint32               `json:"flags"`
234         MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
235         ClaimScript  chainjson.HexBytes     `json:"claim_script"`
236 }) Response {
237         tmpl, err := a.createContractRawPegin(ctx, ins)
238         if err != nil {
239                 log.WithField("build err", err).Error("fail on claimContractPeginTx.")
240                 return NewErrorResponse(err)
241         }
242         // 交易签名
243         if err := txbuilder.Sign(ctx, tmpl, ins.Password, a.PseudohsmSignTemplate); err != nil {
244                 log.WithField("build err", err).Error("fail on sign transaction.")
245                 return NewErrorResponse(err)
246         }
247
248         // submit
249         if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
250                 return NewErrorResponse(err)
251         }
252
253         log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
254         return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
255 }
256
257 func (a *API) createContractRawPegin(ctx context.Context, ins struct {
258         Password     string                 `json:"password"`
259         RawTx        bytomtypes.Tx          `json:"raw_transaction"`
260         BlockHeader  bytomtypes.BlockHeader `json:"block_header"`
261         TxHashes     []chainjson.HexBytes   `json:"tx_hashes"`
262         StatusHashes []chainjson.HexBytes   `json:"status_hashes"`
263         Flags        []uint32               `json:"flags"`
264         MatchedTxIDs []chainjson.HexBytes   `json:"matched_tx_ids"`
265         ClaimScript  chainjson.HexBytes     `json:"claim_script"`
266 }) (*txbuilder.Template, error) {
267         // proof验证
268         var flags []uint8
269         for flag := range ins.Flags {
270                 flags = append(flags, uint8(flag))
271         }
272         txHashes := toHash(ins.TxHashes)
273         matchedTxIDs := toHash(ins.MatchedTxIDs)
274         statusHashes := toHash(ins.StatusHashes)
275         if !bytomtypes.ValidateTxMerkleTreeProof(txHashes, flags, matchedTxIDs, ins.BlockHeader.BlockCommitment.TransactionsMerkleRoot) {
276                 return nil, errors.New("Merkleblock validation failed")
277         }
278         // CheckBytomProof
279         //difficulty.CheckBytomProofOfWork(ins.BlockHeader.Hash(), ins.BlockHeader)
280         // 增加spv验证以及连接主链api查询交易的确认数
281         if util.ValidatePegin {
282                 if err := util.IsConfirmedBytomBlock(ins.BlockHeader.Height, consensus.ActiveNetParams.PeginMinDepth); err != nil {
283                         return nil, err
284                 }
285         }
286         // 找出与claim script有关联的交易的输出
287         var claimScript []byte
288         nOut := len(ins.RawTx.Outputs)
289         if ins.ClaimScript == nil {
290                 // 遍历寻找与交易输出有关的claim script
291                 cps, err := a.wallet.AccountMgr.ListControlProgram()
292                 if err != nil {
293                         return nil, err
294                 }
295
296                 for _, cp := range cps {
297                         _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
298                         // 获取交易的输出
299                         nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
300                         if nOut != len(ins.RawTx.Outputs) {
301                                 claimScript = cp.ControlProgram
302                         }
303                 }
304         } else {
305                 claimScript = ins.ClaimScript
306                 _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
307                 // 获取交易的输出
308                 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
309         }
310         if nOut == len(ins.RawTx.Outputs) || nOut == -1 {
311                 return nil, errors.New("Failed to find output in bytom to the mainchain_address from createContractRawPegin")
312         }
313
314         // 根据ClaimScript 获取account id
315         var hash [32]byte
316         sha3pool.Sum256(hash[:], claimScript)
317         data := a.wallet.DB.Get(account.ContractKey(hash))
318         if data == nil {
319                 return nil, errors.New("Failed to find control program through claim script")
320         }
321
322         cp := &account.CtrlProgram{}
323         if err := json.Unmarshal(data, cp); err != nil {
324                 return nil, errors.New("Failed on unmarshal control program")
325         }
326
327         // 构造交易
328         // 用输出作为交易输入 生成新的交易
329         builder := txbuilder.NewBuilder(time.Now())
330         // TODO 根据raw tx生成一个utxo
331         //txInput := types.NewClaimInputInput(nil, *ins.RawTx.Outputs[nOut].AssetId, ins.RawTx.Outputs[nOut].Amount, cp.ControlProgram)
332         assetId := bc.AssetID{}
333         assetId.V0 = ins.RawTx.Outputs[nOut].AssetId.GetV0()
334         assetId.V1 = ins.RawTx.Outputs[nOut].AssetId.GetV1()
335         assetId.V2 = ins.RawTx.Outputs[nOut].AssetId.GetV2()
336         assetId.V3 = ins.RawTx.Outputs[nOut].AssetId.GetV3()
337
338         sourceID := bc.Hash{}
339         sourceID.V0 = ins.RawTx.OutputID(nOut).GetV0()
340         sourceID.V1 = ins.RawTx.OutputID(nOut).GetV1()
341         sourceID.V2 = ins.RawTx.OutputID(nOut).GetV2()
342         sourceID.V3 = ins.RawTx.OutputID(nOut).GetV3()
343         outputAccount := ins.RawTx.Outputs[nOut].Amount
344
345         txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
346         if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
347                 return nil, err
348         }
349         program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
350         if err != nil {
351                 return nil, err
352         }
353
354         if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
355                 return nil, err
356         }
357
358         tmpl, txData, err := builder.Build()
359         if err != nil {
360                 return nil, err
361         }
362
363         // todo把一些主链的信息加到交易的stack中
364         var stack [][]byte
365
366         //amount
367         amount := strconv.FormatUint(ins.RawTx.Outputs[nOut].Amount, 10)
368         stack = append(stack, []byte(amount))
369         // 主链的gennesisBlockHash
370         stack = append(stack, []byte(consensus.ActiveNetParams.ParentGenesisBlockHash))
371         // claim script
372         stack = append(stack, claimScript)
373         // raw tx
374         tx, _ := ins.RawTx.MarshalText()
375         //tx, _ := json.Marshal(ins.RawTx)
376         stack = append(stack, tx)
377         // proof
378         blockHeader, err := ins.BlockHeader.MarshalText()
379         if err != nil {
380                 return nil, err
381         }
382         merkleBlock := validation.MerkleBlock{
383                 BlockHeader:  blockHeader,
384                 TxHashes:     txHashes,
385                 StatusHashes: statusHashes,
386                 Flags:        ins.Flags,
387                 MatchedTxIDs: matchedTxIDs,
388         }
389         txOutProof, _ := json.Marshal(merkleBlock)
390         stack = append(stack, txOutProof)
391         //      tmpl.Transaction.Inputs[0].Peginwitness = stack
392         txData.Inputs[0].Peginwitness = stack
393
394         //交易费估算
395         txGasResp, err := EstimateTxGas(*tmpl)
396         if err != nil {
397                 return nil, err
398         }
399         txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
400         //重设置Transaction
401         tmpl.Transaction = types.NewTx(*txData)
402         return tmpl, nil
403 }