@@ -7,6 +7,8 @@ use crate::context::Context;
77use crate :: error:: RpcError ;
88use crate :: handlers:: { ensure_no_params, params_array, required_str} ;
99
10+ const NETWORK_HASHPS_WINDOW : u32 = 120 ;
11+
1012pub ( crate ) fn getblocktemplate ( ctx : & Arc < Context > , params : & Value ) -> Result < Value , RpcError > {
1113 if !params. is_null ( ) {
1214 let request = params_array ( params) ?. first ( ) ;
@@ -72,13 +74,50 @@ pub(crate) fn getmininginfo(ctx: &Arc<Context>, params: &Value) -> Result<Value,
7274 "currentblockweight" : 0_u64 ,
7375 "currentblocktx" : 0_u64 ,
7476 "difficulty" : difficulty,
75- "networkhashps" : 0.0_f64 ,
77+ "networkhashps" : estimate_network_hashps ( ctx ) ,
7678 "pooledtx" : pooledtx,
7779 "chain" : chain,
7880 "warnings" : ""
7981 } ) )
8082}
8183
84+ fn estimate_network_hashps ( ctx : & Context ) -> f64 {
85+ let tree = ctx. block_tree . read ( ) ;
86+ let Some ( tip_snapshot) = ctx. applied_tip . load_full ( ) else {
87+ return 0.0 ;
88+ } ;
89+ let tip_id = tip_snapshot. tip_id ;
90+ let Ok ( tip_node) = tree. node ( tip_id) else {
91+ return 0.0 ;
92+ } ;
93+ let target_height = tip_node. height . saturating_sub ( NETWORK_HASHPS_WINDOW ) ;
94+ let Some ( earliest_id) = tree. node_at_height_from ( tip_id, target_height) else {
95+ return 0.0 ;
96+ } ;
97+ let Ok ( earliest_node) = tree. node ( earliest_id) else {
98+ return 0.0 ;
99+ } ;
100+ if earliest_node. height == tip_node. height {
101+ return 0.0 ;
102+ }
103+
104+ let work_delta = tip_node. chainwork . saturating_sub ( earliest_node. chainwork ) ;
105+ let time_delta_secs =
106+ i64:: from ( tip_node. header . time ) . saturating_sub ( i64:: from ( earliest_node. header . time ) ) ;
107+ if time_delta_secs <= 0 {
108+ return 0.0 ;
109+ }
110+
111+ chainwork_to_f64 ( work_delta) / f64:: from ( u32:: try_from ( time_delta_secs) . unwrap_or ( u32:: MAX ) )
112+ }
113+
114+ fn chainwork_to_f64 ( work : bitcoin_rs_chain:: ChainWork ) -> f64 {
115+ let bytes: [ u8 ; 32 ] = work. to_be_bytes ( ) ;
116+ bytes
117+ . iter ( )
118+ . fold ( 0.0_f64 , |acc, & byte| acc. mul_add ( 256.0 , f64:: from ( byte) ) )
119+ }
120+
82121pub ( crate ) fn submitblock ( _ctx : & Arc < Context > , params : & Value ) -> Result < Value , RpcError > {
83122 use bitcoin:: consensus:: encode:: deserialize;
84123 use bitcoin:: hex:: FromHex as _;
@@ -175,4 +214,15 @@ mod getmininginfo_tests {
175214 } ;
176215 assert_eq ! ( pooledtx, 0 ) ;
177216 }
217+
218+ #[ test]
219+ fn getmininginfo_networkhashps_zero_when_no_applied_tip ( ) {
220+ let ctx = Arc :: new ( Context :: new ( ) ) ;
221+ let result = getmininginfo ( & ctx, & json ! ( [ ] ) )
222+ . unwrap_or_else ( |err| panic ! ( "getmininginfo failed: {err}" ) ) ;
223+ let Some ( rate) = result. get ( "networkhashps" ) . and_then ( JsonValueTrait :: as_f64) else {
224+ panic ! ( "networkhashps missing: {result:?}" ) ;
225+ } ;
226+ assert ! ( rate. abs( ) < f64 :: EPSILON , "expected zero, got {rate}" ) ;
227+ }
178228}
0 commit comments