Skip to content

Latest commit

 

History

History
376 lines (277 loc) · 15.7 KB

aip-102.md

File metadata and controls

376 lines (277 loc) · 15.7 KB
Error in user YAML: (<unknown>): did not find expected alphabetic or numeric character while scanning an alias at line 1 column 8
---
  AIP: *102*
  Title: *Hashed Time-Locked Contracts*
  Authors: *ARK.io Team <aips@ark.io>*
  Status: *Draft*
  Discussions-To: https://github.com/arkecosystem/AIPS/issues/102
  Type: *Standards*
  Category: *Core | Network | Protocol*
  Created: *2019-06-27*
  Last Update: *2019-09-15*
  Requires: AIP-11, AIP-28, AIP-29
--- 

Hashed Time-Locked Contracts

Introduction

A Hashed Time-Lock Contract (HTLC) is a set of transaction types that permits a designated party (the "sender/seller") to LOCK funds by disclosing the preimage (secret) of a hash. It also permits a second party (the "recipient/buyer") to CLAIM the funds, or after a timeout is reached enter a REFUND situation.

To understand a basic flow of atomic-swap HTLC contracts interacting with each-other check the diagram below, where Alice is swapping 300 ARK for 3000 NOS with Bob.

Basic Scenario

Atomic Swap (Source: Basic Cross-Chain Swap Scenario)

Atomic-Swap Use-Case Scenario

Step 1

Alice chooses a secret password (e.g: gtMcn7XGlIbq). She hashes the password using a one-way hash to generate the following hash: 0f128d401958b1b30ad0d10406f47f9489321017b4614e6cb993fc63913c5454

Alice sends an ARK blockchain transaction with the instructions: Lock 300 ARK from my account for Bob, with the following password hash: 0f128d401958b1b30ad0d10406f47f9489321017b4614e6cb993fc63913c5454

Step 2

After waiting for Alice's transaction to appear on the blockchain, Bob sends a NOS transaction using the same password hash, the instruction reads as Lock 3000 NOS from my account for Alice, with the following password hash: 0f128d401958b1b30ad0d10406f47f9489321017b4614e6cb993fc63913c5454

Step 3

Alice sees that Bob has initiated a transaction, and claims his 3000 NOS, using the secret password (gtMcn7XGlIbq) known only to him. Alice claims the NOS with the blockchain transaction: Claim 3000 Nos from Bob, the secret password is (gtMcn7XGlIbq) for the password hash: 0f128d401958b1b30ad0d10406f47f9489321017b4614e6cb993fc63913c5454

The password is verified on the NOS blockchain to match the password hash, and the funds are transferred to Alice.

Step 4

Bob sees the secret password from Alice' blockchain transaction. He can then make the claim of 300 ARK from Alice by sending a blockchain transaction with the following instructions: Claim 300 ARK from Alice, the secret password is (gtMcn7XGlIbq) for the password hash: 0f128d401958b1b30ad0d10406f47f9489321017b4614e6cb993fc63913c5454

The password is verified on the ARK blockchain to match the password hash and the funds are transferred to Bob.

Best practices

In terms of the best practice, the implementation should respect a few rules in order to have a secure atomic swaps:

Double spend attack mitigation

The expiration is an indication, but the more the htlc_claim is sent close to the expiration the more room for double spend attack. The double spend attack consists in leverage the different meaning of expiration in different blockchains:

  • Either expiration is relative block height, but due to possible reorganisations, it is impossible to sync the actual expiration date
  • Either expiration is absolute time, but again a reorganisation around the expiration time could make a htlc_claim transaction valid become invalid after the reorganisation.

The best practice is:

  • To set an expiration >> commonly observed reorganisation time (CORT)
  • Alice SHOULD set the htlc_lock expiration > Bobs htlc_lock expiration + CORT

Motivation

HTLC transactions are a safe and cheap method of exchanging secrets for money over the blockchain, due to the ability to recover funds from an uncooperative counterparty and the opportunity that the possessor of a secret has to receive the funds before such a refund can occur. HTLCs also enable cross-chain atomic swaps. Although the initial compatibility with Bitcoin and other HTLC script based chains will not be possible out of the box, a new plugin will be developed in order to support this kind of compatibility.

