HTLC跨链

HTLC跨链

跨链需求

​ 随着区块链技术的发展,各种具有不同特点,不同应用场景的区块链比如比特币、以太坊等公链以及私链和联盟链大量共存,它们之间相互独立,进行数据通信和…

HTLC跨链

跨链需求

​ 随着区块链技术的发展,各种具有不同特点,不同应用场景的区块链比如比特币、以太坊等公链以及私链和联盟链大量共存,它们之间相互独立,进行数据通信和价值转移仍面临挑战,区块链孤岛现象十分严重。为了解决区块链孤岛,实现链与链之间互联互通,进行价值转移,就必须实现跨链技术。就连银保监会陈伟刚也曾说过:“区块链的跨链需求会越来越多,因为区块链之一的联盟链,只是在自己的行业里应用,效益就得不到最优,更多的是行业与行业之间的联盟链跨链,所以跨链需求会成为区块链的一个拓展方向”。

​ 目前,主流的跨链技术方案主要有三种,分别是哈希时间锁定、公证人机制和中继链方式。本篇主要分析实现哈希时间锁定跨链技术方案。哈希时间锁定本质是一种智能合约,其最先出现于2013年的BitcoinTalk论坛的一次讨论中,最早在闪电网络[1]中实现。

哈希时间锁的简单介绍

哈希时间锁是基于hash锁和时间锁来实现的。

  1. hash锁 hash算法是一个可以将任意长度的输入内容以一个固定长度输出的算法,输入内容称为原始值,输出内容称为hash值,hash算法可以保证: a. 输入内容一致的情况下,输出内容也是相同的; b. 从输出内容不能反向推导出输入内容。 基于hash算法的特点,我们可以将hash值看成一把锁,而原始值则是唯一可以解开这把锁的钥匙。
  2. 时间锁 时间锁比较简单,时间锁上会记录一个时间点,即使有正确的钥匙,也必须在此时间点前才能打开,过时则无法打开。 介绍完哈希时间锁的以后,我们来看下哈希时间锁是如果实现跨链的。

哈希时间锁定原理

​ 哈希时间锁定是基于哈希锁和时间锁来实现跨链。假如有两条区块链ChainA和ChainB,每个链的原生资产分别为a、b,两个用户Alice和Bob,Alice有资产a,Bob有资产b,Alice与Bob想交换资产,那么他们可以使用以下的方式来实现:

  1. Alice选取一个秘密随机数S,计算出S的哈希值H(S),Alice将H(S)发送给Bob,然后Alice指定一个时间点T1。接着Alice在ChainA上创建资产锁定合约,合约执行以下步骤:
    • 使用hash值H(S)将自己在ChainA上的资产a加上哈希锁;
    • 使用时间点T1将自己在ChainA上资产a加上时间锁; 只有获得S并且在时间点T1之前才将资产a转移走,如果到时间点T1后仍未解锁,则允许撤销锁定,且不会发生资产转移。
  2. Alice将H(S)发送给Bob,Bob基于H(S)和一个小于T1的时间点T2在ChainB上创建资产锁定合约,合约执行以下步骤:
    • 使用hash值H(S)将自己在ChainB上的资产b加上哈希锁;
    • 使用时间点T2将自己在ChainB上资产b加上时间锁; 只有拥有S并且在时间点T2之前才将资产b转移走,如果到时间点T2后仍未解锁,则b资产超时锁定。
  3. Alice使用S在时间点T2前调用ChainB上的智能合约解锁资产b,并将资产转移给自己。
  4. 因为Alice解锁了资产b,所以Bob也获得S的值,Bob使用S在时间点T1前解锁资产a,并将资产转移给自己。

通过以上的过程Alice和Bob就使用哈希时间锁在两条链上完成了资产转移。

适用场景

​ 虽然哈希时间锁定可以完成跨链,但它仅适用于不同区块链间的资产价值交换,且使用它的区块链网络需要如下三点:第一必须要有账户资产,第二必须通过可编程智能合约建立信任,第三交易双方必须在两种区块链网络中拥有各自的资产托管账户。哈希时间锁作为跨链资产交换解决方案,目前应用于WeCross[2]、闪电网络、Interledger、雷电网络、Sprites 通道等[3]。除此之外还在证券结算、去中心化交易所[4]以及央行数字货币跨境支付得到广泛引用。

HTLC在Fabric与Ethereum间的跨链探索

​ 因为哈希时间锁定不适用于fabric等没有资产的联盟链,所以我们需要先让我们的fabric网络拥有账户资产,为此我们需要实现账户资产链码,再实现哈希时间锁定链码,所以fabric网络需要账户资产和哈希时间锁定两种链码。对于以太坊,我们只需要编写一个哈希时间锁定合约即可。

Fabric上面的实现

账户资产链码

type Account struct {
Address  string      `json:"address"`     // 账户地址
Amount   uint64      `json:"amount"`      // 账户资产余额
Passwd   string      `json:"passwd"`      // 账户密码
Sequence uint64      `json:"sequence"`    // 账户序列号,用于创建中间账户
Type     int         `json:"type"`        // 账户类型,0为中间账户1为普通账户
TransferTo [2]string `json:"transfer_to"` // 中间账户资产能够转出的地址,为哈希交易的双方地址
}

Account结构体为账户资产模型,其中 Amount 为资产金额, Sequence 序列号主要用于创建中间账户, Type 为账户类型, 0 为中间账户, 1 为普通账户, TransferTo 表示该账户资产能够转入的账户地址,当为普通账户时为空,为中间账户时是hash交易的双方地址。

Account提供一个转账方法:

/**
*to:接收者账户
*amount:转账金额
*/
func (from *Account) Transfer(stub shim.ChaincodeStubInterface, to *Account, amount uint64) error {
sendKey := fmt.Sprintf(AccountPrefix, from.Address)
from.Amount = safeSub(from.Amount, amount)
receiverKey := fmt.Sprintf(AccountPrefix, to.Address)
to.Amount = safeAdd(to.Amount, amount)
...
}

账户结构体的转账方法,发送者账户减少资产接收者账户增加资产,在分别保存发送者账户和接收者账户。

账户资产链码主要提供4个接口,分别是中间用户注册、用户注册、资产转账和用户查询。

