diff --git a/runtime/src/attestation.rs b/runtime/src/attestation.rs index 2448d72192..dc3183d6b1 100644 --- a/runtime/src/attestation.rs +++ b/runtime/src/attestation.rs @@ -152,7 +152,10 @@ mod tests { impl delegation::Trait for Test { type Signature = Ed25519Signature; type DelegationNodeId = H256; - } + fn print_hash(hash: Self::Hash) { + ::runtime_io::print(&hash.as_bytes()[..]); + } +} impl Trait for Test { } diff --git a/runtime/src/delegation.rs b/runtime/src/delegation.rs index 34c3827fdc..f240e417f9 100644 --- a/runtime/src/delegation.rs +++ b/runtime/src/delegation.rs @@ -27,7 +27,7 @@ impl Permissions { let b2 : u8 = ((x >> 16) & 0xff) as u8; let b3 : u8 = ((x >> 8) & 0xff) as u8; let b4 : u8 = (x & 0xff) as u8; - return [b1, b2, b3, b4]; + return [b4, b3, b2, b1]; } } @@ -41,6 +41,8 @@ pub trait Trait: ctype::Trait + system::Trait { type Signature: Verify + Member + Codec + Default; type DelegationNodeId: Parameter + Member + Codec + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]> + AsMut<[u8]>; + + fn print_hash(hash: Self::Hash); } decl_module! { @@ -67,21 +69,12 @@ decl_module! { return Err("delegation already exist") } - let mut hashed_values : Vec> = Vec::new(); - hashed_values.push(delegation_id.as_ref().to_vec()); - hashed_values.push(root_id.as_ref().to_vec()); - match parent_id { - Some(p) => hashed_values.push(p.as_ref().to_vec()), - None => {} - } - let p = permissions.as_u8(); - hashed_values.push((&p).to_vec()); - let hashed_value_array = hashed_values.iter().map(Vec::as_slice).collect::>(); - let hash_root = T::Hashing::enumerated_trie_root(&hashed_value_array); - if !verify_encoded_lazy(&delegate_signature, &hash_root, &delegate) { + let hash_root = Self::calculate_hash(delegation_id, root_id, parent_id, permissions); + if !verify_encoded_lazy(&delegate_signature, &&hash_root, &delegate) { // TODO: abort on signature error - ::runtime_io::print("WARNING: SIGNATURE DOES NOT MATCH!"); - // return Err("bad delegate signature") + ::runtime_io::print("Error: signature does not match, hash:"); + T::print_hash(hash_root); + return Err("bad delegate signature") } if >::exists(root_id) { @@ -149,6 +142,21 @@ decl_module! { } impl Module { + + fn calculate_hash(delegation_id: T::DelegationNodeId, + root_id: T::DelegationNodeId, parent_id: Option, + permissions: Permissions) -> T::Hash { + let mut hashed_values : Vec = delegation_id.as_ref().to_vec(); + hashed_values.extend_from_slice(root_id.as_ref()); + match parent_id { + Some(p) => hashed_values.extend_from_slice(p.as_ref()), + None => {} + } + hashed_values.extend_from_slice(permissions.as_u8().as_ref()); + let hash_root = T::Hashing::hash(&hashed_values); + return hash_root; + } + pub fn is_delegating(account: &T::AccountId, delegation: &T::DelegationNodeId) -> result::Result { if !>::exists(delegation) { return Err("delegation not found") @@ -204,3 +212,175 @@ decl_storage! { pub Children get(children): map T::DelegationNodeId => Vec; } } + + + +#[cfg(test)] +mod tests { + use super::*; + use system; + use runtime_io::with_externalities; + use primitives::{H256, H512, Blake2Hasher}; + use runtime_primitives::Ed25519Signature; + use primitives::*; + use support::{impl_outer_origin, assert_ok, assert_err}; + use parity_codec::Encode; + + use runtime_primitives::{ + BuildStorage, traits::{BlakeTwo256, IdentityLookup}, testing::{Digest, DigestItem, Header} + }; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = H256; + type Header = Header; + type Event = (); + type Log = DigestItem; + type Lookup = IdentityLookup; + } + + impl ctype::Trait for Test { + } + + impl Trait for Test { + type Signature = Ed25519Signature; + type DelegationNodeId = H256; + + fn print_hash(hash: Self::Hash) { + ::runtime_io::print(&hash.as_bytes()[..]); + } + } + + type Ctype = ctype::Module; + type Delegation = Module; + + fn hash_to_u8 (hash : T) -> Vec{ + return hash.encode(); + } + + fn new_test_ext() -> runtime_io::TestExternalities { + system::GenesisConfig::::default().build_storage().unwrap().0.into() + } + + #[test] + fn check_add_and_revoke_delegations() { + with_externalities(&mut new_test_ext(), || { + let pair_alice = ed25519::Pair::from_seed(b"Alice "); + let account_hash_alice = H256::from(pair_alice.public().0); + let pair_bob = ed25519::Pair::from_seed(b"Bob "); + let account_hash_bob = H256::from(pair_bob.public().0); + let pair_charlie = ed25519::Pair::from_seed(b"Charlie "); + let account_hash_charlie = H256::from(pair_charlie.public().0); + + let ctype_hash = H256::from_low_u64_be(1); + let id_level_0 = H256::from_low_u64_be(1); + let id_level_1 = H256::from_low_u64_be(2); + let id_level_2_1 = H256::from_low_u64_be(21); + let id_level_2_2 = H256::from_low_u64_be(22); + + assert_ok!(Ctype::add(Origin::signed(account_hash_alice.clone()), ctype_hash.clone())); + + assert_ok!(Delegation::create_root(Origin::signed(account_hash_alice.clone()), id_level_0.clone(), ctype_hash.clone())); + assert_err!(Delegation::create_root(Origin::signed(account_hash_alice.clone()), id_level_0.clone(), ctype_hash.clone()), + "root already exist"); + assert_err!(Delegation::create_root(Origin::signed(account_hash_alice.clone()), id_level_1.clone(), H256::from_low_u64_be(2)), + "CTYPE does not exist"); + + assert_ok!(Delegation::add_delegation(Origin::signed(account_hash_alice.clone()), id_level_1.clone(), id_level_0.clone(), + None, account_hash_bob.clone(), Permissions::DELEGATE, + Ed25519Signature::from(pair_bob.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_1.clone(), id_level_0.clone(), None, Permissions::DELEGATE)))))); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_alice.clone()), id_level_1.clone(), id_level_0.clone(), + None, account_hash_bob.clone(), Permissions::DELEGATE, + Ed25519Signature::from(pair_bob.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_1.clone(), id_level_0.clone(), None, Permissions::DELEGATE))))), + "delegation already exist"); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_bob.clone()), id_level_2_1.clone(), id_level_0.clone(), + Some(id_level_1.clone()), account_hash_charlie.clone(), Permissions::ATTEST, Ed25519Signature::from(H512::from_low_u64_be(0))), + "bad delegate signature"); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_charlie.clone()), id_level_2_1.clone(), id_level_0.clone(), + None, account_hash_bob.clone(), Permissions::DELEGATE, + Ed25519Signature::from(pair_bob.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_1.clone(), id_level_0.clone(), None, Permissions::DELEGATE))))), + "not owner of root"); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_alice.clone()), id_level_2_1.clone(), id_level_1.clone(), + None, account_hash_bob.clone(), Permissions::DELEGATE, + Ed25519Signature::from(pair_bob.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_1.clone(), id_level_1.clone(), None, Permissions::DELEGATE))))), + "root not found"); + + + assert_ok!(Delegation::add_delegation(Origin::signed(account_hash_bob.clone()), id_level_2_1.clone(), id_level_0.clone(), + Some(id_level_1.clone()), account_hash_charlie.clone(), Permissions::ATTEST, + Ed25519Signature::from(pair_charlie.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_1.clone(), id_level_0.clone(), Some(id_level_1.clone()), Permissions::ATTEST)))))); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_alice.clone()), id_level_2_2.clone(), id_level_0.clone(), + Some(id_level_1.clone()), account_hash_charlie.clone(), Permissions::ATTEST, + Ed25519Signature::from(pair_charlie.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_2.clone(), id_level_0.clone(), Some(id_level_1.clone()), Permissions::ATTEST))))), + "not owner of parent"); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_charlie.clone()), id_level_2_2.clone(), id_level_0.clone(), + Some(id_level_2_1.clone()), account_hash_alice.clone(), Permissions::ATTEST, + Ed25519Signature::from(pair_alice.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_2.clone(), id_level_0.clone(), Some(id_level_2_1.clone()), Permissions::ATTEST))))), + "not authorized to delegate"); + assert_err!(Delegation::add_delegation(Origin::signed(account_hash_bob.clone()), id_level_2_2.clone(), id_level_0.clone(), + Some(id_level_0.clone()), account_hash_charlie.clone(), Permissions::ATTEST, + Ed25519Signature::from(pair_charlie.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_2.clone(), id_level_0.clone(), Some(id_level_0.clone()), Permissions::ATTEST))))), + "parent not found"); + + assert_ok!(Delegation::add_delegation(Origin::signed(account_hash_bob.clone()), id_level_2_2.clone(), id_level_0.clone(), + Some(id_level_1.clone()), account_hash_charlie.clone(), Permissions::ATTEST | Permissions::DELEGATE, + Ed25519Signature::from(pair_charlie.sign(&hash_to_u8( + Delegation::calculate_hash(id_level_2_2.clone(), id_level_0.clone(), Some(id_level_1.clone()), + Permissions::ATTEST | Permissions::DELEGATE)))))); + + let root = Delegation::root(id_level_0.clone()); + assert_eq!(root.0, ctype_hash.clone()); + assert_eq!(root.1, account_hash_alice.clone()); + assert_eq!(root.2, false); + + let delegation_1 = Delegation::delegation(id_level_1.clone()); + assert_eq!(delegation_1.0, id_level_0.clone()); + assert_eq!(delegation_1.1, None); + assert_eq!(delegation_1.2, account_hash_bob.clone()); + assert_eq!(delegation_1.3, Permissions::DELEGATE); + assert_eq!(delegation_1.4, false); + + let delegation_2 = Delegation::delegation(id_level_2_2.clone()); + assert_eq!(delegation_2.0, id_level_0.clone()); + assert_eq!(delegation_2.1, Some(id_level_1.clone())); + assert_eq!(delegation_2.2, account_hash_charlie.clone()); + assert_eq!(delegation_2.3, Permissions::ATTEST | Permissions::DELEGATE); + assert_eq!(delegation_2.4, false); + + let children = Delegation::children(id_level_1.clone()); + assert_eq!(children.len(), 2); + assert_eq!(children[0], id_level_2_1.clone()); + assert_eq!(children[1], id_level_2_2.clone()); + + // check is_delgating + assert_eq!(Delegation::is_delegating(&account_hash_alice, &id_level_1), Ok(true)); + assert_eq!(Delegation::is_delegating(&account_hash_alice, &id_level_2_1), Ok(true)); + assert_eq!(Delegation::is_delegating(&account_hash_bob, &id_level_2_1), Ok(true)); + assert_eq!(Delegation::is_delegating(&account_hash_charlie, &id_level_2_1), Ok(true)); + assert_eq!(Delegation::is_delegating(&account_hash_charlie, &id_level_1), Ok(false)); + assert_eq!(Delegation::is_delegating(&account_hash_charlie, &id_level_0), Err("delegation not found")); + + // TODO: test revocation with state and errors + // TODO: test attestation based on delegation + }); + } +} \ No newline at end of file diff --git a/runtime/src/did.rs b/runtime/src/did.rs index 9460443380..5a5915afcc 100644 --- a/runtime/src/did.rs +++ b/runtime/src/did.rs @@ -16,13 +16,13 @@ decl_module! { pub fn add(origin, sign_key: T::PublicSigningKey, box_key: T::PublicBoxKey, doc_ref: Option>) -> Result { let sender = ensure_signed(origin)?; - >::insert(sender.clone(), (sign_key, box_key, doc_ref)); + >::insert(sender.clone(), (sign_key, box_key, doc_ref)); Ok(()) } pub fn remove(origin) -> Result { let sender = ensure_signed(origin)?; - >::remove(sender.clone()); + >::remove(sender.clone()); Ok(()) } } @@ -30,8 +30,8 @@ decl_module! { decl_storage! { trait Store for Module as DID { - // DID: account-id -> (did-reference?) - DID get(dids): map T::AccountId => (T::PublicSigningKey, T::PublicBoxKey, Option>); + // DID: account-id -> (public-signing-key, public-encryption-key, did-reference?) + DIDs get(dids): map T::AccountId => (T::PublicSigningKey, T::PublicBoxKey, Option>); } } @@ -84,10 +84,20 @@ mod tests { fn check_add_did() { with_externalities(&mut new_test_ext(), || { let pair = ed25519::Pair::from_seed(b"Alice "); - let hash = H256::from_low_u64_be(1); + let signing_key = H256::from_low_u64_be(1); + let box_key = H256::from_low_u64_be(2); let account_hash = H256::from(pair.public().0); assert_ok!(DID::add(Origin::signed(account_hash.clone()), - hash.clone(), hash.clone(), Some(b"http://kilt.org/submit".to_vec()))); + signing_key.clone(), box_key.clone(), Some(b"http://kilt.org/submit".to_vec()))); + + assert_eq!(>::exists(account_hash), true); + let did = DID::dids(account_hash.clone()); + assert_eq!(did.0, signing_key.clone()); + assert_eq!(did.1, box_key.clone()); + assert_eq!(did.2, Some(b"http://kilt.org/submit".to_vec())); + + assert_ok!(DID::remove(Origin::signed(account_hash.clone()))); + assert_eq!(>::exists(account_hash), false); }); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 00538ec014..63fabd7131 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -190,6 +190,11 @@ impl ctype::Trait for Runtime { impl delegation::Trait for Runtime { type Signature = Ed25519Signature; type DelegationNodeId = Hash; + + fn print_hash(hash: Hash) { + ::runtime_io::print(&hash.as_bytes()[..]); + } + } impl did::Trait for Runtime {