以太坊基础
以太坊(Ethereum)是一个建立在区块链技术之上的去中心化应用平台。它允许任何人在其平台上创建和使用通过区块链技术运行的去中心化应用(DApp)。
智能合约是以太坊上运行的程序。就像其他计算机程序一样,它由代码和数据组成。智能合约中的数据通常被称为“状态”,因为整个区块链可以看作是所有数据状态的一个确定的记录。
请不要被智能合约名称中的“智能”一词所误导,它与人工智能(AI:Artificial Intelligence)无关。智能合约的概念最早由尼克·萨博提出,实质上是将法律条款编写成可执行代码,从而使法律执行变得分散化。这与区块链上的程序无法篡改地执行的理念相契合,因此智能合约成为了区块链的重要概念。
账户&状态
当我们将合约部署到链上之后,它会用一个地址来表示(称为合约地址),这是一个以太坊网络中的一种帐户:合约账户。
账户在以太坊中是非常重要的概念,任何事情都离不开它。以太坊中有两类账户:
- 外部用户账户(EOAs)——该类账户由公钥-私钥对控制(由人控制)。
- 合约账户(CA:Contract Account)——该类账户由存储在账户中的代码控制。
外部用户账户和合约账户,它们都使用相同的地址格式来表示,在EVM层面是一样的,地址格式为:0xea674fdde714fd979de3edf0f56aa9716b898ec8
,是一个20字节的16进制数。
它们之间还有一个不可忽视的区别:只有外部用户账户可以发起交易(主动行为),而合约账户只能被动地响应操作,并且所有的手续费(Gas)必须由外部账户支付。
上图中绿色箭头表示发起交易,灰色箭头表示消息调用
无论账户类型如何,账户状态都由4个基本组成部分组成:
- nonce :如果账户是一个外部用户账户,nonce代表从此账户地址发送的交易序号。如果账户是一个合约账户,nonce代表此账户创建的合约序号。
提示:以太坊中有两种nonce , 一种是账号nonce——表示一个账号的交易数量;一种是工作量证明nonce——一个用于计算满足工作量证明的随机数。
balance:此地址拥有的以太币余额数量。单位是Wei,1 ether=10^18 wei。当向地址发送带有以太币的交易时,balance会随之改变。外部用户账户和合约账户都可以有余额;合约账户使用代码管理所拥有的资金,外部用户账户则是使用私钥签名来花费资金;合约账户存储了代码,外部用户账户则没有。
storageRoot:Merkle Patricia树的根节点哈希值。Merkle树会将此账户存储内容的哈希值进行编码,默认是空值。
codeHash:此账户代码的哈希值。对于合约账户,它是合约代码被哈希计算之后的结果作为codeHash保存。对于外部用户账户,codeHash 是一个空字符串的哈希值。
以太坊的全局共享状态由所有账户状态组成,这些状态以账户地址和账户状态之间的映射形式存储在区块的状态树中,如下图所示:
上述内容总结为:
以太币
以太币是一种货币,不同单位的货币就像法币中的不同面额,对于用户来说,最常用的单位是ether,1个ether通常也简称为以太。而对于开发者来说,可能更常使用wei,它是以太币的最小单位,其他单位包括finney和szabo,此外,wei还有几个衍生单位,包括Kwei、Mwei和Gwei。它们之间的换算关系如下:
以太坊虚拟机EVM
EVM(以太坊虚拟机)是一种虚拟计算机,用于执行以太坊区块链上的智能合约和去中心化应用程序。EVM是以太坊的核心组件之一,它负责处理和执行智能合约的代码。
EVM的工作原理与传统的计算机虚拟机类似,但它是专门为区块链和智能合约而设计的。智能合约是以太坊上的自动化合同,其代码在EVM上运行,并且可以执行各种任务,例如转移加密货币资金、管理数字资产、实施投票机制等。
EVM执行智能合约时,会将智能合约的字节码加载到内存中,然后按照预定的规则执行该字节码。EVM提供了一种安全的执行环境,以确保合约的正确执行,同时还实施了燃气(Gas)的概念,以防止恶意代码无限循环或耗尽计算资源。
以太坊客户端
以太坊客户端是连接到以太坊网络的节点程序,其中以太坊虚拟机(EVM)是客户端的重要组成部分。通过运行节点程序,您可以成为以太坊网络中的一个节点,参与区块链网络的操作和维护。
以太坊网络分为两个主要层次:
执行层(Execution Layer):执行层负责处理交易的执行,这包括智能合约的部署和执行。执行层客户端包括 Geth(Go语言实现)、Nethermind(C#实现)和 Erigon(Go语言实现)。这些客户端用于处理以太坊网络上的智能合约操作和交易执行。
共识层(Consensus Layer):共识层负责处理共识算法,即出块和确认交易。共识层客户端包括 Prysm(Go语言实现)和 Lighthouse(Rust实现)。这些客户端用于参与共识过程,确保区块链网络的一致性和安全性。
以太坊节点程序可以执行多种任务,包括创建账户、发起交易、部署智能合约、执行智能合约、挖矿出块等。常见的以太坊客户端包括 Geth 和 Parity。Geth 是以太坊官方社区开发的客户端,使用 Go 语言编写。Parity 也是一种以太坊客户端,使用 Rust 语言编写。开发者通常使用 Geth,因为它是官方维护的客户端,并且提供了广泛的文档和支持。
以太坊虚拟机(EVM)是以太坊节点客户端的核心组件,它执行智能合约的代码,并处理交易。EVM与节点客户端紧密结合,使得以太坊网络能够运行智能合约并处理交易。它是以太坊的计算引擎,负责执行智能合约代码并维护全网的状态。所有用户都通过节点与区块链网络进行交互,一般用户无需设置节点,因为运行节点需要大量资源。目前,有许多专业的节点服务提供商,例如 Infura 和 Alchemy,它们提供了便捷的方式来连接到区块链网络。
钱包
钱包是管理账户的重要工具,用户可以使用钱包创建账户、进行交易签名,并在需要时连接到区块链节点来执行交易。需要注意的是,钱包本身并不存储用户的资产,而是管理访问这些资产的密钥和签名功能。
常见的移动端钱包包括 ImToken、Trust Wallet 等,它们适用于一般用户,提供了便捷的方式来管理加密资产。
MetaMask 是一个浏览器插件,支持多种主流浏览器,如 Chrome、Firefox 和 Opera。它不仅可以用于管理账户,还可以用于部署和执行智能合约。开发者通常使用 MetaMask 与 Remix IDE 等工具结合使用,以便更轻松地开发和测试智能合约。
Gas
在以太坊上,智能合约的“图灵完备性”允许编写执行各种任务的程序。然而,为了防止恶意行为,以太坊引入了Gas机制,它是一种衡量执行操作所需工作量的单位。
Gas价格(Gas price)是Gas机制中的一个关键概念。每笔交易需要指定Gas预算(Gas limit)和愿意支付的Gas价格。Gas预算乘以Gas价格等于交易费用。如果Gas预算不足以覆盖实际Gas消耗,交易将失败,状态更改会被回滚。
另一个要注意的是,如果交易执行结束后还有剩余Gas,剩余Gas会退还给发起交易的账户。这使得Gas机制更加灵活,用户可以根据需求调整Gas价格和Gas预算。
Gas机制是以太坊的重要特性,它确保了网络的稳定性和安全性,同时也为矿工提供了适当的激励来执行交易和合约。
以太坊交易
以太坊交易可以分为以下三种类型:
- 普通交易:用于向其他地址转移以太币。
- 创建合约:用于在区块链上创建智能合约。这种交易类型的to字段为空,data字段包含智能合约的字节码。
- 调用合约函数:用于调用已部署的智能合约的函数。这种交易类型的to字段包含目标合约地址,data字段包含函数名称和参数。
普通交易示例(将一定数量的以太币转移到指定地址,如果愿意,还可以在交易中包含一条消息):
|
|
创建智能合约示例(TO字段留空表示创建智能合约,而DATA字段包含了智能合约的字节码):
|
|
调用智能合约示例(调用智能合约函数的信息封装在DATA字段中,将此交易信息发送到要要调用的智能合约的地址):
|
|
EVM兼容链和网络
EVM兼容链是那些设计和实现与以太坊虚拟机(EVM)兼容的区块链。这意味着它们可以运行与以太坊相同的智能合约,使用相同的编程语言和工具。这一兼容性使得开发者可以轻松将他们的以太坊应用迁移到这些链上,而无需进行大规模修改。目前,有许多流行的EVM兼容链,其中包括Polygon链、BNB链(BSC)、OK链、Avalanche C链、Fantom等。
每个EVM兼容链都有自己的共识机制和原生代币,这些代币用于支付交易手续费(Gas)。由于竞争和不同的设计选择,这些链通常具有较低的交易费用,这对于用户和开发者来说是一个吸引人的特点。
此外,还存在一种称为Layer2解决方案的技术,如Arbitrum和Optimism,它们也兼容EVM,但在第二层网络上运行,与主要区块链有所不同。这些解决方案通常用于扩展区块链的性能和吞吐量。
一些最初并不支持EVM的区块链项目也开始了EVM兼容性的开发,如:
- NEAR Protocol上的Aurora
- 波卡上的Moonbeam
- Cosmos上的Evmos
- Solana上的Neon
这些EVM生态链的存在进一步丰富了以太坊的生态系统。每个链都有自己的chainId和相应的节点RPC,您可以在MetaMask等钱包中通过添加自定义网络RPC来添加它们。
区块链网络
在每个区块链上,通常都存在多个网络,其中最重要的是主网(Mainnet)和测试网(Testnet)。
主网网络(Mainnet)
主网网络是真正的生产环境,其中的交易使用真实的代币进行结算。这是真正的价值交换发生的地方,因此需要小心谨慎。
测试网络(Testnet)
在主网上进行开发和测试可能会非常昂贵,因为每个操作都需要使用真实代币支付Gas费用。为了解决这个问题,每个区块链都提供了测试网络,这些网络上的代币没有实际价值,而且通常可以通过水龙头免费获得。开发者可以在测试网络上构建和测试智能合约,以确保它们在主网上能够正确运行
开发者网络或者节点
为了在本地进行区块链开发和测试,开发者通常会使用开发者网络或本地节点。这些网络是虚拟的区块链环境,可以在本地计算机上轻松运行。它们通常具有以下特点:
- 提供了模拟的区块链数据,包括交易和区块。
- 允许开发者创建测试账户,并分配虚拟代币以供测试使用。
- 可以在没有真实交易费用的情况下进行开发和测试。
一些流行的开发者网络包括Ganache和Hardhat。这些工具使开发者能够在本地模拟区块链环境,以便更轻松地开发、调试和测试智能合约。需要注意的是,这些本地模拟的区块链数据通常存储在内存中,因此在重启节点后数据将会丢失,这是开发过程中需要考虑的重要因素。
合约开发基础
Hello, Solidity
第一个合约:
|
|
合约是可部署到区块链的最小单元, 一个合约通常由状态变量(合约数据)和合约函数组成。
pragma solidity >=0.8.0
表示使用大于等于0.8.0 版本的编译编译 Counter 合约,版本表达式遵循npm版本语义。
Solidity使用contract
定义合约,类似其他语言的class
,在Solidity中,合约本身也是一个数据类型, 称为合约类型。合约部署到链上后,使用地址来表示一个合约。
constructor
定义的是构造函数,用来初始化合约。如果没有初始化代码也可以省略构造函数(此时,编译器会添加一个默认的构造函数constructor() public {}
)。状态变量的初始化,也可以在声明时进行指定,未指定时,默认为0。
构造函数示例:
合约代码编译成功后,会输出两个重要的内容: ABI (合约接口描述) 和 Bytecode 字节码。ABI 是 Application Binary Interface,即应用程序二进制接口,ABI 用来描述当前合约的所有接口,当我们与合约交互时,就需要使用 ABI。Bytecode 是部署合约所需的字节码(也称为创建时字节码),部署合约时,就是把该字节码作为交易的输入数据发送链上。
数据类型
变量和函数的可见性
合约添加的变量与函数,也是使用public
, private
等关键字来控制变量和函数是否可以被外部使用。Solidity对函数和状态变量提供了4种可见性:external
、public
、internal
、private
。
public
: 声明为public
的函数或变量,他们既可以在合约内部访问,也以合约接口形式暴露合约外部(其他合约或链下)调用。另外public
类型的状态变量,会自动创建一个同名的外部函数(称为访问器),来获取状态变量的值。
external
: ``
合约开发进阶
事件日志
日志(Logs)是存储在以太坊区块链上的数据结构,用于记录发生在智能合约中的事件。当事件被触发时,相应的日志条目会被创建并添加到区块中。这些信息可以由区块链上的外部消费者(比如前端应用和服务器应用)读取和做出响应。通过日志,智能合约可以生成一种轻量级的输出,无需更改合约的状态或者进行复杂的计算。
开发实战
foundry学习
Foundry 是一个Solidity框架,用于构建、测试、模糊、调试和部署Solidity智能合约, Foundry 的优势是以Solidity 作为第一公民,完全使用 Solidity 进行开发与测试。Foundry 的测试功能非常强大,通过作弊码来操纵区块链的状态, 可以方便我们模拟各种情况, 还支持基于属性的模糊测试。
|
|
安装安装后,有三个命令行工具forge
,cast
,anvil
组成
forge
: 用来执行初始化项目、管理依赖、测试、构建、部署智能合约 ;
cast
: 执行以太坊 RPC 调用的命令行工具, 进行智能合约调用、发送交易或检索任何类型的链数据
anvil
: 创建一个本地测试网节点, 也可以用来分叉其他与 EVM 兼容的网络。
项目初始化:forge init [project_name]
,init 命令会创建一个项目目录,并安装好forge-std
库。代码目录结构:
其中:
src
:智能合约目录script
:部署脚本文件lib
: 依赖库目录test
:智能合约测试用例文件夹foundry.toml
:配置文件,配置连接的网络URL 及编译选项。
Foundry 使用 Git submodule 来管理依赖库, .gitmodules 文件记录了目录与子库的关系:
合约编译:forge build
合约测试: forge test
安装第三方库:forge install [OPTIONS] <github username>/<github project>@<tag>
。不同于 npm,forge 会把整个第三方的库的 Git 仓库作为子模块放在lib目录下。命令操作后,.gitmodules
会添加新记录。