Skip to content

Commit

Permalink
fix: add first proper test using ledger and CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
willemneal committed Nov 19, 2024
1 parent 98b0100 commit 37f302d
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 56 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ walkdir = "2.5.0"
toml_edit = "0.22.20"
toml = "0.8.19"
reqwest = "0.12.7"
# testing
predicates = "3.1.2"
testcontainers = { version = "0.20.1" }


[profile.test-wasms]
inherits = "release"
Expand Down
4 changes: 4 additions & 0 deletions cmd/crates/soroban-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ stellar-strkey = { workspace = true }
sep5 = { workspace = true }
soroban-cli = { workspace = true }
soroban-rpc = { workspace = true }
stellar-ledger = { workspace = true }

thiserror = "1.0.31"
sha2 = "0.10.6"
Expand All @@ -32,6 +33,7 @@ assert_fs = "1.0.7"
predicates = { workspace = true }
fs_extra = "1.3.0"
toml = { workspace = true }
testcontainers = { workspace = true }


[dev-dependencies]
Expand All @@ -42,6 +44,8 @@ walkdir = "2.4.0"
ulid.workspace = true
ed25519-dalek = { workspace = true }
hex = { workspace = true }
test-case = "3.3.1"

[features]
it = []
emulator-tests = ["stellar-ledger/emulator-tests"]
12 changes: 12 additions & 0 deletions cmd/crates/soroban-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ use soroban_cli::{
};

mod wasm;
use stellar_ledger::emulator_test_support::{
enable_hash_signing, get_container, speculos::Speculos, wait_for_emulator_start_text,
};
use testcontainers::ContainerAsync;
pub use wasm::Wasm;

pub const TEST_ACCOUNT: &str = "test";
Expand Down Expand Up @@ -308,6 +312,14 @@ impl TestEnv {
pub fn client(&self) -> soroban_rpc::Client {
soroban_rpc::Client::new(&self.rpc_url).unwrap()
}

pub async fn speculos_container(ledger_device_model: &str) -> ContainerAsync<Speculos> {
let container = get_container(ledger_device_model).await;
let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap();
wait_for_emulator_start_text(ui_host_port).await;
enable_hash_signing(ui_host_port).await;
container
}
}

pub fn temp_ledger_file() -> OsString {
Expand Down
3 changes: 3 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ mod snapshot;
mod tx;
mod util;
mod wrap;

#[cfg(feature = "emulator-tests")]
mod emulator;
83 changes: 83 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration/emulator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use stellar_ledger::{Blob, Error};

use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE};
use std::sync::Arc;

use soroban_cli::xdr::{
self, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, SequenceNumber,
Transaction, TransactionExt, Uint256,
};

use stellar_ledger::emulator_test_support::*;

use test_case::test_case;

use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD};

// #[test_case("nanos"; "when the device is NanoS")]
#[test_case("nanox"; "when the device is NanoX")]
// #[test_case("nanosp"; "when the device is NanoS Plus")]
#[tokio::test]
async fn test_get_public_key(ledger_device_model: &str) {
let sandbox = Arc::new(TestEnv::new());
let container = TestEnv::speculos_container(ledger_device_model).await;
let host_port = container.get_host_port_ipv4(9998).await.unwrap();
let ui_host_port = container.get_host_port_ipv4(5000).await.unwrap();

let ledger = ledger(host_port).await;

let key = ledger.get_public_key(&0.into()).await.unwrap();
let account = &key.to_string();
sandbox.fund_account(account);
sandbox
.new_assert_cmd("contract")
.arg("install")
.args([
"--wasm",
HELLO_WORLD.path().as_os_str().to_str().unwrap(),
"--source",
account,
])
.assert()
.success();

let tx_simulated =
deploy_contract(&sandbox, HELLO_WORLD, DeployKind::SimOnly, Some(account)).await;
dbg!("{tx_simulated}");
let key = ledger.get_public_key(&0.into()).await.unwrap();
println!("{key}");
let sign = tokio::task::spawn_blocking({
let sandbox = Arc::clone(&sandbox);

move || {
sandbox
.new_assert_cmd("tx")
.arg("sign")
.arg("--sign-with-ledger")
.write_stdin(tx_simulated.as_bytes())
.env("SPECULOS_PORT", host_port.to_string())
.env("RUST_LOGS", "trace")
.assert()
.success()
.stdout_as_str()
}
});
let approve = tokio::task::spawn(approve_tx_hash_signature(
ui_host_port,
ledger_device_model.to_string(),
));

let response = sign.await.unwrap();
approve.await.unwrap();

dbg!("{tx_signed}");

sandbox
.clone()
.new_assert_cmd("tx")
.arg("send")
.write_stdin(response.as_bytes())
.assert()
.success()
.stdout(predicates::str::contains("SUCCESS"));
}
13 changes: 11 additions & 2 deletions cmd/crates/soroban-test/tests/it/integration/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,19 @@ async fn txn_hash() {
#[tokio::test]
async fn build_simulate_sign_send() {
let sandbox = &TestEnv::new();
build_sim_sign_send(sandbox, "test", "--sign-with-key=test").await;
}

pub(crate) async fn build_sim_sign_send(sandbox: &TestEnv, account: &str, sign_with: &str) {
sandbox
.new_assert_cmd("contract")
.arg("install")
.args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()])
.args([
"--wasm",
HELLO_WORLD.path().as_os_str().to_str().unwrap(),
"--source",
account,
])
.assert()
.success();

