ERC-1123: 修订后的以太坊智能合约打包标准
Authors | g. nicholas d’andrea (@gnidan), Piper Merriam (@pipermerriam), Nick Gheorghita (@njgheorghita), Danny Ryan (@djrtwo) |
---|---|
Created | 2018-06-01 |
Discussion Link | https://github.com/ethereum/EIPs/issues/1123 |
本 ERC 已被放弃,取而代之的是 ERC-2678 中定义的 EthPM V3 智能合约打包标准。
简单概要
一种描述智能合约软件包的数据格式。
摘要
本 EIP 定义了一种用于包清单文档的数据格式,用于表示一个或多个智能合约的包,可以选择性地包括源代码以及跨多个网络的任何/所有已部署实例。包清单是精简的 JSON 对象,将通过内容可寻址存储网络(例如 IPFS)进行分发。
本文档介绍了此格式版本 2 的正式规范的自然语言描述。
动机
本标准旨在鼓励以太坊开发生态系统朝着围绕代码重用的软件最佳实践发展。通过定义一个开放的、社区驱动的包数据格式标准,这项工作旨在通过提供一个通用解决方案来支持包管理工具的开发,该解决方案的设计考虑了观察到的常见实践。
作为本规范的第 2 版,本标准旨在解决先前版本(在 EIP-190 中定义)中发现的许多需要改进的方面。此版本:
-
概括了存储 URI 以表示任何内容可寻址 URI 方案,而不仅仅是 IPFS。
-
将发布锁文件重命名为包清单。
-
通过概括编译器信息格式来增加对 Solidity 以外的语言的支持。
-
重新定义链接引用以使其更灵活,以更直接的方式表示字节码中的任意间隙(除了地址之外)。
-
强制执行格式严格性,要求包清单不包含多余的空格,并按字母顺序对对象键进行排序,以防止哈希不匹配。
规范
本文档定义了 EthPM 包清单的规范。包清单提供了关于 包 的元数据,并且在大多数情况下,应提供关于打包合约及其依赖项的足够信息,以进行其合约的字节码验证。
注意
此规范的 托管版本 可通过 GitHub Pages 获得。此 EIP 和托管的 HTML 文档都是从相同的文档源自动生成的。
指导原则
本规范对文档生命周期做出了以下假设。
-
包清单旨在由包管理软件以编程方式生成,作为发布过程的一部分。
-
包清单将在包管理器执行诸如安装包依赖项或构建和部署新版本之类的任务期间被使用。
-
包清单通常不会与源代码一起存储,而是由包注册表存储或由包注册表引用并存储在类似于 IPFS 的东西中。
约定
RFC2119
本文档中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY” 和 “OPTIONAL” 应按照 RFC 2119 中的描述进行解释。
带前缀与不带前缀
带前缀的 十六进制值以 0x
开头。不带前缀的 值没有前缀。除非另有说明,否则所有十六进制值应使用 0x
前缀表示。
带前缀的 |
|
不带前缀的 |
|
文档格式
规范格式是单个 JSON 对象。包 必须 符合以下序列化规则。
-
文档 必须 紧密打包,这意味着没有换行符或额外的空格。
-
所有对象中的键必须按字母顺序排序。
-
同一对象中的重复键无效。
-
文档 必须 使用 UTF-8 编码。
-
文档 必须 没有尾随换行符。
文档规范
为包定义了以下字段。可以包含自定义字段。自定义字段应以 x-
为前缀,以防止与规范的未来版本发生名称冲突。
另请参阅 |
此规范的正式化(JSON-Schema)版本:package.spec.json |
跳转至 |
EthPM 清单版本:manifest_version
manifest_version
字段定义了本文档符合的规范版本。包 必须 包含此字段。
必需 |
是 |
键 |
|
类型 |
字符串 |
允许的值 |
|
包名称:package_name
package_name
字段为此包定义了一个人类可读的名称。包 必须 包含此字段。包名称 必须 以小写字母开头,并且只能由小写字母、数字字符和破折号字符 -
组成。包名称的长度 必须 不超过 214 个字符。
必需 |
是 |
键 |
|
类型 |
字符串 |
格式 |
必须 匹配正则表达式 |
包元数据:meta
meta
字段定义了关于包的元数据的位置,这些元数据本质上不是包安装所必需的,但可能很重要或方便手头有其他原因。此字段 应该 包含在所有包中。
必需 |
否 |
键 |
|
类型 |
版本:version
version
字段声明了此版本的版本号。所有包 必须 包含此值。此值 应该 符合 semver 版本编号规范。
必需 |
是 |
键 |
|
类型 |
字符串 |
源代码:sources
sources
字段定义了一个源代码树,它 应该 包含重新编译此版本中包含的合约所需的完整源代码树。源代码在键/值映射中声明。
键 |
|
类型 |
对象(字符串:字符串) |
格式 |
见下文。 |
格式
键 必须 是以 ./
开头的相对文件系统路径。
路径 必须 解析为当前工作目录中的路径。
值 必须 符合以下格式之一。
-
源代码字符串。
当该值为源代码字符串时,该键应被解释为文件路径。
-
如果结果文档是目录,则该键应被解释为目录路径。
-
如果结果文档是文件,则该键应被解释为文件路径。
合约类型:contract_types
contract_types
字段包含已包含在此版本中的 合约类型。包 应该 仅包含可以在此包的源文件中找到的合约类型。包 不应 包含来自依赖项的合约类型。包 不应 在版本的合约类型部分中包含抽象合约。
键 |
|
类型 |
对象(字符串:合约类型对象) |
格式 |
键 必须 是有效的 合约别名。 值 必须 符合 合约类型对象 定义。 |
部署:deployments
deployments
字段包含此版本已部署 合约实例 的链的信息,以及这些已部署合约实例的 合约类型 和其他部署详细信息。由此对象的 *BIP122 URI <#bip122-uris>*
键定义的链集 必须 是唯一的。
键 |
|
类型 |
对象(字符串:对象(字符串:合约实例对象)) |
格式 |
见下文。 |
格式
键 必须 是有效的 BIP122 URI 链定义。
值 必须 是符合以下格式的对象。
构建依赖项:build_dependencies
build_dependencies
字段定义了此项目依赖的以太坊 包 的键/值映射。
必需 |
否 |
键 |
|
类型 |
对象(字符串:字符串) |
格式 |
键 必须 是有效的 包名称,匹配正则表达式 值 必须 是有效的 IPFS URI,可以解析为有效的包。 |
定义
包中使用的不同对象的定义。所有对象都允许包含自定义字段。自定义字段应以 x-
为前缀,以防止与规范的未来版本发生名称冲突。
链接引用对象
链接引用 对象具有以下键/值对。所有链接引用都被假定为与某些相应的 字节码 相关联。
偏移量:offsets
offsets
字段是一个整数数组,对应于链接引用在字节码中出现的每个起始位置。位置从相应字节码的字节表示形式的开头开始,从 0 开始索引。如果它引用的位置超出了字节码的末尾,则此字段无效。
必需 |
是 |
类型 |
数组 |
长度:length
length
字段是一个整数,用于定义链接引用的字节长度。如果定义的链接引用的末尾超过了字节码的末尾,则此字段无效。
必需 |
是 |
类型 |
整数 |
名称:name
name
字段是一个字符串,必须 是有效的 标识符。任何 应该 与同一链接值链接的链接引用 应该 被赋予相同的名称。
必需 |
否 |
类型 |
字符串 |
格式 |
必须 符合 标识符 格式。 |
链接值对象
描述单个 链接值。
链接值对象 被定义为具有以下键/值对。
偏移量:offsets
offsets
字段定义了在相应的字节码中写入此链接值的 value
的位置。这些位置从相应字节码的字节表示形式的开头开始,从 0 开始索引。
必需 |
是 |
类型 |
整数 |
格式 |
见下文。 |
格式
整数数组,其中每个整数 必须 符合以下所有条件。
-
大于或等于零
-
严格小于相应字节码的不带前缀的十六进制表示形式的长度。
类型:type
type
字段定义了 value
类型,用于确定在 链接 相应字节码时编码的内容。
必需 |
是 |
类型 |
字符串 |
允许的值 |
|
值:value
value
字段定义了在 链接 相应字节码时应写入的值。
必需 |
是 |
类型 |
字符串 |
格式 |
根据 |
格式
对于静态值字面量(例如,地址),值 必须 是字节字符串
要引用当前包中的 合约实例 的地址,该值应为该合约实例的名称。
-
此值 必须 是有效的合约实例名称。
-
此链接值所属的合约实例所在的链定义必须在其键中包含此值。
-
此值 可能不会 引用与此链接值所属的合约实例相同的合约实例。
要从依赖树中的某个位置引用来自 包 的合约实例,该值构建如下。
-
设
[p1, p2, .. pn]
定义依赖树中的路径。 -
p1, p2, pn
中的每一个 必须 是有效的包名称。 -
p1
必须 存在于当前包的build_dependencies
的键中。 -
对于每个
pn
,其中n > 1
,pn
必须 存在于pn-1
的包的build_dependencies
的键中。 -
该值由字符串
<p1>:<p2>:<...>:<pn>:<contract-instance>
表示,其中所有<p1>
、<p2>
、<pn>
都是有效的包名称,并且<contract-instance>
是有效的 合约名称。 -
<contract-instance>
值 必须 是有效的 合约实例名称。 -
在由
<pn>
定义的依赖项的包中,必须满足以下所有条件:-
在
deployments
键下 必须 完全 定义一个链,该链与此链接值嵌套在下的链定义匹配。 -
<contract-instance>
值 必须 出现在匹配链的键中。
-
字节码对象
字节码对象具有以下键/值对。
字节码:bytecode
bytecode
字段是一个字符串,包含字节码的 0x
前缀的十六进制表示形式。
必需 |
是 |
类型 |
字符串 |
格式 |
|
链接引用:link_references
link_references
字段定义了相应字节码中需要 链接 的位置。
必需 |
否 |
类型 |
数组 |
格式 |
所有值 必须 是有效的 链接引用对象。另请参见下文。 |
格式
如果 任何 链接引用 在应用于相应的 bytecode
字段时无效,或者 如果任何链接引用相交,则此字段被视为无效。
相交被定义为重叠的两个链接引用。
链接依赖项:link_dependencies
link_dependencies
定义了已用于链接相应字节码的 链接值。
必需 |
否 |
类型 |
数组 |
格式 |
所有值 必须 是有效的 链接值对象。另请参见下文。 |
格式
此字段的验证包括以下内容:
-
两个链接值对象 不得 包含
offsets
的任何相同值。 -
已解析的
value
的长度 必须 等于相应 链接引用 的length
。
包元数据对象
包元数据对象被定义为具有以下键/值对。
作者:authors
authors
字段定义了此包作者的人类可读名称列表。包 可以 包含此字段。
必需 |
否 |
键 |
|
类型 |
数组(字符串) |
许可证:license
license
字段声明了发布此包所依据的许可证。此值 应该 符合 SPDX 格式。包 应该 包含此字段。
必需 |
否 |
键 |
|
类型 |
字符串 |
描述:description
description
字段提供了可能与包相关的其他详细信息。包 可以 包含此字段。
必需 |
否 |
键 |
|
类型 |
字符串 |
关键词:keywords
keywords
字段提供了与此包相关的相关关键词。
必需 |
否 |
键 |
|
类型 |
字符串列表 |
链接:links
links
字段提供了与此包相关的相关资源的 URI。如果可能,作者 应该 对以下常见资源使用以下键。
-
website
: 包的主要网站。 -
documentation
: 包文档 -
repository
: 项目源代码的位置。
键 |
|
类型 |
对象(字符串:字符串) |
合约类型对象
合约类型对象被定义为具有以下键/值对。
合约名称:contract_name
contract_name
字段为此 合约类型 定义了 合约名称。
必需 |
|
类型 |
字符串 |
格式 |
必须 是有效的 合约名称。 |
部署字节码:deployment_bytecode
deployment_bytecode
字段为此 合约类型 定义了字节码。
必需 |
否 |
类型 |
对象 |
格式 |
必须 符合 字节码对象 格式。 |
运行时字节码:runtime_bytecode
runtime_bytecode
字段为此 合约类型 定义了未链接的 0x
前缀的运行时 字节码 部分。
必需 |
否 |
类型 |
对象 |
格式 |
必须 符合 字节码对象 格式。 |
ABI:abi
必需 |
否 |
类型 |
列表 |
格式 |
必须 符合 以太坊合约 ABI JSON 格式。 |
Natspec:natspec
必需 |
否 |
类型 |
对象 |
格式 |
编译器:compiler
必需 |
否 |
类型 |
对象 |
格式 |
必须 符合 编译器信息对象 格式。 |
合约实例对象
合约实例对象 表示单个已部署的 合约实例,并被定义为具有以下键/值对。
合约类型:contract_type
contract_type
字段为此 合约实例 定义了 合约类型。 这可以引用此 包 中包含的任何合约类型或此 包清单 的 build_dependencies
部分中任何包依赖项中找到的任何合约类型。
必需 |
是 |
类型 |
字符串 |
格式 |
请参见下文。 |
格式
此字段的值必须符合此处的以下两种格式之一。
要引用此包中的合约类型,请使用 <contract-alias>
格式。
-
<contract-alias>
值必须是有效的 合约别名。 -
该值必须存在于此包的
contract_types
部分的键中。
要引用依赖项中的合约类型,请使用 <package-name>:<contract-alias>
格式。
-
<package-name>
值必须存在于此包的build_dependencies
的键中。 -
<contract-alias>
值必须是有效的 合约别名。 -
<package-name>
解析的包必须在contract_types
部分的键中包含<contract-alias>
值。
地址:address
必需 |
是 |
类型 |
字符串 |
格式 |
十六进制编码的 |
交易:transaction
transaction
字段定义了创建此 合约实例 的交易哈希。
必需 |
否 |
类型 |
字符串 |
格式 |
|
区块:block
block
字段定义了挖掘创建此合约实例的交易的区块哈希。
必需 |
否 |
类型 |
字符串 |
格式 |
|
运行时字节码:runtime_bytecode
runtime_bytecode
字段定义了此 合约实例 的字节码的运行时部分。 如果存在,则此字段的值取代此 合约实例 的 合约类型 中的 runtime_bytecode
。
必需 |
否 |
类型 |
对象 |
格式 |
必须符合 字节码对象 格式 |
此字节码的 link_references
中的每个条目必须在 link_dependencies
部分中具有相应的条目。
编译器:compiler
compiler
字段定义了在此 合约实例 编译期间使用的编译器信息。 此字段应该存在于所有包含 bytecode
或 runtime_bytecode
的 合约类型 中。
必需 |
否 |
类型 |
对象 |
格式 |
必须符合 编译器信息对象 格式 |
编译器信息对象
compiler
字段定义了在此 合约实例 编译期间使用的编译器信息。 此字段应该存在于本地声明 runtime_bytecode
的所有合约实例中。
编译器信息对象定义为具有以下键/值对。
名称 name
name
字段定义了编译中使用的编译器。
必需 |
是 |
键 |
|
类型 |
字符串 |
版本:version
version
字段定义编译器的版本。 该字段应该与操作系统无关(字符串中不包含操作系统),并且应采用 semver 格式的稳定版本形式,或者如果在 nightly 上构建,则应以 <semver>-<commit-hash>
形式表示,例如:0.4.8-commit.60cc1668
。
必需 |
是 |
键 |
|
类型 |
字符串 |
设置:settings
settings
字段定义了编译中使用的任何设置或配置。 对于 "solc"
编译器,这应该符合 编译器输入和输出说明。
必需 |
否 |
键 |
|
类型 |
对象 |
BIP122 URI
BIP122 URI 用于通过 BIP-122 规范的子集来定义区块链。
blockchain://<genesis_hash>/block/<latest confirmed block hash>
<genesis hash>
表示链上第一个区块的区块哈希,而 <latest confirmed block hash>
表示已可靠确认的最新区块的哈希(包管理器应可以自由选择所需的确认级别)。
原理
在本规范的创建过程中考虑了以下用例。
已拥有 |
一个包含不打算单独使用,而是作为基本合约,通过继承为其他合约提供功能的合约的包。 |
可转移 |
具有单个依赖项的包。 |
标准令牌 |
一个包含可重用合约的包。 |
安全数学库 |
一个包含包合约之一的已部署实例的包。 |
派珀币 |
一个包含来自依赖项的可重用合约的已部署实例的包。 |
托管 |
一个包含本地合约的已部署实例的包,该合约已链接到本地库的已部署实例。 |
钱包 |
一个包,其中包含本地合约的已部署实例,该实例已链接到来自依赖项的库的已部署实例。 |
带有发送功能的钱包 |
一个包含链接到深层依赖项的已部署实例的包。 |
每个用例都在前一个用例的基础上递增构建。
用例 的完整列表可以在本规范的托管版本上找到。
词汇表
ABI
应用程序二进制接口的 JSON 表示形式。 有关更多信息,请参见正式 规范。
地址
特定链上帐户的公共标识符
字节码
编译器生成的 EVM 指令集。 除非另有说明,否则应假定这是十六进制编码的,表示整数个字节,并且 带有前缀 0x
。
字节码可以是链接的,也可以是未链接的。(请参阅 链接)
未链接的字节码 |
合约的 EVM 指令的十六进制表示形式,其中包含需要 链接才能使合约正常运行的代码段。 未链接的代码段必须用零字节填充。 示例: |
链接的字节码 |
合约的 EVM 指令的十六进制表示形式,其中所有 链接引用都已替换为所需的 链接值。 示例: |
链定义
此定义源自 BIP122 URI。
格式为 blockchain://<chain_id>/block/<block_hash>
的 URI
-
chain_id
是链的创世哈希的无前缀十六进制表示形式。 -
block_hash
是链上区块哈希的无前缀十六进制表示形式。
如果创世区块哈希与 chain_id
匹配,并且可以在该链上找到由 block_hash
定义的区块,则认为链与链定义匹配。 多个链有可能与单个 URI 匹配,在这种情况下,所有链都被认为是有效的匹配项
内容可寻址 URI
任何包含加密哈希的 URI,该哈希可用于验证在 URI 上找到的内容的完整性。
URI 格式在 RFC3986 中定义
建议工具支持 IPFS 和 Swarm。
合约别名
这是用于引用特定 合约类型 的名称。 合约别名在单个 包 中必须是唯一的。
合约别名必须使用以下命名方案之一:
-
<contract-name>
-
<contract-name>[<identifier>]
<contract-name>
部分必须与此合约类型的 合约名称 相同。
[<identifier>]
部分必须与正则表达式 \[[-a-zA-Z0-9]{1,256}]
匹配。
合约实例
合约实例是 合约类型 的特定已部署版本。
所有合约实例在某些特定链上都有一个 地址。
合约实例名称
一个名称,指的是来自单个 包 部署的特定链上的特定 合约实例。 此名称对于给定链的所有其他合约实例必须是唯一的。 该名称必须符合正则表达式 [a-zA-Z][a-zA-Z0-9_]{0,255}
如果给定 合约类型 只有一个已部署的实例,则包管理器应该使用该合约类型的 合约别名 作为此名称。
如果给定合约类型有多个已部署的实例,则包管理器应该使用提供一些附加语义信息的名称,以帮助以有意义的方式区分两个已部署的实例。
合约名称
在源代码中找到的名称,用于定义特定的 合约类型。 这些名称必须符合正则表达式 [a-zA-Z][-a-zA-Z0-9_]{0,255}
。
在一个项目的源文件中可以有多个具有相同合约名称的合约。
合约类型
指的是包源中的特定合约。 该术语可用于指代抽象合约、普通合约或库。 如果两个合约具有相同的字节码,则它们属于同一合约类型。
示例:
contract Wallet {
...
}
Wallet
合约的已部署实例的类型将为 Wallet
。
标识符
通常指的是 包 中的命名实体。
与正则表达式 [a-zA-Z][-_a-zA-Z0-9]{0,255}
匹配的字符串
链接引用
合约字节码中需要链接的位置。 链接引用具有以下属性。
|
定义链接引用在字节码中开始的位置。 |
|
定义引用的长度。 |
|
(可选。)用于标识引用的字符串 |
链接值
链接值是可以插入 链接引用 位置的值
链接
包
应用程序的源代码或已编译字节码的分布,以及与作者、许可、版本控制等相关的元数据。
为简洁起见,术语包通常用作换喻,表示 包清单。
包清单
包的机器可读描述(有关包清单格式的信息,请参见 规范)。
带前缀
带有前导 0x
的 字节码 字符串。
示例 |
|
无前缀
非 带前缀。
示例 |
|
向后兼容性
本规范通过使用 manifest_version 属性来支持向后兼容性。 本规范对应于该字段的值 2
版本。
实施
此提交旨在与在常用开发工具中广泛实施的开发工作相吻合。
已知以下工具已开始或即将完成支持实施。
在实施中完全支持可能需要 进一步工作(如下所述)。
进一步工作
此 EIP 仅解决包描述的数据格式。 本规范的范围不包括:
-
包注册表接口定义
-
工具集成,或包在磁盘上的存储方式。
这些工作应该被认为是独立的,需要未来的依赖 EIP 提交。
致谢
本文档的作者要感谢 EIP-190 的原始作者,ETHPrize 的资金支持,所有社区的 贡献者 以及整个 Ethereum 社区。
版权
通过 CC0 放弃版权和相关权利。
Citation
Please cite this document as:
g. nicholas d’andrea (@gnidan), Piper Merriam (@pipermerriam), Nick Gheorghita (@njgheorghita), Danny Ryan (@djrtwo), "ERC-1123: 修订后的以太坊智能合约打包标准 [DRAFT]," Ethereum Improvement Proposals, no. 1123, June 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1123.