第三部分. 编程

在Truffle中进行测验
Truffle用来做智能合约的测验驱动开发(TDD)十分棒,我强烈推荐你在学习中运用它。它也是学习运用JavaScript Promise的一个好途径,例如deferred和异步调用。Promise机制有点像是说“做这件事,假如成果是这样,做甲,假如成果是那样,做乙... 与此同时不要在那儿干等着成果回来,行不?”。Truffle运用了包装web3.js的一个JS Promise结构Pudding(因而它为为你装置web3.js)。(译注:Promise是流行于JavaScript社区中的一种异步调用模式。它很好的封装了异步调用,使其能够灵活组合,而不会堕入callback hell.)
Transaction times. Promise对于DApp十分有用,因为买卖写入以太坊区块链需求大约12-15秒的时刻。即便在测验网络上看起来没有那么慢,在正式网络上却或许会要更长的时刻(例如你的买卖或许用光了Gas,或许被写入了一个孤儿块)。
下面让咱们给一个简略的智能合约写测验用例吧。
运用Truffle
首先保证你 1.装置好了solc以及 2.testrpc。(testrpc需求Python和pip。假如你是Python新手,你或许需求用virtualenv来装置,这能够将Python程序库装置在一个独立的环境中。)
接下来装置 3.Truffle(你能够运用NodeJS's npm来装置:npm install -g truffle, -g开关或许会需求sudo)。装置好之后,在指令行中输入truffle list来验证装置成功。然后创立一个新的项目目录(我把它命名为'conference'),进入这个目录,运转truffle init。该指令会树立如下的目录结构:
现在让咱们在另一个终端里经过履行testrpc来发动一个节点(你也能够用geth):
回到之前的终端中,输入truffle deploy。这条指令会布置之前truffle init发生的模板合约到网络上。任何你或许遇到的错误信息都会在testrpc的终端或许履行truffle的终端中输出。
在开发过程中你随时能够运用truffle compile指令来承认你的合约能够正常编译(或许运用solc YourContract.sol),truffle deploy来编译和布置合约,最终是truffle test来运转智能合约的测验用例。
第一个合约
下面是一个针对会议的智能合约,经过它参会者能够买票,组织者能够设置参会人数上限,以及退款策略。本文触及的所有代码都能够在这个代码库房找到。
contract Conference https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
address public organizer;
mapping (address => uint) public registrantsPaid;
uint public numRegistrants;
uint public quota;
event Deposit(address _from, uint _amount); // so you can log these events
event Refund(address _to, uint _amount);
function Conference() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg // Constructor
organizer = msg.sender;
quota = 500;
numRegistrants = 0;
}
function buyTicket() public returns (bool success) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
if (numRegistrants >= quota) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg return false; }
registrantsPaid[msg.sender] = msg.value;
numRegistrants++;
Deposit(msg.sender, msg.value);
return true;
}
function changeQuota(uint newquota) public https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
if (msg.sender != organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg return; }
quota = newquota;
}
function refundTicket(address recipient, uint amount) public https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
if (msg.sender != organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg return; }
if (registrantsPaid[recipient] == amount) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
address myAddress = this;
if (myAddress.balance >= amount) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
recipient.send(amount);
registrantsPaid[recipient] = 0;
numRegistrants--;
Refund(recipient, amount);
}
}
}
function destroy() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg // so funds not locked in contract forever
if (msg.sender == organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
suicide(organizer); // send funds to organizer
}
}
}
接下来让咱们布置这个合约。(留意:本文写作时我运用的是Mac OS X 10.10.5, solc 0.1.3+ (经过brew装置),Truffle v0.2.3, testrpc v0.1.18 (运用venv))
布置合约
(译注:图中过程翻译如下:)
运用truffle布置智能合约的过程:
1. truffle init (在新目录中) => 创立truffle项目目录结构
2. 编写合约代码,保存到contracts/YourContractName.sol文件。
3. 把合约姓名加到config/app.json的'contracts'部分。
4. 发动以太坊节点(例如在另一个终端里边运转testrpc)。
5. truffle deploy(在truffle项目目录中)
增加一个智能合约。 在truffle init履行后或是一个现有的项目目录中,仿制粘帖上面的会议合约到contracts/Conference.sol文件中。然后翻开config/app.json文件,把'Conference'参加'deploy'数组中。
发动testrpc。 在另一个终端中发动testrpc。
编译或布置。 履行truffle compile看一下合约是否能成功编译,或许直接truffle deploy一步完成编译和布置。这条指令会把布置好的合约的地址和ABI(应用接口)参加到装备文件中,这样之后的truffle test和truffle build过程能够运用这些信息。
出错了? 编译是否成功了?记住,错误信息即或许出现在testrpc终端也或许出现在truffle终端。
重启节点后记住重新布置! 假如你中止了testrpc节点,下一次运用任何合约之前牢记运用truffle deploy重新布置。testrpc在每一次重启之后都会回到完全空白的状况。
合约代码解读
让咱们从智能合约头部的变量声明开端:
address public organizer;
mapping (address => uint) public registrantsPaid;
uint public numRegistrants;
uint public quota;
address. 地址类型。第一个变量是会议组织者的钱包地址。这个地址会在合约的结构函数function Conference()中被赋值。许多时分也称呼这种地址为'owner'(所有人)。
uint. 无符号整型。区块链上的存储空间很严重,保持数据尽或许的小。
public. 这个要害字表明变量能够被合约之外的目标运用。private修饰符则表明变量只能被本合约(或许衍生合约)内的目标运用。假如你想要在测验中经过web3.js运用合约中的某个变量,记住把它声明为public。
Mapping或数组。(译注:Mapping相似Hash, Directory等数据类型,不做翻译。)在Solidity参加数组类型之前,大家都运用相似mapping (address => uint)的Mapping类型。这个声明也能够写作address registrantsPaid[],不过Mapping的存储占用更小(smaller footprint)。这个Mapping变量会用来保存参加者(用他们的钱包地址表明)的付款数量以便在退款时运用。
关于地址。 你的客户端(比如testrpc或许geth)能够生成一个或多个账户/地址。testrpc发动时会显现10个可用地址:
第一个地址, accounts[0],是建议调用的默许地址,假如没有特别指定的话。
组织者地址 vs. 合约地址。 布置好的合约会在区块链上具有自己的地址(与组织者具有的是不同的地址)。在Solidity合约中能够运用this来访问这个合约地址,正如refundTicket函数所展示的:address myAddress = this;
Suicide, Solidity的好东西。(译注:suicide意为'自杀', 为Solidity供给的要害字,不做翻译。)转给合约的资金会保存于合约(地址)中。最终这些资金经过destroy函数被释放给了结构函数中设置的组织者地址。这是经过suicide(orgnizer);这行代码完成的。没有这个,资金或许被永久锁定在合约之中(reddit上有些人就遇到过),因而假如你的合约会承受资金一定要记住在合约中运用这个办法!
假如想要模仿另一个用户或许对手方(例如你是卖家想要模仿一个买家),你能够运用可用地址数组中别的的地址。假定你要以另一个用户,accounts[1], 的身份来买票,能够经过from参数设置:
conference.buyTicket(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[1], value: some_ticket_price_integer });
函数调用能够是买卖。 改动合约状况(修正动量值,增加记载,等等)的函数调用自身也是转账买卖,隐式的包括了发送人和买卖价值。因而web3.js的函数调用能够经过指定https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: __, value: __ }参数来发送以太币。在Solidity合约中,你能够经过msg.sender和msg.value来获取这些信息:
function buyTicket() public https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
...
registrantsPaid[msg.sender] = msg.value;
...
}
作业(Event)。 可选的功能。合约中的Deposit(充值)和Send(发送)作业是会被记载在以太坊虚拟机日志中的数据。它们实际上没有任何效果,可是用作业(Event)把买卖记载进日志是好的做法。
好了,现在让咱们给这个智能合约写一个测验,来保证它能作业。
写测验
把项目目录test/中的example.js文件重命名为conference.js,文件中所有的'Example'替换为'Conference'。
contract('Conference', function(accounts) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
it("should assert true", function(done) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var conference = Conference.at(Conference.deployed_address);
assert.isTrue(true);
done(); // stops tests at this point
});
});
在项目根目录下运转truffle test,你应该看到测验经过。在上面的测验中truffle经过Conference.deployed_address获得合约布置在区块链上的地址。
让咱们写一个测验来初始化一个新的Conference,然后查看变量都正确赋值了。将conference.js中的测验代码替换为:
contract('Conference', function(accounts) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
it("Initial conference settings should match", function(done) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var conference = Conference.at(Conference.deployed_address);
// same as previous example up to here
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] })
.then(function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
conference.quota.call().then(
function(quota) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(quota, 500, "Quota doesn't match!");
}).then( function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
return conference.numRegistrants.call();
}).then( function(num) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(num, 0, "Registrants should be zero!");
return conference.organizer.call();
}).then( function(organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(organizer, accounts[0], "Owner doesn't match!");
done(); // to stop these tests earlier, move this up
}).catch(done);
}).catch(done);
});
});
结构函数。 Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] })经过调用合约结构函数发明了一个新的Conference实例。因为不指定from时会默许运用accounts[0],它其实能够被省掉掉:
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] }); // 和Conference.new()效果相同
Promise. 代码中的那些then和return就是Promise。它们的效果写成一个深深的嵌套调用链的话会是这样:
conference.numRegistrants.call().then(
function(num) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(num, 0, "Registrants should be zero!");
conference.organizer.call().then(
function(organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(organizer, accounts[0], "Owner doesn't match!");
}).then(
function(...))
}).then(
function(...))
// Because this would get hairy...
Promise减少嵌套,使代码变得扁平,允许调用异步回来,而且简化了表达“成功时做这个”和“失败时做那个”的语法。Web3.js经过回调函数完成异步调用,因而你不需求比及买卖完成就能够持续履行前端代码。Truffle借助了用Promise封装web3.js的一个结构,叫做Pudding,这个结构自身又是根据Bluebird的,它支持Promise的高档特性。
call. 咱们运用call来查看变量的值,例如conference.quota.call().then(...,还能够经过传参数,例如call(0), 来获取mapping在index 0处的元素。Solidity的文档说这是一种特别的“音讯调用”因为 1.不会为矿工记载和 2.不需求从钱包账户/地址建议(因而它没有被账户持有者私钥做签名)。另一方面,买卖/事务(Transaction)会被矿工记载,必须来自于一个账户(也就是有签名),会被记载到区块链上。对合约中数据做的任何修正都是买卖。只是是查看一个变量的值则不是。因而在读取变量时不要忘掉加上call()!不然会发生奇怪的作业。(此外假如在读取变量是遇到问题别忘掉查看它是否是public。)call()也能用于调用不是买卖的函数。假如一个函数本来是买卖,但你却用call()来调用,则不会在区块链上发生买卖。
断语。 标准JS测验中的断语(假如你不小心拼成了复数方式'asserts',truffle会报错,让你一头雾水),assert.equal是最常用的,其他类型的断语能够在Chai的文档中找到。
再一次运转truffle test保证一切作业正常。
测验合约函数调用
现在咱们测验一下改动quote变量的函数能作业。在tests/conference.js文件的contract('Conference', function(accounts) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg...};)的函数体中增加如下测验用例:
it("Should update quota", function(done) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var c = Conference.at(Conference.deployed_address);
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: accounts[0] }).then(
function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
conference.quota.call().then(
function(quota) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(quota, 500, "Quota doesn't match!");
}).then( function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
return conference.changeQuota(300);
}).then( function(result) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg // result here is a transaction hash
console.log(result); // if you were to print this out it’d be long hex - the transaction hash
return conference.quota.call()
}).then( function(quota) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(quota, 300, "New quota is not correct!");
done();
}).catch(done);
}).catch(done);
});
这儿的新东西是调用changeQuota函数的那一行。console.log对于调试很有用,用它能在运转truffle的终端中输出信息。在要害点刺进console.log能够查看履行到了哪一步。记住把Solidity合约中changeQuota函数被声明为public,不然你不能调用它:
function changeQuota(uint newquota) public https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg }
测验买卖
现在让咱们调用一个需求建议人发送资金的函数。
Wei. 以太币有许多种单位(这儿有个很有用的转化器),在合约中通常用的是Wei,最小的单位。Web3.js供给了在各单位与Wei之间互相转化的便当办法,形如web3.toWei(.05, 'ether')。JavaScript在处理很大的数字时有问题,因而web3.js运用了程序库BigNumber,并建议在代码各处都以Wei做单位,直到要给用户看的时分(文档。
账户余额。 Web3.js供给了许多供给方便的办法,其中另一个会在下面测验用到的是web3.eth.getBalance(some_address)。记住发送给合约的资金会由合约自己持有直到调用suicide。
在contract(Conference, function(accounts) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg...};)的函数体中刺进下面的测验用例。在高亮显现的办法中,测验用例让另一个用户(accounts[1])以ticketPrice的价格买了一张门票。然后它查看合约的账户余额增加了ticketPrice,以及购票用户被参加了参会者列表。
这个测验中的buyTicket是一个买卖函数:
it("Should let you buy a ticket", function(done) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var c = Conference.at(Conference.deployed_address);
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] }).then(
function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
conference.buyTicket(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[1], value: ticketPrice }).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var newBalance = web3.eth.getBalance(conference.address).toNumber();
var difference = newBalance - initialBalance;
assert.equal(difference, ticketPrice, "Difference should be what was sent");
return conference.numRegistrants.call();
}).then(function(num) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(num, 1, "there should be 1 registrant");
return conference.registrantsPaid.call(accounts[1]);
}).then(function(amount) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
assert.equal(amount.toNumber(), ticketPrice, "Sender's paid but is not listed");
done();
}).catch(done);
}).catch(done);
});
买卖需求签名。 和之前的函数调用不同,这个调用是一个会发送资金的买卖,在这种情况下购票用户(accounts[1])会用他的私钥对buyTicket()调用做签名。(在geth中用户需求在发送资金之前经过输入密码来批准这个买卖或是解锁钱包的账户。)
toNumber(). 有时咱们需求把Solidity回来的十六进制成果转码。假如成果或许是个很大的数字能够用web3.toBigNumber(numberOrHexString)来处理因为JavaScript直接对付大数要糟。
测验包括转账的合约
最终,为了完整性,咱们承认一下refundTicket办法能正常作业,而且只有会议组织者能调用。下面是测验用例:
it("Should issue a refund by owner only", function(done) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var c = Conference.at(Conference.deployed_address);
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] }).then(
function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
conference.buyTicket(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[1], value: ticketPrice }).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var newBalance = web3.eth.getBalance(conference.address).toNumber();
var difference = newBalance - initialBalance;
assert.equal(difference, ticketPrice, "Difference should be what was sent"); // same as before up to here
// Now try to issue refund as second user - should fail
return conference.refundTicket(accounts[1], ticketPrice, https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: accounts[1]});
}).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var balance = web3.eth.getBalance(conference.address).toNumber();
assert.equal(web3.toBigNumber(balance), ticketPrice, "Balance should be unchanged");
// Now try to issue refund as organizer/owner - should work
return conference.refundTicket(accounts[1], ticketPrice, https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: accounts[0]});
}).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var postRefundBalance = web3.eth.getBalance(conference.address).toNumber();
assert.equal(postRefundBalance, initialBalance, "Balance should be initial balance");
done();
}).catch(done);
}).catch(done);
});
这个测验用例掩盖的Solidity函数如下:
function refundTicket(address recipient, uint amount) public returns (bool success) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
if (msg.sender != organizer) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg return false; }
if (registrantsPaid[recipient] == amount) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
address myAddress = this;
if (myAddress.balance >= amount) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
recipient.send(amount);
Refund(recipient, amount);
registrantsPaid[recipient] = 0;
numRegistrants--;
return true;
}
}
return false;
}
合约中发送以太币。 address myAddress = this展示了如何获取该会议合约实例的地址,以变接下来查看这个地址的余额(或许直接运用this.balance)。合约经过recipient.send(amount)办法把资金发回了购票人。
买卖无法回来成果给web3.js. 留意这一点!refundTicket函数会回来一个布尔值,可是这在测验中无法查看。因为这个办法是一个买卖函数(会改动合约内数据或是发送以太币的调用),而web3.js得到的买卖运转成果是一个买卖哈希(假如打印出来是一个长长的十六进制/怪怪的字符串)。既然如此为什么还要让refundTicket回来一个值?因为在Solidity合约内能够读到这个回来值,例如当另一个合约调用refundTicket()的时分。也就是说Solidity合约能够读取买卖运转的回来值,而web3.js不可。另一方面,在web3.js中你能够用作业机制(Event, 下文会解说)来监控买卖运转,而合约不可。合约也无法经过call()来查看买卖是否修正了合约内变量的值。
关于sendTransaction(). 当你经过web3.js调用相似buyTicket()或许refundTicket()的买卖函数时(运用web3.eth.sendTransaction),买卖并不会立即履行。事实上买卖会被提交到矿工网络中,买卖代码直到其中一位矿工发生一个新区块把买卖记载进区块链之后才履行。因而你必须等买卖进入区块链而且同步回本地节点之后才干验证买卖履行的成果。用testrpc的时分或许看上去是实时的,因为测验环境很快,可是正式网络会比较慢。
作业/Event. 在web3.js中你应该监听作业而不是回来值。咱们的智能合约示例定义了这些作业:
event Deposit(address _from, uint _amount);
event Refund(address _to, uint _amount);
它们在buyTicket()和refundTicket()中被触发。触发时你能够在testrpc的输出中看到日志。要监听作业,你能够运用web.js监听器(listener)。在写本文时我还不能在truffle测验中记载作业,可是在应用中没问题:
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] }).then(
function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var event = conference.allEvents().watch(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg}, ''); // or use conference.Deposit() or .Refund()
event.watch(function (error, result) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
if (error) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
console.log("Error: " + error);
} else https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
console.log("Event: " + result.event);
}
});
// ...
过滤器/Filter. 监听所有作业或许会发生大量的轮询,作为代替能够运用过滤器。它们能够更灵活的开端或是中止对作业的监听。更多过滤器的信息可查看Solidity文档。
总的来说,运用作业和过滤器的组合比查看变量耗费的Gas更少,因而在验证正式网络的买卖运转成果时十分有用。
Gas. (译注:以太坊上的燃料,因为代码的履行必须耗费Gas。直译为汽油比较突兀,故保留原文做专有名词。)直到现在咱们都没有触及Gas的概念,因为在运用testrpc时通常不需求显式的设置。当你转向geth和正式网络时会需求。在买卖函数调用中能够在https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: __, value: __, gas: __}目标内设置Gas参数。Web3.js供给了web3.eth.gasPrice调用来获取当前Gas的价格,Solidity编译器也供给了一个参数让你能够从指令行获取合约的Gas开销概要:solc --gas YouContract.sol。下面是Conference.sol的成果:
为合约创立DApp界面
下面的段落会假定你没有网页开发经历。
上面编写的测验用例用到的都是在前端界面中也能够用的办法。你能够把前端代码放到app/目录中,运转truffle build之后它们会和合约装备信息一起编译输出到build/目录。在开发时能够运用truffle watch指令在app/有任何变化时自动编译输出到build/目录。然后在浏览器中改写页面即可看到build/目录中的最新内容。(truffle serve能够发动一个根据build/目录的网页服务器。)
app/目录中有一些样板文件协助你开端:
index.html会加载app.js:
因而咱们只需求增加代码到app.js就能够了。
默许的app.js会在浏览器的console(控制台)中输出一条"Hello from Truffle!"的日志。在项目根目录中运转truffle watch,然后在浏览器中翻开build/index.html文件,再翻开浏览器的console就能够看到。(大部分浏览器例如Chrome中,单击右键 -> 挑选Inspect Element然后切换到Console即可。)
在app.js中,增加一个在页面加载时会运转的window.onload调用。下面的代码会承认web3.js现已正常载入并显现所有可用的账户。(留意:你的testrpc节点应该保持运转。)
window.onload = function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var accounts = web3.eth.accounts;
console.log(accounts);
}
看看你的浏览器console中看看是否打印出了一组账户地址。
现在你能够从tests/conference.js中仿制一些代码过来(去掉只和测验有关的断语),将调用回来的成果输出到console中以承认代码能作业。下面是个例子:
window.onload = function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var accounts = web3.eth.accounts;
var c = Conference.at(Conference.deployed_address);
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[0] }).then(
function(conference) https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
console.log("The conference's initial balance is: " + initialBalance);
conference.buyTicket(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg from: accounts[1], value: ticketPrice }).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var newBalance = web3.eth.getBalance(conference.address).toNumber();
console.log("After someone bought a ticket it's: " + newBalance);
return conference.refundTicket(accounts[1], ticketPrice, https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: accounts[0]});
}).then(
function() https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpg
var balance = web3.eth.getBalance(conference.address).toNumber();
console.log("After a refund it's: " + balance);
});
});
};
上面的代码应该输出如下:
(console输出的warning信息可忽略。)
现在起你就能够运用你喜爱的任何前端东西,jQuery, ReactJS, Meteor, Ember, AngularJS,等等等等,在app/目录中构建能够与以太坊智能合约互动的DApp界面了!接下来咱们给出一个极端简略根据jQuery的界面作为示例。
这儿是index.html的代码,这儿是app.js的代码。
经过界面测验了智能合约之后我意识到最好参加查看以保证相同的用户不能注册两次。别的因为现在是运转在testrpc节点上,速度很快,最好是切换到geth节点并承认买卖过程仍然能及时响应。不然的话界面上就应该显现提示信息而且在处理买卖时禁用相关的按钮。
测验geth. 假如你运用geth, 能够测验以下面的指令发动 - 在我这儿(geth v1.2.3)作业的很好:
build/bin/geth --rpc --rpcaddr="0.0.0.0" --rpccorsdomain="*" --mine --unlock='0 1' --verbosity=5 --maxpeers=0 --minerthreads='4' --networkid '12345' --genesis test-genesis.json
这条指令解锁了两个账户, 0和1。1. 在geth控制台发动后你或许需求输入这两个账户的密码。2. 你需求在test-genesis.json文件里边的'alloc'装备中参加你的这两个账户,而且给它们充足的资金。3. 最终,在创立合约实例时加上gas参数:
Conference.new(https://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpgfrom: accounts[0], gas: 3141592})
然后把整个truffle deploy, truffle build流程重来一遍。
教程中的代码。 在这篇基础教程中用到的所有代码都能够在这个代码库房中找到。
自动为合约生成界面。 SilentCicero制作了一个叫做DApp Builder的东西,能够用Solidity合约自动生成HTML, jQuery和web.js的代码。这种模式也正在被越来越多的正在开发中的开发者东西采用。
教程到此结束! 最终一章咱们只是学习了一套东西集,主要是Truffle和testrpc. 要知道即便在ConsenSys内部,不同的开发者运用的东西和结构也不尽相同。你或许会发现更适合你的东西,这儿所说的东西或许很快也会有改善。可是本文介绍的作业流程协助我走上了DApp开发之路。
(⊙⊙) wonk wonk
感谢Joseph Chow的校阅和建议,Christian Lundkvist, Daniel Novy, Jim Berry, Peter Borah和Tim Coulter帮我修正文字和debug,以及Tim Coulter, Nchinda Nchinda和Mike Goldin对DApp前端过程图供给的协助。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注