Expand All @@ -69,7 +78,7 @@ async fn build_simulate_sign_send() {
let tx_signed = sandbox
.new_assert_cmd("tx")
.arg("sign")
.arg("--sign-with-key=test")
.arg(sign_with)
.write_stdin(tx_simulated.as_bytes())
.assert()
.success()
Expand Down
2 changes: 1 addition & 1 deletion cmd/crates/stellar-ledger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ tokio = { version = "1", features = ["full"] }
reqwest = { workspace = true, features = ["json"] }
phf = { version = "0.11.2", features = ["macros"], optional = true }
async-trait = { workspace = true }
testcontainers = { version = "0.20.1", optional = true }
testcontainers = { workspace = true, optional = true }

[dependencies.stellar-xdr]
workspace = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl Exchange for EmulatorHttpTransport {
let resp: Response = HttpClient::new()
.post(&self.url)
.headers(headers)
.timeout(Duration::from_secs(25))
.timeout(Duration::from_secs(60))
.json(&request)
.send()
.await
Expand Down
58 changes: 49 additions & 9 deletions cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use std::{borrow::Cow, collections::HashMap, path::PathBuf, str::FromStr};
use testcontainers::{
core::{Mount, WaitFor},
Image,
Expand Down Expand Up @@ -55,14 +55,54 @@ impl Speculos {
}

fn get_cmd(ledger_device_model: String) -> String {
let device_model = ledger_device_model.clone();
let container_elf_path = match device_model.as_str() {
"nanos" => format!("{DEFAULT_APP_PATH}/stellarNanoSApp.elf"),
"nanosp" => format!("{DEFAULT_APP_PATH}/stellarNanoSPApp.elf"),
"nanox" => format!("{DEFAULT_APP_PATH}/stellarNanoXApp.elf"),
_ => panic!("Unsupported device model"),
};
format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}")
let device_model: DeviceModel = ledger_device_model.parse().unwrap();
let container_elf_path = format!("{DEFAULT_APP_PATH}/{}", device_model.as_file());
format!(
"/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN \
--display headless \
-s {TEST_SEED_PHRASE} \
-m {device_model} {container_elf_path}"
)
}
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DeviceModel {
NanoS,
NanoSP,
NanoX,
}

impl FromStr for DeviceModel {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"nanos" => Ok(DeviceModel::NanoS),
"nanosp" => Ok(DeviceModel::NanoSP),
"nanox" => Ok(DeviceModel::NanoX),
_ => Err(format!("Unsupported device model: {}", s)),
}
}
}

impl std::fmt::Display for DeviceModel {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DeviceModel::NanoS => write!(f, "nanos"),
DeviceModel::NanoSP => write!(f, "nanosp"),
DeviceModel::NanoX => write!(f, "nanox"),
}
}
}

impl DeviceModel {
pub fn as_file(&self) -> &str {
match self {
DeviceModel::NanoS => "stellarNanoSApp.elf",
DeviceModel::NanoSP => "stellarNanoSPApp.elf",
DeviceModel::NanoX => "stellarNanoXApp.elf",
}
}
}

Expand Down
9 changes: 4 additions & 5 deletions cmd/crates/stellar-ledger/src/emulator_test_support/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use ledger_transport::Exchange;
use serde::Deserialize;
use std::ops::Range;
use std::sync::LazyLock;
Expand All @@ -24,7 +23,7 @@ pub fn test_network_hash() -> Hash {
Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into())
}

pub async fn ledger(host_port: u16) -> LedgerSigner<impl Exchange> {
pub async fn ledger(host_port: u16) -> LedgerSigner<EmulatorHttpTransport> {
LedgerSigner::new(get_http_transport("127.0.0.1", host_port).await.unwrap())
}

Expand Down Expand Up @@ -83,9 +82,9 @@ struct EventsResponse {
events: Vec<EmulatorEvent>,
}

pub async fn get_container(ledger_device_model: String) -> ContainerAsync<Speculos> {
pub async fn get_container(ledger_device_model: &str) -> ContainerAsync<Speculos> {
let (tcp_port_1, tcp_port_2) = get_available_ports(2);
Speculos::new(ledger_device_model)
Speculos::new(ledger_device_model.to_string())
.with_mapped_port(tcp_port_1, ContainerPort::Tcp(9998))
.with_mapped_port(tcp_port_2, ContainerPort::Tcp(5000))
.start()
Expand All @@ -110,7 +109,7 @@ pub fn get_available_ports(n: usize) -> (u16, u16) {
(ports[0], ports[1])
}

pub async fn get_http_transport(host: &str, port: u16) -> Result<impl Exchange, Error> {
pub async fn get_http_transport(host: &str, port: u16) -> Result<EmulatorHttpTransport, Error> {
let max_retries = 5;
let mut retries = 0;
let mut wait_time = Duration::from_secs(1);
Expand Down
Loading

0 comments on commit 37f302d

Please sign in to comment.