1use crate::constants::METACHAIN_SHARD_ID;
7use crate::error::CoreError;
8
9pub fn shard_of(address: &str, num_shards: u32) -> Result<u32, CoreError> {
23 let (_hrp, raw) =
24 bech32::decode(address).map_err(|e| CoreError::InvalidBech32(e.to_string()))?;
25 shard_of_address_bytes(&raw, num_shards)
26}
27
28pub fn shard_of_address_bytes(address: &[u8], num_shards: u32) -> Result<u32, CoreError> {
40 if address.is_empty() {
41 return Err(CoreError::EmptyAddress);
42 }
43
44 let (mask_high, mask_low) = calculate_masks(num_shards);
46
47 Ok(compute_shard_id(address, num_shards, mask_high, mask_low))
49}
50
51fn calculate_masks(num_shards: u32) -> (u32, u32) {
54 let n = f64::from(num_shards).log2().ceil() as u32;
55 let mask_high = (1u32 << n) - 1;
56 let mask_low = if n > 0 { (1u32 << (n - 1)) - 1 } else { 0 };
57 (mask_high, mask_low)
58}
59
60fn compute_shard_id(address: &[u8], num_shards: u32, mask_high: u32, mask_low: u32) -> u32 {
63 let bytes_needed = if num_shards <= 256 {
65 1
66 } else if num_shards <= 65536 {
67 2
68 } else if num_shards <= 16_777_216 {
69 3
70 } else {
71 4
72 };
73
74 let starting_index = if address.len() > bytes_needed {
76 address.len() - bytes_needed
77 } else {
78 0
79 };
80
81 let buff_needed = &address[starting_index..];
82
83 if is_smart_contract_on_metachain(buff_needed, address) {
85 return METACHAIN_SHARD_ID;
86 }
87
88 let mut addr: u32 = 0;
90 for &byte in buff_needed {
91 addr = (addr << 8) + u32::from(byte);
92 }
93
94 let mut shard = addr & mask_high;
96 if shard > num_shards - 1 {
97 shard = addr & mask_low;
98 }
99
100 shard
101}
102
103fn is_smart_contract_on_metachain(_buff_needed: &[u8], address: &[u8]) -> bool {
110 if address.len() < 8 || address[..8] != [0u8; 8] {
112 return false;
113 }
114
115 if address.len() >= 2 {
118 let shard_byte_index = address.len() - 2;
119 if address[shard_byte_index] == 0xFF {
120 return true;
121 }
122 }
123
124 false
125}
126
127#[inline]
138pub fn select_shard(last_byte: u8, num_shards: u32) -> u32 {
139 if num_shards == 3 {
140 u32::from(last_byte & 0x03)
141 } else {
142 u32::from(last_byte) % num_shards
143 }
144}
145
146#[inline]
160pub fn shard_of_bytes(raw: &[u8], num_shards: u32) -> Option<u32> {
161 let last = *raw.last()?;
162 if last == 0xFF {
163 Some(0xFF) } else {
165 Some(select_shard(last, num_shards))
166 }
167}
168
169pub fn decode_embedded_receiver(data: &[u8]) -> Option<Vec<u8>> {
182 fn parse(txt: &str) -> Option<Vec<u8>> {
184 let mut parts = txt.split('@');
185 let func = parts.next()?.to_ascii_lowercase();
186 match func.as_str() {
187 "esdtnfttransfer" => parts.nth(3).and_then(|h| hex::decode(h).ok()),
189 "multiesdtnfttransfer" => parts.next().and_then(|h| hex::decode(h).ok()),
191 "esdttransfer" => parts.nth(2).and_then(|h| hex::decode(h).ok()),
193 _ => None,
194 }
195 }
196
197 if let Ok(txt) = std::str::from_utf8(data)
199 && let Some(res) = parse(txt)
200 {
201 return Some(res);
202 }
203
204 if let Ok(decoded) = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, data)
206 && let Ok(txt) = std::str::from_utf8(&decoded)
207 {
208 return parse(txt);
209 }
210 None
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_shard_calculation() {
219 let cases = [(
221 "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
222 1,
223 )];
224
225 for (addr, expected_shard) in cases {
226 let shard = shard_of(addr, 3).unwrap();
227 assert_eq!(
228 shard, expected_shard,
229 "address {addr} expected shard {expected_shard}"
230 );
231 }
232 }
233
234 #[test]
235 fn test_mask_calculation() {
236 let (mask_high, mask_low) = calculate_masks(3);
238 assert_eq!(mask_high, 0b11);
239 assert_eq!(mask_low, 0b01);
240
241 let (mask_high, mask_low) = calculate_masks(4);
243 assert_eq!(mask_high, 0b11);
244 assert_eq!(mask_low, 0b01);
245 }
246
247 #[test]
248 fn test_empty_address() {
249 let result = shard_of_address_bytes(&[], 3);
250 assert!(result.is_err());
251 }
252
253 #[test]
254 fn test_select_shard() {
255 assert_eq!(select_shard(0b0000_0000, 3), 0); assert_eq!(select_shard(0b0000_0001, 3), 1); assert_eq!(select_shard(0b0000_0010, 3), 2); assert_eq!(select_shard(0b0000_0011, 3), 3); assert_eq!(select_shard(0b1111_1100, 3), 0); assert_eq!(select_shard(5, 4), 1); assert_eq!(select_shard(10, 7), 3); }
266
267 #[test]
268 fn test_shard_of_bytes() {
269 assert_eq!(shard_of_bytes(&[0x00], 3), Some(0));
271 assert_eq!(shard_of_bytes(&[0x01], 3), Some(1));
272 assert_eq!(shard_of_bytes(&[0x02], 3), Some(2));
273 assert_eq!(shard_of_bytes(&[0x03], 3), Some(3));
274
275 assert_eq!(shard_of_bytes(&[0xFF], 3), Some(0xFF));
277
278 assert_eq!(shard_of_bytes(&[], 3), None);
280
281 assert_eq!(shard_of_bytes(&[0xAA, 0xBB, 0x02], 3), Some(2));
283 }
284
285 #[test]
286 fn test_decode_embedded_receiver_esdt_nft_transfer() {
287 let data = b"ESDTNFTTransfer@544f4b454e@01@0a@aabbccdd";
289 let result = decode_embedded_receiver(data);
290 assert_eq!(result, Some(vec![0xaa, 0xbb, 0xcc, 0xdd]));
291 }
292
293 #[test]
294 fn test_decode_embedded_receiver_multi_esdt_nft_transfer() {
295 let data = b"MultiESDTNFTTransfer@aabbccdd@02";
297 let result = decode_embedded_receiver(data);
298 assert_eq!(result, Some(vec![0xaa, 0xbb, 0xcc, 0xdd]));
299 }
300
301 #[test]
302 fn test_decode_embedded_receiver_esdt_transfer() {
303 let data = b"ESDTTransfer@544f4b454e@0a@aabbccdd";
305 let result = decode_embedded_receiver(data);
306 assert_eq!(result, Some(vec![0xaa, 0xbb, 0xcc, 0xdd]));
307 }
308
309 #[test]
310 fn test_decode_embedded_receiver_case_insensitive() {
311 let data1 = b"esdttransfer@544f4b454e@0a@aabbccdd";
313 let data2 = b"ESDTTRANSFER@544f4b454e@0a@aabbccdd";
314 assert_eq!(
315 decode_embedded_receiver(data1),
316 decode_embedded_receiver(data2)
317 );
318 }
319
320 #[test]
321 fn test_decode_embedded_receiver_base64() {
322 let data = b"RVNEVFRyYW5zZmVyQDU0NGY0YjQ1NGVAMGFAYWFiYmNjZGQ=";
324 let result = decode_embedded_receiver(data);
325 assert_eq!(result, Some(vec![0xaa, 0xbb, 0xcc, 0xdd]));
326 }
327
328 #[test]
329 fn test_decode_embedded_receiver_invalid() {
330 assert_eq!(decode_embedded_receiver(b"SomeOtherFunction@arg"), None);
332
333 assert_eq!(decode_embedded_receiver(b"ESDTTransfer@token"), None);
335
336 assert_eq!(
338 decode_embedded_receiver(b"ESDTTransfer@token@amount@zzzz"),
339 None
340 );
341
342 assert_eq!(decode_embedded_receiver(b""), None);
344 }
345}