区块链安全

区块链安全第 4 课——使用 Solidity 进行编程

距离上一课已经过去了两个月,为什么这么久没有更新呢?一方面是因为新的工作环境,另一方面是因为一场突如其来的严重疾病,不过所幸我已经康复。

所以这节课作为一节复习课来再次熟悉一下 Solidity。

注:下文中的模块均选取部分需要 highlight 的概念来说,对于一些基础概念默认跳过。

数据类型

  • address
    20 字节,160 bit,因为1个hex用4bit表示,所以通常用40个hex字符表示地址。address 对象有很多有用的成员函数,如 balance,用来返回地址上以太坊的余额。transfer 用于向地址发送以太币。
  • 字节数组(固定的)
    使用 bytes1 到 bytes32 进行声明。
  • 字节数组(动态的)
    使用 bytes 或者 string 进行声明。

…… 关于数据类型我认为均可在编程时候再查询文档,故略过。

预定义的全局变量和函数

即为可以在智能合约中访问的以太坊全局变量和函数。

交易/消息的调用上下文

msg 对象是指触发合约执行的交易。

  • msg.sender:代表发起合约调用的以太坊地址,可能是外部账户对交易签名的地址,也可能是另一个合约,总之就是调用合约的地址。
  • msg.value:调用中发送的以太币数量(多少wei)。
  • msg.gas:已废弃。
  • msg.data:调用合约时传入的数据。可以联想到向0x0传入合约的字节码可以注册合约。
  • msg.sig:传输数据的前四个字节,这是一个函数选择器。

交易的上下文

tx 对象提供了访问交易相关信息的方法。

  • tx.gasprice:调用交易的 gas 价格。
  • tx.origin:发起这个交易的外部账户的地址。不安全。

区块的上下文

block 对象包含了当前区块的信息。具体略。

地址对象

  • address.balance
  • address.transfer(amount)
  • address.send(a mount)
  • ……

合约的构造函数和自毁函数

回退函数

关于什么是合约、函数的定义,以及修饰函数可见范围、行为的关键字,这里略。

但要记住一个概念:被 payable 关键字修饰的函数叫回退函数,这是合约中唯一可以用来接收以太币支付的函数。这个概念很好理解,记住回退函数这个名字只是说万一你要去面试,避免一些沟通上的尴尬。

构造函数

构造函数是一个特殊的函数,它只会被用到一次。当合约实力被创建时,如果定义了构造函数,就会被执行用来初始化合约的状态。构造函数会运行在创建合约交易的上下文中,但构造函数并不是必须的。

定义构造函数方法1:和合约同名,如:
contract MeContract{
function MeContract(){
//This is a the constructor
}
}

定义构造函数方法2:constructor 关键字:
pragma ^0.4.22
contract MeContract{
constructor(){
//This is a the constructor
}
}

自毁函数

合约被称为 SELFDESTRUCT 的特殊 EVM 字节码销毁。在 Solidity 中,此字节码作为高级内置函数公开,称为 selfdestruct,它接收一个参数:合同帐户中剩余的任何以太币余额的地址。代码如下:
selfdestruct(address recipient);

注:必须手动在合约中包含这个指令,合约才能被销毁。这是销毁合约的唯一途径。如果合约中没有加入 SELFDESTRUCT 字节码,合约就会永久存在于以太坊中。

合约示例:添加构造函数和自毁函数

需求:只有部署合约的外部账户才能调用自毁函数。

pragma solidity ^0.4.22;
contract Faucet{
address owner;
constructor(){
owner = msg.sender;
}
function destroy() public{
require(msg.sender == owner);
selfdestruct(owner);
}
}

  • 确保只有合约的所有者(部署合约的账户)才能执行自毁函数,通过 require 来控制对于这个函数的访问。
  • 构造函数只会运行一次,那么触发这个合约的外部账户的地址必定是合约调用者。

使用函数修饰符改写代码

pragma solidity ^0.4.22;
contract Faucet{
address owner;
constructor(){
owner = msg.sender;
}
modifier onlyOwner{
require(msg.sender == owner);
_;
}
function destroy() public onlyOwner{
selfdestruct(owner);
}
}


修饰符非常好理解:
1. 使用 modifier 关键字
2. 类似于宏,主要是把条件控制剥离出来。
3. 具体的作用相当于宏的代码替换。_ 符号的作用可以简单理解为,在被修饰函数之前执行或之后执行。

第 4 课先复习这些,下节课从合约的继承开始学习,预计语法一节课,vyper 一节课,然后就可以进入智能合约安全了。总之要快速掌握语言的基本知识然后进入代码阶段,正如以太社区的开发文化:move fast and break things.看代码是学不会编程的,快速进入安全阶段然后从真实源码阅读开始。