0x04 智能合约之truffle
1.安装truffle
npm install -g truffle
2.选用 ETH
(1)自带的
truffle develop
(2)常用
- Geth (go-ethereum): https://github.com/ethereum/go-ethereum
- WebThree (cpp-ethereum): https://github.com/ethereum/cpp-ethereum
- Parity: https://github.com/paritytech/parity
- More: https://www.ethereum.org/cli
3.创建项目
(1)可以使用现成的库
mkdir MetaCoin
cd MetaCoin
truffle unbox metacoin
可以用 ``` truffle unbox <box-name> ```` 去下载任何想使用的库。
可以在这找到你想用的库 Truffle Boxes.
(2)diy一个库
mkdir myproject
cd myproject
truffle init
目录结构:
-
contracts/
: Directory for Solidity contracts -
migrations/
: Directory for scriptable deployment files -
test/
: Directory for test files for testing your application and contracts -
truffle.js
: Truffle configuration file
4.编译合同
(1)结构
所有的合同都位于您项目的contracts/
目录中。由于合同是用Solidity编写的,所有包含合同的文件的文件扩展名为.sol
。Associated Solidity 库也将有一个.sol
扩展。
通过初始化Truffle 项目(通过创建truffle init
),您可以获得Migrations.sol
帮助部署过程的单个文件。如果您使用的是box,您将在这里获得多个文件。
(2)命令
truffle compile
第一次运行时,所有合同都将被编译。后续运行时,只会编译自上次编译以来已更改的合同。如果您想覆盖此行为,请使用该--all
选项运行上述命令。
(3)依赖
通过文件名导入依赖关系
要从单独的文件导入合约,请将以下代码添加到您的Solidity源文件中::
import "./AnotherContract.sol";
这将使所有合同AnotherContract.sol
可用。这里AnotherContract.sol
是相对于当前正在编写的合同的路径。
请注意,Solidity也允许其他导入语法。请参阅Solidity 导入文档以获取更多信息。
从外部包导入合同
Truffle支持通过EthPM和NPM安装的依赖项。要从依赖项导入合约,请使用以下语法
import "somepackage/SomeContract.sol";
这里somepackage
表示通过EthPM或NPM安装的软件包,并SomeContract.sol
表示该软件包提供的Solidity 源文件。
请注意,在搜索从NPM安装的软件包之前,Truffle会首先从EthPM中搜索已安装的软件包,因此在极少数的命名冲突情况下,将使用通过EthPM安装的软件包。
有关如何使用Truffle包装管理功能的更多信息,请参阅Truffle EthPM和NPM文档。
5.迁移
truffle migrate
6.测试
全部
truffle test
单个
truffle test ./path/to/test/file.js
在SOLIDITY中编写测试,部署的地址
DeployedAddresses.<contract name>();
这将返回一个地址,然后您可以使用该地址访问该合同。请参阅上面的示例测试了解使用情况
为了使用已部署的合同,您必须将合同代码导入到测试套件中。import "../contracts/MetaCoin.sol";
在示例中注意。此导入与./test
目录中存在的测试合同相关,并且它位于测试目录之外以便查找MetaCoin合约。然后它使用该合约将该地址转换为该MetaCoin
类型。
测试合同名称
所有测试合同必须Test
以大写字母开头T
。这与测试助手和项目合同(即被测合同)区别开来,让测试运行人员知道哪些合同代表测试套件。
测试函数名称
与测试合同名称一样,所有测试函数都必须以test
小写字母开头。每个测试函数都按单个事务执行,按测试文件中的外观顺序(如Javascript测试)执行。由truffle/Assert.sol
测试运行程序评估以确定测试结果的触发事件提供的断言函数。断言函数返回一个表示断言结果的布尔值,您可以使用它来尽早从测试中返回以防止执行错误(例如,Ganache或Truffle Develop将会公开的错误)。
挂钩之前/之后
您提供了许多测试挂钩,如下例所示。这些钩子是beforeAll
,和beforeEach
,afterAll
和afterEach
你的Javascript测试中由Mocha提供的相同钩子。您可以使用这些钩子在每次测试之前和之后,或每个套件运行之前和之后执行设置和拆卸操作。像测试函数一样,每个钩子都作为单个事务执行。请注意,一些复杂的测试需要执行大量的设置,这可能会溢出单个事务的天然气限制; 您可以通过创建具有不同后缀的多个挂钩来解决此限制,如下例所示:
import "truffle/Assert.sol";
contract TestHooks {
uint someValue;
function beforeEach() {
someValue = 5;
}
function beforeEachAgain() {
someValue += 1;
}
function testSomeValueIsSix() {
uint expected = 6;
Assert.equal(someValue, expected, "someValue should have been 6");
}
}
该测试合同还显示您的测试功能和挂接功能都共享相同的合同状态。您可以在测试前设置合同数据,在测试过程中使用该数据,然后重新设置,以备下一个数据。请注意,就像您的Javascript测试一样,您的下一个测试函数将从之前运行的测试函数的状态继续。
高级功能
Solidity测试具有一些高级功能,可以让您测试Solidity中的特定用例。
测试例外
您可以轻松测试您的合同是否应该引发异常(例如,针对require()
/ assert()
/ revert()
陈述; throw
以前版本的Solidity)。
这个主题首先由作者Simon de la Rouviere在他的教程“truffle测试中的投掷测试”中撰写。NB的教程中大量使用通过过时的关键字例外throw
,取而代之的是revert()
,require()
和assert()
在密实v0.4.13开始。
测试以太网交易
您还可以测试您的合同如何对接收Ether做出反应,并在Solidity中编写该交互的脚本。要做到这一点,你的Solidity测试应该有一个返回一个uint
叫做的公共函数initialBalance
。这可以直接写成函数或公共变量,如下所示。当您的测试合同被部署到网络中时,trullfe公司将从测试账户中将这一数量的Ether发送到您的测试合同。然后,您的测试合同可以使用该以太网来编写您的合同测试中的以太网交互。请注意,这initialBalance
是可选的,不是必需的。
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/MyContract.sol";
contract TestContract {
// Truffle will send the TestContract one Ether after deploying the contract.
uint public initialBalance = 1 ether;
function testInitialBalanceUsingDeployedContract() {
MyContract myContract = MyContract(DeployedAddresses.MyContract());
// perform an action which sends value to myContract, then assert.
myContract.send(...);
}
function () {
// This will NOT be executed when Ether is sent. \o/
}
}
请注意,Truffle以不执行回退函数的方式将Ether发送到您的测试合同,因此您仍然可以在您的Solidity测试中使用回退函数进行高级测试用例。
7.控制台
truffle console
MyContract.at("0xabcd...").getValue.call();
8.合约交互
介绍
如果您为了与您的合同进行交互而自己向以太坊网络写入原始请求,您很快就会意识到编写这些请求非常笨重和麻烦。同样,你可能会发现管理状态的每个请求都很复杂。幸运的是,trullfe为您处理这一复杂问题,轻松与您的合同进行互动。
读取和写入数据
以太坊网络区分了将数据写入网络和从中读取数据的区别,这种区别在您编写应用程序的方式中起着重要作用。一般来说,写入数据称为事务,而读取数据称为调用。事务和调用的处理方式非常不同,并具有以下特征。
交易
事务从根本上改变了网络的状态。交易可以像发送以太网到其他账户一样简单,或像执行合同功能或向网络添加新合同一样复杂。事务的定义特征是它写入(或更改)数据。交易使Ether运行成为“ gas”,交易需要时间来处理。当您通过交易执行合约的功能时,您不能立即处理该交易,因此无法接收该函数的返回值。一般来说,意图通过事务执行的函数不会返回值; 他们将会返回一个事务ID。所以总之,交易:
- Cost gas (Ether)
- 改变网络的状态
- 不会立即处理
- 不会公开一个返回值(只有一个事务ID)。
呼叫
另一方面,呼叫则非常不同。通话可用于在网络上执行代码,但不会永久更改数据。通话可以自由运行,其特征是他们读取数据。当您通过呼叫执行合约功能时,您将立即收到返回值。总之,电话:
- 是免费的(不花费gas)
- 不要改变网络的状态
- 立即处理
- 将公开一个返回值(hooray!)
在事务和调用之间进行选择与决定是要读取数据还是写入数据一样简单。
引入抽象
合同抽象是与来自Javascript的以太坊合约进行交互的面包和黄油。简而言之,合同抽象是一种包装代码,可以让您轻松地与合同进行交互,从而让您忘记在引擎盖下执行的许多引擎和齿轮。trullfe通过truffle-contract
模块使用其自己的合同抽象,正是这种合同抽象如下所述。
然而,为了了解合同抽象的有用性,我们首先需要一个合同来谈论。我们将通过Truffle Boxes使用可用的MetaCoin合同truffle unbox metacoin
。
pragma solidity ^0.4.2;
import "./ConvertLib.sol";
// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!
contract MetaCoin {
mapping (address => uint) balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
function MetaCoin() {
balances[tx.origin] = 10000;
}
function sendCoin(address receiver, uint amount) returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Transfer(msg.sender, receiver, amount);
return true;
}
function getBalanceInEth(address addr) returns(uint){
return ConvertLib.convert(getBalance(addr),2);
}
function getBalance(address addr) returns(uint) {
return balances[addr];
}
}
本合同有三种方法除了构造函数(sendCoin
,getBalanceInEth
,和getBalance
)。所有三种方法都可以作为交易或通话来执行。
现在让我们来看一看MetaCoin
Truffle提供给我们的Javascript对象,就像在Truffle控制台中提供的那样:
// Print the deployed version of MetaCoin.
// Note that getting the deployed version requires a promise, hence the .then.
MetaCoin.deployed().then(function(instance) {
console.log(instance);
});
// outputs:
//
// Contract
// - address: "0xa9f441a487754e6b27ba044a5a8eb2eec77f6b92"
// - allEvents: ()
// - getBalance: ()
// - getBalanceInEth: ()
// - sendCoin: ()
// ...
请注意,抽象包含我们合约中存在的完全相同的函数。它还包含指向MetaCoin合约的已部署版本的地址。
执行合约功能
使用抽象,您可以轻松地在以太坊网络上执行合约功能。
进行交易
MetaCoin合约上有三项我们可以执行的功能。如果你分析它们中的每一个,你会发现这sendCoin
是唯一一个旨在改变网络的功能。目标sendCoin
是从一个帐户“发送”一些元币到下一个,并且这些更改应该持续。
在打电话时sendCoin
,我们会将其作为交易执行。在下面的例子中,我们将从一个账户向另一个账户发送10个Meta硬币,以保持网络变化的方式:
var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// If this callback is called, the transaction was successfully processed.
alert("Transaction successful!")
}).catch(function(e) {
// There was an error! Handle it.
})
上面的代码有一些有趣的地方:
- 我们
sendCoin
直接调用了抽象的功能。这将导致默认交易(即写入数据)而不是通话。 - 当事务成功时,直到事务处理完成后才会触发回调函数。这使生活变得轻松,意味着您不必亲自检查交易状态。
- 我们通过一个对象作为第三个参数
sendCoin
。请注意,sendCoin
我们的Solidity合同中的功能没有第三个参数。你在上面看到的是一个特殊的对象,它可以作为函数的最后一个参数传递,让你编辑有关事务的特定细节。在这里,我们设置from
确保此交易来自的地址account_one
。
拨打电话
继续使用MetaCoin,请注意,该getBalance
函数是从网络读取数据的理想选择。它不需要做任何改变,因为它只是返回传递给它的地址的MetaCoin余额。让我们试试看:
var account_one = "0x1234..."; // an address
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one, {from: account_one});
}).then(function(balance) {
// If this callback is called, the call was successfully executed.
// Note that this returns immediately without any waiting.
// Let's print the return value.
console.log(balance.toNumber());
}).catch(function(e) {
// There was an error! Handle it.
})
这里有趣的是:
- 我们必须
.call()
明确地执行该功能,以让以太坊网络知道我们不打算坚持任何更改。 - 我们在成功时收到了返回值而不是事务ID。请注意,因为以太坊网络可以处理非常大的数字,所以我们得到一个BigNumber对象,然后我们将其转换为数字。
警告:我们将返回值转换为数字,因为在这个例子中数字很小。但是,如果您尝试转换大于Javascript支持的最大整数的BigNumber,则可能会遇到错误或意外行为。
捕捉事件
您的合同可以触发您可以捕获的事件,以更深入地了解您的合同在做什么。处理事件的最简单方法是处理触发事件的事务的结果对象,如下所示:
var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// result is an object with the following values:
//
// result.tx => transaction hash, string
// result.logs => array of decoded events that were triggered within this transaction
// result.receipt => transaction receipt object, which includes gas used
// We can loop through result.logs to see if we triggered the Transfer event.
for (var i = 0; i < result.logs.length; i++) {
var log = result.logs[i];
if (log.event == "Transfer") {
// We found the event!
break;
}
}
}).catch(function(err) {
// There was an error! Handle it.
});
处理交易结果
当您进行交易时,您会收到一个result
对象,可以为您提供有关交易的丰富信息。具体来说,你会得到以下结果:
-
result.tx
(字符串) - 事务散列 -
result.logs
(数组) - 解码事件(日志) -
result.receipt
(对象) - 交易收据
欲了解更多信息,请参阅README中truffle-contract
的项目。
向网络添加新的合同
在上述所有情况下,我们一直在使用已经部署的合同抽象。我们可以使用以下.new()
功能将我们自己的版本部署到网络中:
MetaCoin.new().then(function(instance) {
// Print the new address
console.log(instance.address);
}).catch(function(err) {
// There was an error! Handle it.
});
在特定地址使用合同
如果您已经拥有合同地址,则可以创建一个新抽象来表示该地址的合同。
var instance = MetaCoin.at("0x1234...");
发送ETHER到合同
您可能只想直接将Ether发送给合同,或者触发合同的后备功能。您可以使用以下两个选项之一来完成此操作。
选项1:通过直接发送交易给合约instance.sendTransaction()
。这与所有可用的契约实例函数一样是promisified,并且具有相同的API,web3.eth.sendTransaction
但没有回调。to
如果没有指定,该值将自动填入。
instance.sendTransaction({...}).then(function(result) {
// Same transaction result object as above.
});
选项2:直接发送Ether也有简写:
instance.send(web3.toWei(1, "ether")).then(function(result) {
// Same result object as above.
});
进一步阅读
trullfe提供的合同抽象包含丰富的实用工具,可轻松与您的合同进行交互。查看trullfe contract文档以了解提示,技巧和见解。
9.通过ETHPM进行包裹管理
安装一个包
truffle install <package name>
安装特定版本的软件包:
truffle install <package name>@<version>
安装依赖关系
您的项目可以定义一个ethpm.json
文件,其中可以将您的项目固定到特定的依赖项和版本。要安装ethpm.json
文件中列出的所有依赖项,请运行:
$ truffle install
有关该ethpm.json
文件的更多详细信息,请参阅下面的包配置。
消费已安装的合同
安装的软件包将被放置在installed_contracts
您的项目文件夹中的目录中。如果没有installed_contracts
目录存在,它会为你创建。你应该像对待node_modules
NPM 文件夹那样对待这个文件夹 - 也就是说,除非你知道你在做什么,否则你不应该编辑内部的内容。:)
安装的软件包可以在您的测试,迁移和固化合同文件中使用,通过import
“ require
包装”或按名称进行合同。例如,以下Solidity合约将从包中导入owned.sol
文件owned
:
pragma solidity ^0.4.2;
import "owned/owned.sol";
contract MyContract is owned {
// ...
}
同样,以下迁移文件将使用包中的ENS.sol
合约ens
:
文件: ./migrations/2_deploy_contracts.js
var ENS = artifacts.require("ens/ENS");
var MyContract = artifacts.require("MyContract");
module.exports = function(deployer) {
// Only deploy ENS if there's not already an address already.
// i.e., don't deploy if we're using the canonical ENS address,
// but do deploy it if we're on a test network and ENS doesn't exist.
deployer.deploy(ENS, {overwrite: false}).then(function() {
return deployer.deploy(MyContract, ENS.address);
});
};
请注意,在上面的迁移过程中,我们会根据ens
ENS是否已设置地址来有条件地使用该包并部署ENS合同。这是部署人员提供给你的一个奇妙的技巧,它使写入迁移依赖于网络工件的存在变得更加容易。在这种情况下,如果我们在Ropsten网络上运行我们的迁移,此迁移不会部署该ENS
合同,因为(在撰写本文时)Ropsten是规范ENS
合同存在的地方 - 我们不想部署我们自己的合同。但是,如果我们正在针对不同的网络或测试网络运行我们的迁移,那么我们想要部署该ENS
合同,以便我们有一个依赖合约来处理。
发布你自己的软件包
发布自己的软件包与安装一样简单,但与NPM一样,需要更多配置。
ROPSTEN,ROPSTEN,ROPSTEN
Ethereum软件包注册表目前存在于Ropsten测试网络中。要发布到注册中心,我们需要建立我们自己的Ropsten配置,因为我们将进行需要签名的交易。
在本例中,我们将使用Infura与truffle-hdwallet-provider
NPM模块一起发布软件包以及一个代表Ropsten网络上Ethereum地址的12字高清钱包助记符。首先,truffle-hdwallet-provider
在您的项目目录中安装通过NPM:
$ npm install truffle-hdwallet-provider --save
然后编辑您的配置,ropsten
使用12字助记符添加网络:
文件: truffle.js
var HDWalletProvider = require("truffle-hdwallet-provider");
// 12-word mnemonic
var mnemonic = "opinion destroy betray ...";
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
},
ropsten: {
provider: new HDWalletProvider(mnemonic, "https://ropsten.infura.io/"),
network_id: 3 // official id of the ropsten network
}
}
};
程序包配置
与NPM类似,EthPM的配置选项位于单独的JSON文件中ethpm.json
。此文件与您的Truffle配置并列,并为Truffle发布包装所需的所有信息。您可以在配置部分看到可用选项的完整列表。
文件: ethpm.json
{
"package_name": "adder",
"version": "0.0.3",
"description": "Simple contract to add two numbers",
"authors": [
"Tim Coulter <tim.coulter@consensys.net>"
],
"keywords": [
"ethereum",
"addition"
],
"dependencies": {
"owned": "^0.0.1"
},
"license": "MIT"
}
命令
在确定您的配置后,发布很简单:
$ truffle publish
您会看到类似于下面的输出,并确认您的包已成功发布。
$ truffle publish
Gathering contracts...
Finding publishable artifacts...
Uploading sources and publishing to registry...
+ adder@0.0.3
发布之前
当使用develop
配置为匹配任何Ethereum客户端的默认网络(如Ganache或Truffle Develop)时,您肯定会在网络中存在不希望发布的工件。在发布您的软件包之前,请考虑运行以下命令以删除任何无关的网络工件:
$ truffle networks --clean
10.通过NPM进行包装管理
Truffle标配npm
集成,并且知道node_modules
项目中的目录(如果存在)。这意味着您可以通过使用和分发契约,dapps和启用以太坊的库npm
,使您的代码可用于其他人以及其他可用的代码。
包装布局
使用Truffle创建的项目默认具有特定的布局,可以将它们用作包。这种布局不是必需的,但如果用作公共约定 - 或“事实上的标准” - 则通过NPM分发契约和dapps将变得更容易。
truffle套餐中最重要的目录如下:
/contracts
-
/build
(其中包括/build/contracts
由truffle创建的)
第一个目录是您的合同目录,其中包含您的原始Solidity合同。第二个目录是构建目录,更具体地说/build/contracts
,它以.json
文件形式存放构建工件。在你的软件包中包括原始合同将允许其他人在他们自己的固体代码中导入这些合同。同样,.json
在你的软件包中包含你的构建工件将允许其他人与你的JavaScript合约进行无缝交互,这可以在dapps,脚本和迁移中使用。
使用一个包
在自己的项目中使用软件包时,需要注意的是,有两个地方可能对使用其他合同代码感兴趣:在您的合同中以及您的Javascript代码(迁移和测试)中。以下提供了每个案例的示例,并讨论了充分利用其他合约并构建工件的技巧。
安装
在本例中,我们将使用示例truffle库,该库提供了一个简单的名称注册表,该注册表已部署到Morden测试网络。为了将其用作依赖项,我们必须首先通过npm
以下方式将其安装在我们的项目中:
$ cd my_project
$ npm install example-truffle-library
请注意,上面的最后一个命令会下载该软件包并将其放在my_project/node_modules
目录中,这对于下面的示例很重要。请参阅npm文档以获取使用npm
安装软件包的帮助。
在你的合同中
要在合同中使用包裹的合同,这可以与Solidity的进口声明一样简单。当您的导入路径不是明确的相对或绝对时,这意味着您要从特定命名包中查找文件。考虑使用上面提到的示例truffle库的这个例子:
import "example-truffle-library/contracts/SimpleNameRegistry.sol";
由于路径并未开始./
,Truffle知道要在项目的node_modules
目录中查找该example-truffle-library
文件夹。从那里,它解决了为您提供您所要求的合同的途径。
在JAVASCRIPT代码中
要在JavaScript代码中与包的合约进行交互,您只需要require
该包的.json
文件,然后使用truffle-contract模块将这些文件转换为可用的抽象:
var contract = require("truffle-contract");
var data = require("example-truffle-library/build/contracts/SimpleNameRegistry.json");
var SimpleNameRegistry = contract(data);
要使用这些抽象概念,请参阅与您的合同交互一节以获取更多详细信息。
包的部署地址
有时候你希望你的合同与这个包的先前部署的合同进行交互。由于部署的地址存在于包的.json
文件中,因此您必须执行额外的步骤才能将这些地址存入您的合同。为此,请使您的合同接受依存关系合同的地址,然后使用迁移。以下是项目中存在的示例合同以及示例迁移:
合同: MyContract.sol
pragma solidity ^0.4.13;
import "example-truffle-library/contracts/SimpleNameRegistry.sol";
contract MyContract {
SimpleNameRegistry registry;
address public owner;
function MyContract {
owner = msg.sender;
}
// Simple example that uses the deployed registry from the package.
function getModule(bytes32 name) returns (address) {
return registry.names(name);
}
// Set the registry if you're the owner.
function setRegistry(address addr) {
require(msg.sender == owner);
registry = SimpleNameRegistry(addr);
}
}
移民: 3_hook_up_example_library.js
// Note that artifacts.require takes care of creating an abstraction for us.
var SimpleNameRegistry = artifacts.require("example-truffle-library/SimpleNameRegistry");
module.exports = function(deployer) {
// Deploy our contract, then set the address of the registry.
deployer.deploy(MyContract).then(function() {
return MyContract.deployed();
}).then(function(deployed) {
return deployed.setRegistry(SimpleNameRegistry.address);
});
};
发布之前
当使用develop
配置为匹配任何Ethereum客户端的默认网络(如Ganache或Truffle Develop)时,您肯定会在网络中存在不希望发布的工件。在发布您的软件包之前,请考虑运行以下命令以删除任何无关的网络工件:
$ truffle networks --clean
11.调试您的合同
truffle包含一个集成的调试器,以便您可以调试对您的合同进行的交易。这个调试器看起来和现有的可用于传统开发环境的命令行调试器相似。
概观
在区块链上调试事务与调试传统应用程序(例如,用C ++或Javascript编写的应用程序)不同。在区块链上调试事务时,您不是实时运行代码; 相反,您正在逐步执行该事务的历史执行,并将该执行映射到其相关代码。这为我们调试提供了许多自由度,因为我们可以在任何时候调试任何事务,只要我们拥有事务交互的合同的代码和工件即可。将这些代码和工件看作类似于传统调试器所需的调试符号。
为了调试交易,您需要以下内容:
- truffle4.0或以上。
- 交易在您想要的区块链上的散列。
- 事务遇到的源代码和工件。
请注意,如果您希望的交易导致异常情况或气体用完,那也没问题。事务仍然存在于链上,所以你仍然可以调试它!
命令
要使用调试器,请收集您想要调试的事务,然后运行以下命令:
$ truffle debug <transaction hash>
以一个开始的事务0x8e5dadfb921dd...
为例,该命令如下所示:
$ truffle debug 0x8e5dadfb921ddddfa8f53af1f9bd8beeac6838d52d7e0c2fe5085b42a4f3ca76
这将启动下面描述的调试接口。
调试接口
启动调试器将打开一个熟悉调试其他类型应用程序的界面。开始时,您会看到以下内容:
- 在此交易过程中进行交易或创建的地址列表。
- 使用调试器的可用命令列表。
- 以及交易的初始入口点,包括合同源文件和代码预览。
该enter
键被设置为执行输入的最后一个命令。当调试器启动时,该enter
键被设置为步进到在执行期间遇到的下一个逻辑源代码元素(即,由以太坊虚拟机评估的下一个表达式或语句)。此时,您可以按键enter
逐步完成事务,或者输入其中一个可用命令来更详细地分析事务。命令列表详述如下。
(O)跨越
此命令遍历当前行,相对于当前在Solidity源文件中评估的语句或表达式的位置。如果您不想在当前行中进入函数调用或合约创建,或者想要快速跳转到源文件中的特定点,请使用此命令。
(I)进入
该命令进入当前正在评估的函数调用或合同创建。使用此命令跳转到该函数并快速开始调试那里存在的代码。
(你)走出去
该命令退出当前正在运行的功能。如果这是事务的入口点,则使用此命令可以快速返回调用函数,或者结束事务的执行。
(N)下一步
该命令转到源代码中的下一个逻辑语句或表达式。例如,在虚拟机可以评估完整表达式之前,需要先评估子表达式。如果您想分析虚拟机评估的每个逻辑项,请使用此命令。
(;)步指令
该命令允许您逐步查看虚拟机评估的每条单独指令。如果您有兴趣了解由Solidity源代码创建的低级别字节码,这非常有用。当你使用这个命令时,调试器在评估指令时也会打印出栈数据。
(P)打印指示
该命令打印当前的指令和堆栈数据,但不会进入下一条指令。如果希望在使用上述逻辑命令导航事务后查看当前指令和堆栈数据,请使用此选项。
(H)打印此帮助
打印可用命令的列表。
(Q)退出
退出调试器。
12.使用truffle开发和控制台
有时,为了测试和调试目的,或者手动执行交易,交互式地处理您的合同是很好的。truffle为您提供了两种通过交互式控制台执行此操作的简单方法,您的合同可供使用并随时可用。
- truffle控制台:连接到任何以太坊客户端的基本交互式控制台
- truffle开发:交互式控制台,也产生一个发展区块链
为什么两个不同的控制台?
拥有两个不同的控制台可让您根据自己的需求选择最佳工具。
使用truffle平台的理由:
- 你有一个你已经使用的客户,如Ganache或geth
- 你想迁移到测试网络(或以太坊网络)
- 您想要使用特定的助记符或帐户列表
使用truffle的原因发展:
- 您正在测试您的项目,无意立即部署
- 您无需使用特定帐户(并且使用默认开发帐户即可)
- 您不想安装和管理单独的区块链客户端
命令
所有的命令都要求你在你的项目文件夹中。你不需要在根。
安慰
要启动控制台:
truffle console
这将查找development
在配置中调用的网络定义,并连接到它(如果可用)。您可以使用该--network <name>
选项覆盖此选项。在“ 网络”部分以及命令参考中查看更多详细信息。
当您加载控制台时,您会立即看到以下提示:
truffle(development)>
这告诉你你正在使用development
网络在Truffle控制台中运行。
truffle开发
开展truffle开发:
truffle develop
9545
无论您的truffle.js
配置文件需要什么,这将在端口上本地产生一个开发区块链。
当你加载truffle开发,你会看到以下内容:
Truffle Develop started at http://localhost:9545/
Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de
Private Keys:
(0) c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
(1) ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
(2) 0dbbe8e4ae425a6d2687f1a7e3ba17bc98c673636790f1b8ad91193c05875ef1
(3) c88b703fb08cbea894b6aeff5a544fb92e78a18e19814cd85da83b71f772aa6c
(4) 388c684f0ba1ef5017716adb5d21a053ea8e90277d0868337519f97bede61418
(5) 659cbb0e2411a44db63778987b1e22153c086a95eb6b18bdf89de078917abc63
(6) 82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8
(7) aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7
(8) 0f62d96d6675f32685bbdb8ac13cda7c23436f63efbb9d07700d8669ff12b7c4
(9) 8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5
Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat
这会向您显示此特定区块链的地址,私钥和助记符。
注意:助记符和地址不能更改。如果您想使用不同的助记符或一组地址,我们推荐使用Ganache。
警告:切记不要使用任何这些地址或主网上的助记符。这仅用于开发。
特征
Truffle Develop和控制台都提供了Truffle命令行工具中的大部分功能。例如,您可以migrate --reset
在控制台中输入内容,并将其解释为与truffle migrate --reset
在命令行上运行时相同。
此外,truffle开发和控制台还具有以下特点:
-
你所有的编译合同都可以使用。
-
在每个命令(例如
migrate --reset
)您的合同被重新配置后,您可以立即开始使用新分配的地址和二进制文件。 -
该
web3
库已经可用,并已设置为连接到您的以太坊客户端。 -
所有返回承诺的命令都会自动解析,并打印结果,不需要使用
.then()
简单的命令。例如,以下命令:MyContract.at("0xabcd...").getValue.call();
会返回类似于:
5
命令可用
build
compile
create
debug
exec
install
migrate
networks
opcode
publish
test
version
如果truffle命令不可用,那是因为它与现有项目无关(例如init
)或者没有意义(例如develop
或console
)。
查看完整的命令参考了解更多信息。
13.编写外部脚本
通常您可能想要运行与您的合同交互的外部脚本。truffle提供了一种简单的方法来实现这一点,根据您所需的网络引导您的合同,并根据您的项目配置自动连接到您的以太坊客户端。
命令
要运行外部脚本,请执行以下操作:
$ truffle exec <path/to/file.js>
文件结构
为了使外部脚本正确运行,Truffle期望它们导出一个函数,该函数将一个参数作为回调函数:
module.exports = function(callback) {
// perform actions
}
只要在脚本结束时调用回调,就可以在该脚本中执行任何您想要的操作。回调接受一个错误作为它的第一个也是唯一的参数。如果提供了错误,执行将暂停并且该进程将返回非零退出代码。
13.使用构建管道
Truffle 1.0和2.0标准的默认构建系统主要面向Web应用程序(这里,术语“构建”意味着将代码工件转换为HTML,Javascript和CSS)。该构建系统已被引入自己的模块,使Truffle可用于各种应用并可扩展。
truffle可以配置为与任何构建系统紧密集成。要配置自定义生成系统,请参阅高级生成进程部分以获取更多详细信息。
命令
要在构建系统时配置应用程序,请运行:
$ truffle build
请注意,如果您尝试在build
未首先配置自定义构建过程的情况下运行该命令,则会收到错误。