use super::*;
#[test]
fn shared_protocol_vectors() {
let vectors: serde_json::Value =
serde_json::from_str(include_str!("../fixtures/text-encoding.json")).unwrap();
for vector in vectors["valid"].as_array().unwrap() {
let bytes: Vec<u8> = vector["bytes"]
.as_array()
.unwrap()
.iter()
.map(|byte| u8::try_from(byte.as_u64().unwrap()).unwrap())
.collect();
let text = vector["base58"].as_str().unwrap();
assert_eq!(encode(&bytes), text);
assert_eq!(decode(text, bytes.len()).unwrap(), bytes);
if let Some(keys) = vector.get("strkey") {
let fixed: [u8; 32] = bytes.try_into().unwrap();
assert!(parse_address(keys["contract"].as_str().unwrap()).is_err());
assert_eq!(
public_key_strkey(&fixed),
keys["publicKey"].as_str().unwrap()
);
assert_eq!(
secret_seed_strkey(&fixed).as_str(),
keys["seed"].as_str().unwrap()
);
}
}
for text in vectors["invalidBase58"].as_array().unwrap() {
assert!(decode(text.as_str().unwrap(), 256).is_err());
}
for text in vectors["invalidStrkey"].as_array().unwrap() {
assert!(parse_public_key(text.as_str().unwrap()).is_err());
}
}
#[test]
fn published_base58_example_and_leading_zeros() {
assert_eq!(encode(b"Hello World"), "JxF12TrwUP45BMd");
assert_eq!(decode("JxF12TrwUP45BMd", 11).unwrap(), b"Hello World");
assert_eq!(encode(&[0; 32]), "1".repeat(32));
assert_eq!(decode_fixed::<32>(&"1".repeat(32)).unwrap(), [0; 32]);
assert_eq!(encode(&[]), "");
assert_eq!(decode("", 0).unwrap(), Vec::<u8>::new());
assert_eq!(decode_fixed::<0>("").unwrap(), [0u8; 0]);
}
#[test]
fn checks_limits_before_and_after_conversion() {
assert_eq!(max_encoded_len(0), 0);
assert_eq!(max_encoded_len(32), 45);
assert_eq!(max_encoded_len(usize::MAX), usize::MAX);
assert_eq!(decode("1", 0), Err(Error::TooLong { max_bytes: 0 }));
assert_eq!(
decode(&"1".repeat(33), 32),
Err(Error::TooLong { max_bytes: 32 })
);
assert_eq!(
decode_fixed::<32>(&"1".repeat(31)),
Err(Error::InvalidLength {
expected: 32,
actual: 31
})
);
assert_eq!(
decode_fixed::<32>(&"z".repeat(46)),
Err(Error::TooLong { max_bytes: 32 })
);
for text in ["0", "O", "I", "l", " 1", "1 ", "1\n", "é", "1", "0x01"] {
assert_eq!(decode(text, 32), Err(Error::InvalidBase58));
}
}
#[test]
fn typed_strkeys_preserve_bytes_and_reject_wrong_types() {
let bytes = [0; 32];
let z = "ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ4L";
let c = "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4";
let g = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
let s = "SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSU2";
assert_eq!(address_strkey(&bytes), z);
assert_eq!(public_key_strkey(&bytes), g);
assert_eq!(secret_seed_strkey(&bytes).as_str(), s);
assert_eq!(parse_address(z).unwrap(), bytes);
assert!(parse_address(c).is_err());
assert_eq!(parse_public_key(g).unwrap(), bytes);
assert_eq!(*parse_secret_seed(s).unwrap(), bytes);
assert!(parse_address(g).is_err());
assert!(parse_address(s).is_err());
assert!(parse_public_key(z).is_err());
assert!(parse_secret_seed(g).is_err());
assert!(parse_secret_seed(z).is_err());
for text in [z, c, g, s] {
assert!(decode_fixed::<32>(text).is_err());
}
assert!(parse_public_key(&g.to_lowercase()).is_err());
assert!(parse_address(&format!("{z}=")).is_err());
assert!(parse_secret_seed(&"ab".repeat(32)).is_err());
let invalid_secret = "SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASECRET";
let error = parse_secret_seed(invalid_secret).unwrap_err();
assert!(!format!("{error:?} {error}").contains(invalid_secret));
}
#[test]
fn z_address_vectors_preserve_bytes_and_checksum_the_version() {
for (bytes, expected) in [
(
[0; 32],
"ZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ4L",
),
(
[1; 32],
"ZAAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQDWEJ",
),
(
[255; 32],
"ZD777777777777777777777777777777777777777777777777777UYP",
),
(
std::array::from_fn(|index| u8::try_from(index).unwrap()),
"ZAAACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB7GCZ",
),
] {
assert_eq!(address_strkey(&bytes), expected);
assert_eq!(parse_address(expected).unwrap(), bytes);
}
let valid = address_strkey(&[0; 32]);
let wrong_version = stellar_strkey::convert::encode::<32, 35, 56>(201, &[0; 32]);
let short = stellar_strkey::convert::encode::<31, 34, 55>(200, &[0; 31]);
let long = stellar_strkey::convert::encode::<33, 36, 58>(200, &[0; 33]);
for text in [
valid.to_lowercase(),
format!(" {valid}"),
format!("{valid}\n"),
format!("{}A", &valid[..55]),
format!(
"Z{}",
&"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4"[1..]
),
wrong_version.to_string(),
short.to_string(),
long.to_string(),
] {
assert!(parse_address(&text).is_err(), "accepted {text}");
}
}
#[test]
fn base58_is_not_case_normalized_or_detected_as_hex() {
assert_ne!(decode("ab", 2).unwrap(), decode("AB", 2).unwrap());
assert_eq!(decode("ff", 2).unwrap(), [8, 194]);
for byte in 0..=255 {
let bytes = [byte; 32];
let encoded = encode(&bytes);
assert_eq!(decode_fixed::<32>(&encoded).unwrap(), bytes);
assert_eq!(parse_address(&encoded).unwrap(), bytes);
assert_eq!(parse_public_key(&encoded).unwrap(), bytes);
assert_eq!(*parse_secret_seed(&encoded).unwrap(), bytes);
}
}