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 var signMessagePayload = `{
282 type signMessageResponse struct {
283 Signature string `json:"signature"`
284 DerivedXPub string `json:"derived_xpub"`
287 func signMessage(address, message, password string) (string, error) {
288 payload := []byte(fmt.Sprintf(
294 res := new(signMessageResponse)
295 if err := request(signMessageURl, payload, res); err != nil {
298 return res.Signature, nil
301 type signUnlockHTLCContractTransactionRequest struct {
302 Password string `json:"password"`
303 Transaction buildUnlockHTLCContractTransactionResponse `json:"transaction"`
306 var signUnlockHTLCContractTransactionPayload = `{
309 "raw_transaction": "%s",
310 "signing_instructions": [
313 "witness_components": [
331 "allow_additional_actions": false
335 func signUnlockHTLCContractTransaction(account HTLCAccount, preimage, recipientSig, rawTransaction, signingInst string) (string, error) {
336 payload := []byte(fmt.Sprintf(
337 signUnlockHTLCContractTransactionPayload,
343 strconv.FormatUint(account.TxFee, 10),
345 res := new(signTransactionResponse)
346 if err := request(signTransactionURL, payload, res); err != nil {
350 return res.Tx.RawTransaction, nil
353 // DeployHTLCContract deploy HTLC contract.
354 func DeployHTLCContract(account HTLCAccount, contractValue AssetAmount, contractArgs HTLCContractArgs) (string, error) {
355 // compile locked HTLC cotnract
356 HTLCContractControlProgram, err := compileLockHTLCContract(contractArgs)
361 // build locked HTLC contract
362 txLocked, err := buildLockHTLCContractTransaction(account, contractValue, HTLCContractControlProgram)
367 // sign locked HTLC contract transaction
368 signedTransaction, err := signTransaction(account.Password, txLocked)
373 // submit signed HTLC contract transaction
374 txID, err := submitTransaction(signedTransaction)
379 // get HTLC contract output ID
380 contractUTXOID, err := getContractUTXOID(txID, HTLCContractControlProgram)
384 return contractUTXOID, nil
387 // // CallHTLCContract call HTLC contract.
388 // func CallHTLCContract(account HTLCAccount, contractUTXOID, preimage, recipientSig string, contractArgs HTLCContractArgs, contractValue AssetAmount) (string, string, string, error) {
389 // // build unlocked contract transaction
390 // buildTxResp, err := buildUnlockHTLCContractTransaction(account, contractUTXOID, contractValue)
392 // return "", "", "", err
395 // fmt.Println("raw transaction:", buildTxResp.RawTransaction)
396 // contractControlProgram, signData, err := decodeRawTransaction(buildTxResp.RawTransaction, contractValue)
400 // fmt.Println("contractControlProgram:", contractControlProgram)
401 // fmt.Println("signData:", signData)
403 // return buildTxResp.RawTransaction, contractControlProgram, signData, nil
405 // // // sign unlocked HTLC contract transaction
406 // // signedTransaction, err := signUnlockHTLCContractTransaction(account, preimage, recipientSig, *buildTxResp)
407 // // if err != nil {
408 // // return "", "", err
411 // // // submit signed HTLC contract transaction
412 // // txID, err := submitTransaction(signedTransaction)
413 // // if err != nil {
414 // // return "", "", err
417 // // return "", txID, nil