合同元数据

Solidity编译器自动生成一个JSON文件,即契约元数据,其中包含有关已编译契约的信息。您可以使用此文件查询编译器版本、使用的源代码、ABI和NatSpec文档,以便更安全地与契约交互并验证其源代码。

编译器默认情况下将元数据文件的IPFS散列附加到每个合同的字节码末尾(有关详细信息,请参阅下文),以便您可以以经过验证的方式检索该文件,而无需求助于集中数据提供程序。其他可用选项是Swarm哈希,而不是将元数据散列附加到字节码。这些可以通过 Standard JSON Interface .

您必须将元数据文件发布到IPFS、Swarm或其他服务,以便其他人可以访问它。您可以使用 solc --metadata 生成名为 ContractName_meta.json . 它包含IPFS和Swarm对源代码的引用,因此您必须上载所有源文件和元数据文件。

元数据文件的格式如下。下面的示例以人类可读的方式呈现。正确格式化的元数据应该正确使用引号,将空白减到最小,并对所有对象的键进行排序,以获得唯一的格式化。此处不允许使用注释,仅用于解释目的。

{
  // Required: The version of the metadata format
  "version": "1",
  // Required: Source code language, basically selects a "sub-version"
  // of the specification
  "language": "Solidity",
  // Required: Details about the compiler, contents are specific
  // to the language.
  "compiler": {
    // Required for Solidity: Version of the compiler
    "version": "0.4.6+commit.2dabbdf0.Emscripten.clang",
    // Optional: Hash of the compiler binary which produced this output
    "keccak256": "0x123..."
  },
  // Required: Compilation source files/source units, keys are file names
  "sources":
  {
    "myFile.sol": {
      // Required: keccak256 hash of the source file
      "keccak256": "0x123...",
      // Required (unless "content" is used, see below): Sorted URL(s)
      // to the source file, protocol is more or less arbitrary, but a
      // Swarm URL is recommended
      "urls": [ "bzzr://56ab..." ],
      // Optional: SPDX license identifier as given in the source file
      "license": "MIT"
    },
    "destructible": {
      // Required: keccak256 hash of the source file
      "keccak256": "0x234...",
      // Required (unless "url" is used): literal contents of the source file
      "content": "contract destructible is owned { function destroy() { if (msg.sender == owner) selfdestruct(owner); } }"
    }
  },
  // Required: Compiler settings
  "settings":
  {
    // Required for Solidity: Sorted list of remappings
    "remappings": [ ":g=/dir" ],
    // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated
    // and are only given for backwards-compatibility.
    "optimizer": {
      "enabled": true,
      "runs": 500,
      "details": {
        // peephole defaults to "true"
        "peephole": true,
        // inliner defaults to "true"
        "inliner": true,
        // jumpdestRemover defaults to "true"
        "jumpdestRemover": true,
        "orderLiterals": false,
        "deduplicate": false,
        "cse": false,
        "constantOptimizer": false,
        "yul": true,
        // Optional: Only present if "yul" is "true"
        "yulDetails": {
          "stackAllocation": false,
          "optimizerSteps": "dhfoDgvulfnTUtnIf..."
        }
      }
    },
    "metadata": {
      // Reflects the setting used in the input json, defaults to false
      "useLiteralContent": true,
      // Reflects the setting used in the input json, defaults to "ipfs"
      "bytecodeHash": "ipfs"
    },
    // Required for Solidity: File and name of the contract or library this
    // metadata is created for.
    "compilationTarget": {
      "myFile.sol": "MyContract"
    },
    // Required for Solidity: Addresses for libraries used
    "libraries": {
      "MyLib": "0x123123..."
    }
  },
  // Required: Generated information about the contract.
  "output":
  {
    // Required: ABI definition of the contract
    "abi": [/* ... */],
    // Required: NatSpec user documentation of the contract
    "userdoc": [/* ... */],
    // Required: NatSpec developer documentation of the contract
    "devdoc": [/* ... */]
  }
}

警告

由于默认情况下,结果协定的字节码包含元数据哈希,因此对元数据的任何更改都可能导致字节码的更改。这包括对文件名或路径的更改,并且由于元数据包含所使用的所有源的哈希,因此一个空白更改将导致不同的元数据和不同的字节码。

注解

上面的ABI定义没有固定的顺序。它可以随编译器版本而改变。不过,从Solidity版本0.5.12开始,数组保持一定的顺序。

字节码中元数据哈希的编码

因为我们可能支持将来检索元数据文件的其他方式,所以映射 {{"ipfs": <IPFS hash>, "solc": <compiler version>}} 已存储 CBOR -已编码。由于映射可能包含更多键(见下文),并且不容易找到编码的开头,因此它的长度被添加到两个字节的Big-Endian编码中。当前版本的固定性编译器通常会将以下内容添加到已部署字节码的末尾

0xa2
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
0x00 0x33

因此,为了检索数据,可以检查已部署字节码的末尾以匹配该模式,并使用IPFS哈希来检索文件。

虽然solc的发布版本使用上面所示的版本的3字节编码(主要、次要和补丁版本号各一个字节),但是预发布版本将使用完整的版本字符串,包括提交哈希和生成日期。

注解

cbor映射还可以包含其他键,因此最好完全解码数据,而不是从开始依赖它。 0xa264 .例如,如果使用任何影响代码生成的实验特性,则映射也将包含 "experimental": true .

注解

编译器当前默认使用元数据的IPFS哈希,但将来也可能使用bzzr1哈希或其他哈希,因此不要依赖此序列开始 0xa2 0x64 'i' 'p' 'f' 's' . 我们还可以向这个CBOR结构添加额外的数据,所以最好的选择是使用适当的CBOR解析器。

自动生成接口和natspec的用法

元数据的使用方式如下:想要与合同交互的组件(例如Mist或任何钱包)检索合同的代码,从中检索文件的IPFS/Swarm散列。这个文件被JSON解码成上面这样的结构。

然后,组件可以使用ABI自动生成合同的基本用户界面。

此外,钱包可以使用natspec用户文档在用户与合同交互时向用户显示确认消息,并请求授权进行交易签名。

有关更多信息,请阅读 Ethereum Natural Language Specification (NatSpec) format .

源代码验证的用法

为了验证编译,可以通过元数据文件中的链接从IPFS/Swarm检索源代码。正确版本的编译器(被检查为“官方”编译器的一部分)将使用指定的设置对该输入进行调用。生成的字节码与创建事务的数据或 CREATE 操作码数据。这将自动验证元数据,因为它的哈希是字节码的一部分。多余的数据对应于构造函数的输入数据,这些数据应根据接口进行解码并呈现给用户。

In the repository sourcify (npm package) you can see example code that shows how to use this feature.