Disclaimer

The initial HTLC implementation is experimental until finality issues have been resolved. Therefore use at your own risk.

Prerequisites

AIP-29, AIP-11, AIP-18.

This specification supersedes the basic timelock specification in AIP-11.

General Features

  • All new transaction types SHOULD also support multisig
  • By using the power of vendorField we can add any custom logic, also related to the DEX marketplace
  • Transaction Processing rules are under each section of transaction type
  • Locks are indexed by the wallet manager for faster access (locktx.id => wallet)
  • Fees are addressed in the last section, as well as some remarks updated to transaction types

Check below for specifications of new transaction types. We are introducing the following ones: i.) htlc_lock, ii.) htlc_claim and iii.) htlc_refund. htlc_claim and htlc_refund are mutually exclusive, meaning that for referring to the same htlc_lock id, they cannot be added both on the blockchain.

1.HTLC_Lock

A htlc_lock transaction will be introduced. The purpose of this transaction is to lock funds of the sender and made them possible for retrieval by the recipient, if he knows the shared secret.

Name: htlc_lock

Fields

Include base fields from AIP-11

Field Size (bytes) Required
amount \ Amount of network currency that will be locked 8 Y
secret_hash \ A hash of the secret. The SAME hash must be used in the corresponding “claim” transaction. 32 y
expiration_type \ Type of the expiration. Either block height or network epoch timestamp based. 1 Y
expiration \ Expiration of transaction in seconds or height depending on expiration_type. 4 Y
recipient \ Address 21 Y

Limitations/Rules

  • When htlc_lock transaction is applied, the sender’s funds are locked removed (we MUST apply transaction only to sender and DO NOT apply to recipient, this will be done with htlc_claim)
  • The apply logic has to be on the protocol level, as well as on the pool level (tx.blockHeight being replaced by lastBlock.height to account for expiration)
  • Expiration time starts counting when htlc_lock transaction is forged
  • Expiration is defined in seconds/height. A minimum value should be set to at least one round (think about chain rebuild and possible scenarios; this will be mitigated with tendermint like improvements later on)
  • Set a maximum value on expiration or change dynamic-fee logic to make htlc_locks more expensive, based on number of htlc_lock transaction from sender.
  • Update transaction pool validation logic
  • Update consensus protocol validation logic
  • Add transaction bootstrap for getting this data and update wallets in memory (apply only to sender)

Wallets

In order to implement the HTLC logic in our wallet system, new fields are added:

  • locks (default empty) which is a list of htlc_lock transaction ids owned by the wallet
  • lockedBalance (default 0) which is the balance of tokens locked

Important note: the algorithm to account for voting stake is modified to use lockedBalance, so balance field is modified subtracted.

Transaction Processing

Verify

All conditions should be true otherwise fails at first fail

  • generic checks (signature, fee, sender, …)

Apply

  • w.lockBalance = w.lockBalance + tx.amount
  • w.balance = w.balance - tx.amount - tx.fee
  • w.locks.push(tx)

Note: there is no need to verify that tx.recipient exists or to create a wallet if it does not exist

Fees

There is a memory footprint cost for opening a lock on a wallet (storing at least the list of transactionids in the wallet, if not the whole transaction for optimisation purpose)

An obvious attack would be to open zillions of 0.00000001 ARK locks.

Possibilities are:

  • to limit the number of total open locks at a given time, but would potentially lock out legit people from using htlc
  • adjust dynamic htlc_lock fees to total number of open locks

2.HTLC_Claim

A htlc_claim Transaction will be introduced. The purpose of this transaction is for the recipient to CLAIM funds from the sender - if he knows the shared secret.

Name: htlc_claim

Fields

Include base fields from AIP-11

Field Size (bytes) Required
lockTransactionId \ Htlc_lock transaction id, to serve as a reference 32 Y
unlockSecret \ In order to issue CLAIM transactions this must be made visible on chain. Its hash must be equal to htlc_lock.secret_hash. 32 Y

