truffle migrate
迁移脚本是 JavaScript 文件,用于将智能合约部署到以太坊网络。这些文件负责暂存部署任务,并且假设部署需求会随着时间的推移而改变。随着项目的发展,将创建新的迁移脚本,以进一步推动智能合约在区块链上的发展。先前运行的迁移的历史记录通过特殊的迁移智能合约记录在链上,详细信息如下所述。
1. 命令(Command)
执行下面的操作开始迁移。
$ truffle migrate
该命令将执行项目的 migrations 目录中的所有迁移脚本。最简单的迁移只是一组托管部署脚本。如果之前的迁移已成功运行,则该命令将从上次执行的迁移开始执行,仅执行新创建的迁移。如果不存在新的迁移,该命令将根本不执行任何操作。可以使用--reset 选项从头开始执行所有迁移。对于本地测试,请确保在执行迁移之前安装并运行了一个测试区块链,例如 Ganache。
问题:truffle 是怎么知道哪些迁移已经执行了,哪些没有执行,又是如何发现新增的迁移的?
2. 迁移文件(Migration files)
一个简单的迁移脚本文件如下:
文件名:4_example_migrations.js
var MyContract = artifacts.require("MyContract");
module.exports = function(deployer) {
// deployment steps
deployer.deploy(MyContract);
};
需要注意的是,文件名以数字为前缀,后缀为描述。编号前缀是必需的,以便记录迁移是否成功运行。后缀纯粹是为了人类的可读性和理解力。
2.1 artifacts.require()
在迁移开始时,我们通过 artifacts.require() 方法告诉 Truffle 我们想与哪些合约进行交互。这个方法类似于 Node 的 require,但在我们的例子中,它特别返回了我们可以在其余部署脚本中使用的合约抽象。指定的名称应与该源文件中的合约定义的名称匹配。不要传递源文件的名称,因为文件可以包含多个合约。
例如下面这个示例,其中在同一源文件中指定了两个合同:
文件名:./contracts/Contracts.sol
contract ContractOne {
// ...
}
contract ContractTwo {
// ...
}
如果只是使用 ContractTwo,artifacts.require() 语句将如下所示:
var ContractTwo = artifacts.require("ContractTwo");
如果同时使用 ContractOne 和 ContractTwo,需要两个 artifacts.require() 语句:
var ContractOne = artifacts.require("ContractOne");
var ContractTwo = artifacts.require("ContractTwo");
2.2 module.exports
所有迁移都必须通过 module.exports 语法导出函数。每次迁移导出的函数都应接受 deployer 对象作为其第一个参数。此对象通过为部署智能合约提供清晰的语法以及执行某些部署更普通的职责(例如保存已部署的组件以供以后使用)来帮助部署。deployer 对象是用于暂存部署任务的主要接口,其 API 在本文底部描述。
迁移函数也可以接受其他参数。请参阅以下示例。
3. 初始迁移(Initial migration)
Truffle 要求我们拥有 Migrations 合约才能使用迁移功能。此合约必须包含特定的接口,但我们也可以随意编辑此合约。对于大多数项目,此合约最初将作为第一次迁移进行部署,不会再次更新。在使用 truffle init 创建新项目时,我们也会默认收到此合约。
文件名:contracts/Migrations.sol
pragma solidity ^0.4.8;
contract Migrations {
address public owner;
// A function with the signature `last_completed_migration()`, returning a uint, is required.
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
function Migrations() {
owner = msg.sender;
}
// A function with the signature `setCompleted(uint)` is required.
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);
}
}
我们必须在第一次迁移中部署此合约才能利用迁移功能。为此,请创建以下迁移:
文件名:migrations/1_initial_migration.js
var Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
// Deploy the Migrations contract as our only task
deployer.deploy(Migrations);
};
之后可以使用增加的编号前缀创建新的迁移,以部署其他合约并执行进一步的部署步骤。
4. 部署者(Deployer)
迁移文件将使用 deployer 来部署部署任务。因此,可以同步编写部署任务,它们将以正确的顺序执行:
// Stage deploying A before B
deployer.deploy(A);
deployer.deploy(B);
或者,deployer 上的每个函数都可以用作 Promise,以排队依赖于前一个任务执行的部署任务:
// Deploy A, then deploy B, passing in A's newly deployed address
deployer.deploy(A).then(function() {
return deployer.deploy(B, A.address);
});
如果我们发现语法更清晰,则可以将部署编写为单个 Promise 链。部署 API 将在本文底部讨论。
5. 网络考虑因素(Network considerations)
可以根据部署到的网络有条件地执行部署步骤。这是一项高级功能,因此请在继续之前先查看“网络”部分。
要有条件地暂存部署步骤,请编写迁移,以便它们接受第二个参数,称为 network。例:
module.exports = function(deployer, network) {
if (network == "live") {
// Do something specific to the network named "live".
} else {
// Perform a different step otherwise.
}
}
6. 可获得的账户列表(Available accounts)
迁移也会传递以太坊客户端和 Web3 提供的帐户列表,供我们在部署期间使用。这是从 web3.eth.getAccounts() 返回的完全相同的帐户列表。
module.exports = function(deployer, network, accounts) {
// Use the accounts within your migrations.
}
7. 部署者接口(Deployer API)
deployer 包含许多可用于简化迁移的功能。
7.1 deployer.deploy(contract, args..., options)
使用可选的构造函数参数部署由合约对象指定的特定合约。这对单例合约很有用,因此 dapp 只存在此合约的一个实例。这将在部署后设置合约的地址(即,Contract.address 将等于新部署的地址),并且它将覆盖存储的任何先前地址。
我们可以选择传递一组合约或一组数组,以加快多个合约的部署。此外,最后一个参数是一个可选对象,可以包含名为 overwrite 的键以及其他交易参数,例如 gas 和 from。如果 overwrite 设置为 false,则deployer 将不会部署此合约(如果已经部署了该合约)。这对于由外部依赖项提供合约地址的某些情况很有用。
请注意,在调用 deploy 之前,我们需要首先部署和链接合约所依赖的任何库。有关详细信息,请参阅下面的 link 函数。
有关更多信息,请参阅 truffle-contract 文档。
Examples:
// Deploy a single contract without constructor arguments
deployer.deploy(A);
// Deploy a single contract with constructor arguments
deployer.deploy(A, arg1, arg2, ...);
// Don't deploy this contract if it has already been deployed
deployer.deploy(A, {overwrite: false});
// Set a maximum amount of gas and `from` address for the deployment
deployer.deploy(A, {gas: 4612388, from: "0x...."});
// Deploy multiple contracts, some with arguments and some without.
// This is quicker than writing three `deployer.deploy()` statements as the deployer
// can perform the deployment as a single batched request.
deployer.deploy([
[A, arg1, arg2, ...],
B,
[C, arg1]
]);
// External dependency example:
//
// For this example, our dependency provides an address when we're deploying to the
// live network, but not for any other networks like testing and development.
// When we're deploying to the live network we want it to use that address, but in
// testing and development we need to deploy a version of our own. Instead of writing
// a bunch of conditionals, we can simply use the `overwrite` key.
deployer.deploy(SomeDependency, {overwrite: false});
7.2 deployer.link(library, destinations)
将已部署的库链接到合约或多个合约。目标可以是单个合约或多个合约的数组。如果目标内的任何合约不依赖于链接的库,该合约将被忽略。
Example:
// Deploy library LibA, then link LibA to contract B, then deploy B.
deployer.deploy(LibA);
deployer.link(LibA, B);
deployer.deploy(B);
// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);
7.3 deployer.then(function() {...})
就像 promise 一样,执行任意部署步骤。使用此方法可在迁移期间调用特定的合约函数,以添加,编辑和重新组织合约数据。
Example:
var a, b;
deployer.then(function() {
// Create a new version of A
return A.new();
}).then(function(instance) {
a = instance;
// Get the deployed instance of B
return B.deployed();
}).then(function(instance) {
b = instance;
// Set the new instance of A's address on B via B's setA() function.
return b.setA(a.address);
});
Reference
- https://truffleframework.com/docs/truffle/getting-started/running-migrations
- https://truffleframework.com/ganache
- https://truffleframework.com/docs/truffle/advanced/networks-and-app-deployment
- https://github.com/trufflesuite/truffle/tree/next/packages/truffle-contract
- https://github.com/trufflesuite/truffle-contract
- https://github.com/trufflesuite/truffle
Contributor
- Windstamp, https://github.com/windstamp