/**
*args[0]:address,中间账户地址。
*args[1]:passwd,中间账户的密码或者密码的哈希值。
*args[2]:flag,标志位,为hash表示为passwd为中间账户的密码的哈希值。
*args[3]:sender,哈希时间锁交易的发送者。
*args[4]:receiver,哈希时间锁交易的接收者。
*/
func (a *AccountAssert) createMidAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 计算passwd的哈希值,以存储
if flag == "hash" {
pw = passwd
} else {
sha256Passwd := sha256.Sum256([]byte(passwd))
pw = hex.EncodeToString(sha256Passwd[:])
}
transferTo := [2]string{sender, receiver}
acc := Account{
Address:  address,
Amount:   0,
Passwd:   pw,
Sequence: 0,
Type: MidAccount,
TransferTo: transferTo,
}
...
}

中间用户注册需要5个参数,账户地址address、账户密码passwd、账户标志位flag、哈希时间锁交易发送者sender和哈希时间锁交易接收者receiver。中间账户的密码就是哈希时间锁定交易里面的哈希原像,因为可能为后创建哈希时间锁的账户只能知道哈希值,所以通过标志位flag区分passwd是哈希原像还是哈希值。

/**
*args[0]:address,普通账户地址。
*args[1]:passwd,普通账户的密码。
*/
func (a *AccountAssert) createAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
passwd := args[1]
// 计算passwd的哈希值,以存储
sha256Passwd := sha256.Sum256([]byte(passwd))
acc := Account{
Address:  address,
Amount:   0,
Passwd:   hex.EncodeToString(sha256Passwd[:]),
Sequence: 0,
Type: GenAccount,
TransferTo: [2]string{}, // 普通账户,TransferTo为空值
}
...
}

用户注册需要传入地址address,密码passwd,密码存储的是哈希值。

/**
*args[0]:from,交易转出方地址。
*args[1]:to,交易转入方地址。
*args[2]:amount,转账金额。
*args[3]:passwd,交易转出方账户密码。
*/
func (a *Account) transfer(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 得到交易转出方账户
fromKey := fmt.Sprintf(AccountPrefix, from)
senderInfo, err := stub.GetState(fromKey)
sender := &Account{}
err = json.Unmarshal(senderInfo, sender)
...
// 验证交易转出方账户密码是否正确
sha256Passwd := sha256.Sum256([]byte(passwd))
if strings.Compare(sender.Passwd, hex.EncodeToString(sha256Passwd[:])) != 0 {
return shim.Error("sender account passwd is error")
}
...
// 交易转出方是中间账户,判断转入方地址是否正确,保证中间账户资产的安全性
if sender.Type == MidAccount {
if sender.TransferTo[0] != to && sender.TransferTo[1] != to {
return shim.Error("mid account not can transfer to other account")
}
}
...
// 调用账户的转账方法执行转账操作
err = sender.Transfer(stub, receiver, amount)
...
}

资产转账需要传入发送者from,接收者to,金额amount和发送者密码passwd四个参数。首先判断发送者是否存在,再判断账户密码是否正确,接着再判断接收者是否存在以及发送者金额是否充足,当发送者是中间账户还要判断接收者地址的正确性,这些都校验通过最后在在发送者账户执行减法操作,在接收者账户执行加法操作,以完成转账。

/**
*args[0]:address,交易转出方地址。
*/
func (a *AccountAssert) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
key := fmt.Sprintf(AccountPrefix, address)
accByte, err := stub.GetState(key)
...
}

账户查询只需要传入账户地址,账户存在就返回账户信息,不存在就返回账户不存在。

HTLC链码

type HTLC struct {
Sender      string    `json:"sender"`
Receiver    string    `json:"receiver"`
Amount      uint64    `json:"amount"`
HashValue   string    `json:"hash_value"`
TimeLock    int64     `json:"time_lock"`
PreImage    string    `json:"pre_image"`
LockAddress string    `json:"lock_address"`
State       HTLCState `json:"state"`
}

HTLC结构体是哈希时间锁定的交易数据结构, HashValuePreImage 的sha256哈希值, LockAddress 就是用户注册的中间账户地址, PreImage 为该中间账户的密码,状态 State 共分为已锁定 HashLOCK 、已领取 Received 和已退款 Refund 共三种。

HTLC链码主要有创建中间账户、创建哈希时间锁定、领取资产、退回资产和查询哈希时间锁定信息共5个功能。

/**
*args[0]:sender,哈希时间锁定交易发送者。
*args[1]:preImage,哈希时间锁定交易的哈希原像或者是原像的哈希值。
*args[2]:flag,标志位,为hash表示preImage为原像的哈希值。
*args[3]:receiver,哈希时间锁定交易接收者。
*/
func (h *HTLCChaincode) createMidAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 中间账户地址以发送者地址+发送者的序列号方式创建的
midAddress := senderAccount.Address + uint64ToString(senderAccount.Sequence)
...
// 调用账号资产链码的创建中间账户接口
trans = [][]byte{[]byte("registermidaccount"), []byte(midAddress), []byte(preImage), []byte(flag), []byte(sender), []byte(receiver)}
resPonse = stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
if resPonse.Status != shim.OK {
return shim.Error("craete htlc register mid account error: " + resPonse.Message)
}
...
// 返回中间账户地址和哈希时间锁定交易的哈希值
respon := ResponseMidAccount{}
respon.Address = midAddress
if flag == "hash" {
respon.Hash = preImage
} else {
hashByte := sha256.Sum256([]byte(preImage))
respon.Hash = hex.EncodeToString(hashByte[:])
}
responByte, err := json.Marshal(respon)
...
return shim.Success(responByte)
}

创建中间账户需要传入交易发送者地址,哈希原像(哈希原像为中间账户的密码)或者哈希值,标志位和交易接收者地址。它主要调用账户资产链码的中间用户注册接口,返回的是中间账户地址和哈希时间锁定的哈希值。

/**
*args[0]:sender,哈希时间锁定交易发送者地址。
*args[1]:receiver,哈希时间锁定交易接收者地址。
*args[2]:amount,哈希时间锁定交易金额。
*args[3]:timelock,哈希时间锁定交易的时间锁。
*args[4]:hashValue,哈希时间锁交易哈希值。
*args[5]:passwd,哈希时间锁定交易发送者账户密码。
*args[6]:midaddress,哈希时间锁定交易的锁定地址。
*/
func (h *HTLCChaincode) createHash(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 调用账户资产链码的转账接口,完成资产从发送者账户到锁定账户的转移
trans = [][]byte{[]byte("transfer"), []byte(sender), []byte(midaddress), []byte(amountStr), []byte(passwd)}
resPonse = stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
if resPonse.Status != shim.OK {
return shim.Error("craete htlc transfer mid  account error:" + resPonse.Message)
}
...
htlc := HTLC{
Sender:      sender,
Receiver:    receive,
Amount:      amount,
HashValue:   hashValue,
TimeLock:    timeLock,
PreImage:    "", // 先设置为空,在对方领取资产的时候在给值
LockAddress: midaddress,
State:       HashLOCK,
}
htlcByte, err := json.Marshal(htlc)
idByte := sha256.Sum256(htlcByte)
id := hex.EncodeToString(idByte[:])
...
// 返回id
return shim.Success([]byte(id))
...
}

