ethPM
## 概览
这是以太坊智能合约打包规范 V3 的 Python 实现,由 ERC 190 、 ERC 1123 、 ERC 1319 中的讨论驱动。
作为一个底层库,帮助开发人员利用 ethPM 规范。包括…
- 解析和验证包。
- 构造和发布新的包。
- 提供对合约工厂类的访问。
- 提供对包的所有部署的访问。
- 验证包字节码是否匹配编译输出。
- 验证部署的字节码是否匹配编译输出。
- 对包的依赖项的访问。
- 与编译元数据的本机集成。
包裹
Package
对象的功能很像由web3
提供的Contract
类。不是实例化由ethpm
提供的基类,而是使用一个classmethod
为给定的包生成一个新的Package
类。
Package
对象必须用有效的web3
对象实例化。
>>> from ethpm import Package, get_ethpm_spec_dir
>>> from web3 import Web3
>>> w3 = Web3(Web3.EthereumTesterProvider())
>>> ethpm_spec_dir = get_ethpm_spec_dir()
>>> owned_manifest_path = ethpm_spec_dir / 'examples' / 'owned' / 'v3.json'
>>> OwnedPackage = Package.from_file(owned_manifest_path, w3)
>>> assert isinstance(OwnedPackage, Package)
为了进一步了解如何使用 web3 与 EthPM 包进行交互,请查看 示例页面 。
性能
每个Package
公开了以下属性。
Package.w3
当前在此Package
上设置的Web3
实例。包中可用的部署被自动过滤,只包含那些属于当前设置的w3
实例的部署。
Package.manifest
用于实例化一个Package
的清单字典。
方法
每个Package
公开以下方法。
确认
Package
类目前验证以下内容。
- 用于实例化一个
Package
对象的清单符合 EthPM V3 清单规范,并且被紧密打包,关键字按字母顺序排序,没有尾随换行符。
可链接合约
Py-EthPM 使用自定义子类Web3.contract.Contract
来管理可能需要字节码链接的合约工厂和实例。为了创建一个可部署的合约工厂,合约类型的abi
和deploymentBytecode
必须在包的清单中可用。
>>> from eth_utils import is_address
>>> from web3 import Web3
>>> from ethpm import Package, ASSETS_DIR
>>> w3 = Web3(Web3.EthereumTesterProvider())
>>> escrow_manifest_path = ASSETS_DIR / 'escrow' / 'with_bytecode_v3.json'
>>> # Try to deploy from unlinked factory
>>> EscrowPackage = Package.from_file(escrow_manifest_path, w3)
>>> EscrowFactory = EscrowPackage.get_contract_factory("Escrow")
>>> assert EscrowFactory.needs_bytecode_linking
>>> escrow_instance = EscrowFactory.constructor(w3.eth.accounts[0]).transact()
Traceback (most recent call last):
...
ethpm.exceptions.BytecodeLinkingError: Contract cannot be deployed until its bytecode is linked.
>>> # Deploy SafeSendLib
>>> SafeSendFactory = EscrowPackage.get_contract_factory("SafeSendLib")
>>> safe_send_tx_hash = SafeSendFactory.constructor().transact()
>>> safe_send_tx_receipt = w3.eth.wait_for_transaction_receipt(safe_send_tx_hash)
>>> # Link Escrow factory to deployed SafeSendLib instance
>>> LinkedEscrowFactory = EscrowFactory.link_bytecode({"SafeSendLib": safe_send_tx_receipt.contractAddress})
>>> assert LinkedEscrowFactory.needs_bytecode_linking is False
>>> escrow_tx_hash = LinkedEscrowFactory.constructor(w3.eth.accounts[0]).transact()
>>> escrow_tx_receipt = w3.eth.wait_for_transaction_receipt(escrow_tx_hash)
>>> assert is_address(escrow_tx_receipt.contractAddress)
性能
LinkableContract.unlinked_references
部署字节码的链接引用数据列表,如果出现在用于生成LinkableContract
工厂的清单数据中。部署字节码链接引用数据必须出现在清单中,以便为需要字节码链接的合约生成工厂。
LinkableContract.linked_references
运行时字节码的链接引用数据列表,如果出现在用于生成LinkableContract
工厂的清单数据中。如果你想使用web 3T3】Deployer 工具,那么运行时字节码链接引用数据必须出现在清单中。
LinkableContract.needs_bytecode_linking
一个布尔属性,用于指示协定工厂是否有未解析的链接引用,必须先解析这些引用,然后才能在给定地址部署或实例化新的协定实例。
方法
*classmethod* LinkableContract.link_bytecode(*attr_dict*)
该方法返回一个新创建的合约工厂,其中应用了在attr_dict
中定义的链接引用。对于所有未链接的链接引用,该方法期望attr_dict
的类型为Dict[
contract_name:
address]
。
URI 计划和后端
BaseURIBackend
Py-EthPM
使用BaseURIBackend
作为其所有 URI 后端的父类。要编写自己的后端,它必须实现以下方法。
BaseURIBackend.can_resolve_uri(*uri*)
返回一个布尔值,表明后端是否能够将给定的 URI 解析为清单。指向有效清单的内容寻址 URI 被称为能够“解析”。
BaseURIBackend.can_translate_uri(*uri*)
返回一个布尔值,表明这个后端类是否可以将给定的 URI 转换为相应的内容寻址的 URI。如果一个注册 URI 指向其各自链上注册中的另一个内容寻址的 URI,则称其能够“翻译”。
BaseURIBackend.fetch_uri_contents(*uri*)
如果可用的后端能够解析 URI,则获取存储在所提供的 uri 中的内容。验证存储在 uri 中的内容是否与作为 uri 后缀的内容哈希相匹配。
IPFS 吗
Py-EthPM
有多个后端可用于将文件提取/固定到 IPFS。可以通过环境变量ETHPM_IPFS_BACKEND_CLASS
设置所需的后端。
-
InfuraIPFSBackend
(默认)- https://ipfs . in fura . io
-
IPFSGatewayBackend
(暂时弃用) -
LocalIPFSBacked
- 连接到运行在端口 5001 上的本地 IPFS API 网关。
-
DummyIPFSBackend
- 不会将文件固定/提取到实际的 IPFS 节点,但会模仿这种行为。
BaseIPFSBackend.pin_assets(*file_or_directory_path*)
锁定在给定路径找到的资产,并返回锁定的资产数据。
HTTPS
Py-EthPM
提供了从 Github 获取文件的后端,GithubOverHTTPSBackend
。
一个有效的内容寻址 Github URI 必须符合下面的方案,如 ERC1319 中所述,才能用于这个后端。
https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha
create_content_addressed_github_uri(*uri*)
这个 util 函数将返回一个内容寻址的 URI,正如 Github 的 blob 方案所定义的那样。要为存储在 github 上的任何清单生成内容寻址的 URI,该函数需要接受遵循以下方案的 Github API uri。
https://api.github.com/repos/:owner/:repo/contents/:path/:to/manifest.json
>>> from ethpm.uri import create_content_addressed_github_uri
>>> owned_github_api_uri = "https://api.github.com/repos/ethpm/ethpm-spec/contents/examples/owned/1.0.0.json"
>>> content_addressed_uri = "https://api.github.com/repos/ethpm/ethpm-spec/git/blobs/8f9dc767d4c8b31fec4a08d9c0858d4f37b83180"
>>> actual_blob_uri = create_content_addressed_github_uri(owned_github_api_uri)
>>> assert actual_blob_uri == content_addressed_uri
URIs 登记处
从注册表中查找包的 URI 应该遵循以下格式。(随着注册管理机构合约标准进入 EIP 流程,可能会有所变化)
scheme://address:chain_id/package_name@version
- URI 必须是字符串类型
scheme
:(必选)ethpm
或erc1319
address
:(必需)必须是有效的 ENS 域或指向注册管理机构合约的有效校验和地址。chain_id
:注册表所在链的链 ID。默认为 Mainnet。支持的链包括…
- 1: home page 3: Gravel 4: Myrica rubra* 5: OK.
package-name
:必须符合 EthPM-Spec 中规定的包名。version
:URI 转义版本字符串,应符合永远版本编号规范。
*示例…
ethpm://packages.zeppelinos.eth/owned@1.0.0
ethpm://0x808B53bF4D70A24bA5cb720D37A4835621A9df00:1/ethregistrar@1.0.0
要指定包中的特定资产,您可以命名目标资产。
ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/sources/token.sol
ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/contractTypes/DSToken/abi
ethpm://maker.snakecharmers.eth:1/dai-dai@1.0.0/deployments/mainnet/dai
## 建设者
清单生成器是一个工具,旨在帮助构建自定义清单。构建器仍在积极开发中,目前只能处理简单的用例。
创建简单的清单
对于所有货单,以下成分是必需的。
build( {}, package_name(str), version(str), manifest_version(str), ..., ) # Or build( init_manifest(package_name: str, version: str, manifest_version: str="ethpm/3") ..., )
构建器(即build()
)期望一个字典作为第一个参数。这个 dict 可以是空的,或者如果您想要扩展一个现有的清单,可以填充它。
>>> from ethpm.tools.builder import *
>>> expected_manifest = {
... "name": "owned",
... "version": "1.0.0",
... "manifest": "ethpm/3"
... }
>>> base_manifest = {"name": "owned"}
>>> built_manifest = build(
... {},
... package_name("owned"),
... manifest_version("ethpm/3"),
... version("1.0.0"),
... )
>>> extended_manifest = build(
... base_manifest,
... manifest_version("ethpm/3"),
... version("1.0.0"),
... )
>>> assert built_manifest == expected_manifest
>>> assert extended_manifest == expected_manifest
带init_manifest()
,用“ethpm/3”(唯一支持的 ethpm 规范版本)填充“manifest”,除非提供了替代“版本”。
>>> build(
... init_manifest("owned", "1.0.0"),
... )
{'name': 'owned', 'version': '1.0.0', 'manifest': 'ethpm/3'}
返回一个Package
build( ..., as_package(w3: Web3), )
默认情况下,清单生成器返回一个表示清单的 dict。要从构建器返回一个Package
实例(用生成的清单实例化),将带有有效web3
实例的as_package()
构建器函数添加到构建器的末尾。
>>> from ethpm import Package
>>> from web3 import Web3
>>> w3 = Web3(Web3.EthereumTesterProvider())
>>> built_package = build(
... {},
... package_name("owned"),
... manifest_version("ethpm/3"),
... version("1.0.0"),
... as_package(w3),
... )
>>> assert isinstance(built_package, Package)
验证清单
build( ..., validate(), )
By default, the manifest builder does not perform any validation that the generated fields are correctly formatted. There are two ways to validate that the built manifest conforms to the EthPM V3 Specification.
- 返回一个包,它会自动运行验证。
- 将
validate()
函数添加到清单生成器的末尾。
>>> valid_manifest = build(
... {},
... package_name("owned"),
... manifest_version("ethpm/3"),
... version("1.0.0"),
... validate(),
... )
>>> assert valid_manifest == {"name": "owned", "manifest": "ethpm/3", "version": "1.0.0"}
>>> invalid_manifest = build(
... {},
... package_name("_InvalidPkgName"),
... manifest_version("ethpm/3"),
... version("1.0.0"),
... validate(),
... )
Traceback (most recent call last):
ethpm.exceptions.EthPMValidationError: Manifest invalid for schema version 2\. Reason: '_InvalidPkgName' does not match '^[a-z][-a-z0-9]{0,255}$'
将清单写入磁盘
build( ..., write_to_disk( manifest_root_dir: Optional[Path], manifest_name: Optional[str], prettify: Optional[bool], ), )
将活动清单写入磁盘。不会覆盖具有相同名称和根目录的现有清单。
Defaults -将清单写入当前工作目录(由os.getcwd()
返回),除非提供了一个Path
作为 manifest_root_dir。-使用文件名<version>.json
写入清单,除非需要清单名称(必须以“.”结尾)。json”)作为 manifest_name 提供。-将缩小的清单版本写入磁盘,除非 prettify 设置为 True
>>> from pathlib import Path
>>> import tempfile
>>> p = Path(tempfile.mkdtemp("temp"))
>>> build(
... {},
... package_name("owned"),
... manifest_version("ethpm/3"),
... version("1.0.0"),
... write_to_disk(manifest_root_dir=p, manifest_name="manifest.json", prettify=True),
... )
{'name': 'owned', 'manifest': 'ethpm/3', 'version': '1.0.0'}
>>> with open(str(p / "manifest.json")) as f:
... actual_manifest = f.read()
>>> print(actual_manifest)
{
"manifest": "ethpm/3",
"name": "owned",
"version": "1.0.0"
}
给 IPFS 寄一份舱单
build( ..., pin_to_ipfs( backend: BaseIPFSBackend, prettify: Optional[bool], ), )
将活动的 manfiest 固定到磁盘。必须是构建器集中的结束函数,因为它返回 IPFS pin 数据,而不是返回清单以供进一步处理。
添加元字段
build( ..., description(str), license(str), authors(*args: str), keywords(*args: str), links(*kwargs: str), ..., )
>>> BASE_MANIFEST = {"name": "owned", "manifest": "ethpm/3", "version": "1.0.0"}
>>> expected_manifest = {
... "name": "owned",
... "manifest": "ethpm/3",
... "version": "1.0.0",
... "meta": {
... "authors": ["Satoshi", "Nakamoto"],
... "description": "An awesome package.",
... "keywords": ["auth"],
... "license": "MIT",
... "links": {
... "documentation": "www.readthedocs.com/...",
... "repo": "www.github.com/...",
... "website": "www.website.com",
... }
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... authors("Satoshi", "Nakamoto"),
... description("An awesome package."),
... keywords("auth"),
... license("MIT"),
... links(documentation="www.readthedocs.com/...", repo="www.github.com/...", website="www.website.com"),
... )
>>> assert expected_manifest == built_manifest
编译器输出
要为 solidity 合约构建更复杂的清单,需要从 solidity 编译器提供标准的 json 输出。或者为了更方便的体验,使用 EthPM CLI 。
下面是一个如何编译合约并生成标准 json 输出的示例。更多信息可以在 Solidity 编译器文档中找到。
solc --allow-paths --standard-json < standard-json-input.json > owned_compiler_output.json
样本标准-json-input.json
{ "language": "Solidity", "sources": { "Contract.sol": { "urls": [""] } }, "settings": { "outputSelection": { "*": { "*": ["abi", "evm.bytecode.object"] } } } }
以下示例中使用的compiler_output
是 solc 输出的contracts
键的整个值,它包含所有已编译合约的编译数据。
要添加信号源
# To inline a source build( ..., inline_source( contract_name: str, compiler_output: Dict[str, Any], package_root_dir: Optional[Path] ), ..., ) # To pin a source build( ..., pin_source( contract_name: str, compiler_output: Dict[str, Any], ipfs_backend: BaseIPFSBackend, package_root_dir: Optional[Path] ), ..., )
有两种方法可以在清单中包含合约源。
Both strategies require that either …
- 当前工作目录设置为包根目录或
- 包根目录作为参数(
package_root_dir
)提供
要在清单中直接内联源代码,使用inline_source()
或source_inliner()
(内联来自同一个 compiler_output 的多个源代码),这需要合约名称和编译器输出作为参数。
注意
下面的output_v3.json
预计是 solidity 编译器生成的标准 json 输出,如这里的所述。输出必须包含来自编译的abi
和bytecode
对象。
>>> import json
>>> from ethpm import ASSETS_DIR, get_ethpm_spec_dir
>>> ethpm_spec_dir = get_ethpm_spec_dir()
>>> owned_dir = ethpm_spec_dir / "examples" / "owned" / "contracts"
>>> compiler_output = json.loads((ASSETS_DIR / "owned" / "output_v3.json").read_text())['contracts']
>>> expected_manifest = {
... "name": "owned",
... "version": "1.0.0",
... "manifest": "ethpm/3",
... "sources": {
... "./Owned.sol": {
... "content": """// SPDX-License-Identifier: MIT\npragma solidity ^0.6.8;\n\ncontract Owned """
... """{\n address owner;\n \n modifier onlyOwner { require(msg.sender == owner); _; }"""
... """\n\n constructor() public {\n owner = msg.sender;\n }\n}""",
... "type": "solidity",
... "installPath": "./Owned.sol"
... }
... }
... }
>>> # With `inline_source()`
>>> built_manifest = build(
... BASE_MANIFEST,
... inline_source("Owned", compiler_output, package_root_dir=owned_dir),
... )
>>> assert expected_manifest == built_manifest
>>> # With `source_inliner()` for multiple sources from the same compiler output
>>> inliner = source_inliner(compiler_output, package_root_dir=owned_dir)
>>> built_manifest = build(
... BASE_MANIFEST,
... inliner("Owned"),
... # inliner("other_source"), etc...
... )
>>> assert expected_manifest == built_manifest
要将源作为内容寻址 URI 包含在内,Py-EthPM
可以通过 Infura IPFS API 锁定您的源。除了合约名称和编译器输出,该函数还要求您提供所需的 IPFS 后端来固定合约源。
>>> import json >>> from ethpm import ASSETS_DIR, get_ethpm_spec_dir >>> from ethpm.backends.ipfs import get_ipfs_backend >>> ethpm_spec_dir = get_ethpm_spec_dir() >>> owned_dir = ethpm_spec_dir / "examples" / "owned" / "contracts" >>> compiler_output = json.loads((ASSETS_DIR / "owned" / "output_v3.json").read_text())['contracts'] >>> ipfs_backend = get_ipfs_backend() >>> expected_manifest = { ... "name": "owned", ... "version": "1.0.0", ... "manifest": "ethpm/3", ... "sources": { ... "./Owned.sol": { ... "installPath": "./Owned.sol", ... "type": "solidity", ... "urls": ["ipfs://QmU8QUSt56ZoBDJgjjXvAZEPro9LmK1m2gjVG5Q4s9x29W"] ... } ... } ... } >>> # With `pin_source()` >>> built_manifest = build( ... BASE_MANIFEST, ... pin_source("Owned", compiler_output, ipfs_backend, package_root_dir=owned_dir), ... ) >>> assert expected_manifest == built_manifest >>> # With `source_pinner()` for multiple sources from the same compiler output >>> pinner = source_pinner(compiler_output, ipfs_backend, package_root_dir=owned_dir) >>> built_manifest = build( ... BASE_MANIFEST, ... pinner("Owned"), ... # pinner("other_source"), etc ... ) >>> assert expected_manifest == built_manifest
添加合约类型
build( ..., contract_type( contract_name: str, compiler_output: Dict[str, Any], alias: Optional[str], abi: Optional[bool], compiler: Optional[bool], contract_type: Optional[bool], deployment_bytecode: Optional[bool], devdoc: Optional[bool], userdoc: Optional[bool], source_id: Optional[bool], runtime_bytecode: Optional[bool] ), ..., )
清单生成器的contract_type()
函数的默认行为是用在compiler_output
中找到的所有合约类型数据填充清单。
>>> expected_manifest = {
... 'name': 'owned',
... 'manifest': 'ethpm/3',
... 'version': '1.0.0',
... 'compilers': [
... {'name': 'solc', 'version': '0.6.8+commit.0bbfe453', 'settings': {'optimize': True}, 'contractTypes': ['Owned']}
... ],
... 'contractTypes': {
... 'Owned': {
... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}],
... 'deploymentBytecode': {
... 'bytecode': '0x6080604052348015600f57600080fd5b50600080546001600160a01b03191633179055603f80602f6000396000f3fe6080604052600080fdfea26469706673582212208cbf6c3ccde7837026b3ec9660a0e95f1dbee0ce985f6879d7bc7e422519cc7564736f6c63430006080033'
... },
... 'sourceId': 'Owned.sol',
... 'devdoc': {'methods': {}},
... 'userdoc': {'methods': {}}
... }
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... contract_type("Owned", compiler_output)
... )
>>> assert expected_manifest == built_manifest
To select only certain contract type data to be included in your manifest, provide the desired fields as True
keyword arguments. The following fields can be specified for inclusion in the manifest …
abi
compiler
deployment_bytecode
runtime_bytecode
devdoc
userdoc
source_id
>>> expected_manifest = {
... 'name': 'owned',
... 'manifest': 'ethpm/3',
... 'version': '1.0.0',
... 'contractTypes': {
... 'Owned': {
... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}],
... }
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... contract_type("Owned", compiler_output, abi=True)
... )
>>> assert expected_manifest == built_manifest
如果您想为您的合约类型起别名,请以 kwarg 的形式提供所需的别名。这将自动在contractType
字段中包含原始合约类型。除非以 kwargs 的形式提供特定的合约类型字段,contractType
仍将默认包含编译器输出中找到的所有可用的合约类型数据。
>>> expected_manifest = {
... 'name': 'owned',
... 'manifest': 'ethpm/3',
... 'version': '1.0.0',
... 'contractTypes': {
... 'OwnedAlias': {
... 'abi': [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}],
... 'contractType': 'Owned'
... }
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... contract_type("Owned", compiler_output, alias="OwnedAlias", abi=True)
... )
>>> assert expected_manifest == built_manifest
添加部署
build( ..., deployment( block_uri, contract_instance, contract_type, address, transaction=None, block=None, deployment_bytecode=None, runtime_bytecode=None, compiler=None, ), ..., )
向清单中添加部署有两种策略。
deployment(*block_uri*, *contract_instance*, *contract_type*, *address*, *transaction=None*, *block=None*, *deployment_bytecode=None*, *runtime_bytecode=None*, *compiler=None*)
这是用于向清单添加部署的最简单的构建器函数。所有参数都需要关键字。该构建器函数需要一个有效的block_uri
,由用户确定代表相同区块链的多个链 URIs 不包含在“部署”对象键中。
根据 EthPM 规范,所有的runtime_bytecode
、deployment_bytecode
和compiler
都必须是有效格式的字典。如果您的合约有链接依赖,请确保将它们包含在字节码对象中。
>>> expected_manifest = {
... 'name': 'owned',
... 'manifest': 'ethpm/3',
... 'version': '1.0.0',
... 'deployments': {
... 'blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef': {
... 'Owned': {
... 'contractType': 'Owned',
... 'address': '0x4F5B11C860B37B68De6d14FB7e7b5f18A9a1BD00',
... }
... }
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... deployment(
... block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
... contract_instance='Owned',
... contract_type='Owned',
... address='0x4F5B11C860B37B68De6d14FB7e7b5f18A9a1BD00',
... ),
... )
>>> assert expected_manifest == built_manifest
deployment_type(*contract_instance*, *contract_type*, *deployment_bytecode=None*, *runtime_bytecode=None*, *compiler=None*)
此构建器功能简化了跨多个链添加相同合约类型部署的过程。它需要一个contract_instance
和contract_type
参数(在许多情况下,这两个参数是相同的,尽管contract_type
必须总是匹配其在清单的“合约类型”中的对应方),并且所有参数都需要关键字。
根据 EthPM 规范,所有的runtime_bytecode
、deployment_bytecode
和compiler
都必须是有效格式的字典。如果您的合约有链接依赖,请确保将它们包含在字节码对象中。
owned_type = deployment_type(contract_instance="Owned", contract_type="Owned") escrow_type = deployment_type( contract_instance = "Escrow", contract_type = "Escrow", deployment_bytecode = { "bytecode": "0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001" }, runtime_bytecode = { "bytecode": "0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029" }, compiler = { "name": "solc", "version": "0.4.24+commit.e67f0147.Emscripten.clang", "settings": { "optimize": True } } ) manifest = build( package_name("escrow"), version("1.0.0"), manifest_version("ethpm/3"), owned_type( block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', address=owned_testnet_address, ), owned_type( block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', address=owned_mainnet_address, transaction=owned_mainnet_transaction, block=owned_mainnet_block, ), escrow_type( block_uri='blockchain://abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', address=escrow_testnet_address, ), escrow_type( block_uri='blockchain://1234567890123456789012345678901234567890123456789012345678901234/block/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', address=escrow_mainnet_address, transaction=escrow_mainnet_transaction, ), )
添加生成依赖项
build( ..., build_dependency( package_name, uri, ), ..., )
build_dependency(*package_name*, *uri*)
要将构建依赖项添加到您的清单中,只需提供包的名称和受支持的内容寻址 URI。
>>> expected_manifest = {
... 'name': 'owned',
... 'manifest': 'ethpm/3',
... 'version': '1.0.0',
... 'buildDependencies': {
... 'owned': 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW',
... }
... }
>>> built_manifest = build(
... BASE_MANIFEST,
... build_dependency('owned', 'ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW'),
... )
>>> assert expected_manifest == built_manifest
棋子
manifest Checker 是一个根据自然语言规范(链接)帮助验证清单的工具。
验证清单
```py
from ethpm.tools.checker import check_manifest
basic_manifest = {"name": "example", "version": "1.0.0", "manifest": "ethpm/3"} check_manifest(basic_manifest) {'meta': "Manifest missing a suggested 'meta' field.", 'sources': 'Manifest is missing a sources field, which defines a source tree that should comprise the full source tree necessary to recompile the contracts contained in this release.', 'contractTypes': "Manifest does not contain any 'contractTypes'. Packages should only include contract types that can be found in the source files for this package. Packages should not include contract types from dependencies. Packages should not include abstract contracts in the contract types section of a release.", 'compilers': 'Manifest is missing a suggested
compilers
field.'} ```*