mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-18 08:45:00 +00:00
Add processor Docker tests
Adds tests/docker for code common to Docker-based tests.
This commit is contained in:
parent
624fb2781d
commit
523a055b74
13 changed files with 444 additions and 164 deletions
2
.github/workflows/message-queue-tests.yml
vendored
2
.github/workflows/message-queue-tests.yml
vendored
|
@ -6,11 +6,13 @@ on:
|
||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- "message-queue/**"
|
- "message-queue/**"
|
||||||
|
- "tests/docker/**"
|
||||||
- "tests/message-queue/**"
|
- "tests/message-queue/**"
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "message-queue/**"
|
- "message-queue/**"
|
||||||
|
- "tests/docker/**"
|
||||||
- "tests/message-queue/**"
|
- "tests/message-queue/**"
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
32
.github/workflows/processor-tests.yml
vendored
Normal file
32
.github/workflows/processor-tests.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: Processor Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
paths:
|
||||||
|
- "processor/**"
|
||||||
|
- "tests/docker/**"
|
||||||
|
- "tests/processor/**"
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "processor/**"
|
||||||
|
- "tests/docker/**"
|
||||||
|
- "tests/processor/**"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Build Dependencies
|
||||||
|
uses: ./.github/actions/build-dependencies
|
||||||
|
with:
|
||||||
|
github-token: ${{ inputs.github-token }}
|
||||||
|
|
||||||
|
- name: Run processor Docker tests
|
||||||
|
run: cd tests/processor && cargo test
|
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -8736,6 +8736,10 @@ dependencies = [
|
||||||
"rocksdb",
|
"rocksdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serai-docker-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serai-env"
|
name = "serai-env"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -8805,6 +8809,7 @@ dependencies = [
|
||||||
"dockertest",
|
"dockertest",
|
||||||
"hex",
|
"hex",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"serai-docker-tests",
|
||||||
"serai-message-queue",
|
"serai-message-queue",
|
||||||
"serai-primitives",
|
"serai-primitives",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -8931,6 +8936,25 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serai-processor-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ciphersuite",
|
||||||
|
"dkg",
|
||||||
|
"dockertest",
|
||||||
|
"hex",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"serai-docker-tests",
|
||||||
|
"serai-message-queue",
|
||||||
|
"serai-message-queue-tests",
|
||||||
|
"serai-primitives",
|
||||||
|
"serai-processor-messages",
|
||||||
|
"serai-validator-sets-primitives",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serai-runtime"
|
name = "serai-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -51,7 +51,10 @@ members = [
|
||||||
"substrate/client",
|
"substrate/client",
|
||||||
|
|
||||||
"tests/no-std",
|
"tests/no-std",
|
||||||
|
|
||||||
|
"tests/docker",
|
||||||
"tests/message-queue",
|
"tests/message-queue",
|
||||||
|
"tests/processor",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Always compile Monero (and a variety of dependencies) with optimizations due
|
# Always compile Monero (and a variety of dependencies) with optimizations due
|
||||||
|
|
|
@ -66,7 +66,9 @@ exceptions = [
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-client" },
|
{ allow = ["AGPL-3.0"], name = "serai-client" },
|
||||||
|
|
||||||
|
{ allow = ["AGPL-3.0"], name = "serai-docker-tests" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-message-queue-tests" },
|
{ allow = ["AGPL-3.0"], name = "serai-message-queue-tests" },
|
||||||
|
{ allow = ["AGPL-3.0"], name = "serai-processor-tests" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[licenses.clarify]]
|
[[licenses.clarify]]
|
||||||
|
|
14
tests/docker/Cargo.toml
Normal file
14
tests/docker/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "serai-docker-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Docker-based testing infrastructure for Serai"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/tests/docker"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = []
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
15
tests/docker/LICENSE
Normal file
15
tests/docker/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
AGPL-3.0-only license
|
||||||
|
|
||||||
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
45
tests/docker/src/lib.rs
Normal file
45
tests/docker/src/lib.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use std::{
|
||||||
|
sync::{Mutex, OnceLock},
|
||||||
|
collections::HashMap,
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
|
||||||
|
static BUILT: OnceLock<Mutex<HashMap<String, bool>>> = OnceLock::new();
|
||||||
|
pub fn build(name: String) {
|
||||||
|
let built = BUILT.get_or_init(|| Mutex::new(HashMap::new()));
|
||||||
|
// Only one call to build will acquire this lock
|
||||||
|
let mut built_lock = built.lock().unwrap();
|
||||||
|
if built_lock.contains_key(&name) {
|
||||||
|
// If it was built, return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, hold the lock while we build
|
||||||
|
let mut path = env::current_exe().unwrap();
|
||||||
|
path.pop();
|
||||||
|
assert!(path.as_path().ends_with("deps"));
|
||||||
|
path.pop();
|
||||||
|
assert!(path.as_path().ends_with("debug"));
|
||||||
|
path.pop();
|
||||||
|
assert!(path.as_path().ends_with("target"));
|
||||||
|
path.pop();
|
||||||
|
path.push("deploy");
|
||||||
|
|
||||||
|
println!("Building {}...", &name);
|
||||||
|
|
||||||
|
assert!(std::process::Command::new("docker")
|
||||||
|
.current_dir(path)
|
||||||
|
.arg("compose")
|
||||||
|
.arg("build")
|
||||||
|
.arg(&name)
|
||||||
|
.spawn()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap()
|
||||||
|
.success());
|
||||||
|
|
||||||
|
println!("Built!");
|
||||||
|
|
||||||
|
// Set built
|
||||||
|
built_lock.insert(name, true);
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "serai-message-queue-tests"
|
name = "serai-message-queue-tests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Tests for Serai's message-queue"
|
description = "Tests for Serai's Message Queue"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/tests/message-queue"
|
repository = "https://github.com/serai-dex/serai/tree/develop/tests/message-queue"
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
@ -25,3 +25,4 @@ serai-message-queue = { path = "../../message-queue" }
|
||||||
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
dockertest = "0.3"
|
dockertest = "0.3"
|
||||||
|
serai-docker-tests = { path = "../docker" }
|
||||||
|
|
|
@ -1,176 +1,125 @@
|
||||||
#[cfg(test)]
|
use std::collections::HashMap;
|
||||||
mod tests {
|
|
||||||
use std::{
|
|
||||||
sync::{Mutex, OnceLock},
|
|
||||||
collections::HashMap,
|
|
||||||
env,
|
|
||||||
};
|
|
||||||
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{
|
group::{ff::Field, GroupEncoding},
|
||||||
ff::{Field, PrimeField},
|
Ciphersuite, Ristretto,
|
||||||
GroupEncoding,
|
};
|
||||||
},
|
|
||||||
Ciphersuite, Ristretto,
|
use serai_primitives::NetworkId;
|
||||||
};
|
|
||||||
|
use dockertest::{PullPolicy, Image, LogAction, LogPolicy, LogSource, LogOptions, Composition};
|
||||||
|
|
||||||
|
pub type MessageQueuePrivateKey = <Ristretto as Ciphersuite>::F;
|
||||||
|
pub fn instance(
|
||||||
|
) -> (MessageQueuePrivateKey, HashMap<NetworkId, MessageQueuePrivateKey>, Composition) {
|
||||||
|
serai_docker_tests::build("message-queue".to_string());
|
||||||
|
|
||||||
|
let coord_key = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||||
|
let priv_keys = HashMap::from([
|
||||||
|
(NetworkId::Bitcoin, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||||
|
(NetworkId::Ethereum, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||||
|
(NetworkId::Monero, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut composition = Composition::with_image(
|
||||||
|
Image::with_repository("serai-dev-message-queue").pull_policy(PullPolicy::Never),
|
||||||
|
)
|
||||||
|
.with_log_options(Some(LogOptions {
|
||||||
|
action: LogAction::Forward,
|
||||||
|
policy: LogPolicy::Always,
|
||||||
|
source: LogSource::Both,
|
||||||
|
}))
|
||||||
|
.with_env(
|
||||||
|
[
|
||||||
|
("COORDINATOR_KEY".to_string(), hex::encode((Ristretto::generator() * coord_key).to_bytes())),
|
||||||
|
(
|
||||||
|
"BITCOIN_KEY".to_string(),
|
||||||
|
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Bitcoin]).to_bytes()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ETHEREUM_KEY".to_string(),
|
||||||
|
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Ethereum]).to_bytes()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"MONERO_KEY".to_string(),
|
||||||
|
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Monero]).to_bytes()),
|
||||||
|
),
|
||||||
|
("DB_PATH".to_string(), "./message-queue-db".to_string()),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
composition.publish_all_ports();
|
||||||
|
|
||||||
|
(coord_key, priv_keys, composition)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_functionality() {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use ciphersuite::group::ff::PrimeField;
|
||||||
|
|
||||||
|
use dockertest::DockerTest;
|
||||||
|
|
||||||
use serai_primitives::NetworkId;
|
|
||||||
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
||||||
|
|
||||||
use dockertest::{
|
let mut test = DockerTest::new();
|
||||||
PullPolicy, Image, LogAction, LogPolicy, LogSource, LogOptions, Composition, DockerTest,
|
let (coord_key, priv_keys, composition) = instance();
|
||||||
};
|
test.add_composition(composition);
|
||||||
|
test.run(|ops| async move {
|
||||||
|
// Sleep for a second for the message-queue to boot
|
||||||
|
// It isn't an error to start immediately, it just silences an error
|
||||||
|
tokio::time::sleep(core::time::Duration::from_secs(1)).await;
|
||||||
|
|
||||||
static BUILT: OnceLock<Mutex<bool>> = OnceLock::new();
|
let rpc = ops.handle("serai-dev-message-queue").host_port(2287).unwrap();
|
||||||
fn build() {
|
// TODO: Add new to MessageQueue to avoid needing to use set_var
|
||||||
let built = BUILT.get_or_init(|| Mutex::new(false));
|
env::set_var("MESSAGE_QUEUE_RPC", rpc.0.to_string() + ":" + &rpc.1.to_string());
|
||||||
// Only one call to build will acquire this lock
|
env::set_var("MESSAGE_QUEUE_KEY", hex::encode(coord_key.to_repr()));
|
||||||
let mut built_lock = built.lock().unwrap();
|
|
||||||
if *built_lock {
|
|
||||||
// If it was built, return
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else, hold the lock while we build
|
// Queue some messages
|
||||||
let mut path = env::current_exe().unwrap();
|
let coordinator = MessageQueue::from_env(Service::Coordinator);
|
||||||
path.pop();
|
coordinator
|
||||||
assert!(path.as_path().ends_with("deps"));
|
.queue(
|
||||||
path.pop();
|
Metadata {
|
||||||
assert!(path.as_path().ends_with("debug"));
|
from: Service::Coordinator,
|
||||||
path.pop();
|
to: Service::Processor(NetworkId::Bitcoin),
|
||||||
assert!(path.as_path().ends_with("target"));
|
intent: b"intent".to_vec(),
|
||||||
path.pop();
|
},
|
||||||
path.push("deploy");
|
b"Hello, World!".to_vec(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
println!("Building message-queue...");
|
coordinator
|
||||||
|
.queue(
|
||||||
|
Metadata {
|
||||||
|
from: Service::Coordinator,
|
||||||
|
to: Service::Processor(NetworkId::Bitcoin),
|
||||||
|
intent: b"intent 2".to_vec(),
|
||||||
|
},
|
||||||
|
b"Hello, World, again!".to_vec(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
assert!(std::process::Command::new("docker")
|
// Successfully get it
|
||||||
.current_dir(path)
|
env::set_var("MESSAGE_QUEUE_KEY", hex::encode(priv_keys[&NetworkId::Bitcoin].to_repr()));
|
||||||
.arg("compose")
|
let bitcoin = MessageQueue::from_env(Service::Processor(NetworkId::Bitcoin));
|
||||||
.arg("build")
|
let msg = bitcoin.next(0).await;
|
||||||
.arg("message-queue")
|
assert_eq!(msg.from, Service::Coordinator);
|
||||||
.spawn()
|
assert_eq!(msg.id, 0);
|
||||||
.unwrap()
|
assert_eq!(&msg.msg, b"Hello, World!");
|
||||||
.wait()
|
|
||||||
.unwrap()
|
|
||||||
.success());
|
|
||||||
|
|
||||||
println!("Built!");
|
// If we don't ack it, it should continue to be returned
|
||||||
|
assert_eq!(msg, bitcoin.next(0).await);
|
||||||
|
|
||||||
// Set built
|
// Acknowledging it should yield the next message
|
||||||
*built_lock = true;
|
bitcoin.ack(0).await;
|
||||||
}
|
|
||||||
|
|
||||||
type PrivateKey = <Ristretto as Ciphersuite>::F;
|
let next_msg = bitcoin.next(1).await;
|
||||||
fn instance() -> (PrivateKey, HashMap<NetworkId, PrivateKey>, Composition) {
|
assert!(msg != next_msg);
|
||||||
build();
|
assert_eq!(next_msg.from, Service::Coordinator);
|
||||||
|
assert_eq!(next_msg.id, 1);
|
||||||
let coord_key = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
assert_eq!(&next_msg.msg, b"Hello, World, again!");
|
||||||
let priv_keys = HashMap::from([
|
});
|
||||||
(NetworkId::Bitcoin, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
|
||||||
(NetworkId::Ethereum, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
|
||||||
(NetworkId::Monero, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let mut composition = Composition::with_image(
|
|
||||||
Image::with_repository("serai-dev-message-queue").pull_policy(PullPolicy::Never),
|
|
||||||
)
|
|
||||||
.with_log_options(Some(LogOptions {
|
|
||||||
action: LogAction::Forward,
|
|
||||||
policy: LogPolicy::Always,
|
|
||||||
source: LogSource::Both,
|
|
||||||
}))
|
|
||||||
.with_env(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"COORDINATOR_KEY".to_string(),
|
|
||||||
hex::encode((Ristretto::generator() * coord_key).to_bytes()),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"BITCOIN_KEY".to_string(),
|
|
||||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Bitcoin]).to_bytes()),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"ETHEREUM_KEY".to_string(),
|
|
||||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Ethereum]).to_bytes()),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"MONERO_KEY".to_string(),
|
|
||||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Monero]).to_bytes()),
|
|
||||||
),
|
|
||||||
("DB_PATH".to_string(), "./message-queue-db".to_string()),
|
|
||||||
]
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
composition.publish_all_ports();
|
|
||||||
|
|
||||||
(coord_key, priv_keys, composition)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_functionality() {
|
|
||||||
let mut test = DockerTest::new();
|
|
||||||
let (coord_key, priv_keys, composition) = instance();
|
|
||||||
test.add_composition(composition);
|
|
||||||
test.run(|ops| async move {
|
|
||||||
// Sleep for a second for the message-queue to boot
|
|
||||||
// It isn't an error to start immediately, it just silences an error
|
|
||||||
tokio::time::sleep(core::time::Duration::from_secs(1)).await;
|
|
||||||
|
|
||||||
let rpc = ops.handle("serai-dev-message-queue").host_port(2287).unwrap();
|
|
||||||
// TODO: MessageQueue directly read from env to remove this boilerplate from all binaries,
|
|
||||||
// yet it's now annoying as hell to parameterize. Split into new/from_env?
|
|
||||||
env::set_var(
|
|
||||||
"MESSAGE_QUEUE_RPC",
|
|
||||||
"http://".to_string() + &rpc.0.to_string() + ":" + &rpc.1.to_string(),
|
|
||||||
);
|
|
||||||
env::set_var("MESSAGE_QUEUE_KEY", hex::encode(coord_key.to_repr()));
|
|
||||||
|
|
||||||
// Queue some messagse
|
|
||||||
let coordinator = MessageQueue::new(Service::Coordinator);
|
|
||||||
coordinator
|
|
||||||
.queue(
|
|
||||||
Metadata {
|
|
||||||
from: Service::Coordinator,
|
|
||||||
to: Service::Processor(NetworkId::Bitcoin),
|
|
||||||
intent: b"intent".to_vec(),
|
|
||||||
},
|
|
||||||
b"Hello, World!".to_vec(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
coordinator
|
|
||||||
.queue(
|
|
||||||
Metadata {
|
|
||||||
from: Service::Coordinator,
|
|
||||||
to: Service::Processor(NetworkId::Bitcoin),
|
|
||||||
intent: b"intent 2".to_vec(),
|
|
||||||
},
|
|
||||||
b"Hello, World, again!".to_vec(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Successfully get it
|
|
||||||
env::set_var("MESSAGE_QUEUE_KEY", hex::encode(priv_keys[&NetworkId::Bitcoin].to_repr()));
|
|
||||||
let bitcoin = MessageQueue::new(Service::Processor(NetworkId::Bitcoin));
|
|
||||||
let msg = bitcoin.next(0).await;
|
|
||||||
assert_eq!(msg.from, Service::Coordinator);
|
|
||||||
assert_eq!(msg.id, 0);
|
|
||||||
assert_eq!(&msg.msg, b"Hello, World!");
|
|
||||||
|
|
||||||
// If we don't ack it, it should continue to be returned
|
|
||||||
assert_eq!(msg, bitcoin.next(0).await);
|
|
||||||
|
|
||||||
// Acknowledging it should yield the next message
|
|
||||||
bitcoin.ack(0).await;
|
|
||||||
|
|
||||||
let next_msg = bitcoin.next(1).await;
|
|
||||||
assert!(msg != next_msg);
|
|
||||||
assert_eq!(next_msg.from, Service::Coordinator);
|
|
||||||
assert_eq!(next_msg.id, 1);
|
|
||||||
assert_eq!(&next_msg.msg, b"Hello, World, again!");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
36
tests/processor/Cargo.toml
Normal file
36
tests/processor/Cargo.toml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
[package]
|
||||||
|
name = "serai-processor-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Tests for Serai's Processor"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/tests/processor"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = []
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hex = "0.4"
|
||||||
|
|
||||||
|
rand_core = "0.6"
|
||||||
|
|
||||||
|
ciphersuite = { path = "../../crypto/ciphersuite", features = ["ristretto"] }
|
||||||
|
dkg = { path = "../../crypto/dkg" }
|
||||||
|
|
||||||
|
messages = { package = "serai-processor-messages", path = "../../processor/messages" }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../../substrate/primitives" }
|
||||||
|
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives" }
|
||||||
|
serai-message-queue = { path = "../../message-queue" }
|
||||||
|
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
||||||
|
dockertest = "0.3"
|
||||||
|
serai-docker-tests = { path = "../docker" }
|
||||||
|
serai-message-queue-tests = { path = "../message-queue" }
|
15
tests/processor/LICENSE
Normal file
15
tests/processor/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
AGPL-3.0-only license
|
||||||
|
|
||||||
|
Copyright (c) 2023 Luke Parker
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
142
tests/processor/src/lib.rs
Normal file
142
tests/processor/src/lib.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
|
use ciphersuite::{group::ff::PrimeField, Ciphersuite, Ristretto};
|
||||||
|
|
||||||
|
use dockertest::{
|
||||||
|
PullPolicy, Image, LogAction, LogPolicy, LogSource, LogOptions, StartPolicy, Composition,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bitcoin_instance() -> Composition {
|
||||||
|
serai_docker_tests::build("bitcoin".to_string());
|
||||||
|
|
||||||
|
Composition::with_image(
|
||||||
|
Image::with_repository("serai-dev-bitcoin").pull_policy(PullPolicy::Never),
|
||||||
|
)
|
||||||
|
.with_log_options(Some(LogOptions {
|
||||||
|
action: LogAction::Forward,
|
||||||
|
policy: LogPolicy::Always,
|
||||||
|
source: LogSource::Both,
|
||||||
|
}))
|
||||||
|
.with_cmd(vec![
|
||||||
|
"bitcoind".to_string(),
|
||||||
|
"-txindex".to_string(),
|
||||||
|
"-regtest".to_string(),
|
||||||
|
"-rpcuser=serai".to_string(),
|
||||||
|
"-rpcpassword=seraidex".to_string(),
|
||||||
|
"-rpcbind=0.0.0.0".to_string(),
|
||||||
|
"-rpcallowip=0.0.0.0/0".to_string(),
|
||||||
|
"-rpcport=8332".to_string(),
|
||||||
|
])
|
||||||
|
.with_start_policy(StartPolicy::Strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance(message_queue_key: <Ristretto as Ciphersuite>::F) -> Composition {
|
||||||
|
serai_docker_tests::build("processor".to_string());
|
||||||
|
|
||||||
|
let mut entropy = [0; 32];
|
||||||
|
OsRng.fill_bytes(&mut entropy);
|
||||||
|
|
||||||
|
Composition::with_image(
|
||||||
|
Image::with_repository("serai-dev-processor").pull_policy(PullPolicy::Never),
|
||||||
|
)
|
||||||
|
.with_log_options(Some(LogOptions {
|
||||||
|
action: LogAction::Forward,
|
||||||
|
policy: LogPolicy::Always,
|
||||||
|
source: LogSource::Both,
|
||||||
|
}))
|
||||||
|
.with_env(
|
||||||
|
[
|
||||||
|
("MESSAGE_QUEUE_KEY".to_string(), hex::encode(message_queue_key.to_repr())),
|
||||||
|
("ENTROPY".to_string(), hex::encode(entropy)),
|
||||||
|
("NETWORK".to_string(), "bitcoin".to_string()),
|
||||||
|
("NETWORK_RPC_LOGIN".to_string(), "serai:seraidex".to_string()),
|
||||||
|
("NETWORK_RPC_PORT".to_string(), "8332".to_string()),
|
||||||
|
("DB_PATH".to_string(), "./processor-db".to_string()),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.with_start_policy(StartPolicy::Strict)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_functionality() {
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use serai_primitives::NetworkId;
|
||||||
|
use serai_validator_sets_primitives::{Session, ValidatorSet};
|
||||||
|
|
||||||
|
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
||||||
|
|
||||||
|
use dockertest::DockerTest;
|
||||||
|
|
||||||
|
let bitcoin_composition = bitcoin_instance();
|
||||||
|
|
||||||
|
let (coord_key, message_queue_keys, message_queue_composition) =
|
||||||
|
serai_message_queue_tests::instance();
|
||||||
|
let message_queue_composition = message_queue_composition.with_start_policy(StartPolicy::Strict);
|
||||||
|
|
||||||
|
let mut processor_composition = instance(message_queue_keys[&NetworkId::Bitcoin]);
|
||||||
|
processor_composition.inject_container_name(bitcoin_composition.handle(), "NETWORK_RPC_HOSTNAME");
|
||||||
|
processor_composition
|
||||||
|
.inject_container_name(message_queue_composition.handle(), "MESSAGE_QUEUE_RPC");
|
||||||
|
|
||||||
|
let mut test = DockerTest::new();
|
||||||
|
test.add_composition(bitcoin_composition);
|
||||||
|
test.add_composition(message_queue_composition);
|
||||||
|
test.add_composition(processor_composition);
|
||||||
|
|
||||||
|
test.run(|ops| async move {
|
||||||
|
// Sleep for 10 seconds to be polite and let things boot
|
||||||
|
tokio::time::sleep(core::time::Duration::from_secs(10)).await;
|
||||||
|
|
||||||
|
// Connect to the Message Queue as the coordinator
|
||||||
|
let rpc = ops.handle("serai-dev-message-queue").host_port(2287).unwrap();
|
||||||
|
// TODO: MessageQueue::new
|
||||||
|
env::set_var(
|
||||||
|
"MESSAGE_QUEUE_RPC",
|
||||||
|
"http://".to_string() + &rpc.0.to_string() + ":" + &rpc.1.to_string(),
|
||||||
|
);
|
||||||
|
env::set_var("MESSAGE_QUEUE_KEY", hex::encode(coord_key.to_repr()));
|
||||||
|
let coordinator = MessageQueue::from_env(Service::Coordinator);
|
||||||
|
|
||||||
|
// Order a key gen
|
||||||
|
let id = messages::key_gen::KeyGenId {
|
||||||
|
set: ValidatorSet { session: Session(0), network: NetworkId::Bitcoin },
|
||||||
|
attempt: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
coordinator
|
||||||
|
.queue(
|
||||||
|
Metadata {
|
||||||
|
from: Service::Coordinator,
|
||||||
|
to: Service::Processor(NetworkId::Bitcoin),
|
||||||
|
intent: b"key_gen_0".to_vec(),
|
||||||
|
},
|
||||||
|
serde_json::to_string(&messages::CoordinatorMessage::KeyGen(
|
||||||
|
messages::key_gen::CoordinatorMessage::GenerateKey {
|
||||||
|
id,
|
||||||
|
params: dkg::ThresholdParams::new(3, 4, dkg::Participant::new(1).unwrap()).unwrap(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
.into_bytes(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Read the created commitments
|
||||||
|
let msg = coordinator.next(0).await;
|
||||||
|
assert_eq!(msg.from, Service::Processor(NetworkId::Bitcoin));
|
||||||
|
assert_eq!(msg.id, 0);
|
||||||
|
let msg: messages::ProcessorMessage = serde_json::from_slice(&msg.msg).unwrap();
|
||||||
|
match msg {
|
||||||
|
messages::ProcessorMessage::KeyGen(messages::key_gen::ProcessorMessage::Commitments {
|
||||||
|
id: this_id,
|
||||||
|
commitments: _,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(this_id, id);
|
||||||
|
}
|
||||||
|
_ => panic!("processor didn't return Commitments in response to GenerateKey"),
|
||||||
|
}
|
||||||
|
coordinator.ack(0).await;
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue