设置开发环境
在我们开始之前,有一些技术要求。请安装以下内容:
- Node.js v6 + LTS和npm(自带Node)(https://nodejs.org/en/)
一旦我们安装了这些,我们只需要一个命令来安装Truffle:
npm install -g truffle
要验证Truffle是否已正确安装,请truffle version在终端上键入。如果发现错误,请确保将npm模块添加到路径中。
我们还将使用Ganache,一个用于以太坊开发的个人区块链,您可以使用它来部署合同,开发应用程序和运行测试。您可以通过导航到http://truffleframework.com/ganache并单击“下载”按钮来下载Ganache 。
Truffle在当前目录中初始化,因此首先在您选择的开发文件夹中创建一个目录,然后在其中移动。
mkdir pet-shop-tutorial
cd pet-shop-tutor
mkdir pet-shop-tutorial
cd pet-shop-tutorial
al
truffle unbox命令解压缩此松露盒。
truffle unbox命令解压缩此松露盒。
truffle unbox pet-shop
truffle unbox pet-shop
目录结构
默认的Truffle目录结构包含以下内容:
- contracts/:包含我们的智能合约的Solidity源文件。这里有一个重要的合同Migrations.sol,我们将在后面讨论。
- migrations/:Truffle使用迁移系统来处理智能合约部署。迁移是一种额外的特殊智能合约,可以跟踪变化。
- test/:包含我们的智能合约的JavaScript和Solidity测试
- truffle.js:松露配置文件
该pet-shop松露盒有额外的文件和文件夹,但我们不会担心这些,只是还没有。
写出智能合约
我们将通过编写充当后端逻辑和存储的智能合约来启动我们的dapp。
1创建一个名为一个新的文件Adoption.sol在contracts/目录中。
2将以下内容添加到文件中:
pragma solidity ^0.4.17;
contract Adoption {
}
需要注意的事项:
- 合同顶部注明了所需的最低Solidity版本:pragma solidity ^0.4.17;。该pragma命令表示“ 只有编译器关心的附加信息 ”,而插入符号(^)表示“ 指示的版本或更高 ”。
- 与JavaScript或PHP一样,语句以分号结束。
变量设置
Solidity是一种静态类型语言,意味着必须定义字符串,整数和数组等数据类型。Solidity有一种称为地址的独特类型。地址是以太坊地址,存储为20字节值。以太坊区块链上的每个账户和智能合约都有一个地址,可以通过该地址发送和接收以太网。、
之后的下一行添加以下变量contract Adoption {
address[16] public adopters;
我们定义了一个变量:adopters。这是以太坊地址的数组。数组包含一种类型,可以具有固定或可变长度。在这种情况下,类型是address和长度是16。
你也会注意到adopters是公开的。公共变量具有自动getter方法,但在数组的情况下,需要一个键,并且只返回一个值。稍后,我们将编写一个函数来返回整个数组,以便在我们的UI中使用。
你的第一个功能:采用宠物
1在我们上面设置的变量声明之后,将以下函数添加到智能合约中。
// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
需要注意的事项:
在Solidity中,必须指定函数参数和输出的类型。在这种情况下,我们将接受一个petId(整数)并返回一个整数。
我们正在检查以确保petId在我们的adopters阵列范围内。Solidity中的数组从0开始索引,因此ID值必须介于0到15之间。我们使用该require()语句确保ID在范围内。
如果ID在范围内,我们然后添加调用我们的adopters数组的地址。调用此函数的人或智能合约的地址表示为msg.sender。
最后,我们将petId提供的内容作为确认返回。
您的第二个功能:检索采用者
如上所述,数组getter仅从给定键返回单个值。我们的UI需要更新所有宠物采用状态,但进行16次API调用并不理想。所以我们下一步是编写一个函数来返回整个数组。
1在我们上面添加的getAdopters()函数之后,将以下函数添加到智能合约中adopt():
// Retrieving the adopters
function getAdopters() public view returns (address[16]) {
return adopters;
}
既然adopters已经宣布,我们可以简单地归还它。请务必将返回类型(在本例中为类型adopters)指定为address[16]。
编译和迁移智能合约
现在我们已经编写了智能合约,接下来的步骤是编译和迁移它。
Truffle有一个内置的开发者控制台,我们称之为Truffle Develop,它生成一个开发区块链,我们可以使用它来测试部署合同。它还能够直接从控制台运行Truffle命令。在本教程中,我们将使用Truffle Develop来执行我们合同中的大部分操作。
Solidity是一种编译语言,这意味着我们需要将我们的Solidity编译为字节码,以便执行以太坊虚拟机(EVM)。可以把它想象成将人类可读的Solidity翻译成EVM理解的东西。
1在终端中,确保您位于包含dapp的目录的根目录中并键入:
truffle compile
您应该看到类似于以下内容的输出:
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Adoption.sol...
Writing artifacts to ./build/contracts
移民
现在我们已经成功编译了合同,是时候将它们迁移到区块链了!
迁移是一种部署脚本,用于更改应用程序合同的状态,将其从一个状态移动到另一个状态。对于第一次迁移,您可能只是部署新代码,但随着时间的推移,其他迁移可能会移动数据或用新代码替换合同。
您将在migrations/目录中看到一个JavaScript文件:1_initial_migration.js。这会处理部署Migrations.sol合同以观察后续智能合约迁移,并确保我们不会在未来双重迁移未更改的合同。
现在我们准备创建自己的迁移脚本了。
创建一个名为一个新的文件2_deploy_contracts.js在migrations/目录中。
将以下内容添加到2_deploy_contracts.js文件中:
var Adoption = artifacts.require("Adoption"); module.exports = function(deployer) { deployer.deploy(Adoption); };
在我们将合同迁移到区块链之前,我们需要运行区块链。对于本教程,我们将使用Ganache,一个用于以太坊开发的个人区块链,您可以使用它来部署合同,开发应用程序和运行测试。如果您还没有,请下载Ganache并双击该图标以启动该应用程序。这将生成在端口7545上本地运行的区块链。
回到我们的终端,将合同迁移到区块链。
truffle migrate
truffle migrate
您应该看到类似于以下内容的输出:
Using network 'development'.
Running migration: 1_initial_migration.js Deploying Migrations... ... 0xcc1a5aea7c0a8257ba3ae366b83af2d257d73a5772e84393b0576065bf24aedf Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
您可以按顺序查看正在执行的迁移,然后是每个已部署合同的区块链地址。
在Ganache中,请注意区块链的状态已发生变化。现在,区块链显示了当前的块,0现在是4。此外,虽然第一个帐户最初有100个以太,但由于迁移的交易成本,它现在更低。我们稍后会详细讨论交易成本。
迁移后的Ganache
您现在已经编写了第一份智能合约,并将其部署到本地运行的区块链中。现在是时候与我们的智能合约进行互动,以确保它能够满足我们的需求。
测试智能合约
Truffle在智能合约测试方面非常灵活,因为测试可以用JavaScript或Solidity编写。在本教程中,我们将在Solidity中编写测试。
创建一个名为一个新的文件TestAdoption.sol在test/目录中。
将以下内容添加到TestAdoption.sol文件中:
pragma solidity ^0.4.17; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Adoption.sol"; contract TestAdoption { Adoption adoption = Adoption(DeployedAddresses.Adoption()); }
我们用3个进口开始合同:
- Assert.sol:给我们在测试中使用的各种断言。在测试中,断言检查诸如平等,不平等或空虚之类的事情,以便从我们的测试中返回通过/失败。以下是Truffle附带的断言的完整列表。
- DeployedAddresses.sol:运行测试时,Truffle会将正在测试的合同的新实例部署到区块链。这个智能合约获得已部署合同的地址。
- Adoption.sol:我们想要测试的智能合约。
-
注意:前两个导入是指全局Truffle文件,而不是truffle目录。您不应该在truffle目录中看到test/目录。
然后我们定义一个合约范围的变量,其中包含要测试的DeployedAddresses智能合约,调用智能合约来获取其地址。
测试adopt()函数
要测试该adopt()功能,请记住,一旦成功,它将返回给定的petId。通过比较adopt()我们传入的ID 的返回值,我们可以确保返回一个ID并且它是正确的。
TestAdoption.sol声明后,在智能合约中添加以下功能Adoption:
// Testing the adopt() function function testUserCanAdoptPet() public { uint returnedId = adoption.adopt(8); uint expected = 8; Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded."); }
需要注意的事项:
- 我们将之前声明的智能合约称为ID 8。
- 然后我们也声明了预期值8。
- 最后,我们传递实际值,期望值和失败消息(如果测试未通过则将其打印到控制台)Assert.equal()。
测试单个宠物主人的检索
记住上面公共变量有自动getter方法,我们可以检索上面的采用测试存储的地址。存储的数据将在我们的测试期间持续存在,因此我们对8上面的pet的采用可以通过其他测试来检索。
在以前添加的函数下面添加此函数TestAdoption.sol。
// Testing retrieval of a single pet's owner function testGetAdopterAddressByPetId() public { // Expected owner is this contract address expected = this; address adopter = adoption.adopters(8); Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded."); }
由于TestAdoption合约将发送交易,我们将预期值设置为此,这是一个获得当前合约地址的合约范围变量。从那里我们就像上面所说的那样断言平等。
测试所有宠物主人的检索
由于数组只能在给定单个键的情况下返回单个值,因此我们为整个数组创建自己的getter。
在以前添加的函数下面添加此函数TestAdoption.sol。
// Testing retrieval of all pet owners function testGetAdopterAddressByPetIdInArray() public {
// Expected owner is this contract
address expected = this;
// Store adopters in memory rather than contract's storage
address[16] memory adopters = adoption.getAdopters(); Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
}
请注意内存属性adopters。memory属性告诉Solidity将值临时存储在内存中,而不是将其保存到合同的存储中。由于adopters是一个数组,我们从第一次采用测试我们知道采用pet 8,我们将测试合同地址与8数组中的位置进行比较。
-
运行测试
回到终端,运行测试
运行测试 回到终端,运行测试: truffle test 如果所有测试都通过,您将看到与此类似的控制台输出: Using network 'development'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... Compiling truffle/Assert.sol... Compiling truffle/DeployedAddresses.sol... TestAdoption ✓ testUserCanAdoptPet (91ms) ✓ testGetAdopterAddressByPetId (70ms) ✓ testGetAdopterAddressByPetIdInArray (89ms) 3 passing (670ms)
创建用户界面以与智能合约进行交互<下一章>