Skip to content

Commit

Permalink
Merge pull request #485 from okp4/feat/dataverse-claims-storage
Browse files Browse the repository at this point in the history
Feat/dataverse claims storage
  • Loading branch information
amimart authored Feb 18, 2024
2 parents e39c75c + bd4a169 commit ed75190
Show file tree
Hide file tree
Showing 24 changed files with 885 additions and 70 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/okp4-dataverse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ cw-utils.workspace = true
cw2.workspace = true
itertools = "0.12.1"
multibase = "0.9.1"
okp4-cognitarium-client.workspace = true
okp4-cognitarium.workspace = true
okp4-rdf.workspace = true
rio_api.workspace = true
Expand Down
267 changes: 259 additions & 8 deletions contracts/okp4-dataverse/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,34 +67,48 @@ pub fn instantiate(
pub fn execute(
deps: DepsMut<'_>,
_env: Env,
_info: MessageInfo,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::SubmitClaims {
metadata,
format: _,
} => execute::submit_claims(deps, metadata),
} => execute::submit_claims(deps, info, metadata),
_ => Err(StdError::generic_err("Not implemented").into()),
}
}

pub mod execute {
use super::*;
use crate::credential::vc::VerifiableCredential;
use crate::registrar::credential::DataverseCredential;
use crate::registrar::registry::ClaimRegistrar;
use okp4_rdf::dataset::Dataset;
use okp4_rdf::serde::NQuadsReader;
use std::io::BufReader;

pub fn submit_claims(deps: DepsMut<'_>, data: Binary) -> Result<Response, ContractError> {
pub fn submit_claims(
deps: DepsMut<'_>,
info: MessageInfo,
data: Binary,
) -> Result<Response, ContractError> {
let buf = BufReader::new(data.as_slice());
let mut reader = NQuadsReader::new(buf);
let rdf_quads = reader.read_all()?;
let vc_dataset = Dataset::from(rdf_quads.as_slice());
let vc = VerifiableCredential::try_from(&vc_dataset)?;
vc.verify(deps)?;
vc.verify(&deps)?;

let credential = DataverseCredential::try_from((info.sender, &vc))?;
let registrar = ClaimRegistrar::try_new(deps.storage)?;

Ok(Response::default())
Ok(Response::default()
.add_attribute("action", "submit_claims")
.add_attribute("credential", credential.id)
.add_attribute("subject", credential.subject)
.add_attribute("type", credential.r#type)
.add_message(registrar.submit_claim(&deps, &credential)?))
}
}

Expand All @@ -108,12 +122,19 @@ pub mod query {}
#[cfg(test)]
mod tests {
use super::*;
use crate::msg::{TripleStoreConfig, TripleStoreLimitsInput};
use crate::msg::{RdfFormat, TripleStoreConfig, TripleStoreLimitsInput};
use crate::testutil::testutil::read_test_data;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{
Addr, Attribute, ContractResult, HexBinary, SubMsg, SystemError, SystemResult, Uint128,
Uint64, WasmQuery,
from_json, Addr, Attribute, ContractResult, CosmosMsg, HexBinary, SubMsg, SystemError,
SystemResult, Uint128, Uint64, WasmQuery,
};
use okp4_cognitarium::msg::{
DataFormat, Head, Node, Results, SelectItem, SelectQuery, SelectResponse,
SimpleWhereCondition, TriplePattern, Value, VarOrNode, VarOrNodeOrLiteral, WhereCondition,
IRI,
};
use std::collections::BTreeMap;

#[test]
fn proper_instantiate() {
Expand Down Expand Up @@ -175,4 +196,234 @@ mod tests {
}
)
}

#[test]
fn proper_submit_claims() {
let mut deps = mock_dependencies();
deps.querier.update_wasm(|query| match query {
WasmQuery::Smart { contract_addr, msg } => {
if contract_addr != "my-dataverse-addr" {
return SystemResult::Err(SystemError::NoSuchContract {
addr: contract_addr.to_string(),
});
}
let query_msg: StdResult<okp4_cognitarium::msg::QueryMsg> = from_json(msg);
assert_eq!(
query_msg,
Ok(okp4_cognitarium::msg::QueryMsg::Select {
query: SelectQuery {
prefixes: vec![],
limit: Some(1u32),
select: vec![SelectItem::Variable("p".to_string())],
r#where: vec![WhereCondition::Simple(
SimpleWhereCondition::TriplePattern(TriplePattern {
subject: VarOrNode::Node(Node::NamedNode(IRI::Full(
"http://example.edu/credentials/3732".to_string(),
))),
predicate: VarOrNode::Variable("p".to_string()),
object: VarOrNodeOrLiteral::Variable("o".to_string()),
})
)],
}
})
);

let select_resp = SelectResponse {
results: Results { bindings: vec![] },
head: Head { vars: vec![] },
};
SystemResult::Ok(ContractResult::Ok(to_json_binary(&select_resp).unwrap()))
}
_ => SystemResult::Err(SystemError::Unknown {}),
});

DATAVERSE
.save(
deps.as_mut().storage,
&Dataverse {
name: "my-dataverse".to_string(),
triplestore_address: Addr::unchecked("my-dataverse-addr"),
},
)
.unwrap();

let resp = execute(
deps.as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary(read_test_data("vc-eddsa-2020-ok.nq")),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_ok());
let resp = resp.unwrap();
assert_eq!(resp.messages.len(), 1);
assert_eq!(
resp.attributes,
vec![
Attribute::new("action", "submit_claims"),
Attribute::new("credential", "http://example.edu/credentials/3732"),
Attribute::new(
"subject",
"did:key:zDnaeUm3QkcyZWZTPttxB711jgqRDhkwvhF485SFw1bDZ9AQw"
),
Attribute::new(
"type",
"https://example.org/examples#UniversityDegreeCredential"
),
]
);

let expected_data = "<http://example.edu/credentials/3732> <dataverse:credential#submitterAddress> \"okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf\" .
<http://example.edu/credentials/3732> <dataverse:credential#issuer> <did:key:z6MkpwdnLPAm4apwcrRYQ6fZ3rAcqjLZR4AMk14vimfnozqY> .
<http://example.edu/credentials/3732> <dataverse:credential#type> <https://example.org/examples#UniversityDegreeCredential> .
<http://example.edu/credentials/3732> <dataverse:credential#validFrom> \"2024-02-16T00:00:00Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
<http://example.edu/credentials/3732> <dataverse:credential#subject> <did:key:zDnaeUm3QkcyZWZTPttxB711jgqRDhkwvhF485SFw1bDZ9AQw> .
_:c0 <https://example.org/examples#degree> _:b2 .
_:b2 <http://schema.org/name> \"Bachelor of Science and Arts\"^^<http://www.w3.org/1999/02/22-rdf-syntax-ns#HTML> .
_:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://example.org/examples#BachelorDegree> .
<http://example.edu/credentials/3732> <dataverse:credential#claim> _:c0 .
<http://example.edu/credentials/3732> <dataverse:credential#validUntil> \"2026-02-16T00:00:00Z\"^^<http://www.w3.org/2001/XMLSchema#dateTime> .\n";

match resp.messages[0].msg.clone() {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) if contract_addr == "my-dataverse-addr".to_string() && funds == vec![] => {
let exec_msg: StdResult<okp4_cognitarium::msg::ExecuteMsg> = from_json(msg);
assert!(exec_msg.is_ok());
match exec_msg.unwrap() {
okp4_cognitarium::msg::ExecuteMsg::InsertData { format, data } => {
assert_eq!(format, Some(DataFormat::NTriples));
assert_eq!(String::from_utf8(data.0).unwrap(), expected_data);
}
_ => assert!(false),
}
}
_ => assert!(false),
}
}

