Source code for solidbyte.compile.linker

""" Functions used for linking libraries for Solidity libraries

Example Solidity placeholder: :code:`__$13811623e8434e588b8942cf9304d14b96$__`
"""
import re
from typing import Tuple, Dict, Set, Pattern
from ..common.utils import all_defs_in, defs_not_in
from ..common.web3 import remove_0x, hash_string, hash_hexstring
from ..common.exceptions import LinkError
from ..common.logging import getLogger

log = getLogger(__name__)

# Regexes to match parts of a solc bin file
# TODO: Always 34?
LINK_CONTRACT_REGEX = r':([A-Za-z0-9]+)$'
LINK_PLACEHOLDER_REGEX = r'\$[A-Za-z0-9]{34}\$'
# BYTECODE_PLACEHOLDER_REGEX = r'__(\$[A-Za-z0-9]{34}\$)__'
BYTECODE_PLACEHOLDER_REGEX = '__({})__'


[docs]def make_placeholder_regex(placeholder: str) -> Pattern[str]: """ Return a regex pattern for a placeholder :param placeholder: (:code:`str`) Given a solidity placeholder, make a regex """ placeholder = re.escape(placeholder) return re.compile(BYTECODE_PLACEHOLDER_REGEX.format(placeholder))
[docs]def placeholder_from_def(s: str) -> str: """ return a placeholder form a solc file link definition :param s: (:code:`str`) A "definition" from a solidity bytecode output file """ placeholder = re.search(LINK_PLACEHOLDER_REGEX, s) if not placeholder: raise LinkError("Unable to find placeholder") return placeholder.group(0)
[docs]def contract_from_def(s: str) -> str: """ return a contract name form a solc file link definition :param s: (:code:`str`) A "definition" from a solidity bytecode output file """ contract_match = re.search(LINK_CONTRACT_REGEX, s) if not contract_match: raise LinkError("Unable to find contract") return contract_match.group(1)
[docs]def replace_placeholders(bytecode: str, placeholder: str, addr: str) -> str: """ Replace the placeholders with the contract address :param bytecode: (:code:`str`) Solidity bytecode output :param placeholder: (:code:`str`) The placeholder to replace :param addr: (:code:`str`) Address to replace the placeholder with """ pattern = make_placeholder_regex(placeholder) return re.sub(pattern, remove_0x(addr), bytecode)
[docs]def clean_bytecode(bytecode: str) -> str: """ Clean the bytecode string of any comments and whitespace :param bytecode: (:code:`str`) Bytecode output from the Solidity compiler """ blist = [x for x in bytecode.split('\n') if x.strip() != '' and not x.startswith('//')] btxt = ''.join(blist) return btxt
[docs]def address_placeholder(name): """ Provide a false, but repeatable address for a link ref with name. Used in bytecode hashing. :param name: (:code:`str`) The name to use for the placeholder :returns: (:code:`str`) A false address derrived from a placeholder """ return remove_0x(hash_string('__{}__'.format(name)))
[docs]def hash_linked_bytecode(bytecode) -> str: """ Hash bytecode that has link references in a way that the addresses for delegate calls don't matter. Useful for comparing bytecode hashes when you don't know deployed addresses. :param bytecode: (:code:`str`) Bytecode output from the Solidity compiler :returns: (:code:`str`) A link-agnostic hash of the bytecode """ links: Dict[str, str] = {} link_defs = bytecode_link_defs(bytecode) if link_defs: for name, _ in link_defs: links[name] = address_placeholder(name) if len(links) > 0: bytecode = link_library(bytecode, links) else: bytecode = clean_bytecode(bytecode) bytecode = remove_0x(bytecode) return hash_hexstring(bytecode)