智能合约,Nthereum,truffle

在上一篇文章中,以太坊区块链与Nethereum的交互中我已经在dotnet客户端内硬编码了合约代码和ABI。它看起来很一般,我不太喜欢它!

此外,我没有使用truffle测试功能,因为我遇到了ganache-cli没有传播事件的问题。

首先,我创建了一个空文件夹,并运行:

1
truffle init

之后,我在合约文件夹中添加了合约。我不喜欢原始结构,因此我删除了构造函数参数并将事件更改为具有被乘数,乘数和乘积参数名称。

1
2
3
4
5
6
7
8
9
10
11
12
pragma solidity ^0.4.21;

contract Multiplier {
event Multiplied(uint256 indexed multiplicand, uint256 indexed multiplier, address indexed sender, uint256 product);

function multiply(uint256 a, uint256 b) public returns(uint r) {
// looks like an overflow waiting to happen
r = a * b;
emit Multiplied(a, b, msg.sender, r);
return r;
}
}

添加测试

接下来,我在合约中添加了简单的健全性检查。合约工件首先需要:

1
var Multiplier = artifacts.require("./Multiplier.sol");

然后,可以通过调用deployed()方法来创建此合约的实例:

1
contract = await Multiplier.deployed();

之后你可以写测试了!Truffle js测试带有异步/等待支持,这意味着更短,更易于理解的代码!

1
2
3
4
it("should multiply", async function () {
let res = await contract.multiply.call(4, 5);
assert.equal(res, 20);
})

起初,测试将失败并出现神秘错误。

1
2
3
4
5
1) Contract: Multiplier with deployment "before each" hook: deploy contract for "deploy":
Error: Multiplier has not been deployed to detected network (network/artifact mismatch)
at /usr/local/lib/node_modules/truffle/build/webpack:/~/truffle-contract/contract.js:454:1
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)

事实证明我忘记在迁移文件夹中添加2_deploy_contract.js迁移文件:

1
2
3
4
5
var Multiplier = artifacts.require("./Multiplier.sol");

module.exports = function(deployer) {
deployer.deploy(Multiplier);
};

以下是完整的测试代码。当然,在生产代码中可以预期需要更多的测试。这是一个我保持简洁的样本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Multiplier = artifacts.require("./Multiplier.sol");

ccontract('Multiplier', function () {
let contract = null;

beforeEach('deploy', async function() {
contract = await Multiplier.deployed();
})

describe("multiply", function() {
it("should multiply", async function () {
var res = await contract.multiply.call(2, 2));
assert.equal(res, 4);
});
});
});

编制合约

运行truffle compile命令将在json文件中生成字节码和ABI。 默认情况下,此文件位于合约项目的build/contracts文件夹中。这可以在truffle.js文件中更改。

1
2
3
4
5
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
contracts_build_directory: "./../../Client/NethClient/Contracts/Multiplier"
};

加载合约

要将合约复制到输出文件夹,请通过添加以下内容来更改csproj文件:

1
2
3
<ItemGroup>
<None Include="Contracts/**" CopyToOutputDirectory="Always" />
</ItemGroup>

既然合约存在于dotnet项目中,它也可以使用JObject加载。

1
2
3
var json = JObject.Load(new JsonTextReader(new StreamReader(File.OpenRead("Contracts/Multiplier/Multiplier.json"))));
var abi = json["abi"].ToString();
var bytecode = json["bytecode"].ToString();

我借此机会稍微重构代码以进一步分离另一个类中的合约操作。这可能对以后与其他合约一起使用很有用。

事件和gas

运行代码后,一切都很顺利,除了没有返回任何事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
client_1  | Multiply 8 by 9 using 24082 gas
geth_1 | INFO [05-15|20:01:08] Submitted transaction fullhash=0xdfde494ea6d244f927066bf877862b400e0b358bfba33bb790a907736a4d1a1c recipient=0x98245fba911B71ECAe3490ba9D324a2C7038ffbB
geth_1 | INFO [05-15|20:01:13] Successfully sealed new block number=361 hash=377574…fffd4f
geth_1 | INFO [05-15|20:01:13] 🔗 block reached canonical chain number=356 hash=281662…94a4fd
geth_1 | INFO [05-15|20:01:13] 🔨 mined potential block number=361 hash=377574…fffd4f
geth_1 | INFO [05-15|20:01:13] Commit new mining work number=362 txs=1 uncles=0 elapsed=15.164ms
geth_1 | INFO [05-15|20:01:18] Successfully sealed new block number=362 hash=460dc2…306b71
geth_1 | INFO [05-15|20:01:18] 🔗 block reached canonical chain number=357 hash=cd3d35…0435c3
geth_1 | INFO [05-15|20:01:18] 🔨 mined potential block number=362 hash=460dc2…306b71
geth_1 | INFO [05-15|20:01:18] Commit new mining work number=363 txs=0 uncles=0 elapsed=1.468ms
client_1 | -> Done at block 362 using 24082 gas
client_1 | Get all events for filter 317348022035050220953892121001492572177
client_1 | -> Got 0

