原文地址:http://solidity.readthedocs.io/en/latest/solidity-in-depth.html
本节提供您需要了解的有关Solidity的所有信息。 如果这里缺少某些东西,请与Gitter联系或者在Github上发出pull请求。
一、纯文件的布局
源文件可以包含任意数量的智能合约定义,包括指令和pragma指令。
pragma版本
源文件可以使用所谓的pragma版本进行注释,以防将来可能引入不兼容更改的编译器版本进行编译。 我们试图将这种变化保持在绝对最小值,特别是以语义上的变化也需要改变语法的方式引入变化,但这当然不能完全保证。 因此,对于包含中断更改的版本,通读更新日志是一个好办法,这些版本将始终具有0.x.0或x.0.0格式的版本。
pragma版本使用如下:
pragma solidity ^0.4.0;
这样的源文件将不会使用早于版本0.4.0的编译器进行编译,并且它也不能在编译器开始版本0.5.0(第二个条件通过使用^添加)上工作。 这个想法是,在版本0.5.0之前不会有任何突变,所以我们可以始终确保我们的代码将按照我们的意图编译。 我们不修复编译器的确切版本,所以仍然可能是bug修正版本。
可以为编译器版本指定更复杂的规则,表达式遵循npm使用的表达式。
导入其他源文件
1.语法和语义
Solidity支持与JavaScript(从ES6开始)中可用的import语句非常相似的import语句,尽管Solidity不知道“default export”的概念。
在全局级别,可以使用以下形式的import语句:
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";
2.路径
文件名总是作为一个路径用/作为目录分隔符, ..作为父目录。当 . 或 .. 后跟一个字符而不是/,它不被认为是当前或父目录。 所有路径名称都被视为绝对路径,除非它们以当前路径 . 或父目录 … 开始。
要从当前文件所在的目录导入文件x,请使用import“./x”作为x。 如果使用import“x”作为x,可能引用的是不同的文件(在全局“包括目录”中)。
它取决于编译器(见下文)如何实际解析路径。 一般来说,目录层次结构不需要严格映射到您的本地文件系统,它也可以映射到通过例如 ipfs,http或git。
3.在实际编译器中使用
当编译器被调用时,不仅可以指定如何发现路径的第一个元素,而且可以指定路径前缀重映射,例如:github.com/ethereum/dapp-bin/library重映射到/ usr/local/dapp-bin/library,编译器将从那里读取文件。 如果可以应用多个重新映射,则首先尝试使用最长键。 这允许使用例如图2的“回退重新映射”。“”映射到“/usr/local/include/solidity”。 此外,这些重新映射可以取决于上下文,这允许您配置要导入的包。 例如不同版本的同名库。
solc:
对于solc(命令行编译器),这些重映射作为上下文提供: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被检出在/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只允许包含来自某些目录的文件:它们必须位于某个明确指定的源文件的目录(或子目录)中,或者位于重新映射目标的目录(或子目录)中。 如果要允许直接绝对包含,只需添加remapping = /。
如果存在导致有效文件的多个重新映射,则选择具有最长公共前缀的重新映射。
browser-solidity:
基于浏览器的编译器提供了github的自动重映射,并且还将通过网络自动检索文件:您可以导入可迭代映射通过:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
将来可能会添加其他源代码提供程序。
注释
单行注释(//)和多行注释(/…/)都可以。
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
此外,还有另一种类型的注释,称为natspec注释,文档尚未写入。 它们使用三斜线(///)或双星号(/ * … /)编写,并且应该直接在函数声明或语句之上使用。 您可以在这些注释中使用Doxygen风格的标记来记录函数,注释形式验证的条件,并提供确认文本,当用户尝试调用函数时向用户显示。
在下面的示例中,我们记录了合同的标题,两个输入参数的说明和两个返回值。
pragma solidity ^0.4.0;
/** @title Shape calculator.*/
contract shapeCalculator{
/**@dev Calculates a rectangle's surface and perimeter.
* @param w Width of the rectangle.
* @param h Height of the rectangle.
* @return s The calculated surface.
* @return p The calculated perimeter.
*/
function rectangle(uint w, uint h) returns (uint s, uint p) {
s = w * h;
p = 2 * (w + h);
}
}
二、合约的结构
solidity写的合约类似于面向对象语言中的类。 每个合约可以包含状态变量,函数,函数修饰符,事件,结构类型和枚举类型的声明。 此外,合约可以继承其他合约。
状态变量
状态变量是永久存储在合约存储中的值。
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData; // State variable
// ...
}
函数
函数是合约中代码的可执行单元。
pragma solidity ^0.4.0;
contract SimpleAuction {
function bid() payable { // Function
// ...
}
}
函数调用可以在内部或外部发生,对其他合同具有不同级别的可见性(后面会有详细讲解)。
函数修饰符
函数修饰符可以用于以声明方式修改函数的语义。
pragma solidity ^0.4.0;
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
if (msg.sender != seller) throw;
_;
}
function abort() onlySeller { // Modifier usage
// ...
}
}
事件
事件是EVM日志记录工具的便利接口。
pragma solidity ^0.4.0;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
function bid() payable {
// ...
HighestBidIncreased(msg.sender, msg.value); // Triggering event
}
}
有关如何声明事件以及如何从dapp中使用事件的信息,请参阅后续部分。
结构类型
结构是可以将多个变量放在一组的自定义类型。
pragma solidity ^0.4.0;
contract Ballot {
struct Voter { // Struct
uint weight;
bool voted;
address delegate;
uint vote;
}
}
枚举类型
枚举可用于创建具有有限值集合的自定义类型。
pragma solidity ^0.4.0;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}