OSDN Git Service

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