OSDN Git Service

update buildUnlockHTLCContractTransaction
[bytom/shuttle.git] / swap / htlc.go
1 package swap
2
3 import (
4         "errors"
5         "fmt"
6         "strconv"
7 )
8
9 var (
10         errFailedGetSignData = errors.New("Failed to get sign data")
11 )
12
13 type HTLCAccount struct {
14         AccountID string
15         Password  string
16         Receiver  string
17         TxFee     uint64
18 }
19
20 type HTLCContractArgs struct {
21         SenderPublicKey    string
22         RecipientPublicKey string
23         BlockHeight        uint64
24         Hash               string
25 }
26
27 type compileLockHTLCContractResponse struct {
28         Program string `json:"program"`
29 }
30
31 var compileLockHTLCContractPayload = `{
32     "contract":"contract HTLC(sender: PublicKey, recipient: PublicKey, blockHeight: Integer, hash: Hash) locks valueAmount of valueAsset { clause complete(preimage: String, sig: Signature) {verify sha256(preimage) == hash verify checkTxSig(recipient, sig) unlock valueAmount of valueAsset} clause cancel(sig: Signature) {verify above(blockHeight) verify checkTxSig(sender, sig) unlock valueAmount of valueAsset}}",
33     "args":[
34         {
35             "string":"%s"
36         },
37         {
38             "string":"%s"
39         },
40         {
41             "integer":%s
42         },
43         {
44             "string":"%s"
45         }
46     ]
47 }`
48
49 func compileLockHTLCContract(contractArgs HTLCContractArgs) (string, error) {
50         payload := []byte(fmt.Sprintf(
51                 compileLockHTLCContractPayload,
52                 contractArgs.SenderPublicKey,
53                 contractArgs.RecipientPublicKey,
54                 strconv.FormatUint(contractArgs.BlockHeight, 10),
55                 contractArgs.Hash,
56         ))
57         res := new(compileLockHTLCContractResponse)
58         if err := request(compileURL, payload, res); err != nil {
59                 return "", err
60         }
61         return res.Program, nil
62 }
63
64 var buildLockHTLCContractTransactionPayload = `{
65     "actions": [
66         {
67             "account_id": "%s",
68             "amount": %s,
69             "asset_id": "%s",
70             "use_unconfirmed":true,
71             "type": "spend_account"
72         },
73         {
74             "amount": %s,
75             "asset_id": "%s",
76             "control_program": "%s",
77             "type": "control_program"
78         },
79         {
80             "account_id": "%s",
81             "amount": %s,
82             "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
83             "use_unconfirmed":true,
84             "type": "spend_account"
85         }
86     ],
87     "ttl": 0,
88     "base_transaction": null
89 }`
90
91 func buildLockHTLCContractTransaction(account HTLCAccount, contractValue AssetAmount, contractControlProgram string) (interface{}, error) {
92         payload := []byte(fmt.Sprintf(
93                 buildLockHTLCContractTransactionPayload,
94                 account.AccountID,
95                 strconv.FormatUint(contractValue.Amount, 10),
96                 contractValue.Asset,
97                 strconv.FormatUint(contractValue.Amount, 10),
98                 contractValue.Asset,
99                 contractControlProgram,
100                 account.AccountID,
101                 strconv.FormatUint(account.TxFee, 10),
102         ))
103         res := new(interface{})
104         if err := request(buildTransactionURL, payload, res); err != nil {
105                 return "", err
106         }
107         return res, nil
108 }
109
110 // type SigningInstruction struct {
111 //      Position          uint64        `json:"position"`
112 //      WitnessComponents []interface{} `json:"witness_components"`
113 // }
114
115 type buildUnlockHTLCContractTransactionResponse struct {
116         RawTransaction         string        `json:"raw_transaction"`
117         SigningInstructions    []interface{} `json:"signing_instructions"`
118         TxFee                  uint64        `json:"fee"`
119         AllowAdditionalActions bool          `json:"allow_additional_actions"`
120 }
121
122 var buildUnlockHTLCContractTransactionPayload = `{
123     "actions": [
124         {
125             "type": "spend_account_unspent_output",
126             "use_unconfirmed":true,
127             "arguments": [],
128             "output_id": "%s"
129         },
130         {
131             "account_id": "%s",
132             "amount": %s,
133             "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
134             "use_unconfirmed":true,
135             "type": "spend_account"
136         },
137         {
138             "amount": %s,
139             "asset_id": "%s",
140             "control_program": "%s",
141             "type": "control_program"
142         }
143     ],
144     "ttl": 0,
145     "base_transaction": null
146 }`
147
148 func buildUnlockHTLCContractTransaction(account HTLCAccount, contractUTXOID string, contractValue AssetAmount) (*buildUnlockHTLCContractTransactionResponse, error) {
149         payload := []byte(fmt.Sprintf(
150                 buildUnlockHTLCContractTransactionPayload,
151                 contractUTXOID,
152                 account.AccountID,
153                 strconv.FormatUint(account.TxFee, 10),
154                 strconv.FormatUint(contractValue.Amount, 10),
155                 contractValue.Asset,
156                 account.Receiver,
157         ))
158         res := new(buildUnlockHTLCContractTransactionResponse)
159         if err := request(buildTransactionURL, payload, res); err != nil {
160                 return nil, err
161         }
162         // signingInst, err := json.Marshal(res.SigningInstructions[1])
163         // if err != nil {
164         //      return nil, err
165         // }
166         // fmt.Println("signingInst:", string(signingInst))
167         return res, nil
168 }
169
170 type TransactionInput struct {
171         AssetID        string `json:"asset_id"`
172         ControlProgram string `json:"control_program"`
173         SignData       string `json:"sign_data"`
174 }
175
176 type decodeRawTransactionResponse struct {
177         TransactionInputs []TransactionInput `json:"inputs"`
178 }
179
180 var decodeRawTransactionPayload = `{
181         "raw_transaction":"%s"
182 }`
183
184 func decodeRawTransaction(rawTransaction string, contractValue AssetAmount) (string, string, error) {
185         payload := []byte(fmt.Sprintf(
186                 decodeRawTransactionPayload,
187                 rawTransaction,
188         ))
189         res := new(decodeRawTransactionResponse)
190         if err := request(decodeRawTransactionURL, payload, res); err != nil {
191                 return "", "", err
192         }
193
194         for _, v := range res.TransactionInputs {
195                 if v.AssetID == contractValue.Asset {
196                         return v.ControlProgram, v.SignData, nil
197                 }
198         }
199         return "", "", errFailedGetSignData
200 }
201
202 type signUnlockHTLCContractTransactionRequest struct {
203         Password    string                                     `json:"password"`
204         Transaction buildUnlockHTLCContractTransactionResponse `json:"transaction"`
205 }
206
207 var signUnlockHTLCContractTransactionPayload = `{
208     "password": "%s",
209     "transaction": {
210         "raw_transaction": "%s",
211         "signing_instructions": [
212             {
213                 "position": 0,
214                 "witness_components": [
215                     {
216                         "type": "data",
217                         "value": "%s"
218                     },
219                     {
220                         "type": "data",
221                         "value": "%s"
222                     },
223                     {
224                         "type": "data",
225                         "value": ""
226                     }
227                 ]
228             },
229             %s
230         ],
231         "fee": %s,
232         "allow_additional_actions": false
233     }
234 }`
235
236 // func signUnlockHTLCContractTransaction(account HTLCAccount, preimage, recipientSig, rawTransaction string) (string, error) {
237 //      rawSigningInstruction, err := json.Marshal(buildTxResp.SigningInstructions[1])
238 //      if err != nil {
239 //              return "", err
240 //      }
241
242 //      fmt.Println("rawSigningInstruction string:", string(rawSigningInstruction))
243
244 //      payload := []byte(fmt.Sprintf(
245 //              signUnlockHTLCContractTransactionPayload,
246 //              account.Password,
247 //              rawTransaction,
248 //              preimage,
249 //              recipientSig,
250 //              string(rawSigningInstruction),
251 //              strconv.FormatUint(account.TxFee, 10),
252 //      ))
253 //      res := new(signTransactionResponse)
254 //      if err := request(signTransactionURL, payload, res); err != nil {
255 //              return "", err
256 //      }
257
258 //      return res.Tx.RawTransaction, nil
259 // }
260
261 // DeployHTLCContract deploy HTLC contract.
262 func DeployHTLCContract(account HTLCAccount, contractValue AssetAmount, contractArgs HTLCContractArgs) (string, error) {
263         // compile locked HTLC cotnract
264         HTLCContractControlProgram, err := compileLockHTLCContract(contractArgs)
265         if err != nil {
266                 return "", err
267         }
268
269         // build locked HTLC contract
270         txLocked, err := buildLockHTLCContractTransaction(account, contractValue, HTLCContractControlProgram)
271         if err != nil {
272                 return "", err
273         }
274
275         // sign locked HTLC contract transaction
276         signedTransaction, err := signTransaction(account.Password, txLocked)
277         if err != nil {
278                 return "", err
279         }
280
281         // submit signed HTLC contract transaction
282         txID, err := submitTransaction(signedTransaction)
283         if err != nil {
284                 return "", err
285         }
286
287         // get HTLC contract output ID
288         contractUTXOID, err := getContractUTXOID(txID, HTLCContractControlProgram)
289         if err != nil {
290                 return "", err
291         }
292         return contractUTXOID, nil
293 }
294
295 // // CallHTLCContract call HTLC contract.
296 // func CallHTLCContract(account HTLCAccount, contractUTXOID, preimage, recipientSig string, contractArgs HTLCContractArgs, contractValue AssetAmount) (string, string, string, error) {
297 //      // build unlocked contract transaction
298 //      buildTxResp, err := buildUnlockHTLCContractTransaction(account, contractUTXOID, contractValue)
299 //      if err != nil {
300 //              return "", "", "", err
301 //      }
302
303 //      fmt.Println("raw transaction:", buildTxResp.RawTransaction)
304 //      contractControlProgram, signData, err := decodeRawTransaction(buildTxResp.RawTransaction, contractValue)
305 //      if err != nil {
306 //              fmt.Println(err)
307 //      }
308 //      fmt.Println("contractControlProgram:", contractControlProgram)
309 //      fmt.Println("signData:", signData)
310
311 //      return buildTxResp.RawTransaction, contractControlProgram, signData, nil
312
313 //      // // sign unlocked HTLC contract transaction
314 //      // signedTransaction, err := signUnlockHTLCContractTransaction(account, preimage, recipientSig, *buildTxResp)
315 //      // if err != nil {
316 //      //      return "", "", err
317 //      // }
318
319 //      // // submit signed HTLC contract transaction
320 //      // txID, err := submitTransaction(signedTransaction)
321 //      // if err != nil {
322 //      //      return "", "", err
323 //      // }
324
325 //      // return "", txID, nil
326 // }