1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
crate::ix!();
impl AddrManImpl {
/**
| Serialized format.
|
| - format version byte (@see `Format`)
|
| - lowest compatible format version
| byte. This is used to help old software
| decide whether to parse the file. For
| example:
| - Bitcoin Core version N knows how to
| parse up to format=3. If a new format=4
| is introduced in version N+1 that is
| compatible with format=3 and it is
| known that version N will be able to
| parse it, then version N+1 will write
| (format=4, lowest_compatible=3) in the
| first two bytes of the file, and so
| version N will still try to parse it.
| - Bitcoin Core version N+2 introduces
| a new incompatible format=5. It will
| write (format=5, lowest_compatible=5)
| and so any versions that do not know
| how to parse format=5 will not try to
| read the file.
|
| - nKey
| - n_new
| - nTried
| - number of "new" buckets XOR 2**30
| - all new addresses (total count: n_new)
| - all tried addresses (total count: nTried)
| - for each new bucket:
| - number of elements
| - for each element: index in the
| serialized "all new addresses"
|
| - asmap checksum
|
| 2**30 is xorred with the number of buckets
| to make addrman deserializer v0 detect it
| as incompatible. This is necessary because
| it did not check the version number on
| deserialization.
|
| vvNew, vvTried, mapInfo, mapAddr and
| vRandom are never encoded explicitly; they
| are instead reconstructed from the other
| information.
|
| This format is more complex, but
| significantly smaller (at most 1.5 MiB),
| and supports changes to the ADDRMAN_
| parameters without breaking the on-disk
| structure.
|
| We don't use SERIALIZE_METHODS since the
| serialization and deserialization code has
| very little in common.
*/
pub fn serialize<Stream: GetVersion + GetType>(&self, stream: &mut Stream) {
let inner = self.cs.lock();
// Always serialize in the latest version
// (FILE_FORMAT).
let mut s: OverrideStream::<Stream> = OverrideStream::<Stream>::new(
stream,
stream.get_type(),
stream.get_version() | ADDRV2_FORMAT
);
s.stream(ADDR_MAN_FILE_FORMAT as u8);
// Increment `lowest_compatible` iff
// a newly introduced format is
// incompatible with the previous one.
const lowest_compatible: u8 = AddrManFormat::V3_BIP155 as u8;
s.stream((ADDR_MAN_INCOMPATIBILITY_BASE + lowest_compatible) as u8);
s.stream(&self.n_key);
s.stream(&inner.n_new);
s.stream(&inner.n_tried);
let n_ubuckets: i32 = (ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)).try_into().unwrap();
s.stream(&n_ubuckets);
let mut map_unk_ids: HashMap<i32,RefCell<i32>> = HashMap::<i32,RefCell<i32>>::default();
let mut n_ids: i32 = 0;
for entry in &inner.map_info {
*map_unk_ids[entry.0].borrow_mut() = n_ids;
let info = &entry.1;
if info.n_ref_count != 0 {
assert!{n_ids != inner.n_new}; // this means n_new was wrong, oh ow
s.stream(&info);
n_ids += 1;
}
}
n_ids = 0;
for entry in &inner.map_info {
let info = &entry.1;
if info.in_tried {
// this means nTried was wrong, oh ow
assert!{n_ids != inner.n_tried};
s.stream(&info);
n_ids += 1;
}
}
for bucket in 0..ADDRMAN_NEW_BUCKET_COUNT {
let mut n_size = 0;
for i in 0..ADDRMAN_BUCKET_SIZE {
if inner.vv_new[bucket][i] != -1 {
n_size += 1;
}
}
s.stream(&n_size);
for i in 0..ADDRMAN_BUCKET_SIZE {
if inner.vv_new[bucket][i] != -1 {
let n_index: i32 = *map_unk_ids[&inner.vv_new[bucket][i]].borrow();
s.stream(&n_index);
}
}
}
/*
| Store asmap checksum after bucket
| entries so that it can be ignored
| by older clients for backward
| compatibility.
*/
let mut asmap_checksum: u256 = u256::default();
if self.asmap.len() != 0 {
asmap_checksum = serialize_hash(&self.asmap, None, None);
}
s.stream(&asmap_checksum);
}
}