Hi,
Once a txn submitted to the network with the correct hash preimage, the bad guys could intercept this transaction having no encryption whatsoever, and could submit THEIR transaction, too, with the correct hash preimage taken from the originally submitted txn, and divert the funds to their account.
So, it seems to me that hash time lock contracts are fundamentally insecure.
What is the opinion of the community?
Let’s look at the Algorand reference HTLC TEAL contract, specifically where TMPL_RCV is defined in the Hash Lock portion of the code:
Each time this template is used, the TMPL_OWN will define fields including TMPL_RCV and a unique TMPL_HASHIMG which will result in a distinct contract account when compiled by goal clerk compile htlc.teal. Thus the funds are secure within the HTLC and may only exit to the defined TMPL_RCV account with the TMPL_HASHIMG (Hash Lock) -OR- to TMPL_OWN after TMPL_TIMEOUT (Time Lock).
@ryanRFox
Thank you.
I rewrote my faucet contract to make additional checks…
$ cat teal_badge_v1.2.teal
// Make faucet contract account
// Params:
// TMPL_FEE, uint8, fee in microalgos, format: integer
// TMPL_MAX_OUTPUT, uint8, max. faucet output in microalgos, format: integer
// TMPL_ROUNDS, uint8, minimal rounds between to call, format: integer
// TMPL_LEASE, [32]bytes, lease bytes, format: base64
// TMPL_HASH, [32]bytes, sha256(preimage), format: base64
// TMPL_OWN, addr, address of faucet owner, format: addr
// V1, 16-Dec-2019
// V1.1, 17-Dec-2019, added check of txn.Fee
// V1.2, 20-Dec-2019, added various checks and params desc
// Guard agains malicious user specifying huge fee
// txn.Fee <= 0.001 Algo ?
txn Fee
int 1000 // TMPL_FEE
<=
// Guard against malicious user specifying txn.CloseRemainderTo
txn CloseRemainderTo
global ZeroAddress
==
&&
// Limit faucet output to max. 0.1 Algo
// txn.Amount <= 0.1 Algo ?
txn Amount
int 100000 // TMPL_MAX_OUTPUT
<=
&&
// Limit too frequent use of faucet.
// Faucet can be used again only after at least 68 round, i.e. 5 minutes
// txn.LastValid - txn.FirstValid >= floor(300/4.4) ?
txn LastValid
txn FirstValid
-
int 68 // TMPL_ROUNDS
>=
&&
// txn.Lease is given and valid?
txn Lease
byte base64 XlTiv+2SHsY9dttPpSXC+mj4m0rihuavdMKhYjsXDdU= // TMPL_LEASE
==
&&
// OR
// owner may empty contract account, using a hash preimage
arg_0
sha256
byte base64 PF62+e9yBNw+5oJhaRZ1t7pLtUYA8Bm4sdeJukxzoJs= // TMPL_HASH
==
// but check txn.Receiver, so bad guys intercepting the
// hash preimage can't give their address.
txn Receiver
addr JW6L2ZCQT3UIQH5AFM3CVW3C7M3QFYHXA3EU4WHREOATRWMXP6MBFNKILM // TMPL_OWN
==
&&
// add txn.CloseRemainderTo, to guard against bad guys
txn CloseRemainderTo
addr JW6L2ZCQT3UIQH5AFM3CVW3C7M3QFYHXA3EU4WHREOATRWMXP6MBFNKILM // TMPL_OWN
==
&&
// combine
end:
||
My problems:
faucet seems to work more often than every 68 rounds, please see on Testnet txn
Q3FIVIAJO7KHMOO27KHTPSOZEXXSNY4VZNNKAF23J6N7WIJQKNOA
and
LD4BX3T7OCMM2KBPRW4PVWLE36FWETKPKDX4KWW3M6TXFCCHRGIA
So lease seems to be broken.
And my very last problem is, that I was unable to get back funds. When I issued the txn with hash preimage, in dryrun mode it passed, but in reality it began looping:
Hi Maugli, txn lease does not work when you equate it with >= or =<. It has to be an equal sign “==” condition. At least that is what I see it working. Your code can be re written as
txn TypeEnum int 1 == txn Fee int 1000 <= && txn FirstValid int 100 //TMPL_PERIOD - I picked 100 so that this condition passes every 100 rounds % int 0 == && txn LastValid int 100 //TMPL_DURATION - I picked 100 so that this condition passes every 100 rounds txn FirstValid + == && txn Lease byte base64 AQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwQ= ==
&& txn Amount int 1000 == &&
Here I made the round duration to 100 so that the first condition pass instead of 68. Try out and let me know.