From aa0d364fc217944bcba3529a87470fa295782976 Mon Sep 17 00:00:00 2001 From: Luke Parker <lukeparker5132@gmail.com> Date: Mon, 18 Jul 2022 01:52:59 -0400 Subject: [PATCH] First passing multisig vote test --- Cargo.lock | 1 + contracts/extension/Cargo.toml | 2 + contracts/extension/src/lib.rs | 56 +++++++++++++++++ contracts/multisig/lib.rs | 106 ++++++++++++++++++++++++++++++++- 4 files changed, 162 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2681d850..c198df8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7513,6 +7513,7 @@ version = "0.1.0" dependencies = [ "ink_env", "ink_lang", + "parity-scale-codec", ] [[package]] diff --git a/contracts/extension/Cargo.toml b/contracts/extension/Cargo.toml index f0f0c23d..b36450a2 100644 --- a/contracts/extension/Cargo.toml +++ b/contracts/extension/Cargo.toml @@ -7,6 +7,8 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"] edition = "2021" [dependencies] +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } + ink_env = { version = "3", default-features = false } ink_lang = { version = "3", default-features = false } diff --git a/contracts/extension/src/lib.rs b/contracts/extension/src/lib.rs index a29cb754..ab1e4d24 100644 --- a/contracts/extension/src/lib.rs +++ b/contracts/extension/src/lib.rs @@ -33,3 +33,59 @@ impl Environment for SeraiEnvironment { type ChainExtension = SeraiExtension; } + +pub fn test_register() { + struct ExtensionLen; + impl ink_env::test::ChainExtension for ExtensionLen { + fn func_id(&self) -> u32 { + 0 + } + + fn call(&mut self, _: &[u8], output: &mut Vec<u8>) -> u32 { + scale::Encode::encode_to(&5u16, output); + 0 + } + } + ink_env::test::register_chain_extension(ExtensionLen); + + struct ExtensionId; + impl ink_env::test::ChainExtension for ExtensionId { + fn func_id(&self) -> u32 { + 1 + } + + fn call(&mut self, _: &[u8], output: &mut Vec<u8>) -> u32 { + scale::Encode::encode_to(&[0xffu8; 32], output); + 0 + } + } + ink_env::test::register_chain_extension(ExtensionId); + + struct ExtensionActive; + impl ink_env::test::ChainExtension for ExtensionActive { + fn func_id(&self) -> u32 { + 2 + } + + fn call(&mut self, input: &[u8], output: &mut Vec<u8>) -> u32 { + use scale::Decode; + let potential = AccountId::decode(&mut &input[1 ..]).unwrap(); // TODO: Why is this 1 ..? + + let mut presence = false; + for validator in [ + AccountId::from([1; 32]), + AccountId::from([2; 32]), + AccountId::from([3; 32]), + AccountId::from([4; 32]), + AccountId::from([5; 32]) + ].clone() { + if potential == validator { + presence = true; + } + } + scale::Encode::encode_to(&presence, output); + 0 + } + } + ink_env::test::register_chain_extension(ExtensionActive); +} diff --git a/contracts/multisig/lib.rs b/contracts/multisig/lib.rs index 9ba129c3..bbf62802 100644 --- a/contracts/multisig/lib.rs +++ b/contracts/multisig/lib.rs @@ -39,7 +39,7 @@ mod multisig { #[ink(topic)] hash: [u8; 32], /// Keys voted on. - keys: Vec<Vec<u8>>, + keys: Option<Vec<Vec<u8>>>, } /// Event emitted when the new keys are fully generated for all curves, having been fully voted @@ -123,14 +123,14 @@ mod multisig { self.voted.insert((validator, keys_hash), &()); let votes = if let Some(votes) = self.votes.get((validator_set, keys_hash)) { - self.env().emit_event(Vote { validator, validator_set, hash: keys_hash, keys: vec![] }); + self.env().emit_event(Vote { validator, validator_set, hash: keys_hash, keys: None }); votes + 1 } else { self.env().emit_event(Vote { validator, validator_set, hash: keys_hash, - keys: keys.clone(), + keys: Some(keys.clone()), }); 1 }; @@ -149,4 +149,104 @@ mod multisig { Ok(()) } } + + #[cfg(test)] + mod tests { + use super::*; + + use ink_env::{ + hash::{CryptoHash, Blake2x256}, + AccountId, + topics::PrefixedValue, + }; + use ink_lang as ink; + + type Event = <Multisig as ::ink_lang::reflect::ContractEventBase>::Type; + + fn hash_prefixed<T: scale::Encode>(prefixed: PrefixedValue<T>) -> [u8; 32] { + let encoded = prefixed.encode(); + let mut hash = [0; 32]; + if encoded.len() < 32 { + hash[.. encoded.len()].copy_from_slice(&encoded); + } else { + Blake2x256::hash(&encoded, &mut hash); + } + hash + } + + fn assert_vote( + event: &ink_env::test::EmittedEvent, + expected_validator: AccountId, + expected_validator_set: [u8; 32], + expected_hash: [u8; 32], + expected_keys: Option<Vec<Vec<u8>>>, + ) { + let decoded_event = <Event as scale::Decode>::decode(&mut &event.data[..]) + .expect("encountered invalid contract event data buffer"); + + if let Event::Vote(Vote { validator, validator_set, hash, keys }) = decoded_event { + assert_eq!(validator, expected_validator); + assert_eq!(validator_set, expected_validator_set); + assert_eq!(hash, expected_hash); + assert_eq!(keys, expected_keys); + } else { + panic!("invalid Vote event") + } + + let expected_topics = vec![ + hash_prefixed(PrefixedValue { prefix: b"", value: b"Multisig::Vote" }), + hash_prefixed(PrefixedValue { + prefix: b"Multisig::Vote::validator", + value: &expected_validator, + }), + hash_prefixed(PrefixedValue { + prefix: b"Multisig::Vote::validator_set", + value: &expected_validator_set, + }), + hash_prefixed(PrefixedValue { prefix: b"Multisig::Vote::hash", value: &expected_hash }), + ]; + + for (n, (actual_topic, expected_topic)) in + event.topics.iter().zip(expected_topics).enumerate() + { + assert_eq!(actual_topic, &expected_topic, "encountered invalid topic at {}", n); + } + } + + /// The default constructor does its job. + #[ink::test] + fn new() { + let multisig = Multisig::new(); + assert_eq!(multisig.validator_set(), [0; 32]); + } + + #[ink::test] + fn non_existent_curve() { + assert_eq!(Multisig::new().key(0), Err(Error::NonExistentCurve)); + } + + #[ink::test] + fn vote() { + serai_extension::test_register(); + ink_env::test::set_caller::<ink_env::DefaultEnvironment>(AccountId::from([1; 32])); + + let mut multisig = Multisig::new(); + + let keys = vec![vec![0, 1], vec![2, 3]]; + multisig.vote(keys.clone()).unwrap(); + let emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>(); + assert_eq!(emitted_events.len(), 1); + assert_vote( + &emitted_events[0], + AccountId::from([1; 32]), + [0xff; 32], + { + let mut hash = [0; 32]; + ink_env::hash_encoded::<Blake2x256, _>(&keys, &mut hash); + hash + }, + Some(keys), + ); + } + } }