Demystifying Truffle Migrate: What does it actually do?

Truffle is the most popular ethereum development framework. If you worked with it before, you might have used the “truffle migrate” command to deploy your smart contracts. If you use the command without thinking what it does, it might be worth revisiting it to fully understand what it is doing.

If not done already, install ethereum testrpc, truffle and a simple eth explorer. Simply follow the steps below if you are new to ethereum development.

Let us install and start testrpc.

// install testrpc
-> npm install -g ethereumjs-testrpc

// start testrpc
-> testrpc

In another terminal, install and start ethexplorer.

// install etherparty explorer
-> git clone https://github.com/etherparty/explorer
-> npm start

Go to http://localhost:8000 and you should see block 0 only.

Its time to install truffle with the metacoin example and deploy it to the testrpc network.

// install truffle
-> npm install -g truffle

// This famous command installs the default metacoin contract
-> truffle init

// deploy metacoin and related contracts to the network
-> truffle migrate

Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

Using network 'development'.

Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x68fe0ebe7abfaff577de76f01fb5a4602374ed22b1f3e76ef4e3acb6d9604fc4
Migrations: 0x213dcbe43fd8741adf42c37e8df8a1a37eede7f7
Saving successful migration to network...
... 0xe2807a60304aea783a1b5a56eb4968a53ae4b8f58f97f55bcbd44d401cb9186c
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
... 0xce79273e1ceeb59d2e39e6ab7f43318e235ec6af42d04d5e787bbc2c94a6438d
ConvertLib: 0x13e984d7bb330502b404739ef02525199745a1d8
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
... 0x2fcabb69ddc6527ceee67b606f0bb40c9726f95236472438496cffb7154d0296
MetaCoin: 0x0d9f1b12926f10add2ed0691dca10909a80a22e8
Saving successful migration to network...
... 0x8e7d508dd570d6aca60d1cc3d7f5a6c3ea906a9e3399cfb7755b267cf4b36312
Saving artifacts...

Let us run through what “truffle migrate” is doing one step at a time.

It started by compiling ConvertLib.sol, MetaCoin.sol and Migrations.sol under the contracts folder into the build folder. We could have done this step first using the “truffle compile” command. Since we didn’t do it, “truffle migrate” did it for us.

Next, it deployed the 3 contracts to the testrpc network. We got the transaction and contract addresses for each contract deployed. The number of transactions could be verified in ether explorer.

In testrpc, there is no mining. So each block holds 1 transaction only. We saw 5 blocks, meaning 5 transactions. Wait, why 5 transactions? Looking at the console output carefully, there were 2 more transactions for “saving migrations” to the network, ie tx id 0xe2807a60304aea783a1b5a56eb4968a53ae4b8f58f97f55bcbd44d401cb9186c and 0x8e7d508dd570d6aca60d1cc3d7f5a6c3ea906a9e3399cfb7755b267cf4b36312. You could verify that they happened in block 2 and 5 respectively. Block 1, 3 4 were the actual deployment of the 3 contracts.

Take block 5 for example,

You (account holder) paid gas to change the state on the Migrations contract. So what exactly did you do?

To cut the story short, let us look at the original Migrations.sol,

pragma solidity ^0.4.4;

contract Migrations {
address public owner;
uint public last_completed_migration;

modifier restricted() {
if (msg.sender == owner) _;
}

function Migrations() {
owner = msg.sender;
}

function setCompleted(uint completed) restricted {
last_completed_migration = completed;
}

function upgrade(address new_address) restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

The Migrations.sol contract is required by the “truffle migrate” command. Every time a migration step is completed successfully, “setCompleted” function is triggered to update the last_completed_migration number. In this case, “setCompleted” was ran 2 times because there were 2 deployment files in migrations folder, ie 1_initial_migration.js and 2_deploy_contracts.js. They were executed in sequential order. We could verify that the value of last_completed_migration was step 2.

-> truffle console

truffle(development)> Migrations.deployed().then(function(ins){ins.last_completed_migration.call().then(function(v) {console.log(v)} )})

truffle(development)> { [String: '2'] s: 1, e: 0, c: [ 2 ] }

Why the hassle?

While you could redeploy everything again easily using “truffle migrate — reset”, it might not be practical to keep doing it in the real network simply because every deployment requires gas(ether).

In the blockchain, every deployment is a transaction and every transaction is non reversible. The whole idea behind this architecture is to allow us to add/upgrade contracts and redeploy the affected contracts only. Imagine you have upgraded MetaCoin.sol, how would you deploy this contract without redeploying the other contracts? You will need to create a new migration script called 3_deploy_upgraded_metacoin.js for example and add it to the migrations folder. The content might look something like:

var ConvertLib = artifacts.require("./ConvertLib.sol");
var MetaCoin = artifacts.require("./MetaCoin.sol");

module.exports = function(deployer) {
// we don't have to redeploy ConvertLib again
// deployer.deploy(ConvertLib);
deployer.link(ConvertLib, MetaCoin);
deployer.deploy(MetaCoin);
};

Practically, running truffle migrate now gave different results.

-> truffle compile
...
-> truffle migrate

Using network 'development'.

Running migration: 3_deploy_upgraded_metacoin.js
Replacing MetaCoin...
... 0xe9d048108ea7bf375c0e040341119f7107a7eec585e6852d56185601509e0be1
MetaCoin: 0xf559003551057f56a9a1daff6355565b949dc5af
Saving successful migration to network...
... 0xcf8f926c801f1fafa13acba80a682f2cb04f4accc61e0e02cf9220ab2340988f
Saving artifacts...

The Migrations contract knew the last_completed_migration was 2 and would not run step 2 again, so it executed only step 3 and skipped the first 2 steps. What if the Migrations contract is itself upgraded? the last_completed_migration number could be transferred over to a new Migration contract using the “upgrade” function within Migrations.sol.

There has been arguments whether it is necessary to deploy a separate contract to the network simply to track number of deployments. There should be ways to track it within truffle and possibly something for the truffle team to look at in the future. Better still, decouple Migrations.sol from the “truffle migrate” command so that people who chooses not to use it can do so.