5. 移植
原文地址:http://truffleframework.com/docs/getting_started/migrations
移植是由一些Javascript文件组成来协助发布到以太坊网络。主要目的是用来缓存你的发布任务,它的存在基于你的发布需求会改变的前提。当你的工程发生了重要的改变,你将创建新的移植脚本来将这些变化带到区块链上。之前运行移植的历史记录通过一个特殊的Migrations
合约来记录到链上,下面有详细说明。
命令
执行移植,使用下述命令:
truffle migrate
这个命令会执行所有的位于migrations
目录内的移植脚本。如果你之前的移植是成功执行的。truffle migrate
仅会执行新创建的移植。如果没有新的移植脚本,这个命令不同执行任何操作。可以使用选项--reset
来从头执行移植脚本。
移植脚本文件
一个样例文件如下:
文件名:4_example_migration.js
module.exports = function(deployer) {
// deployment steps
deployer.deploy(MyContract);
};
需要注意的是文件名以数字开头,一个描述性的后缀结尾。数字前缀是必须的,用于记录移植是否成功。后缀仅是为了提高可读性,以方便理解。
移植js里的exports
的函数接受一个deployer
对象作为第一个参数。这个对象用于发布过程,提供了一个清晰的语法支持,同时提供一些通过的合约部署职责,比如保存发布的文件以备稍后使用。deployer
对象是用来缓存(stage)发布任务的主要操作接口。API接口见后说明。
像所有其它在Truffle中的代码一样,Truffle为你提供了你自己代码的合约抽象层(contract abstractions)
,并且进行了初始化,以方便你可以便利的与以太坊的网络交互。这些抽象接口是发布流程的一部分,稍后你将会看到。
初始移植
Truffle需要一个移植合约来使用移植特性。这个合约内需要指定的接口,但你可以按你的意味修改合约。对大多数工程来说,这个合约会在第一次移植时进行的第一次部署,后续都不会再更新。通过truffle init
创建一个全新工程时,你会获得一个默认的合约。
文件名:contracts/Migration.sol
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_migrations.js
module.exports = function(deployer) {
// Deploy the Migrations contract as our only task
deployer.deploy(Migrations);
};
由此,你可以接着创建递增的数字前缀来部署其它合约。
部署器(deployer)
你的移植文件会使用部署器来缓存部署任务。所以,你可以按一定顺序排列发布任务,他们会按正确顺序执行。
// Stage deploying A before B
deployer.deploy(A);
deployer.deploy(B);
另一选中可选的部署方式是使用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,在后面进行说明。
网络相关
可以根据发布到的网络的具体情况进行不同的部署流程。这是一个高级特性,你先看2. 网络与APP部署的相关内容后,再继续。
要实现不同条件的不同部署步骤,移植代码中需要第二个参数network
。示例如下:
module.exports = function(deployer, network) {
// Add demo data if we're not deploying to the live network.
if (network != "live") {
deployer.exec("add_demo_data.js");
}
}
部署API
部署器有许多的可用函数,用来简化部署流程。
DEPLOYER.DEPLOY(CONTRACT, ARGS...)
发布一个指定的合约,第一参数是合约对象,后面是一些可选的构造器参数。
这个函数适用于单例合约,它只会在你的dapp中只创建一个这个合约的实例(单例)。函数会在部署后设置合约的地址(如:Contract.address 将等于新的部署地址),它将会覆盖之前存储的地址。
你也可以传入一个合约数组,或数组的数组来加速多合约的部署。
需要注意的是如果库的地址可用,deploy
会自动为这个部署的合约联接任何需要的库。所以,如果合约依赖某个库,你应该先部署这个库。
例子:
// Deploy a single contract without constructor arguments
deployer.deploy(A);
// Deploy a single contract with constructor arguments
deployer.deploy(A, arg1, arg2, ...);
// 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 batched request.
deployer.deploy([
[A, arg1, arg2, ...],
B,
[C, arg1]
]);
DEPLOYER.LINK(LIBRARY, DESTINATIONS)
联接一个已经发布的库到一个或多个合约。destinations
可以是一个合约或多个合约组成的一个数组。如果目标合约并不依赖这个库,部署器会忽略掉这个合约。
这对于在dapp中不打算部署的合约(如:非单例)但却需要在使用前先联接的情况下非常有用。
// Deploy library LibA, then link LibA to contract B
deployer.deploy(LibA);
deployer.link(LibA, B);
// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);
DEPLOYER.AUTOLINK(CONTRACT)
关联合约依赖的所有库。这需要所依赖的库已经部署,或在其前一步部署。
例子:
// Assume A depends on a LibB and LibC
deployer.deploy([LibB, LibC]);
deployer.autolink(A);
另外你可以省略参数来调用函数autolink()
。这会自动关联合约依赖的所有库。需要保证在调用这个函数前,所有被需要的库已经部署了。
例子:
// Link *all* libraries to all available contracts
deployer.autolink();
DEPLOYER.THEN(FUNCTION() {...})
Promise语法糖,执行做生意的部署流程。
例子:
deployer.then(function() {
// Create a new version of A
return A.new();
}).then(function(instance) {
// Set the new instance of A's address on B.
var b = B.deployed();
return b.setA(instance.address);
});
DEPLOYER.EXEC(PATHTOFILE)
执行truffle exec
做为部署的一部分。查看10. 外部脚本章节了解更多。
例子:
// Run the script, relative to the migrations file.
deployer.exec("../path/to/file/demo_data.js");
如果任何问题,欢迎留言批评指正。
处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。