Facebook币Libra学习-6.发行属于自己的代币Token案例(含源码)
在这个简短的概述中,我们描述了我们在eToro标记化资产背后实施技术的初步经验,即MoveIR语言中的(eToken),用于在Libra网络上进行部署。
Libra协议是一个确定性状态机,它将数据存储在版本化数据库中。使用新颖的领域特定语言:Move。Move允许可编程事务和模块重用代码和状态 - 类似于我们定义为智能合约。
目前,Libra不允许发布模块,这就是为什么eToken的源代码应该作为测试运行的原因。可以在此处找到此Move IR eToken实现的完整源代码。
此测试已在提交哈希中使用Libra版本执行#004d472。可以在此处找到存储库。
Move中间表示语言
随着Libra的发布,定义了一种名为Move的新的特定于域的语言。直到现在,Libra团队尚未提供更高级别的实施。随着该协议的最近宣布,一个名为'Move IR'的lanaguge的一个不太符合人体工程学的中间表示被释放
Move内化了内存所有权和借用的概念,与Rust的运作方式非常相似。然而,Move语言的新颖性是定义“资源”的方式。
“资源”是一种利用所有权模型的结构数据类型,但永远不能仅复制Move和借用。这是Move语言的核心功能,因为它保证没有定义的资源意外重复,从而消除了双重支出或重新发生攻击的可能性。
因此,“资源”的概念与数字资产概念很好地对应是明智的
eToken实施
modules: module Capability { // Capability is responsible for declaring an account's permissions.
// We define the notion of an owner, minter and blacklisted.
// Only the owner can assign blacklisted and minter capabilities. // The owner is defined by hardcoding its account before publishing the module. // ----------------------------------------------------------------- // Declare owner as a resource. It's only meant to be published once
resource Owner { } // Declare a resource that declares an address's capabilities.
// Every address using EToken will need to publish a resource themselves.
// However, only the owner can change its content.
resource T {
minter: bool,
blacklisted: bool,
} // Every account should execute this once before using Capability and EToken module.
// If the sender is the hardcoded owner, then owner capability is published.
// Reverts if already published
public publish() {
let sender: address;
sender = get_txn_sender(); // Publish owner capability if sender is the privileged account
// Uncomment the following line in production and use a real owner account: if (move(sender) == 0x0) {
if (true) {
// Always branch to here when testing, otherwise the test can't complete as the sender address is randomly chosen.
Self.grant_owner_capability();
} // Publish a new capability with no permissions.
move_to_sender<T>(T{ minter: false, blacklisted: false }); return;
} // Internal function that grants owner capability
grant_owner_capability() {
move_to_sender<Owner>(Owner {});
return;
} // Grants minter capability to receiver, but can only succeed if sender owns the owner capability.
public grant_minter_capability(receiver: address, owner_capability: &R#Self.Owner) {
let capability_ref: &mut R#Self.T; release(move(owner_capability)); // Pull a mutable reference to the receiver's capability, and change its permission.
capability_ref = borrow_global<T>(move(receiver));
*(&mut move(capability_ref).minter) = true; return;
} // Grants blacklist capability to receiver, but can only succeed if sender owns the owner capability.
public grant_blacklisted_capability(receiver: address, owner_capability: &R#Self.Owner) {
let capability_ref: &mut R#Self.T; release(move(owner_capability)); // Pull a mutable reference to the receiver's capability, and change its permission.
capability_ref = borrow_global<T>(move(receiver));
*(&mut move(capability_ref).blacklisted) = true; return;
} // This returns an immutable reference to the owner capability if it exists.
// Is used by the owner to show ownership to privileged functions.
// Reverts if owner capability does not exist.
public borrow_owner_capability(): &R#Self.Owner {
let sender: address;
let owner_capability_ref: &mut R#Self.Owner;
let owner_capability_immut_ref: &R#Self.Owner; sender = get_txn_sender();
owner_capability_ref = borrow_global<Owner>(move(sender));
owner_capability_immut_ref = freeze(move(owner_capability_ref)); return move(owner_capability_immut_ref);
} // This returns an immutable reference to the general capability if it exists.
// Should be used by every account to prove capabilities.
// Reverts if capability does not exist.
public borrow_capability(): &R#Self.T {
let sender: address;
let capability_ref: &mut R#Self.T;
let capability_immut_ref: &R#Self.T; sender = get_txn_sender();
capability_ref = borrow_global<T>(move(sender));
capability_immut_ref = freeze(move(capability_ref)); return move(capability_immut_ref);
} // Return whether the capability allows minting.
public is_minter(capability: &R#Self.T): bool {
let is_minter: bool;
is_minter = *(&move(capability).minter);
return move(is_minter);
} // Return true the capability is not blacklisted.
public is_not_blacklisted(capability: &R#Self.T): bool {
let is_blacklisted: bool;
is_blacklisted = *(&move(capability).blacklisted);
return !move(is_blacklisted);
} // Reverts if capability does not allow minting
public require_minter(capability: &R#Self.T) {
let is_minter: bool;
is_minter = Self.is_minter(move(capability));
assert(move(is_minter), );
return;
} // Reverts if capability is blacklisted
public require_not_blacklisted(capability: &R#Self.T) {
let is_not_blacklisted: bool;
is_not_blacklisted = Self.is_not_blacklisted(move(capability));
assert(move(is_not_blacklisted), );
return;
}
} module EToken { // This module is responsible for an actual eToken.
// For it to be useful a capability has to be published by using the Capability module above. // ----------------------------------------------------------------- import Transaction.Capability; // Declare the eToken resource, storing an account's total balance.
resource T {
value: u64,
} // Publishes an initial zero eToken to the sender.
// Should be called once before using this module.
public publish() {
move_to_sender<T>(T{ value: });
return;
} // Mint new eTokens.
// Reverts if capability does not allow it.
public mint(value: u64, capability: &R#Capability.T): R#Self.T {
Capability.require_minter(move(capability));
return T{value: move(value)};
} // Returns an account's eToken balance.
// Reverts if an initial eToken hasn't been published.
public balance(): u64 {
let sender: address;
let token_ref: &mut R#Self.T;
let token_value: u64; sender = get_txn_sender();
token_ref = borrow_global<T>(move(sender));
token_value = *(&move(token_ref).value); return move(token_value);
} // Deposit owned tokens to an payee's address, and destroy the tokens to deposit,
// Reverts if user is blacklisted.
public deposit(payee: address, to_deposit: R#Self.T, capability: &R#Capability.T) {
let payee_token_ref: &mut R#Self.T;
let payee_token_value: u64;
let to_deposit_value: u64; Capability.require_not_blacklisted(move(capability)); payee_token_ref = borrow_global<T>(move(payee));
payee_token_value = *(©(payee_token_ref).value); // Unpack and destroy to_deposit tokens
T{ value: to_deposit_value } = move(to_deposit); // Increase the payees balance with the destroyed token amount
*(&mut move(payee_token_ref).value) = move(payee_token_value) + move(to_deposit_value); return;
} // Withdraw an amount of tokens of the sender and return it.
// This works by splitting the token published and returning the specified amount as tokens.
public withdraw(amount: u64, capability: &R#Capability.T): R#Self.T {
let sender: address;
let sender_token_ref: &mut R#Self.T;
let value: u64; Capability.require_not_blacklisted(move(capability)); sender = get_txn_sender();
sender_token_ref = borrow_global<T>(move(sender));
value = *(©(sender_token_ref).value); // Make sure that sender has enough tokens
assert(copy(value) >= copy(amount), ); // Split the senders token and return the amount specified
*(&mut move(sender_token_ref).value) = move(value) - copy(amount);
return T{ value: move(amount) };
}
} script: // Performs simple testing to crudely verify the published modules above. import Transaction.Capability;
import Transaction.EToken; main() {
let sender: address;
let owner_capability: &R#Capability.Owner;
let capability: &R#Capability.T;
let minted_tokens: R#EToken.T;
let balance: u64; sender = get_txn_sender(); // Publish initial capability
Capability.publish(); // Borrow owner_capability for minter delegation
owner_capability = Capability.borrow_owner_capability(); // Delegate itself as a minter
Capability.grant_minter_capability(copy(sender), move(owner_capability)); // Borrow general capability for proof of minting capability
capability = Capability.borrow_capability(); // Publish an eToken account
EToken.publish(); // Mint 100 eTokens and prove minter capability
minted_tokens = EToken.mint(, copy(capability)); // Deposit the freshly minted tokens to itself
EToken.deposit(move(sender), move(minted_tokens), move(capability)); // Test that the balance corresponds with the intended behaviour
balance = EToken.balance();
assert(move(balance) == , ); return;
}
eToken目前部署在以太坊区块链上,包括为生产中使用标记化而实现的几个重要功能。
最重要的功能如下所列。粗体功能也已在Move IR中实现。
角色(破坏者,黑名单)
造币
燃烧
暂停
可升级
我们将一个角色定义为Move实现中的一个功能,命名更改是为了遵守Libra自己的硬币实现的标准。
能力
为了能够授予minter和blacklist权限,我们必须指定模块的所有者。作为模块的所有者,用户可以将帐户添加为小工具和黑名单。
我们首先定义所有者资源,该资源仅用于发布一次。它在Capability模块中声明。
resource Owner { }
然后,我们通过将已发布资源Move到指定所有者来授予所有权。在这里,我们在尝试使用该语言时遇到了一些问题,以保证所有者功能只发布一次。
在初始模块发布期间,Move IR定义似乎不支持仅定义为可执行一次的函数,也称为构造函数。
当然,这将是授予“所有者”能力的理想场所。
此外,Move IR不直接支持全局变量,这可能是将函数定义为已执行的不安全方式。
为了规避这些限制,我们使用硬编码的所有者地址创建了模块,从而创建了单例资源。因此,只有在所有者作为发件人发布功能资源时才会执行所有权授予:
public publish() {
let sender: address;
sender = get_txn_sender();
// Publish owner capability if sender is the privileged account
// Replace 0x0 address with a real owner account address
if (move(sender) == 0x0) {
Self.grant_owner_capability();
}
...
模块不能代表发件人之外的其他帐户发布资源。从而,让帐户完全控制与他们相关的内容。publish()因此,该功能必须由希望获得有效能力的所有帐户执行,这对于进一步的令牌使用是强制性的。
Neverthelss,强制执行硬编码的所有者地址并不是一个优雅的解决方案。我们向Libra团队提出了这个问题,团队成员建议实施合成糖替换硬编码地址fx。Self.publisher_address。
实际所有权授予是通过调用内部函数来完成的,该函数grant_owner_capability()创建Owner资源并将其发布到发件人帐户。这是通过执行以下表达式完成的:
move_to_sender<Owner>(Owner {});
通过将所述功能实现为内部,VM保证它只能由模块内部执行。通过仅在发件人是指定的所有者地址时调用该函数,我们确保它只发布一次。
该publish()还发布没有权限调用它,因为需要进一步令牌使用的所有帐户的能力。
它只会在已经存在的情况下恢复。
通过将所有权定义为资源,我们可以确保它不能被复制。它还为我们提供了一种令人愉快的类型安全的方式来保护特权功能,例如授予其他人的铸造能力。这是通过简单地要求借用Owner资源作为特权函数的参数来实现的。帐户只能通过调用borrow_owner_capability()函数来获取借用的引用,如果函数存在于发送方地址,则返回借用的引用。以下摘录举例说明了所有者特权函数:
// Grants minter capability to receiver, but can only succeed if sender owns the owner capability.
public grant_minter_capability(receiver: address, owner_capability: &R#Self.Owner) {
let capability_ref: &mut R#Self.T;
release(move(owner_capability));
...
借用的所有者功能仅用于类型安全性,因此立即释放给发送方:如果功能成功执行,则会改变位于该receiver地址的功能资源。
...
// Pull a mutable reference to the receiver's capability, and change its permission.
capability_ref = borrow_global<T>(move(receiver));
*(&mut move(capability_ref).minter) = true;
...
代币
通过使用功能模块定义eToken的角色和权限,我们现在可以继续实际的令牌实现。
我们首先声明令牌资源,该资源包含所述令牌的数量。
resource T {
value: u64,
}
与其他智能合约语言相比,Move的优势显而易见。如果我们要存放一些令牌,我们必须控制令牌的内存所有权。我们只能通过拆分现有的自有令牌(也称为撤销)或铸造新鲜令牌来获得此所有权。
这种所有权属性保证了相同的令牌不能存在于其他地方,从而消除了因错误复制而导致的错误,从而导致双重花费和其他错误行为。
此外,内存所有权模型还要求必须明确销毁拥有的令牌或将其Move到另一个所有者。这可以保证令牌不会被意外锁定在模块内,永远不会被再次检索。
通过利用这种类型安全的属性,我们可以定义存放自有令牌的功能。
public deposit(payee: address, to_deposit: R#Self.T, capability: &R#Capability.T) {
...
Capability.require_not_blacklisted(move(capability));
payee_token_ref = borrow_global<T>(move(payee));
payee_token_value = *(©(payee_token_ref).value);
// Unpack and destory to_deposit tokens
T{ value: to_deposit_value } = move(to_deposit);
// Increase the payees balance with the destroyed token amount
*(&mut move(payee_token_ref).value) = move(payee_token_value) + move(to_deposit_value);
...
我们首先确保用户未被列入黑名单。接下来,我们通过解压缩其内部数量变量来销毁所拥有的令牌。最后,我们通过解压缩的金额增加收款人的代币。
与存款相反,当从发件人的帐户中提取令牌时,我们将令牌分成两部分,并将新令牌的所有权归还给来电者。
通过首先减少发件人令牌数量,然后返回新的令牌资源来完成拆分。
*(&mut move(sender_token_ref).value) = move(value) - copy(amount);
return T{ value: move(amount) };
结论
总而言之,Libra和Move IR是智能合约开发中值得欢迎的一步。拥有强大的资产保证可以帮助开发人员减少容易出错的代码并加快Move速度。
尽管如此,Move IR仍然处于早期阶段,并且在当前迭代中不是用户友好的。由于某种原因,它被称为“中间代表”:-)
我们将密切关注这一发展,并对新的发展感到兴奋。
尝试并运行测试
如果您有兴趣了解Libra网络和Move IR,我们建议您运行测试以熟悉上述概念。要运行原始eToken实现测试,您应该执行以下步骤:
克隆Libra存储库(最好是在引言中声明的提交哈希)
跟随LibraREADME如何编译和安装它。
克隆此存储库
将位于src/eToken.mvir此存储库中的eToken Move IR源代码复制到位于的Libra存储库中的测试文件夹language/functional_tests/tests/testsuite/modules/
在Libra存储库中的某处执行以下命令: cargo test -p functional_tests eToken
Libra国内开发者微信交流群:
不能入群请加管理微信
Facebook币Libra学习-6.发行属于自己的代币Token案例(含源码)的更多相关文章
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析
原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...
- hadoop2.5.2学习及实践笔记(二)—— 编译源代码及导入源码至eclipse
生产环境中hadoop一般会选择64位版本,官方下载的hadoop安装包中的native库是32位的,因此运行64位版本时,需要自己编译64位的native库,并替换掉自带native库. 源码包下的 ...
- TensorFlow (RNN)深度学习 双向LSTM(BiLSTM)+CRF 实现 sequence labeling 序列标注问题 源码下载
http://blog.csdn.net/scotfield_msn/article/details/60339415 在TensorFlow (RNN)深度学习下 双向LSTM(BiLSTM)+CR ...
- Facebook币Libra学习-2.交易生命周期
交易生命周期 为了更加深入的理解Libra的交易生命周期,我们将跟随一个交易的全过程,从其被提交到Libra validator始,直至其被添加到区块链上止.我们将“放大”来看每个validator逻 ...
- Facebook币Libra学习-1.核心概念
Libra区块链是一个基于Libra协议的加密认证的分布式数据库.本文将简略介绍Libra协议的核心概念.其详细说明请参阅Libra技术白皮书. Libra区块链由分布式的Validator节点网络维 ...
- Facebook币Libra学习-5.Move组织目录
Move是一种新的编程语言,旨在为Libra Blockchain提供安全可编程的基础. 组织 Move语言目录由五部分组成: 的虚拟机(VM),其中包含的字节码格式,字节码解释器,和基础设施执行事务 ...
- Facebook币Libra学习-4.新的智能合约语言Move入门
Move是一种新的编程语言,旨在为Libra Blockchain提供安全可编程的基础.Libra Blockchain中的帐户是任意数量的Move资源和Move模块的容器.提交给Libra Bloc ...
- Facebook币Libra学习-3.小试牛刀第一笔交易
我们提供了一个命令行界面(CLI)客户端来与区块链进行交互. 假设 本文档中的所有命令均假定: 您运行的是Linux(基于Red Hat或Debian)或macOS系统. 您可以稳定地连接到互联网. ...
随机推荐
- selenium网页截图和截图定位(无界面)phantomjs
phantomjs是一款软件,需要重新安装. 参考: https://blog.csdn.net/liyahui_3163/article/details/79064108 案例代码: from se ...
- 查看mysql的bin-log日志
1.查看有哪些binlog show binary logs; show master logs; 2.如何查看log_bin中的内容 show binlog events; 查看第一个binlog的 ...
- 新版mysql的配置文件my.ini位置
在网上的博客上找了好久的my.ini,一直找不到.最后发现原来新版本的mysql已经不把my.ini放在原始的安装目录了.而是放在了C:/ProgramData下.
- myeclipse 添加反编译插件
文件下载地址: 链接: https://pan.baidu.com/s/1th2goaA2aS45kO84dX1Bdg 密码: g1fu 先关闭myeclipse1.下载jad1.5.8g 下载后解压 ...
- sqlmap中文帮助文档
Options(选项): -h,--help 显示基本帮助消息并退出 -hh 显示高级帮助消息并退出 --version ...
- Vue 日期下拉框
<!-- html --> <template> <!-- 控件样式 --> <div class="select"> <di ...
- 安装Vue脚手架和创建一个简单的Demo
https://www.cnblogs.com/pengjunhao/p/6762141.html https://www.cnblogs.com/yanxulan/p/8978732.html 查看 ...
- Linux如何杀掉tty终端
今天工作中遇到了同事的终端登陆不上去的问题,尝试着如何解决,首先想到的就是先干掉tty终端. 下面是总结的如何杀掉tty终端: 1.使用w命令查看当前登陆的用户及使用的tty [root@host ~ ...
- delphi TAdoQuery组件的close方法可能导致”列名无效“错误
1,故障现象 一次程序运行,出现如下错误: 对应代码如下: 2,故障分析 Query_alert_2的语句在查询分析器中单独执行是正常的.排除语句出错. 如果注解掉Query_alert_1,则错误变 ...
- 一个有趣的 SQL 查询
来源:站长资讯 一个朋友有这样一个SQL查询需求: 有一个登录表(tmp_test),包含用户ID(uid)和登录时间(login_time).表结构如下: ********************* ...