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"
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 []*bytom.Hash) {
34 for _, data := range hexBytes {
37 res := bytom.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 // 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"`
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) {
94 for flag := range ins.Flags {
95 flags = append(flags, uint8(flag))
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")
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 {
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()
120 for _, cp := range cps {
121 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(cp.ControlProgram)
122 if controlProg == nil {
126 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
127 if nOut != len(ins.RawTx.Outputs) {
128 claimScript = cp.ControlProgram
132 claimScript = ins.ClaimScript
133 _, controlProg := a.wallet.AccountMgr.GetPeginControlPrograms(claimScript)
135 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
137 if nOut == len(ins.RawTx.Outputs) {
138 return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
141 // 根据ClaimScript 获取account id
143 sha3pool.Sum256(hash[:], claimScript)
144 data := a.wallet.DB.Get(account.ContractKey(hash))
146 return nil, errors.New("Failed to find control program through claim script")
149 cp := &account.CtrlProgram{}
150 if err := json.Unmarshal(data, cp); err != nil {
151 return nil, errors.New("Failed on unmarshal control program")
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()
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
172 txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
173 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
176 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
181 if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
185 tmpl, txData, err := builder.Build()
190 // todo把一些主链的信息加到交易的stack中
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))
199 stack = append(stack, claimScript)
201 tx, _ := json.Marshal(ins.RawTx)
202 stack = append(stack, tx)
204 MerkleBLock := GetMerkleBlock{
205 BlockHeader: ins.BlockHeader,
206 TxHashes: ins.TxHashes,
207 StatusHashes: ins.StatusHashes,
209 MatchedTxIDs: ins.MatchedTxIDs,
211 txOutProof, _ := json.Marshal(MerkleBLock)
212 stack = append(stack, txOutProof)
214 // tmpl.Transaction.Inputs[0].Peginwitness = stack
215 txData.Inputs[0].Peginwitness = stack
218 txGasResp, err := EstimateTxGas(*tmpl)
222 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
224 tmpl.Transaction = types.NewTx(*txData)
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"`
238 tmpl, err := a.createContractRawPegin(ctx, ins)
240 log.WithField("build err", err).Error("fail on createrawpegin.")
241 return NewErrorResponse(err)
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)
250 if err := txbuilder.FinalizeTx(ctx, a.chain, tmpl.Transaction); err != nil {
251 return NewErrorResponse(err)
254 log.WithField("tx_id", tmpl.Transaction.ID.String()).Info("claim script tx")
255 return NewSuccessResponse(&submitTxResp{TxID: &tmpl.Transaction.ID})
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) {
270 for flag := range ins.Flags {
271 flags = append(flags, uint8(flag))
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")
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, err := a.wallet.AccountMgr.GetPeginContractPrograms(cp.ControlProgram)
299 if controlProg == nil || err != nil {
303 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
304 if nOut != len(ins.RawTx.Outputs) {
305 claimScript = cp.ControlProgram
309 claimScript = ins.ClaimScript
310 controlProg, err := a.wallet.AccountMgr.GetPeginContractPrograms(claimScript)
315 nOut = getPeginTxnOutputIndex(ins.RawTx, controlProg)
317 if nOut == len(ins.RawTx.Outputs) {
318 return nil, errors.New("Failed to find output in bytom to the mainchain_address from getpeginaddress")
321 // 根据ClaimScript 获取account id
323 sha3pool.Sum256(hash[:], claimScript)
324 data := a.wallet.DB.Get(account.ContractKey(hash))
326 return nil, errors.New("Failed to find control program through claim script")
329 cp := &account.CtrlProgram{}
330 if err := json.Unmarshal(data, cp); err != nil {
331 return nil, errors.New("Failed on unmarshal control program")
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()
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
352 txInput := types.NewClaimInputInput(nil, sourceID, assetId, outputAccount, uint64(nOut), cp.ControlProgram)
353 if err := builder.AddInput(txInput, &txbuilder.SigningInstruction{}); err != nil {
356 program, err := a.wallet.AccountMgr.CreateAddress(cp.AccountID, false)
361 if err = builder.AddOutput(types.NewTxOutput(assetId, outputAccount, program.ControlProgram)); err != nil {
365 tmpl, txData, err := builder.Build()
370 // todo把一些主链的信息加到交易的stack中
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))
379 stack = append(stack, claimScript)
381 tx, _ := json.Marshal(ins.RawTx)
382 stack = append(stack, tx)
384 MerkleBLock := GetMerkleBlock{
385 BlockHeader: ins.BlockHeader,
386 TxHashes: ins.TxHashes,
387 StatusHashes: ins.StatusHashes,
389 MatchedTxIDs: ins.MatchedTxIDs,
391 txOutProof, _ := json.Marshal(MerkleBLock)
392 stack = append(stack, txOutProof)
394 // tmpl.Transaction.Inputs[0].Peginwitness = stack
395 txData.Inputs[0].Peginwitness = stack
398 txGasResp, err := EstimateTxGas(*tmpl)
402 txData.Outputs[0].Amount = txData.Outputs[0].Amount - uint64(txGasResp.TotalNeu)
404 tmpl.Transaction = types.NewTx(*txData)