创建哈希时间锁定交易需要发送者地址、接收者地址、转账数额、时间锁、哈希值、发送者账户密码和中间账户地址。哈希值和中间账户地址就是上一步创建中间账户时的返回值。它主要调用账户资产链码的资产转账接口,完成资产从发送者账户到中间账户的转移,返回哈希时间锁定交易id。

/**
*args[0]:id,哈希时间锁定交易id。
*args[1]:preImage,哈希时间锁定的哈希原像。
*/
func (h *HTLCChaincode) withdraw(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 状态必须已锁定
if htlc.State != HashLOCK {
return shim.Error("this htlc transaction state is error")
}
// 时间锁不能过期
if htlc.TimeLock < time.Now().Unix() {
return shim.Error("time is expirate")
}
// 调用账户资产链码的转账接口,完成资产从中间账户到交易接收者账户的转移
trans := [][]byte{[]byte("transfer"), []byte(htlc.LockAddress), []byte(htlc.Receiver), []byte(uint64ToString(htlc.Amount)), []byte(preImage)}
resPonse := stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
...
// 对哈希时间锁定交易的哈希原像进行赋值并把状态修改成已领取
htlc.PreImage = preImage
htlc.State = Received
...
}

领取资产需要哈希时间锁定交易id和哈希原像(中间账户密码),首先判断该哈希时间锁定交易是否存在,再判断哈希时间锁定状态是否是已锁定,然后判断时间锁是否超时,都检查通过之后再调用账户链码资产的资产转移接口,完成资产从中间账户到接收者账户的转移并更新哈希原像及状态。

/**
*args[0]:id,哈希时间锁定交易id。
*args[1]:preImage,哈希时间锁定的哈希原像。
*/
func (h *HTLCChaincode) refund(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 时间锁需要过期
if htlc.TimeLock > time.Now().Unix() {
return shim.Error("time is not expirate")
}
// 状态要已锁定
if htlc.State != HashLOCK {
return shim.Error("this htlc transaction state is error")
}
// 调用账户资产链码的转账接口,完成资产从中间账户到交易发送者账户的转移
trans := [][]byte{[]byte("transfer"), []byte(htlc.LockAddress), []byte(htlc.Sender), []byte(uint64ToString(htlc.Amount)), []byte(preImage)}
resPonse := stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
...
// 更新状态为已退款
htlc.State = Refund
...
}

退回资产是通过哈希时间锁定交易id和哈希原像,检查交易是否过期且状态是否为已锁定,检查通过再调用账户资产链码的资产转移接口,完成资产从中间账户到交易发送者账户的转移,之后再更新状态为已退款。

/**
*args[0]:id,哈希时间锁定交易id。
*/
func (h *HTLCChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 根据id查询对应的HTLC并返回
key := fmt.Sprintf(HTLCPrefix, id)
htlcByte, err := stub.GetState(key)
...
return shim.Success(htlcByte)
...
}

查询哈希时间锁定信息通过哈希时间锁定交易id来查看该笔哈希时间锁定交易的信息。

Ethereum上面的实现

以太坊上的哈希时间锁定合约主要包含以下4个功能:资产锁定、提取资产、退回资产以及查询锁定信息。

/**
* @dev 发送者设置哈希时间锁来存储ETH以及其锁定时间。
*
* @param _receiver ETH接收者。
* @param _hashlock 基于sha256的哈希时间锁。
* @param _timelock 过期是的时间戳,在此之间之后若ETH还未被接收者提取,可以被发送者取回。
* @return htlcId 资产被锁定的在的HTLC的Id。之后的调用会需要。
*/
function newHTLC(address payable _receiver, bytes32 _hashlock, uint _timelock)
external
payable
fundsSent
futureTimelock(_timelock)
returns (bytes32 htlcId)
{
htlcId = sha256(abi.encodePacked(msg.sender, _receiver, msg.value, _hashlock, _timelock));
// Reject if a contract already exists with the same parameters. The
// sender must change one of these parameters to create a new distinct
// contract.
if (haveContract(htlcId))
revert("Contract already exists");
contracts[htlcId] = LockHTLC(msg.sender, _receiver, msg.value, _hashlock, _timelock, false, false, '0x0');
emit LogHTLCNew(htlcId, msg.sender, _receiver, msg.value, _hashlock, _timelock);
}

资产锁定操作需要接收者地址、哈希值、时间值、以及锁定的资产(ETH)数量,其中哈希值和时间值作为资产锁定的约束,即相关资产接收者需要在此时间值代表的时间之前提供正确的哈希原像才可以提取锁定的资产。资产发送者执行此操作会生成资产锁定id,并把资产发送者、接收者、锁定资产数量、哈希值、时间值、以及锁定id记录在日志中。

/**
* @dev 接收者一旦知道时间锁原像,会调用此方法提取锁定资产。
*
* @param _htlcId HTLC的Id。
* @param _preimage 哈希锁原像,sha256(_preimage) 等于哈希锁。
* @return bool 成功返回true。
*/
function withdraw(bytes32 _htlcId, bytes calldata _preimage)
external
contractExists(_htlcId)
hashlockMatches(_htlcId, _preimage)
withdrawable(_htlcId)
returns (bool)
{
LockHTLC storage c = contracts[_htlcId];
c.preimage = _preimage;
c.withdrawn = true;
c.receiver.transfer(c.amount);
emit LogHTLCWithdraw(_htlcId);
return true;
}

提取资产操作需要资产锁定id以及哈希原像,资产接收者在限定时间内执行此操作可以提取资产并且之前的资产锁定日志状态会被更新,资产提取也会记录在日志中;

/**
* @dev 如果时间锁过期,发送者调用此方法取回锁定的资产。
*
* @param _htlcId 锁定资产的HTLC的Id
* @return bool 成功返回true。
*/
function refund(bytes32 _htlcId)
external
contractExists(_htlcId)
refundable(_htlcId)
returns (bool)
{
LockHTLC storage c = contracts[_htlcId];
c.refunded = true;
c.sender.transfer(c.amount);
emit LogHTLCRefund(_htlcId);
return true;
}

