Ethereum
Ethereum copied to clipboard
Clarification about event topic reconstruction in EventStorage.sol
I'm trying to better understand event logs in ethereum and their usage with bloom filters. Everything I read about event logs indicates that the very first topic should be the hash of the event signature keccak256(keccak256(DataStored(bytes,bytes)))
However in EventStorage.sol the first topic is the hash of the address of the contract that the event originated from, in this case EventStorage (https://github.com/figs999/Ethereum/blob/b035263cd09ea83fdd1c30e05c30347069d5aa78/EventStorage.sol#L80)
Exerpt from solidity docs:
Up to three parameters can receive the attribute indexed which will cause the respective arguments to be searched for: It is possible to filter for specific values of indexed arguments in the user interface.
If arrays (including string and bytes) are used as indexed arguments, the Keccak-256 hash of it is stored as topic instead.
The hash of the signature of the event is one of the topics except if you declared the event with anonymous specifier. This means that it is not possible to filter for specific anonymous events by name.
As I mentioned in the beginning of the issue, everything I read indicates that the first topic should be the hash of the event signature: https://ethereum.stackexchange.com/questions/12950/what-are-event-topics https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e https://ethereum.stackexchange.com/questions/25006/does-the-address-of-an-event-get-automatically-indexed?rq=1
But as is evident by EventStorage.sol this is not the case (I believe event log records in etherscan indicate this as well)
Yes, some of the wording around the events is a bit confusing I've found.
The main disconnect seems to be the way that the transaction receipt includes a list of "topics" which includes the hash of the logging contract address as topics[0]. The best way to think of it, in my opinion, is that the "topics" array in the transaction header should really be called "bloom indexes". The first entry being the index of the logging address and any additional entries are the topics.
If you look at the log0 operation, you can see that it's possible to emit data to logs without any topics at all, not even the hash of the signature. This is what an anonymous event is in solidity.
Log0 would be practically useless if it did not include the sender, as their would be no way to search the blockchain to find it, unless you crawled every single transaction.
Ah okay that makes sense, thanks!
I have another question with regards to extending the event validation to included additional even topics like so:
bytes32 constant eventTopics = keccak256(keccak256("DataStored(bytes,string,bytes,string"));
event DataStored(bytes indexed _indexedData, string indexed _indexedFile, bytes _data, string _file);
function ValidateEventStorage(bytes rlpBlockHeader, bytes _indexedData, string _indexedFile) view public returns (bool valid){
..... omitted ....
bytes32 _topic1 = keccak256(address(this));
bytes32 _topic2 = eventTopic;
bytes32 _topic3 = keccak256(keccak256(_indexedData));
bytes32 _topic4 = keccak256(keccak256(_indexedFile));
.... omitted .....
Where I get stuck, is in the assembly code that follows that particular topic declaration statements, how to scale up the value appropriately to adding in additional topics.
My initial suspicions are:
for(uint b = 0; b < 8; b++) becomes for(uint b = 0; b < 10; b++)
for(uint i = 0; i < 6; i += 2) becomes for(uint i = 0; i < 8; i += 2)
The following additional if statement is added to the first block of assembly clode:
if eq(mod(byte(i, _topic4),8), b) {
bloom := or(bloom, exp(2,byte(add(1,i), _topic4)))
}
The rest however I get stuck on and can't get working, any ideas what I'm doing wrong?
You don't need to change the for(uint b = 0; b < 8; b++), as this is just iterating over the 8 words of the log bloom, and won't increase in size.
Other than that, everything else looks correct.
Ah I see. I tried my implementation and couldn't get it to work. I also tried the original EventStorage.sol implementation, and couldnk't get it to verify the event data??
I encode the block header using your provided javascript code, and run it like so:
encodeRLPHeader(eth.getBlock(42922))
I'm testing this on a PoA network, is there any chance that doing it on PoA could be causing verification to fail?
Update, it is indeed the fact that the tests I were performing occured on a PoA Clique network, which inserts extra data into the block header. I tried out the example contract on the ropsten PoW and it worked.
I tried using my modfied version which I haven't been able to get to work. Here is the snippet of code I'm using
bytes32 constant public eventTopic = keccak256(keccak256("DataStored(bytes,string,bytes,string)"));
event DataStored(bytes indexed _indexedData, string indexed _indexedFile, bytes _data, string _file);
function ValidateEventStorage(bytes rlpBlockHeader, bytes _indexedData, string _indexedFile) view public returns (bool valid){
bytes memory logsBloom = parseBlockHeader(rlpBlockHeader).logsBloom;
bytes32 _topic1 = keccak256(address(this));
bytes32 _topic2 = eventTopic;
bytes32 _topic3 = keccak256(keccak256(_indexedData));
bytes32 _topic4 = keccak256(keccak256(_indexedFile));
bool foundInLogs = true;
for(uint b = 0; b < 8; b++) {
bytes32 bloom = 0;
for(uint i = 0; i < 8; i += 2) {
assembly {
if eq(mod(byte(i, _topic1),8), b) {
bloom := or(bloom, exp(2,byte(add(1,i), _topic1)))
}
if eq(mod(byte(i, _topic2),8), b) {
bloom := or(bloom, exp(2,byte(add(1,i), _topic2)))
}
if eq(mod(byte(i, _topic3),8), b) {
bloom := or(bloom, exp(2,byte(add(1,i), _topic3)))
}
if eq(mod(byte(i, _topic4),8), b) {
bloom := or(bloom, exp(2,byte(add(1,i), _topic4)))
}
}
}
assembly {
if gt(bloom, 0) {
let bloomAnd := and(mload(add(logsBloom,mul(0x20,sub(8,b)))),bloom)
let equal := eq(bloomAnd,bloom)
if eq(equal,0) {
b := 8
foundInLogs := 0
}
}
}
}
valid = foundInLogs;
}