use core::fmt;
use core::str::FromStr;
use super::Error;
use super::base64;
use super::error::Syntax;
use crate::model::LedgerId;
pub(crate) const SIGNATURE_PREFIX: &str = "\u{2014} ";
pub const MAX_NOTE_BYTES: usize = 1_000_000;
pub const MAX_SIGNATURES: usize = 100;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyName(String);
impl KeyName {
pub const MAX_BYTES: usize = 255;
pub fn new(name: impl Into<String>) -> Result<Self, Error> {
let name = name.into();
if is_valid_name(&name) {
Ok(Self(name))
} else {
Err(Error::BadName)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
pub(crate) fn is_valid_name(name: &str) -> bool {
(1..=KeyName::MAX_BYTES).contains(&name.len())
&& !name
.chars()
.any(|c| c.is_whitespace() || c == '+' || c.is_control())
}
impl From<&LedgerId> for KeyName {
fn from(ledger: &LedgerId) -> Self {
Self(ledger.as_str().to_owned())
}
}
impl fmt::Display for KeyName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl FromStr for KeyName {
type Err = Error;
fn from_str(text: &str) -> Result<Self, Self::Err> {
Self::new(text)
}
}
impl AsRef<str> for KeyName {
fn as_ref(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NoteSignature {
name: KeyName,
key_id: [u8; 4],
signature: Vec<u8>,
}
impl NoteSignature {
pub(crate) fn new(name: KeyName, key_id: [u8; 4], signature: Vec<u8>) -> Self {
Self {
name,
key_id,
signature,
}
}
#[must_use]
pub const fn name(&self) -> &KeyName {
&self.name
}
#[must_use]
pub const fn key_id(&self) -> [u8; 4] {
self.key_id
}
#[must_use]
pub fn signature(&self) -> &[u8] {
&self.signature
}
#[must_use]
pub fn to_line(&self) -> String {
let mut raw = Vec::with_capacity(self.signature.len().saturating_add(4));
raw.extend_from_slice(&self.key_id);
raw.extend_from_slice(&self.signature);
format!("{SIGNATURE_PREFIX}{} {}\n", self.name, base64::encode(&raw))
}
pub(crate) fn parse_line(line: &str, line_no: u32) -> Result<Self, Error> {
let malformed = |what| Error::Malformed {
what,
line: line_no,
};
let rest = line
.strip_prefix(SIGNATURE_PREFIX)
.ok_or(malformed(Syntax::SignatureLine))?;
let (name, encoded) = rest
.split_once(' ')
.ok_or(malformed(Syntax::SignatureLine))?;
let name = KeyName::new(name)?;
let raw = base64::decode(encoded).ok_or(malformed(Syntax::Base64))?;
match raw.split_first_chunk::<4>() {
Some((key_id, signature)) if !signature.is_empty() => {
Ok(Self::new(name, *key_id, signature.to_vec()))
}
_ => Err(malformed(Syntax::SignatureLine)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Note {
text: String,
signatures: Vec<NoteSignature>,
}
impl Note {
#[cfg(feature = "witness")]
pub(crate) fn new(text: String, signatures: Vec<NoteSignature>) -> Result<Self, Error> {
let mut bytes = text.into_bytes();
bytes.push(b'\n');
for signature in &signatures {
bytes.extend_from_slice(signature.to_line().as_bytes());
}
Self::parse(&bytes)
}
pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() > MAX_NOTE_BYTES {
return Err(Error::TooLarge {
limit: MAX_NOTE_BYTES,
});
}
let all = core::str::from_utf8(bytes).map_err(|error| Error::Malformed {
what: Syntax::Utf8,
line: line_of(bytes, error.valid_up_to()),
})?;
if let Some(at) = all.bytes().position(|byte| byte < 0x20 && byte != b'\n') {
return Err(Error::Malformed {
what: Syntax::ControlChar,
line: line_of(bytes, at),
});
}
if !all.ends_with('\n') {
return Err(Error::Malformed {
what: Syntax::MissingNewline,
line: line_of(bytes, bytes.len()),
});
}
let split = all.rfind("\n\n").ok_or(Error::Malformed {
what: Syntax::MissingSeparator,
line: line_of(bytes, bytes.len()),
})?;
let (text, rest) = all.split_at(split.saturating_add(1));
let block = rest.get(1..).unwrap_or_default();
let first = line_of(bytes, split.saturating_add(2));
if block.is_empty() {
return Err(Error::Malformed {
what: Syntax::NoSignature,
line: first,
});
}
let mut signatures: Vec<NoteSignature> = Vec::new();
for (offset, line) in block.split_terminator('\n').enumerate() {
let line_no = first.saturating_add(u32::try_from(offset).unwrap_or(u32::MAX));
if signatures.len() == MAX_SIGNATURES {
return Err(Error::TooManySignatures);
}
let signature = NoteSignature::parse_line(line, line_no)?;
if signatures
.iter()
.any(|seen| (&seen.name, seen.key_id) == (&signature.name, signature.key_id))
{
return Err(Error::DuplicateSignature {
name: signature.name,
key_id: signature.key_id,
});
}
signatures.push(signature);
}
Ok(Self {
text: text.to_owned(),
signatures,
})
}
#[must_use]
pub fn text(&self) -> &str {
&self.text
}
#[must_use]
pub fn signatures(&self) -> &[NoteSignature] {
&self.signatures
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(self.text.len().saturating_add(128));
bytes.extend_from_slice(self.text.as_bytes());
bytes.push(b'\n');
for signature in &self.signatures {
bytes.extend_from_slice(signature.to_line().as_bytes());
}
bytes
}
}
impl fmt::Display for Note {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.text)?;
f.write_str("\n")?;
for signature in &self.signatures {
f.write_str(&signature.to_line())?;
}
Ok(())
}
}
pub(crate) fn line_of(bytes: &[u8], at: usize) -> u32 {
let newlines = bytes
.get(..at)
.unwrap_or(bytes)
.iter()
.filter(|byte| **byte == b'\n')
.count();
u32::try_from(newlines.saturating_add(1)).unwrap_or(u32::MAX)
}