退回资产操作需要资产锁定id,资产发送者在锁定时间过期后执行此操作会退回锁定的资产到自身账户,相关的退款操作也会记录在日志中。

/**
* @dev 获取HTLC的细节。
* @param _htlcId HTLC的Id。
* @return 所有LockHTLC的参数。
*/
function getContract(bytes32 _htlcId)
public
view
returns (
address sender,
address receiver,
uint amount,
bytes32 hashlock,
uint timelock,
bool withdrawn,
bool refunded,
bytes memory preimage
)
{
if (haveContract(_htlcId) == false){
bytes memory pi = '0x0';
return (address(0), address(0), 0, 0, 0, false, false, pi);
}
LockHTLC storage c = contracts[_htlcId];
return (
c.sender,
c.receiver,
c.amount,
c.hashlock,
c.timelock,
c.withdrawn,
c.refunded,
c.preimage
);
}

查询哈希锁定信息需要资产锁定id并返回该笔资产锁定信息。

Fabric与Ethereum基于HTLC的跨链流程

跨链演示

跨链演示背景说明:假设有两个用户Alice和Bob,Alice在fabric网络上面有1000个资产,Bob没有资产,Alice在以太坊上面没有ETH,Bob在以太坊上面有ETH。现在Alice想用fabric网络上面的50个资产换1.5个ETH。

他们之间通过哈希锁定跨链交换资产流程主要包含以下两步[5]:

环境准备

--------------------Create Alice Account on Fabric--------------------
INPUT:
Alice Account:  alice
Alice PassWord:  alicepasswd
OUTPUT:
Successfully Created Account
--------------------Create Bob Account on Fabric--------------------
INPUT:
Bob Account:  alice
Bob PassWord:  alicepasswd
OUTPUT:
Successfully Created Account
--------------------Using Faucet to Recharge 1000 Assets 2 Alice on Fabric--------------------
INPUT:
Alice Account:  alice
Assets Amount:  1000
OUTPUT:
Transfer Succeeds
--------------------Query Alice Account on Fabric--------------------
INPUT:
Alice's Account Address:  alice
OUTPUT:
Name:  alice
Amount:  1000
--------------------Query Bob Account on Fabric--------------------
INPUT:
Bob's Account Address:  bob
OUTPUT:
Name:  bob
Amount:  0
--------------------Bob Deploy Eth Htlc Contract--------------------
INTPUT:
1. Bob's private key; 2. HTLC contract Abi; 3. HTLC contract Bytes Code
OUTPUT:
New Contract Address:  0x1022498a83Aca1AEA280e320bcb10f1d301dB5D5
--------------------Transfer a few Eth to Alice for Paying Transaction Service Fee--------------------
INPUT:
From:  0x93ee701C44f9aa98086685c3AC5810f79762202d
To:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
Value:  0.01Eth
gasLimit:  150000
OUTPUT:
Successfully Transferred Fee 2 Alice, Transaction Hash Is:  0xcb3208ca5b0b22607d91e008c1eb1bf93e37e77ff76480b197616755ae5cb5f2
--------------------Get Alice's Balance on Ethereum--------------------
INPUT:
Alice's Address:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
OUTPUT:
Alice's Balance: 0.01
--------------------Get Bob's Balance on Ethereum--------------------
INPUT:
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Bob's Balance:  119.99

HTLC流程

--------------------Create Mid Account--------------------
INPUT:
sender:  alice
receiver:  bob
preimage:  rootroot
OUTPUT:
Successfully Created MidAccount:  alice0
HashLock:  0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
--------------------Lock Fabric Assets--------------------
INPUT:
Alice's Account:  alice
Bob's Account:  bob
Amount Fabric Assets to be Locked:  50
Expiration Duration:  30000000000
HashLock:  0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
AlicePasswd:  alicepasswd
MidAccount:  alice0
OUTPUT:
Successfully Locked Fabric Assets, HTLC ID:  aba736cb52e6fdfd37d539605574e0f000216d026085a3f5f1d07a4f2406b192
--------------------Bob Lock Eth Asset--------------------
INPUT:
hashlock:  0x0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
expiration timestamp:  1632336156621
Amount of ETH(1ETH = 10^18wei) to be locked:  1.5
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Eth Has Been Locked. Generated HTLC ID Is:  0x0cddac0c8ca3e6f37367ea2ef6f5607c62c6886f098515543d4cfbfb80f69284
--------------------Alice Withdraw Eth--------------------
INPUT:
ETH HTLC ID:  0x0cddac0c8ca3e6f37367ea2ef6f5607c62c6886f098515543d4cfbfb80f69284
PreImage Bytes:  0x726f6f74726f6f74
Alice's Address and Private Key
OUTPUT:
Successfully Withdrew Eth, Preimage Hex Is:  0x726f6f74726f6f74
Convert Preimage Hex To String:  rootroot
--------------------Query Alice's Balance on Ethereum--------------------
INPUT:
Alice's Address:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
OUTPUT:
Alice's Balance: 1.5099999999809461
--------------------Query Bob's Balance on Ethereum--------------------
INPUT:
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Bob's Balance:  192.4900000000190539
--------------------Withdraw Fabric Assets--------------------
INPUT:
Fabric HTLC ID:  aba736cb52e6fdfd37d539605574e0f000216d026085a3f5f1d07a4f2406b192
preimage:  rootroot
OUTPUT: Successfully Received HTLC Assets.
--------------------Query Alice's Amount on Fabric--------------------
INPUT:
Alice's Account:  alice
OUTPUT:
Alice's Amount: 950
--------------------Query Bob's Amount on Fabric--------------------
INPUT:
Bob's Account:  bob
OUTPUT:
Bob's Amount:  50

总结

哈希时间锁定的应用场景只适用于资产或者Token的转移,比较适用于公链带有原生Token的领域,对于不包含资产托管账户(例如Fabric)的区块链需要借助智能合约来构建账户概念。

引用

[1] https://lightning.network/

[2] https://wecross.readthedocs.io/zh_CN/latest/docs/routine/htlc.html

[3] https://www.chainnews.com/articles/365768981629.htm

[4] https://wenku.baidu.com/view/e5eaca43900ef12d2af90242a8956bec0875a577.html?fr=search-1-income2-psrec1&fixfr=7VXqrFWY1qNkjjTZzZW7RQ%3D%3D

[5] https://github.com/ehousecy/htlc-samples

HTLC跨链

跨链需求

