- Libra硬刚微信、支付宝? 你也试试!
- 来源:区块链大本营
作者 | Second State责编 | 乔治出品 | 区块链大本营(blockchain_camp)
本月17日,当 Marcus 被问到 Libra 未来是否会成为支付宝、微信的竞争对手时,Marcus 对此表示了默认,“是的,议员。”
但无论 Libra 未来的命运如何,也无论 Libra 将如何与微信、支付宝竞争,焦点都会回归到金融资产发行的问题上,那如何用代码实现?
今天,营长手把手教你如何基于 ERC20 标准在 Libra 上发布金融资产。
本文是 「Libra 编程」系列文章的第 3 篇,也是最后一篇。在之前的两篇文章,我们分别探讨了 Libra 项目的技术意义以及 Libra Client 与 Validator 内部处理与执行交易。
Part1:被 Libra 刷屏的你绝对不知道, 也许这才是它最大的"核武器"...
Part2:关于 Libra 币交易, 你需要了解的一切...
Libra 作为法币稳定币,成为一个金融系统还需要具有债券股票以及各种证券资产与衍生品,本文将从技术角度入手,使用 ERC20 标准在 Libra 区块链上发布金融资产。主要分为以下两部分:
编写 token moudule编译、部署 token moudule
希望这个教程可以让你对 Libra 的技术细节有更深刻的了解。
编写 token moudule
如何使用 Move IR 编写一个简单的 token module?
为方便理解,我们选择 Ethereum 的 ERC20 token 作为范例,分别执行 mint、balanceOf 和 transfer 三个功能。
开始前,我们需要了解 Libra 与以太坊在处理 resource 的逻辑方面有什么不同。
与 Ethereum global state 不同的是,Libra 并不设置统一集中存储的 global resource,而是将 resource 分散在各个账户存放。因此,以太坊智能合约撰写 "address storage owner = 0x" 这类变量需要用不同的逻辑来实现。每个人拥有多少 token 也分别存放在各自账户的 resource 下,而不是采用 "mapping(address=>uint256) " 这样的统一存储方式处理。
1、Capability
目前 Libra 开发团队推荐的处理 global variable 的方式是使用一个 singleton pattern 的 module 来进行模拟。
因此,我们将事件拥有所有者的 (owner) 权限定义为一种只能被发布一次的 resource,比如 "resource T{}"。针对这个 T 进行的操作有两个方法,一是在初始化时执行 "grant()" 用来确保 Token Capability 被移交给所有者;而 "borrow_sender_capability()" 则是检查操作者是否拥有所有者的权限。
1module TokenCapability {
2 resource T {}
3 // Grant Token Capability to the owner
4 public grant() {}
5 // Return an immutable reference to the TokenCapability of the sender if it exists. This will only succeed if the transaction sender is the owner.
6 public borrow_sender_capability(): &mut; R#Self.T {}
7}
a) grant()
如何执行 "grant()" ?
首先,我们需要定义两个角色,调用这个函数的交易发起者与实际上的所有者 owner。很可惜的是目前 Move IR 并没有提供类似 "Self.published_address" 的方式来让我们获得发布该 module 的账户,因此我们只能在代码中写死 module 所有者的地址,代码如下:
1// Grant Token Capability to the owner
2public grant() {
3 let sender: address;
4 let owner: address;
5 let t: R#Self.T;
6
7 sender = get_txn_sender();
8
9 owner = 0x1234;
10 // 假设 0x1234 是所有者的地址
11 assert(move(sender) == move(owner), 77);
12 // 检查交易发起者是否为所有者
13
14 t = T{};
15 // 为所有者建立新的 Token Capability
16 move_to_sender(move(t));
17 // 将 TokenCapability.T 转移给所有者。
18 return;
19}
从上面的代码中我们可以发现,只有通过 "sender == owner" 检查才能取得所有者的 resource T,因此我们可以确保 resource T 只会被所有者所拥有,其他的账户都没有机会获得这个 resource T。
此外,"move_to_sender(resource)" 是 Move IR 提供的内建函数,它代表了将 resource 移交给交易发起者的账户。
b) Borrow_sender_capability()
如何检查并确认交易发起者拥有所有者的 resource?
借助 Move IR 提供的辅助函数 "borrow_global(resource account)" 处理。borrow_global 会去该账户下面调取出 resource,如果该账户下没有持有这个 resource 则会触发意外情况,交易也会失败。若成功则会返回可变的 resource reference。
1// 如果存在,则返回对交易发起者的 TokenCapability 的不可变引用。
2// 只有在交易发起者是所有者才会执行成功。
3public borrow_sender_capability(): &mut; R#Self.T {
4 let sender: address;
5 let t_ref: &mut; R#Self.T;
6
7 sender = get_txn_sender();
8 t_ref = borrow_global(move(sender));
9
10 return move(t_ref);
11 }
2、Token
Libra 的权限管理方式比较特别,上文已着重介绍,接下来撰写 Token module!
1module Token {
2
3 import Transaction.TokenCapability;
4
5 // Token resource, 代表一个账户的总余额.
6 resource T {
7 value: u64,
8 }
9
10 // 建立一个新的 Token.T , value值为0
11 public zero(): R#Self.T {
12 return T{value: 0};
13 }
14
15 // 返回 Token的值
16 public value(token_ref: &R;#Self.T): u64 {
17 return *&move;(token_ref).value;
18 }
19
20 // 为交易发起者发布初始余额为0的Token
21 public publish() {}
22
23 // `mint_to_address` 只能由所有者调用.
24 // 这会给收款人一个新的Token ,价值是amount
25 public mint_to_address(payee: address, amount: u64) {}
26
27 // Mint 一个新的Token,价值是`value`.
28 mint(value: u64, capability: &mut; R#TokenCapability.T): R#Self.T {}
29 // 返回Token 余额 `account`.
30 public balanceOf(account: address): u64 {}
31
32 // 返回交易发起者的Token 余额.
33 public balance(): u64 {}
34
35 // 将 `to_deposit` 的token 存入 the `payee`\'s 账户
36 public deposit(payee: address, to_deposit: R#Self.T) {}
37 public withdraw(to_withdraw: &mut; R#Self.T, amount: u64): R#Self.T {}
38 // 将Token从交易发起者转到收款人
39 public transfer(payee: address, amount: u64) {}
40}
a) Token Resource
整个 Token module 的结构如上。定义这个 Token 的 resource T {value: u64} 代表了未来每个账户将会持有多少数量 (T.value) 的 token,也要定义两个跟 T 相关的辅助函数 zero() 制作一个数量为零的 Token.T,value() 回传该 Token.T 的实际数值。
b) Publish
如同 Capability 一样,每个账户都是分别持有自己的 resource。Libra 的设计逻辑中并不允许在没经过某账户的同意下为其增加额外的 resource,不像以太坊中只要有地址就可以收到别人的转账。因此,我们需要一个辅助函数供 Token 的所有者调用,为他们建立 Token.T 的 resource。这是 Publish 负责的事情。
1// 为交易发起者 publish 一个初始余额为0的 token
2
3 public publish() {
4 let t: R#Self.T;
5 // 建立一个新的数值为0的 Token.T
6 t = Self.zero();
7 // 将 Token.T 转移到交易发起者的账户下
8 move_to_sender(move(t));
9 return;
10}
c) Minting
让账户拥有 resource Token.T 的下一步便是发送一些 token,因此接下来将具体解释 mint 功能如何实现!
1public mint_to_address(payee: address, amount: u64) {
2 let capability_ref: &mut; R#TokenCapability.T;
3 let mint_token: R#Self.T;
4
5 // 使用 TokenCapability 来确保只有所有者有权限可以增发 token
6 capability_ref = TokenCapability.borrow_sender_capability();
7
8 // 呼叫下方的 mint() 来建立数量为 amount 的 Token.T
9 mint_token = Self.mint(copy(amount), move(capability_ref));
10
11 // 将 mint 出来的 Token.T 合并到收款人的名下,这个函数我们在下面解释。
12 Self.deposit(move(payee), move(mint_token));
13 return;
14}
15
16mint(amount: u64, capability: &mut; R#TokenCapability.T): R#Self.T {
17 // 为确保只有交易发起者拥有 TokenCapability.T,直接发布 resource 即可。
18 release(move(capability));
19
20 // 建立一个有 amount 数量的 Token.T
21 return T{value: move(amount)};
22}
增发 token 时,我们应先确保 sender 有增发的权限,如果没有这个权限,transaction 便会失效;然后建立要增发给 payee 的Token.T,最后通过 Token.deposit 函数将新建的 Token.T 与 payee account 下的 resource Token.T 合并。
d) Balance
增发 token 后,还缺乏查询名下 Token 数量的方法,这就需要撰写 balance 了!
1public balanceOf(account: address): u64 {
2 let token_ref: &mut; R#Self.T;
3 let token_const_ref: &R;#Self.T;
4 let token_val: u64;
5
6 // 从该账户下取得 resource reference
7 token_ref = borrow_global(move(account));
8
9 // 因为我们没有计划改动 resource 的数值,因此把可变的 reference 冻结,改成不可变的 reference
10 token_const_ref = freeze(move(token_ref));
11
12 // 调用 value() 来取得实际的余额
13 token_val = Self.value(move(token_const_ref));
14 return move(token_val);
15}
16
17
18// 这个 balance() 是直接包装 balanceOf() ,提供交易发起者一个简单的接口可以查询。
19public balance(): u64 {
20 let sender: address;
21 let balance_val: u64;
22 sender = get_txn_sender();
23 balance_val = Self.balanceOf(move(sender));
24 return move(balance_val);
25}
e) 转账 Transfer
重头戏当然是转账,transfer 一共分为三个步骤:从交易发起者借用 resource Token.T;将交易发起者的 resource Token.T 分割成要转账的部分与余额 (由 withdraw 函数负责);将交易发起者转账的部分与付款人的 resource Token.T 合并 (deposit 函数负责)。
因此整个 transfer 函数如下:
1public transfer(payee: address, amount: u64) {
2 let to_pay: &mut; R#Self.T;
3 let sender: address;
4 let to_withdraw: R#Self.T;
5
6 sender = get_txn_sender();
7
8 // 借用交易发起者的 resource Token.T
9 to_pay = borrow_global(move(sender));
10
11 // 分割出要给收款人的部分
12 to_withdraw = Self.withdraw(move(to_pay), move(amount));
13
14 // 将要给收款人的部分与收款人账户下原有的 Token.T 合并
15 Self.deposit(move(payee), move(to_withdraw));
16
17 return;
18}
而 Withdraw 与 Deposit 实现如下:
1public deposit(payee: address, to_deposit: R#Self.T) {
2 let deposit_value: u64;
3 let payee_token_ref: &mut; R#Self.T;
4 let payee_token_const_ref: &R;#Self.T;
5 let payee_token_value: u64;
6
7 // 取出要合并的数值
8 T{ value: deposit_value } = move(to_deposit);
9
10 // 获得付款人的 Token.T reference 与现有的数值
11 payee_token_ref = borrow_global(move(payee));
12 payee_token_const_ref = freeze(copy(payee_token_ref));
13 payee_token_value = Self.value(move(payee_token_const_ref));
14
15 // 修改付款人的 Token.T 的数值
16 *(&mut move(payee_token_ref).value) = move(payee_token_value) +
17 move(deposit_value);
18 return;
19}
20
21public withdraw(to_withdraw: &mut; R#Self.T, amount: u64): R#Self.T {
22 let value: u64;
23
24 // 取得交易发起者的 Token.T 数量,并确认是否足够支付这次转账
25 value = *(&mut copy(to_withdraw).value);
26 assert(copy(value) >= copy(amount), 10);
27
28 // 修改交易发起者的 Token.T 数量,并将分割后的 Token.T 转出去
29 *(&mut move(to_withdraw).value) = move(value) - copy(amount);
30 return T{value: move(amount)};
31}
3、测试 module
一个 mvir 的档案含有两个区块,分别是 modules 与 script,在 modules 中会撰写交易中需要部署的所有 modules,script 是在这次交易中我们想执行的程序。
a) Test Script
在我们的范例中,通过使用交易 script 的区块来进行测试。在这个测试中,我们把交易发起者作为所有者,并且 mint 1314 个 token 给交易发起者,最后检查交易发起者的余额是否跟 mint 的数值:1314 一致。
1script:
2import Transaction.TokenCapability;
3import Transaction.Token;
4main() {
5 let sender: address;
6 let balance_val: u64;
7 let sender_balance: u64;
8 sender = get_txn_sender();
9
10 // Grant owner\'s capability
11 TokenCapability.grant();
12
13 // Publish an Token account
14 Token.publish();
15
16 // Mint 1314 tokens to the owner
17 Token.mint_to_address(copy(sender), 1314);
18
19 // Check balance == 1314
20 balance_val = Token.balanceOf(copy(sender));
21 assert(copy(balance_val) == 1314, 2);
22 sender_balance = Token.balance();
23 assert(move(sender_balance) == move(balance_val), 1);
24 return;
25}
b) 测试
在撰写完 modules 与 script 后,依据 Libra 团队的建议,需将档案放入 "language/functional_tests/tests/testsuite/modules/" 下,并执行 "cargo test -p functional_tests ",Libra 就会加载并将执行刚才所撰写的合约,结果如下图:
编译、部署到 local testnet
如今 Libra testnet 尚未开放直接部署 modules,只能通过建立自己的 local testnet 来进行测试。现在部署的工具还在非常早期的阶段,对开发者的使用上也不是十分友好,以下是整理后的部署流程。
1、编译 Libra 后,可以在 "targe/debug/" 资料夹下找到 compiler 和 transaciton_builder 两个工具;2、通过使用 compiler 将撰写的 mvir 编译成 program,"./target/debug/compiler -o ";3、通过 transaction_builder 把 sender, program、argument 等封装成 raw transaction,"./target/debug/transaction_builder --args []";4、最后进到 libra cli 中使用submit 对 Libra cli 发出交易。
注:我们也编写了几笔交易的 scripts 用来操作 Token请参考此链接:https://github.com/second-state/libra-research/tree/master/examples/ERC20Token/transaction_scripts
部署与使用 Token 的顺序
1、先将 token.mvir (含有 Token、TokenCapability 的 module) 部署到 Libra;2、要想使用该 token 账户,必须先调用 init.mvir 将 Token.T 发布到账户的 resource 中;3、所有者可通过 mint.mvir 给其他拥有 resource Token.T 的账户增发 token;4、两个拥有 resource Token.T 的 account 可以通过 transfer.mvir 进行 token 转移。
开启允许部署 modules 的权限
Libra 在编译时期 (compilation-time) 时从 genesis file 里面读取是否可以设定部署 modules 的权限。因此,为把 modules 部署到 local testnet,我们需要在编译前修改这项设定。
在 "language/vm/vm_genesis/genesis/vm_config.toml" 这个档案,只需将 "[publishing_options]" 中的 "type=Locked" 改为 "type=Open" 即可。更改完以后,一定要重新编译使设定生效。
参考链接:1、Singleton Pattern Capability: https://community.libra.org/t/any-notion-of-global-variables/355/22、TokenCapability: https://github.com/second-state/libra-research/blob/master/examples/ERC20Token/token.mvir#L33、Token Source Code: https://github.com/second-state/libra-research/tree/master/examples/ERC20Token
344亿天价罚单也救不了Libra!"别太乐观, 冲破黑暗还很远呀! "
性能提升 3 倍的树莓派 4,被爆设计缺陷!Kubernetes端到端解决方案Part3:如何正确部署Kubernetes金山云肖江:5G 驱动智慧人居新发展中文repo“霸榜”GitHub Trending,国外开发者不开心了
中国第一程序员,微软得不到他就要毁了他!
猛戳""有惊喜哟
老铁在看了吗?👇
Second State
扫一扫下载订阅号助手,用手机发文章
赞赏
长按二维码向我转账
受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。
已同步到看一看
取消
发送
我知道了
朋友会在“发现-看一看”看到你“在看”的内容
确定
已同步到看一看写下你的想法
最多200字,当前共字
发送
已发送
朋友将在看一看看到
确定
写下你的想法...
取消
发布到看一看
确定
最多200字,当前共字
发送中
微信扫一扫
使用小程序
取消
允许
即将打开一个新页面
取消
允许
区块
-
-
- 快手内测10分钟长视频;微信可同时设置5个浮窗 | 产品猎人一周回顾
- 关注产品猎人(ID:qqchanpin),获取更多产品干货。 在产品资讯上,你或许需要一种新的形式。 我们用漫画形式,将易被忽略的内容记录了下来。 本周,互联网厂商们依然热闹非凡: 谷歌承认通
- 区块链探长
-
-
-
- Libra硬刚微信、支付宝? 你也试试!
- 作者 | Second State 责编 | 乔治 出品 | 区块链大本营(blockchain_camp) 本月17日,当 Marcus 被问到 Libra 未来是否会成为支付宝、微信的竞争对手时
- 区块链大本营
-
-
-
- 全球区块链早讯(7.19)
- 每日行情快讯:BTC于10500美元附近宽幅震荡 LTC领涨主流币 BTC于10500美元附近宽幅震荡, OKEx最高报10711美元。OKEx现报10605美元,24h涨幅9.75%。主流数字货币普
- 全球区块链早讯
-
-
-
- 数年前对区块链影响深远的大事件,现在却鲜有人提及
- 2019 年,许多虚拟货币的爱好者仍然在争论国家干预虚拟货币是好事还是坏事。这是一个很有分歧的话题,尤其是当我们考虑到——比特币发展背后的源动力是个体对自由的追求! 很多国家在 2017 年后仍在关注
- 白话区块链
-
-
-
- 比原项目周报(2019.07.19更新)
- 点击蓝字关注 比原链公众号 技术开发进展 Vapor: 1. Vapor测试网络测试; 2. 修复共识过程中存在的bug; 3. vapor 网络功能测试,修复测试过程中出现的消息传输错误
- 比原链Bytom
-
-
-
- 关于以太坊2.0,你不知道的9件事
- 点击上方“Unitimes” 可以订阅哦! 作者 | Bruno Škvorc 编译 | Jhonny 是的,这是一篇清单体文章 😱 以下是一些你可能不知道的关于以太坊2.0的事情,排序不分先后
- Unitimes
-
-
-
- 比原链Bystack首创的“一主多侧”架构,能解决区块链行业的哪些痛点?
- 点击蓝字关注 比原链公众号 区块链的去中心化、不可篡改等技术特性,改变了信任关系,降低了交易成本。区块链技术被认为是——继蒸汽机、电力、互联网之后,最具潜力触发新一轮革命浪潮的核心技术。在未来
- 比原链Bytom
-
-
-
- 7-18|币安未来三个月计划上线5个IEO、PVT在火币FastTrack第三期投票中获胜
- 今天的主要内容有Facebook Libra项目进行第二场听证会;古灵币 Grin 成功完成主网第一次硬分叉升级;赵东:Renrenbit在今年6月完成300万美元A轮融资,Bitfinex领投;币安
- 币圈情报社
-
-
-
- 世界经济论坛报告:评估区块链效益的6种方法
- 点击上方“蓝色字”可关注我们! 暴走时评:世界经济论坛(WEF)在7月16日发布了评估区块链应用效益的指南。该文件深入探讨了如何应用区块链技术,并为企业提供了六条建议。首先企业需要花时间了
- 区块链铅笔Blockchain
-
-
-
- 用50年前NASA送阿波罗上天的计算机挖矿什么体验? 出一个块要10^18年……
- 来源 | Ken Shirriff 编译 | Guoxi 出品 | 区块链大本营(blockchain_camp) 比特币挖矿到底有多难呢?一个外国极客用曾送宇宙飞船上天的 NASA 老古董做了
- 区块链大本营
-
-
-
- 区块链对保险行业的改造
- 保险业务的复杂性在于风险评估的过程不可避免地涉及到多方协调和大量记录的核对。这也让区块链技术在保险领域有用武之地。 菜鸟区块链 文|马库斯 区块链组件在保险市场的价值将以84.9%的复合年增长
- 菜鸟区块链
-
-
-
- 经济学家眼中的数字货币:与时俱进(监管篇)
- 文:蔡凯龙 来源:凯评金科 面对数字货币的全新挑战,经济学家分两类,一类嗤之以鼻,认为数字货币属于投机泡沫不值一提;另一类则谨慎接受,开始前沿的探索性研究。随着数字货币的发展壮大,越来越多的经济学家加
- 陀螺财经
-
-
-
- 钱包安全操作不规范,倾家荡产只需5分钟 : 资深从业者的建议一定要听
- 白话区块链 从入门到精通,看我就够了! 最近,关于加密货币钱包的安全事件不断出现在媒体上: 7 月 10 日,硬件钱包 Trezor 被曝仅用 5 分钟即可提取出密钥种子,且漏洞无法通过补丁修复。7
- 白话区块链
-