10 "github.com/bytom/crypto"
11 "github.com/bytom/protocol/vm/vmutil"
15 errFailedGetSignData = errors.New("Failed to get sign data")
16 errFailedGetAddress = errors.New("Failed to get address by account ID")
19 type HTLCAccount struct {
26 type HTLCContractArgs struct {
27 SenderPublicKey string
28 RecipientPublicKey string
33 type compileLockHTLCContractResponse struct {
34 Program string `json:"program"`
37 var compileLockHTLCContractPayload = `{
38 "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}}",
55 func compileLockHTLCContract(contractArgs HTLCContractArgs) (string, error) {
56 payload := []byte(fmt.Sprintf(
57 compileLockHTLCContractPayload,
58 contractArgs.SenderPublicKey,
59 contractArgs.RecipientPublicKey,
60 strconv.FormatUint(contractArgs.BlockHeight, 10),
63 res := new(compileLockHTLCContractResponse)
64 if err := request(compileURL, payload, res); err != nil {
67 return res.Program, nil
70 var buildLockHTLCContractTransactionPayload = `{
76 "use_unconfirmed":true,
77 "type": "spend_account"
82 "control_program": "%s",
83 "type": "control_program"
88 "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
89 "use_unconfirmed":true,
90 "type": "spend_account"
94 "base_transaction": null
97 func buildLockHTLCContractTransaction(account HTLCAccount, contractValue AssetAmount, contractControlProgram string) (interface{}, error) {
98 payload := []byte(fmt.Sprintf(
99 buildLockHTLCContractTransactionPayload,
101 strconv.FormatUint(contractValue.Amount, 10),
103 strconv.FormatUint(contractValue.Amount, 10),
105 contractControlProgram,
107 strconv.FormatUint(account.TxFee, 10),
109 res := new(interface{})
110 if err := request(buildTransactionURL, payload, res); err != nil {
116 // type SigningInstruction struct {
117 // Position uint64 `json:"position"`
118 // WitnessComponents []interface{} `json:"witness_components"`
121 type buildUnlockHTLCContractTransactionResponse struct {
122 RawTransaction string `json:"raw_transaction"`
123 SigningInstructions []interface{} `json:"signing_instructions"`
124 TxFee uint64 `json:"fee"`
125 AllowAdditionalActions bool `json:"allow_additional_actions"`
128 var buildUnlockHTLCContractTransactionPayload = `{
131 "type": "spend_account_unspent_output",
132 "use_unconfirmed":true,
139 "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
140 "use_unconfirmed":true,
141 "type": "spend_account"
146 "control_program": "%s",
147 "type": "control_program"
151 "base_transaction": null
154 func buildUnlockHTLCContractTransaction(account HTLCAccount, contractUTXOID string, contractValue AssetAmount) (*buildUnlockHTLCContractTransactionResponse, error) {
155 payload := []byte(fmt.Sprintf(
156 buildUnlockHTLCContractTransactionPayload,
159 strconv.FormatUint(account.TxFee, 10),
160 strconv.FormatUint(contractValue.Amount, 10),
164 res := new(buildUnlockHTLCContractTransactionResponse)
165 if err := request(buildTransactionURL, payload, res); err != nil {
168 // signingInst, err := json.Marshal(res.SigningInstructions[1])
172 // fmt.Println("signingInst:", string(signingInst))
176 type TransactionInput struct {
177 AssetID string `json:"asset_id"`
178 ControlProgram string `json:"control_program"`
179 SignData string `json:"sign_data"`
182 type decodeRawTransactionResponse struct {
183 TransactionInputs []TransactionInput `json:"inputs"`
186 var decodeRawTransactionPayload = `{
187 "raw_transaction":"%s"
190 func decodeRawTransaction(rawTransaction string, contractValue AssetAmount) (string, string, error) {
191 payload := []byte(fmt.Sprintf(
192 decodeRawTransactionPayload,
195 res := new(decodeRawTransactionResponse)
196 if err := request(decodeRawTransactionURL, payload, res); err != nil {
200 for _, v := range res.TransactionInputs {
201 if v.AssetID == contractValue.Asset {
202 return v.ControlProgram, v.SignData, nil
205 return "", "", errFailedGetSignData
208 func getRecipientPublicKey(contractControlProgram string) (string, error) {
209 payload := []byte(fmt.Sprintf(
210 decodeProgramPayload,
211 contractControlProgram,
213 res := new(decodeProgramResponse)
214 if err := request(decodeProgramURL, payload, res); err != nil {
218 publicKey := strings.Fields(res.Instructions)[5]
219 return publicKey, nil
222 type AddressInfo struct {
223 AccountAlias string `json:"account_alias"`
224 AccountID string `json:"account_id"`
225 Address string `json:"address"`
226 ControlProgram string `json:"control_program"`
229 var listAddressesPayload = `{
233 func listAddresses(accountID string) ([]AddressInfo, error) {
234 payload := []byte(fmt.Sprintf(
235 listAddressesPayload,
238 res := new([]AddressInfo)
239 if err := request(listAddressesURL, payload, res); err != nil {
246 func getAddress(accountID, contractControlProgram string) (string, error) {
247 publicKey, err := getRecipientPublicKey(contractControlProgram)
252 publicKeyBytes, err := hex.DecodeString(publicKey)
257 publicKeyHash := crypto.Ripemd160(publicKeyBytes)
258 controlProgram, err := vmutil.P2WPKHProgram(publicKeyHash)
263 addressInfos, err := listAddresses(accountID)
268 for _, addressInfo := range addressInfos {
269 if addressInfo.ControlProgram == hex.EncodeToString(controlProgram) {
270 return addressInfo.Address, nil
273 return "", errFailedGetAddress
276 type signUnlockHTLCContractTransactionRequest struct {
277 Password string `json:"password"`
278 Transaction buildUnlockHTLCContractTransactionResponse `json:"transaction"`
281 var signUnlockHTLCContractTransactionPayload = `{
284 "raw_transaction": "%s",
285 "signing_instructions": [
288 "witness_components": [
306 "allow_additional_actions": false
310 // func signUnlockHTLCContractTransaction(account HTLCAccount, preimage, recipientSig, rawTransaction string) (string, error) {
311 // rawSigningInstruction, err := json.Marshal(buildTxResp.SigningInstructions[1])
316 // fmt.Println("rawSigningInstruction string:", string(rawSigningInstruction))
318 // payload := []byte(fmt.Sprintf(
319 // signUnlockHTLCContractTransactionPayload,
324 // string(rawSigningInstruction),
325 // strconv.FormatUint(account.TxFee, 10),
327 // res := new(signTransactionResponse)
328 // if err := request(signTransactionURL, payload, res); err != nil {
332 // return res.Tx.RawTransaction, nil
335 // DeployHTLCContract deploy HTLC contract.
336 func DeployHTLCContract(account HTLCAccount, contractValue AssetAmount, contractArgs HTLCContractArgs) (string, error) {
337 // compile locked HTLC cotnract
338 HTLCContractControlProgram, err := compileLockHTLCContract(contractArgs)
343 // build locked HTLC contract
344 txLocked, err := buildLockHTLCContractTransaction(account, contractValue, HTLCContractControlProgram)
349 // sign locked HTLC contract transaction
350 signedTransaction, err := signTransaction(account.Password, txLocked)
355 // submit signed HTLC contract transaction
356 txID, err := submitTransaction(signedTransaction)
361 // get HTLC contract output ID
362 contractUTXOID, err := getContractUTXOID(txID, HTLCContractControlProgram)
366 return contractUTXOID, nil
369 // // CallHTLCContract call HTLC contract.
370 // func CallHTLCContract(account HTLCAccount, contractUTXOID, preimage, recipientSig string, contractArgs HTLCContractArgs, contractValue AssetAmount) (string, string, string, error) {
371 // // build unlocked contract transaction
372 // buildTxResp, err := buildUnlockHTLCContractTransaction(account, contractUTXOID, contractValue)
374 // return "", "", "", err
377 // fmt.Println("raw transaction:", buildTxResp.RawTransaction)
378 // contractControlProgram, signData, err := decodeRawTransaction(buildTxResp.RawTransaction, contractValue)
382 // fmt.Println("contractControlProgram:", contractControlProgram)
383 // fmt.Println("signData:", signData)
385 // return buildTxResp.RawTransaction, contractControlProgram, signData, nil
387 // // // sign unlocked HTLC contract transaction
388 // // signedTransaction, err := signUnlockHTLCContractTransaction(account, preimage, recipientSig, *buildTxResp)
389 // // if err != nil {
390 // // return "", "", err
393 // // // submit signed HTLC contract transaction
394 // // txID, err := submitTransaction(signedTransaction)
395 // // if err != nil {
396 // // return "", "", err
399 // // return "", txID, nil