​ 随着区块链技术的发展,各种具有不同特点,不同应用场景的区块链比如比特币、以太坊等公链以及私链和联盟链大量共存,它们之间相互独立,进行数据通信和价值转移仍面临挑战,区块链孤岛现象十分严重。为了解决区块链孤岛,实现链与链之间互联互通,进行价值转移,就必须实现跨链技术。就连银保监会陈伟刚也曾说过:“区块链的跨链需求会越来越多,因为区块链之一的联盟链,只是在自己的行业里应用,效益就得不到最优,更多的是行业与行业之间的联盟链跨链,所以跨链需求会成为区块链的一个拓展方向”。

​ 目前,主流的跨链技术方案主要有三种,分别是哈希时间锁定、公证人机制和中继链方式。本篇主要分析实现哈希时间锁定跨链技术方案。哈希时间锁定本质是一种智能合约,其最先出现于2013年的BitcoinTalk论坛的一次讨论中,最早在闪电网络[1]中实现。

哈希时间锁的简单介绍

哈希时间锁是基于hash锁和时间锁来实现的。

  1. hash锁 hash算法是一个可以将任意长度的输入内容以一个固定长度输出的算法,输入内容称为原始值,输出内容称为hash值,hash算法可以保证: a. 输入内容一致的情况下,输出内容也是相同的; b. 从输出内容不能反向推导出输入内容。 基于hash算法的特点,我们可以将hash值看成一把锁,而原始值则是唯一可以解开这把锁的钥匙。
  2. 时间锁 时间锁比较简单,时间锁上会记录一个时间点,即使有正确的钥匙,也必须在此时间点前才能打开,过时则无法打开。 介绍完哈希时间锁的以后,我们来看下哈希时间锁是如果实现跨链的。

    哈希时间锁定原理

​ 哈希时间锁定是基于哈希锁和时间锁来实现跨链。假如有两条区块链ChainA和ChainB,每个链的原生资产分别为a、b,两个用户Alice和Bob,Alice有资产a,Bob有资产b,Alice与Bob想交换资产,那么他们可以使用以下的方式来实现:

  1. Alice选取一个秘密随机数S,计算出S的哈希值H(S),Alice将H(S)发送给Bob,然后Alice指定一个时间点T1。接着Alice在ChainA上创建资产锁定合约,合约执行以下步骤:
    • 使用hash值H(S)将自己在ChainA上的资产a加上哈希锁;
    • 使用时间点T1将自己在ChainA上资产a加上时间锁; 只有获得S并且在时间点T1之前才将资产a转移走,如果到时间点T1后仍未解锁,则允许撤销锁定,且不会发生资产转移。
  2. Alice将H(S)发送给Bob,Bob基于H(S)和一个小于T1的时间点T2在ChainB上创建资产锁定合约,合约执行以下步骤:
    • 使用hash值H(S)将自己在ChainB上的资产b加上哈希锁;
    • 使用时间点T2将自己在ChainB上资产b加上时间锁; 只有拥有S并且在时间点T2之前才将资产b转移走,如果到时间点T2后仍未解锁,则b资产超时锁定。
  3. Alice使用S在时间点T2前调用ChainB上的智能合约解锁资产b,并将资产转移给自己。
  4. 因为Alice解锁了资产b,所以Bob也获得S的值,Bob使用S在时间点T1前解锁资产a,并将资产转移给自己。

通过以上的过程Alice和Bob就使用哈希时间锁在两条链上完成了资产转移。

适用场景

​ 虽然哈希时间锁定可以完成跨链,但它仅适用于不同区块链间的资产价值交换,且使用它的区块链网络需要如下三点:第一必须要有账户资产,第二必须通过可编程智能合约建立信任,第三交易双方必须在两种区块链网络中拥有各自的资产托管账户。哈希时间锁作为跨链资产交换解决方案,目前应用于WeCross[2]、闪电网络、Interledger、雷电网络、Sprites 通道等[3]。除此之外还在证券结算、去中心化交易所[4]以及央行数字货币跨境支付得到广泛引用。

HTLC在Fabric与Ethereum间的跨链探索

​ 因为哈希时间锁定不适用于fabric等没有资产的联盟链,所以我们需要先让我们的fabric网络拥有账户资产,为此我们需要实现账户资产链码,再实现哈希时间锁定链码,所以fabric网络需要账户资产和哈希时间锁定两种链码。对于以太坊,我们只需要编写一个哈希时间锁定合约即可。

Fabric上面的实现

账户资产链码

type Account struct {
Address  string      `json:"address"`     // 账户地址
Amount   uint64      `json:"amount"`      // 账户资产余额
Passwd   string      `json:"passwd"`      // 账户密码
Sequence uint64      `json:"sequence"`    // 账户序列号,用于创建中间账户
Type     int         `json:"type"`        // 账户类型,0为中间账户1为普通账户
TransferTo [2]string `json:"transfer_to"` // 中间账户资产能够转出的地址,为哈希交易的双方地址
}

Account结构体为账户资产模型,其中 Amount 为资产金额, Sequence 序列号主要用于创建中间账户, Type 为账户类型, 0 为中间账户, 1 为普通账户, TransferTo 表示该账户资产能够转入的账户地址,当为普通账户时为空,为中间账户时是hash交易的双方地址。

Account提供一个转账方法:

/**
*to:接收者账户
*amount:转账金额
*/
func (from *Account) Transfer(stub shim.ChaincodeStubInterface, to *Account, amount uint64) error {
sendKey := fmt.Sprintf(AccountPrefix, from.Address)
from.Amount = safeSub(from.Amount, amount)
receiverKey := fmt.Sprintf(AccountPrefix, to.Address)
to.Amount = safeAdd(to.Amount, amount)
...
}

账户结构体的转账方法,发送者账户减少资产接收者账户增加资产,在分别保存发送者账户和接收者账户。

账户资产链码主要提供4个接口,分别是中间用户注册、用户注册、资产转账和用户查询。

/**
*args[0]:address,中间账户地址。
*args[1]:passwd,中间账户的密码或者密码的哈希值。
*args[2]:flag,标志位,为hash表示为passwd为中间账户的密码的哈希值。
*args[3]:sender,哈希时间锁交易的发送者。
*args[4]:receiver,哈希时间锁交易的接收者。
*/
func (a *AccountAssert) createMidAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 计算passwd的哈希值,以存储
if flag == "hash" {
pw = passwd
} else {
sha256Passwd := sha256.Sum256([]byte(passwd))
pw = hex.EncodeToString(sha256Passwd[:])
}
transferTo := [2]string{sender, receiver}
acc := Account{
Address:  address,
Amount:   0,
Passwd:   pw,
Sequence: 0,
Type: MidAccount,
TransferTo: transferTo,
}
...
}

