参考文档
Solidity的event事件(二十一)|入门系列
http://me.tryblockchain.org/blockchain-solidity-event.html
以太坊中的日志和事件:
http://www.8btc.com/ethereum-events-and-logs
事件 event
事件是以太坊EVM提供的一种日志基础设施。
事件可以用来做操作记录,存储为日志。
有了事件DAPP就可以实现一些交互功能,比如通过监控事件并定义回调函数,获取事件触发的日志。
event定义
event关键字
定义一个事件,事件可以被继承。
event transfer(address indexed _from, address indexed _to, uint indexed value);
indexed属性
:可以在事件参数上增加indexed属性,最多可以对三个参数增加这样的属性。属性作用是允许你在web3.js中通过对加了这个属性的参数进行值过滤,比如:
var event = myContract.transfer({value: "100"});
var event = myContract.transfer({value: ["99","100","101"]});
增加了
indexed
的参数值会存到日志结构的Topic部分,便于快速查找。
而未加indexed
的参数值会存在data部分,成为原始日志。
event 触发
类似调用函数的方式,触发事件,比如:
function deposit() payable {
address current = this;
uint value = msg.value;
transfer(msg.sender, current, value);
}
event监听
在web3.js中,提供了响应事件的方法,如下2:
var event = myContract.transfer();
// 监听
event.watch(function(error, result){
console.log("Event are as following:-------");
for(let key in result){
console.log(key + " : " + result[key]);
}
console.log("Event ending-------");
});
另外一种简便写法是直接加入事件回调,这样就不用再写watch的部分:
var event = myContract.transfer(function(error, result){
console.log("Event are as following:-------");
for(let key in result){
console.log(key + " : " + result[key]);
}
console.log("Event ending-------");
});
event.stopWatching();来终止监听
过滤参数
除了indexed属性
,事件还支持传入fromBlock
,toBlock
等过滤条件。
var event = myContract.transfer({value: "100"}, {fromBlock: 0, toBlock: 'latest'});
底层的日志接口
可以通过底层的日志接口来访问底层的日志机制。通过函数log0,log1,log2,log3,log4。logi支持i + 1个类型为bytes32的参数。其中第一个参数是日志的data部分,其它参数为topic。所以下述事件:
log3(
msg.value, 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,
msg.sender,
_id
);
第一个参数为非indexed
的data
部分值。第二个参数为默认主题,即事件签名的哈希值keccak256("Deposit(address,hash256,uint256)")
,后面两个是按顺序的indexed
主题。
使用web3.js读取事件的完整例子
例子展示了很多内容,包括:
- 编译合约
- 部署合约
- 合约部署后的回调函数
- 合约事件监控
let Web3 = require('web3');
let web3;
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
} else {
// set the provider you want from Web3.providers
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
let from = web3.eth.accounts[0];
//编译合约
let source = "pragma solidity ^0.4.0;contract Transfer{ event transfer(address indexed _from, address indexed _to, uint indexed value); function deposit() payable { address current = this; uint value = msg.value; transfer(msg.sender, current, value); } function getBanlance() constant returns(uint) { return this.balance; } /* fallback function */ function(){}}";
let transferCompiled = web3.eth.compile.solidity(source);
console.log(transferCompiled);
console.log("ABI definition:");
console.log(transferCompiled["info"]["abiDefinition"]);
//得到合约对象
let abiDefinition = transferCompiled["info"]["abiDefinition"];
let transferContract = web3.eth.contract(abiDefinition);
//2. 部署合约
//2.1 获取合约的代码,部署时传递的就是合约编译后的二进制码
let deployCode = transferCompiled["code"];
//2.2 部署者的地址,当前取默认账户的第一个地址。
let deployeAddr = web3.eth.accounts[0];
//2.3 异步方式,部署合约
//警告,你不应该每次都部署合约,这里只是为了提供一个可以完全跑通的例子!!!
transferContract.new({
data: deployCode,
from: deployeAddr,
gas: 1000000
}, function(err, myContract) {
if (!err) {
// 注意:这个回调会触发两次
//一次是合约的交易哈希属性完成
//另一次是在某个地址上完成部署
// 通过判断是否有地址,来确认是第一次调用,还是第二次调用。
if (!myContract.address) {
console.log("contract deploy transaction hash: " + myContract.transactionHash) //部署合约的交易哈希值
// 合约发布成功后,才能调用后续的方法
} else {
console.log("contract deploy address: " + myContract.address) // 合约的部署地址
console.log("Current balance: " + myContract.getBanlance());
var event = myContract.transfer();
// 监听
event.watch(function(error, result){
console.log("Event are as following:-------");
for(let key in result){
console.log(key + " : " + result[key]);
}
console.log("Event ending-------");
});
//使用transaction方式调用,写入到区块链上
myContract.deposit.sendTransaction({
from: deployeAddr,
value: 100,
gas: 1000000
}, function(err, result){
console.log("Deposit status: " + err + " result: " + result);
console.log("After deposit balance: " + myContract.getBanlance());
//终止监听,注意这里要在回调里面,因为是异步执行的。
event.stopWatching();
});
}
}
});