11use crate :: { utils:: BlocksGetter , BtcLightClient , BtcLightClientExt } ;
22use btc_types:: {
33 header:: { ExtendedHeader , Header } ,
4- network:: { Network , ZcashConfig } ,
4+ network:: { Network , ZcashConfig , MAX_FUTURE_BLOCK_TIME_LOCAL , MAX_FUTURE_BLOCK_TIME_MTP } ,
55 u256:: U256 ,
66 utils:: target_from_bits,
77} ;
@@ -17,16 +17,43 @@ impl BtcLightClient {
1717 ( "Zcash" . to_owned ( ) , self . network )
1818 }
1919
20+ // Reference implementation: https://github.com/zcash/zcash/blob/v6.2.0/src/main.cpp#L5019
2021 pub ( crate ) fn check_pow ( & self , block_header : & Header , prev_block_header : & ExtendedHeader ) {
21- let expected_bits =
22+ let next_work_result =
2223 zcash_get_next_work_required ( & self . get_config ( ) , block_header, prev_block_header, self ) ;
2324
2425 require ! (
25- expected_bits == block_header. bits,
26- format!(
27- "Error: Incorrect target. Expected bits: {:?}, Actual bits: {:?}" ,
28- expected_bits, block_header. bits
29- )
26+ next_work_result. expected_bits == block_header. bits,
27+ "bad-diffbits: incorrect proof of work"
28+ ) ;
29+
30+ // Check timestamp against prev
31+ require ! (
32+ block_header. time > next_work_result. prev_block_median_time_past,
33+ "time-too-old: block time is before the median time of the previous block"
34+ ) ;
35+
36+ // Check future timestamp soft fork rule introduced in v2.1.1-1.
37+ // This retrospectively activates at block height 2 for mainnet and regtest,
38+ // and 6 blocks after Blossom activation for testnet.
39+ //
40+ // MAX_FUTURE_BLOCK_TIME_MTP is typically 129600 seconds (36 hours) in Zcash
41+ require ! (
42+ block_header. time
43+ <= next_work_result. prev_block_median_time_past + MAX_FUTURE_BLOCK_TIME_MTP ,
44+ "time-too-far-ahead-of-mtp: block timestamp is too far ahead of median-time-past"
45+ ) ;
46+
47+ // Check timestamp
48+ let current_timestamp = u32:: try_from ( env:: block_timestamp_ms ( ) / 1000 ) . unwrap ( ) ; // Convert to seconds
49+ require ! (
50+ block_header. time <= current_timestamp + MAX_FUTURE_BLOCK_TIME_LOCAL ,
51+ "time-too-new: block timestamp is too far ahead of local time"
52+ ) ;
53+
54+ require ! (
55+ block_header. version >= 4 ,
56+ "bad-version: block version must be at least 4"
3057 ) ;
3158
3259 // Check Equihash solution
@@ -41,40 +68,29 @@ impl BtcLightClient {
4168 }
4269}
4370
71+ struct NextWorkResult {
72+ expected_bits : u32 ,
73+ prev_block_median_time_past : u32 ,
74+ }
75+
4476// Reference implementation: https://github.com/zcash/zcash/blob/v6.2.0/src/pow.cpp#L20
4577fn zcash_get_next_work_required (
4678 config : & ZcashConfig ,
4779 block_header : & Header ,
4880 prev_block_header : & ExtendedHeader ,
4981 prev_block_getter : & impl BlocksGetter ,
50- ) -> u32 {
51- use btc_types:: network:: ZCASH_MEDIAN_TIME_SPAN ;
52-
53- if let Some ( pow_allow_min_difficulty_blocks_after_height) =
54- config. pow_allow_min_difficulty_blocks_after_height
55- {
56- // Comparing with >= because this function returns the work required for the block after prev_block_header
57- if prev_block_header. block_height >= pow_allow_min_difficulty_blocks_after_height {
58- // Special difficulty rule for testnet:
59- // If the new block's timestamp is more than 6 * block interval minutes
60- // then allow mining of a min-difficulty block.
61- if i64:: from ( block_header. time )
62- > i64:: from ( prev_block_header. block_header . time ) + config. pow_target_spacing ( ) * 6
63- {
64- return config. proof_of_work_limit_bits ;
65- }
66- }
67- }
82+ ) -> NextWorkResult {
83+ use btc_types:: network:: MEDIAN_TIME_SPAN ;
6884
6985 // Find the first block in the averaging interval
7086 // and the median time past for the first and last blocks in the interval
7187 let mut current_header = prev_block_header. clone ( ) ;
7288 let mut total_target = U256 :: ZERO ;
73- let mut median_time = [ 0u32 ; ZCASH_MEDIAN_TIME_SPAN ] ;
89+ let mut median_time = [ 0u32 ; MEDIAN_TIME_SPAN ] ;
7490
7591 let prev_block_median_time_past = {
7692 for i in 0 ..usize:: try_from ( config. pow_averaging_window ) . unwrap ( ) {
77- if i < ZCASH_MEDIAN_TIME_SPAN {
93+ if i < MEDIAN_TIME_SPAN {
7894 median_time[ i] = current_header. block_header . time ;
7995 }
8096
@@ -91,14 +107,33 @@ fn zcash_get_next_work_required(
91107 } ;
92108
93109 let first_block_in_interval_median_time_past = {
94- for i in 0 ..ZCASH_MEDIAN_TIME_SPAN {
110+ for i in 0 ..MEDIAN_TIME_SPAN {
95111 median_time[ i] = current_header. block_header . time ;
96112 current_header = prev_block_getter. get_prev_header ( & current_header. block_header ) ;
97113 }
98114 median_time. sort_unstable ( ) ;
99115 median_time[ median_time. len ( ) / 2 ]
100116 } ;
101117
118+ if let Some ( pow_allow_min_difficulty_blocks_after_height) =
119+ config. pow_allow_min_difficulty_blocks_after_height
120+ {
121+ // Comparing with >= because this function returns the work required for the block after prev_block_header
122+ if prev_block_header. block_height >= pow_allow_min_difficulty_blocks_after_height {
123+ // Special difficulty rule for testnet:
124+ // If the new block's timestamp is more than 6 * block interval minutes
125+ // then allow mining of a min-difficulty block.
126+ if i64:: from ( block_header. time )
127+ > i64:: from ( prev_block_header. block_header . time ) + config. pow_target_spacing ( ) * 6
128+ {
129+ return NextWorkResult {
130+ expected_bits : config. proof_of_work_limit_bits ,
131+ prev_block_median_time_past,
132+ } ;
133+ }
134+ }
135+ }
136+
102137 // The protocol specification leaves MeanTarget(height) as a rational, and takes the floor
103138 // only after dividing by AveragingWindowTimespan in the computation of Threshold(height):
104139 // <https://zips.z.cash/protocol/protocol.pdf#diffadjustment>
@@ -108,12 +143,17 @@ fn zcash_get_next_work_required(
108143 let average_target = total_target
109144 / U256 :: from ( <i64 as TryInto < u64 > >:: try_into ( config. pow_averaging_window ) . unwrap ( ) ) ;
110145
111- zcash_calculate_next_work_required (
146+ let expected_bits = zcash_calculate_next_work_required (
112147 config,
113148 average_target,
114149 prev_block_median_time_past,
115150 first_block_in_interval_median_time_past,
116- )
151+ ) ;
152+
153+ NextWorkResult {
154+ expected_bits,
155+ prev_block_median_time_past,
156+ }
117157}
118158
119159fn zcash_calculate_next_work_required (
0 commit comments