Skip to content

Commit 35b20f7

Browse files
christophersanbornxeroc
authored andcommitted
Improve HTLC create and redeem
Allow HTLC to be created from hash value, rather than preimage. Add support for Hash160 hash algorithm. Support preimage length of 0 for unrestricted. In redeem, add option to interpret preimage as either ascii or hex bytes.
1 parent 0defdc5 commit 35b20f7

2 files changed

Lines changed: 65 additions & 13 deletions

File tree

bitshares/bitshares.py

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,12 +1566,32 @@ def htlc_create(
15661566
self,
15671567
amount,
15681568
to,
1569-
preimage,
1570-
hash_type="ripemd160",
1571-
account=None,
1569+
*args, # force remaining args to be named not positional
1570+
hash_type=None,
1571+
hash_hex=None,
15721572
expiration=60 * 60,
1573+
preimage=None,
1574+
preimage_length=0,
1575+
account=None,
15731576
**kwargs
15741577
):
1578+
"""Create an HTLC contract.
1579+
1580+
:param Amount amount: Amount to lock
1581+
:param str to: Recipient
1582+
:param int expiration: Contract duration in seconds
1583+
:param str hash_hex: Hash as string of hex digits
1584+
:param str preimage: Preimage as ascii string. Note hex digits would be
1585+
interpretted as ascii text, not as bytes. Not generally recommended
1586+
to use this option. Options hash_hex and preimage are mutually
1587+
exclusive.
1588+
:param int preimage_length: If non-zero, htlc contract will require
1589+
preimage of exact length. Generally OK to leave this as zero. Note
1590+
if preimage param is provided, this value SHOULD be either zero or
1591+
match exactly the length of the preimage, else an irredeemable htlc
1592+
will be created. Optionally, a sentinal value of -1 can be used to
1593+
compute length automatically from the preimage param.
1594+
"""
15751595
import hashlib
15761596
from binascii import hexlify
15771597
from graphenebase.base58 import ripemd160
@@ -1587,37 +1607,63 @@ def htlc_create(
15871607
if not isinstance(amount, (Amount)):
15881608
raise ValueError("'amount' must be of type Amount")
15891609

1610+
if preimage is not None and hash_hex is not None:
1611+
raise ValueError("Must provide either a hash or a preimage, but not both")
1612+
15901613
if hash_type == "ripemd160":
15911614
preimage_type = 0
1592-
preimage_hash = hexlify(
1593-
ripemd160(hexlify(bytes(preimage, "utf-8")))
1594-
).decode("ascii")
15951615
elif hash_type == "sha1":
15961616
preimage_type = 1
1597-
preimage_hash = hashlib.sha1(bytes(preimage, "utf-8")).hexdigest()
15981617
elif hash_type == "sha256":
15991618
preimage_type = 2
1600-
preimage_hash = hashlib.sha256(bytes(preimage, "utf-8")).hexdigest()
1619+
elif hash_type == "hash160":
1620+
preimage_type = 3
16011621
else:
16021622
raise ValueError(
1603-
"Unknown 'hash_type'. Must be 'sha1', 'sha256', or 'ripemd160'"
1623+
"Unknown 'hash_type'. Must be 'sha1', 'sha256', 'ripemd160', 'hash160'"
16041624
)
16051625

1626+
if preimage is not None:
1627+
preimage_size = len(preimage) if preimage_length == -1 else preimage_length
1628+
if hash_type == "ripemd160":
1629+
preimage_hash = hexlify(
1630+
ripemd160(hexlify(bytes(preimage, "utf-8")))
1631+
).decode("ascii")
1632+
elif hash_type == "sha1":
1633+
preimage_hash = hashlib.sha1(bytes(preimage, "utf-8")).hexdigest()
1634+
elif hash_type == "sha256":
1635+
preimage_hash = hashlib.sha256(bytes(preimage, "utf-8")).hexdigest()
1636+
elif hash_type == "hash160":
1637+
preimage_hash = hexlify(ripemd160(
1638+
hashlib.sha256(bytes(preimage, "utf-8")).hexdigest()
1639+
)).decode("ascii")
1640+
elif hash_hex is not None:
1641+
preimage_hash = hexlify(bytes.fromhex(hash_hex)).decode("ascii")
1642+
preimage_size = preimage_length
1643+
else:
1644+
raise ValueError("Must provide either a hash or a preimage")
1645+
16061646
op = operations.Htlc_create(
16071647
**{
16081648
"fee": {"amount": 0, "asset_id": "1.3.0"},
16091649
"from": account["id"],
16101650
"to": to["id"],
16111651
"amount": amount.json(),
16121652
"preimage_hash": [preimage_type, preimage_hash],
1613-
"preimage_size": len(preimage),
1653+
"preimage_size": preimage_size,
16141654
"claim_period_seconds": expiration,
16151655
"extensions": [],
16161656
}
16171657
)
16181658
return self.finalizeOp(op, account, "active", **kwargs)
16191659

1620-
def htlc_redeem(self, htlc_id, preimage, account=None, **kwargs):
1660+
1661+
def htlc_redeem(self, htlc_id, preimage, encoding="utf-8", account=None, **kwargs):
1662+
"""Redeem an htlc contract
1663+
1664+
:param str preimage: The preimage that unlocks the htlc
1665+
:param str encoding: "utf-8", ..., or "hex"
1666+
"""
16211667
from binascii import hexlify
16221668

16231669
htlc = Htlc(htlc_id, blockchain_instance=self)
@@ -1628,11 +1674,16 @@ def htlc_redeem(self, htlc_id, preimage, account=None, **kwargs):
16281674
account = htlc["to"]
16291675
account = Account(account, blockchain_instance=self)
16301676

1677+
if encoding=="hex":
1678+
preimage_hex = hexlify(bytes.fromhex(preimage)).decode("ascii")
1679+
else:
1680+
preimage_hex = hexlify(bytes(preimage, encoding)).decode("ascii")
1681+
16311682
op = operations.Htlc_redeem(
16321683
**{
16331684
"fee": {"amount": 0, "asset_id": "1.3.0"},
16341685
"redeemer": account["id"],
1635-
"preimage": hexlify(bytes(preimage, "utf-8")).decode("ascii"),
1686+
"preimage": preimage_hex,
16361687
"htlc_id": htlc["id"],
16371688
"extensions": [],
16381689
}

bitsharesbase/operations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Ripemd160,
2525
Sha1,
2626
Sha256,
27+
Hash160,
2728
)
2829

2930
from .account import PublicKey
@@ -938,7 +939,7 @@ def detail(self, *args, **kwargs):
938939

939940

940941
class HtlcHash(Static_variant):
941-
elements = [Ripemd160, Sha1, Sha256]
942+
elements = [Ripemd160, Sha1, Sha256, Hash160]
942943

943944
def __init__(self, o):
944945
id = o[0]

0 commit comments

Comments
 (0)