solidity 合约结构
什么是智能合约?
一种旨在以信息化方式传播、验证、或执行合同的计算机协议,它允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。
从技术上,通常我们把在区块链上运行的程序称为智能合约
1. 一个简单的智能合约
下面我们用一个简单的智能合约作为示例,来看看智能合约的基本结构:
pragma solidity ^0.5.11;
contract Counter {
uint count = 0;
address owner;
// events
event EmitIncrement(uint count);
constructor () public {
owner = msg.sender;
}
// Standard modifier on methods invokable only by contract owner.
modifier onlyOwner {
require(msg.sender == owner, "OnlyOwner methods called by non-owner.");
_;
}
function increment() public onlyOwner {
uint step = 10;
count = count + step;
emit EmitIncrement(count);
}
function getCount() public view returns (uint) {
return count;
}
}
1.1 版本声明
pragma solidity ^0.5.11;
pragma
是指定当前Solidity
文件编译器版本的指令,0.5.11
代表Solidity
版本,pragma
指令的用法如下:
pragma Solidity <<version number>>;
注:指令是区分大小写的,随后的有效的版本好和语句以分号结束
版本号由两个数字组成:主版本号+次版本号(上个例子中5是主版本号,11是次版本号)
^
字符也称为脱字符号,指主版本中的最新的版本,因此^0.5.11
是指版主本号为5的最新版本(^
字符只针对提供的主版本号的版本)
1.2 合约声明
contract Demo {
}
该行代码的作用是声明一个合约,其中:
contract
是合约声明的关键字,Demo
是合约的名称
1.3 状态变量
uint count = 0;
address owner;
count
和owner
就是状态变量,合约中的状态变量相当于类中的属性变量
状态变量还有相关联的限定符,可以是以下任何一种:
internal
:默认情况下,如果没有指定任何内容,则状态变量具有 internal 限定符。这意味着这个变量只能在当前的合约函数和任何继承它们的合约中使用。这些变量不能被外部访问修改,但是,可以查看它们。
内部状态变量的示例如下所示:
int internal StateVariable;
private
:这个限定符就像 internal 上附加的约束。私有状态变量只能在声明它们的合约中使用。即使在派生合约中也不能使用它们。
私有状态变量的示例如下所示:
int private privateStateVariable;
public
:这个限定符使得状态变量可以直接访问。Solidity 编译器为每个公共状态变量生成一个 getter 函数。
公共状态变量的示例如下:
int public stateIntVariable;
constant
:这个限定符使得状态变量不可变。变量声明时必须赋初值。实际上,编译器会在所有代码中将变量的引用替换为指定的值。
常量状态变量的示例如下:
bool constant hasIncome = true;
1.4 函数
函数是以太坊和 Solidity
的核心。以太坊维护状态变量的当前状态,并执行交易以更改状态变量中的值。当调用或触发合约中的某个函数时,会导致创建一个交易。函数机制是为了从状态变量读取值和向状态变量写入值。函数是一个按需调用执行的代码单元。函数可以接受参数,执行其逻辑,并可选地将值返回给调用者。可以以匿名方式命名函数。Solidity 提供了命名函数,在合约中只能有一个称为fallback
函数的未命名函数。
函数具有与状态变量类似的可见性限定符。函数的可见性可以是以下任何一种:
public
:这种可见性使得函数可以直接从外部访问。它们成为合约接口的一部分,可以在内部和外部调用。internal
:默认情况下,如果没有指定,则状态变量具有 internal 限定符。这意味着此函数只能用于当前合约以及任何从其继承的合约。这些函数不能从外部访问,它们不是合约接口的一部分。private
:私有函数只能在声明它们的合约中使用,即使在派生合约中也不能使用它们。它们不是合约接口的一部分。external
:这种可见性使得函数可以直接从外部但不是内部访问。这些函数是合约接口的一部分。
函数还可以具有以下附加限定符,这些限定符能够更改合约状态变量:
constant
:这些函数不具有修改区块链状态的能力。它们可以读取状态变量并返回给调用者,但不能修改任何变量、触发事件、创建另一个合约、调用其他可以改变状态的函数等。将常函数看作可以读取和返回当前状态变量值的函数。view
:这些函数是常量函数的别名。pure
:pure
函数进一步限制了函数的能力。pure 函数既不能读取也不能写入,即它们不能访问状态变量。使用此限定符声明的函数应确保它们不会访问当前状态和交易变量。payable
:使用payable
关键字声明的函数能够接受来自调用者的以太币。如果发送者没有提供以太币,则调用将会失败。如果一个函数被标记为payable
,该函数只能接受以太币。
1.4.1 构造函数
constructor () public {
owner = msg.sender;
}
上面就是一个构造函数的示例,当合约对象创建时,会先调用构造函数对相关的数据进行初始化处理,本例子中就在构造函数中定义了合约的owner
1.4.2 fallback
函数
fallback
函数特点:
- 没有名字、没有参数、没有返回值。
- 如果请求的方法在合约中没有的话,就会执行
Fallback
函数。
1.5 修改器
在 Solidity
中,修改器总是与函数关联。编程语言中的修改器是指改变执行代码行为的结构。由于修改器与 Solidity
中的某个函数相关联,因此它可以改变与其关联的函数的行为。为了便于理解修改器,可以将其视为在执行目标函数之前执行的函数。假设你想调用 increment
函数,但是在执行它之前,你想要执行另一个函数来检查合约的当前状态、传入参数中的值、状态变量中的当前值等,并相应地决定是否应该执行目标函数 increment
。这有助于编写更整洁的函数,而不会用验证和确认规则混淆它们。此外,修改器可以与多个函数关联。这确保了更清晰、更易读、更易维护的代码。
修改器使用 modifier
关键字后跟修改器标识符、它应该接受的任何参数、花括号{}内编写的代码来进行定义。修改器中的 “_”(下划线)表示执行目标函数。你可以将此视为下划线被内联的目标函数替换。payable
是一种由 Solidity
提供的开箱即用的修改器,当应用于任何函数时允许该函数接受以太币。
在合约级别声明了 modifier 关键字,如下所示:
// Standard modifier on methods invokable only by contract owner.
modifier onlyOwner {
require(msg.sender == owner, "OnlyOwner methods called by non-owner.");
_;
}
该方法声明了onlyOwner
,他要求有该字段的函数只允许合约的owner
调用
1.6 事件
Solidity
支持事件。Solidity
中的事件就像其他编程语言中的事件一样。事件是从合约中触发的,任何对它们感兴趣的人都可以捕获它们并响应执行代码。Solidity
中的事件主要用于通过 EVM
的日志工具向调用应用程序通知合约的当前状态。它们用于通知应用程序有关合约中的改变,并且应用程序可以使用它们来执行相关逻辑。它们不是应用程序,而是轮询合约中特定状态的更改,合约可以通过事件通知它们。
合约中声明的事件在全局域有效,并且被合约中的函数所调用。使用 event
关键字声明一个事件,后跟一个标识符和参数列表并以分号结尾。参数中的值可用于记录信息或执行条件逻辑。事件信息及其值作为交易的一部分存储在区块内。在上一章讨论交易的属性时,引入了一个名为 LogsBloom
的属性。作为交易的一部分引发的事件存储在此属性中。
没有必要显式地提供参数变量——只有数据类型就足够了,如:
event demo(uint, address);
可以从任何函数调用事件的名称并传递所需的参数,如下:
function increment() public onlyOwner {
uint step = 10;
count = count + step;
emit EmitIncrement(count);
}