结果我忘记了气体计算功能中的一个参数。

1
2
3
4
5
// wrong
var gas = await _multiply.EstimateGasAsync(multiplicand).ConfigureAwait(false);

// correct
var gas = await _multiply.EstimateGasAsync(multiplicand, multiplier).ConfigureAwait(false);

gas低于要求,没有发生任何事件。这很奇怪,因为如果gas不足,应该提出错误?修复代码后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
client_1  | Multiply 8 by 9 using 24274 gas
geth_1 | INFO [05-15|20:15:28] Submitted transaction fullhash=0x1454c7801c61fe19f9950976418a6af1b0e696317f77cfbf5ab65cbbd124c653 recipient=0x68281a51523A439260Ca30c7121D6Ed9B39A95e6
geth_1 | INFO [05-15|20:15:33] Successfully sealed new block number=379 hash=dd9754…a4e240
geth_1 | INFO [05-15|20:15:33] 🔗 block reached canonical chain number=374 hash=5d3b4b…f477a3
geth_1 | INFO [05-15|20:15:33] 🔨 mined potential block number=379 hash=dd9754…a4e240
geth_1 | INFO [05-15|20:15:33] Commit new mining work number=380 txs=1 uncles=0 elapsed=4.295ms
geth_1 | INFO [05-15|20:15:38] Successfully sealed new block number=380 hash=64b91a…0bc39b
geth_1 | INFO [05-15|20:15:38] 🔗 block reached canonical chain number=375 hash=300263…5cc25a
geth_1 | INFO [05-15|20:15:38] 🔨 mined potential block number=380 hash=64b91a…0bc39b
geth_1 | INFO [05-15|20:15:38] Commit new mining work number=381 txs=0 uncles=0 elapsed=3.007ms
client_1 | -> Done at block 380 using 24274 gas
client_1 | Get all events for filter 189326955700024171316112267564238036559
client_1 | -> Got 2
client_1 | -> Block 378 : 7 * 7 = 49
client_1 | -> Block 380 : 8 * 9 = 72

添加更多测试

我要加上chaichai-as-promisechai-bignumber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(web3.BigNumber))
.should();

var Multiplier = artifacts.require("./Multiplier.sol");

contract('Multiplier', function () {
let contract = null;

beforeEach('deploy', async function() {
contract = await Multiplier.deployed();
})

describe("multiply", function() {
it("should multiply", async function () {
(await contract.multiply.call(2, 2)).should.bignumber.equal(4);
});

it("should not overflow", async function () {
// uint256 max value
const max = new web3.BigNumber(2, 10).pow(256).sub(1);

await contract.multiply.call(max, 2).should.eventually.be.rejected;
});
});
});

由于溢出错误,运行测试应该失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ truffle test
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Multiplier.sol...
Compiling openzeppelin-solidity/contracts/math/SafeMath.sol...

Contract: Multiplier
multiply
✓ should multiply simple numbers (66ms)
✓ should multiply BigNumbers (57ms)
1) should not overflow
> No events were emitted


2 passing (286ms)
1 failing

1) Contract: Multiplier multiply should not overflow:
AssertionError: expected promise to be rejected but it was fulfilled with { Object (s, e, ...) }

引用openzeppelin

接下来,我添加了openzeppelin:

1
yarn add openzeppelin-solidity --save-dev

现在,我可以通过在合约代码文件中导入所需的合约来引用此库。在这里,我使用了SafeMath库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

contract Multiplier {

event Multiplied(uint256 indexed multiplicand, uint256 indexed multiplier, address indexed sender, uint256 product);

function multiply(uint256 a, uint256 b) public returns(uint r) {
r = SafeMath.mul(a, b);

emit Multiplied(a, b, msg.sender, r);

return r;
}
}

现在,再次运行测试应该通过:

1
2
3
4
5
6
7
8
9
10
11
12
$ truffle test
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Multiplier.sol...
Compiling openzeppelin-solidity/contracts/math/SafeMath.sol...

Contract: Multiplier
multiply
✓ should multiply simple numbers (52ms)
✓ should multiply BigNumbers (41ms)
✓ should not overflow

3 passing (234ms)

现在一切都很好看。

本文的源代码库位于Github上。

如果希望快速进行以太坊开发,那请看我们精心打造的教程:

C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和事件等。

其他区块链教程如下:

  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。

汇智网原创翻译,转载请标明出处。这里是原文