rain_metadata/meta/
normalize.rs1use super::{
2 KnownMeta,
3 super::error::Error,
4 types::{
5 authoring::v1::AuthoringMeta, authoring::v2::AuthoringMetaV2,
6 solidity_abi::v2::SolidityAbiMeta, interpreter_caller::v1::InterpreterCallerMeta,
7 },
8};
9
10fn normalize_json<'de, T>(data: &'de [u8]) -> Result<Vec<u8>, Error>
11where
12 T: serde::Deserialize<'de> + serde::Serialize + validator::Validate,
13{
14 let parsed = serde_json::from_str::<T>(std::str::from_utf8(data)?)?;
15 parsed.validate()?;
16 Ok(serde_json::to_string(&parsed)?.as_bytes().to_vec())
17}
18
19impl KnownMeta {
20 pub fn normalize(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
22 Ok(match self {
23 KnownMeta::SolidityAbiV2 => normalize_json::<SolidityAbiMeta>(data)?,
24 KnownMeta::InterpreterCallerMetaV1 => normalize_json::<InterpreterCallerMeta>(data)?,
25 KnownMeta::AuthoringMetaV1 => {
26 match AuthoringMeta::abi_decode(data) {
30 Ok(am) => am.abi_encode_validate()?,
31 _ => AuthoringMeta::abi_encode_validate(
32 &serde_json::from_str::<AuthoringMeta>(std::str::from_utf8(data)?)?,
33 )?,
34 }
35 }
36 KnownMeta::AuthoringMetaV2 => {
37 AuthoringMetaV2::abi_decode(data)
40 .map_err(|e| Error::InvalidInput(e.to_string()))?;
41 data.to_vec()
42 }
43 _ => data.to_vec(),
46 })
47 }
48}
49
50#[cfg(all(test, not(target_family = "wasm")))]
51mod tests {
52 use alloy::sol_types::SolValue;
53 use crate::error::Error;
54 use crate::meta::types::authoring::v1::{AuthoringMeta, AuthoringMetaItem};
55 use crate::meta::types::authoring::v2::AuthoringMetaV2Sol;
56 use crate::meta::KnownMeta;
57
58 fn authoring_meta_v2_abi(word: [u8; 32], description: &str) -> Vec<u8> {
59 vec![AuthoringMetaV2Sol {
60 word: word.into(),
61 description: description.to_string(),
62 }]
63 .abi_encode()
64 }
65
66 #[test]
70 fn test_normalize_op_v1_is_a_passthrough() {
71 let bytes = b"{ \"name\" : \"add\" }";
72 assert_eq!(KnownMeta::OpV1.normalize(bytes).unwrap(), bytes.to_vec());
73 }
74
75 #[test]
77 fn test_normalize_solidity_abi_v2_rejects_bad_input() {
78 assert!(matches!(
79 KnownMeta::SolidityAbiV2.normalize(b"not json at all"),
80 Err(Error::SerdeJsonError(_))
81 ));
82 assert!(matches!(
83 KnownMeta::SolidityAbiV2.normalize(&[0xff, 0xfe]),
84 Err(Error::Utf8Error(_))
85 ));
86 }
87
88 #[test]
90 fn test_normalize_solidity_abi_v2_canonicalizes() {
91 assert_eq!(
92 KnownMeta::SolidityAbiV2.normalize(b"[ ]").unwrap(),
93 b"[]".to_vec()
94 );
95 }
96
97 #[test]
100 fn test_normalize_interpreter_caller_rejects_empty_methods() {
101 let invalid = br#"{"name":"Test Caller","abiName":"TestCaller","methods":[]}"#;
102 assert!(matches!(
103 KnownMeta::InterpreterCallerMetaV1.normalize(invalid),
104 Err(Error::ValidationErrors(_))
105 ));
106 }
107
108 #[test]
110 fn test_normalize_passthrough_for_binary_metas() {
111 let data = vec![0x00, 0x01, 0xff];
112 assert_eq!(
113 KnownMeta::ExpressionDeployerV2BytecodeV1
114 .normalize(&data)
115 .unwrap(),
116 data
117 );
118 }
119
120 fn sample_authoring_meta() -> AuthoringMeta {
121 serde_json::from_str(
122 r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#,
123 )
124 .unwrap()
125 }
126
127 #[test]
130 fn test_normalize_authoring_meta_v1_abi_path() {
131 let authoring_meta = sample_authoring_meta();
132 let abi = authoring_meta.abi_encode_validate().unwrap();
133 let normalized = KnownMeta::AuthoringMetaV1.normalize(&abi).unwrap();
134 assert_eq!(normalized, abi);
135 }
136
137 #[test]
140 fn test_normalize_authoring_meta_v1_abi_invalid_rejected() {
141 let invalid = AuthoringMeta(vec![AuthoringMetaItem {
142 word: "NOTKEBAB".to_string(),
143 operand_parser_offset: 0,
144 description: "some description".to_string(),
145 }]);
146 let abi = invalid.abi_encode().unwrap();
148 let result = KnownMeta::AuthoringMetaV1.normalize(&abi);
149 assert!(matches!(result, Err(Error::ValidationErrors(_))));
150 }
151
152 #[test]
155 fn test_normalize_authoring_meta_v1_json_fallback() {
156 let json = r#"[{"word":"stack","description":"Copies an existing value from the stack.","operandParserOffset":16}]"#;
157 let expected = sample_authoring_meta().abi_encode_validate().unwrap();
158 let normalized = KnownMeta::AuthoringMetaV1
159 .normalize(json.as_bytes())
160 .unwrap();
161 assert_eq!(normalized, expected);
162 assert_ne!(normalized, json.as_bytes().to_vec());
163 }
164
165 #[test]
168 fn test_normalize_authoring_meta_v2_abi_passthrough() {
169 let mut word = [0u8; 32];
170 word[..5].copy_from_slice(b"stack");
171 let abi = authoring_meta_v2_abi(word, "Copies an existing value from the stack.");
172 assert_eq!(KnownMeta::AuthoringMetaV2.normalize(&abi).unwrap(), abi);
173 }
174
175 #[test]
178 fn test_normalize_authoring_meta_v2_rejects_arbitrary_bytes() {
179 assert!(matches!(
180 KnownMeta::AuthoringMetaV2.normalize(&[0xde, 0xad]),
181 Err(Error::InvalidInput(_))
182 ));
183 assert!(matches!(
184 KnownMeta::AuthoringMetaV2.normalize(b"[]"),
185 Err(Error::InvalidInput(_))
186 ));
187 }
188
189 #[test]
192 fn test_normalize_authoring_meta_v2_rejects_non_utf8_word() {
193 let mut word = [0u8; 32];
194 word[0] = 0xc3;
196 word[1] = 0x28;
197 let abi = authoring_meta_v2_abi(word, "bad word bytes");
198 assert!(matches!(
199 KnownMeta::AuthoringMetaV2.normalize(&abi),
200 Err(Error::InvalidInput(_))
201 ));
202 }
203
204 #[test]
207 fn test_normalize_default_arm_passthrough() {
208 let data = b"some dotrain text".to_vec();
209 assert_eq!(KnownMeta::DotrainV1.normalize(&data).unwrap(), data);
210 let binary = vec![0xffu8, 0x00, 0x01];
211 assert_eq!(KnownMeta::RainlangV1.normalize(&binary).unwrap(), binary);
212 }
213}