中间用户注册需要5个参数,账户地址address、账户密码passwd、账户标志位flag、哈希时间锁交易发送者sender和哈希时间锁交易接收者receiver。中间账户的密码就是哈希时间锁定交易里面的哈希原像,因为可能为后创建哈希时间锁的账户只能知道哈希值,所以通过标志位flag区分passwd是哈希原像还是哈希值。

/**
*args[0]:address,普通账户地址。
*args[1]:passwd,普通账户的密码。
*/
func (a *AccountAssert) createAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
passwd := args[1]
// 计算passwd的哈希值,以存储
sha256Passwd := sha256.Sum256([]byte(passwd))
acc := Account{
Address:  address,
Amount:   0,
Passwd:   hex.EncodeToString(sha256Passwd[:]),
Sequence: 0,
Type: GenAccount,
TransferTo: [2]string{}, // 普通账户,TransferTo为空值
}
...
}

用户注册需要传入地址address,密码passwd,密码存储的是哈希值。

/**
*args[0]:from,交易转出方地址。
*args[1]:to,交易转入方地址。
*args[2]:amount,转账金额。
*args[3]:passwd,交易转出方账户密码。
*/
func (a *Account) transfer(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 得到交易转出方账户
fromKey := fmt.Sprintf(AccountPrefix, from)
senderInfo, err := stub.GetState(fromKey)
sender := &Account{}
err = json.Unmarshal(senderInfo, sender)
...
// 验证交易转出方账户密码是否正确
sha256Passwd := sha256.Sum256([]byte(passwd))
if strings.Compare(sender.Passwd, hex.EncodeToString(sha256Passwd[:])) != 0 {
return shim.Error("sender account passwd is error")
}
...
// 交易转出方是中间账户,判断转入方地址是否正确,保证中间账户资产的安全性
if sender.Type == MidAccount {
if sender.TransferTo[0] != to && sender.TransferTo[1] != to {
return shim.Error("mid account not can transfer to other account")
}
}
...
// 调用账户的转账方法执行转账操作
err = sender.Transfer(stub, receiver, amount)
...
}

资产转账需要传入发送者from,接收者to,金额amount和发送者密码passwd四个参数。首先判断发送者是否存在,再判断账户密码是否正确,接着再判断接收者是否存在以及发送者金额是否充足,当发送者是中间账户还要判断接收者地址的正确性,这些都校验通过最后在在发送者账户执行减法操作,在接收者账户执行加法操作,以完成转账。

/**
*args[0]:address,交易转出方地址。
*/
func (a *AccountAssert) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
key := fmt.Sprintf(AccountPrefix, address)
accByte, err := stub.GetState(key)
...
}

账户查询只需要传入账户地址,账户存在就返回账户信息,不存在就返回账户不存在。

HTLC链码

type HTLC struct {
Sender      string    `json:"sender"`
Receiver    string    `json:"receiver"`
Amount      uint64    `json:"amount"`
HashValue   string    `json:"hash_value"`
TimeLock    int64     `json:"time_lock"`
PreImage    string    `json:"pre_image"`
LockAddress string    `json:"lock_address"`
State       HTLCState `json:"state"`
}

HTLC结构体是哈希时间锁定的交易数据结构, HashValuePreImage 的sha256哈希值, LockAddress 就是用户注册的中间账户地址, PreImage 为该中间账户的密码,状态 State 共分为已锁定 HashLOCK 、已领取 Received 和已退款 Refund 共三种。

HTLC链码主要有创建中间账户、创建哈希时间锁定、领取资产、退回资产和查询哈希时间锁定信息共5个功能。

/**
*args[0]:sender,哈希时间锁定交易发送者。
*args[1]:preImage,哈希时间锁定交易的哈希原像或者是原像的哈希值。
*args[2]:flag,标志位,为hash表示preImage为原像的哈希值。
*args[3]:receiver,哈希时间锁定交易接收者。
*/
func (h *HTLCChaincode) createMidAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 中间账户地址以发送者地址+发送者的序列号方式创建的
midAddress := senderAccount.Address + uint64ToString(senderAccount.Sequence)
...
// 调用账号资产链码的创建中间账户接口
trans = [][]byte{[]byte("registermidaccount"), []byte(midAddress), []byte(preImage), []byte(flag), []byte(sender), []byte(receiver)}
resPonse = stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
if resPonse.Status != shim.OK {
return shim.Error("craete htlc register mid account error: " + resPonse.Message)
}
...
// 返回中间账户地址和哈希时间锁定交易的哈希值
respon := ResponseMidAccount{}
respon.Address = midAddress
if flag == "hash" {
respon.Hash = preImage
} else {
hashByte := sha256.Sum256([]byte(preImage))
respon.Hash = hex.EncodeToString(hashByte[:])
}
responByte, err := json.Marshal(respon)
...
return shim.Success(responByte)
}

创建中间账户需要传入交易发送者地址,哈希原像(哈希原像为中间账户的密码)或者哈希值,标志位和交易接收者地址。它主要调用账户资产链码的中间用户注册接口,返回的是中间账户地址和哈希时间锁定的哈希值。

/**
*args[0]:sender,哈希时间锁定交易发送者地址。
*args[1]:receiver,哈希时间锁定交易接收者地址。
*args[2]:amount,哈希时间锁定交易金额。
*args[3]:timelock,哈希时间锁定交易的时间锁。
*args[4]:hashValue,哈希时间锁交易哈希值。
*args[5]:passwd,哈希时间锁定交易发送者账户密码。
*args[6]:midaddress,哈希时间锁定交易的锁定地址。
*/
func (h *HTLCChaincode) createHash(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 调用账户资产链码的转账接口,完成资产从发送者账户到锁定账户的转移
trans = [][]byte{[]byte("transfer"), []byte(sender), []byte(midaddress), []byte(amountStr), []byte(passwd)}
resPonse = stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
if resPonse.Status != shim.OK {
return shim.Error("craete htlc transfer mid  account error:" + resPonse.Message)
}
...
htlc := HTLC{
Sender:      sender,
Receiver:    receive,
Amount:      amount,
HashValue:   hashValue,
TimeLock:    timeLock,
PreImage:    "", // 先设置为空,在对方领取资产的时候在给值
LockAddress: midaddress,
State:       HashLOCK,
}
htlcByte, err := json.Marshal(htlc)
idByte := sha256.Sum256(htlcByte)
id := hex.EncodeToString(idByte[:])
...
// 返回id
return shim.Success([]byte(id))
...
}

