diff --git a/contracts/axone-dataverse/src/contract.rs b/contracts/axone-dataverse/src/contract.rs index 278b507b..16fc58c8 100644 --- a/contracts/axone-dataverse/src/contract.rs +++ b/contracts/axone-dataverse/src/contract.rs @@ -68,14 +68,14 @@ pub fn instantiate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut<'_>, - _env: Env, + env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { nonpayable(&info)?; match msg { ExecuteMsg::SubmitClaims { claims, format: _ } => { - execute::submit_claims(deps, info, claims) + execute::submit_claims(deps, env, info, claims) } _ => Err(StdError::generic_err("Not implemented").into()), } @@ -92,6 +92,7 @@ pub mod execute { pub fn submit_claims( deps: DepsMut<'_>, + env: Env, info: MessageInfo, claims: Binary, ) -> Result { @@ -102,7 +103,7 @@ pub mod execute { let vc = VerifiableCredential::try_from(&vc_dataset)?; vc.verify(&deps)?; - let credential = DataverseCredential::try_from((info.sender, &vc))?; + let credential = DataverseCredential::try_from((env, info, &vc))?; let registrar = ClaimRegistrar::try_new(deps.storage)?; Ok(Response::default() @@ -368,16 +369,20 @@ mod tests { ] ); - let expected_data = " \"axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0\" . - . - . - \"2024-02-16T00:00:00Z\"^^ . - . + let expected_data = r#" "12345" . + "1571797419" . + "axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0" . + . + . + "2024-02-16T00:00:00Z"^^ . + . + "3" . _:c0 _:b0 . -_:b0 \"Bachelor of Science and Arts\"^^ . +_:b0 "Bachelor of Science and Arts"^^ . _:b0 . - _:c0 . - \"2026-02-16T00:00:00Z\"^^ .\n"; + _:c0 . + "2026-02-16T00:00:00Z"^^ . +"#; match resp.messages[0].msg.clone() { CosmosMsg::Wasm(WasmMsg::Execute { diff --git a/contracts/axone-dataverse/src/registrar/credential.rs b/contracts/axone-dataverse/src/registrar/credential.rs index 33143151..55e279ce 100644 --- a/contracts/axone-dataverse/src/registrar/credential.rs +++ b/contracts/axone-dataverse/src/registrar/credential.rs @@ -1,12 +1,15 @@ use crate::credential::rdf_marker::IRI_VC_TYPE; use crate::credential::vc::{Claim, VerifiableCredential}; use crate::ContractError; -use cosmwasm_std::Addr; +use cosmwasm_std::{Addr, Env, MessageInfo}; use itertools::Itertools; #[derive(Debug, PartialEq)] pub struct DataverseCredential<'a> { - pub submitter_addr: Addr, + pub height: String, + pub timestamp: String, + pub tx_index: Option, + pub sender: Addr, pub id: &'a str, pub issuer: &'a str, pub r#type: &'a str, @@ -38,14 +41,17 @@ impl<'a> DataverseCredential<'a> { } } -impl<'a> TryFrom<(Addr, &'a VerifiableCredential<'a>)> for DataverseCredential<'a> { +impl<'a> TryFrom<(Env, MessageInfo, &'a VerifiableCredential<'a>)> for DataverseCredential<'a> { type Error = ContractError; fn try_from( - (submitter_addr, vc): (Addr, &'a VerifiableCredential<'a>), + (env, info, vc): (Env, MessageInfo, &'a VerifiableCredential<'a>), ) -> Result { Ok(DataverseCredential { - submitter_addr, + height: env.block.height.to_string(), + timestamp: env.block.time.seconds().to_string(), + tx_index: env.transaction.map(|tx| tx.index.to_string()), + sender: info.sender, id: vc.id, issuer: vc.issuer, r#type: DataverseCredential::extract_vc_type(vc)?, @@ -61,21 +67,25 @@ mod test { use super::*; use crate::testutil::testutil; use axone_rdf::dataset::Dataset; + use cosmwasm_std::testing::message_info; use rio_api::model::{Literal, NamedNode, Quad}; + use testing::addr::{addr, SENDER}; + use testing::mock::mock_env_addr; #[test] fn proper_from_verifiable() { let owned_quads = testutil::read_test_quads("vc-valid.nq"); let dataset = Dataset::from(owned_quads.as_slice()); let vc = VerifiableCredential::try_from(&dataset).unwrap(); - let dc_res = DataverseCredential::try_from(( - Addr::unchecked("axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0"), - &vc, - )); + let dc_res = + DataverseCredential::try_from((mock_env_addr(), message_info(&addr(SENDER), &[]), &vc)); assert!(dc_res.is_ok()); assert_eq!(dc_res.unwrap(), DataverseCredential { - submitter_addr: Addr::unchecked("axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0"), + height: "12345".to_string(), + timestamp: "1571797419".to_string(), + tx_index: Some("3".to_string()), + sender: addr(SENDER), id: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/72cab400-5bd6-4eb4-8605-a5ee8c1a45c9", issuer: "did:key:zQ3shs7auhJSmVJpiUbQWco6bxxEhSqWnVEPvaBHBRvBKw6Q3", r#type: "https://w3id.org/axone/ontology/vnext/schema/credential/digital-service/description/DigitalServiceDescriptionCredential", @@ -120,7 +130,8 @@ mod test { let dataset = Dataset::from(owned_quads.as_slice()); let vc = VerifiableCredential::try_from(&dataset).unwrap(); let dc_res = DataverseCredential::try_from(( - Addr::unchecked("axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0"), + mock_env_addr(), + message_info(&addr(SENDER), &[]), &vc, )); diff --git a/contracts/axone-dataverse/src/registrar/rdf.rs b/contracts/axone-dataverse/src/registrar/rdf.rs index e8f37b2a..4c605fb8 100644 --- a/contracts/axone-dataverse/src/registrar/rdf.rs +++ b/contracts/axone-dataverse/src/registrar/rdf.rs @@ -8,36 +8,48 @@ use cosmwasm_std::{Binary, StdError}; use rio_api::model::{BlankNode, Literal, NamedNode, Subject, Term, Triple}; pub const VC_RESERVED_PREDICATES: &[NamedNode<'_>] = &[ - VC_SUBMITTER_ADDRESS, - VC_TYPE, - VC_ISSUER, - VC_VALID_FROM, - VC_VALID_UNTIL, - VC_SUBJECT, - VC_CLAIM, + VC_HEADER_HEIGHT, + VC_HEADER_TIMESTAMP, + VC_HEADER_TX, + VC_HEADER_SENDER, + VC_BODY_TYPE, + VC_BODY_ISSUER, + VC_BODY_VALID_FROM, + VC_BODY_VALID_UNTIL, + VC_BODY_SUBJECT, + VC_BODY_CLAIM, VC_CLAIM_ORIGINAL_NODE, ]; -pub const VC_SUBMITTER_ADDRESS: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#submitterAddress", +pub const VC_HEADER_HEIGHT: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:header#height", }; -pub const VC_TYPE: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#type", +pub const VC_HEADER_TIMESTAMP: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:header#timestamp", }; -pub const VC_ISSUER: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#issuer", +pub const VC_HEADER_TX: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:header#tx_index", }; -pub const VC_VALID_FROM: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#validFrom", +pub const VC_HEADER_SENDER: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:header#sender", }; -pub const VC_VALID_UNTIL: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#validUntil", +pub const VC_BODY_TYPE: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#type", }; -pub const VC_SUBJECT: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#subject", +pub const VC_BODY_ISSUER: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#issuer", }; -pub const VC_CLAIM: NamedNode<'_> = NamedNode { - iri: "dataverse:credential#claim", +pub const VC_BODY_VALID_FROM: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#validFrom", +}; +pub const VC_BODY_VALID_UNTIL: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#validUntil", +}; +pub const VC_BODY_SUBJECT: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#subject", +}; +pub const VC_BODY_CLAIM: NamedNode<'_> = NamedNode { + iri: "dataverse:credential:body#claim", }; /// Used when a claim triple contains a named node as object to establish a hierarchy, we replace this hierarchical link @@ -85,24 +97,38 @@ impl<'a> DataverseCredential<'a> { let mut triples = vec![ Triple { subject: c_subject, - predicate: VC_SUBMITTER_ADDRESS, + predicate: VC_HEADER_HEIGHT, + object: Term::Literal(Literal::Simple { + value: &self.height, + }), + }, + Triple { + subject: c_subject, + predicate: VC_HEADER_TIMESTAMP, + object: Term::Literal(Literal::Simple { + value: &self.timestamp, + }), + }, + Triple { + subject: c_subject, + predicate: VC_HEADER_SENDER, object: Term::Literal(Literal::Simple { - value: self.submitter_addr.as_str(), + value: self.sender.as_str(), }), }, Triple { subject: c_subject, - predicate: VC_ISSUER, + predicate: VC_BODY_ISSUER, object: Term::NamedNode(NamedNode { iri: self.issuer }), }, Triple { subject: c_subject, - predicate: VC_TYPE, + predicate: VC_BODY_TYPE, object: Term::NamedNode(NamedNode { iri: self.r#type }), }, Triple { subject: c_subject, - predicate: VC_VALID_FROM, + predicate: VC_BODY_VALID_FROM, object: Term::Literal(Literal::Typed { value: self.valid_from, datatype: RDF_DATE_TYPE, @@ -110,17 +136,25 @@ impl<'a> DataverseCredential<'a> { }, Triple { subject: c_subject, - predicate: VC_SUBJECT, + predicate: VC_BODY_SUBJECT, object: Term::NamedNode(NamedNode { iri: self.claim.id }), }, ]; + if let Some(tx_index) = &self.tx_index { + triples.push(Triple { + subject: c_subject, + predicate: VC_HEADER_TX, + object: Term::Literal(Literal::Simple { value: tx_index }), + }); + } + triples.extend(self.claim_as_triples(claim_node, named_issuer, blank_issuer)?); if let Some(valid_until) = self.valid_until { triples.push(Triple { subject: c_subject, - predicate: VC_VALID_UNTIL, + predicate: VC_BODY_VALID_UNTIL, object: Term::Literal(Literal::Typed { value: valid_until, datatype: RDF_DATE_TYPE, @@ -219,7 +253,7 @@ impl<'a> DataverseCredential<'a> { triples.push(Triple { subject: Subject::NamedNode(NamedNode { iri: self.id }), - predicate: VC_CLAIM, + predicate: VC_BODY_CLAIM, object: Term::BlankNode(claim_node), }); @@ -241,28 +275,32 @@ mod test { use crate::credential::vc::VerifiableCredential; use crate::testutil::testutil; use axone_rdf::dataset::Dataset; - use cosmwasm_std::Addr; + use cosmwasm_std::testing::message_info; + use testing::addr::{addr, SENDER}; + use testing::mock::mock_env_addr; #[test] fn proper_serialization() { let owned_quads = testutil::read_test_quads("vc-valid.nq"); let dataset = Dataset::from(owned_quads.as_slice()); let vc = VerifiableCredential::try_from(&dataset).unwrap(); - let dc = DataverseCredential::try_from(( - Addr::unchecked("axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0"), - &vc, - )) - .unwrap(); + let dc = + DataverseCredential::try_from((mock_env_addr(), message_info(&addr(SENDER), &[]), &vc)) + .unwrap(); - let expected = " \"axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0\" . - . - . - \"2024-01-22T00:00:00\"^^ . - . + let expected = r#" "12345" . + "1571797419" . + "cosmwasm1pgm8hyk0pvphmlvfjc8wsvk4daluz5tgrw6pu5mfpemk74uxnx9qlm3aqg" . + . + . + "2024-01-22T00:00:00"^^ . + . + "3" . _:c0 . -_:c0 \"Cloud\" . - _:c0 . - \"2025-01-22T00:00:00\"^^ .\n"; +_:c0 "Cloud" . + _:c0 . + "2025-01-22T00:00:00"^^ . +"#; let serialization_res = dc.serialize(DataFormat::NQuads); assert!(serialization_res.is_ok()); @@ -278,24 +316,26 @@ _:c0 . - . - \"2024-01-22T00:00:00\"^^ . - . + let expected = r#" "12345" . + "1571797419" . + "cosmwasm1pgm8hyk0pvphmlvfjc8wsvk4daluz5tgrw6pu5mfpemk74uxnx9qlm3aqg" . + . + . + "2024-01-22T00:00:00"^^ . + . + "3" . _:c0 . -_:c0 \"Cloud\" . +_:c0 "Cloud" . _:c0 _:a0 . -_:a0 \"nested value\" . +_:a0 "nested value" . _:a0 . - _:c0 . - \"2025-01-22T00:00:00\"^^ .\n"; + _:c0 . + "2025-01-22T00:00:00"^^ . +"#; let serialization_res = dc.serialize(DataFormat::NQuads); assert!(serialization_res.is_ok()); @@ -311,11 +351,9 @@ _:a0 . let owned_quads = testutil::read_test_quads("vc-unsupported-4.nq"); let dataset = Dataset::from(owned_quads.as_slice()); let vc = VerifiableCredential::try_from(&dataset).unwrap(); - let dc = DataverseCredential::try_from(( - Addr::unchecked("axone1072nc6egexqr2v6vpp7yxwm68plvqnkf5uemr0"), - &vc, - )) - .unwrap(); + let dc = + DataverseCredential::try_from((mock_env_addr(), message_info(&addr(SENDER), &[]), &vc)) + .unwrap(); let res = dc.serialize(DataFormat::NQuads); assert!(res.is_err()); diff --git a/contracts/axone-dataverse/testdata/vc-unsupported-4.nq b/contracts/axone-dataverse/testdata/vc-unsupported-4.nq index c540e4a7..270513e3 100644 --- a/contracts/axone-dataverse/testdata/vc-unsupported-4.nq +++ b/contracts/axone-dataverse/testdata/vc-unsupported-4.nq @@ -1,6 +1,6 @@ . "Cloud" . - "this shall not be allowed" . + "this shall not be allowed" . . . .