单公证人模式实现测试链间跨链入门教程(上)
Posted o_young17
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单公证人模式实现测试链间跨链入门教程(上)相关的知识,希望对你有一定的参考价值。
注:为方便表述,本文将Ropsten链的原生代币称为RETH,Rinkeby链的原生代币称为BETH
1.1什么是跨链
跨链(cross-chain),顾名思义,就是通过连接相对独立的区块链系统,实现资产、数据等的跨链互操作。
跨链的主要实现形式包括跨链资产互换和跨链资产转移。
跨链资产互换指将一条链上的资产兑换成等值的另一条链上的资产,每条链上的资产总量不变。跨链资产互换的一个简单例子如下:
Alice用1个ETH交换Bob的5个BNB,互换成功的结果应该是Alice的ETH地址收到Bob的5个BNB,Bob的ETH地址收到Alice的1个ETH。
跨链资产互换
跨链资产转移指将一条链上资产转移到另一条链上,原链上的资产锁定,另一条链上重新铸造等量等值的资产,每条链上的资产总值发生变化,但两条链的资产总值之和不变。跨链资产转移的一个简单例子如下:
Alice将以太坊上的10个ETH转移到币安链,则以太坊上的1个ETH被冻结,币安链上新生成10个WETH(等价替代品)。
跨链资产转移图示
1.2为什么要跨链?
突破底层公链性能和功能瓶颈。随着区块链网络的快速发展,性能逐渐成为制约区块链发展的重大瓶颈,通过将部分事务处理转移到侧链或链下能够提升区块链网络的性能。部分功能创新也可以通过侧链实现,从而保证主链的安全性。
实现跨链互操作。单一的区块链系统相对封闭,随着区块链技术的迅速发展,链与链之间的“互操作性”问题逐渐凸显。跨链互操作的具体应用场景包括但不限于跨链支付结算、非中心化交易所、跨链信息交互等。
1.3跨链的解决方案
目前跨链协议有四个类型:中心化或者多方签名的公证人形式,侧链或者中继形式,哈希时间锁,分布式私钥控制等密码学形式。
(1)公证人机制(Notaryschemes)
在公证人机制下,跨链双方共同引入一个共同信任的第三方作为中介,由这个共同信任的中介进行跨链消息的验证和转发。(2/3门限签名–公证人)
(2)侧链/中继(Sidechains/relays)
侧链技术是相对于主链的概念,侧链需要一份实现主链网络数据获取的智能合约,其中
包含侧链数据和主链数据切换机制的方法,通过智能合约使主链和其它侧链进行交互。
(3)哈希锁定(Hash-locking)
哈希锁定可以构建多个不同的链下支付通道,让这些通道一起形成一个网络。交易双方
的数目比较小的微支付可以通过一系列的链下协议完成,从而拓展主链的性能,同时实现跨链的目的。
(4)分布式私钥控制(Distributedprivatekeycontrol)
分布式私钥控制是基于密码学中的多方计算和门限密钥的一种技术,通过采用分布式节
点来控制区块链系统中各种资产的私钥,将数字资产的使用权和所有权进行分离,使得链上资产的控制权能够安全地转移到非中心化系统中,同时将原链上的资产映射到跨链中,实现不同区块链系统间的资产流通和价值转移。
2 实验场景设计
目标计划是区块链间信息交互之资产转移系统,简称资产跨链系统。以解决区块链之间信息孤岛问题为出发点,将该笼统抽象的问题具体至资产转移这一功能上,实现跨链方案的现实应用。跨链方案有四种:公证人方案,中继链/侧链方案,哈希锁定方案,分布式私钥方案。本跨链设计方案中采用的是单公证人方案。
当前区块链中的代币分为两种:①原生代币(NativeToken);②应用代币(token);
①原生代币:原生代币是指区块链系统正常运行所需要的,体现权益的代币,比如比特币、以太币。在某些共识中持有原生代币的数量可以成为算法中权重的一部分。
②应用代币:应用代币是指在一条区块链中,非原生代币的代币,就是应用代币。如常听到的ERC20代币,ERC666代币等。准确来说ERC20,ERC666代币,他不是一种代币,而是一种代币设计标准。任何人都可以参考这些设计标准去发行任意名称与数量的代币。但是是否有人认可你发行的代币的价值那就是另一回事了。
在资产跨链的过程中,需要了解跨链的实质:跨链的资产永远无法真正的从一条链(例:以太链)转移到另一条链(例:币安链),因此只能在将A链的资产锁定在一个特定的地址中,并在B链铸造与之对应数量的等价替代代币。
例如:我想将10个ETH从以太链转移到币安链中,因此我先将我的10个ETH转移到交易所的地址,交易所再在币安链中铸造对应的10个WETH,并转移到我在币安链上的地址中。由于交易使用智能合约,而智能合约部署在区块链上,任何人都可以进行审查,也可以确定币安链上的每一个WETH都在以太坊中有锁定的ETH与之锚定,即:币安链上的每一个WETH转移到币安链的交易所时,交易所能在以太坊上将等量的ETH提取归还。
资产兑换,转移的需求
随着越来越多区块链的出现,每一种链都有自己的原生代币。当越来越多人认可这条链时,这条链的原生代币的价值也会越来越高。当我们想购买这种新币但是手上没有现金只有数字货币时,资产转移与兑换的需求就应运而生。无论是将数字货币兑换成USDT后再跨链去购买新币,或者是在本链上先将数字货币兑换成新币再将资产跨链,这里将资产兑换后最终都需要进行跨链,才能真正将新币握在手里,而不是新币的等价替代品。当单一区块链无法满足规模扩张的需求,且新链需要时间搭建完善生态的情况时,各式各样的跨链桥在不同公链与L2间运行,解决了资金流转的问题,为用户带来了极大的便利。
3、实验模块
本实验采用单公证人模式实现跨链,合约部分使用solidity语言编写,编译器为REMIX。
在资产跨链的过程中,需要了解跨链的实质:跨链的资产永远无法真正的从一条链(例:以太链)转移到另一条链(例:币安链),因此只能在将A链的资产锁定在一个特定的地址中,并在B链铸造与之对应数量的等价替代代币。 因此在另一条链中需要使用ERC20合约进行铸币(产生与之对应的等量的代币)与销币(防止通货膨胀)。
跨链的过程是在两条链之间进行信息交互,我们已知区块链交易是不能主动触发的,需要被认为提交交易,区块链被动执行交易,因此两条区块链间无法主动形成互动,故需要一个第三方来执行这一步,这个第三方本文称之为监视器,是链下的代码,本文使用web3.js编写,目的是:1、监控链上合约发生的事情;2、往链上发送交易;
对于原生代币与ERC20代币有不同的转账与记录方法,因此执行过程需要将他们稍加区分一下。
在这个过程中我们将这个资产转移的合约称之为跨链桥,用于资产跨链。
综上,我们知道我们需要做的事情有: ERC20合约,原生代币合约,跨链桥合约,链下监控器
3.1ERC20合约
这是一个通用的ERC20合约,我们接下来根据需求对这个ERC20合约进行稍微的改动
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0;
interface IERC20 //ERC20标准
function totalSupply() external view returns (uint256); //总余额
function balanceOf(address account) external view returns (uint256);
// 查询账户余额
function allowance(address owner, address spender) external view returns (uint256);
// 给定一个委托人地址和一个被委托地址,返回被委托代币余额。
function approve(address spender, uint256 amount) external returns (bool);
// 指定一个被委托地址和委托代币数量,被委托地址可以在不超过委托数量的前提下多次从委托账户转移代币
// 被委托人代替委托人转帐,多用于交易所或公司
function transfer(address recipient, uint256 amount) external returns (bool); // 用户给别人转账
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
// 从一个账户到另一个账户,指定发送者,接收者和转移的代币数量
// 与approve结合使用。
// TODO:实现待定,路由与金库之间可能需要使用,再使用一种合约作为逻辑/工具,金库负责存放金额。
event Transfer(address indexed from, address indexed to, uint256 value); // 在成功转移(调用transfer或者transferFrom)后触发的事件,用于记录事件
event Approval(address indexed sender, address indexed spender, uint256 value);// 成功调用approve的事件日志。
contract ERC20Token is IERC20
string public name; // 代币名称
string public symbol; // 代币简称
uint8 public decimals; // 代币数量的小数点位数
// 以上三项都是ERC20代币的基本标准
uint256 private _totalSupply;
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowed;
// ERC20 合约用一个二维映射跟踪委托代币余额,其主键是代币所有者的地址,映射到被委托地址和对应的委托代币余额
// 第一个参数是金额所有者,第二个参数是所有者指定某人帮他代理资产,最后映射的值是代理的资产金额
constructor(uint256 initialSupply, string memory tokenName, string memory tokenSymbol) public
_totalSupply = initialSupply * 10**uint256(decimals); // 初始化代币总数
balances[msg.sender] = _totalSupply; // 创建者拥有所有代币
name = tokenName;
symbol = tokenSymbol;
function transfer(address recipient, uint256 amount) public override returns (bool) // override是函数重载的关键词
require(balances[msg.sender] >= amount, "token balance too low"); // 调用此函数的人有足够余额
balances[msg.sender] -= amount;
balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount); // 抛出事件日志
return true;
function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool)
// 理解成信托函数 找人代理理财
uint256 allowance = allowed[sender][msg.sender];
// 托管代币余额
require(allowance >= amount, "allowance too low");
require(balances[sender] >= amount, "token balance too low");
allowed[sender][msg.sender] -= amount;
balances[sender] -= amount;
balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
function approve(address spender, uint256 amount) public override returns (bool)
// spender就是你要委托的人,amout就是你要让他帮你代管理的资金
allowed[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
function allowance(address owner, address spender) public override view returns (uint256)
return allowed[owner][spender];
function balanceOf(address account) public override view returns (uint256)
return balances[account];
function totalSupply() public override view returns (uint256)
return _totalSupply;
3.2 改·ERC20 合约
这个合约内容就是我们接下来要用的了。
注:本文将Ropsten链的原生代币称为RETH,Rinkeby链的原生代币称为BETH,第三节中所有的合约部署都是发生在Ropsten上
假设从Rinkeby链将10BETH跨链到Ropsten链上,Ropsten是没有BETH的,因此需要写一个ERC20代币来代替Rinkeby链的BETH。
由于是跨链,在rinkeby锁定了10BETH,那么在ropsten就应该释放对应的BETH,而ropsten没有BETH,因此需要在ropsten链上铸造10BETH;同样的,当从ropsten提10BETH回到rinkeby时,也需要将对应的BETH销毁。故需要增加铸/销币功能,基于我们的需求,我们对ERC20代币进行了适当的改动,如下:
pragma solidity >=0.4.16 <0.7.0;
import "hardhat/console.sol";
interface IERC20 //ERC20标准
function totalSupply() external view returns (uint256); //总余额
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool); // 用户给别人转账
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 indexed value); // 在成功转移(调用transfer或者transferFrom)后触发的事件,用于记录事件
event Approval(address indexed sender, address indexed spender, uint256 value);// 成功调用approve的事件日志。
contract BEHT_ERC20Token is IERC20
string public name = "Rinkeby Substitute Ether"; // 代币名称
string public symbol = "BETH"; // 代币简称
uint8 public decimals = 18; // 代币数量的小数点位数
// 以上三项都是ERC20代币的基本标准
address public CrossChain_bridge;//用于铸币,只有跨链桥才可以调用这个函数进行铸币,也是我们初始化需要设置的参数
uint256 private _totalSupply ;
mapping(address => uint256) public balances;
mapping(address => mapping(address => uint256)) public allowed;
constructor(uint256 initialSupply, address Cross_Bridge ) public
// 初始化代币总数
_totalSupply = initialSupply * 10**uint256(decimals);
// 创建者拥有所有代币
// 此处初始化代币总数是为了实验其他功能,如果是原生代币的替代品此处应该为0,如果是普通的ERC20代币可以考虑初始化一定数量的代币
balances[msg.sender] = _totalSupply*10**uint256(decimals);
// 跨链桥合约地址
CrossChain_bridge = Cross_Bridge;
function transfer(address recipient, uint256 amount) public override returns (bool)
// transfer是msg.sender自己发送代币的函数 override是函数重载的关键词
require(balances[msg.sender] >= amount, "token balance too low");
// 调用此函数的人有足够余额
balances[msg.sender] -= amount;
balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
function transferFrom(address sender, address recipient, uint256 amount ) public override returns (bool)// 信托函数 找人代理理财
uint256 allowance = allowed[sender][msg.sender];// 托管代币余额
require(allowance >= amount,"allowance too low");
require(balances[sender]>=amount,"token balance too low");
allowed[sender][msg.sender] -= amount;
balances[sender] -= amount;
balances[recipient] += amount;
emit Transfer(sender,recipient,amount);// 抛出事件日志
return true;
function approve (address spender, uint256 amount ) public override returns (bool) //spender就是你要委托的人,amout就是你要让他帮你代管理的资金——此处应该填写KB_CrossChain_Bridge合约地址而不是该合约的owner的地址
require(balances[msg.sender]>=amount,"token balances too low");
allowed[msg.sender][spender] = amount;
emit Approval(msg.sender,spender,amount);
return true;
function crossED_transfer(address to, uint256 value) public only_brdige returns (bool) //只有跨链桥合约才可以调用
mine_ERC20(value);//铸币
transfer(to,value);//给目标合约转币并抛出日志
return true;
function mine_ERC20( uint256 value) internal returns (bool) //铸币合约 由于资产转移并不能实现真正的资产转移,因此只能生产一种代币作为转移
_totalSupply += value;
balances[msg.sender] += value;
return true;
function burnt ( uint256 value ) public returns (bool) // 销币合约
require(balances[msg.sender] >=value);
balances[msg.sender] -= value;
_totalSupply -= value;
return true;
function allowance (address owner, address spender) public override view returns (uint256)
return allowed[owner][spender];
function balanceOf(address account) public override view returns (uint256)
return balances[account];
function totalSupply() public override view returns (uint256)
return _totalSupply;
modifier only_brdige()
require ( msg.sender == CrossChain_bridge,"Only KB_CrossChain_Bridge can call this function.");
_;
3.3 NATIVE 代币合约
这个代币合约主要是为了记录原生代币的信息,用作备用或者未来拓展,实际用途在本次实验中较小
pragma solidity >=0.4.16 <0.7.0;
contract KETH
// 处理原生代币的合于代码逻辑
// 负责接受转账,提款,及抛出日志
string public name = "Ropsten Ether";
string public symbol = "RETH";
uint8 public decimals = 18;
address public owner;
constructor(address _owner) public
owner = _owner;
// event Approval(address indexed src, address indexed guy, uint wad );
event Transfer(address indexed src, address indexed dst, uint wad );
event Deposit(address src, address dst, uint wad );
event Withdrawal(address indexed dst, uint wad );
mapping (address => uint ) public balances;
// mapping (address => mapping(address=>uint) ) public allowance;
/*function() public payable
deposit();
*/
function deposit() public payable // 由于是原生代币,使用depositvalue:msg.value时就会将代币转到这个合约内了
//此时记录该人转入合约内多少钱,为下面转账做标记。此时钱实际存在了KETH合约中,ERC20合约用来记账?
balances[msg.sender] += msg.value;
emit Deposit(msg.sender,address(this),msg.value);
function mark(uint256 value) public only_bridge returns (bool)
balances[msg.sender] += value;
return true;
function reduce(uint256 value) public returns (bool)
balances[msg.sender] -= value;
return true;
function balanceOf(address account) public view returns (uint256)
return balances[account];
modifier only_bridge()
require (owner == msg.sender,"Only owner can call this function.");
_;
3.4 跨链桥合约
跨链桥合约的主要功能是实现跨链资产转移,因此包括本链跨往其他链的执行函数general_corss_transfer(),执行结束后会抛出日志 emit LogBridge_SwapOut( ERC20_CONTRACT_ADDRESS, symbol, FromChainID, msg.sender, toChainID, to, value ); 链下监控器监控跨链合约的这个日志信息,并解析日志内容,根据需求建立新的交易,然后打包发往目标链。目标链收到交易后先确定发件人是不是指定的公证人(此处使用modifier处理,见合约最底部),是的话执行接下来的转账操作等。
除了跨链外还包括了一些基本的操作,例如,增删可支持的区块链,可支持的代币;同链内的转账,往交易所存款(TODO:添加流动性,返还利息等,可以作为下一个版本的更新内容),提款等。
此示例部署在Ropsten上
pragma solidity >=0.4.16 <0.7.0;
import "hardhat/console.sol";
interface KChain_ERC20
function totalSupply() external view returns (uint256); //总余额
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function crossED_transfer(address to, uint256 value) external returns (bool);
function burnt ( uint256 value ) external returns (bool);
function mark(uint256 value) external returns (bool);
function reduce(uint256 value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed sender, address indexed spender, uint256 value);
contract KB_CrossChain_Bridge
uint64 public FromChainID = 3;
// 通过链id查到链名称
mapping(uint64 => string) public ChainID_TO_NAME;
// 查看某链的某币我是否支持跨链
mapping(uint64=>mapping(uint=> string)) public SYMBOL;
// 币种对应的合约
mapping(string=>address) public ERC20_CONTRACT;
//uint8 SYMBOL_NUMBER;
mapping(address=>mapping(string=>uint256)) public balance_of_deopsit;
address owner;//合约创建者
string NATIVE_COIN;
uint64 NATIVE_CHAINID=3;
event LogBirdge_Refund_COIN(address indexed src,address indexed dst,uint256 indexed value);
event LogBridge_SwapOut(address token, string symbol, uint64 fromChainID, address from, uint64 toChainID, address to, uint256 amount);区块链BaaS云服务(22)趣链BitXHub“跨链网关”
1. 架构
一种通用的链间通用传输协议IBTP,并配合网关+中继链的架构模式实现异构区块链跨链交互。
网关:解决跨链交易的获取和提交问题。
中继链结合IBTP:解决跨链交易的验证和路由问题。
1.1 跨链流程
执行跨链调用之前需要执行一些准备工作,包括应用链注册,验证规则注册以及跨链双方的业务链上依据我们的跨链合约撰写规则设置好相应的跨链合约。
step1. SDK 调用 具体的业务链A的合约方法;
step2. 合约方法被执行,抛出跨链事件T1;
step3. 业务链A的跨链网关监听到T1, 将其转换成IBTP结构,提交到中继链BitXHub上;
step4. BitXHub 依据相关规则对T1进行验证以及路由;
step5. 业务链B的跨链网关接受到T1并根据IBTP结构进行解析,转换成业务链B可识别的交易Tb,并将Tb提交到业务链B上进行执行。
1.2. 网关核心模块
以上是关于单公证人模式实现测试链间跨链入门教程(上)的主要内容,如果未能解决你的问题,请参考以下文章