贡献
我们始终欢迎您的帮助,并且有许多方法可以为 Solidity 做出贡献。
我们尤其感谢您在以下方面的支持。
报告问题。
修复并回复 Solidity 的 GitHub 问题,特别是那些被标记为 “好的第一个问题” 的问题,这些问题是为外部贡献者设计的入门问题。
改进文档。
翻译 文档到更多语言。
回复其他用户在 StackExchange 和 Solidity Gitter 聊天 上提出的问题。
通过在 Solidity 论坛 中提出语言变更或新功能建议,并提供反馈,参与语言设计过程。
要开始,您可以尝试 从源代码构建 以熟悉 Solidity 的组件和构建过程。此外,熟悉用 Solidity 编写智能合约可能也很有用。
请注意,此项目是在 贡献者行为准则 下发布的。通过参与此项目(在问题、拉取请求或 Gitter 频道中),您同意遵守其条款。
团队电话会议
如果您有需要讨论的问题或拉取请求,或者有兴趣了解团队和贡献者正在做什么,您可以加入我们的公开团队电话会议。
每周三下午 3 点 CET/CEST。
电话会议在 Jitsi 上举行。
如何报告问题
要报告问题,请使用 GitHub 问题追踪器。在报告问题时,请提及以下详细信息。
Solidity 版本。
源代码(如果适用)。
操作系统。
重现问题的步骤。
实际行为与预期行为。
将导致问题的源代码缩减到最小程度总是非常有帮助的,有时甚至可以澄清误解。
对于有关语言设计的技术讨论,在 Solidity 论坛 中发帖是正确的位置(参见 Solidity 语言设计)。
拉取请求工作流程
为了做出贡献,请从 develop
分支创建您的分支,并在那里进行您的更改。您的提交消息应该详细说明您进行更改的原因以及您做了什么(除非这是一个微小的更改)。
如果您在创建分支后需要从 develop
中拉取任何更改(例如,为了解决潜在的合并冲突),请避免使用 git merge
,而应使用 git rebase
您的分支。这将有助于我们更轻松地查看您的更改。
此外,如果您正在编写一个新功能,请确保您在 test/
下添加了相应的测试用例(见下文)。
但是,如果您正在进行较大的更改,请首先与 Solidity 开发 Gitter 频道(与上面提到的频道不同 - 这个频道侧重于编译器和语言开发,而不是语言使用)进行协商。
新功能和错误修复应添加到 Changelog.md
文件中:请在适用时遵循之前条目中的风格。
最后,请确保您遵守此项目的 编码风格。此外,即使我们进行 CI 测试,也请测试您的代码并确保它在本地构建后才能提交拉取请求。
我们强烈建议在提交拉取请求之前查看我们的 审查清单。我们会彻底审查每一个 PR,并帮助您将其纠正,但有很多常见的错误可以轻松避免,从而使审查更加顺利。
感谢您的帮助!
运行编译器测试
先决条件
为了运行所有编译器测试,您可能需要可选安装一些依赖项(evmone,libz3)。
在 macOS 系统上,一些测试脚本需要安装 GNU coreutils。这可以通过使用 Homebrew 来轻松完成: brew install coreutils
。
在 Windows 系统上,请确保您有创建符号链接的权限,否则一些测试可能会失败。管理员应该具有该权限,但您也可以 授予其他用户权限 或 启用开发人员模式。
运行测试
Solidity 包含不同类型的测试,大多数测试捆绑在 Boost C++ 测试框架 应用程序 soltest
中。运行 build/test/soltest
或其包装器 scripts/soltest.sh
对于大多数更改已经足够了。
脚本 ./scripts/tests.sh
会自动执行大多数 Solidity 测试,包括捆绑在 Boost C++ 测试框架 应用程序 soltest
(或其包装器 scripts/soltest.sh
)中的测试,以及命令行测试和编译测试。
测试系统会自动尝试发现 evmone 的位置,以运行语义测试。
库 evmone
必须位于当前工作目录相对于目录 deps
或 deps/lib
,或其父目录或其父目录的父目录中。或者,可以通过环境变量 ETH_EVMONE
指定库 evmone
共享对象的明确位置。
evmone
主要用于运行语义和燃气测试。如果您没有安装它,您可以通过将 --no-semantic-tests
标志传递给 scripts/soltest.sh
来跳过这些测试。
库 evmone
应该在 Linux 上以文件扩展名 .so
结尾,在 Windows 系统上以 .dll
结尾,在 macOS 上以 .dylib
结尾。
为了运行 SMT 测试,必须安装 libz3
库,并且在编译器配置阶段可以被 cmake
找到。
如果您的系统上没有安装 libz3
库,您应该在运行 ./scripts/tests.sh
之前导出 SMT_FLAGS=--no-smt
或运行 ./scripts/soltest.sh --no-smt
来禁用 SMT 测试。这些测试是 libsolidity/smtCheckerTests
和 libsolidity/smtCheckerTestsJSON
。
注意
要获得 Soltest 运行的所有单元测试列表,请运行 ./build/test/soltest --list_content=HRF
。
为了更快地获得结果,您可以运行一个子集或特定的测试。
要运行一个子集的测试,可以使用过滤器:./scripts/soltest.sh -t TestSuite/TestName
,其中 TestName
可以是通配符 *
。
例如,要运行 yul 消歧器的所有测试,可以运行:./scripts/soltest.sh -t "yulOptimizerTests/disambiguator/*" --no-smt
。
./build/test/soltest --help
包含有关所有可用选项的详细帮助信息。
特别是参见
show_progress (-p) 用于显示测试完成情况,
run_test (-t) 用于运行特定的测试用例,以及
report-level (-r) 用于提供更详细的报告。
注意
在 Windows 环境中工作并想要运行上述基本集,但没有 libz3 的用户,可以使用 Git Bash 执行以下命令:./build/test/Release/soltest.exe -- --no-smt
。如果在普通命令提示符中运行,则使用 .\build\test\Release\soltest.exe -- --no-smt
。
如果想要使用 GDB 进行调试,请确保构建方式与“通常”不同。例如,可以在 build
文件夹中运行以下命令
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
这会创建符号,以便在使用 --debug
标志调试测试时,可以访问其中的函数和变量,并在其处设置断点或打印。
CI 会运行额外的测试(包括 solc-js
和测试第三方 Solidity 框架),这些测试需要编译 Emscripten 目标。
编写和运行语法测试
语法测试检查编译器是否为无效代码生成正确的错误消息,以及是否正确接受有效代码。它们存储在 tests/libsolidity/syntaxTests
文件夹内的各个文件中。这些文件必须包含注释,说明相应测试的预期结果。测试套件会编译并根据给定的预期检查它们。
例如:./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol
contract test {
uint256 variable;
uint128 variable;
}
// ----
// DeclarationError: (36-52): Identifier already declared.
语法测试至少必须包含要测试的合约本身,后面跟着分隔符 // ----
。分隔符后面的注释用于描述预期的编译器错误或警告。数字范围表示错误发生的位置。如果想要让合约在没有任何错误或警告的情况下编译,可以省略分隔符和它后面的注释。
在上面的示例中,状态变量 variable
被声明了两次,这是不允许的。这将导致 DeclarationError
,表明标识符已声明。
isoltest
工具用于这些测试,它位于 ./build/test/tools/
。它是一个交互式工具,允许使用你喜欢的文本编辑器编辑失败的合约。让我们尝试通过删除 variable
的第二次声明来破坏此测试
contract test {
uint256 variable;
}
// ----
// DeclarationError: (36-52): Identifier already declared.
再次运行 ./build/test/tools/isoltest
会导致测试失败
syntaxTests/double_stateVariable_declaration.sol: FAIL
Contract:
contract test {
uint256 variable;
}
Expected result:
DeclarationError: (36-52): Identifier already declared.
Obtained result:
Success
isoltest
会将预期结果打印在实际结果旁边,并提供一种方法来编辑、更新或跳过当前合约文件,或退出应用程序。
它为失败的测试提供了几种选项
edit
:isoltest
尝试在编辑器中打开合约,以便你可以调整它。它会使用命令行中给定的编辑器(例如isoltest --editor /path/to/editor
),或者使用环境变量EDITOR
,或者使用/usr/bin/editor
(按此顺序)。update
: 更新要测试合约的预期结果。这会通过删除未满足的预期并添加缺少的预期来更新注释。然后重新运行测试。skip
: 跳过执行此特定测试。quit
: 退出isoltest
。
所有这些选项都适用于当前合约,除了 quit
会停止整个测试过程。
自动更新上面的测试会将其更改为
contract test {
uint256 variable;
}
// ----
并重新运行测试。现在它又通过了
Re-running test case...
syntaxTests/double_stateVariable_declaration.sol: OK
注意
为合约文件选择一个名称,该名称解释了它测试的内容,例如 double_variable_declaration.sol
。不要将多个合约放入单个文件中,除非你正在测试继承或跨合约调用。每个文件应该测试新功能的一个方面。
命令行测试
我们的端到端命令行测试套件检查了编译器二进制文件在各种场景中的整体行为。这些测试位于 test/cmdlineTests/,每个子目录一个,可以使用 cmdlineTests.sh
脚本来执行。
默认情况下,脚本会运行所有可用的测试。你也可以提供一个或多个 文件名模式,在这种情况下,只有与至少一个模式匹配的测试才会被执行。还可以通过在模式前添加 --exclude
来排除与特定模式匹配的文件。
默认情况下,脚本假定在工作副本中的 build/
子目录中有一个 solc
二进制文件。如果在源代码树外部构建编译器,可以使用 SOLIDITY_BUILD_DIR
环境变量来指定构建目录的不同位置。
示例
export SOLIDITY_BUILD_DIR=~/solidity/build/
test/cmdlineTests.sh "standard_*" "*_yul_*" --exclude "standard_yul_*"
上面的命令将运行来自以下目录的测试:以 test/cmdlineTests/standard_
开头的目录,以及 test/cmdlineTests/
的子目录,这些子目录的名称中包含 _yul_
,但不会执行名称以 standard_yul_
开头的任何测试。它还将假定你主目录中的文件 solidity/build/solc/solc
是编译器二进制文件(除非你使用的是 Windows - 那么是 solidity/build/solc/Release/solc.exe
)。
命令行测试有几种类型
标准 JSON 测试:至少包含一个
input.json
文件。一般可能包含input.json
: 要传递给命令行上--standard-json
选项的输入文件。output.json
: 预期的标准 JSON 输出。args
: 传递给solc
的额外命令行参数。
CLI 测试:至少包含一个
input.*
文件(除input.json
之外)。一般可能包含input.*
: 一个输入文件,其名称将作为命令行参数提供给solc
。通常是input.sol
或input.yul
。args
: 传递给solc
的额外命令行参数。stdin
: 要通过标准输入传递给solc
的内容。output
: 预期的标准输出内容。err
: 预期的标准错误输出内容。exit
: 预期的退出代码。如果未提供,则预期为零。
脚本测试:包含一个
test.*
文件。一般可能包含test.*
: 要运行的单个脚本,通常是test.sh
或test.py
。该脚本必须是可执行的。
通过 AFL 运行模糊器
模糊测试是一种在或多或少随机输入上运行程序的技术,以查找异常执行状态(分段错误、异常等)。现代模糊器很聪明,在输入中进行定向搜索。我们有一个专门的二进制文件,称为 solfuzzer
,它将源代码作为输入,并在遇到内部编译器错误、分段错误或类似错误时失败,但在例如代码包含错误时不会失败。这样,模糊测试工具就可以找到编译器中的内部问题。
我们主要使用 AFL 进行模糊测试。你需要从你的存储库(afl、afl-clang)下载并安装 AFL 包,或者手动构建它们。接下来,使用 AFL 作为编译器构建 Solidity(或仅构建 solfuzzer
二进制文件)
cd build
# if needed
make clean
cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++
make solfuzzer
在此阶段,你应该能够看到类似以下内容的消息
Scanning dependencies of target solfuzzer
[ 98%] Building CXX object test/tools/CMakeFiles/solfuzzer.dir/fuzzer.cpp.o
afl-cc 2.52b by <[email protected]>
afl-as 2.52b by <[email protected]>
[+] Instrumented 1949 locations (64-bit, non-hardened mode, ratio 100%).
[100%] Linking CXX executable solfuzzer
如果未显示工具消息,请尝试切换指向 AFL 的 clang 二进制文件的 cmake 标志
# if previously failed
make clean
cmake .. -DCMAKE_C_COMPILER=path/to/afl-clang -DCMAKE_CXX_COMPILER=path/to/afl-clang++
make solfuzzer
否则,模糊器在执行时会停止并显示错误,提示二进制文件未经工具处理
afl-fuzz 2.52b by <[email protected]>
... (truncated messages)
[*] Validating target binary...
[-] Looks like the target binary is not instrumented! The fuzzer depends on
compile-time instrumentation to isolate interesting test cases while
mutating the input data. For more information, and for tips on how to
instrument binaries, please see /usr/share/doc/afl-doc/docs/README.
When source code is not available, you may be able to leverage QEMU
mode support. Consult the README for tips on how to enable this.
(It is also possible to use afl-fuzz as a traditional, "dumb" fuzzer.
For that, you can use the -n option - but expect much worse results.)
[-] PROGRAM ABORT : No instrumentation detected
Location : check_binary(), afl-fuzz.c:6920
接下来,你需要一些示例源文件。这使得模糊器更容易找到错误。可以从语法测试中复制一些文件,或者从文档或其他测试中提取测试文件
mkdir /tmp/test_cases
cd /tmp/test_cases
# extract from tests:
path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp
# extract from documentation:
path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs
AFL 文档指出,语料库(初始输入文件)不应该太大。文件本身不应该大于 1 kB,并且每个功能最多应该有一个输入文件,所以最好从少量开始。还有一个名为 afl-cmin
的工具可以修剪会导致二进制文件行为类似的输入文件。
现在运行模糊器(-m
会将内存大小扩展到 60 MB)
afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
模糊器会在 /tmp/fuzzer_reports
中创建导致 solfuzzer
失败的源文件。它通常会找到许多导致相同错误的类似源文件。可以使用 scripts/uniqueErrors.sh
工具过滤掉唯一的错误。
Whiskers
Whiskers 是一种与 Mustache 类似的字符串模板系统。编译器在各个地方使用它来提高代码的可读性,从而提高代码的可维护性和可验证性。
语法与 Mustache 有很大不同。模板标记 {{
和 }}
被替换为 <
和 >
以便于解析并避免与 Yul 冲突(符号 <
和 >
在内联汇编中无效,而 {
和 }
用于分隔块)。另一个限制是列表只解析一层深度,它们不会递归。这在将来可能会改变。
一个粗略的规范如下:
任何出现 <name>
的地方,都会被提供给模板系统的变量 name
的字符串值替换,没有任何转义,也没有迭代替换。一个区域可以被 <#name>...</name>
分隔。它被替换为其内容的多个串联,这些串联与提供给模板系统的变量集的数量一样多,每次替换任何 <inner>
项为它们各自的值。顶级变量也可以在这些区域内使用。
还存在 <?name>...<!name>...</name>
形式的条件语句,其中模板替换在第一段或第二段中递归地继续,具体取决于布尔参数 name
的值。如果使用 <?+name>...<!+name>...</+name>
,则检查字符串参数 name
是否非空。
文档风格指南
在以下部分中,您将找到专门针对 Solidity 文档贡献的样式建议。
英语
使用国际英语,除非使用项目或品牌名称。尽量减少使用当地俚语和参考资料,使您的语言对所有读者尽可能清晰。以下是一些有帮助的参考资料:
标题的标题大小写
对标题使用 标题大小写。这意味着将标题中的所有主要词的首字母大写,但冠词、连词和介词除外,除非它们是标题的开头。
例如,以下都是正确的:
标题的标题大小写。
对标题使用标题大小写。
局部和状态变量名称。
布局顺序。
扩展缩写
对单词使用扩展的缩写,例如
“Do not” 而不是 “Don’t”。
“Can not” 而不是 “Can’t”。
主动语态和被动语态
主动语态通常推荐用于教程式文档,因为它可以帮助读者理解谁或什么正在执行一项任务。但是,由于 Solidity 文档是教程和参考内容的混合,因此被动语态有时更适用。
总结一下:
对技术参考使用被动语态,例如语言定义和以太坊虚拟机的内部机制。
在描述如何应用 Solidity 某个方面的建议时,使用主动语态。
例如,以下内容使用被动语态,因为它指定了 Solidity 的一个方面
函数可以被声明为
pure
,在这种情况下,它们承诺不会从状态中读取或修改状态。
例如,以下内容使用主动语态,因为它讨论了 Solidity 的一个应用
调用编译器时,您可以指定如何发现路径的第一个元素,以及路径前缀重映射。
常用术语
“函数参数”和“返回值变量”,而不是输入和输出参数。
代码示例
CI 进程测试所有以 pragma solidity
、contract
、library
或 interface
开头的代码块格式的代码示例,使用 ./test/cmdlineTests.sh
脚本,当您创建 PR 时。如果您添加新的代码示例,请确保它们有效并通过测试,然后再创建 PR。
确保所有代码示例都以一个 pragma
版本开头,该版本跨越合同代码有效的最大范围。例如 pragma solidity >=0.4.0 <0.9.0;
。
运行文档测试
通过运行 ./docs/docs.sh
来确保您的贡献通过我们的文档测试,该测试会安装文档所需的依赖项,并检查任何问题,例如链接中断或语法问题。
Solidity 语言设计
要积极参与语言设计过程并分享您关于 Solidity 未来发展的想法,请加入 Solidity 论坛。
Solidity 论坛是提出和讨论新语言特性及其在构思或修改现有特性的早期阶段的实现的地方。
一旦提案变得更加具体,它们的实现也会在 Solidity GitHub 仓库 中以问题形式进行讨论。
除了论坛和问题讨论之外,我们还会定期举办语言设计讨论电话会议,在这些电话会议中,会详细讨论选定的主题、问题或特性实现。这些电话会议的邀请会通过论坛分享。
我们还会在论坛中分享与语言设计相关的反馈调查和其他内容。
如果您想知道团队在实施新特性方面处于什么位置,您可以在 Solidity GitHub 项目 中关注实施状态。设计积压中的问题需要进一步的规范,要么在语言设计电话会议中讨论,要么在常规团队电话会议中讨论。您可以通过从默认分支(develop)更改为 breaking 分支 来查看下一个重大版本的即将发布的更改。
对于临时情况和问题,您可以通过 Solidity-dev Gitter 频道 与我们联系,这是一个专门用于围绕 Solidity 编译器和语言开发进行对话的聊天室。
我们很乐意听取您关于如何改进语言设计过程以使其更具协作性和透明度的想法。