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"
24 func getPeginTxnOutputIndex(rawTx bytomtypes.Tx, controlProg []byte) int {
25 for index, output := range rawTx.Outputs {
26 if bytes.Equal(output.ControlProgram, controlProg) {
33 func toHash(hexBytes []chainjson.HexBytes) (hashs []*bc.Hash) {
34 for _, data := range hexBytes {
37 res := bc.NewHash(b32)
38 hashs = append(hashs, &res)
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"`
53 tmpl, err := a.createRawPegin(ctx, ins)
55 log.WithField("build err", err).Error("fail on createrawpegin.")
56 return NewErrorResponse(err)
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)
65 if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
66 return NewErrorResponse(err)
69 log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
70 return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
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) {
85 for flag := range ins.Flags {
86 flags = append(flags, uint8(flag))
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")
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 {
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()
112 for _, cp := range cps {
113 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
114 if controlProg == nil {
118 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
119 if nOut != len(ins.RawTx.Outputs) {
120 claimScript = cp.ControlProgram
124 claimScript = ins.ClaimScript
125 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
127 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
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")
133 // 根据ClaimScript 获取account id
135 sha3pool.Sum256(hash[:], claimScript)
136 data := a.wallet.DB.Get(account.ContractKey(hash))
138 return nil, errors.New("Failed to find control program through claim script")
141 cp := &account.CtrlProgram{}
142 if err := json.Unmarshal(data, cp); err != nil {
143 return nil, errors.New("Failed on unmarshal control program")
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
155 txInput := types.NewClaimInputInput(nil, sourceID, assetID, outputAccount, uint64(nOut), cp.ControlProgram)
156 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
159 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
164 if err = builder.AddOutput(types.NewTxOutput(assetID, outputAccount, program.ControlProgram)); err != nil {
168 tmpl, txData, err := builder.Build()
173 // todo把一些主链的信息加到交易的stack中
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))
182 stack = append(stack, claimScript)
184 tx, _ := json.Marshal(ins.RawTx)
185 stack = append(stack, tx)
187 blockHeader, err := ins.BlockHeader.MarshalText()
191 merkleBlock := validation.MerkleBlock{
192 BlockHeader: blockHeader,
194 StatusHashes: statusHashes,
196 MatchedTxIDs: matchedTxIDs,
199 txOutProof, _ := json.Marshal(merkleBlock)
201 stack = append(stack, txOutProof)
203 // tmpl.Transaction.Inputs[0].Peginwitness = stack
204 txData.Inputs[0].Peginwitness = stack
207 txGasResp, err := EstimateTxGas(*tmpl)
211 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
213 tmpl.Transaction = types.NewTx(*txData)
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"`
227 tmpl, err := a.createContractRawPegin(ctx, ins)
229 log.WithField("build err", err).Error("fail on claimContractPeginTx.")
230 return NewErrorResponse(err)
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)
239 if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
240 return NewErrorResponse(err)
243 log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
244 return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
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) {
259 for flag := range ins.Flags {
260 flags = append(flags, uint8(flag))
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")
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 {
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()
286 for _, cp := range cps {
287 _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
289 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
290 if nOut != len(ins.RawTx.Outputs) {
291 claimScript = cp.ControlProgram
295 claimScript = ins.ClaimScript
296 _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
298 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
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")
304 // 根据ClaimScript 获取account id
306 sha3pool.Sum256(hash[:], claimScript)
307 data := a.wallet.DB.Get(account.ContractKey(hash))
309 return nil, errors.New("Failed to find control program through claim script")
312 cp := &account.CtrlProgram{}
313 if err := json.Unmarshal(data, cp); err != nil {
314 return nil, errors.New("Failed on unmarshal control program")
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)
323 sourceID := *ins.RawTx.OutputID(nOut)
324 outputAccount := ins.RawTx.Outputs[nOut].Amount
325 assetID := *ins.RawTx.Outputs[nOut].AssetId
327 txInput := types.NewClaimInputInput(nil, sourceID, assetID, outputAccount, uint64(nOut), cp.ControlProgram)
328 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
331 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
336 if err = builder.AddOutput(types.NewTxOutput(assetID, outputAccount, program.ControlProgram)); err != nil {
340 tmpl, txData, err := builder.Build()
345 // todo把一些主链的信息加到交易的stack中
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))
354 stack = append(stack, claimScript)
356 tx, _ := ins.RawTx.MarshalText()
357 //tx, _ := json.Marshal(ins.RawTx)
358 stack = append(stack, tx)
360 blockHeader, err := ins.BlockHeader.MarshalText()
364 merkleBlock := validation.MerkleBlock{
365 BlockHeader: blockHeader,
367 StatusHashes: statusHashes,
369 MatchedTxIDs: matchedTxIDs,
371 txOutProof, _ := json.Marshal(merkleBlock)
372 stack = append(stack, txOutProof)
373 // tmpl.Transaction.Inputs[0].Peginwitness = stack
374 txData.Inputs[0].Peginwitness = stack
377 txGasResp, err := EstimateTxGas(*tmpl)
381 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
383 tmpl.Transaction = types.NewTx(*txData)