diff --git a/src/util.rs b/src/util.rs index 5d908a2..f48dd45 100644 --- a/src/util.rs +++ b/src/util.rs @@ -270,3 +270,132 @@ where .spawn(f) .unwrap() } + +#[cfg(test)] +mod tests { + #[test] + fn test_headers() { + use bitcoin::blockdata::block::BlockHeader; + use bitcoin::util::hash::BitcoinHash; + use bitcoin_hashes::sha256d::Hash as Sha256dHash; + use bitcoin_hashes::Hash; + + use super::HeaderList; + + // Test an empty header list + let null_hash = Sha256dHash::default(); + let mut header_list = HeaderList::empty(); + assert_eq!(header_list.tip(), null_hash); + let ordered = header_list.order(vec![]); + assert_eq!(ordered.len(), 0); + + let merkle_root = Sha256dHash::hash(&[255]); + let mut headers = vec![BlockHeader { + version: 1, + prev_blockhash: Sha256dHash::default(), + merkle_root, + time: 0, + bits: 0, + nonce: 0, + }]; + for _height in 1..10 { + let prev_blockhash = headers.last().unwrap().bitcoin_hash(); + let header = BlockHeader { + version: 1, + prev_blockhash, + merkle_root, + time: 0, + bits: 0, + nonce: 0, + }; + headers.push(header); + } + + // Test adding some new headers + let ordered = header_list.order(headers[..3].to_vec()); + assert_eq!(ordered.len(), 3); + header_list.apply(ordered.clone(), ordered[2].hash); + assert_eq!(header_list.len(), 3); + assert_eq!(header_list.tip(), ordered[2].hash); + for h in 0..3 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + + // Test adding some more headers + let ordered = header_list.order(headers[3..6].to_vec()); + assert_eq!(ordered.len(), 3); + header_list.apply(ordered.clone(), ordered[2].hash); + assert_eq!(header_list.len(), 6); + assert_eq!(header_list.tip(), ordered[2].hash); + for h in 0..6 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + + // Test adding some more headers (with an overlap) + let ordered = header_list.order(headers[5..].to_vec()); + assert_eq!(ordered.len(), 5); + header_list.apply(ordered.clone(), ordered[4].hash); + assert_eq!(header_list.len(), 10); + assert_eq!(header_list.tip(), ordered[4].hash); + for h in 0..10 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + + // Reorg the chain and test apply() on it + for h in 8..10 { + headers[h].nonce += 1; + headers[h].prev_blockhash = headers[h - 1].bitcoin_hash() + } + // Test reorging the chain + let ordered = header_list.order(headers[8..10].to_vec()); + assert_eq!(ordered.len(), 2); + header_list.apply(ordered.clone(), ordered[1].hash); + assert_eq!(header_list.len(), 10); + assert_eq!(header_list.tip(), ordered[1].hash); + for h in 0..10 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + + // Test "trimming" the chain + header_list.apply(vec![], headers[7].bitcoin_hash()); + assert_eq!(header_list.len(), 8); + assert_eq!(header_list.tip(), headers[7].bitcoin_hash()); + for h in 0..8 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + + // Test "un-trimming" the chain + let ordered = header_list.order(headers[8..].to_vec()); + assert_eq!(ordered.len(), 2); + header_list.apply(ordered.clone(), ordered[1].hash); + assert_eq!(header_list.len(), 10); + assert_eq!(header_list.tip(), ordered[1].hash); + for h in 0..10 { + let entry = header_list.header_by_height(h).unwrap(); + assert_eq!(entry.header, headers[h]); + assert_eq!(entry.hash, headers[h].bitcoin_hash()); + assert_eq!(entry.height, h); + assert_eq!(header_list.header_by_blockhash(&entry.hash), Some(entry)); + } + } +}