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
159
160
161
162
crate::ix!();

pub trait ConsiderEviction {

    fn consider_eviction(self: Arc<Self>, 
        pto:             Amo<Box<dyn NodeInterface>>,
        time_in_seconds: OffsetDateTime);
}

impl ConsiderEviction for PeerManager {

    /**
      | Consider evicting an outbound peer
      | based on the amount of time they've been
      | behind our tip
      |
      */
    #[EXCLUSIVE_LOCKS_REQUIRED(CS_MAIN)]
    fn consider_eviction(self: Arc<Self>, 
        pto:             Amo<Box<dyn NodeInterface>>,
        time_in_seconds: OffsetDateTime)  {

        assert_lock_held!(CS_MAIN);

        let mut create_state = create_state(pto.get().get_id());

        let mut state = create_state.get_mut();

        let msg_maker: NetMsgMaker = NetMsgMaker::new(pto.get().get_common_version());

        if !state.chain_sync.protect 
        && pto.get().is_outbound_or_block_relay_conn() 
        && state.sync_started.load(atomic::Ordering::Relaxed) {

            //  This is an outbound peer subject to disconnection if they don't
            //  announce a block with as much work as the current tip within
            //  CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if
            //  their chain has more work than ours, we should sync to it,
            //  unless it's invalid, in which case we should find that out and
            //  disconnect from them elsewhere).
            if state.pindex_best_known_block.is_some() 
            && state.pindex_best_known_block.as_ref().unwrap().n_chain_work 
            >= self.chainman.get().active_chain().tip().as_ref().unwrap().n_chain_work {

                if state.chain_sync.timeout.is_some() {
                    state.chain_sync.timeout         = None;
                    state.chain_sync.work_header     = None;
                    state.chain_sync.sent_getheaders = false;
                }

            } else {

                if state.chain_sync.timeout.is_none()
                || (
                    state.chain_sync.work_header.is_some() 
                    && state.pindex_best_known_block.is_some() 
                    && state.pindex_best_known_block.as_ref().unwrap().n_chain_work 
                    >= state.chain_sync.work_header.as_ref().unwrap().n_chain_work
                ) {
                    // Our best block known by
                    // this peer is behind our
                    // tip, and we're either
                    // noticing that for the first
                    // time, OR this peer was able
                    // to catch up to some earlier
                    // point where we checked
                    // against our tip.
                    //
                    // Either way, set a new
                    // timeout based on current
                    // tip.
                    state.chain_sync.timeout         = Some(time_in_seconds + CHAIN_SYNC_TIMEOUT);
                    state.chain_sync.work_header     = self.chainman.get().active_chain().tip();
                    state.chain_sync.sent_getheaders = false;

                } else {

                    if state.chain_sync.timeout.is_some() 
                    && Some(time_in_seconds) > state.chain_sync.timeout {

                        // No evidence yet that
                        // our peer has synced to
                        // a chain with work equal
                        // to that of our tip,
                        // when we first detected
                        // it was behind. Send
                        // a single getheaders
                        // message to give the
                        // peer a chance to update
                        // us.
                        if state.chain_sync.sent_getheaders {

                            //  They've run out of time to catch up!
                            log_printf!(
                                "Disconnecting outbound peer %d for old chain, best known block = %s\n",
                                pto.get_id(),
                                match state.pindex_best_known_block.is_some() {
                                    true   => (*state.pindex_best_known_block()).get_block_hash().to_string(),
                                    false  => "<none>"
                                }
                            );

                            pto.get_mut().mark_for_disconnect();

                        } else {

                            assert!(state.chain_sync.work_header.is_some());

                            log_print!(
                                LogFlags::NET, 
                                "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", 
                                pto.get_id(), 
                                match state.pindex_best_known_block.is_some() {
                                    true   => (*state.pindex_best_known_block()).get_block_hash().to_string(),
                                    false  => "<none>"
                                }, 
                                (*state.chain_sync().work_header()).get_block_hash().to_string()
                            );

                            let msg = {

                                let chainman     = self.chainman.get();
                                let active_chain = chainman.active_chain();
                                let work_header  = state.chain_sync.work_header.as_ref().unwrap();
                                let locator      = active_chain.get_locator(work_header.pprev.clone());

                                msg_maker.make(
                                    NetMsgType::GETHEADERS, 

                                    &[
                                        &locator, 
                                        &u256::default()
                                    ]
                                )
                            };

                            self.connman
                                .get_mut()
                                .push_message(
                                    &mut pto.get_mut(), 

                                    msg
                                );

                            state.chain_sync.sent_getheaders = true;

                            //  2 minutes
                            pub const HEADERS_RESPONSE_TIME: Duration = Duration::minutes(2);

                            // Bump the timeout to allow a response, which could clear the timeout
                            // (if the response shows the peer has synced), reset the timeout (if
                            // the peer syncs to the required work but not to our tip), or result
                            // in disconnect (if we advance to the timeout and pindexBestKnownBlock
                            // has not sufficiently progressed)
                            state.chain_sync.timeout = Some(time_in_seconds + HEADERS_RESPONSE_TIME);
                        }
                    }
                }
            }
        }
    }
}