Example #1
0
    def link_bytecode(cls, attr_dict: Dict[str,
                                           str]) -> Type["LinkableContract"]:
        """
        Return a cloned contract factory with the deployment / runtime bytecode linked.

        :attr_dict: Dict[`ContractType`: `Address`] for all deployment and runtime link references.
        """
        if not cls.unlinked_references and not cls.linked_references:
            raise BytecodeLinkingError(
                "Contract factory has no linkable bytecode.")
        if not cls.needs_bytecode_linking:
            raise BytecodeLinkingError(
                "Bytecode for this contract factory does not require bytecode linking."
            )
        cls.validate_attr_dict(attr_dict)
        bytecode = apply_all_link_refs(cls.bytecode, cls.unlinked_references,
                                       attr_dict)
        runtime = apply_all_link_refs(cls.bytecode_runtime,
                                      cls.linked_references, attr_dict)
        linked_class = cls.factory(cls.web3,
                                   bytecode_runtime=runtime,
                                   bytecode=bytecode)
        if linked_class.needs_bytecode_linking:
            raise BytecodeLinkingError(
                "Expected class to be fully linked, but class still needs bytecode linking."
            )
        return linked_class
Example #2
0
    def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None:
        """
        Validates that ContractType keys in attr_dict reference existing manifest ContractTypes.
        """
        attr_dict_names = list(attr_dict.keys())

        if not self.unlinked_references and not self.linked_references:
            raise BytecodeLinkingError(
                "Unable to validate attr dict, this contract has no linked/unlinked references."
            )

        unlinked_refs = self.unlinked_references or ({}, )
        linked_refs = self.linked_references or ({}, )
        all_link_refs = unlinked_refs + linked_refs

        all_link_names = [ref["name"] for ref in all_link_refs if ref]
        if set(attr_dict_names) != set(all_link_names):
            raise BytecodeLinkingError(
                "All link references must be defined when calling "
                "`link_bytecode` on a contract factory.")
        for address in attr_dict.values():
            if not is_canonical_address(address):
                raise BytecodeLinkingError(
                    f"Address: {address} as specified in the attr_dict is not "
                    "a valid canoncial address.")
Example #3
0
 def _resolve_linked_references(
     self, link_ref: Tuple[int, str, str],
     deployments: Dict[str,
                       Any]) -> Generator[Tuple[int, bytes], None, None]:
     # No nested deployment: i.e. 'Owned'
     offset, link_type, value = link_ref
     if link_type == "literal":
         yield offset, to_canonical_address(value)
     elif value in deployments:
         yield offset, to_canonical_address(deployments[value]["address"])
     # No nested deployment, but invalid ref
     elif ":" not in value:
         raise BytecodeLinkingError(
             f"Contract instance reference: {value} not found in package's deployment data."
         )
     # Expects child pkg in build_dependencies
     elif value.split(":")[0] not in self.build_dependencies:
         raise BytecodeLinkingError(
             f"Expected build dependency: {value.split(':')[0]} not found "
             "in package's build dependencies.")
     # Find and return resolved, nested ref
     else:
         unresolved_linked_ref = value.split(":", 1)[-1]
         build_dependency = self.build_dependencies[value.split(":")[0]]
         yield build_dependency._resolve_link_dependencies(
             unresolved_linked_ref)
Example #4
0
 def __init__(self, address: bytes, **kwargs: Any) -> None:
     if self.needs_bytecode_linking:
         raise BytecodeLinkingError(
             "Contract cannot be instantiated until its bytecode is linked."
         )
     validate_address(address)
     super(LinkableContract, self).__init__(address=address, **kwargs)
Example #5
0
 def __init__(self, address: bytes, **kwargs: Any) -> None:
     if self.needs_bytecode_linking:
         raise BytecodeLinkingError(
             "Contract cannot be instantiated until its bytecode is linked."
         )
     validate_address(address)
     # type ignored to allow for undefined **kwargs on `Contract` base class __init__
     super().__init__(address=address, **kwargs)  # type: ignore
Example #6
0
 def __init__(self, address: bytes, **kwargs: Any) -> None:
     if self.needs_bytecode_linking:
         raise BytecodeLinkingError(
             "Contract cannot be instantiated until its bytecode is linked."
         )
     validate_address(address)
     # todo: remove automatic checksumming of address once web3 dep is updated in pytest-ethereum
     super(LinkableContract,
           self).__init__(address=to_checksum_address(address), **kwargs)
Example #7
0
    def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None:
        """
        Validates that ContractType keys in attr_dict reference existing manifest ContractTypes.
        """
        attr_dict_names = attr_dict.keys()

        if not self.unlinked_references and not self.linked_references:
            raise BytecodeLinkingError(
                "Unable to validate attr dict, this contract has no linked/unlinked references."
            )

        unlinked_refs = self.unlinked_references or ({}, )
        linked_refs = self.linked_references or ({}, )
        all_link_refs = unlinked_refs + linked_refs

        all_link_names = {ref["name"] for ref in all_link_refs if ref}
        if attr_dict_names != all_link_names:
            raise BytecodeLinkingError(
                "All link references must be defined when calling "
                "`link_bytecode` on a contract factory.")
        for address in attr_dict.values():
            validate_address(address)
Example #8
0
def apply_link_ref(offset: int, length: int, value: bytes, bytecode: bytes) -> bytes:
    """
    Returns the new bytecode with `value` put into the location indicated by `offset` and `length`.
    """
    try:
        validate_empty_bytes(offset, length, bytecode)
    except EthPMValidationError:
        raise BytecodeLinkingError("Link references cannot be applied to bytecode")

    address = value if is_canonical_address(value) else to_canonical_address(value)
    new_bytes = (
        # Ignore linting error b/c conflict b/w black & flake8
        bytecode[:offset] + address + bytecode[offset + length:]  # noqa: E201, E203
    )
    return new_bytes
Example #9
0
def get_linked_deployments(deployments: Dict[str, Any]) -> Dict[str, Any]:
    """
    Returns all deployments found in a chain URI's deployment data that contain link dependencies.
    """
    linked_deployments = {
        dep: data
        for dep, data in deployments.items()
        if get_in(("runtimeBytecode", "linkDependencies"), data)
    }
    for deployment, data in linked_deployments.items():
        if any(link_dep["value"] == deployment
               for link_dep in data["runtimeBytecode"]["linkDependencies"]):
            raise BytecodeLinkingError(
                f"Link dependency found in {deployment} deployment that references its "
                "own contract instance, which is disallowed")
    return linked_deployments
Example #10
0
 def constructor(cls, *args: Any, **kwargs: Any) -> bool:
     if cls.needs_bytecode_linking:
         raise BytecodeLinkingError(
             "Contract cannot be deployed until its bytecode is linked.")
     return super(LinkableContract, cls).constructor(*args, **kwargs)