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