术语
当然,第一个问题就提出来了。 什么是Solidity?
正如你所想象的,Solidity与智能合约有关。Solidity正是我们要用来实现智能合约的编程语言!
多么令人兴奋,不是吗?
关于智能合约的一个简单而又全面的目标定义指出。
定义:"智能合约是存储在区块链上的计算机程序,当预定的条件得到满足时运行。它们通常用于自动化协议的执行,以便所有参与者可以立即确定结果,而没有中间人的参与或时间损失。它们也可以使工作流程自动化,当条件得到满足时触发下一个行动。" -IBM
在这一点上,我将指出这个定义中出现的几个关键概念:程序、区块链、条件、协议自动化、参与者、结果和没有中间人。
一个有点技术含量的定义说。
定义:"'智能合约'只是一个在以太坊区块链上运行的程序。它是代码(其功能)和数据(其状态)的集合,驻留在以太坊区块链的一个特定地址。" -Ethereum.org
从这个定义中,我们可以再取几个关键的概念,这次是从技术角度出发:代码、功能、数据、状态和地址。
在我们探索Solidity的过程中,我们会遇到这些关键概念,并解释它们在智能合约背景下的意义和作用。
什么是Solidity?
Solidity是一种高级的、面向对象的编程语言,这意味着它的抽象水平远远高于它所运行的机器和平台的物理层。
它将隐藏底层硬件和通信网络的所有错综复杂的问题,使我们能够专注于最终功能。
变量、对象、类型、函数和其他语言结构将成为我们的积木和垫脚石,用于创建更多令人兴奋的组件,这些组件在连接后将形成我们的智能合约。
该语言的面向对象的特性正好有利于将现实世界的实体和它们的行为自然地转移到我们的模型中,并最终以智能合约的形式表示和部署。
Solidity的香味让人想起C++、Python和JavaScript,它是静态类型的,支持继承(将智能合约视为一个类)、库和复杂的用户定义类型(文档)。
Solidity合约在Ethereum虚拟机或EVM上运行。这篇文章(以及后面的文章)将建立在官方Solidity文档的基础上,我鼓励你定期到docs查看。
如URL所示,在写这篇文章时,当前的Solidity版本是v0.8.15,除非明确说明,否则我们将在整个系列中坚持使用它。
我们可以通过使用Solidity实现的智能合约的典型例子是投票,众筹,盲目拍卖,多签名钱包,托管安排,以及许多其他。
Solidity中的智能合约 - 一个例子
一个简单的,但能说明问题的智能合约的例子使我们注意到Solidity中智能合约实现的基本要素。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract SimpleStorage
{
uint storedData;
function set(uint x) public
{
storedData = x;
}
function get() public view returns (uint)
{
return storedData;
}
}
我们的第一个例子显示了在Solidity中实现的一个简短的智能合约,它只做了两件事:它通过函数set公开实现了在状态变量storedData ,并通过函数get ,使其公开可供读取。
然而,我们这个例子的简单性将向我们介绍基本的合约结构和在构建智能合约的过程中最常用的语言结构。
这一行// SPDX-License-Identifier: GPL-3.0 ,说明源代码是根据GPL3.0版本授权的。你可以在以下网站了解更多关于软件许可模式的信息
pragma solidity >=0.4.16 <0.9.0; 行规定了支持源代码执行的 Solidity 编译器版本的范围,从(包括)Solidity v0.4.16 到,但不包括 Solidity v0.9.0。在一般情况下,pragma 关键字代表一个编译器指令,指定如何处理源代码。
在我们的具体案例中,它指定了与我们的源代码兼容的编译器版本。
**注意:**我想指出一个简单而实用的细节:一个开放的大括号可以和合同名称放在同一行,这是我在其他语言中通常喜欢的风格,因为它为每个块节省了一行代码,使代码更加紧凑而不影响阅读性。然而,当涉及到Solidity时,代码的可读性得益于将开头的大括号放在函数头的下面。以后,当我们引入函数修饰符时,第一眼就能注意到我们是在看函数的头部还是主体,就会容易得多。当然,我们也可以根据情况混合这两种风格,但为了保持一致性,我建议保持开头的大括号在其行中。
从内容上看,合同是代码和数据的集合。代码代表合同的行为,我们以现实世界的实体为模型,并通过函数来实现。
数据代表合同的状态,它通过不同类型的对象实现,使合同成为一个有状态的实体。
数据驻留在存储器中,这是一个映射到以太坊区块链上的地址的内存空间。
这一行contract SimpleStorage ,定义了我们合约的名称,SimpleStorage ,我们也会把它称为头。
接下来是一个合同主体,标明了开头的 "{" 和结尾的 "}" 大括号。我们还将对其他块结构(如函数)使用这种头部/主体的引用约定。
uint storedData; 这一行声明了一个名为storedData 的状态变量,其类型为uint (长度为 256 位的无符号整数)。
storedData 该变量可以被读写,类似于一个初级的数据库行为。在我们的例子中,有两个函数, 和 ,它们从变量 读取和写入。get set storedData
行函数set(uint x) public 声明了一个公共函数set,它接收一个无符号整数x 。没有关键字return的函数不返回结果。
行storedData = x; ,将存储在参数变量x 中的一个值分配给状态变量storedData 。一个没有状态可变性关键字的函数被认为是有一个隐含的、默认的状态可变性非支付的(下面有解释)。
第function get() public view returns (uint) 行声明了一个公共函数get ,由于可见性关键字view ,它只能从其外部范围读取状态变量(下面有解释)。该函数还必须返回一个对象,即一个类型为uint 的结果。
这一行return storedData; ,返回对象storedData 。
可见性和状态可变性
函数可以而且经常用可见性关键字来增强。
get 和set 函数都是用可见性关键字public 声明的,这意味着这些函数可以从父合同和所有其他合同中调用(对其可见)。
还有三个可见性关键字。private,internal, 和external 。
- 可见性关键字
private使得函数只能从父合同内部调用。 - 可见性关键字
internal使得函数可以从父合同和所有继承的合同中被调用。 - 可见性关键字
external使得该函数只能从其他合约中调用,而不能从父合约中调用。
除了可见性关键字外,还有三个明确的状态可变性关键字。pure,view, 和 [payable](https://blog.finxter.com/what-is-payable-in-solidity/).这些状态可变性关键字决定了一个函数是否可以访问或修改其外部范围内的对象(状态变量)。
- 关键字
pure完全阻止函数访问其外部范围,这意味着函数只能访问其内部对象和参数。 - 关键词
view防止函数改变它的外部作用域,意味着函数可以访问它的外部作用域对象(合约状态),它的内部对象和它的论据。 - 关键词
payable使得函数可以接收以太,改变其外部范围的对象(合约状态)、内部对象和参数。如果没有指定可变性关键字,则隐含地假定默认状态可变性非支付,在这种情况下,函数可以改变其外部范围对象(合同状态)、内部对象和参数。
**注意:**定义为纯的函数部分类似于函数式编程范式中的纯函数,具有两个重要属性中的第二个。
- 该函数对相同的参数返回相同的值。
- 该函数没有副作用,也就是说,它不修改其外部范围。如果一个定义为
pure的 Solidity 函数具有这两个属性,那么就函数式编程范式而言,它将被认为是真正纯粹的。
在接下来的文章中,会有更多关于可见性、状态可变性及其影响的细节,但在我们建立更多的知识之前,我们将坚持总体概述。
更多的细节也可以在描述语言语法的链接中找到,我强烈建议大家深入了解一下语言的可能性。
总结
在这篇文章中,我们向自己介绍了智能合约和Solidity的基础知识。
首先,我们偷看了智能合约和 Solidity 领域的一些关键概念和术语。
第二,我们了解了Solidity是什么,以及我们可以用它来做哪些事情。
第三,我们冒险看了一个智能合约的简短例子,看到了它的一些法术--在我们的旅程中还有许多法术在等着我们。
第四,我们面对最常见的Solidity魔法,由可见性和状态可变性施展。