Solidity是一种用于在以太坊区块链上开发dApp(去中心化应用程序)的面向对象的高级语言.
区块链是计算机的对等网络,称为节点,它们共享网络中的所有数据和代码。.
因此,如果您是连接到区块链的设备,那么您就是网络中的一个节点,并且您与网络中的所有其他计算机节点通信(我们将在后面的教程中讨论如何在本地计算机上设置以太坊节点).
现在,您在区块链上拥有所有数据和代码的副本。不再需要中央服务器.
什么是以太坊?
简单来说,以太坊是一个基于区块链技术的开放软件平台,使开发人员能够构建和部署去中心化应用程序。.
虽然比特币区块链用于跟踪数字货币(比特币)的所有权,但以太坊区块链专注于运行去中心化应用程序的代码.
在以太坊区块链中,矿工们不是在挖比特币,而是在赚取以太币,这是一种为网络提供动力的加密代币。除了可交易的加密货币外,应用程序开发人员还使用Ether支付以太坊网络上的交易费和服务.
还有第二种令牌,用于向矿工支付将其交易包含在其区块中的费用,称为代币,每份智能合约的执行都需要发送一定量的代币,以吸引矿工将其放入区块链.
从基础开始
Solidity的代码封装在合同中.
以太坊区块链允许我们使用称为智能合约的东西在区块链上使用以太坊虚拟机(EVM)执行代码.
智能合约是我们应用程序所有业务逻辑赖以生存的地方–所有变量和功能都属于合约,这将是您所有项目的起点.
智能联系人以称为Solidity的编程语言编写,看起来像Javascript和C的混合体.
混音IDE
Remix是一种在线工具,可让您编写Solidity智能合约,然后进行部署和运行.
只是去 https://remix.ethereum.org 从您的浏览器开始,我们就可以开始编码了.
如您所见,您可以在Solidity和Vyper之间进行选择。两者都是用于编写智能合约的语言,Vyper类似于python,Solidity类似于javascript.
两者都可以编译为EVM字节码,有点像Javascript和Typescript。我们正在选择Solidity.
左侧是文件浏览器。默认情况下,有两个.sol文件,仅用于演示基本语法(ballot.sol是智能合约,ballot_test.sol是用于测试该智能合约的脚本).
您只需要单击该加号按钮,我们就可以开始编写第一个智能合约了.
所有solidity源代码都应以“版本编译指示”开头-该代码应使用的Solidity编译器版本的声明。这是为了防止将来的编译器版本出现问题,以免引入可能破坏代码的更改.
看起来像这样:
语用强度^ 0.4.25;
(对于0.4.25以上的Solidity版本)
或者
语用固执 >= 0.5.0 < 0.6.0;
(对于介于0.5.0和0.6.0之间的Solidity版本)
然后您通过输入保留字来创建合同 合同 以及.sol文件的名称(合同名称必须与文件名相匹配很重要,稍后我们将讨论原因)。就我们而言,
合同MyFirstContract {
}
让我们编译一下。您只需要导航到左侧的编译选项卡,然后单击大编译按钮即可。如果代码有问题,您会在此处看到错误和警告(对Solidity充满同情心,它仍然是“年轻的语言”).
根据我们目前的合同,一切都很好,因为我们真的什么都没做.
现在,我将故意向您显示一些错误。您可以从该下拉菜单中手动选择编译器.
让我们选择例如0.4.26版本。现在,再次编译它。现在,您将看到“尚未加载编译器”错误.
这是因为我们指定了pragma来使用高于0.5.0的编译器版本。只需再次更改编译器版本,错误就会消失.
好的,让我们现在编码!
我们将从简单的“ Hello world”代码开始,并获取和设置函数,以更加熟悉语法.
实体意义上的合同是驻留在以太坊区块链上特定地址的代码(其功能)和数据(其状态)的集合.
首先,让我们定义一个名为message的状态变量,其类型为字符串.
我们的get函数将返回变量消息的值,而set函数将为变量消息分配一个新值.
如何输入函数?
一,保留字 功能 然后是特定功能和参数的名称,之后 .
函数myFunction()返回(布尔值){
返回true;
}
功能可以 上市 或者 私人的. 如果某个功能是公共的,则可以在合同外部调用它。如果一个函数是私有的,则它的作用域是有限的,只能从其当前合同中调用(例如,从其他函数中调用).
这是所有功能可见性说明符的列表:
- 上市:在外部和内部可见(为存储/状态变量创建一个getter函数)
- 私人的:仅在当前合同中可见
- 外部的:仅在外部可见(仅对函数可见)–即只能通过(this.func)调用消息
- 内部的:仅在内部可见
功能可以 纯的, 看法, 或者 应付款. 如果某个函数未在区块链上写入任何数据,则强烈建议对其进行查看,因为视图函数不会花费任何精力.
这是所有功能修饰符的列表(还有状态变量,事件和事件参数的修饰符,但稍后会讨论它们):
- 纯的:不允许修改或访问状态.
- 看法:不允许修改状态.
- 应付款:允许他们与呼叫一起接收以太.
如果Function返回某个值,则需要使用保留字指定 退货 然后在常规括号中指定函数返回的类型。在我们的例子中,它将是字符串(因为我们返回的变量消息是字符串)
如果函数未返回任何值,则无需 退货 陈述.
要访问状态变量,您不需要前缀 这. 和其他语言一样.
因此,一种常见的做法是使用下划线语法编写函数参数 (_信息). 该约定来自Javascript,其中私有方法和变量以_开头.
需要说明的是,您的代码可以正常工作并且没有下划线,但是使用下划线会更简洁.
您会注意到保留字 记忆 在我们的代码中。如果您编写的代码没有内存,并且将编译指示设置为低于0.5。*的某个版本,则可以很好地工作,但是当您将编译器更改为高于0.5时。.
为什么会这样?
好吧,以太坊虚拟机有三个区域可以存储项目.
- 首先是 贮存, 所有合同状态变量所在的位置。每个合同都有其自己的存储,并且在函数调用之间是持久的,使用起来非常昂贵.
- 第二个是 记忆, 这用于保存临时值。在(外部)函数调用之间将其擦除,并且使用起来更便宜.
- 第三个是 堆, 用于保存较小的局部变量。它几乎可以免费使用,但只能容纳有限数量的值.
对于几乎所有类型,您都无法指定应将它们存储在何处,因为每次使用它们时都会复制它们.
但是,当您使用数组或结构时,以及从字符串的最新版本开始,编译器将强制您指定存储区.
因此,我们的代码现在如下所示:
语用强度^ 0.5.0;
合同MyFirstContract {
字符串信息;
函数get()公共视图返回(字符串内存){
返回消息;
}
函数集(字符串内存_message)public {
消息= _消息;
}
}
请注意,一些Solidity开发人员将这些可见性说明符分为几行,以使代码更简洁。因此我们的get函数可以这样写:
函数get()
上市
看法
返回(字符串)
{
返回消息;
}
真正由您决定如何选择编写函数.
让我们现在编译合同并进行测试.
要编译它,只需重复下面的步骤(编译.sol 按钮或 cmd / ctrl + S 从键盘开始,它将自动重新编译)
要实际查看其工作原理(如果编译不会产生错误),则需要部署合同.
为此,请从左侧导航至“部署”选项卡,为环境选择JavaScriptVM并单击“部署”按钮.
部署后,我们现在可以查看合同中的方法。现在让我们只关注屏幕的那一部分.
您会看到有两个按钮( & 设置)用于我们的两个公共功能。如果其中任何一个是私人的,我们在这里看不到.
如果单击获取按钮,EVM将执行我们的获取功能.
让我们看看效果如何.
我们有空字符串。不好,不可怕。但为什么?好吧,因为我们最初没有初始化消息变量.
只需快速停顿一下。我希望您介绍一下Remix Terminal。它在代码编辑器下,您可以在这里跟踪所有交易,查看交易是否成功执行,调试,查看详细信息(交易哈希等)等等。.
目前,我们有两项成功的交易。第一个是合同部署,它使我们付出了以太币(但不用担心,我们现在都在编辑器中,一切都是虚拟的),第二个是我们的电话 看法 功能.
好的,让我们现在回去。如果现在调用set函数会发生什么?
我们需要传递一个参数_message(例如“ Hello World”)并点击事务按钮以执行功能。您可以在终端中跟踪交易成功.
现在,让我们再次调用get函数。现在它返回我们的信息.
让我们对代码进行一些改进。我们没有初始化变量消息。我们开工吧.
合同MyFirstContract {
字符串消息= "你好,世界!";
函数get()公共视图返回(字符串内存){
返回消息;
}
函数集(字符串内存_message)public {
消息= _消息;
}
}
请注意,该消息现在是“ Hello world!”,当我们第一次调用get函数时,它不会返回空字符串.
要对此进行测试,我们需要编译合约(cmd / ctrl + S).
然后再次部署它。我们需要创建一个新的合同实例(由于我们所做的更改),并将其发布在区块链上.
只需从编辑器(当然不是从我们的虚拟区块链)中删除以前的版本,然后再次单击Deploy按钮。现在调用我们的get函数.
真好!现在调用设置功能.
再来一次.
凉.
现在让我们把信息 不变.
现在我们的代码:
语用强度^ 0.5.0;
合同MyFirstContract {
字符串常量消息= "你好世界!";
函数get()公共视图返回(字符串内存){
返回消息;
}
函数集(字符串内存_message)public {
消息= _消息;
}
}
当我们尝试编译它时,我们在set函数中出错。那是因为一个人不能改变一个常数的值.
现在我们将摆脱那个常数.
像这样初始化变量不是一个错误,但是如果在构造函数中执行此操作会更好。您可以使用以下方法在Solidity中编写构造函数:
构造函数()公共{
// 做一点事…
}
构造函数只是在智能合约部署期间被调用的另一个功能。我们的代码看起来有些不同,但工作原理相同.
语用强度^ 0.5.0;
合同MyFirstContract {
字符串信息;
构造函数()公共{
讯息= "你好世界!";
}
函数get()公共视图返回(字符串内存){
返回消息;
}
函数集(字符串内存_message)public {
消息= _消息;
}
}
您可以再次编译并测试它.
最后,可以更改状态变量的可见性。如果您使状态变量 上市 这意味着人们可以从合同之外要求其价值.
坚固性将为每个公共状态变量提供一个具有相同名称的方法,该方法可以称为常规函数(有点像getter函数).
这意味着,我们可以摆脱我们的get函数,只需将变量message声明为 上市, 而且我们的代码将保持相同的工作方式,它将变得更加整洁,并且一天将其部署到主网络的费用也将减少.
代码越大,执行它所需的气体就越多,并且运行我们的dApp的成本也会增加.
开发智能合约时,我们需要:
- 有效率的 –消耗的燃气费率应较低
- 精确 –一旦部署了智能合约,它就不能更改,并且每天24/7小时都是公共的,每一行代码都可以(假设有一个发现漏洞并可以利用您的dApp的黑客)
我们今天的最终代码如下:
语用强度^ 0.5.0;
合同MyFirstContract {
字符串公共消息;
构造函数()公共{
讯息= "你好世界!";
}
函数集(字符串内存_message)public {
消息= _消息;
}
}
让我们部署并测试它.
您可以看到该消息按钮。之所以创建它,是因为我们的状态变量消息是公共的.
如果我们调用它,它应该返回给我们一个通过构造函数(即“ Hello world!”)初始化的值。.
真好现在测试功能.
如何学习团结?
Solidity本身是一种非常简单的语言,但是要成为一名优秀的Solidity开发人员,需要了解一切在以太坊上的工作方式.
- Solidity是高级编程语言,其语法类似于ECMAScript(javascript).
- 它将编译为EVM字节码,只有EVM可以理解.
- 编译器称为Solc.
让我们以这个简单的合同为例:
语用强度^ 0.5.0;
合同示例{
uint = 10 + 5;
}
就那么简单。现在编译它。如果我们进入终端的合同详细信息,我们会看到很多信息.
在这种情况下,编译后的代码为:
0x6080604052600f600055348015601457600080fd5b5060358060226000396000f3fe6080604052600080fdfea165627a7a72305820bf75c57b7d8745a79baee513ead21a9eb8b075896f8e4c591c8916574d317c750029
这些长值是最终合同的十六进制表示形式,也称为字节码。 EVM仅了解字节码.
但是,如果出现问题,我们会陷入一些错误,例如,无法调试字节码.
操作码
字节码上方的语言是操作码。操作码是低级编程语言。 Solidity和Opcode例如像C和汇编语言.
因此,当我们需要调试一些失败的事务时,我们可以调试操作码.
您应该了解有关Solidity和调试的一件事-这非常困难。但并非不可能,所以让我们深入了解.
这是我们的示例合同的操作码:
0推1 60
02 PUSH1 40
04 MSTORE
05 PUSH1 0f
07 PUSH1 00
09店铺
10 CALLVALUE
11个DUP1
12零
13推1 14
15 JUMPI
16推1 00
18个DUP1
19 REVERT
20跳
流行音乐21
22推1 35
24个DUP1
25推1 22
27推1 00
29密码复制
30推1 00
32返回
33无效
34推1 80
36推1 40
38 MSTORE
39推1 00
41 DUP1
42个
43无效
44个LOG1
45 PUSH6 627a7a723058
52 SHA3
53无效
54 PUSH22 c57b7d8745a79baee513ead21a9eb8b075896f8e4c59
77无效
78个DUP10
79和
80连比
81无效
82平衡
83推29 750029
操作码是程序的低级人类可读指令。所有操作码都有对应的十六进制,例如 MSTORE 是 0x52.
EVM是堆栈机。它基于LIFO结构(后进先出)。为简单起见,想象一下将面包切片放在微波炉中,最后放入的切片是您取出的第一片.
在普通算术中,我们这样写方程:
10 + 2 * 2
答案是14,因为我们在加法之前进行乘法.
在堆栈机中,它按照LIFO原理工作:
2 2 * 10 +
这意味着,首先将2放入堆栈,然后再放入2,然后再进行乘法运算。结果是4坐在堆栈顶部。现在在4的顶部加上数字10,最后将2的数字相加。堆栈的最终值变为14.
将数据放入堆栈的动作称为PUSH指令,而从堆栈中删除数据的动作称为POP指令。很明显,我们在上面的示例中看到的最常见的操作码是PUSH1,这意味着将1个字节的数据放入堆栈中.
因此,此指令:
PUSH1 0x60
表示将1字节值“ 0x60”放入堆栈中。巧合的是,PUSH1的十六进制值也恰好是“ 0x60”。删除非强制性的“ 0x”,我们可以将此逻辑以字节码形式写为“ 6060”.
让我们走得更远.
PUSH1 0x60 PUSH1 0x40 MSTORE
MSTORE(0x52)接受2个输入,但不产生任何输出。上面的操作码表示:
PUSH1(0x60):将0x60放入堆栈.
PUSH1(0x40):将0x40放入堆栈.
MSTORE(0x52):分配0x60的内存空间并移至0x40的位置.
结果字节码为:
6060604052
实际上,在任何固定性字节码的开头,我们总会看到这个魔术数字“ 6060604052”,因为它是智能合约引导的方式.
更为复杂的是,不能将0x40或0x60解释为实数40或60。由于它们是十六进制,因此40实际上等于十进制的64(16¹x 4),而60等于十进制的96(16¹x 6)。.
简而言之,“ PUSH1 0x60 PUSH1 0x40 MSTORE”正在做的是分配96个字节的内存并将指针移到第64个字节的开头。现在,我们有64个字节用于暂存空间,而32个字节用于临时内存存储.
在EVM中,有3个地方可以存储数据。首先,在堆栈中。根据上面的示例,我们刚刚使用PUSH操作码在此处存储数据.
其次是使用MSTORE操作码的内存(RAM),最后是我们使用SSTORE存储数据的磁盘存储。将数据存储到存储所需的气体最昂贵,而将数据存储到堆栈所需的气体最便宜.
现在是时候回顾本教程中的Solidity代码,并回顾一下我们对保留字的了解 记忆 以及编译器如何迫使我们指定如何存储字符串,例如.
我们仅介绍了字节码和一些操作码的基础.
我们不需要知道操作码就可以开始编写智能合约!
另一方面,EVM错误处理仍然非常原始,在出错时可以方便地查看操作码.
结论
第一课比实际编码更多的理论知识,但是对于初学者来说,了解事物在以太坊上的工作方式非常重要。在接下来的教程中,我们将编写一些更有趣的代码,并学习如何在以太坊区块链上部署我们自己的令牌.
直到那时 &#128075;