#[test]
fn submit_nonrdf_claims() {
let resp = execute(
mock_dependencies().as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary("notrdf".as_bytes().to_vec()),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_err());
assert!(matches!(resp.err().unwrap(), ContractError::ParseRDF(_)))
}

#[test]
fn submit_invalid_claims() {
let resp = execute(
mock_dependencies().as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary(vec![]),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_err());
assert!(matches!(
resp.err().unwrap(),
ContractError::InvalidCredential(_)
))
}

#[test]
fn submit_unverified_claims() {
let resp = execute(
mock_dependencies().as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary(read_test_data("vc-eddsa-2020-ok-unsecured.nq")),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_err());
assert!(matches!(
resp.err().unwrap(),
ContractError::CredentialVerification(_)
))
}

#[test]
fn submit_unsupported_claims() {
let resp = execute(
mock_dependencies().as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary(read_test_data("vc-unsupported-1.nq")),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_err());
assert!(matches!(
resp.err().unwrap(),
ContractError::UnsupportedCredential(_)
))
}

#[test]
fn submit_existing_claims() {
let mut deps = mock_dependencies();
deps.querier.update_wasm(|query| match query {
WasmQuery::Smart { .. } => {
let select_resp = SelectResponse {
results: Results {
bindings: vec![BTreeMap::from([(
"p".to_string(),
Value::BlankNode {
value: "".to_string(),
},
)])],
},
head: Head { vars: vec![] },
};
SystemResult::Ok(ContractResult::Ok(to_json_binary(&select_resp).unwrap()))
}
_ => SystemResult::Err(SystemError::Unknown {}),
});

DATAVERSE
.save(
deps.as_mut().storage,
&Dataverse {
name: "my-dataverse".to_string(),
triplestore_address: Addr::unchecked("my-dataverse-addr"),
},
)
.unwrap();

let resp = execute(
deps.as_mut(),
mock_env(),
mock_info("okp41072nc6egexqr2v6vpp7yxwm68plvqnkf6xsytf", &[]),
ExecuteMsg::SubmitClaims {
metadata: Binary(read_test_data("vc-eddsa-2020-ok.nq")),
format: Some(RdfFormat::NQuads),
},
);

assert!(resp.is_err());
assert!(
matches!(resp.err().unwrap(), ContractError::CredentialAlreadyExists(id) if id == "http://example.edu/credentials/3732")
);
}
}
4 changes: 2 additions & 2 deletions contracts/okp4-dataverse/src/credential/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl From<(CanonicalizationAlg, DigestAlg, SignatureAlg)> for CryptoSuite {
impl CryptoSuite {
pub fn verify_document(
&self,
deps: DepsMut<'_>,
deps: &'_ DepsMut<'_>,
unsecured_doc: &[Quad<'_>],
proof_opts: &[Quad<'_>],
proof_value: &[u8],
Expand Down Expand Up @@ -77,7 +77,7 @@ impl CryptoSuite {

fn verify(
&self,
deps: DepsMut<'_>,
deps: &'_ DepsMut<'_>,
message: &[u8],
signature: &[u8],
pub_key: &[u8],
Expand Down
3 changes: 0 additions & 3 deletions contracts/okp4-dataverse/src/credential/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ pub enum InvalidCredentialError {
#[error("Missing issuance date")]
MissingIssuanceDate,

#[error("Missing proof, at least a supported one")]
MissingProof,

#[error("Invalid proof: {0}")]
InvalidProof(#[from] InvalidProofError),

Expand Down
2 changes: 1 addition & 1 deletion contracts/okp4-dataverse/src/credential/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod crypto;
pub mod error;
mod proof;
mod rdf_marker;
pub mod rdf_marker;
pub mod vc;
5 changes: 2 additions & 3 deletions contracts/okp4-dataverse/src/credential/rdf_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ pub const RDF_DATE_TYPE: NamedNode<'_> = NamedNode {
iri: "http://www.w3.org/2001/XMLSchema#dateTime",
};

pub const VC_RDF_TYPE: Term<'_> = Term::NamedNode(NamedNode {
iri: "https://www.w3.org/2018/credentials#VerifiableCredential",
});
pub const IRI_VC_TYPE: &str = "https://www.w3.org/2018/credentials#VerifiableCredential";
pub const VC_RDF_TYPE: Term<'_> = Term::NamedNode(NamedNode { iri: IRI_VC_TYPE });
pub const VC_RDF_ISSUER: NamedNode<'_> = NamedNode {
iri: "https://www.w3.org/2018/credentials#issuer",
};
Expand Down
Loading

0 comments on commit ed75190

Please sign in to comment.