公链新宠Move合约如何快速入手
Posted Wally学习Web3.0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了公链新宠Move合约如何快速入手相关的知识,希望对你有一定的参考价值。
目录
欢迎关注笔者 twitter: @wallywxy沟通交流
引言
Move 语言伴随着新公链 Aptos 和 Sui 迅速崛起,其设计理念“资源是一等公民”,非常适合进行资产的数字化编程。Move 的白皮书中指出数字化资源应该不可以被复制,不能被隐式丢弃,只能在程序的存储地址之间进行移动,依据该理念 Move 为资产的稀缺性表达 和 访问控制提供了高于 Solidity 的表达能力。本文主要探求一下 Move 的基本概念,体验基本语法,感受在 Aptos 上如何部署和调用 Move 的hello world
。
什么是 Move 合约
Move 是基于 Rust 演化出来的合约编程语言,基本的语法结构和 Rust 的范式类似,但是更加简单,也做了一些支持资源特征表达方面的专门设计。
Move 项目工程结构
如下所示是 Move 工程的典型结构,其中 Move.toml 和 sources 是必备的,其他部分按需安排,其中 Move.toml 是工程的配置管理文件,包括版本号、依赖、包名什么的。sources 里面就是源码,源码以 .move
作为后缀名。
a_move_package
├── Move.toml (required)
├── sources (required)
├── examples (optional, test & dev mode)
├── scripts (optional)
├── doc_templates (optional)
└── tests (optional, test mode)
Move 的源码结构
如下所示为一个简单的 Move 的源码实例, 在 Move 中代码组织形式包括两种,一种是 module
一种叫 script
。
- 模块(module): 定义结构体以及函数,类似于 solidity 中的合约,会被发布到链上永久存储,模块进一步可以分为系统模块如 std 和 用户自定义模块,不同的区块链如 Aptos, Sui 又根据自身链的特性提供了一些链的特殊能力模块。
- 脚本(script): 可执行的入口函数,类似传统程序中的 main 函数,一半儿也直接定义为 main, 属于执行过程中的临时代码,不存储到链上。
// module example
module 0xCAFE::BasicCoin
struct Coin has key
value: u64,
public fun mint(account: signer, value: u64)
move_to(&account, Coin value )
// script example
script
// Import the Debug module published at the named account address std.
use std::debug;
const ONE: u64 = 1;
fun main(x: u64)
let sum = x + ONE;
debug::print(&sum)
其中 module 的完整语法定义为:
module <address>::<identifier>
(<use> | <friend> | <type> | <function> | <constant>)*
module
: 关键词address
: 该 module 发布到链上的用户的地址,在写合约代码时,可以用别名标识identifier
: 模块名称,一般大写驼峰结构表示
Move 的数据存储
从区块链或者智能合约虚拟机的视角而言,Move 在链上存储的数据大体上分为 module 和 state 两块,也就是合约的字节码和合约运行过程中产生数据,它们的存储示意图如下, 可以看到 Move 的数据都存储在账户地址下,这为其权限控制提供了很好的帮助。
相对而言,solidity 大部分 ERC20 合约内部用户的数据是以状态变量的形式存储的,这样一来用户的核心资产数据存储的是在合约内部,这和 move 存储在账户下面是天然的不同,这样 Solidity 中的资产除了要控制好用户的访问安全之外,也要控制好合约自身的安全性,甚至可以说用户数字资产的安全性完全取决于合约的控制逻辑。
尝试一下 Move 的 hello-world
由于 Move 的合约要上链的话其可能会涉及到具体链的 framework, 为了操作方便,这里笔者以 Aptos 的例子来进行阐述,示例代码。
工具&环境准备
- 下载 Move 命令行工具,访问 aptos-cli 下载页面,下载对应的版本;
- 解压 aptos 安装包,将 aptos 的二进制放到本地的 Path 中;
- 如果使用 vscode 或者其他 IDE 可以在相应的市场中搜索 move 关键字安装 move 插件。
运行 aptos move
即可查看我们 aptos move 的可用功能:
aptos move 命令行工具
Aptos 区块链的交互以及 module 的存储都和个人的账户息息相关,因此这里我们得准备一下个人的账户:
- 运行指令
aptos init
,过程中有一些交互,可以直接默认值往下走,会得到一个.aptos文件夹 ,存储了生成的账户信息和交互的链节点的配置信息:profiles: default: private_key: "your private_key" public_key: "0x4acd9175462e7382307af2d43a50e540042ca6cdcab3329618c939a9264c0096" account: 49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba rest_url: "https://fullnode.devnet.aptoslabs.com/v1" faucet_url: "https://faucet.devnet.aptoslabs.com/"
-
给本地生成账户接水(接受测试 Token),运行
aptos account fund-with-faucet --account default
:"Result": "Added 50000 coins to account 49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba"
好了现在身份和钱(可能不一定够,如果不够多接几次水,或者用钱包去接,钱包会弄得多一点)都有了,我们进入下一个环节。
合约编译&测试
进入 示例代码moveasy/src/contract/aptos 这里保存了一些 aptos 上的示例,我们从 hello-world 开始。合约的功能很简单,核心方法就是 get_message
和 set_message
,联盟链的同学可能对这个比较熟悉,就是存证和取证 😃。
前文提到 move 中的 module 是存储在具体的账户之下,在本地编译调试时为了方便,可以采用 named-addresses 也就是别名来替代 0xbb 类型的真实地址,因此在编译、测试等命令运行时也要加上相应的默认 named-addresses
-
编译合约
aptos move compile --named-addresses hello_blockchain=default
返回值如下,这里就验证了module 存储在账户的概念了,Result 中的 49cf…
就是个人的公钥信息。
"Result": [
"49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba::message"
]
如图 build 文件夹是编译出来的内容,我们可以看到 message.mv 这个就是本例子中的字节码,其依赖的部分除了 move 语言自身的依赖库 MoveStdlib
还包含了 AptosFramewrok
和 AptosStdlib
-
本地测试合约,测试代码如下
#[test(account = @0x1)] public entry fun sender_can_set_message(account: signer) acquires MessageHolder let addr = signer::address_of(&account); aptos_framework::account::create_account_for_test(addr); set_message(account, string::utf8(b"Hello, Blockchain")); assert!( get_message(addr) == string::utf8(b"Hello, Blockchain"), ENO_MESSAGE );
运行指令:aptos move test --named-addresses hello_blockchain=default
正确执行输出:
合约发布&调试
1. 发布合约,运行指令 aptos move publish --named-addresses hello_blockchain=default
至此这个hello-world的 module 就被部署到你的个人链上账户下面了,下面针对发布合约进行调用。
- 合约调用
-
使用命令行提交交易
aptos move run \\\\ --function-id 'default::message::set_message' \\\\ --args 'string:hello, blockchain'aptos move run \\\\ --function-id 'default::message::set_message' \\\\ --args 'string:hello, blockchain'
-
使用 RestAPI 读取数据
https://fullnode.devnet.aptoslabs.com/v1/accounts/49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba/resource/0x49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba::message::MessageHolder
返回值:
"type": "0x49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba::message::MessageHolder", "data": "message": "hello, blockchain", // 返回的消息体 "message_change_events": "counter": "0", "guid": "id": "addr": "0x49cf0e5ae7e483f82119eb697dd8da93af1a1703a5cd152cbb99001b3d1eb3ba", "creation_num": "4"
-
至此,我们完整地体验了 Move 合约的完整调用流程。
Move 的一些优质学习资源
- Move 语言代码库
- Introduction - The Move Book
- Developer Tutorials | Aptos Docs
- Write Smart Contracts with Sui Move | Sui Docs
- asome-move 系列
欢迎关注笔者 twitter: @wallywxy沟通交流
区块链合约安全系列:如何认识及预防公链合约中的自毁攻击
id:BSN_2021 公众号:BSN 研习社 作者:红枣科技张雪良
背景:由于公链环境下所有的信息都是共享的,智能合约相当于是完全透明化,任何人都可以调用,外加一些利益的驱动,导致引发了很多hacker的攻击。其中self destruct攻击也是常见的攻击方式之一。
目标:将目标合约瘫痪掉,无法做正常的业务,从而认识以及预防自毁攻击漏洞。
对象:适用于用Solidity语言开发的智能合约,例如BSN中的武汉链(基于ETH)和泰安链(基于 fisco bcos)上运行的智能合约。
前言
在进入正题之前,我先带大家从基础知识点开始一点点深入到怎么攻击以及预防。好,废话不多话,先看下selfdestruct的官方解释:
selfdestruct(address payable recipient)
Destroy the current contract, sending its funds to the given Address and end execution
理解起来也很简单,就是说合约销毁的时候,可以把ether转到指定的地址。
常见的用法 : 当我们的合约有漏洞或者业务变更的变化时,需要把它销毁掉,以避免造成更多的影响。此时可以在合约里提供一个销毁方法;
示例如下:
pragma solidity >=0.7.0 <0.9.0;
contract Destructible
address payable owner;
constructor()
owner = payable(msg.sender);
// 销毁合约
function destroy() public
if (msg.sender == owner)
selfdestruct(owner);
当需要销毁时,合约的owner可以调用destory()
方法进行合约销毁。
那接下来,我们正式进入主题,如何使用self-destory进行攻击。
攻击演示
1. 合约示例
演示需要用到的两个合约,一个模拟业务合约,一个为攻击合约。
业务合约: 一个简单的游戏,每个用户每次可以存放1ether到合约里,等到第7次存放的用户将成为赢家,可以把7ether提到自己账户里。
攻击合约: 编写了一个合约销毁的方法,即本合约销毁时会发送ether到指定的合约。
攻击逻辑: 调用攻击合约的attack方法,使得上述的业务合约余额超过7。
示例如下:
pragma solidity >=0.7.0 <0.9.0;
// 业务合约
contract EtherGame
uint public targetAmount = 7 ether;
address public winner;
// 充值ether
function deposit() public payable
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount)
winner = msg.sender;
// 提取ether
function claimReward() public
require(msg.sender == winner, "Not winner");
winner = address(0);
(bool sent, ) = msg.sender.callvalue: address(this).balance("");
require(sent, "Failed to send Ether");
// 查询当前余额
function balanceOf() public view returns (uint)
return address(this).balance;
// 攻击合约
contract Attack
EtherGame etherGame;
constructor(EtherGame _etherGame)
etherGame = EtherGame(_etherGame);
// 合约销毁和发送ether
function attack() public payable
// 发送ether到指定的业务合约
selfdestruct(payable(address(etherGame)));
2. 合约部署
老规矩,我们使用remix进行部署测试。
部署成功后,截图如下:
3. 正常业务操作
准备两个账户A和B,分别为100ether。 具体操作流程为:A调用5次存放5ether,B调用2次存放2ether,B将成为winner,然后提取7ether。
两个账户合计调用7次后,查询余额以及winner信息,截图如下:
B账户提取ether,结果截图如下:
上面的图中可以看到,B账户成功的提取了合约里的7 ether。
4. 攻击操作
我们还是用上面的两个账户账户A和B。 具体操作流程为:A调用5次存放5ether,B调用攻击合约的attack方法并发送3ether。
上图可以发现业务合约当前余额为8 ether。
上图可以看到攻击合约的owner变为0x0地址。
到此,业务合约已被攻击,即业务无法正常进行,不能存放以及提取。
下面,我们进行测试depoit和claimReward的方法调用,结果信息截图如下:
解决方案
最后,给大家推荐一个常用的方案:将全局address(this).balance改为变量统计进入deposit逻辑的ether数量。
最终代码如下所示:
pragma solidity >=0.7.0 <0.9.0;
// 业务合约
contract EtherGame
uint public targetAmount = 7 ether;
address public winner;
uint public balance;// 记录ether数量
// 充值ether
function deposit() public payable
require(msg.value == 1 ether, "You can only send 1 Ether");
balance += msg.value;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount)
winner = msg.sender;
// 提取ether
function claimReward() public
require(msg.sender == winner, "Not winner");
winner = address(0);
(bool sent, ) = msg.sender.callvalue: balance("");
require(sent, "Failed to send Ether");
// 查询当前余额
function balanceOf() public view returns (uint)
return address(this).balance;
今天的讲解到此结束,感谢大家的阅读,如果你有其他的想法或者建议,欢迎一块交流。
本文由 mdnice 多平台发布
以上是关于公链新宠Move合约如何快速入手的主要内容,如果未能解决你的问题,请参考以下文章
智能合约开发——Sui/Move vs. Solana/Rust
刷完欧拉计划中难度系数为5%的所有63道题,我学会了Rust中的哪些知识点?