HiBlock区块链社区区块链技术文章

深入理解Solidity之源文件及合约结构——Solidity中

2018-04-12  本文已影响15人  宇宙永恒
image

写在前面:HiBlock区块链社区成立了翻译小组,翻译区块链相关的技术文档及资料,本文为Solidity文档翻译的第四部分《深入理解Solidity之源文件及合约结构》,特发布出来邀请solidity爱好者、开发者做公开的审校,您可以添加微信baobaotalk_com,验证输入“solidity”,然后将您的意见和建议发送给我们,也可以在文末“留言”区留言,有效的建议我们会采纳及合并进下一版本,同时将送一份小礼物给您以示感谢。

Solidity 源文件结构

源文件中可以包含任意多个合约定义、导入指令和杂注指令。

1

版本杂注

为了避免未来被可能引入不兼容变更的编译器所编译,源文件可以(也应该)被所谓的版本 杂注pragma所注解。 我们力图把这类变更做到尽可能小,特别是,我们需要以一种当修改语义时必须同步修改语法的方式引入变更,当然这有时候也难以做到。 因此,至少对含重大变更的版本,通读变更日志永远是好办法。 这些版本的版本号始终是 0.x.0 或者 x.0.0 的形式。

版本杂注使用如下:

pragma solidity ^0.4.0;

这样,源文件将既不允许低于 0.4.0 版本的编译器编译, 也不允许高于(包含) 0.5.0 版本的编译器编译(第二个条件因使用 ^ 被添加)。 这种做法的考虑是,编译器在 0.5.0 版本之前不会有重大变更,所以可确保源代码始终按预期被编译。 上面例子中不固定编译器的具体版本号,因此编译器的补丁版也可以使用。

可以使用更复杂的规则来指定编译器的版本,表达式遵循 npm 版本语义。

image

2

导入其他源文件

语法与语义

虽然 Solidity 不知道 "default export" 为何物, 但是 Solidity 所支持的导入语句,其语法同 JavaScript(从 ES6 起)非常类似。

image

在全局层面上,可使用如下格式的导入语句:

import "filename";

此语句将从 “filename” 中导入所有的全局符号到当前全局作用域中(不同于 ES6,Solidity 是向后兼容的)。

import * as symbolName from "filename";

...创建一个新的全局符号 symbolName,其成员均来自 "filename" 中全局符号。

import {symbol1 as alias, symbol2} from "filename";

...创建新的全局符号 alias 和 symbol2,分别从 "filename" 引用 symbol1 和 symbol2 。

另一种语法不属于 ES6,但或许更简便:

import "filename" as symbolName;

这条语句等同于

import * as symbolName from "filename";

路径

上文中的 filename 总是会按路径来处理,以 / 作为目录分割符、以 . 标示当前目录、以 .. 表示父目录。 当 . 或 .. 后面跟随的字符是 / 时,它们才能被当做当前目录或父目录。 只有路径以当前目录 . 或父目录 .. 开头时,才能被视为相对路径。

用 import "./x" as x; 语句导入当前源文件同目录下的文件 x 。 如果用 import "x" as x; 代替,可能会引入不同的文件(在全局 include directory 中)。

最终导入哪个文件取决于编译器(见下文)到底是怎样解析路径的。 通常,目录层次不必严格映射到本地文件系统, 它也可以映射到能通过诸如 ipfs,http 或者 git 发现的资源。

在实际的编译器中使用

当运行编译器时,它不仅能指定如何发现路径的第一个元素,还可指定路径前缀 重映射remapping。 例如,github.com/ethereum/dapp-bin/library 会被重映射到 /usr/local/dapp-bin/library , 此时编译器将从重映射位置读取文件。如果重映射到多个路径,优先尝试重映射路径最长的一个。 这允许将比如 "" 被映射到 "/usr/local/include/solidity" 来进行“回退重映射”。 同时,这些重映射可取决于上下文,允许你配置要导入的包,比如同一个库的不同版本。

solc:

对于 solc(命令行编译器),这些重映射以 context:prefix=target 形式的参数提供。 其中,context: 和 =target 部分是可选的(此时 target 默认为 prefix )。 所有重映射的值都是被编译过的常规文件(包括他们的依赖),这个机制完全是向后兼容的(只要文件名不包含 = 或 : ), 因此这不是一个破坏性修改。 在 content 目录或其子目录中的源码文件中,所有导入语句里以 prefix 开头的导入文件都将被用 target 替换 prefix 来重定向。

举个例子,如果你已克隆 github.com/ethereum/dapp-bin/ 到本地 /usr/local/dapp-bin , 可在源文件中使用:

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;

