1use unsigned_varint::{decode, encode};
2
3use crate::error::{MaError, MaResult as Result};
4
5pub const CODEC_IDENTITY: u64 = 0x00;
9pub const CODEC_RAW: u64 = 0x55;
10pub const CODEC_CBOR: u64 = 0x51;
11pub const CODEC_DAG_CBOR: u64 = 0x71;
12pub const CODEC_DAG_JSON: u64 = 0x0129;
13pub const CODEC_JSON: u64 = 0x0200;
14
15pub fn multibase_encode(data: &[u8]) -> String {
16 multibase::encode(multibase::Base::Base58Btc, data)
17}
18
19pub fn multibase_decode(input: &str) -> Result<Vec<u8>> {
20 multibase::decode(input)
21 .map(|(_, data)| data)
22 .map_err(|_| MaError::InvalidPublicKeyMultibase)
23}
24
25pub fn multicodec_encode(codec: u64, payload: &[u8]) -> Vec<u8> {
26 let mut buffer = encode::u64_buffer();
27 let prefix = encode::u64(codec, &mut buffer);
28 let mut out = prefix.to_vec();
29 out.extend_from_slice(payload);
30 out
31}
32
33pub fn multicodec_decode(encoded: &[u8]) -> Result<(u64, Vec<u8>)> {
34 let (codec, remainder) =
35 decode::u64(encoded).map_err(|_| MaError::InvalidPublicKeyMultibase)?;
36 if remainder.is_empty() {
37 return Err(MaError::InvalidPublicKeyMultibase);
38 }
39 Ok((codec, remainder.to_vec()))
40}
41
42pub fn public_key_multibase_encode(codec: u64, public_key: &[u8]) -> String {
43 multibase_encode(&multicodec_encode(codec, public_key))
44}
45
46pub fn public_key_multibase_decode(input: &str) -> Result<(u64, Vec<u8>)> {
47 let decoded = multibase_decode(input)?;
48 multicodec_decode(&decoded)
49}
50
51pub fn signature_multibase_encode(codec: u64, signature: &[u8]) -> String {
52 multibase_encode(&multicodec_encode(codec, signature))
53}
54
55pub fn signature_multibase_decode(input: &str) -> Result<(u64, Vec<u8>)> {
56 let decoded = multibase_decode(input)?;
57 multicodec_decode(&decoded)
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn multicodec_round_trip_dag_cbor() {
66 let payload = b"hello dag-cbor";
67 let encoded = multicodec_encode(CODEC_DAG_CBOR, payload);
68 let (codec, decoded) = multicodec_decode(&encoded).unwrap();
69 assert_eq!(codec, CODEC_DAG_CBOR);
70 assert_eq!(decoded, payload);
71 }
72
73 #[test]
74 fn multicodec_round_trip_identity() {
75 let payload = b"raw bytes";
76 let encoded = multicodec_encode(CODEC_IDENTITY, payload);
77 let (codec, decoded) = multicodec_decode(&encoded).unwrap();
78 assert_eq!(codec, CODEC_IDENTITY);
79 assert_eq!(decoded.as_slice(), payload);
80 }
81
82 #[test]
83 fn multicodec_round_trip_large_codec_varint() {
84 let payload = b"json payload";
86 let encoded = multicodec_encode(CODEC_DAG_JSON, payload);
87 let (codec, decoded) = multicodec_decode(&encoded).unwrap();
88 assert_eq!(codec, CODEC_DAG_JSON);
89 assert_eq!(decoded.as_slice(), payload);
90 }
91
92 #[test]
93 fn multicodec_decode_empty_payload_fails() {
94 let mut buf = unsigned_varint::encode::u64_buffer();
96 let prefix = unsigned_varint::encode::u64(CODEC_CBOR, &mut buf);
97 assert!(multicodec_decode(prefix).is_err());
98 }
99
100 #[test]
101 fn multicodec_decode_empty_slice_fails() {
102 assert!(multicodec_decode(&[]).is_err());
103 }
104
105 #[test]
106 fn multibase_round_trip() {
107 let data = b"test data 12345";
108 let encoded = multibase_encode(data);
109 assert!(
110 encoded.starts_with('z'),
111 "base58btc multibase prefix is 'z'"
112 );
113 let decoded = multibase_decode(&encoded).unwrap();
114 assert_eq!(decoded, data);
115 }
116
117 #[test]
118 fn multibase_decode_invalid_input_fails() {
119 assert!(multibase_decode("!!!not-valid!!!").is_err());
120 }
121
122 #[test]
123 fn public_key_multibase_round_trip() {
124 let codec = 0xed_u64;
126 let key_bytes = [42u8; 32];
127 let encoded = public_key_multibase_encode(codec, &key_bytes);
128 let (decoded_codec, decoded_bytes) = public_key_multibase_decode(&encoded).unwrap();
129 assert_eq!(decoded_codec, codec);
130 assert_eq!(decoded_bytes.as_slice(), &key_bytes);
131 }
132
133 #[test]
134 fn signature_multibase_round_trip() {
135 let codec = 0xd0ed_u64;
137 let sig_bytes = [99u8; 64];
138 let encoded = signature_multibase_encode(codec, &sig_bytes);
139 let (decoded_codec, decoded_bytes) = signature_multibase_decode(&encoded).unwrap();
140 assert_eq!(decoded_codec, codec);
141 assert_eq!(decoded_bytes.as_slice(), &sig_bytes);
142 }
143
144 #[test]
145 fn multicodec_encode_prepends_prefix_bytes() {
146 let payload = b"x";
148 let encoded = multicodec_encode(CODEC_IDENTITY, payload);
149 assert_eq!(encoded[0], 0x00);
150 assert_eq!(&encoded[1..], payload);
151 }
152}