Skip to content

Commit 7c120b1

Browse files
authored
Merge pull request #109 from hashed-io/develop
Adds confidential documents pallet
2 parents c3fa368 + 66110d5 commit 7c120b1

File tree

10 files changed

+1365
-0
lines changed

10 files changed

+1365
-0
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[package]
2+
name = "pallet-confidential-docs"
3+
version = "4.0.0-dev"
4+
description = "Provides backend services for the confidentials docs solution"
5+
authors = ["Hashed <https://github.com/hashed-io"]
6+
homepage = "https://hashed.io"
7+
edition = "2021"
8+
license = "Unlicense"
9+
publish = false
10+
repository = "https://github.com/hashed-io/hashed-substrate"
11+
12+
[package.metadata.docs.rs]
13+
targets = ["x86_64-unknown-linux-gnu"]
14+
15+
[dependencies]
16+
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [
17+
"derive",
18+
] }
19+
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
20+
frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23"}
21+
frame-system = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" }
22+
frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23", optional = true }
23+
24+
[dev-dependencies]
25+
sp-core = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" }
26+
sp-io = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" }
27+
sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" }
28+
29+
[features]
30+
default = ["std"]
31+
std = [
32+
"codec/std",
33+
"scale-info/std",
34+
"frame-support/std",
35+
"frame-system/std",
36+
"frame-benchmarking/std",
37+
]
38+
39+
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
40+
try-runtime = ["frame-support/try-runtime"]
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Confidential documents
2+
Provides the backend services and metadata storage for the confidential docs solution
3+
4+
- [Confidential documents](#confidential-documents)
5+
- [Overview](#overview)
6+
- [Interface](#interface)
7+
- [Dispachable functions](#dispachable-functions)
8+
- [Getters](#getters)
9+
- [Usage](#usage)
10+
- [Polkadot-js api (javascript library)](#polkadot-js-api-javascript-library)
11+
- [Create a vault](#create-a-vault)
12+
- [Get a vault](#get-a-vault)
13+
- [Get a public key](#get-a-public-key)
14+
- [Create an owned confidential document](#create-an-owned-confidential-document)
15+
- [Get an owned confidential document by CID](#get-an-owned-confidential-document-by-cid)
16+
- [Remove an owned confidential document](#remove-an-owned-confidential-document)
17+
- [Share a confidential document](#share-a-confidential-document)
18+
- [Get a shared confidential document by CID](#get-a-shared-confidential-document-by-cid)
19+
- [Update a shared confidential document's metadata](#update-a-shared-confidential-documents-metadata)
20+
- [Remove a shared confidential document](#remove-a-shared-confidential-document)
21+
## Overview
22+
23+
This module allows a user to:
24+
- Create their vault. The vault stores the cipher private key used to cipher the user documents. The way the user vault is ciphered depends on the login method used by the user.
25+
- Create on owned confidential document that only the user has access to
26+
- Update the metadata of an owned confidential document
27+
- Share a confidential document with another user
28+
29+
## Interface
30+
31+
### Dispachable functions
32+
- `set_vault` Creates/Updates the calling user's vault and sets their public cipher key
33+
- `set_owned_document` Creates a new owned document or updates an existing owned document's metadata
34+
- `remove_owned_document` Removes an owned document
35+
- `share_document` Creates a shared document
36+
- `update_shared_document_metadata` Updates share document metadata
37+
- `remove_shared_document` Removes a shared document
38+
39+
### Getters
40+
- `vaults`
41+
- `public_keys`
42+
- `owned_docs`
43+
- `owned_docs_by_owner`
44+
- `shared_docs`
45+
- `shared_docs_by_to`
46+
- `shared_docs_by_from`
47+
48+
## Usage
49+
50+
The following examples will be using these prefunded accounts and testing data:
51+
52+
```bash
53+
# Alice's mnemonic seed
54+
"//Alice"
55+
# Alice's public address
56+
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
57+
58+
# Bob's mnemonic seed
59+
"//Bob"
60+
# Bob's public address
61+
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
62+
```
63+
64+
### Polkadot-js api (javascript library)
65+
66+
#### Create a vault
67+
```js
68+
const response = await api.tx.confidentialDocs.setVault(userId, publicKey, cid).signAndSend(alice);
69+
```
70+
71+
#### Get a vault
72+
```js
73+
const vault = await api.query.confidentialDocs.vaults(userId);
74+
console.log(vault.toHuman());
75+
```
76+
```bash
77+
# Output should look like this:
78+
{
79+
cid: 'QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr',
80+
owner: '5FSuxe2q7qCYKie8yqmM56U4ovD1YtBb3DoPzGKjwZ98vxua'
81+
}
82+
```
83+
84+
#### Get a public key
85+
```js
86+
const publicKey = await api.query.confidentialDocs.publicKeys(address);
87+
console.log(markets.toHuman());
88+
```
89+
```bash
90+
# Output should look like this:
91+
'0xabe44a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b75'
92+
```
93+
94+
#### Create an owned confidential document
95+
```js
96+
const response = await api.tx.confidentialDocs.setOwnedDocument({
97+
"cid": "QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr",
98+
"name": "name",
99+
"description": "desc",
100+
"owner": "5FSuxe2q7qCYKie8yqmM56U4ovD1YtBb3DoPzGKjwZ98vxua"
101+
}).signAndSend(alice);
102+
```
103+
104+
#### Get an owned confidential document by CID
105+
```js
106+
const ownedDoc = await api.query.confidentialDocs.ownedDocs(cid);
107+
console.log(ownedDoc.toHuman());
108+
```
109+
```bash
110+
# Output should look like this:
111+
{
112+
"cid": "QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr",
113+
"name": "name",
114+
"description": "desc",
115+
"owner": "5FSuxe2q7qCYKie8yqmM56U4ovD1YtBb3DoPzGKjwZ98vxua"
116+
}
117+
```
118+
119+
#### Remove an owned confidential document
120+
```js
121+
const response = await api.tx.confidentialDocs.removeOwnedDocument("QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr").signAndSend(alice);
122+
```
123+
124+
#### Share a confidential document
125+
```js
126+
const response = await api.tx.confidentialDocs.shareDocument({
127+
"cid": "QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr",
128+
"name": "name",
129+
"description": "desc",
130+
"to": "5FSuxe2q7qCYKie8yqmM56U4ovD1YtBb3DoPzGKjwZ98vxua",
131+
"from": "5FWtfhKTuGKm9yWqzApwTfiUL4UPWukJzEcCTGYDiYHsdKaG"
132+
}).signAndSend(alice);
133+
```
134+
135+
#### Get a shared confidential document by CID
136+
```js
137+
const sharedDoc = await api.query.confidentialDocs.sharedDocs(cid);
138+
console.log(sharedDoc.toHuman());
139+
```
140+
```bash
141+
# Output should look like this:
142+
{
143+
"cid": "QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr",
144+
"name": "name",
145+
"description": "desc",
146+
"to": "5FSuxe2q7qCYKie8yqmM56U4ovD1YtBb3DoPzGKjwZ98vxua",
147+
"from": "5FWtfhKTuGKm9yWqzApwTfiUL4UPWukJzEcCTGYDiYHsdKaG"
148+
}
149+
```
150+
151+
#### Update a shared confidential document's metadata
152+
```js
153+
const response = await api.tx.confidentialDocs.updateSharedDocumentMetadata({
154+
"cid": "QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr",
155+
"name": "name",
156+
"description": "desc"
157+
}).signAndSend(alice);
158+
```
159+
160+
#### Remove a shared confidential document
161+
```js
162+
const response = await api.tx.confidentialDocs.removeSharedDocument("QmeHEb5TF4zkP2H6Mg5TcrvDs5egPCJgWFBB7YZaLmK7jr").signAndSend(alice);
163+
```
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use super::*;
2+
use frame_support::pallet_prelude::*;
3+
use frame_support::sp_io::hashing::blake2_256;
4+
//use frame_system::pallet_prelude::*;
5+
use crate::types::*;
6+
7+
impl<T: Config> Pallet<T> {
8+
9+
pub fn do_set_vault(owner: T::AccountId, user_id: UserId, public_key: PublicKey, cid: CID) -> DispatchResult {
10+
Self::validate_cid(&cid)?;
11+
let hashed_account = owner.using_encoded(blake2_256);
12+
if let Some(uid) = <UserIds<T>>::get(&hashed_account){
13+
ensure!(uid == user_id, <Error<T>>::NotOwnerOfUserId);
14+
} else {
15+
ensure!(!<Vaults<T>>::contains_key(&user_id), <Error<T>>::NotOwnerOfVault);
16+
}
17+
18+
let vault = Vault{
19+
cid: cid.clone(),
20+
owner: owner.clone()
21+
};
22+
<Vaults<T>>::insert(user_id, vault.clone());
23+
<PublicKeys<T>>::insert(owner.clone(), public_key);
24+
<UserIds<T>>::insert(hashed_account.clone(), user_id);
25+
26+
Self::deposit_event(Event::VaultStored(user_id, public_key, vault));
27+
Ok(())
28+
}
29+
30+
pub fn do_set_owned_document(owner: T::AccountId, mut owned_doc: OwnedDoc<T>) -> DispatchResult {
31+
owned_doc.owner = owner.clone();
32+
Self::validate_owned_doc(&owned_doc)?;
33+
let OwnedDoc {
34+
cid,
35+
..
36+
} = owned_doc.clone();
37+
if let Some(doc) = <OwnedDocs<T>>::get(&cid) {
38+
ensure!(doc.owner == owner, <Error<T>>::NotDocOwner);
39+
} else {
40+
<OwnedDocsByOwner<T>>::try_mutate(&owner, |owned_vec| {
41+
owned_vec.try_push(cid.clone())
42+
}).map_err(|_| <Error<T>>::ExceedMaxOwnedDocs)?;
43+
}
44+
<OwnedDocs<T>>::insert(cid.clone(), owned_doc.clone());
45+
Self::deposit_event(Event::OwnedDocStored(owned_doc));
46+
Ok(())
47+
}
48+
49+
pub fn do_remove_owned_document(owner: T::AccountId, cid: CID) -> DispatchResult {
50+
let doc = <OwnedDocs<T>>::try_get(&cid).map_err(|_| <Error<T>>::DocNotFound)?;
51+
ensure!(doc.owner == owner, <Error<T>>::NotDocOwner);
52+
<OwnedDocsByOwner<T>>::try_mutate(&owner, |owned_vec| {
53+
let cid_index = owned_vec.iter().position(|c| *c==cid).ok_or(<Error<T>>::CIDNotFound)?;
54+
owned_vec.remove(cid_index);
55+
Ok(())
56+
}).map_err(|_:Error::<T>| <Error<T>>::CIDNotFound)?;
57+
<OwnedDocs<T>>::remove(cid.clone());
58+
Self::deposit_event(Event::OwnedDocRemoved(doc));
59+
Ok(())
60+
}
61+
62+
pub fn do_share_document(owner: T::AccountId, mut shared_doc: SharedDoc<T>) -> DispatchResult {
63+
shared_doc.from = owner;
64+
Self::validate_shared_doc(&shared_doc)?;
65+
let SharedDoc {
66+
cid,
67+
to,
68+
from,
69+
..
70+
} = shared_doc.clone();
71+
72+
<SharedDocsByFrom<T>>::try_mutate(&from, |shared_vec| {
73+
shared_vec.try_push(cid.clone())
74+
}).map_err(|_| <Error<T>>::ExceedMaxSharedFromDocs)?;
75+
76+
<SharedDocsByTo<T>>::try_mutate(&to, |shared_vec| {
77+
shared_vec.try_push(cid.clone())
78+
}).map_err(|_| <Error<T>>::ExceedMaxSharedToDocs)?;
79+
80+
<SharedDocs<T>>::insert(cid.clone(), shared_doc.clone());
81+
Self::deposit_event(Event::SharedDocStored(shared_doc));
82+
Ok(())
83+
}
84+
85+
pub fn do_update_shared_document_metadata(to: T::AccountId, mut shared_doc: SharedDoc<T>) -> DispatchResult {
86+
let doc = <SharedDocs<T>>::try_get(&shared_doc.cid).map_err(|_| <Error<T>>::DocNotFound)?;
87+
ensure!(doc.to == to, <Error<T>>::NotDocSharee);
88+
shared_doc.from = doc.from;
89+
shared_doc.to = to;
90+
<SharedDocs<T>>::insert(doc.cid.clone(), shared_doc.clone());
91+
Self::deposit_event(Event::SharedDocUpdated(shared_doc));
92+
Ok(())
93+
}
94+
95+
pub fn do_remove_shared_document(to: T::AccountId, cid: CID) -> DispatchResult {
96+
let doc = <SharedDocs<T>>::try_get(&cid).map_err(|_| <Error<T>>::DocNotFound)?;
97+
ensure!(doc.to == to, <Error<T>>::NotDocSharee);
98+
<SharedDocsByTo<T>>::try_mutate(&to, |shared_vec| {
99+
let cid_index = shared_vec.iter().position(|c| *c==cid).ok_or(<Error<T>>::CIDNotFound)?;
100+
shared_vec.remove(cid_index);
101+
Ok(())
102+
}).map_err(|_:Error::<T>| <Error<T>>::CIDNotFound)?;
103+
<SharedDocsByFrom<T>>::try_mutate(&doc.from, |shared_vec| {
104+
let cid_index = shared_vec.iter().position(|c| *c==cid).ok_or(<Error<T>>::CIDNotFound)?;
105+
shared_vec.remove(cid_index);
106+
Ok(())
107+
}).map_err(|_:Error::<T>| <Error<T>>::CIDNotFound)?;
108+
<SharedDocs<T>>::remove(cid.clone());
109+
Self::deposit_event(Event::SharedDocRemoved(doc));
110+
Ok(())
111+
}
112+
113+
fn validate_owned_doc(owned_doc: &OwnedDoc<T>)->DispatchResult{
114+
let OwnedDoc {
115+
cid,
116+
name,
117+
description,
118+
owner
119+
} = owned_doc;
120+
Self::validate_cid(cid)?;
121+
Self::validate_doc_name(name)?;
122+
Self::validate_doc_desc(description)?;
123+
Self::validate_has_public_key(owner)?;
124+
Ok(())
125+
}
126+
127+
fn validate_shared_doc(shared_doc: &SharedDoc<T>)->DispatchResult{
128+
let SharedDoc {
129+
cid,
130+
name,
131+
description,
132+
from,
133+
to,
134+
} = shared_doc;
135+
Self::validate_cid(cid)?;
136+
Self::validate_doc_name(name)?;
137+
Self::validate_doc_desc(description)?;
138+
ensure!(from != to, <Error<T>>::DocSharedWithSelf);
139+
ensure!(!<SharedDocs<T>>::contains_key(cid), <Error<T>>::DocAlreadyShared);
140+
Self::validate_has_public_key(from)?;
141+
Self::validate_has_public_key(to)?;
142+
Ok(())
143+
}
144+
145+
fn validate_has_public_key(who: &T::AccountId)->DispatchResult{
146+
ensure!(<PublicKeys<T>>::contains_key(who), <Error<T>>::AccountHasNoPublicKey);
147+
Ok(())
148+
}
149+
fn validate_cid(cid: &CID)->DispatchResult{
150+
ensure!(cid.len() > 0, <Error<T>>::CIDNoneValue);
151+
Ok(())
152+
}
153+
154+
fn validate_doc_name(doc_name: &DocName<T>)->DispatchResult{
155+
ensure!(doc_name.len() >= T::DocNameMinLen::get().try_into().unwrap(), <Error<T>>::DocNameTooShort);
156+
Ok(())
157+
}
158+
159+
fn validate_doc_desc(doc_desc: &DocDesc<T>)->DispatchResult{
160+
ensure!(doc_desc.len() >= T::DocDescMinLen::get().try_into().unwrap(), <Error<T>>::DocDescTooShort);
161+
Ok(())
162+
}
163+
}

0 commit comments

Comments
 (0)