@@ -2,94 +2,165 @@ const EventEmitter = require('events');
22const axios = require ( 'axios' ) ;
33const { Web3} = require ( 'web3' ) ;
44const Deque = require ( 'collections/deque' ) ;
5- const { num_from_id} = require ( './utils' ) ;
5+ const { num_from_id, convert_to_epoch} = require ( './utils' ) ;
6+ const { clearTimeout } = require ( 'timers' ) ;
67class BlockMonitor extends EventEmitter {
78
8- constructor ( { web3_rpc_endpoint, nodeos_rpc_endpoint, poll_interval, logger} ) {
9+ constructor ( { web3_rpc_endpoint, nodeos_rpc_endpoint, poll_interval, genesis , logger} ) {
910 super ( ) ;
1011 this . web3_rpc_endpoint = web3_rpc_endpoint ;
1112 this . nodeos_rpc_endpoint = nodeos_rpc_endpoint ;
1213 this . poll_interval = poll_interval ;
1314 this . web3 = new Web3 ( web3_rpc_endpoint ) ;
1415 this . logger = logger ;
16+ this . genesis = genesis ;
17+
18+ if ( this . genesis == undefined || this . genesis . timestamp == undefined ) throw ( "invalid genesis timestamp" ) ;
19+ this . genesis_timestamp = parseInt ( this . genesis . timestamp , 16 ) ;
20+ this . logger . debug ( `Using genesis timestamp: ${ this . genesis_timestamp } ` ) ;
1521
1622 this . reversible_blocks = new Deque ( ) ;
1723 this . run = false ;
1824 this . timer_id = null ;
1925 }
2026
21- async get_eos_lib ( ) {
22- const response = await axios . post ( this . nodeos_rpc_endpoint + '/v1/chain/get_info' , { } ) ;
23- return response . data . last_irreversible_block_num ;
27+ async get_evm_lib ( ) {
28+ let response = await axios . post ( this . nodeos_rpc_endpoint + '/v1/chain/get_info' , { } ) ;
29+ response = await axios . post ( this . nodeos_rpc_endpoint + '/v1/chain/get_block' , { block_num_or_id :response . data . last_irreversible_block_num } ) ;
30+ let lib = this . timestamp_to_evm_block_num ( convert_to_epoch ( response . data . timestamp ) ) ;
31+ this . logger . debug ( `BlockMonitor::get_evm_lib ${ lib } ` ) ;
32+ return lib ;
2433 }
2534
35+ timestamp_to_evm_block_num ( timestamp ) {
36+ const block_interval = 1 ;
37+ if ( timestamp < this . genesis_timestamp ) {
38+ return 0 ;
39+ }
40+ return 1 + Math . floor ( ( timestamp - this . genesis_timestamp ) / block_interval ) ;
41+ }
42+
43+
2644 remove_front_block ( ) {
2745 const block = this . reversible_blocks . shift ( ) ;
46+ this . logger . debug ( `BlockMonitor::remove_front_block ${ block . number } ` ) ;
2847 this . emit ( 'block_removed' , { block} ) ;
2948 }
3049
3150 fork_last_block ( ) {
3251 const block = this . reversible_blocks . pop ( ) ;
33- this . logger . debug ( `FORK_LAST_BLOCK ${ block } ` ) ;
52+ this . logger . debug ( `BlockMonitor::fork_last_block ${ block . number } ` ) ;
3453 this . emit ( 'block_forked' , { block} ) ;
3554 return this . reversible_blocks . peekBack ( ) ;
3655 }
3756
3857 append_new_block ( block ) {
3958 this . reversible_blocks . add ( block ) ;
59+ this . logger . debug ( `BlockMonitor::append_new_block ${ block . number } ${ block . hash } ` ) ;
4060 this . emit ( 'block_appended' , { block} ) ;
61+ return block ;
62+ }
63+
64+ async getBlockWithLogs ( number_ ) {
65+ let number = Number ( number_ ) ;
66+
67+ let id1 = "get_block_" + number ;
68+ let id2 = "get_logs_" + number ;
69+ let requests = [
70+ { jsonrpc :"2.0" , method :"eth_getBlockByNumber" , params :[ "0x" + number . toString ( 16 ) , true ] , id : id1 } ,
71+ { jsonrpc :"2.0" , method :"eth_getLogs" , params :[ { fromBlock : "0x" + number . toString ( 16 ) , toBlock : "0x" + number . toString ( 16 ) } ] , id : id2 }
72+ ]
73+ const results = await axios . post ( this . web3_rpc_endpoint , requests ) ;
74+
75+ if ( ! Array . isArray ( results . data ) || results . data . length != 2 ) {
76+ throw new Error ( "invalid RPC response of [getBlock, GetPastLogs] batch request" ) ;
77+ }
78+ const block = results . data [ 0 ] . result ;
79+ const logs = results . data [ 1 ] . result ;
80+
81+ block . logs = logs ;
82+ //console.log("RPC batch result:" + JSON.stringify(block));
83+ return block ;
84+ }
85+
86+ async get_next_block ( block ) {
87+ // need to be conservative, sometimes getLogs return empty result for head block
88+ let head_block = await this . web3 . eth . getBlock ( "latest" , true ) ;
89+ if ( head_block == null ) return null ;
90+
91+ if ( block == null )
92+ return await this . getBlockWithLogs ( Number ( head_block . number ) - 1 ) ;
93+
94+ let next_block_num = Number ( block . number ) + 1 ;
95+
96+ let max_block_num = Number ( head_block . number ) - 1 ;
97+ if ( next_block_num >= max_block_num ) {
98+ return null ;
99+ }
100+
101+ return await this . getBlockWithLogs ( next_block_num ) ;
41102 }
42103
43104 async poll ( ) {
44105 try {
106+
45107 let last = this . reversible_blocks . peekBack ( ) ;
46108 if ( last == undefined ) {
47- last = await this . web3 . eth . getBlock ( "latest" , true ) ;
48- this . append_new_block ( last ) ;
109+ this . logger . debug ( "BlockMonitor::poll last not defined" ) ;
110+ last = await this . get_next_block ( )
111+ if ( last != null ) {
112+ this . logger . debug ( `BlockMonitor::poll Obtained ${ last . number } ` ) ;
113+ this . append_new_block ( last ) ;
114+ } else {
115+ throw new Error ( "BlockMonitor::poll Unable to get block" ) ;
116+ }
49117 }
50118
51- let next_block = await this . web3 . eth . getBlock ( last . number + BigInt ( 1 ) , true ) ;
119+ let next_block = await this . get_next_block ( last ) ;
52120 let found_next_block = false ;
53121 while ( last != null && next_block != null ) {
54122 found_next_block = true ;
55123 if ( next_block . parentHash == last . hash ) {
56- this . append_new_block ( next_block ) ;
57- last = next_block ;
58- next_block = await this . web3 . eth . getBlock ( last . number + BigInt ( 1 ) , true ) ;
124+ last = this . append_new_block ( next_block ) ;
125+ next_block = await this . get_next_block ( last ) ;
59126 } else {
127+ this . logger . debug ( `BlockMonitor::poll next: ${ next_block . number } ${ next_block . parentHash } != last: ${ last . number } ${ last . hash } ` ) ;
60128 last = this . fork_last_block ( ) ;
129+ next_block = await this . get_next_block ( last ) ;
61130 }
62131 }
63132
64133 if ( found_next_block == true ) {
65- const eos_lib = await this . get_eos_lib ( ) ;
66- while ( this . reversible_blocks . length > 0 && num_from_id ( this . reversible_blocks . peek ( ) . mixHash ) <= eos_lib ) {
67- this . logger . debug ( `eoslib: ${ eos_lib } ${ num_from_id ( this . reversible_blocks . peek ( ) . mixHash ) } ${ this . reversible_blocks . peek ( ) . number } ${ this . reversible_blocks . peek ( ) . mixHash } ` ) ;
134+ const evm_lib = await this . get_evm_lib ( ) ;
135+ while ( this . reversible_blocks . length > 0 && this . reversible_blocks . peek ( ) . number < evm_lib ) {
68136 this . remove_front_block ( ) ;
69137 }
70138 }
71139
72140 } catch ( error ) {
73- this . logger . error ( error . message ) ;
141+ this . logger . error ( `BlockMonitor::poll => ERR: ${ error . message } ` ) ;
74142 }
75143
76144 if ( this . run == true ) {
145+ if ( this . timer_id != null ) clearTimeout ( this . timer_id ) ;
77146 this . timer_id = setTimeout ( ( ) => this . poll ( ) , this . poll_interval || 5000 ) ;
78147 } else {
79148 this . reversible_blocks . clear ( ) ;
80- this . logger . info ( "BlockMonitor stopped" ) ;
149+ this . logger . info ( "BlockMonitor::poll => Stopped" ) ;
150+ if ( this . timer_id != null ) clearTimeout ( this . timer_id ) ;
81151 }
82152 }
83153
84154 start ( ) {
85- this . logger . info ( "BlockMonitor start" ) ;
155+ if ( this . run == true ) return ;
156+ this . logger . info ( "BlockMonitor::start => BlockMonitor starting" ) ;
86157 this . run = true ;
87- setTimeout ( ( ) => this . poll ( ) , 0 ) ;
158+ if ( this . timer_id != null ) clearTimeout ( this . timer_id ) ;
159+ this . timer_id = setTimeout ( ( ) => this . poll ( ) , 0 ) ;
88160 }
89161
90162 stop ( ) {
91- clearTimeout ( this . timer_id ) ;
92- this . logger . info ( "BlockMonitor stopping" ) ;
163+ this . logger . info ( "BlockMonitor::stop => BlockMonitor stopping" ) ;
93164 this . run = false ;
94165 }
95166
0 commit comments