Populus 1.0-beta5 - Introducing Migrations

in #populus8 years ago

For those that do not know, Populus is a smart contract development framework.  Over the last month it has received a significant overhaul and is currently in a beta release cycle approaching a 1.0 release.

Earlier this week I published the 5th beta release, 1.0.0b5 which contains the new Migrations feature.  I have to thank Tim Coulter, the creator and maintainer of the Truffle framework for the concept, as it already exists within Truffle.

The Previous State of Affairs

Populous already has functionality to handle deployment of your smart contracts, including things like automatically deriving library dependencies, figuring out the deploy order off of those dependencies, and automatically linking the contract bytecode to the deployed library addresses.

This feature was developed based on my own personal needs as I was developing the first versions of Ethereum Alarm Clock.  As I iterated past the initial release, I found that the deployment feature was lacking in certain ways.

Inflexible

As stated above, $ populus deploy command is smart in that it is able to automatically derive the correct way to deploy your contracts.  If your needs diverged from this in some way, tough luck.  Part of this limitation comes from limitations of a command line interface.

Constructor Arguments

There was no support for constructor arguments.  This could be worked around by creating an inherited contract with those arguments hard coded, but even this doesn't fit many use cases, such as when the constructor argument needs to be the address of one of the other contracts that needs to be deployed.

Non Contract Creation Operations

I know that much of the MakerDAO deploy process involves post-deploy transactions to do things like setting up access controls, pushing contract addresses into registries, and general configuration of contracts.  None of this is possible using $ populus deploy

Redundant Deploys

Each run of $ populus deploy is unaware of your previous deployments.  This means that it will re-deploy each of your libraries, even if they have not changed since your last deployment.

Introducing Migrations

Migrations are able to handle all of these issues.  While the idea for migrations originated from Truffle, the architecture and design borrows heavily from Django's Migrations feature.

A migration in Populus is a python module with a single instance of the populus.migrations.Migration class.

# migrations/0002_deploy_token.py
from populus import migrations

class Migration(migrations.Migration):
    migration_id = '0002_deploy_token'
    dependencies = ['0001_initial']
    operations = [
        migrations.DeployContract(
            contract_name='Token',
            arguments=['0xd3cda913deb6f67967b99d67acdfa1712c293601'],
        ),
        migrations.TransactContract(
            contract_name='Token',
            method_name='mint',
            arguments=['d3cda913deb6f67967b99d67acdfa1712c293601', 100],
            contract_address=migrations.Address.defer(key='contracts/Token'),
        ),
    ]
    compiled_contracts = {
        'Token': {
            'code': '0x...',
            'code_runtime': '0x...',
            'abi': [...],
            'source': 'contract Token {....}',
        },
        ...
    }

A migration consists of four primary things.

  • Migration ID: A non-empty unique string identifier for the migration.
  • Dependencies: A list of migration_ids for other migrations that this migration depends on.
  • Operations: A list of populus.migations.operations.Operation objects that make up the actions this migration will perform when executed.
  • Compiled Contracts: A dictionary of the compiled contract assets that this migration will have access to.

The migration above would perform two operations.

  1. Deploy the Token contract with a single constructor argument '0xd3cda913deb6f67967b99d67acdfa1712c293601'
  2. Send a transaction to the Token contract, calling the transfer() method with the arguments ['0xd3cda913deb6f67967b99d67acdfa1712c293601', 100]

Addressing Inflexibility

Migrations should facilitate even the most complex deployment workflows.  Populus ships with the following operations for the most common actions users are likely to need.

  • SendTransaction: for sending basic transactions.
  • DeployContract: for deploying contracts.
  • TransactContract: for interacting with a deployed contract.

And for needs that do not fit within these operations you can use the RunPython operation which allows you to execute whatever python code you would like.

Addressing Constructor Arguments

As you should see in the example migration above, you may now easily specify constructor arguments for you deployments.  You can also specify values that cannot be known at the time of authoring the migration, or values that must be computed using the populus.migrations.DeferredValue object.  More on these down below.

Addressing Non Contract Creation Operations

The TransactContract, SendTransaction, and RunPython operations provide tools for executing a wide array of blockchain interactions.

Addressing Redundant Deploys

One of the major components that make migrations awesome is the Registrar.  The registrar is a contract that acts as a database to track which migrations have been executed already, as well as tracking meta information like the latest deployed version of specific contracts or libraries.

In the example above, you might have noticed something interesting about the TransactContract operation.

migrations.TransactContract(
    contract_name='Token',
    method_name='mint',
    arguments=['d3cda913deb6f67967b99d67acdfa1712c293601', 100],
    contract_address=migrations.Address.defer(key='contracts/Token'),  #  <----- This Part
)

The Address object that you see there is a special version of a DeferredValue.  This specific one looks up the latest known address for the contract named Token from the registrar contract.  Each time a contract is deployed, the migration engine stores some meta information about the various operations.  This makes the values available to future operations through use of the DeferredValue API.

At runtime, this operation will query the registrar contract for the registered key and use the address found at that key as the contract_address for this transaction.

Current Status

As stated above, Populus is in a beta release cycle.  That means you are welcome to use the beta releases, but you do so knowing that you are using pre-released software.

$ pip install populus==1.0.0b5

This feature is the first step towards some much loftier goals that I have for the framework, but I'll have to get into that in another post.  If you would like to chat about any of this you can find me on the Populus Gitter Channel. If you run into any issues using the library feel free to open and issue on the Populus github repository.

Sort:  

Very cool, I had not heard of this solidity develpment framework before...bookmarking it. Nice to finally see some Ethereum heavyweights creating immutable posts at Steemit.

So basically it's Truffle.py ?