创建哈希时间锁定交易需要发送者地址、接收者地址、转账数额、时间锁、哈希值、发送者账户密码和中间账户地址。哈希值和中间账户地址就是上一步创建中间账户时的返回值。它主要调用账户资产链码的资产转账接口,完成资产从发送者账户到中间账户的转移,返回哈希时间锁定交易id。

/**
*args[0]:id,哈希时间锁定交易id。
*args[1]:preImage,哈希时间锁定的哈希原像。
*/
func (h *HTLCChaincode) withdraw(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 状态必须已锁定
if htlc.State != HashLOCK {
return shim.Error("this htlc transaction state is error")
}
// 时间锁不能过期
if htlc.TimeLock < time.Now().Unix() {
return shim.Error("time is expirate")
}
// 调用账户资产链码的转账接口,完成资产从中间账户到交易接收者账户的转移
trans := [][]byte{[]byte("transfer"), []byte(htlc.LockAddress), []byte(htlc.Receiver), []byte(uint64ToString(htlc.Amount)), []byte(preImage)}
resPonse := stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
...
// 对哈希时间锁定交易的哈希原像进行赋值并把状态修改成已领取
htlc.PreImage = preImage
htlc.State = Received
...
}

领取资产需要哈希时间锁定交易id和哈希原像(中间账户密码),首先判断该哈希时间锁定交易是否存在,再判断哈希时间锁定状态是否是已锁定,然后判断时间锁是否超时,都检查通过之后再调用账户链码资产的资产转移接口,完成资产从中间账户到接收者账户的转移并更新哈希原像及状态。

/**
*args[0]:id,哈希时间锁定交易id。
*args[1]:preImage,哈希时间锁定的哈希原像。
*/
func (h *HTLCChaincode) refund(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 时间锁需要过期
if htlc.TimeLock > time.Now().Unix() {
return shim.Error("time is not expirate")
}
// 状态要已锁定
if htlc.State != HashLOCK {
return shim.Error("this htlc transaction state is error")
}
// 调用账户资产链码的转账接口,完成资产从中间账户到交易发送者账户的转移
trans := [][]byte{[]byte("transfer"), []byte(htlc.LockAddress), []byte(htlc.Sender), []byte(uint64ToString(htlc.Amount)), []byte(preImage)}
resPonse := stub.InvokeChaincode(AccountChainCodeName, trans, AccountChainCodeChannel)
...
// 更新状态为已退款
htlc.State = Refund
...
}

退回资产是通过哈希时间锁定交易id和哈希原像,检查交易是否过期且状态是否为已锁定,检查通过再调用账户资产链码的资产转移接口,完成资产从中间账户到交易发送者账户的转移,之后再更新状态为已退款。

/**
*args[0]:id,哈希时间锁定交易id。
*/
func (h *HTLCChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
...
// 根据id查询对应的HTLC并返回
key := fmt.Sprintf(HTLCPrefix, id)
htlcByte, err := stub.GetState(key)
...
return shim.Success(htlcByte)
...
}

查询哈希时间锁定信息通过哈希时间锁定交易id来查看该笔哈希时间锁定交易的信息。

Ethereum上面的实现

以太坊上的哈希时间锁定合约主要包含以下4个功能:资产锁定、提取资产、退回资产以及查询锁定信息。

/**
* @dev 发送者设置哈希时间锁来存储ETH以及其锁定时间。
*
* @param _receiver ETH接收者。
* @param _hashlock 基于sha256的哈希时间锁。
* @param _timelock 过期是的时间戳,在此之间之后若ETH还未被接收者提取,可以被发送者取回。
* @return htlcId 资产被锁定的在的HTLC的Id。之后的调用会需要。
*/
function newHTLC(address payable _receiver, bytes32 _hashlock, uint _timelock)
external
payable
fundsSent
futureTimelock(_timelock)
returns (bytes32 htlcId)
{
htlcId = sha256(abi.encodePacked(msg.sender, _receiver, msg.value, _hashlock, _timelock));
// Reject if a contract already exists with the same parameters. The
// sender must change one of these parameters to create a new distinct
// contract.
if (haveContract(htlcId))
revert("Contract already exists");
contracts[htlcId] = LockHTLC(msg.sender, _receiver, msg.value, _hashlock, _timelock, false, false, '0x0');
emit LogHTLCNew(htlcId, msg.sender, _receiver, msg.value, _hashlock, _timelock);
}

资产锁定操作需要接收者地址、哈希值、时间值、以及锁定的资产(ETH)数量,其中哈希值和时间值作为资产锁定的约束,即相关资产接收者需要在此时间值代表的时间之前提供正确的哈希原像才可以提取锁定的资产。资产发送者执行此操作会生成资产锁定id,并把资产发送者、接收者、锁定资产数量、哈希值、时间值、以及锁定id记录在日志中。

/**
* @dev 接收者一旦知道时间锁原像,会调用此方法提取锁定资产。
*
* @param _htlcId HTLC的Id。
* @param _preimage 哈希锁原像,sha256(_preimage) 等于哈希锁。
* @return bool 成功返回true。
*/
function withdraw(bytes32 _htlcId, bytes calldata _preimage)
external
contractExists(_htlcId)
hashlockMatches(_htlcId, _preimage)
withdrawable(_htlcId)
returns (bool)
{
LockHTLC storage c = contracts[_htlcId];
c.preimage = _preimage;
c.withdrawn = true;
c.receiver.transfer(c.amount);
emit LogHTLCWithdraw(_htlcId);
return true;
}

提取资产操作需要资产锁定id以及哈希原像,资产接收者在限定时间内执行此操作可以提取资产并且之前的资产锁定日志状态会被更新,资产提取也会记录在日志中;

/**
* @dev 如果时间锁过期,发送者调用此方法取回锁定的资产。
*
* @param _htlcId 锁定资产的HTLC的Id
* @return bool 成功返回true。
*/
function refund(bytes32 _htlcId)
external
contractExists(_htlcId)
refundable(_htlcId)
returns (bool)
{
LockHTLC storage c = contracts[_htlcId];
c.refunded = true;
c.sender.transfer(c.amount);
emit LogHTLCRefund(_htlcId);
return true;
}

退回资产操作需要资产锁定id,资产发送者在锁定时间过期后执行此操作会退回锁定的资产到自身账户,相关的退款操作也会记录在日志中。

