gemachain_client/
rpc_filter.rs1use thiserror::Error;
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
4#[serde(rename_all = "camelCase")]
5pub enum RpcFilterType {
6 DataSize(u64),
7 Memcmp(Memcmp),
8}
9
10impl RpcFilterType {
11 pub fn verify(&self) -> Result<(), RpcFilterError> {
12 match self {
13 RpcFilterType::DataSize(_) => Ok(()),
14 RpcFilterType::Memcmp(compare) => {
15 let encoding = compare.encoding.as_ref().unwrap_or(&MemcmpEncoding::Binary);
16 match encoding {
17 MemcmpEncoding::Binary => {
18 let MemcmpEncodedBytes::Binary(bytes) = &compare.bytes;
19
20 if bytes.len() > 128 {
21 Err(RpcFilterError::Base58DataTooLarge)
22 } else {
23 bs58::decode(&bytes)
24 .into_vec()
25 .map(|_| ())
26 .map_err(|e| e.into())
27 }
28 }
29 }
30 }
31 }
32 }
33}
34
35#[derive(Error, PartialEq, Debug)]
36pub enum RpcFilterError {
37 #[error("bs58 decode error")]
38 DecodeError(#[from] bs58::decode::Error),
39 #[error("encoded binary (base 58) data should be less than 129 bytes")]
40 Base58DataTooLarge,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub enum MemcmpEncoding {
46 Binary,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase", untagged)]
51pub enum MemcmpEncodedBytes {
52 Binary(String),
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub struct Memcmp {
57 pub offset: usize,
59 pub bytes: MemcmpEncodedBytes,
61 pub encoding: Option<MemcmpEncoding>,
63}
64
65impl Memcmp {
66 pub fn bytes_match(&self, data: &[u8]) -> bool {
67 match &self.bytes {
68 MemcmpEncodedBytes::Binary(bytes) => {
69 let bytes = bs58::decode(bytes).into_vec();
70 if bytes.is_err() {
71 return false;
72 }
73 let bytes = bytes.unwrap();
74 if self.offset > data.len() {
75 return false;
76 }
77 if data[self.offset..].len() < bytes.len() {
78 return false;
79 }
80 data[self.offset..self.offset + bytes.len()] == bytes[..]
81 }
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_bytes_match() {
92 let data = vec![1, 2, 3, 4, 5];
93
94 assert!(Memcmp {
96 offset: 0,
97 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![1, 2, 3, 4, 5]).into_string()),
98 encoding: None,
99 }
100 .bytes_match(&data));
101
102 assert!(Memcmp {
104 offset: 0,
105 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![1, 2]).into_string()),
106 encoding: None,
107 }
108 .bytes_match(&data));
109
110 assert!(Memcmp {
112 offset: 2,
113 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![3, 4]).into_string()),
114 encoding: None,
115 }
116 .bytes_match(&data));
117
118 assert!(!Memcmp {
120 offset: 0,
121 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![2]).into_string()),
122 encoding: None,
123 }
124 .bytes_match(&data));
125
126 assert!(!Memcmp {
128 offset: 2,
129 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![3, 4, 5, 6]).into_string()),
130 encoding: None,
131 }
132 .bytes_match(&data));
133
134 assert!(!Memcmp {
136 offset: 6,
137 bytes: MemcmpEncodedBytes::Binary(bs58::encode(vec![5]).into_string()),
138 encoding: None,
139 }
140 .bytes_match(&data));
141
142 assert!(!Memcmp {
144 offset: 0,
145 bytes: MemcmpEncodedBytes::Binary("III".to_string()),
146 encoding: None,
147 }
148 .bytes_match(&data));
149 }
150
151 #[test]
152 fn test_verify_memcmp() {
153 let base58_bytes = "\
154 1111111111111111111111111111111111111111111111111111111111111111\
155 1111111111111111111111111111111111111111111111111111111111111111";
156 assert_eq!(base58_bytes.len(), 128);
157 assert_eq!(
158 RpcFilterType::Memcmp(Memcmp {
159 offset: 0,
160 bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()),
161 encoding: None,
162 })
163 .verify(),
164 Ok(())
165 );
166
167 let base58_bytes = "\
168 1111111111111111111111111111111111111111111111111111111111111111\
169 1111111111111111111111111111111111111111111111111111111111111111\
170 1";
171 assert_eq!(base58_bytes.len(), 129);
172 assert_eq!(
173 RpcFilterType::Memcmp(Memcmp {
174 offset: 0,
175 bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()),
176 encoding: None,
177 })
178 .verify(),
179 Err(RpcFilterError::Base58DataTooLarge)
180 );
181 }
182}