use crate::error::{DeserializeError, DeserializeFailure};
use crate::serialization::{fit_sz, Deserialize, LenEncoding, Serialize, StringEncoding};
use crate::Int;
use cbor_event::{de::Deserializer, se::Serializer};
use derivative::Derivative;
use std::io::{BufRead, Seek, Write};
pub type TransactionMetadatumLabel = u64;
pub const METADATA_MAX_LEN: usize = 64;
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
pub struct Metadata {
pub entries: Vec<(TransactionMetadatumLabel, TransactionMetadatum)>,
#[serde(skip)]
pub encodings: Option<MetadataEncoding>,
}
impl Metadata {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn set(&mut self, label: TransactionMetadatumLabel, datum: TransactionMetadatum) {
self.entries.retain(|(l, _)| *l != label);
self.entries.push((label, datum));
}
pub fn get(&self, label: TransactionMetadatumLabel) -> Option<&TransactionMetadatum> {
self.entries
.iter()
.find(|(l, _)| *l == label)
.map(|(_, md)| md)
}
pub fn get_all(&self, label: TransactionMetadatumLabel) -> Option<Vec<&TransactionMetadatum>> {
let matches = self
.entries
.iter()
.filter_map(|(l, md)| if *l == label { Some(md) } else { None })
.collect::<Vec<_>>();
if matches.is_empty() {
None
} else {
Some(matches)
}
}
}
#[derive(Clone, Debug, Default)]
pub struct MetadataEncoding {
pub len_encoding: LenEncoding,
pub label_encodings: Vec<cbor_event::Sz>,
}
impl Serialize for Metadata {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
force_canonical: bool,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_map_sz(
self.encodings
.as_ref()
.map(|encs| encs.len_encoding)
.unwrap_or_default()
.to_len_sz(self.entries.len() as u64, force_canonical),
)?;
let mut key_order = Vec::new();
for (i, (label, datum)) in self.entries.iter().enumerate() {
let mut buf = cbor_event::se::Serializer::new_vec();
let metadata_key_encoding = self
.encodings
.as_ref()
.and_then(|encs| encs.label_encodings.get(i))
.cloned();
buf.write_unsigned_integer_sz(
*label,
fit_sz(*label, metadata_key_encoding, force_canonical),
)?;
key_order.push((buf.finalize(), label, datum));
}
if force_canonical {
key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
match lhs_bytes.len().cmp(&rhs_bytes.len()) {
std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
diff_ord => diff_ord,
}
});
}
for (key_bytes, _key, value) in key_order {
serializer.write_raw_bytes(&key_bytes)?;
value.serialize(serializer, force_canonical)?;
}
self.encodings
.as_ref()
.map(|encs| encs.len_encoding)
.unwrap_or_default()
.end(serializer, force_canonical)
}
}
impl Deserialize for Metadata {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
let mut entries = Vec::new();
let len = raw.map_sz()?;
let len_encoding = len.into();
let mut label_encodings = Vec::new();
while match len {
cbor_event::LenSz::Len(n, _) => (entries.len() as u64) < n,
cbor_event::LenSz::Indefinite => true,
} {
if raw.cbor_type()? == cbor_event::Type::Special {
assert_eq!(raw.special()?, cbor_event::Special::Break);
break;
}
let (metadatum_label, label_encoding) = raw.unsigned_integer_sz()?;
let metadatum = TransactionMetadatum::deserialize(raw)?;
entries.push((metadatum_label, metadatum));
label_encodings.push(label_encoding);
}
Ok(Self {
entries,
encodings: Some(MetadataEncoding {
len_encoding,
label_encodings,
}),
})
}
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, schemars::JsonSchema, Derivative,
)]
#[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct MetadatumMap {
pub entries: Vec<(TransactionMetadatum, TransactionMetadatum)>,
#[serde(skip)]
#[derivative(
PartialEq = "ignore",
Ord = "ignore",
PartialOrd = "ignore",
Hash = "ignore"
)]
pub entries_encoding: LenEncoding,
}
impl MetadatumMap {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn set(&mut self, key: TransactionMetadatum, datum: TransactionMetadatum) {
self.entries.retain(|(k, _)| *k != key);
self.entries.push((key, datum));
}
pub fn get(&self, key: &TransactionMetadatum) -> Option<&TransactionMetadatum> {
self.entries
.iter()
.find(|(k, _)| *k == *key)
.map(|(_, md)| md)
}
pub fn get_all(&self, key: &TransactionMetadatum) -> Option<Vec<&TransactionMetadatum>> {
let matches = self
.entries
.iter()
.filter_map(|(k, md)| if *k == *key { Some(md) } else { None })
.collect::<Vec<_>>();
if matches.is_empty() {
None
} else {
Some(matches)
}
}
pub fn get_str(&self, key: &str) -> Option<&TransactionMetadatum> {
self.get(&TransactionMetadatum::new_text(key.to_owned()).ok()?)
}
}
impl Serialize for MetadatumMap {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
force_canonical: bool,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_map_sz(
self.entries_encoding
.to_len_sz(self.entries.len() as u64, force_canonical),
)?;
let mut key_order = self
.entries
.iter()
.map(|(k, v)| {
let mut buf = cbor_event::se::Serializer::new_vec();
k.serialize(&mut buf, force_canonical)?;
Ok((buf.finalize(), k, v))
})
.collect::<Result<Vec<(Vec<u8>, &_, &_)>, cbor_event::Error>>()?;
if force_canonical {
key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| {
match lhs_bytes.len().cmp(&rhs_bytes.len()) {
std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes),
diff_ord => diff_ord,
}
});
}
for (key_bytes, _key, value) in key_order {
serializer.write_raw_bytes(&key_bytes)?;
value.serialize(serializer, force_canonical)?;
}
self.entries_encoding.end(serializer, force_canonical)
}
}
impl Deserialize for MetadatumMap {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
let mut entries = Vec::new();
let entries_len = raw.map_sz()?;
let entries_encoding = entries_len.into();
while match entries_len {
cbor_event::LenSz::Len(n, _) => (entries.len() as u64) < n,
cbor_event::LenSz::Indefinite => true,
} {
if raw.cbor_type()? == cbor_event::Type::Special {
assert_eq!(raw.special()?, cbor_event::Special::Break);
break;
}
let key = TransactionMetadatum::deserialize(raw)?;
let value = TransactionMetadatum::deserialize(raw)?;
entries.push((key, value));
}
Ok(Self {
entries,
entries_encoding,
})
}
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, Derivative)]
#[derivative(
Eq,
PartialEq,
Ord = "feature_allow_slow_enum",
PartialOrd = "feature_allow_slow_enum",
Hash
)]
pub enum TransactionMetadatum {
Map(MetadatumMap),
List {
elements: Vec<TransactionMetadatum>,
#[derivative(
PartialEq = "ignore",
Ord = "ignore",
PartialOrd = "ignore",
Hash = "ignore"
)]
#[serde(skip)]
elements_encoding: LenEncoding,
},
Int(Int),
Bytes {
bytes: Vec<u8>,
#[derivative(
PartialEq = "ignore",
Ord = "ignore",
PartialOrd = "ignore",
Hash = "ignore"
)]
#[serde(skip)]
bytes_encoding: StringEncoding,
},
Text {
text: String,
#[derivative(
PartialEq = "ignore",
Ord = "ignore",
PartialOrd = "ignore",
Hash = "ignore"
)]
#[serde(skip)]
text_encoding: StringEncoding,
},
}
impl TransactionMetadatum {
pub fn new_map(map: MetadatumMap) -> Self {
Self::Map(map)
}
pub fn new_list(elements: Vec<TransactionMetadatum>) -> Self {
Self::List {
elements,
elements_encoding: LenEncoding::default(),
}
}
pub fn new_int(int: Int) -> Self {
Self::Int(int)
}
pub fn new_bytes(bytes: Vec<u8>) -> Result<Self, DeserializeError> {
if bytes.len() > METADATA_MAX_LEN {
return Err(DeserializeFailure::RangeCheck {
found: bytes.len() as isize,
min: None,
max: Some(METADATA_MAX_LEN as isize),
}
.into());
}
Ok(Self::Bytes {
bytes,
bytes_encoding: StringEncoding::default(),
})
}
pub fn new_text(text: String) -> Result<Self, DeserializeError> {
if text.len() > METADATA_MAX_LEN {
return Err(DeserializeFailure::RangeCheck {
found: text.len() as isize,
min: None,
max: Some(METADATA_MAX_LEN as isize),
}
.into());
}
Ok(Self::Text {
text,
text_encoding: StringEncoding::default(),
})
}
pub fn as_map(&self) -> Option<&MetadatumMap> {
match self {
Self::Map(map) => Some(map),
_ => None,
}
}
pub fn as_list(&self) -> Option<&Vec<TransactionMetadatum>> {
match self {
Self::List { elements, .. } => Some(elements),
_ => None,
}
}
pub fn as_int(&self) -> Option<&Int> {
match self {
Self::Int(x) => Some(x),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&Vec<u8>> {
match self {
Self::Bytes { bytes, .. } => Some(bytes),
_ => None,
}
}
pub fn as_text(&self) -> Option<&String> {
match self {
Self::Text { text, .. } => Some(text),
_ => None,
}
}
}
impl Serialize for TransactionMetadatum {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
force_canonical: bool,
) -> cbor_event::Result<&'se mut Serializer<W>> {
match self {
TransactionMetadatum::Map(map) => map.serialize(serializer, force_canonical),
TransactionMetadatum::List {
elements,
elements_encoding,
} => {
serializer.write_array_sz(
elements_encoding.to_len_sz(elements.len() as u64, force_canonical),
)?;
for element in elements.iter() {
element.serialize(serializer, force_canonical)?;
}
elements_encoding.end(serializer, force_canonical)
}
TransactionMetadatum::Int(int) => int.serialize(serializer, force_canonical),
TransactionMetadatum::Bytes {
bytes,
bytes_encoding,
} => serializer.write_bytes_sz(
bytes,
bytes_encoding.to_str_len_sz(bytes.len() as u64, force_canonical),
),
TransactionMetadatum::Text {
text,
text_encoding,
} => serializer.write_text_sz(
text,
text_encoding.to_str_len_sz(text.len() as u64, force_canonical),
),
}
}
}
impl Deserialize for TransactionMetadatum {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<_, DeserializeError> {
match raw.cbor_type()? {
cbor_event::Type::Map => MetadatumMap::deserialize(raw).map(Self::Map),
cbor_event::Type::Array => {
let mut elements_arr = Vec::new();
let len = raw.array_sz()?;
let elements_encoding = len.into();
while match len {
cbor_event::LenSz::Len(n, _) => (elements_arr.len() as u64) < n,
cbor_event::LenSz::Indefinite => true,
} {
if raw.cbor_type()? == cbor_event::Type::Special {
assert_eq!(raw.special()?, cbor_event::Special::Break);
break;
}
elements_arr.push(TransactionMetadatum::deserialize(raw)?);
}
Ok(Self::List {
elements: elements_arr,
elements_encoding,
})
}
cbor_event::Type::UnsignedInteger | cbor_event::Type::NegativeInteger => {
Int::deserialize(raw).map(Self::Int)
}
cbor_event::Type::Bytes => raw
.bytes_sz()
.map_err(Into::<DeserializeError>::into)
.and_then(|(bytes, enc)| {
if bytes.len() > METADATA_MAX_LEN {
Err(DeserializeFailure::RangeCheck {
found: bytes.len() as isize,
min: None,
max: Some(METADATA_MAX_LEN as isize),
}
.into())
} else {
Ok(Self::Bytes {
bytes,
bytes_encoding: StringEncoding::from(enc),
})
}
}),
cbor_event::Type::Text => raw
.text_sz()
.map_err(Into::<DeserializeError>::into)
.and_then(|(text, enc)| {
if text.len() > METADATA_MAX_LEN {
Err(DeserializeFailure::RangeCheck {
found: text.len() as isize,
min: None,
max: Some(METADATA_MAX_LEN as isize),
}
.into())
} else {
Ok(Self::Text {
text,
text_encoding: StringEncoding::from(enc),
})
}
}),
_ => Err(DeserializeFailure::NoVariantMatched.into()),
}
})()
.map_err(|e| e.annotate("TransactionMetadatum"))
}
}
pub fn encode_arbitrary_bytes_as_metadatum(bytes: &[u8]) -> TransactionMetadatum {
let mut list = Vec::new();
for chunk in bytes.chunks(METADATA_MAX_LEN) {
list.push(
TransactionMetadatum::new_bytes(chunk.to_vec())
.expect("this should never fail as we are already chunking it"),
);
}
TransactionMetadatum::new_list(list)
}
pub fn decode_arbitrary_bytes_from_metadatum(metadata: &TransactionMetadatum) -> Option<Vec<u8>> {
let mut bytes = Vec::new();
for elem in metadata.as_list()? {
bytes.extend(elem.as_bytes()?.iter());
}
Some(bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn metadata_duplicate_labels() {
let bytes_hex = "a219270fa16474657374747365636f6e64206d657461646174612066696c6519270fa16474657374736669727374206d657461646174612066696c65";
let md = Metadata::from_cbor_bytes(&hex::decode(bytes_hex).unwrap()).unwrap();
assert_eq!(bytes_hex, hex::encode(md.to_cbor_bytes()));
}
#[test]
fn metdatum_duplicate_keys() {
let bytes_hex = "a100a567536572766963656c4c4946542042616c6c6f7473685175657374696f6e6d536f6d65207175657374696f6e66417574686f7273736f6d652d677569642d686572652d736f6f6e64547970656653696e676c656743686f69636573a26643686f6963656b536f6d652043686f6963656643686f69636573536f6d6520416e6f746865722043686f696365";
let md = Metadata::from_cbor_bytes(&hex::decode(bytes_hex).unwrap()).unwrap();
assert_eq!(bytes_hex, hex::encode(md.to_cbor_bytes()));
}
#[test]
fn binary_encoding() {
let input_bytes = (0..1000).map(|x| x as u8).collect::<Vec<u8>>();
let metadata = encode_arbitrary_bytes_as_metadatum(input_bytes.as_ref());
let output_bytes = decode_arbitrary_bytes_from_metadatum(&metadata).expect("decode failed");
assert_eq!(input_bytes, output_bytes);
}
}