use mesmo::accounts::{Account, SigningRole};
use mesmo::{error_codes, Mesmo, Network};
use serde_json::{json, Value};
const TEST_MNEMONIC: &str =
"test walk nut penalty hip pave soap entry language right filter choice";
const TESTNET: Network = Network::Testnet;
fn protocol_params() -> Value {
json!({
"min_fee_a": 44, "min_fee_b": 155381, "max_tx_size": 16384,
"key_deposit": "2000000", "pool_deposit": "500000000",
"coins_per_utxo_size": "4310", "max_val_size": "5000",
"max_tx_ex_mem": "10000000", "max_tx_ex_steps": "10000000000",
"price_mem": 0.0577, "price_step": 0.0000721, "collateral_percent": 150,
"max_collateral_inputs": 3
})
}
fn unsigned_stake_reg(lib: &Mesmo, info: &Value) -> String {
let base = info["base_address"].as_str().unwrap();
let stake = info["stake_address"].as_str().unwrap();
let yaml = format!(
"version: 1.0\ntransaction:\n - tx:\n from: {base}\n intents:\n - type: stake_registration\n stake_address: {stake}\n"
);
let utxos = json!([{
"tx_hash": "a".repeat(64), "output_index": 0, "address": base,
"amount": [{"unit": "lovelace", "quantity": "2000000000"}]
}]);
lib
.quicktx()
.build(&yaml, &utxos, &protocol_params(), None, 1)
.expect("build")
.tx_cbor
}
#[test]
fn open_info_matches_pinned_derivation() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
let info = acct.info().unwrap();
assert_eq!(
info["base_address"],
"addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer\
3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp"
);
assert_eq!(
info["stake_address"],
"stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl"
);
assert_eq!(
info["change_address"],
"addr_test1qz4kjk0as0x7ptt54l6cnfyzejqg22cku0qhqx6al4g2xe\
pjcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq5hxe5g"
);
assert_eq!(info["network"], 1);
assert!(info.get("mnemonic").is_none());
}
#[test]
fn sign_determinism_and_role_witnesses() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
let unsigned = unsigned_stake_reg(&lib, &acct.info().unwrap());
let managed = acct.sign_tx(&unsigned, SigningRole::PAYMENT).unwrap();
assert_eq!(managed, acct.sign_tx(&unsigned, SigningRole::PAYMENT).unwrap());
let managed2 = acct
.sign_tx(&unsigned, SigningRole::PAYMENT | SigningRole::STAKE)
.unwrap();
assert!(managed2.len() > managed.len());
assert_eq!(
acct.sign_tx(&unsigned, SigningRole::STAKE | SigningRole::PAYMENT).unwrap(),
managed2
);
}
#[test]
fn empty_role_mask_rejected() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
let unsigned = unsigned_stake_reg(&lib, &acct.info().unwrap());
let err = acct.sign_tx(&unsigned, SigningRole(0)).unwrap_err();
assert_eq!(err.code, error_codes::MESMO_ERROR_INVALID_ARGUMENT);
}
#[test]
fn lifecycle_close_idempotent_use_after_close_typed() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
acct.close().unwrap();
acct.close().unwrap(); let err = acct.info().unwrap_err();
assert_eq!(err.code, error_codes::MESMO_ERROR_INVALID_HANDLE);
}
#[test]
fn create_export_once_and_restore() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().create(TESTNET).unwrap();
let base = acct.info().unwrap()["base_address"].clone();
let phrase = acct.export_recovery_phrase().unwrap();
assert_eq!(phrase.split_whitespace().count(), 24);
let restored = lib.accounts().from_mnemonic(&phrase, TESTNET, 0, 0).unwrap();
assert_eq!(restored.info().unwrap()["base_address"], base);
let err = acct.export_recovery_phrase().unwrap_err();
assert_eq!(err.code, error_codes::MESMO_ERROR_INVALID_ARGUMENT);
let err = restored.export_recovery_phrase().unwrap_err();
assert_eq!(err.code, error_codes::MESMO_ERROR_INVALID_ARGUMENT);
}
#[test]
fn debug_never_contains_secrets() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().create(TESTNET).unwrap();
let phrase = acct.export_recovery_phrase().unwrap();
let debug = format!("{:?}", acct);
assert!(!debug.contains("addr")); assert!(!debug.contains(phrase.split_whitespace().next().unwrap()));
acct.close().unwrap();
assert_eq!(format!("{:?}", acct), "<mesmo::Account closed>");
}
#[test]
fn owned_account_storable_next_to_mesmo() {
struct WalletService {
_mesmo: Mesmo,
account: Account,
}
let lib = Mesmo::new().unwrap();
let account = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
let service = WalletService { _mesmo: lib, account };
assert!(service.account.info().unwrap()["base_address"]
.as_str()
.unwrap()
.starts_with("addr_test1"));
}
#[test]
fn mesmo_drop_invalidates_outstanding_accounts() {
let lib = Mesmo::new().unwrap();
let acct = lib.accounts().from_mnemonic(TEST_MNEMONIC, TESTNET, 0, 0).unwrap();
drop(lib);
let err = acct.info().unwrap_err();
assert_eq!(err.code, error_codes::MESMO_ERROR_INVALID_HANDLE);
acct.close().unwrap(); }