- fn handle_reply_channel_range(&self, their_node_id: &PublicKey, msg: &ReplyChannelRange) -> Result<(), LightningError> {
- log_debug!(self.logger, "Handling reply_channel_range peer={}, first_blocknum={}, number_of_blocks={}, full_information={}, scids={}", log_pubkey!(their_node_id), msg.first_blocknum, msg.number_of_blocks, msg.full_information, msg.short_channel_ids.len(),);
-
- // First we obtain a lock on the task hashmap. In order to avoid borrowing issues
- // we will access the task as needed.
- let mut query_range_tasks = self.chan_range_query_tasks.lock().unwrap();
-
- // If there is no currently executing task then we have received
- // an invalid message and will return an error
- if query_range_tasks.get(their_node_id).is_none() {
- return Err(LightningError {
- err: String::from("Received unknown reply_channel_range message"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Now that we know we have a task, we can extract a few values for use
- // in validations without having to access the task repeatedly
- let (task_chain_hash, task_first_blocknum, task_number_of_blocks, task_received_first_block, task_received_last_block, task_number_of_replies) = {
- let task = query_range_tasks.get(their_node_id).unwrap();
- (task.chain_hash, task.first_blocknum, task.number_of_blocks, task.received_first_block, task.received_last_block, task.number_of_replies)
- };
-
- // Validate the chain_hash matches the chain_hash we used in the query.
- // If it does not, then the message is malformed and we return an error
- if msg.chain_hash != task_chain_hash {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Received reply_channel_range with invalid chain_hash"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Validate that the remote node maintains up-to-date channel
- // information for chain_hash. Some nodes use the full_information
- // flag to indicate multi-part messages so we must check whether
- // we received information as well.
- if !msg.full_information && msg.short_channel_ids.len() == 0 {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Received reply_channel_range with no information available"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Calculate the last block for the message and the task
- let msg_last_block = last_blocknum(msg.first_blocknum, msg.number_of_blocks);
- let task_last_block = last_blocknum(task_first_blocknum, task_number_of_blocks);
-
- // On the first message...
- if task_received_first_block.is_none() {
- // The replies can be a superset of the queried block range, but the
- // replies must include our requested query range. We check if the
- // start of the replies is greater than the start of our query. If
- // so, the start of our query is excluded and the message is malformed.
- if msg.first_blocknum > task_first_blocknum {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Failing reply_channel_range with invalid first_blocknum"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Next, we ensure the reply has at least some information matching
- // our query. If the received last_blocknum is less than our query's
- // first_blocknum then the reply does not encompass the query range
- // and the message is malformed.
- if msg_last_block < task_first_blocknum {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Failing reply_channel_range with non-overlapping first reply"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Capture the first block and last block so that subsequent messages
- // can be validated.
- let task = query_range_tasks.get_mut(their_node_id).unwrap();
- task.received_first_block = Some(msg.first_blocknum);
- task.received_last_block = Some(msg_last_block);
- }
- // On subsequent message(s)...
- else {
- // We need to validate the sequence of the reply message is expected.
- // Subsequent messages must set the first_blocknum to the previous
- // message's first_blocknum plus number_of_blocks. There is discrepancy
- // in implementation where some resume on the last sent block. We will
- // loosen the restriction and accept either, and otherwise consider the
- // message malformed and return an error.
- let task_received_last_block = task_received_last_block.unwrap();
- if msg.first_blocknum != task_received_last_block && msg.first_blocknum != task_received_last_block + 1 {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Failing reply_channel_range with invalid sequence"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Next we check to see that we have received a realistic number of
- // reply messages for a query. This caps the allocation exposure
- // for short_channel_ids that will be batched and sent in query channels.
- if task_number_of_replies + 1 > MAX_REPLY_CHANNEL_RANGE_PER_QUERY {
- query_range_tasks.remove(their_node_id);
- return Err(LightningError {
- err: String::from("Failing reply_channel_range due to excessive messages"),
- action: ErrorAction::IgnoreError,
- });
- }
-
- // Capture the last_block in our task so that subsequent messages
- // can be validated.
- let task = query_range_tasks.get_mut(their_node_id).unwrap();
- task.number_of_replies += 1;
- task.received_last_block = Some(msg_last_block);
- }
-
- // We filter the short_channel_ids to those inside the query range.
- // The most significant 3-bytes of the short_channel_id are the block.
- {
- let mut filtered_short_channel_ids: Vec<u64> = msg.short_channel_ids.clone().into_iter().filter(|short_channel_id| {
- let block = short_channel_id >> 40;
- return block >= query_range_tasks.get(their_node_id).unwrap().first_blocknum as u64 && block <= task_last_block as u64;
- }).collect();
- let task = query_range_tasks.get_mut(their_node_id).unwrap();
- task.short_channel_ids.append(&mut filtered_short_channel_ids);
- }