Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Contracts per Account - Code Register Migration #82

Closed
janezpodhostnik opened this issue Oct 7, 2020 · 1 comment
Closed

Comments

@janezpodhostnik
Copy link
Contributor

janezpodhostnik commented Oct 7, 2020

ref: https://github.com/dapperlabs/flow-go/issues/4806

Since one account can now have multiple contracts, they are now saved differently. The register key was previously just "code" but will now be "code.{contract_name}".

Because of this all of the old registers need to be moved.

However, because previously the code could contain multiple contract interfaces and one contract, but can now only contain one contract interface or one contract, the code needs to split up during migration.

below is some pseudocode of how the migration might go:

def migrate(register):
    if not needs_migration(register):
        # every account has a code register, even if it is empty
        # the number of registers that need migration is equal to the number of accounts
        return

    # whatever we do, we need to delete the old register first
    delete_register(register)

    if register_is_empty(register):
        # most accounts won't have any code deployed
        # we can simply ignore those (we already deleted them)
        return

    # load code
    try:
        parsed_programs = parse_program(register.code)
    except:
        # program is not parsable! should not happen!
        raise Exception()

    if len(parsed_programs) == 1:
        # only one interface or contract (easy case). Should be most common
        # just save the new contract
        save_contract(register, parsed_programs[0])
        return

    # this case is much harder. We have one or more interfaces and one contract
    # they need to be saved separately, so multiple registers will be created.
    # However they might depend on each other, so we potentially need to add some imports to each of the pieces
    # In the worst case scenario the interfaces depend on each other. In which case I dont know what to do
    # (there needs to be more than one interface for this case)
    while len(parsed_programs) > 0:
        # there should be at least one that can be parsed without the others (otherwise there is circular dependencies)
        first_ok_program = next(p for p in parsed_programs if p.is_valid)
        # save it
        new_register = save_contract(register, first_ok_program)
        # remove it from the list
        parsed_programs.remove(first_ok_program)
        # add imports to the rest
        # this is an aggressive approach there will mostly be more imports than needed
        for p in parsed_programs:
            add_contract_import(p, first_ok_program, new_register)

    # if there is a circular dependency we can just report an error and solve that case manually.
    # there should only be a handful of cases like that, if any


def needs_migration(register) -> bool:
    """Only need migration if key is 'code' ('636f6465' in hex)"""
    return register.key == '636f6465'


def register_is_empty(register):
    return register.value == ''


def delete_register(register):
    pass


class ParsedProgram:
    def __init__(self) -> None:
        self.name = ''  # the interface or contract name
        self.code = ''
        self.is_valid = True # if the contract can be parsed as is


def save_contract(register, program: ParsedProgram):
    new_register = copy(register)
    new_register.key = hex('code.' + program.name)
    new_register.code = hex(program.code)
    save_register(new_register)
    return new_register


def save_register(register):
    pass


def parse_program(code) -> List[ParsedProgram]:
    # parse the program and split it so there is only one interface or contract per program
    # duplicate all the original imports on all programs
    return []

def add_contract_import(program: ParsedProgram, dependency_program: ParsedProgram, dependency_register):
    # prepend import
    program.code = f'import {dependency_program.name} from {dependency_register.owner}\n' + program.code
    program.is_valid = # check it is valid now
@turbolent
Copy link
Member

Given that this migration went successful on testnet, I think we can close it now

@sifmoon sifmoon closed this as completed Dec 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants