-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathEthbase.sol
105 lines (87 loc) · 3.49 KB
/
Ethbase.sol
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
pragma solidity ^0.4.24;
import "./Receipt.sol";
import "./Block.sol";
import "./lib/PatriciaTrie.sol";
contract Ethbase {
using Block for Block.BlockHeader;
event Subscribed(bytes32 eventId, address emitter, bytes32 eventTopic, address account, bytes4 method);
struct Subscriber {
bytes4 method;
uint timestamp;
}
// Multiple contracts can subscribe to the same event
// Key is keccak256(emitterAddr, eventName)
mapping(bytes32 => mapping(address => Subscriber)) subscribers;
mapping(bytes32 => address[]) subscriberList;
// Keep track of submitted event logs to prevent re-submitting.
// Key: keccak256(blockHash, txId, logId)
mapping(bytes32 => bool) logs;
modifier isSubscribed(bytes32 _eventId, address _account) {
require(subscribers[_eventId][_account].timestamp != 0, "not subscribed");
_;
}
/**
* @dev Subscribes to an event.
* @param _emitter Address of contract emitting event.
* @param _eventTopic E.g. keccak256(ExampleEvent(type1,type2)).
* @param _account Address of subscribing contract, which should be invoked.
* @param _method bytes4(keccak256(signature)) where signature ~= method(param1,param2).
*/
function subscribe(address _emitter, bytes32 _eventTopic, address _account, bytes4 _method) public {
bytes32 eventId = keccak256(abi.encodePacked(_emitter, _eventTopic));
Subscriber storage s = subscribers[eventId][_account];
s.method = _method;
s.timestamp = now;
subscriberList[eventId].push(_account);
emit Subscribed(eventId, _emitter, _eventTopic, _account, _method);
}
/**
* @dev Unsubscribers from an event.
* @param _eventId Name of the event.
* @param _subscriber Address of contract wanting to unsubscribe.
*/
function unsubscribe(bytes32 _eventId, address _subscriber) public isSubscribed(_eventId, _subscriber) {
delete subscribers[_eventId][_subscriber];
uint i = accountIndex(_eventId, _subscriber);
delete subscriberList[_eventId][i];
}
/**
* @dev Submits proof of log, and invokes subscriber.
* @param _receipt RLP-encoded receipt which contains log.
* @param _parentNodes RLP-encoded list of proof nodes from root to leaf.
* @param _key Index of TX in block.
* @param _logIndex Index of log in receipt.
* @param _blockHeader RLP-encoded block header.
* @param _subscriber Address of subscriber.
* @param _eventId EventId emitted after subscribing.
*/
function submitLog(
bytes _receipt,
bytes _parentNodes,
bytes _key,
uint _logIndex,
bytes _blockHeader,
address _subscriber,
bytes32 _eventId
) public isSubscribed(_eventId, _subscriber) {
Block.BlockHeader memory header = Block.decodeBlockHeader(_blockHeader);
require(header.validateHeader(), "invalid block header");
bytes32 logId = keccak256(abi.encodePacked(header.hash, _key, _logIndex));
require(logs[logId] == false, "log already submitted");
// Verify proof
require(PatriciaTrie.verifyProof(_receipt, _parentNodes, _key, header.receiptHash), "proof verification failed");
// Mark log as submitted
logs[logId] = true;
// Call subscriber
Subscriber storage s = subscribers[_eventId][_subscriber];
bytes memory data = Receipt.extractLog(_receipt, _logIndex);
require(_subscriber.call(s.method, data), "call to subscriber failed");
}
function accountIndex(bytes32 _eventId, address _account) internal view returns(uint) {
uint i = 0;
while (subscriberList[_eventId][i] != _account) {
i++;
}
return i;
}
}