然后运行编译器:

solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

举个更复杂的例子,假设你依赖了一些使用了非常旧版本的 dapp-bin 的模块。 旧版本的 dapp-bin 已经被 checkout 到 /usr/local/dapp-bin_old ,此时你可使用:

solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \

module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ source.sol

这样, module2 中的所有导入都指向旧版本,而 module1 中的导入则获取新版本。

注意, solc 只允许包含来自特定目录的文件:它们必须位于显式地指定的源文件目录(或子目录)中,或者重映射的目标目录(或子目录)中。 如果你想直接用绝对路径来包含文件,只需添加重映射 =/。

如果有多个重映射指向一个有效文件,那么具有最长公共前缀的重映射会被选用。

Remix:

Remix 提供一个为 github 源代码平台的自动重映射,它将通过网络自动获取文件: 比如,你可以使用

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;

导入一个 map 迭代器。

未来, Remix 可能支持其他源代码平台。

3

注 释

可以使用单行注释(//)和多行注释(/.../)

// 这是一个单行注释。

/*

这是一个多行注释。

*/

此外,有另一种注释称为 natspec 注释,其文档还尚未编写。 它们是用三个反斜杠(///)或双星号开头的块(/** ... */)书写,它们应该直接在函数声明或语句上使用。 可在注释中使用 Doxygen 样式的标签来文档化函数、 标注形式校验通过的条件,和提供一个当用户试图调用一个函数时显示给用户的 确认文本

在下面的例子中,我们记录了合约的标题、两个入参和两个返回值的说明:

pragma solidity ^0.4.0;

/* @title 形状计算器。 /

**contract **shapeCalculator {

}

合约结构

在 Solidity 中,合约类似于面向对象编程语言中的类。 每个合约中可以包含 状态变量、 函数、 函数修饰器、事件、 结构类型、 和 枚举类型 的声明,且合约可以从其他合约继承。

1 状态变量

状态变量是永久地存储在合约存储中的值。

pragma solidity ^0.4.0;

contract SimpleStorage {
** uint** storedData; // 状态变量
// ...

}

有效的状态变量类型参阅 类型 章节, 对状态变量可见性有可能的选择参阅 Visibility and Getters 。

2 函 数

函数是合约中代码的可执行单元。

pragma solidity ^0.4.0;

contract SimpleAuction {
function bid() public payable { // 函数
// ...
}

}

Function Calls 可发生在合约内部或外部,且函数对其他合约有不同程度的可见性( Visibility and Getters)。

3 函数修饰器

函数修饰器可以用来以声明的方式改良函数语义(参阅合约章节中 Function Modifiers)。

pragma solidity ^0.4.11;

**contract **Purchase {
address public seller;

**modifier **onlySeller() { // 修饰器
require(msg.sender == seller);
_;
}

** function** abort() **public **onlySeller { // Modifier usage
// ...
}

}

4 事 件

事件是与以太坊虚拟机日志工具的方便接口。

pragma solidity ^0.4.0;

**contract **SimpleAuction {

event HighestBidIncreased(address bidder, uint amount);* // 事件*

** function** bid() public payable {
// ...
HighestBidIncreased(msg.sender, msg.value); // 触发事件
}

}

有关如何声明事件和如何在 dapp 中使用事件的信息,参阅合约章节中的 Events。

5 结构类型

结构是可以将几个变量分组的自定义类型(参阅类型章节中的 Structs)。

pragma solidity ^0.4.0;

**contract **Ballot {
** struct Voter { // 结构
uint weight;
bool voted;
address delegate;
uint vote;
}

}

6 枚举类型

枚举可用来创建有一定数量的值的自定义类型(参阅类型章节中的 Enums)。

pragma solidity ^0.4.0;

contract Purchase {
enum State { Created, Locked, Inactive } // 枚举

}

本文内容来源于HiBlock区块链社区翻译小组,感谢全体译者的辛苦工作。

:本文为solidity翻译的第四部分《深入理解Solidity之源文件及合约结构》,特发布出来邀请solidity爱好者、开发者做公开的审校,您可以添加微信baobaotalk_com,验证输入“solidity”,然后将您的意见和建议发送给我们,也可在文末“留言”区留言,或通过原文链接访问我们的Github。有效的建议我们会收纳并及时改进,同时将送一份小礼物给您以示感谢。

课程推荐案例|以钢铁行业为例,分享区块链在仓储领域的应用实践

课程回放:(识别图中二维码即可观看视频)

image

以下是我们的社区介绍,欢迎各种合作、交流、学习:)

image
上一篇下一篇

猜你喜欢

热点阅读