|
|
@ -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)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|