OSDN Git Service

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