|
| 1 | +use fvm_sdk::crypto; |
| 2 | + |
| 3 | +/// Minimal interface for a hashing function |
| 4 | +/// |
| 5 | +/// Hasher::hash() must return a digest that is at least 4 bytes long so that it can be cast to a u32 |
| 6 | +pub trait Hasher { |
| 7 | + /// For an input of bytes return a digest that is at least 4 bytes long |
| 8 | + fn hash(&self, bytes: &[u8]) -> Vec<u8>; |
| 9 | +} |
| 10 | + |
| 11 | +/// Hasher that uses the hash_blake2b syscall provided by the FVM |
| 12 | +#[derive(Default)] |
| 13 | +pub struct Blake2bSyscall {} |
| 14 | + |
| 15 | +impl Hasher for Blake2bSyscall { |
| 16 | + fn hash(&self, bytes: &[u8]) -> Vec<u8> { |
| 17 | + crypto::hash_blake2b(bytes).try_into().unwrap() |
| 18 | + } |
| 19 | +} |
| 20 | + |
| 21 | +#[derive(Default)] |
| 22 | +pub struct MethodHasher<T: Hasher> { |
| 23 | + hasher: T, |
| 24 | +} |
| 25 | + |
| 26 | +impl<T: Hasher> MethodHasher<T> { |
| 27 | + const CONSTRUCTOR_METHOD_NAME: &'static str = "Constructor"; |
| 28 | + const CONSTRUCTOR_METHOD_NUMBER: u64 = 1_u64; |
| 29 | + pub fn new(hasher: T) -> Self { |
| 30 | + Self { hasher } |
| 31 | + } |
| 32 | + |
| 33 | + pub fn method_number(&self, method_name: &str) -> u64 { |
| 34 | + if method_name == Self::CONSTRUCTOR_METHOD_NAME { |
| 35 | + Self::CONSTRUCTOR_METHOD_NUMBER |
| 36 | + } else { |
| 37 | + let digest = self.hasher.hash(method_name.as_bytes()); |
| 38 | + if digest.len() < 4 { |
| 39 | + panic!("Invalid hasher used: digest must be at least 4 bytes long"); |
| 40 | + } |
| 41 | + as_u32(digest.as_slice()) as u64 |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +/// Takes a byte array and interprets it as a u32 number |
| 47 | +/// Assumes little-endian order |
| 48 | +#[rustfmt::skip] |
| 49 | +fn as_u32(bytes: &[u8]) -> u32 { |
| 50 | + ((bytes[0] as u32) << (8 * 3)) + |
| 51 | + ((bytes[1] as u32) << (8 * 2)) + |
| 52 | + ((bytes[2] as u32) << (8 * 1)) + |
| 53 | + (bytes[3] as u32) |
| 54 | +} |
| 55 | + |
| 56 | +#[cfg(test)] |
| 57 | +mod tests { |
| 58 | + |
| 59 | + use super::{Blake2bSyscall, Hasher, MethodHasher}; |
| 60 | + |
| 61 | + #[derive(Clone, Copy)] |
| 62 | + struct FakeHasher {} |
| 63 | + impl Hasher for FakeHasher { |
| 64 | + fn hash(&self, bytes: &[u8]) -> Vec<u8> { |
| 65 | + return bytes.to_vec(); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + #[test] |
| 70 | + #[allow(unused)] |
| 71 | + fn compile() { |
| 72 | + let method_hasher = MethodHasher::new(Blake2bSyscall {}); |
| 73 | + } |
| 74 | + |
| 75 | + #[test] |
| 76 | + fn constructor_method_number() { |
| 77 | + let method_hasher = MethodHasher::new(FakeHasher {}); |
| 78 | + assert_eq!(method_hasher.method_number("Constructor"), 1); |
| 79 | + } |
| 80 | + |
| 81 | + #[test] |
| 82 | + fn normal_method_number() { |
| 83 | + let fake_hasher = FakeHasher {}; |
| 84 | + let method_hasher = MethodHasher::new(fake_hasher); |
| 85 | + assert_eq!( |
| 86 | + method_hasher.method_number("NormalMethod"), |
| 87 | + super::as_u32(&fake_hasher.hash(b"NormalMethod")) as u64 |
| 88 | + ); |
| 89 | + } |
| 90 | +} |
0 commit comments