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"
25 func getPeginTxnOutputIndex(rawTx bytomtypes.Tx, controlProg []byte) int {
26 for index, output := range rawTx.Outputs {
27 if bytes.Equal(output.ControlProgram, controlProg) {
34 func toHash(hexBytes []chainjson.HexBytes) (hashs []*bytom.Hash) {
35 for _, data := range hexBytes {
38 res := bytom.NewHash(b32)
39 hashs = append(hashs, &res)
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"`
54 tmpl, err := a.createRawPegin(ctx, ins)
56 log.WithField("build err", err).Error("fail on createrawpegin.")
57 return NewErrorResponse(err)
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)
66 if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
67 return NewErrorResponse(err)
70 log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
71 return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
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) {
86 for flag := range ins.Flags {
87 flags = append(flags, uint8(flag))
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")
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 {
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()
113 for _, cp := range cps {
114 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
115 if controlProg == nil {
119 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
120 if nOut != len(ins.RawTx.Outputs) {
121 claimScript = cp.ControlProgram
125 claimScript = ins.ClaimScript
126 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
128 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
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")
134 // 根据ClaimScript 获取account id
136 sha3pool.Sum256(hash[:], claimScript)
137 data := a.wallet.DB.Get(account.ContractKey(hash))
139 return nil, errors.New("Failed to find control program through claim script")
142 cp := &account.CtrlProgram{}
143 if err := json.Unmarshal(data, cp); err != nil {
144 return nil, errors.New("Failed on unmarshal control program")
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()
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
165 txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
166 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
169 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
174 if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
178 tmpl, txData, err := builder.Build()
183 // todo把一些主链的信息加到交易的stack中
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))
192 stack = append(stack, claimScript)
194 tx, _ := json.Marshal(ins.RawTx)
195 stack = append(stack, tx)
197 blockHeader, err := ins.BlockHeader.MarshalText()
201 merkleBlock := validation.MerkleBlock{
202 BlockHeader: blockHeader,
204 StatusHashes: statusHashes,
206 MatchedTxIDs: matchedTxIDs,
209 txOutProof, _ := json.Marshal(merkleBlock)
211 stack = append(stack, txOutProof)
213 // tmpl.Transaction.Inputs[0].Peginwitness = stack
214 txData.Inputs[0].Peginwitness = stack
217 txGasResp, err := EstimateTxGas(*tmpl)
221 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
223 tmpl.Transaction = types.NewTx(*txData)
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"`
237 tmpl, err := a.createContractRawPegin(ctx, ins)
239 log.WithField("build err", err).Error("fail on claimContractPeginTx.")
240 return NewErrorResponse(err)
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)
249 if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
250 return NewErrorResponse(err)
253 log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
254 return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
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) {
269 for flag := range ins.Flags {
270 flags = append(flags, uint8(flag))
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")
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 {
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()
296 for _, cp := range cps {
297 _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
299 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
300 if nOut != len(ins.RawTx.Outputs) {
301 claimScript = cp.ControlProgram
305 claimScript = ins.ClaimScript
306 _, controlProg := a.wallet.AccountMgr.GetPeginContractControlPrograms(claimScript)
308 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
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")
314 // 根据ClaimScript 获取account id
316 sha3pool.Sum256(hash[:], claimScript)
317 data := a.wallet.DB.Get(account.ContractKey(hash))
319 return nil, errors.New("Failed to find control program through claim script")
322 cp := &account.CtrlProgram{}
323 if err := json.Unmarshal(data, cp); err != nil {
324 return nil, errors.New("Failed on unmarshal control program")
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()
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
345 txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
346 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
349 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
354 if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
358 tmpl, txData, err := builder.Build()
363 // todo把一些主链的信息加到交易的stack中
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))
372 stack = append(stack, claimScript)
374 tx, _ := ins.RawTx.MarshalText()
375 //tx, _ := json.Marshal(ins.RawTx)
376 stack = append(stack, tx)
378 blockHeader, err := ins.BlockHeader.MarshalText()
382 merkleBlock := validation.MerkleBlock{
383 BlockHeader: blockHeader,
385 StatusHashes: statusHashes,
387 MatchedTxIDs: matchedTxIDs,
389 txOutProof, _ := json.Marshal(merkleBlock)
390 stack = append(stack, txOutProof)
391 // tmpl.Transaction.Inputs[0].Peginwitness = stack
392 txData.Inputs[0].Peginwitness = stack
395 txGasResp, err := EstimateTxGas(*tmpl)
399 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
401 tmpl.Transaction = types.NewTx(*txData)