116 lines
4.1 KiB
Rust
116 lines
4.1 KiB
Rust
use async_trait::async_trait;
|
|
use iota_connection::connection_handler::{PeerRouter, RouteDestination, RouteError, RouteOutcome};
|
|
use iota_connection::relay_service::PendingRelayDispatcher;
|
|
use iota_identity::{IotaNodeId, PrincipalHandle};
|
|
use iota_storage::util::relay_queue::{self, RelayIdentity, RelayTarget};
|
|
use mtp::codec::{CommunicationType, CommunicationValue};
|
|
use mtp::crypto::Keyring;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
struct AcceptingRouter;
|
|
|
|
#[async_trait]
|
|
impl PeerRouter for AcceptingRouter {
|
|
async fn route(
|
|
&self,
|
|
destination: &RouteDestination,
|
|
_: CommunicationValue,
|
|
) -> Result<RouteOutcome, RouteError> {
|
|
assert!(matches!(destination, RouteDestination::Iota(_)));
|
|
Ok(RouteOutcome::Accepted {
|
|
relay_message_id: "restart-relay".into(),
|
|
destination_accepted_at: 10,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn initialize(storage: &Path) {
|
|
iota_util::file_util::configure_storage_directory(storage.to_owned());
|
|
iota_storage::util::db::initialize_database().unwrap();
|
|
}
|
|
|
|
fn seed(storage: &Path) {
|
|
initialize(storage);
|
|
let (signer, recipient) = iota_storage::util::db::with_db(|connection| {
|
|
connection.execute(
|
|
"INSERT INTO principals (authority_kind, authority_id, remote_user_id, descriptor_revision, last_resolved_at) VALUES ('iota', 'iota:first', 1, 1, 1)",
|
|
[],
|
|
)?;
|
|
let signer = connection.last_insert_rowid();
|
|
connection.execute(
|
|
"INSERT INTO principals (authority_kind, authority_id, remote_user_id, descriptor_revision, last_resolved_at) VALUES ('iota', 'iota:second', 1, 1, 1)",
|
|
[],
|
|
)?;
|
|
Ok((signer, connection.last_insert_rowid()))
|
|
})
|
|
.unwrap();
|
|
let frame = CommunicationValue::new(CommunicationType::Relay).with_id(7);
|
|
let version = frame.type_map().unwrap().version.to_string();
|
|
let bytes = frame.to_bytes().unwrap();
|
|
relay_queue::enqueue(
|
|
RelayTarget::Iota(
|
|
IotaNodeId::from_public_keys(&Keyring::generate().public_key_bundle()).unwrap(),
|
|
),
|
|
&RelayIdentity {
|
|
signer: PrincipalHandle(signer),
|
|
recipient: PrincipalHandle(recipient),
|
|
message_id: "restart-relay".into(),
|
|
legacy_signer_id: None,
|
|
legacy_recipient_id: None,
|
|
},
|
|
&bytes,
|
|
0,
|
|
7,
|
|
&version,
|
|
)
|
|
.unwrap();
|
|
assert_eq!(relay_queue::list_active(10).unwrap().len(), 1);
|
|
}
|
|
|
|
async fn deliver(storage: &Path) {
|
|
initialize(storage);
|
|
let dispatcher = PendingRelayDispatcher::new(std::sync::Arc::new(AcceptingRouter));
|
|
assert_eq!(dispatcher.dispatch_ready(10).await.unwrap(), 1);
|
|
assert_eq!(dispatcher.dispatch_ready(10).await.unwrap(), 0);
|
|
assert!(relay_queue::list(10).unwrap().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pending_relay_survives_process_restart_and_delivers_once() {
|
|
let storage =
|
|
std::env::var_os("TENSAMIN_PENDING_RESTART_STORAGE").map(std::path::PathBuf::from);
|
|
match std::env::var("TENSAMIN_PENDING_RESTART_PHASE").as_deref() {
|
|
Ok("seed") => {
|
|
seed(storage.as_deref().unwrap());
|
|
return;
|
|
}
|
|
Ok("deliver") => {
|
|
deliver(storage.as_deref().unwrap()).await;
|
|
return;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let storage = std::env::temp_dir().join(format!(
|
|
"tensamin-pending-restart-{}-{}",
|
|
std::process::id(),
|
|
iota_storage::util::sync::now_millis()
|
|
));
|
|
std::fs::create_dir(&storage).unwrap();
|
|
let executable = std::env::current_exe().unwrap();
|
|
for phase in ["seed", "deliver"] {
|
|
let status = Command::new(&executable)
|
|
.args([
|
|
"--exact",
|
|
"pending_relay_survives_process_restart_and_delivers_once",
|
|
"--nocapture",
|
|
])
|
|
.env("TENSAMIN_PENDING_RESTART_PHASE", phase)
|
|
.env("TENSAMIN_PENDING_RESTART_STORAGE", &storage)
|
|
.status()
|
|
.unwrap();
|
|
assert!(status.success(), "{phase} child process failed");
|
|
}
|
|
std::fs::remove_dir_all(&storage).unwrap();
|
|
}
|