OSDN Git Service

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