/**
* @dev 获取HTLC的细节。
* @param _htlcId HTLC的Id。
* @return 所有LockHTLC的参数。
*/
function getContract(bytes32 _htlcId)
public
view
returns (
address sender,
address receiver,
uint amount,
bytes32 hashlock,
uint timelock,
bool withdrawn,
bool refunded,
bytes memory preimage
)
{
if (haveContract(_htlcId) == false){
bytes memory pi = '0x0';
return (address(0), address(0), 0, 0, 0, false, false, pi);
}
LockHTLC storage c = contracts[_htlcId];
return (
c.sender,
c.receiver,
c.amount,
c.hashlock,
c.timelock,
c.withdrawn,
c.refunded,
c.preimage
);
}

查询哈希锁定信息需要资产锁定id并返回该笔资产锁定信息。

Fabric与Ethereum基于HTLC的跨链流程

跨链演示

跨链演示背景说明:假设有两个用户Alice和Bob,Alice在fabric网络上面有1000个资产,Bob没有资产,Alice在以太坊上面没有ETH,Bob在以太坊上面有ETH。现在Alice想用fabric网络上面的50个资产换1.5个ETH。

他们之间通过哈希锁定跨链交换资产流程主要包含以下两步[5]:

环境准备

--------------------Create Alice Account on Fabric--------------------
INPUT:
Alice Account:  alice
Alice PassWord:  alicepasswd
OUTPUT:
Successfully Created Account
--------------------Create Bob Account on Fabric--------------------
INPUT:
Bob Account:  alice
Bob PassWord:  alicepasswd
OUTPUT:
Successfully Created Account
--------------------Using Faucet to Recharge 1000 Assets 2 Alice on Fabric--------------------
INPUT:
Alice Account:  alice
Assets Amount:  1000
OUTPUT:
Transfer Succeeds
--------------------Query Alice Account on Fabric--------------------
INPUT:
Alice's Account Address:  alice
OUTPUT:
Name:  alice
Amount:  1000
--------------------Query Bob Account on Fabric--------------------
INPUT:
Bob's Account Address:  bob
OUTPUT:
Name:  bob
Amount:  0
--------------------Bob Deploy Eth Htlc Contract--------------------
INTPUT:
1. Bob's private key; 2. HTLC contract Abi; 3. HTLC contract Bytes Code
OUTPUT:
New Contract Address:  0x1022498a83Aca1AEA280e320bcb10f1d301dB5D5
--------------------Transfer a few Eth to Alice for Paying Transaction Service Fee--------------------
INPUT:
From:  0x93ee701C44f9aa98086685c3AC5810f79762202d
To:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
Value:  0.01Eth
gasLimit:  150000
OUTPUT:
Successfully Transferred Fee 2 Alice, Transaction Hash Is:  0xcb3208ca5b0b22607d91e008c1eb1bf93e37e77ff76480b197616755ae5cb5f2
--------------------Get Alice's Balance on Ethereum--------------------
INPUT:
Alice's Address:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
OUTPUT:
Alice's Balance: 0.01
--------------------Get Bob's Balance on Ethereum--------------------
INPUT:
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Bob's Balance:  119.99

HTLC流程

--------------------Create Mid Account--------------------
INPUT:
sender:  alice
receiver:  bob
preimage:  rootroot
OUTPUT:
Successfully Created MidAccount:  alice0
HashLock:  0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
--------------------Lock Fabric Assets--------------------
INPUT:
Alice's Account:  alice
Bob's Account:  bob
Amount Fabric Assets to be Locked:  50
Expiration Duration:  30000000000
HashLock:  0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
AlicePasswd:  alicepasswd
MidAccount:  alice0
OUTPUT:
Successfully Locked Fabric Assets, HTLC ID:  aba736cb52e6fdfd37d539605574e0f000216d026085a3f5f1d07a4f2406b192
--------------------Bob Lock Eth Asset--------------------
INPUT:
hashlock:  0x0242c0436daa4c241ca8a793764b7dfb50c223121bb844cf49be670a3af4dd18
expiration timestamp:  1632336156621
Amount of ETH(1ETH = 10^18wei) to be locked:  1.5
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Eth Has Been Locked. Generated HTLC ID Is:  0x0cddac0c8ca3e6f37367ea2ef6f5607c62c6886f098515543d4cfbfb80f69284
--------------------Alice Withdraw Eth--------------------
INPUT:
ETH HTLC ID:  0x0cddac0c8ca3e6f37367ea2ef6f5607c62c6886f098515543d4cfbfb80f69284
PreImage Bytes:  0x726f6f74726f6f74
Alice's Address and Private Key
OUTPUT:
Successfully Withdrew Eth, Preimage Hex Is:  0x726f6f74726f6f74
Convert Preimage Hex To String:  rootroot
--------------------Query Alice's Balance on Ethereum--------------------
INPUT:
Alice's Address:  0x87CDaCDACBCF9e560336A4352B7E89646A4402D1
OUTPUT:
Alice's Balance: 1.5099999999809461
--------------------Query Bob's Balance on Ethereum--------------------
INPUT:
Bob's Address:  0x93ee701C44f9aa98086685c3AC5810f79762202d
OUTPUT:
Bob's Balance:  192.4900000000190539
--------------------Withdraw Fabric Assets--------------------
INPUT:
Fabric HTLC ID:  aba736cb52e6fdfd37d539605574e0f000216d026085a3f5f1d07a4f2406b192
preimage:  rootroot
OUTPUT: Successfully Received HTLC Assets.
--------------------Query Alice's Amount on Fabric--------------------
INPUT:
Alice's Account:  alice
OUTPUT:
Alice's Amount: 950
--------------------Query Bob's Amount on Fabric--------------------
INPUT:
Bob's Account:  bob
OUTPUT:
Bob's Amount:  50

总结

哈希时间锁定的应用场景只适用于资产或者Token的转移,比较适用于公链带有原生Token的领域,对于不包含资产托管账户(例如Fabric)的区块链需要借助智能合约来构建账户概念。

引用

[1] https://lightning.network/

[2] https://wecross.readthedocs.io/zh_CN/latest/docs/routine/htlc.html

[3] https://www.chainnews.com/articles/365768981629.htm

[4] https://wenku.baidu.com/view/e5eaca43900ef12d2af90242a8956bec0875a577.html?fr=search-1-income2-psrec1&fixfr=7VXqrFWY1qNkjjTZzZW7RQ%3D%3D

[5] https://github.com/ehousecy/htlc-samples

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 14分钟前
  • 阅读 ( 10 )
  • 学分 ( 0 )
深入浅出区块链
我还没有学会写个人说明!
上一篇

百度公开车窗调节方法相关专利 可提醒车主清洗车辆

下一篇

波音陷入困境的Starliner又遭遇延误 第二次无人试飞推迟到4月初

你也可能喜欢

评论已经被关闭。

插入图片