// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see .
//! Everything about outgoing messages sending.
use bp_messages::{LaneId, MessageData, MessageNonce, OutboundLaneData};
/// Outbound lane storage.
pub trait OutboundLaneStorage {
/// Delivery and dispatch fee type on source chain.
type MessageFee;
/// Lane id.
fn id(&self) -> LaneId;
/// Get lane data from the storage.
fn data(&self) -> OutboundLaneData;
/// Update lane data in the storage.
fn set_data(&mut self, data: OutboundLaneData);
/// Returns saved outbound message payload.
#[cfg(test)]
fn message(&self, nonce: &MessageNonce) -> Option>;
/// Save outbound message in the storage.
fn save_message(&mut self, nonce: MessageNonce, message_data: MessageData);
/// Remove outbound message from the storage.
fn remove_message(&mut self, nonce: &MessageNonce);
}
/// Outbound messages lane.
pub struct OutboundLane {
storage: S,
}
impl OutboundLane {
/// Create new inbound lane backed by given storage.
pub fn new(storage: S) -> Self {
OutboundLane { storage }
}
/// Get this lane data.
pub fn data(&self) -> OutboundLaneData {
self.storage.data()
}
/// Send message over lane.
///
/// Returns new message nonce.
pub fn send_message(&mut self, message_data: MessageData) -> MessageNonce {
let mut data = self.storage.data();
let nonce = data.latest_generated_nonce + 1;
data.latest_generated_nonce = nonce;
self.storage.save_message(nonce, message_data);
self.storage.set_data(data);
nonce
}
/// Confirm messages delivery.
///
/// Returns `None` if confirmation is wrong/duplicate.
/// Returns `Some` with inclusive ranges of message nonces that have been received.
pub fn confirm_delivery(&mut self, latest_received_nonce: MessageNonce) -> Option<(MessageNonce, MessageNonce)> {
let mut data = self.storage.data();
if latest_received_nonce <= data.latest_received_nonce || latest_received_nonce > data.latest_generated_nonce {
return None;
}
let prev_latest_received_nonce = data.latest_received_nonce;
data.latest_received_nonce = latest_received_nonce;
self.storage.set_data(data);
Some((prev_latest_received_nonce + 1, latest_received_nonce))
}
/// Prune at most `max_messages_to_prune` already received messages.
///
/// Returns number of pruned messages.
pub fn prune_messages(&mut self, max_messages_to_prune: MessageNonce) -> MessageNonce {
let mut pruned_messages = 0;
let mut anything_changed = false;
let mut data = self.storage.data();
while pruned_messages < max_messages_to_prune && data.oldest_unpruned_nonce <= data.latest_received_nonce {
self.storage.remove_message(&data.oldest_unpruned_nonce);
anything_changed = true;
pruned_messages += 1;
data.oldest_unpruned_nonce += 1;
}
if anything_changed {
self.storage.set_data(data);
}
pruned_messages
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
mock::{message_data, run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
outbound_lane,
};
#[test]
fn send_message_works() {
run_test(|| {
let mut lane = outbound_lane::(TEST_LANE_ID);
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
assert!(lane.storage.message(&1).is_some());
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
});
}
#[test]
fn confirm_delivery_works() {
run_test(|| {
let mut lane = outbound_lane::(TEST_LANE_ID);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 2);
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 3);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
});
}
#[test]
fn confirm_delivery_rejects_nonce_lesser_than_latest_received() {
run_test(|| {
let mut lane = outbound_lane::(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
assert_eq!(lane.confirm_delivery(3), None);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
assert_eq!(lane.confirm_delivery(2), None);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 3);
});
}
#[test]
fn confirm_delivery_rejects_nonce_larger_than_last_generated() {
run_test(|| {
let mut lane = outbound_lane::(TEST_LANE_ID);
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
assert_eq!(lane.confirm_delivery(10), None);
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
assert_eq!(lane.storage.data().latest_received_nonce, 0);
});
}
#[test]
fn prune_messages_works() {
run_test(|| {
let mut lane = outbound_lane::(TEST_LANE_ID);
// when lane is empty, nothing is pruned
assert_eq!(lane.prune_messages(100), 0);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// when nothing is confirmed, nothing is pruned
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
lane.send_message(message_data(REGULAR_PAYLOAD));
assert_eq!(lane.prune_messages(100), 0);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
// after confirmation, some messages are received
assert_eq!(lane.confirm_delivery(2), Some((1, 2)));
assert_eq!(lane.prune_messages(100), 2);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3);
// after last message is confirmed, everything is pruned
assert_eq!(lane.confirm_delivery(3), Some((3, 3)));
assert_eq!(lane.prune_messages(100), 1);
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4);
});
}
}