Limitations/Rules

  • Anyone can issue htlc_claim transaction. This makes it easier for businesses and other applications run on top of this to issue automatic refund (controlled by their business logic)
  • transactionId of htlc_lock transaction must be forged and accessible inside a block. We should introduce a minimum height/confirmations before we can process this. This goes related to rebuild process and forks (if we can’t replay this during the rebuild process).
  • Fee should be very low, same as transfer / and dynamic

Transaction Processing

Verify:

All conditions should be true otherwise fails at first fail

  • generic checks on sender (signature, fee, …)
  • find locktx and corresponding wallet (i.e. wallet = walletManager.byLockId(locktx.id))
  • verify sender is recipient of locktx
  • tx.blockHeight < locktx.blockHeight + locktx.expiration
  • locktx.secret_hash = sha256(tx.unlockSecret) // expensive, should be last verification

Apply:

  • rw.balance = rw.balance + locktx.amount - tx.fee (tx.fee given to block forger)
  • lw.lockedBalance = w.lockBalance - locktx.amount
  • lw.locks.pop(locktx)

Fees

  • The fees of htlc_claim are being applied to the recipient. E.g. if sender wallet is empty we cannot apply transaction/the funds are locked. **htlc_claim **fees MUST be applied to receiver (subtracted from claimed amount).

3.HTLC_Refund

A htlc_refund Transaction will be introduced. The purpose of this transaction is for the sender to receive back his locked funds, in the case of recipient not claiming them.

Name: htlc_refund

Fields:

Include base fields from AIP-11

Field Size (bytes) Required
lockTransactionId \ Htlc_lock transaction id, to serve as a reference 32 Y

Limitations/Rules

  • Anyone can issue htlc_refund transaction. This makes it easier for businesses and other applications run on top of this to issue automatic refund (controlled by their business logic)
  • In order to refund transaction the expiration of initial transactionID height (htlc_lock) MUST be completed
  • Check if the funds were already claimed. In this case refund MUST NOT be allowed.
  • Fee should be very low, same as transfer / and dynamic
  • The locked balance is still used to compute the stake for voting, hence the amount is no subtracted with the lockedBalance

Transaction Processing

Verify:

All conditions should be true otherwise fails at first fail

  • generic checks on sender (signature, fee)
  • get locker wallet lw from wallet manager, if not found it means it was already claimed or refunded
  • lastBlockHeight > locktx.blockHeight + locktx.expiration

Apply:

  • Locker:
    • locker.lockedBalance -= txw.lockedBalance
    • locker.locks.remove(lockTx)
  • Sender of refund:
    • sender.balance -= tx.fee

Implementation remarks

Bootstrap

  • To rebuild the htlc locks, we need to review all htlc_lock ids that have not been included into any htlc_claim or htlc_refund. Thus the pseudocode algorithm is (might be optimised):
Build walletManager index of active locks { lockTxId => wallet }

count = 0
max = htlc_lock_list - (htlc_claim_list + htlc_refund_list)
for (tx in htlc_lock_list ordered by most recent tx)
  if (count == max)
    exit
  else if htlc_claim_list.pop(lockTransactionId == tx.id)
    continue
  else if htlc_refund_list.pop(lockTransactionId == tx.id)
    continue
  else
    // should exist otherwise raise error
    find wallet w where w.publicKey = tx.senderPublicKey 
    apply tx to w
    count++

The algorithm is expected to be O (log(htlc_lock_list.size))

Integrity

Verifying integrity is reviewed using the following rules:

  • htlc_lock-(htlc_claim+htlc_refund) == sum(wallets.locks.size)
  • sum(wallets.lockedBalance + wallets.balance) == supply
  • For all wallets w, w.lockedBalance > 0 || w.locks.size == 0

References:

  1. https://github.com/bitcoin/bips/blob/master/bip-0199.mediawiki
  2. ethereum/EIPs#1631
  3. https://rusty.ozlabs.org/?p=462
  4. https://www.youtube.com/watch?v=hs79R8kd_70
  5. https://dev.lightning.community/resources/index.html