use crate::b64::decode_int;
use crate::core::counter::CounterCodeV1;
use crate::core::counter::CounterCodeV2;
use crate::core::indexer::Indexer;
use crate::core::indexer::IndexerBuilder;
use crate::core::indexer::code::IndexedSigCode;
use crate::core::indexer::code::hardage;
use crate::core::indexer::xizage::XizageSize;
use crate::core::matter::Matter;
use crate::core::matter::builder::MatterBuilder;
use crate::core::matter::code::DigestCode;
use crate::core::matter::code::LabelerCode;
use crate::core::matter::code::MatterCode;
use crate::core::matter::code::NoncerCode;
use crate::core::matter::code::NumberCode;
use crate::core::matter::code::SignatureCode;
use crate::core::matter::code::TexterCode;
use crate::core::matter::code::VerKeyCode;
use crate::core::matter::code::VerserCode;
use crate::core::matter::error::MatterBuildError;
use crate::core::matter::sizage::SizeType;
use crate::core::primitives::Cigar;
use crate::core::primitives::Diger;
use crate::core::primitives::Labeler;
use crate::core::primitives::Noncer;
use crate::core::primitives::Number;
use crate::core::primitives::Prefixer;
use crate::core::primitives::Saider;
use crate::core::primitives::Siger;
use crate::core::primitives::Texter;
use crate::core::primitives::Verfer;
use crate::core::primitives::Verser;
#[cfg(feature = "alloc")]
#[allow(
unused_imports,
reason = "alloc prelude items; subset used per cfg/feature combination"
)]
use alloc::{borrow::ToOwned, format, string::ToString, vec, vec::Vec};
use crate::stream::error::ParseError;
pub(crate) fn parse_matter(
input: &[u8],
) -> Result<(Matter<'static, MatterCode>, &[u8]), ParseError> {
let code = MatterCode::from_base64_stream(input)?;
let sizage = code.get_sizage();
let hs = sizage.hs();
let ss = sizage.ss();
let cs = hs + ss;
let fs = if let SizeType::Fixed(n) = sizage.fs() {
usize::from(*n)
} else {
if input.len() < cs {
return Err(ParseError::NeedBytes(cs - input.len()));
}
let soft = core::str::from_utf8(&input[hs..cs])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in soft field".into()))?;
let xs = sizage.xs();
let soft_value = &soft[xs..];
let size: usize = decode_int(soft_value)?;
(size * 4) + cs
};
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
let matter = MatterBuilder::new()
.from_qualified_base64(&input[..fs])
.map_err(|err| match err {
MatterBuildError::Parsing(pe) => ParseError::from(pe),
MatterBuildError::Validation(ve) => ParseError::from(ve),
})?
.into_static();
Ok((matter, &input[fs..]))
}
pub(crate) fn parse_indexer(input: &[u8]) -> Result<(Indexer<'static>, &[u8]), ParseError> {
if input.is_empty() {
return Err(ParseError::NeedBytes(1));
}
let (indexer, consumed) = IndexerBuilder::new()
.from_qb64(input)
.map_err(ParseError::from)?;
Ok((indexer, &input[consumed..]))
}
fn extract_hard(input: &[u8]) -> Result<(&str, usize), ParseError> {
if input.is_empty() {
return Err(ParseError::NeedBytes(1));
}
if input[0] != b'-' {
return Err(ParseError::Malformed(format!(
"expected counter '-', got '{}'",
char::from(input[0])
)));
}
let hs = if input.len() >= 2 {
match input[1] {
b'-' => 3,
b'_' => 5,
_ => 2,
}
} else {
return Err(ParseError::NeedBytes(1));
};
if input.len() < hs {
return Err(ParseError::NeedBytes(hs - input.len()));
}
let hard = core::str::from_utf8(&input[..hs])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in counter".into()))?;
Ok((hard, hs))
}
#[allow(dead_code, reason = "used by upcoming Bytes-backed group refactor")]
pub(crate) fn skip_matter(input: &[u8]) -> Result<usize, ParseError> {
let code = MatterCode::from_base64_stream(input)?;
let sizage = code.get_sizage();
let hs = sizage.hs();
let ss = sizage.ss();
let cs = hs + ss;
let fs = if let SizeType::Fixed(n) = sizage.fs() {
usize::from(*n)
} else {
if input.len() < cs {
return Err(ParseError::NeedBytes(cs - input.len()));
}
let soft = core::str::from_utf8(&input[hs..cs])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in soft field".into()))?;
let xs = sizage.xs();
let soft_value = &soft[xs..];
let size: usize = decode_int(soft_value)?;
(size * 4) + cs
};
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
Ok(fs)
}
pub(crate) fn skip_indexer(input: &[u8]) -> Result<usize, ParseError> {
let &first_byte = input.first().ok_or(ParseError::NeedBytes(1))?;
let first_char = char::from(first_byte);
let hard_size = hardage(first_char).ok_or_else(|| {
ParseError::Malformed(format!("unknown indexer code lead: '{first_char}'"))
})?;
if input.len() < hard_size {
return Err(ParseError::NeedBytes(hard_size - input.len()));
}
let hard = core::str::from_utf8(&input[..hard_size])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in indexer hard field".into()))?;
let code = IndexedSigCode::from_hard(hard)
.map_err(|e| ParseError::Malformed(format!("unknown indexer code: {e}")))?;
let xizage = code.get_xizage();
let hs = usize::from(xizage.hs);
let ss = usize::from(xizage.ss);
let cs = hs + ss;
let fs = match xizage.fs {
XizageSize::Fixed(n) => usize::from(n),
XizageSize::Variable => {
if input.len() < cs {
return Err(ParseError::NeedBytes(cs - input.len()));
}
let os = usize::from(xizage.os);
let ms = ss - os;
let index_str = core::str::from_utf8(&input[hs..hs + ms])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in indexer soft".into()))?;
let index: usize = decode_int(index_str)?;
index * 4 + cs
}
};
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
Ok(fs)
}
#[allow(dead_code, reason = "used by upcoming Bytes-backed group refactor")]
pub(crate) fn skip_counter(input: &[u8]) -> Result<usize, ParseError> {
let (hard, hs) = extract_hard(input)?;
let ss = if let Ok(code) = CounterCodeV1::from_hard(hard) {
code.soft_size()
} else if let Ok(code) = CounterCodeV2::from_hard(hard) {
code.soft_size()
} else {
return Err(ParseError::UnknownCounterCode(hard.to_owned()));
};
let fs = hs + ss;
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
Ok(fs)
}
pub(crate) fn parse_counter(input: &[u8]) -> Result<(CounterCodeV1, u32, &[u8]), ParseError> {
let (hard, hs) = extract_hard(input)?;
let code = CounterCodeV1::from_hard(hard)?;
let ss = code.soft_size();
let fs = hs + ss;
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
let count_str = core::str::from_utf8(&input[hs..fs])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in counter soft field".into()))?;
let count: u32 = decode_int(count_str)?;
Ok((code, count, &input[fs..]))
}
pub(crate) fn parse_counter_v2(input: &[u8]) -> Result<(CounterCodeV2, u32, &[u8]), ParseError> {
let (hard, hs) = extract_hard(input)?;
let code = CounterCodeV2::from_hard(hard)?;
let ss = code.soft_size();
let fs = hs + ss;
if input.len() < fs {
return Err(ParseError::NeedBytes(fs - input.len()));
}
let count_str = core::str::from_utf8(&input[hs..fs])
.map_err(|_| ParseError::Malformed("invalid UTF-8 in counter soft field".into()))?;
let count: u32 = decode_int(count_str)?;
Ok((code, count, &input[fs..]))
}
pub(crate) fn parse_verfer(input: &[u8]) -> Result<(Verfer<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let verfer = matter
.narrow::<VerKeyCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "VerKeyCode",
got: e.to_string(),
})?;
Ok((verfer, rest))
}
pub(crate) fn parse_prefixer(input: &[u8]) -> Result<(Prefixer<'static>, &[u8]), ParseError> {
parse_verfer(input)
}
pub(crate) fn parse_diger(input: &[u8]) -> Result<(Diger<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let diger = matter
.narrow::<DigestCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "DigestCode",
got: e.to_string(),
})?;
Ok((diger, rest))
}
pub(crate) fn parse_saider(input: &[u8]) -> Result<(Saider<'static>, &[u8]), ParseError> {
parse_diger(input)
}
pub(crate) fn parse_cigar(input: &[u8]) -> Result<(Cigar<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let cigar = matter
.narrow::<SignatureCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "SignatureCode",
got: e.to_string(),
})?;
Ok((cigar, rest))
}
pub(crate) fn parse_siger(input: &[u8]) -> Result<(Siger<'static>, &[u8]), ParseError> {
let (indexer, rest) = parse_indexer(input)?;
Ok((Siger::new(indexer), rest))
}
pub(crate) fn parse_verser(input: &[u8]) -> Result<(Verser<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let verser = matter
.narrow::<VerserCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "VerserCode",
got: e.to_string(),
})?;
Ok((verser, rest))
}
pub(crate) fn parse_noncer(input: &[u8]) -> Result<(Noncer<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let noncer = matter
.narrow::<NoncerCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "NoncerCode",
got: e.to_string(),
})?;
Ok((noncer, rest))
}
pub(crate) fn parse_labeler(input: &[u8]) -> Result<(Labeler<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let labeler = matter
.narrow::<LabelerCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "LabelerCode",
got: e.to_string(),
})?;
Ok((labeler, rest))
}
pub(crate) fn parse_texter(input: &[u8]) -> Result<(Texter<'static>, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let texter = matter
.narrow::<TexterCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "TexterCode",
got: e.to_string(),
})?;
Ok((texter, rest))
}
pub(crate) fn parse_number(input: &[u8]) -> Result<(Number, &[u8]), ParseError> {
let (matter, rest) = parse_matter(input)?;
let narrowed = matter
.narrow::<NumberCode>()
.map_err(|e| ParseError::UnexpectedCodeType {
expected: "NumberCode",
got: e.to_string(),
})?;
let raw = narrowed.raw();
let mut value: u128 = 0;
for &byte in raw {
value = (value << 8) | u128::from(byte);
}
Ok((Number::with_code(*narrowed.code(), value), rest))
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::as_conversions,
reason = "test code: panics and type conversions acceptable"
)]
mod tests {
use super::*;
use crate::core::indexer::code::IndexedSigCode;
use core::num::NonZeroUsize;
fn expect_err<T>(result: Result<T, ParseError>) -> ParseError {
assert!(result.is_err(), "expected Err, got Ok");
match result {
Err(e) => e,
Ok(_) => unreachable!(),
}
}
fn build_ed25519_qb64() -> Vec<u8> {
use base64::{Engine, engine::general_purpose as b64};
let raw = [0xAB_u8; 32];
let ps = 1_usize;
let mut padded = vec![0u8; ps];
padded.extend_from_slice(&raw);
let payload_b64 = b64::URL_SAFE_NO_PAD.encode(&padded);
format!("D{}", &payload_b64[ps..]).into_bytes()
}
fn build_blake3_256_qb64() -> Vec<u8> {
use base64::{Engine, engine::general_purpose as b64};
let raw = [0xCD_u8; 32];
let ps = 1_usize;
let mut padded = vec![0u8; ps];
padded.extend_from_slice(&raw);
let payload_b64 = b64::URL_SAFE_NO_PAD.encode(&padded);
format!("E{}", &payload_b64[ps..]).into_bytes()
}
fn build_ed25519_sig_qb64() -> Vec<u8> {
use base64::{Engine, engine::general_purpose as b64};
let raw = [0xAB_u8; 64];
let ps = 2_usize;
let mut padded = vec![0u8; ps];
padded.extend_from_slice(&raw);
let payload_b64 = b64::URL_SAFE_NO_PAD.encode(&padded);
format!("0B{}", &payload_b64[ps..]).into_bytes()
}
fn build_indexer_qb64() -> (Vec<u8>, IndexedSigCode, u32) {
let code = IndexedSigCode::Ed25519;
let index = 3_u32;
let raw = [0u8; 64];
let indexer = IndexerBuilder::new()
.with_code(code)
.with_index(index)
.unwrap()
.with_raw(&raw[..])
.unwrap();
let qb64 = indexer.to_qb64();
(qb64.into_bytes(), code, index)
}
fn build_counter_qb64(code: CounterCodeV1, count: u32) -> Vec<u8> {
let hard = code.as_str();
let ss = code.soft_size();
let ss_nz = NonZeroUsize::new(ss).unwrap();
let soft = crate::b64::encode_int(count, ss_nz);
format!("{hard}{soft}").into_bytes()
}
#[test]
fn parse_matter_ed25519_roundtrip() {
let qb64 = build_ed25519_qb64();
let (matter, rest) = parse_matter(&qb64).unwrap();
assert_eq!(*matter.code(), MatterCode::Ed25519);
assert_eq!(matter.raw(), &[0xAB_u8; 32]);
assert!(rest.is_empty());
}
#[test]
fn parse_matter_with_trailing_bytes() {
let mut qb64 = build_ed25519_qb64();
qb64.extend_from_slice(b"TRAILING");
let (matter, rest) = parse_matter(&qb64).unwrap();
assert_eq!(*matter.code(), MatterCode::Ed25519);
assert_eq!(rest, b"TRAILING");
}
#[test]
fn parse_matter_need_bytes_empty() {
let result = parse_matter(b"");
assert!(result.is_err());
let err = expect_err(result);
assert!(matches!(err, ParseError::NeedBytes(_)));
}
#[test]
fn parse_matter_need_bytes_truncated() {
let qb64 = build_ed25519_qb64();
let result = parse_matter(&qb64[..10]);
assert!(result.is_err());
let err = expect_err(result);
assert!(matches!(err, ParseError::NeedBytes(34)));
}
#[test]
fn parse_matter_ed25519_sig() {
let qb64 = build_ed25519_sig_qb64();
assert_eq!(qb64.len(), 88);
let (matter, rest) = parse_matter(&qb64).unwrap();
assert_eq!(*matter.code(), MatterCode::Ed25519Sig);
assert_eq!(matter.raw().len(), 64);
assert!(rest.is_empty());
}
#[test]
fn parse_matter_unknown_code() {
let result = parse_matter(b"!AAAAAAA");
assert!(result.is_err());
}
#[test]
fn parse_matter_blake3_256() {
let qb64 = build_blake3_256_qb64();
assert_eq!(qb64.len(), 44);
let (matter, rest) = parse_matter(&qb64).unwrap();
assert_eq!(*matter.code(), MatterCode::Blake3_256);
assert_eq!(matter.raw(), &[0xCD_u8; 32]);
assert!(rest.is_empty());
}
#[test]
fn parse_indexer_roundtrip() {
let (qb64, code, index) = build_indexer_qb64();
let (indexer, rest) = parse_indexer(&qb64).unwrap();
assert_eq!(indexer.code(), code);
assert_eq!(indexer.index(), index);
assert_eq!(indexer.raw().len(), 64);
assert!(rest.is_empty());
}
#[test]
fn parse_indexer_with_trailing_bytes() {
let (mut qb64, _, _) = build_indexer_qb64();
qb64.extend_from_slice(b"EXTRA");
let (_, rest) = parse_indexer(&qb64).unwrap();
assert_eq!(rest, b"EXTRA");
}
#[test]
fn parse_indexer_need_bytes_empty() {
let result = parse_indexer(b"");
assert!(result.is_err());
let err = expect_err(result);
assert!(matches!(err, ParseError::NeedBytes(1)));
}
#[test]
fn parse_indexer_need_bytes_truncated() {
let (qb64, _, _) = build_indexer_qb64();
let result = parse_indexer(&qb64[..4]);
assert!(result.is_err());
let err = expect_err(result);
assert!(matches!(err, ParseError::NeedBytes(_)));
}
#[test]
fn parse_counter_standard() {
let qb64 = build_counter_qb64(CounterCodeV1::ControllerIdxSigs, 2);
assert_eq!(qb64.len(), 4);
let (code, count, rest) = parse_counter(&qb64).unwrap();
assert_eq!(code, CounterCodeV1::ControllerIdxSigs);
assert_eq!(count, 2);
assert!(rest.is_empty());
}
#[test]
fn parse_counter_big_variant() {
let qb64 = build_counter_qb64(CounterCodeV1::BigAttachmentGroup, 100);
assert_eq!(qb64.len(), 8);
let (code, count, rest) = parse_counter(&qb64).unwrap();
assert_eq!(code, CounterCodeV1::BigAttachmentGroup);
assert_eq!(count, 100);
assert!(rest.is_empty());
}
#[test]
fn parse_counter_with_trailing_data() {
let mut qb64 = build_counter_qb64(CounterCodeV1::WitnessIdxSigs, 5);
qb64.extend_from_slice(b"MORESTUFF");
let (code, count, rest) = parse_counter(&qb64).unwrap();
assert_eq!(code, CounterCodeV1::WitnessIdxSigs);
assert_eq!(count, 5);
assert_eq!(rest, b"MORESTUFF");
}
#[test]
fn parse_counter_need_bytes_empty() {
let result = parse_counter(b"");
assert!(result.is_err());
assert!(matches!(expect_err(result), ParseError::NeedBytes(1)));
}
#[test]
fn parse_counter_need_bytes_one_byte() {
let result = parse_counter(b"-");
assert!(result.is_err());
assert!(matches!(expect_err(result), ParseError::NeedBytes(1)));
}
#[test]
fn parse_counter_need_bytes_short_soft() {
let result = parse_counter(b"-AB");
assert!(result.is_err());
assert!(matches!(expect_err(result), ParseError::NeedBytes(1)));
}
#[test]
fn parse_counter_error_non_counter_input() {
let result = parse_counter(b"AABC");
assert!(result.is_err());
let err = expect_err(result);
assert!(matches!(err, ParseError::Malformed(_)));
}
#[test]
fn parse_counter_unknown_code() {
let result = parse_counter(b"-JAB");
assert!(result.is_err());
assert!(matches!(
expect_err(result),
ParseError::UnknownCounterCode(_)
));
}
#[test]
fn parse_counter_zero_count() {
let qb64 = build_counter_qb64(CounterCodeV1::ControllerIdxSigs, 0);
let (code, count, rest) = parse_counter(&qb64).unwrap();
assert_eq!(code, CounterCodeV1::ControllerIdxSigs);
assert_eq!(count, 0);
assert!(rest.is_empty());
}
#[test]
fn parse_verfer_success() {
let qb64 = build_ed25519_qb64();
let (verfer, rest) = parse_verfer(&qb64).unwrap();
assert_eq!(*verfer.code(), VerKeyCode::Ed25519);
assert_eq!(verfer.raw(), &[0xAB_u8; 32]);
assert!(rest.is_empty());
}
#[test]
fn parse_verfer_wrong_code_type() {
let qb64 = build_blake3_256_qb64();
let result = parse_verfer(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "VerKeyCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_prefixer_success() {
let qb64 = build_ed25519_qb64();
let (prefixer, rest) = parse_prefixer(&qb64).unwrap();
assert_eq!(*prefixer.code(), VerKeyCode::Ed25519);
assert!(rest.is_empty());
}
#[test]
fn parse_diger_success() {
let qb64 = build_blake3_256_qb64();
let (diger, rest) = parse_diger(&qb64).unwrap();
assert_eq!(*diger.code(), DigestCode::Blake3_256);
assert!(rest.is_empty());
}
#[test]
fn parse_diger_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_diger(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "DigestCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_saider_success() {
let qb64 = build_blake3_256_qb64();
let (saider, rest) = parse_saider(&qb64).unwrap();
assert_eq!(*saider.code(), DigestCode::Blake3_256);
assert!(rest.is_empty());
}
#[test]
fn parse_cigar_success() {
let qb64 = build_ed25519_sig_qb64();
let (cigar, rest) = parse_cigar(&qb64).unwrap();
assert_eq!(*cigar.code(), SignatureCode::Ed25519Sig);
assert!(rest.is_empty());
}
#[test]
fn parse_cigar_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_cigar(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "SignatureCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_siger_roundtrip() {
let (qb64, code, index) = build_indexer_qb64();
let (siger, rest) = parse_siger(&qb64).unwrap();
assert_eq!(siger.code(), code);
assert_eq!(siger.index(), index);
assert_eq!(siger.raw().len(), 64);
assert!(siger.verfer().is_none());
assert!(rest.is_empty());
}
#[test]
fn parse_siger_with_trailing_bytes() {
let (mut qb64, _, _) = build_indexer_qb64();
qb64.extend_from_slice(b"TRAIL");
let (_, rest) = parse_siger(&qb64).unwrap();
assert_eq!(rest, b"TRAIL");
}
#[test]
fn parse_siger_need_bytes_empty() {
let result = parse_siger(b"");
assert!(result.is_err());
assert!(matches!(expect_err(result), ParseError::NeedBytes(1)));
}
#[test]
fn parse_verser_tag7_success() {
let qb64 = b"YAAAAAAA";
let (verser, rest) = parse_verser(qb64).unwrap();
assert_eq!(*verser.code(), VerserCode::Tag7);
assert!(verser.raw().is_empty());
assert!(rest.is_empty());
}
#[test]
fn parse_verser_tag10_success() {
let qb64 = b"0OAAAAAAAAAA";
let (verser, rest) = parse_verser(qb64).unwrap();
assert_eq!(*verser.code(), VerserCode::Tag10);
assert!(rest.is_empty());
}
#[test]
fn parse_verser_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_verser(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "VerserCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_noncer_blake3_256_success() {
let qb64 = build_blake3_256_qb64();
let (noncer, rest) = parse_noncer(&qb64).unwrap();
assert_eq!(*noncer.code(), NoncerCode::Blake3_256);
assert_eq!(noncer.raw(), &[0xCD_u8; 32]);
assert!(rest.is_empty());
}
#[test]
fn parse_noncer_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_noncer(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "NoncerCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_labeler_tag3_success() {
let qb64 = b"XAAA";
let (labeler, rest) = parse_labeler(qb64).unwrap();
assert_eq!(*labeler.code(), LabelerCode::Tag3);
assert!(labeler.raw().is_empty());
assert!(rest.is_empty());
}
#[test]
fn parse_labeler_tag7_success() {
let qb64 = b"YAAAAAAA";
let (labeler, rest) = parse_labeler(qb64).unwrap();
assert_eq!(*labeler.code(), LabelerCode::Tag7);
assert!(rest.is_empty());
}
#[test]
fn parse_labeler_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_labeler(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "LabelerCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_texter_bytes_l0_success() {
let qb64 = b"4BACW19uJT6H";
let (texter, rest) = parse_texter(qb64).unwrap();
assert_eq!(*texter.code(), TexterCode::Bytes_L0);
assert_eq!(texter.raw(), &[0x5b, 0x5f, 0x6e, 0x25, 0x3e, 0x87]);
assert!(rest.is_empty());
}
#[test]
fn parse_texter_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_texter(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "TexterCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn parse_number_short_success() {
let qb64 = b"MAAB";
let (number, rest) = parse_number(qb64).unwrap();
assert_eq!(*number.code(), crate::core::matter::code::NumberCode::Short);
assert_eq!(number.value(), 1);
assert!(rest.is_empty());
}
#[test]
fn parse_number_short_value_five() {
let qb64 = b"MAAF";
let (number, rest) = parse_number(qb64).unwrap();
assert_eq!(*number.code(), crate::core::matter::code::NumberCode::Short);
assert_eq!(number.value(), 5);
assert!(rest.is_empty());
}
#[test]
fn parse_number_with_trailing_bytes() {
let mut qb64 = b"MAAB".to_vec();
qb64.extend_from_slice(b"EXTRA");
let (number, rest) = parse_number(&qb64).unwrap();
assert_eq!(number.value(), 1);
assert_eq!(rest, b"EXTRA");
}
#[test]
fn parse_number_wrong_code_type() {
let qb64 = build_ed25519_qb64();
let result = parse_number(&qb64);
assert!(result.is_err());
match expect_err(result) {
ParseError::UnexpectedCodeType { expected, .. } => {
assert_eq!(expected, "NumberCode");
}
other => unreachable!("expected UnexpectedCodeType, got {other:?}"),
}
}
#[test]
fn keripy_verser_keri_v2() {
let qb64 = b"YKERICAA";
let (verser, rest) = parse_verser(qb64).unwrap();
assert_eq!(*verser.code(), VerserCode::Tag7);
assert!(rest.is_empty());
assert_eq!(verser.soft(), "KERICAA");
}
#[test]
fn keripy_verser_all_zero_soft() {
let qb64 = b"YAAAAAAA";
let (verser, rest) = parse_verser(qb64).unwrap();
assert_eq!(*verser.code(), VerserCode::Tag7);
assert!(rest.is_empty());
assert_eq!(verser.soft(), "AAAAAAA");
}
#[test]
fn keripy_texter_empty() {
let qb64 = b"4BAA";
let (texter, rest) = parse_texter(qb64).unwrap();
assert_eq!(*texter.code(), TexterCode::Bytes_L0);
assert!(texter.raw().is_empty());
assert!(rest.is_empty());
}
#[test]
fn keripy_texter_6_bytes() {
let qb64 = b"4BACW19uJT6H";
let (texter, rest) = parse_texter(qb64).unwrap();
assert_eq!(*texter.code(), TexterCode::Bytes_L0);
assert_eq!(texter.raw(), &[0x5b, 0x5f, 0x6e, 0x25, 0x3e, 0x87]);
assert!(rest.is_empty());
}
#[test]
fn keripy_number_zero() {
let qb64 = b"MAAA";
let (number, rest) = parse_number(qb64).unwrap();
assert_eq!(*number.code(), crate::core::matter::code::NumberCode::Short);
assert_eq!(number.value(), 0);
assert!(rest.is_empty());
}
#[test]
fn keripy_number_256() {
let qb64 = b"MAEA";
let (number, rest) = parse_number(qb64).unwrap();
assert_eq!(number.value(), 256);
assert!(rest.is_empty());
}
#[test]
fn keripy_labeler_tag3_empty() {
let qb64 = b"XAAA";
let (labeler, rest) = parse_labeler(qb64).unwrap();
assert_eq!(*labeler.code(), LabelerCode::Tag3);
assert!(labeler.raw().is_empty());
assert_eq!(labeler.soft(), "AAA");
assert!(rest.is_empty());
}
#[test]
fn keripy_noncer_blake3_256() {
let qb64 = build_blake3_256_qb64();
let (noncer, rest) = parse_noncer(&qb64).unwrap();
assert_eq!(*noncer.code(), NoncerCode::Blake3_256);
assert_eq!(noncer.raw(), &[0xCD_u8; 32]);
assert!(rest.is_empty());
}
#[test]
fn parse_counter_v2_seal_group_codes() {
use crate::core::counter::CounterCodeV2;
let seal_codes = [
CounterCodeV2::DigestSealSingles,
CounterCodeV2::MerkleRootSealSingles,
CounterCodeV2::SealSourceLastSingles,
CounterCodeV2::BackerRegistrarSealCouples,
CounterCodeV2::TypedDigestSealCouples,
CounterCodeV2::BlindedStateQuadruples,
CounterCodeV2::BoundStateSextuples,
CounterCodeV2::TypedMediaQuadruples,
];
for code in seal_codes {
let hard = code.as_str();
let ss = code.soft_size();
let ss_nz = NonZeroUsize::new(ss).unwrap();
let count = 7_u32;
let soft = crate::b64::encode_int(count, ss_nz);
let qb64 = format!("{hard}{soft}");
let (parsed_code, parsed_count, rest) = parse_counter_v2(qb64.as_bytes()).unwrap();
assert_eq!(parsed_code, code, "code mismatch for {hard}");
assert_eq!(parsed_count, count, "count mismatch for {hard}");
assert!(rest.is_empty(), "trailing bytes for {hard}");
}
}
#[test]
fn skip_matter_ed25519() {
let qb64 = build_ed25519_qb64();
assert_eq!(skip_matter(&qb64).unwrap(), 44);
}
#[test]
fn skip_matter_ed25519_with_trailing() {
let mut qb64 = build_ed25519_qb64();
qb64.extend_from_slice(b"TRAILING");
assert_eq!(skip_matter(&qb64).unwrap(), 44);
}
#[test]
fn skip_matter_blake3_256() {
let qb64 = build_blake3_256_qb64();
assert_eq!(skip_matter(&qb64).unwrap(), 44);
}
#[test]
fn skip_matter_ed25519_sig() {
let qb64 = build_ed25519_sig_qb64();
assert_eq!(skip_matter(&qb64).unwrap(), 88);
}
#[test]
fn skip_matter_need_bytes_empty() {
let err = expect_err(skip_matter(b""));
assert!(matches!(err, ParseError::NeedBytes(_)));
}
#[test]
fn skip_matter_need_bytes_truncated() {
let qb64 = build_ed25519_qb64();
let err = expect_err(skip_matter(&qb64[..10]));
assert!(matches!(err, ParseError::NeedBytes(34)));
}
#[test]
fn skip_matter_variable_size_texter() {
let qb64 = b"4BACW19uJT6H";
assert_eq!(skip_matter(qb64).unwrap(), 12);
}
#[test]
fn skip_matter_variable_size_texter_empty() {
let qb64 = b"4BAA";
assert_eq!(skip_matter(qb64).unwrap(), 4);
}
#[test]
fn skip_indexer_ed25519() {
let (qb64, _, _) = build_indexer_qb64();
assert_eq!(skip_indexer(&qb64).unwrap(), qb64.len());
}
#[test]
fn skip_indexer_with_trailing() {
let (mut qb64, _, _) = build_indexer_qb64();
let expected_len = qb64.len();
qb64.extend_from_slice(b"EXTRA");
assert_eq!(skip_indexer(&qb64).unwrap(), expected_len);
}
#[test]
fn skip_indexer_need_bytes_empty() {
let err = expect_err(skip_indexer(b""));
assert!(matches!(err, ParseError::NeedBytes(1)));
}
#[test]
fn skip_counter_standard() {
let qb64 = build_counter_qb64(CounterCodeV1::ControllerIdxSigs, 2);
assert_eq!(skip_counter(&qb64).unwrap(), 4);
}
#[test]
fn skip_counter_big() {
let qb64 = build_counter_qb64(CounterCodeV1::BigAttachmentGroup, 100);
assert_eq!(skip_counter(&qb64).unwrap(), 8);
}
}