Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema and Credential Definition validations in VDR #21

Merged
merged 19 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/design/endorsement.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,13 @@ function endorseCredentialDefinition(
// Prepare schema endorsing bytes which need to be signed by an identity owner
pub async fn build_create_schema_endorsing_data(
client: &LedgerClient,
id: &SchemaId,
schema: &Schema,
) -> VdrResult<TransactionEndorsingData>;

// Build transaction to endorse Schema
pub async fn build_create_schema_signed_transaction(
client: &LedgerClient,
sender: &Address,
id: &SchemaId,
schema: &Schema,
signature: &SignatureData,
) -> VdrResult<Transaction>;
Expand Down
6 changes: 0 additions & 6 deletions docs/design/vdr.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,30 +754,26 @@ pub async fn resolve_did(
/// # Params
/// - `client` client connected to the network where contract will be executed
/// - `from` transaction sender account address
/// - `id` id of schema to be created
/// - `schema` Schema object matching to the specification - https://hyperledger.github.io/anoncreds-spec/#term:schema
///
/// # Returns
/// Write transaction to sign and submit
pub async fn build_create_schema_transaction(
client: &LedgerClient,
from: &Address,
id: &SchemaId,
schema: &Schema,
) -> VdrResult<Transaction>;

/// Prepared data for execution of SchemaRegistry.createSchema contract method to endorse a new Schema
///
/// #Params
/// - `client` client connected to the network where contract will be executed
/// - `id` id of schema to be created
/// - `schema` Schema object matching to the specification - https://hyperledger.github.io/anoncreds-spec/#term:schema
///
/// #Returns
/// data: TransactionEndorsingData - transaction endorsement data to sign
pub async fn build_create_schema_endorsing_data(
client: &LedgerClient,
id: &SchemaId,
schema: &Schema,
) -> VdrResult<TransactionEndorsingData>;

Expand All @@ -787,7 +783,6 @@ pub async fn build_create_schema_endorsing_data(
///
/// #Params
/// - `client` client connected to the network where contract will be executed
/// - `id` id of schema to be created
/// - `schema` Schema object matching to the specification - https://hyperledger.github.io/anoncreds-spec/#term:schema
/// - `signature` signature of schema issuer
///
Expand All @@ -796,7 +791,6 @@ pub async fn build_create_schema_endorsing_data(
pub async fn build_create_schema_signed_transaction(
client: &LedgerClient,
sender: &Address,
id: &SchemaId,
schema: &Schema,
signature: &SignatureData,
) -> VdrResult<Transaction>
Expand Down
30 changes: 0 additions & 30 deletions vdr/Cargo.lock

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

2 changes: 1 addition & 1 deletion vdr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ rstest = "0.18.2"
mockall = "0.12.0"
env_logger = "0.10.0"
rand = "0.8.5"
ed25519-dalek = { version = "2", features = ["rand_core"] }
ed25519-dalek = { version = "2", features = ["rand_core"] }
53 changes: 12 additions & 41 deletions vdr/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,11 @@ impl Debug for LedgerClient {
#[cfg(test)]
pub mod test {
use super::*;
use crate::{client::MockClient, signer::basic_signer::test::basic_signer};
use crate::{
client::MockClient, types::transaction::test::read_transaction, utils::init_env_logger,
};
use once_cell::sync::Lazy;
use std::{env, fs, sync::RwLock};
use std::{env, fs};

pub const CHAIN_ID: u64 = 1337;
pub const CONTRACTS_SPEC_BASE_PATH: &str = "../smart_contracts/artifacts/contracts/";
Expand Down Expand Up @@ -313,6 +315,7 @@ pub mod test {
}

pub fn mock_client() -> LedgerClient {
init_env_logger();
let mut ledger_client = LedgerClient::new(
CHAIN_ID,
RPC_NODE_ADDRESS,
Expand All @@ -328,38 +331,6 @@ pub mod test {
ledger_client
}

pub fn write_transaction() -> Transaction {
let transaction = Transaction {
type_: TransactionType::Write,
from: Some(TRUSTEE_ACC.clone()),
to: VALIDATOR_CONTROL_ADDRESS.clone(),
nonce: Some(DEFAULT_NONCE.clone()),
chain_id: CHAIN_ID,
data: vec![],
signature: RwLock::new(None),
hash: None,
};
let signer = basic_signer();
let sign_bytes = transaction.get_signing_bytes().unwrap();
let signature = signer.sign(&sign_bytes, TRUSTEE_ACC.as_ref()).unwrap();
transaction.set_signature(signature);

transaction
}

pub fn read_transaction() -> Transaction {
Transaction {
type_: TransactionType::Read,
from: None,
to: VALIDATOR_CONTROL_ADDRESS.clone(),
nonce: None,
chain_id: CHAIN_ID,
data: vec![],
signature: RwLock::new(None),
hash: None,
}
}

pub fn mock_custom_client(client: Box<dyn Client>) -> LedgerClient {
let mut ledger_client = LedgerClient::new(
CHAIN_ID,
Expand Down Expand Up @@ -413,17 +384,17 @@ pub mod test {
name: VALIDATOR_CONTROL_NAME.to_string(),
abi: Value::Array(vec ! []),
}),
}], VdrError::ContractInvalidSpec("".to_string()))]
}], VdrError::ContractInvalidSpec("Either `spec_path` or `spec` must be provided".to_string()))]
#[case::non_existent_spec_path(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: Some(build_contract_path("")),
spec: None,
}], VdrError::ContractInvalidSpec("".to_string()))]
}], VdrError::ContractInvalidSpec("Unable to read contract spec file. Err: \"Is a directory (os error 21)\"".to_string()))]
#[case::empty_contract_spec(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: None,
spec: None,
}], VdrError::ContractInvalidSpec("".to_string()))]
}], VdrError::ContractInvalidSpec("Either `spec_path` or `spec` must be provided".to_string()))]
fn test_create_client_errors(
#[case] contract_config: Vec<ContractConfig>,
#[case] expected_error: VdrError,
Expand All @@ -432,12 +403,12 @@ pub mod test {
.err()
.unwrap();

assert!(matches!(client_err, expected_error));
assert_eq!(client_err, expected_error);
}

#[rstest]
#[case::empty_recipient_address("", VdrError::ClientInvalidTransaction("".to_string()))]
#[case::invalid_recipient_address(INVALID_ADDRESS, VdrError::ClientInvalidTransaction("".to_string()))]
#[case::empty_recipient_address("", VdrError::ClientInvalidTransaction("Invalid transaction target address \"0x\"".to_string()))]
#[case::invalid_recipient_address(INVALID_ADDRESS, VdrError::ClientInvalidTransaction("Invalid transaction target address \"0x123\"".to_string()))]
async fn call_transaction_various_recipient_addresses(
#[case] recipient_address: &str,
#[case] expected_error: VdrError,
Expand All @@ -450,7 +421,7 @@ pub mod test {

let error = client.submit_transaction(&transaction).await.unwrap_err();

assert!(matches!(error, expected_error));
assert_eq!(error, expected_error);
}

#[async_std::test]
Expand Down
1 change: 0 additions & 1 deletion vdr/src/contracts/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub mod role_control;
pub mod types;

pub use role_control::*;
pub use types::*;
54 changes: 47 additions & 7 deletions vdr/src/contracts/cl/credential_definition_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub async fn build_create_credential_definition_transaction(
id: &CredentialDefinitionId,
credential_definition: &CredentialDefinition,
) -> VdrResult<Transaction> {
// TODO: validate credential definition
credential_definition.validate()?;
let identity = Address::try_from(&credential_definition.issuer_id)?;
TransactionBuilder::new()
.set_contract(CONTRACT_NAME)
Expand Down Expand Up @@ -70,6 +70,7 @@ pub async fn build_create_credential_definition_endorsing_data(
id: &CredentialDefinitionId,
credential_definition: &CredentialDefinition,
) -> VdrResult<TransactionEndorsingData> {
credential_definition.validate()?;
let identity = Address::try_from(&credential_definition.issuer_id)?;
TransactionEndorsingDataBuilder::new()
.set_contract(CONTRACT_NAME)
Expand Down Expand Up @@ -104,7 +105,7 @@ pub async fn build_create_credential_definition_signed_transaction(
credential_definition: &CredentialDefinition,
signature: &SignatureData,
) -> VdrResult<Transaction> {
// TODO: validate credential definition
credential_definition.validate()?;
let identity = Address::try_from(&credential_definition.issuer_id)?;
TransactionBuilder::new()
.set_contract(CONTRACT_NAME)
Expand Down Expand Up @@ -207,7 +208,6 @@ pub fn parse_credential_definition_created_event(
client: &LedgerClient,
log: &EventLog,
) -> VdrResult<CredentialDefinitionCreatedEvent> {
// TODO: validate schema
EventParser::new()
.set_contract(CONTRACT_NAME)
.set_event(EVENT_CREDENTIAL_DEFINITION_CREATED)
Expand Down Expand Up @@ -243,11 +243,21 @@ pub async fn resolve_credential_definition(

if events.len() != 1 {
return Err(VdrError::ClientInvalidResponse(
format!("Unable to resolve schema: Unexpected amout of schema created events received for id: {:?}", id)
format!("Unable to resolve schema: Unexpected amount of schema created events received for id: {:?}", id)
));
}

let cred_def = parse_credential_definition_created_event(client, &events[0])?.cred_def;

let cred_def_id = cred_def.id();
if &cred_def_id != id {
return Err(VdrError::InvalidCredentialDefinition(format!(
"Credential Definition ID {} does not match to requested {}",
cred_def_id.to_string(),
id.to_string()
)));
}

Ok(cred_def)
}

Expand All @@ -260,7 +270,9 @@ pub mod test {
},
contracts::{
cl::types::{
credential_definition::test::{credential_definition, CREDENTIAL_DEFINITION_TAG},
credential_definition::test::{
credential_definition, credential_definition_value, CREDENTIAL_DEFINITION_TAG,
},
schema::test::SCHEMA_ID,
schema_id::SchemaId,
},
Expand All @@ -272,6 +284,8 @@ pub mod test {

mod build_create_credential_definition_transaction {
use super::*;
use rstest::rstest;
use serde_json::Value;

#[async_std::test]
async fn build_create_credential_definition_transaction_test() {
Expand Down Expand Up @@ -329,6 +343,32 @@ pub mod test {
};
assert_eq!(expected_transaction, transaction);
}

#[rstest]
#[case("", credential_definition_value())]
#[case(CREDENTIAL_DEFINITION_TAG, Value::Null)]
async fn build_create_credential_definition_transaction_errors(
#[case] tag: &str,
#[case] value: Value,
) {
init_env_logger();
let client = mock_client();
let (id, mut cred_def) =
credential_definition(&DID::from(ISSUER_ID), &SchemaId::from(SCHEMA_ID), Some(tag));
cred_def.tag = tag.to_string();
cred_def.value = value;

let err = build_create_credential_definition_transaction(
&client,
&TRUSTEE_ACC,
&id,
&cred_def,
)
.await
.unwrap_err();

assert!(matches!(err, VdrError::InvalidCredentialDefinition { .. }));
}
}

mod build_resolve_credential_definition_transaction {
Expand Down Expand Up @@ -360,8 +400,8 @@ pub mod test {
}

mod parse_resolve_credential_definition_result {
use super::*;

// use super::*;
//
// #[test]
// fn parse_resolve_credential_definition_result_test() {
// init_env_logger();
Expand Down
Loading
Loading