diff --git a/Jenkinsfile.cd b/Jenkinsfile.cd index 8cd3aaa6da..119d5014d3 100644 --- a/Jenkinsfile.cd +++ b/Jenkinsfile.cd @@ -1429,7 +1429,7 @@ def shell(command) { } def setupRust() { - shell("rustup default 1.31.0") + shell("rustup default 1.32.0") } def androidPublishing() { diff --git a/Jenkinsfile.ci b/Jenkinsfile.ci index 9cc2965793..ca436d7547 100644 --- a/Jenkinsfile.ci +++ b/Jenkinsfile.ci @@ -791,7 +791,7 @@ def shell(command) { } def setupRust() { - shell("rustup default 1.31.0") + shell("rustup default 1.32.0") } def setupBrewPackages() { diff --git a/README.md b/README.md index 362d32d527..e4f3aeb7e4 100644 --- a/README.md +++ b/README.md @@ -194,16 +194,11 @@ See section "Release channels" for more details. ### MacOS -Pre-built libraries are not provided for MacOS. Please look [here](docs/build-guides/mac-build.md) -for details on building from source for MacOS. - - **Note:** After building `libindy`, add the path containing the library the `LD_LIBRARY_PATH` and -`DYLD_LIBRARY_PATH` environment variables. This is necessary for dynamically linking -your application with `libindy`. The dynamic linker will first check for the library in -`LD_LIBRARY_PATH` if the library in your application doesn't include directory names. -If the library in your application does include any directory name, then dynamic -linker will search for the library in `DYLD_LIBRARY_PATH` (not `LD_LIBRARY_PATH`) -so we recommend you set both variables to be safe. +Pre-built libraries are not provided for MacOS. + +Clone the repo and run `mac.build.sh` in the `libindy` folder. + +Please see [here](docs/build-guides/mac-build.md) for manual build steps. ### RHEL-based distributions (Amazon Linux 2017.03) Pre-built libraries are not provided for RHEL-based distributions. Please look [here](docs/build-guides/rhel-build.md) diff --git a/ci/indy-pool.dockerfile b/ci/indy-pool.dockerfile index eddce0a419..67d51a182b 100644 --- a/ci/indy-pool.dockerfile +++ b/ci/indy-pool.dockerfile @@ -24,9 +24,9 @@ RUN echo "deb https://repo.sovrin.org/deb xenial $indy_stream" >> /etc/apt/sourc RUN useradd -ms /bin/bash -u $uid indy -ARG indy_plenum_ver=1.6.662 +ARG indy_plenum_ver=1.6.705 ARG indy_anoncreds_ver=1.0.32 -ARG indy_node_ver=1.6.772 +ARG indy_node_ver=1.6.839 ARG python3_indy_crypto_ver=0.4.5 ARG indy_crypto_ver=0.4.5 diff --git a/cli/src/commands/ledger.rs b/cli/src/commands/ledger.rs index 5f648fd817..32da867bd6 100644 --- a/cli/src/commands/ledger.rs +++ b/cli/src/commands/ledger.rs @@ -1218,6 +1218,78 @@ pub mod sign_multi_command { } } +pub mod auth_rule_command { + use super::*; + + command!(CommandMetadata::build("auth-rule", "Send AUTH_RULE request to change authentication rules for a ledger transaction.") + .add_required_param("txn_type", "Ledger transaction alias or associated value") + .add_required_param("action", "Type of an action. One of: ADD, EDIT") + .add_required_param("field", "Transaction field") + .add_optional_param("old_value", "Old value of field, which can be changed to a new_value (mandatory for EDIT action)") + .add_required_param("new_value", "New value that can be used to fill the field") + .add_required_param("constraint", r#"Set of constraints required for execution of an action + { + constraint_id - type of a constraint. Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + role - role associated value {TRUSTEE: 0, STEWARD: 2, TRUST_ANCHOR: 101, NETWORK_MONITOR: 201, ANY: *}. + sig_count - the number of signatures required to execution action. + need_to_be_owner - if user must be an owner of transaction. + metadata - additional parameters of the constraint. + } + can be combined by + { + constraint_id: <"AND" or "OR"> + auth_constraints: [, ] + } + "#) + .add_example(r#"ledger auth-rule txn_type=NYM action=ADD field=role new_value=101 constraint="{"sig_count":1,"role":"0","constraint_id":"ROLE","need_to_be_owner":false}""#) + .add_example(r#"ledger auth-rule txn_type=NYM action=EDIT field=role old_value=101 new_value=0 constraint="{"sig_count":1,"role":"0","constraint_id":"ROLE","need_to_be_owner":false}""#) + .finalize() + ); + + fn execute(ctx: &CommandContext, params: &CommandParams) -> Result<(), ()> { + trace!("execute >> ctx {:?} params {:?}", ctx, params); + + let (pool_handle, pool_name) = ensure_connected_pool(&ctx)?; + let (wallet_handle, wallet_name) = ensure_opened_wallet(&ctx)?; + let submitter_did = ensure_active_did(&ctx)?; + + let txn_type = get_str_param("txn_type", params).map_err(error_err!())?; + let action = get_str_param("action", params).map_err(error_err!())?; + let field = get_str_param("field", params).map_err(error_err!())?; + let old_value = get_opt_str_param("old_value", params).map_err(error_err!())?; + let new_value = get_str_param("new_value", params).map_err(error_err!())?; + let constraint = get_str_param("constraint", params).map_err(error_err!())?; + + let request = Ledger::build_auth_rule_request(&submitter_did, txn_type, &action.to_uppercase(), field, old_value, new_value, constraint) + .map_err(|err| handle_indy_error(err, None, None, None))?; + + let response_json = Ledger::sign_and_submit_request(pool_handle, wallet_handle, &submitter_did, &request) + .map_err(|err| handle_indy_error(err, Some(&submitter_did), Some(&pool_name), Some(&wallet_name)))?; + + let mut response: Response = serde_json::from_str::>(&response_json) + .map_err(|err| println_err!("Invalid data has been received: {:?}", err))?; + + if let Some(result) = response.result.as_mut() { + result["txn"]["data"]["auth_type"] = get_txn_title(&result["txn"]["data"]["auth_type"]); + result["txn"]["data"]["constraint"] = serde_json::Value::String(::serde_json::to_string_pretty(&result["txn"]["data"]["constraint"]).unwrap()); + } + + let res = handle_transaction_response(response) + .map(|result| print_transaction_response(result, + "Auth Rule request has been sent to Ledger.", + None, + &mut vec![("auth_type", "Txn Type"), + ("auth_action", "Action"), + ("field", "Field"), + ("old_value", "Old Value"), + ("new_value", "New Value"), + ("constraint", "Constraint")]))?; + + trace!("execute << {:?}", res); + Ok(res) + } +} + pub fn set_request_fees(request: &mut String, wallet_handle: i32, submitter_did: Option<&str>, fees_inputs: &Option>, fees_outputs: &Option>, extra: Option<&str>) -> Result, ()> { let mut payment_method: Option = None; if let &Some(ref inputs) = fees_inputs { @@ -1413,6 +1485,33 @@ fn get_role_title(role: &serde_json::Value) -> serde_json::Value { }.to_string()) } +fn get_txn_title(role: &serde_json::Value) -> serde_json::Value { + serde_json::Value::String(match role.as_str() { + Some("0") => "NODE", + Some("1") => "NYM", + Some("3") => "GET_TXN", + Some("100") => "ATTRIB", + Some("101") => "SCHEMA", + Some("104") => "GET_ATTR", + Some("105") => "GET_NYM", + Some("107") => "GET_SCHEMA", + Some("108") => "GET_CRED_DEF", + Some("102") => "CRED_DEF", + Some("109") => "POOL_UPGRADE", + Some("111") => "POOL_CONFIG", + Some("113") => "REVOC_REG_DEF", + Some("114") => "REVOC_REG_ENTRY", + Some("115") => "GET_REVOC_REG_DEF", + Some("116") => "GET_REVOC_REG", + Some("117") => "GET_REVOC_REG_DELTA", + Some("118") => "POOL_RESTART", + Some("119") => "GET_VALIDATOR_INFO", + Some("120") => "AUTH_RULE", + Some(val) => val, + _ => "-" + }.to_string()) +} + fn timestamp_to_datetime(_time: i64) -> String { NaiveDateTime::from_timestamp(_time, 0).to_string() } @@ -1444,7 +1543,7 @@ pub struct ReplyResult { pub mod tests { use super::*; use commands::wallet::tests::{create_and_open_wallet, close_and_delete_wallet, open_wallet, close_wallet}; - use commands::pool::tests::{disconnect_and_delete_pool}; + use commands::pool::tests::disconnect_and_delete_pool; use commands::did::tests::{new_did, use_did, SEED_TRUSTEE, DID_TRUSTEE, DID_MY1, VERKEY_MY1, SEED_MY3, DID_MY3, VERKEY_MY3}; #[cfg(feature = "nullpay_plugin")] use commands::common::tests::{load_null_payment_plugin, NULL_PAYMENT_METHOD}; @@ -3473,6 +3572,35 @@ pub mod tests { } } + mod auth_rule { + use super::*; + + const ROLE_CONSTRAINT: &str = r#"{ + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": false + }"#; + + #[test] + pub fn auth_rule_works() { + let ctx = setup_with_wallet_and_pool(); + use_trustee(&ctx); + { + let cmd = auth_rule_command::new(); + let mut params = CommandParams::new(); + params.insert("txn_type", "NYM".to_string()); + params.insert("action", "add".to_string()); + params.insert("field", "role".to_string()); + params.insert("new_value", "101".to_string()); + params.insert("constraint", ROLE_CONSTRAINT.to_string()); + cmd.execute(&ctx, ¶ms).unwrap(); + } + tear_down_with_wallet_and_pool(&ctx); + } + } + fn create_new_did(ctx: &CommandContext) -> (String, String) { let (wallet_handle, _) = get_opened_wallet(ctx).unwrap(); Did::new(wallet_handle, "{}").unwrap() diff --git a/cli/src/libindy/ledger.rs b/cli/src/libindy/ledger.rs index a15dd3aed6..4c1322b217 100644 --- a/cli/src/libindy/ledger.rs +++ b/cli/src/libindy/ledger.rs @@ -77,7 +77,18 @@ impl Ledger { pub fn indy_build_pool_upgrade_request(submitter_did: &str, name: &str, version: &str, action: &str, sha256: &str, timeout: Option, schedule: Option<&str>, justification: Option<&str>, reinstall: bool, force: bool, package: Option<&str>) -> Result { ledger::build_pool_upgrade_request(submitter_did, name, version, action, sha256, - timeout, schedule, justification, - reinstall, force, package).wait() + timeout, schedule, justification, + reinstall, force, package).wait() + } + + pub fn build_auth_rule_request(submitter_did: &str, + txn_type: &str, + action: &str, + field: &str, + old_value: Option<&str>, + new_value: &str, + constraint: &str, ) -> Result { + ledger::build_auth_rule_request(submitter_did, txn_type, action, field, + old_value, new_value, constraint).wait() } } \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 1e58388311..b8f7ab9619 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -126,6 +126,7 @@ fn build_executor() -> CommandExecutor { .add_command(ledger::set_fees_prepare_command::new()) .add_command(ledger::verify_payment_receipt_command::new()) .add_command(ledger::sign_multi_command::new()) + .add_command(ledger::auth_rule_command::new()) .finalize_group() .add_group(payment_address::group::new()) .add_command(payment_address::create_command::new()) diff --git a/docs/build-guides/mac-build.md b/docs/build-guides/mac-build.md index bbc1bd2849..5bad3bc0a4 100644 --- a/docs/build-guides/mac-build.md +++ b/docs/build-guides/mac-build.md @@ -1,8 +1,11 @@ -# Setup Indy SDK build environment for MacOS +# MacOS build guide + +Automated build: clone the repo and run `mac.build.sh` in the `libindy` folder. + +## Manual steps 1. Install Rust and rustup (https://www.rust-lang.org/install.html). 2. Install required native libraries and utilities (libsodium is added with URL to homebrew since version<1.0.15 is required) - ``` brew install pkg-config brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/65effd2b617bade68a8a2c5b39e1c3089cc0e945/Formula/libsodium.rb @@ -13,8 +16,6 @@ brew install zeromq brew install zmq ``` - - 3. Setup environment variables: ``` export PKG_CONFIG_ALLOW_CROSS=1 @@ -24,7 +25,10 @@ ``` 4. Setup OPENSSL_DIR variable: path to installed openssl library ``` - export OPENSSL_DIR=/usr/local/Cellar/openssl/1.0.2n # path changes with version number + for version in `ls -t /usr/local/Cellar/openssl/`; do + export OPENSSL_DIR=/usr/local/Cellar/openssl/$version + break + done ``` 5. Checkout and build the library: ``` @@ -38,13 +42,14 @@ cd ../cli cargo build ``` +7. Set your `DYLD_LIBRARY_PATH` and `LD_LIBRARY_PATH` environment variables to the path of `indy-sdk/libindy/target/debug`. You may want to put these in your `.bash_profile` to persist them. -# Note on running local nodes +## Note on running local nodes In order to run local nodes on MacOS, it may be necessary to set up port mapping between the Docker container and local host. Follow the instructions in [Indy SDK README](https://github.com/hyperledger/indy-sdk#how-to-start-local-nodes-pool-with-docker) -# IOError while running of whole set of tests on MacOS +## IOError while running of whole set of tests on MacOS There is a possible case when some tests are failed if whole set of tests is run (`cargo test`). But failed tests will be successful in case of separate runs. diff --git a/docs/design/001-cli/README.md b/docs/design/001-cli/README.md index 1100ed366a..6523f95116 100644 --- a/docs/design/001-cli/README.md +++ b/docs/design/001-cli/README.md @@ -333,6 +333,12 @@ Send custom transaction with user defined json body and optional signature ledger custom [txn=] [sign=] ``` +#### AUTH_RULE transaction +Send AUTH_RULE transaction +``` +ledger auth-rule txn_type= action= field= [old_value=] new_value= constraint=<{constraint json}> +``` + #### GET_PAYMENT_SOURCES transaction Send GET_PAYMENT_SOURCES transaction ``` diff --git a/docs/getting-started/indy-walkthrough.md b/docs/getting-started/indy-walkthrough.md index 10c40df9e5..97526c75a0 100644 --- a/docs/getting-started/indy-walkthrough.md +++ b/docs/getting-started/indy-walkthrough.md @@ -21,6 +21,7 @@ - [Alice Gets a Transcript](#alice-gets-a-transcript) - [Apply for a Job](#apply-for-a-job) - [Apply for a Loan](#apply-for-a-loan) + - [Alice Quits her Job](#alice-quits-her-job) - [Explore the Code](#explore-the-code) ## What Indy and Libindy are and Why They Matter @@ -246,7 +247,7 @@ By having independent pairwise relationships, you're reducing the ability for ot #### Getting Verinym It is important to understand that earlier created **Faber** DID is not, in and of itself, the same thing as self-sovereign identity. This DID must be used only for secure interaction with the **Steward**. -After the connection is established **Faber** must create new DID record that he will use as Verinym in the Ledger. +After the connection is established **Faber** must create a new DID record that he will use as Verinym in the Ledger. 1. **Faber** creates a new DID in its wallet by calling ``did.create_and_store_my_did``. ```python @@ -415,8 +416,34 @@ The same way **Acme** creates and publishes a **Credential Definition** for the await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], cred_def_request) ``` -At this point we have a **Credential Definition** for the **Job-Certificate** Credential Schema published by **Acme** and a - **Credential Definition** for the **Transcript** Credential Schema published by **Faber**. +**Acme** anticipates revoking **Job-Certificate* credentials. It decides to create a revocation registry. One of Hyperledger Indy's revocation registry types uses cryptographic accumulators for publishing revoked credentials. For details about the inner working of those accumulators see [here](https://github.com/fabienpe/indy-sdk/blob/master/docs/concepts/revocation/cred-revocation.md)). The use of those accumulators requires the publication of "validity tails" outside of the Ledger. For the purpose of this demo, the validity tails are written in a file using a 'blob storage'. + +```python + # Acme Agent + acme['tails_writer_config'] = json.dumps({'base_dir': "/tmp/indy_acme_tails", 'uri_pattern': ''}) + tails_writer = await blob_storage.open_writer('default', acme['tails_writer_config']) +``` +Once the validity tails are configured, **Acme** can create a new revocation registry for the given credential definition. + +```python + # Acme Agent + (acme['revoc_reg_id'], acme['revoc_reg_def'], acme['revoc_reg_entry']) = \ + await anoncreds.issuer_create_and_store_revoc_reg(acme['wallet'], acme['did'], 'CL_ACCUM', 'TAG1', + acme['job_certificate_cred_def_id'], + json.dumps({'max_cred_num': 5, + 'issuance_type': 'ISSUANCE_ON_DEMAND'}), + tails_writer) + + acme['revoc_reg_def_request'] = await ledger.build_revoc_reg_def_request(acme['did'], acme['revoc_reg_def']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_def_request']) + + acme['revoc_reg_entry_request'] = \ + await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM', + acme['revoc_reg_entry']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_request']) +``` + +At this point we have a **Credential Definition** (supporting revocation) for the **Job-Certificate** Credential Schema published by **Acme** and a **Credential Definition** for the **Transcript** Credential Schema published by **Faber**. ## Alice Gets a Transcript @@ -529,7 +556,7 @@ In this case, Acme Corp is requesting that Alice provide a **Job Application**. The Job Application requires a name, degree, status, SSN and also the satisfaction of the condition about the average mark or grades. In this case, **Job-Application** Proof Request looks like: -``` +```python # Acme Agent acme['job_application_proof_request'] = json.dumps({ 'nonce': '1432422343242122312411212', @@ -638,7 +665,7 @@ Now Alice has everything to create the Proof for **Acme Job-Application** Proof ``` When **Acme** inspects the received Proof he will see following structure: -``` +```python # Acme Agent { 'requested_proof': { @@ -711,16 +738,49 @@ Alice goes through a familiar sequence of interactions. "salary": {"raw": "2400", "encoded": "2400"}, "experience": {"raw": "10", "encoded": "10"} }) - job_certificate_cred_json, _, _ = \ - await anoncreds.issuer_create_credential(acme['wallet'], acme['job_certificate_cred_offer'],acme['job_certificate_cred_request'], - alice_job_certificate_cred_values_json, None, None) +``` + +One difference with the ussuance of the Transcript by Faber here is that a **Job-Certificate** can be revoked and the credential creation takes the ID of the revocation registry created earlier by **Acme** and a handle to the blob storage containing the validity tails: + + +```python + # Acme Agent + acme['blob_storage_reader_cfg_handle'] = await blob_storage.open_reader('default', acme['tails_writer_config']) + acme['job_certificate_cred'], acme['job_certificate_cred_rev_id'], acme['alice_cert_rev_reg_delta'] = \ + await anoncreds.issuer_create_credential(acme['wallet'], acme['job_certificate_cred_offer'], + acme['job_certificate_cred_request'], + acme['job_certificate_cred_values'], + acme['revoc_reg_id'], + acme['blob_storage_reader_cfg_handle']) +``` + +Furthermore **Acme** must publish a revocation registry entry on the Ledger so other parties can verify later the revocation state of the credential. + +```python + # Acme agent + acme['revoc_reg_entry_req'] = \ + await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM', + acme['alice_cert_rev_reg_delta']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_req']) +``` + +When **Alice** receives her **Job-Certificate** credential from **Acme**, she should request the revocation registry definition from the Ledger before storing the credential. + +```python + # Alice Agent + alice['acme_revoc_reg_des_req'] = \ + await ledger.build_get_revoc_reg_def_request(alice['did_for_acme'], + alice_job_certificate_cred['rev_reg_id']) + alice['acme_revoc_reg_des_resp'] = await ledger.submit_request(alice['pool'], alice['acme_revoc_reg_des_req']) + (alice['acme_revoc_reg_def_id'], alice['acme_revoc_reg_def_json']) = \ + await ledger.parse_get_revoc_reg_def_response(alice['acme_revoc_reg_des_resp']) ``` Now the **Job-Certificate** Credential has been issued and Alice now has it in her possession. Alice stores **Job-Certificate** Credential in her wallet. ```python # Alice Agent await anoncreds.prover_store_credential(alice['wallet'], None, alice['job_certificate_cred_request_metadata'], - alice['job_certificate_cred'], alice['acme_job_certificate_cred_def'], None) + alice['job_certificate_cred'], alice['acme_job_certificate_cred_def'], alice['acme_revoc_reg_def_json']) ``` She can use it when she applies for her loan, in much the same way that she used her transcript when applying for a job. @@ -755,9 +815,11 @@ Alice gets a **Loan-Application-Basic** Proof Request from Thrift Bank that look 'p_value': 1, 'restrictions': [{'cred_def_id': acme_job_certificate_cred_def_id}] } - } + }, + 'non_revoked': {'to': int(time.time())} }) ``` +The last line indicates that the *Job-Certificate* provided should not be revoked by the application time. Alice has only one credential that meets the proof requirements for this **Loan-Application-Basic** Proof Request. ```python @@ -777,39 +839,45 @@ Alice has only one credential that meets the proof requirements for this **Loan- } ``` -For the **Loan-Application-Basic** Proof Request Alice divided attributes as follows: +For the **Loan-Application-Basic** Proof Request Alice divided attributes as follows. She can get the validity time stamp for each attribute from the revocation states queried from the Ledger: ```python # Alice Agent - alice['apply_loan_requested_creds'] = json.dumps({ - 'self_attested_attributes': {}, - 'requested_attributes': { - 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True} - }, - 'requested_predicates': { - 'predicate1_referent': {'cred_id': cred_for_predicate1['referent']}, - 'predicate2_referent': {'cred_id': cred_for_predicate2['referent']} - } - }) + revoc_states_for_loan_app = json.loads(alice['revoc_states_for_loan_app']) + timestamp_for_attr1 = await get_timestamp_for_attribute(cred_for_attr1, revoc_states_for_loan_app) + timestamp_for_predicate1 = await get_timestamp_for_attribute(cred_for_predicate1, revoc_states_for_loan_app) + timestamp_for_predicate2 = await get_timestamp_for_attribute(cred_for_predicate2, revoc_states_for_loan_app) + alice['apply_loan_requested_creds'] = json.dumps({ + 'self_attested_attributes': {}, + 'requested_attributes': { + 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True, 'timestamp': timestamp_for_attr1} + }, + 'requested_predicates': { + 'predicate1_referent': {'cred_id': cred_for_predicate1['referent'], 'timestamp': timestamp_for_predicate1}, + 'predicate2_referent': {'cred_id': cred_for_predicate2['referent'], 'timestamp': timestamp_for_predicate2} + } + }) ``` Alice creates the Proof for the **Loan-Application-Basic** Proof Request. ```python # Alice Agent alice['apply_loan_proof'] = \ - await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_proof_request'], alice['apply_loan_requested_creds'], - alice['master_secret_id'], alice['schemas'], alice['cred_defs'], alice['revoc_states']) + await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_proof_request'], + alice['apply_loan_requested_creds'], alice['master_secret_id'], + alice['schemas_for_loan_app'], alice['cred_defs_for_loan_app'], + alice['revoc_states_for_loan_app']) ``` Alice sends just the **Loan-Application-Basic** proof to the bank. This allows her to minimize the PII (personally identifiable information) that she has to share when all she's trying to do right now is prove basic eligibility. When **Thrift** inspects the received Proof he will see following structure: -``` +```python # Thrift Agent { 'requested_proof': { - 'revealed_attributess': { - 'attr1_referent': {'sub_proof_index': 0, 'raw':'Permanent', 'encoded':'2143135425425143112321314321'}, + 'revealed_attrs': { + 'attr1_referent': {'sub_proof_index': 0, 'raw': 'Permanent', 'encoded':'2143135425425143112321314321'}, }, 'self_attested_attrs': {}, 'unrevealed_attrs': {}, @@ -820,10 +888,10 @@ When **Thrift** inspects the received Proof he will see following structure: }, 'proof' : [] # Validity Proof that Thrift can check 'identifiers' : [ # Identifiers of credentials were used for Proof building - 'schema_id': job_certificate_schema_id, - 'cred_def_id': acme_job_certificate_cred_def_id, - 'revoc_reg_seq_no': None, - 'timestamp': None + 'schema_id': acme['job_certificate_schema_id'], + 'cred_def_id': acme['job_certificate_cred_def_id'], + 'rev_reg_id': acme['revoc_reg_id'], + 'timestamp': 1550503925 # A integer timestamp ] } ``` @@ -831,8 +899,12 @@ When **Thrift** inspects the received Proof he will see following structure: **Thrift Bank** successfully verified the **Loan-Application-Basic** Proof from Alice. ```python # Thrift Agent - assert await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'], thrift['alice_apply_loan_proof'], - thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], thrift['revoc_regs']) + assert await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'], + thrift['alice_apply_loan_proof'], + thrift['schemas_for_loan_app'], + thrift['cred_defs_for_loan_app'], + thrift['revoc_defs_for_loan_app'], + thrift['revoc_regs_for_loan_app']) ``` Thrift Bank sends the second Proof Request where Alice needs to share her personal information with the bank. @@ -909,7 +981,7 @@ Alice creates the Proof for **Loan-Application-KYC** Proof Request. ``` When **Thrift** inspects the received Proof he will see following structure: -``` +```python # Thrift Agent { 'requested_proof': { @@ -926,13 +998,13 @@ When **Thrift** inspects the received Proof he will see following structure: 'identifiers' : [ # Identifiers of credentials were used for Proof building { 'schema_id': transcript_schema_id, - 'cred_def_id': faber_transcript_cred_def_id, + 'cred_def_id': faber['transcript_cred_def_id'], 'rev_reg_id': None, 'timestamp': None }, { 'schema_key': job_certificate_schema_id, - 'cred_def_id': acme_job_certificate_cred_def_id, + 'cred_def_id': acme['job_certificate_cred_def_id'], 'rev_reg_id': None, 'timestamp': None } @@ -949,6 +1021,22 @@ When **Thrift** inspects the received Proof he will see following structure: Both of Alice's Proofs have been successfully verified and she got loan from **Thrift Bank**. +## Alice Quits her Job + +Later, **Alice** decides to quit her job so **Acme** revokes the **Job-Certificate** credential: + +```python + # Acme Agent + await anoncreds.issuer_revoke_credential(acme['wallet'], + acme['blob_storage_reader_cfg_handle'], + acme['revoc_reg_id'], + acme['job_certificate_cred_rev_id']) +``` + +**Acme** then just needs to publish the revocation on the ledger calling `ledger.build_revoc_reg_entry_request` and `ledger.sign_and_submit_request`. + +If Alice tries to apply for a loan (**Loan-Application-Basic**) again, the proof verification will then fail. + ## Explore the Code Now that you've had a chance to see how the Libindy implementation works from the outside, perhaps you'd like to see how it works underneath, from code? diff --git a/libindy/benches/wallet.rs b/libindy/benches/wallet.rs index a3355e15b0..2847e46d5d 100644 --- a/libindy/benches/wallet.rs +++ b/libindy/benches/wallet.rs @@ -236,7 +236,7 @@ mod delete { mod get_record { use super::*; - fn get_record(wallet_handle: i32, type_: &str, id: &str) { + fn get_record(wallet_handle: WalletHandle, type_: &str, id: &str) { NonSecretsUtils::get_wallet_record(wallet_handle, type_, id, "{}").unwrap(); } @@ -263,7 +263,7 @@ mod delete_record { } } - fn delete_record(wallet_handle: i32, type_: &str, id: &str) { + fn delete_record(wallet_handle: WalletHandle, type_: &str, id: &str) { NonSecretsUtils::delete_wallet_record(wallet_handle, type_, id).unwrap(); } @@ -290,7 +290,7 @@ mod add_record { } } - fn add_record(wallet_handle: i32, type_: &str, id: &str, value: &str, tags: &str) { + fn add_record(wallet_handle: WalletHandle, type_: &str, id: &str, value: &str, tags: &str) { NonSecretsUtils::add_wallet_record(wallet_handle, type_, id, value, Some(tags)).unwrap(); } @@ -315,7 +315,7 @@ mod add_record_tags { (type_, id, r#"{"tag_1": "value_1", "~tag_2": "value_2"}"#.to_string()) } - fn add_record_tags(wallet_handle: i32, type_: &str, id: &str, tags: &str) { + fn add_record_tags(wallet_handle: WalletHandle, type_: &str, id: &str, tags: &str) { NonSecretsUtils::add_wallet_record_tags(wallet_handle, type_, id, tags).unwrap(); } @@ -340,7 +340,7 @@ mod delete_record_tags { (type_, id, r#"["tag_id_1"]"#.to_string()) } - fn delete_record_tags(wallet_handle: i32, type_: &str, id: &str, tag_names: &str) { + fn delete_record_tags(wallet_handle: WalletHandle, type_: &str, id: &str, tag_names: &str) { NonSecretsUtils::delete_wallet_record_tags(wallet_handle, type_, id, tag_names).unwrap(); } @@ -360,7 +360,7 @@ mod delete_record_tags { mod search_records { use super::*; - fn open_search(wallet_handle: i32, query: &str) { + fn open_search(wallet_handle: WalletHandle, query: &str) { NonSecretsUtils::open_wallet_search(wallet_handle, TYPE_1, query, "{}").unwrap(); } @@ -469,7 +469,7 @@ fn _tags(suffix: usize) -> String { }).to_string() } -fn add_records(wallet_handle: i32) { +fn add_records(wallet_handle: WalletHandle) { for i in 0..COUNT { NonSecretsUtils::add_wallet_record(wallet_handle, &_type(i), diff --git a/libindy/ci/amazon.dockerfile b/libindy/ci/amazon.dockerfile index 80329b91de..72c6464574 100755 --- a/libindy/ci/amazon.dockerfile +++ b/libindy/ci/amazon.dockerfile @@ -39,7 +39,7 @@ RUN wget https://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-mav RUN sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo RUN yum install -y apache-maven -ENV RUST_ARCHIVE=rust-1.31.0-x86_64-unknown-linux-gnu.tar.gz +ENV RUST_ARCHIVE=rust-1.32.0-x86_64-unknown-linux-gnu.tar.gz ENV RUST_DOWNLOAD_URL=https://static.rust-lang.org/dist/$RUST_ARCHIVE RUN mkdir -p /rust @@ -74,6 +74,4 @@ RUN cd /tmp && \ RUN useradd -ms /bin/bash -u $uid indy USER indy -RUN cargo install --git https://github.com/DSRCorporation/cargo-test-xunit - WORKDIR /home/indy diff --git a/libindy/ci/ubuntu.dockerfile b/libindy/ci/ubuntu.dockerfile index 380cd6bf12..a69723353e 100755 --- a/libindy/ci/ubuntu.dockerfile +++ b/libindy/ci/ubuntu.dockerfile @@ -62,7 +62,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN useradd -ms /bin/bash -u $uid indy USER indy -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.31.0 +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.32.0 ENV PATH /home/indy/.cargo/bin:$PATH # Install clippy to the Rust toolchain diff --git a/libindy/include/indy_ledger.h b/libindy/include/indy_ledger.h index d2a511ab06..06ecda893b 100644 --- a/libindy/include/indy_ledger.h +++ b/libindy/include/indy_ledger.h @@ -955,6 +955,52 @@ extern "C" { const char* response_metadata) ); + /// Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. + /// + /// #Params + /// command_handle: command handle to map callback to caller context. + /// txn_type: ledger transaction alias or associated value. + /// action: type of an action. + /// Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). + /// field: transaction field. + /// old_value: old value of a field, which can be changed to a new_value (mandatory for EDIT action). + /// new_value: new value that can be used to fill the field. + /// constraint: set of constraints required for execution of an action in the following format + /// { + /// constraint_id - type of a constraint. + /// Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + /// role - role of a user which satisfy to constrain. + /// sig_count - the number of signatures required to execution action. + /// need_to_be_owner - if user must be an owner of transaction. + /// metadata - additional parameters of the constraint. + /// } + /// can be combined by + /// { + /// 'constraint_id': <"AND" or "OR"> + /// 'auth_constraints': [, ] + /// } + /// + /// cb: Callback that takes command result as parameter. + /// + /// #Returns + /// Request result as json. + /// + /// #Errors + /// Common* + extern indy_error_t indy_build_auth_rule_request(indy_handle_t command_handle, + const char * submitter_did, + const char * txn_type, + const char * action, + const char * field, + const char * old_value, + const char * new_value, + const char * constraint, + + void (*cb)(indy_handle_t command_handle_, + indy_error_t err, + const char* request_json) + ); + #ifdef __cplusplus } #endif diff --git a/libindy/mac.build.sh b/libindy/mac.build.sh new file mode 100644 index 0000000000..829fb5c23d --- /dev/null +++ b/libindy/mac.build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +onred='\033[41m' +ongreen='\033[42m' +onyellow='\033[43m' +endcolor="\033[0m" + +# Handle errors +set -e +error_report() { + echo -e "${onred}Error: failed on line $1.$endcolor" +} +trap 'error_report $LINENO' ERR + +echo -e "${onyellow}Installing libindy...$endcolor" + +if [[ "$OSTYPE" == "darwin"* ]]; then + xcode-select --version || xcode-select --install + brew --version || yes | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + cmake --version || brew install cmake # brew install cmake throws error, not warning if already installed + curl https://sh.rustup.rs -sSf | sh -s -- -y + export PATH="$HOME/.cargo/bin:$PATH" # so can use cargo without relog + brew install pkg-config \ + https://raw.githubusercontent.com/Homebrew/homebrew-core/65effd2b617bade68a8a2c5b39e1c3089cc0e945/Formula/libsodium.rb \ + automake \ + autoconf \ + openssl \ + zeromq \ + zmq + export PKG_CONFIG_ALLOW_CROSS=1 + export CARGO_INCREMENTAL=1 + export RUST_LOG=indy=trace + export RUST_TEST_THREADS=1 + for version in `ls -t /usr/local/Cellar/openssl/`; do + export OPENSSL_DIR=/usr/local/Cellar/openssl/$version + break + done + cargo build + export LIBRARY_PATH=$(pwd)/target/debug + cd ../cli + cargo build + echo 'export DYLD_LIBRARY_PATH='$LIBRARY_PATH' +export LD_LIBRARY_PATH='$LIBRARY_PATH >> ~/.bash_profile + echo -e "${ongreen}Libindy installed.$endcolor" +else + echo -e "${onred}You are not running MacOS. This is a MacOS installer.$endcolor" +fi diff --git a/libindy/src/api/crypto.rs b/libindy/src/api/crypto.rs index 92cb5edabc..90e5b2e64f 100644 --- a/libindy/src/api/crypto.rs +++ b/libindy/src/api/crypto.rs @@ -706,7 +706,7 @@ pub extern fn indy_pack_message( #[no_mangle] pub extern fn indy_unpack_message( command_handle: i32, - wallet_handle: i32, + wallet_handle: WalletHandle, jwe_data: *const u8, jwe_len: u32, cb: Option< diff --git a/libindy/src/api/ledger.rs b/libindy/src/api/ledger.rs index 48ff888af7..4465b281e4 100644 --- a/libindy/src/api/ledger.rs +++ b/libindy/src/api/ledger.rs @@ -1834,3 +1834,92 @@ pub extern fn indy_get_response_metadata(command_handle: CommandHandle, res } + +/// Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. +/// +/// #Params +/// command_handle: command handle to map callback to caller context. +/// txn_type: ledger transaction alias or associated value. +/// action: type of an action. +/// Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). +/// field: transaction field. +/// old_value: old value of a field, which can be changed to a new_value (mandatory for EDIT action). +/// new_value: new value that can be used to fill the field. +/// constraint: set of constraints required for execution of an action in the following format: +/// { +/// constraint_id - type of a constraint. +/// Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. +/// role - role of a user which satisfy to constrain. +/// sig_count - the number of signatures required to execution action. +/// need_to_be_owner - if user must be an owner of transaction. +/// metadata - additional parameters of the constraint. +/// } +/// can be combined by +/// { +/// 'constraint_id': <"AND" or "OR"> +/// 'auth_constraints': [, ] +/// } +/// +/// Default ledger auth rules: https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md +/// +/// More about AUTH_RULE request: https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule +/// +/// cb: Callback that takes command result as parameter. +/// +/// #Returns +/// Request result as json. +/// +/// #Errors +/// Common* +#[no_mangle] +pub extern fn indy_build_auth_rule_request(command_handle: CommandHandle, + submitter_did: *const c_char, + txn_type: *const c_char, + action: *const c_char, + field: *const c_char, + old_value: *const c_char, + new_value: *const c_char, + constraint: *const c_char, + cb: Option) -> ErrorCode { + trace!("indy_build_auth_rule_request: >>> submitter_did: {:?}, txn_type: {:?}, action: {:?}, field: {:?}, \ + old_value: {:?}, new_value: {:?}, constraint: {:?}", + submitter_did, txn_type, action, field, old_value, new_value, constraint); + + check_useful_c_str!(submitter_did, ErrorCode::CommonInvalidParam2); + check_useful_c_str!(txn_type, ErrorCode::CommonInvalidParam3); + check_useful_c_str!(action, ErrorCode::CommonInvalidParam4); + check_useful_c_str!(field, ErrorCode::CommonInvalidParam5); + check_useful_opt_c_str!(old_value, ErrorCode::CommonInvalidParam6); + check_useful_c_str!(new_value, ErrorCode::CommonInvalidParam7); + check_useful_c_str!(constraint, ErrorCode::CommonInvalidParam8); + check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam9); + + trace!("indy_build_auth_rule_request: entities >>> submitter_did: {:?}, txn_type: {:?}, action: {:?}, field: {:?}, \ + old_value: {:?}, new_value: {:?}, constraint: {:?}", + submitter_did, txn_type, action, field, old_value, new_value, constraint); + + let result = CommandExecutor::instance() + .send(Command::Ledger(LedgerCommand::BuildAuthRuleRequest( + submitter_did, + txn_type, + action, + field, + old_value, + new_value, + constraint, + Box::new(move |result| { + let (err, request_json) = prepare_result_1!(result, String::new()); + trace!("indy_build_auth_rule_request: request_json: {:?}", request_json); + let request_json = ctypes::string_to_cstring(request_json); + cb(command_handle, err, request_json.as_ptr()) + }) + ))); + + let res = prepare_result!(result); + + trace!("indy_build_auth_rule_request: <<< res: {:?}", res); + + res +} diff --git a/libindy/src/api/mod.rs b/libindy/src/api/mod.rs index 241d57d897..4aa583a9c6 100644 --- a/libindy/src/api/mod.rs +++ b/libindy/src/api/mod.rs @@ -19,7 +19,13 @@ use utils::ctypes; pub type IndyHandle = i32; -pub type WalletHandle = i32; +//pub type WalletHandle = i32; +#[repr(transparent)] +#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] +pub struct WalletHandle(pub i32); +pub const INVALID_WALLET_HANDLE : WalletHandle = WalletHandle(0); + +pub type CallbackHandle = i32; pub type PoolHandle = i32; pub type CommandHandle = i32; pub type StorageHandle = i32; diff --git a/libindy/src/api/wallet.rs b/libindy/src/api/wallet.rs index eca31552ed..695e16bad7 100644 --- a/libindy/src/api/wallet.rs +++ b/libindy/src/api/wallet.rs @@ -1,6 +1,6 @@ extern crate libc; -use api::{ErrorCode, IndyHandle, CommandHandle, WalletHandle, SearchHandle, StorageHandle}; +use api::{ErrorCode, IndyHandle, CommandHandle, WalletHandle, SearchHandle, StorageHandle, INVALID_WALLET_HANDLE}; use commands::{Command, CommandExecutor}; use commands::wallet::WalletCommand; use domain::wallet::{Config, Credentials, ExportConfig, KeyConfig}; @@ -268,7 +268,7 @@ pub extern fn indy_open_wallet(command_handle: CommandHandle, credentials: *const c_char, cb: Option) -> ErrorCode { + wallet_handle: WalletHandle)>) -> ErrorCode { trace!("indy_open_wallet: >>> command_handle: {:?}, config: {:?}, credentials: {:?}, cb: {:?}", command_handle, config, credentials, cb); @@ -284,7 +284,7 @@ pub extern fn indy_open_wallet(command_handle: CommandHandle, config, credentials, Box::new(move |result| { - let (err, handle) = prepare_result_1!(result, 0); + let (err, handle) = prepare_result_1!(result, INVALID_WALLET_HANDLE); trace!("indy_open_wallet: cb command_handle: {:?} err: {:?}, handle: {:?}", command_handle, err, handle); cb(command_handle, err, handle) diff --git a/libindy/src/commands/anoncreds/issuer.rs b/libindy/src/commands/anoncreds/issuer.rs index f47e43efe1..c67a743fc3 100644 --- a/libindy/src/commands/anoncreds/issuer.rs +++ b/libindy/src/commands/anoncreds/issuer.rs @@ -52,6 +52,7 @@ use services::pool::PoolService; use services::wallet::{RecordOptions, WalletService}; use super::tails::{SDKTailsAccessor, store_tails_from_generator}; +use api::{WalletHandle, CallbackHandle}; pub enum IssuerCommand { CreateSchema( @@ -61,7 +62,7 @@ pub enum IssuerCommand { AttributeNames, // attribute names Box) + Send>), CreateAndStoreCredentialDefinition( - i32, // wallet handle + WalletHandle, String, // issuer did Schema, // schema String, // tag @@ -74,7 +75,7 @@ pub enum IssuerCommand { CredentialPrivateKey, CredentialKeyCorrectnessProof)>) + Send>), CreateAndStoreCredentialDefinitionContinue( - i32, // config + WalletHandle, SchemaV1, // credentials String, String, @@ -85,7 +86,7 @@ pub enum IssuerCommand { CredentialKeyCorrectnessProof)>, i32), CreateAndStoreRevocationRegistry( - i32, // wallet handle + WalletHandle, String, // issuer did Option, // type String, // tag @@ -94,11 +95,11 @@ pub enum IssuerCommand { i32, // tails writer handle Box) + Send>), CreateCredentialOffer( - i32, // wallet handle + WalletHandle, String, // credential definition id Box) + Send>), CreateCredential( - i32, // wallet handle + WalletHandle, CredentialOffer, // credential offer CredentialRequest, // credential request HashMap, // credential values @@ -106,13 +107,13 @@ pub enum IssuerCommand { Option, // blob storage reader config handle Box, Option)>) + Send>), RevokeCredential( - i32, // wallet handle + WalletHandle, i32, // blob storage reader config handle String, //revocation revoc id String, //credential revoc id Box) + Send>), /* RecoverCredential( - i32, // wallet handle + WalletHandle, i32, // blob storage reader config handle String, //revocation revoc id String, //credential revoc id @@ -234,7 +235,7 @@ impl IssuerCommandExecutor { } fn create_and_store_credential_definition(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, issuer_did: &str, schema: &SchemaV1, tag: &str, @@ -289,8 +290,8 @@ impl IssuerCommandExecutor { } fn _create_and_store_credential_definition_continue(&self, - cb_id: i32, - wallet_handle: i32, + cb_id: CallbackHandle, + wallet_handle: WalletHandle, schema: &SchemaV1, schema_id: &str, cred_def_id: &str, @@ -307,7 +308,7 @@ impl IssuerCommandExecutor { } fn _prepare_create_and_store_credential_definition(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, issuer_did: &str, schema: &SchemaV1, tag: &str, @@ -337,7 +338,7 @@ impl IssuerCommandExecutor { } fn _complete_create_and_store_credential_definition(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, schema: &SchemaV1, schema_id: &str, cred_def_id: &str, @@ -377,7 +378,7 @@ impl IssuerCommandExecutor { } fn create_and_store_revocation_registry(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, issuer_did: &str, type_: Option<&str>, tag: &str, @@ -466,7 +467,7 @@ impl IssuerCommandExecutor { } fn create_credential_offer(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, cred_def_id: &str) -> IndyResult { debug!("create_credential_offer >>> wallet_handle: {:?}, cred_def_id: {:?}", wallet_handle, cred_def_id); @@ -493,7 +494,7 @@ impl IssuerCommandExecutor { } fn new_credential(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, cred_offer: &CredentialOffer, cred_request: &CredentialRequest, cred_values: &HashMap, @@ -614,7 +615,7 @@ impl IssuerCommandExecutor { } fn revoke_credential(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, blob_storage_reader_handle: i32, rev_reg_id: &str, cred_revoc_id: &str) -> IndyResult { @@ -673,7 +674,7 @@ impl IssuerCommandExecutor { } fn _recovery_credential(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, blob_storage_reader_handle: i32, rev_reg_id: &str, cred_revoc_id: &str) -> IndyResult { @@ -749,26 +750,26 @@ impl IssuerCommandExecutor { } // TODO: DELETE IT - fn _wallet_set_schema_id(&self, wallet_handle: i32, id: &str, schema_id: &str) -> IndyResult<()> { + fn _wallet_set_schema_id(&self, wallet_handle: WalletHandle, id: &str, schema_id: &str) -> IndyResult<()> { self.wallet_service.add_record(wallet_handle, &self.wallet_service.add_prefix("SchemaId"), id, schema_id, &Tags::new()) } // TODO: DELETE IT - fn _wallet_get_schema_id(&self, wallet_handle: i32, key: &str) -> IndyResult { + fn _wallet_get_schema_id(&self, wallet_handle: WalletHandle, key: &str) -> IndyResult { let schema_id_record = self.wallet_service.get_record(wallet_handle, &self.wallet_service.add_prefix("SchemaId"), &key, &RecordOptions::id_value())?; Ok(schema_id_record.get_value() .ok_or(err_msg(IndyErrorKind::InvalidStructure, format!("SchemaId not found for id: {}", key)))?.to_string()) } - fn _wallet_get_rev_reg_def(&self, wallet_handle: i32, key: &str) -> IndyResult { + fn _wallet_get_rev_reg_def(&self, wallet_handle: WalletHandle, key: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &key, &RecordOptions::id_value()) } - fn _wallet_get_rev_reg(&self, wallet_handle: i32, key: &str) -> IndyResult { + fn _wallet_get_rev_reg(&self, wallet_handle: WalletHandle, key: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &key, &RecordOptions::id_value()) } - fn _wallet_get_rev_reg_info(&self, wallet_handle: i32, key: &str) -> IndyResult { + fn _wallet_get_rev_reg_info(&self, wallet_handle: WalletHandle, key: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &key, &RecordOptions::id_value()) } } diff --git a/libindy/src/commands/anoncreds/prover.rs b/libindy/src/commands/anoncreds/prover.rs index ab2215f84c..19e94adf55 100644 --- a/libindy/src/commands/anoncreds/prover.rs +++ b/libindy/src/commands/anoncreds/prover.rs @@ -25,21 +25,22 @@ use services::wallet::{RecordOptions, SearchOptions, WalletRecord, WalletSearch, use utils::sequence; use super::tails::SDKTailsAccessor; +use api::WalletHandle; pub enum ProverCommand { CreateMasterSecret( - i32, // wallet handle + WalletHandle, Option, // master secret id Box) + Send>), CreateCredentialRequest( - i32, // wallet handle + WalletHandle, String, // prover did CredentialOffer, // credential offer CredentialDefinition, // credential def String, // master secret name Box) + Send>), StoreCredential( - i32, // wallet handle + WalletHandle, Option, // credential id CredentialRequestMetadata, // credential request metadata Credential, // credentials @@ -47,15 +48,15 @@ pub enum ProverCommand { Option, // revocation registry definition Box) + Send>), GetCredentials( - i32, // wallet handle + WalletHandle, Option, // filter json Box) + Send>), GetCredential( - i32, // wallet handle + WalletHandle, String, // credential id Box) + Send>), SearchCredentials( - i32, // wallet handle + WalletHandle, Option, // query json Box) + Send>), FetchCredentials( @@ -66,11 +67,11 @@ pub enum ProverCommand { i32, // search handle Box) + Send>), GetCredentialsForProofReq( - i32, // wallet handle + WalletHandle, ProofRequest, // proof request Box) + Send>), SearchCredentialsForProofReq( - i32, // wallet handle + WalletHandle, ProofRequest, // proof request Option, // extra query Box) + Send>), @@ -83,7 +84,7 @@ pub enum ProverCommand { i32, // search handle Box) + Send>), CreateProof( - i32, // wallet handle + WalletHandle, ProofRequest, // proof request RequestedCredentials, // requested credentials String, // master secret name @@ -225,7 +226,7 @@ impl ProverCommandExecutor { } fn create_master_secret(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, master_secret_id: Option<&str>) -> IndyResult { debug!("create_master_secret >>> wallet_handle: {:?}, master_secret_id: {:?}", wallet_handle, master_secret_id); @@ -249,7 +250,7 @@ impl ProverCommandExecutor { } fn create_credential_request(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, prover_did: &str, cred_offer: &CredentialOffer, cred_def: &CredentialDefinitionV1, @@ -294,7 +295,7 @@ impl ProverCommandExecutor { } fn store_credential(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, cred_id: Option<&str>, cred_req_metadata: &CredentialRequestMetadata, credential: &mut Credential, @@ -325,7 +326,7 @@ impl ProverCommandExecutor { } fn get_credentials(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, filter_json: Option<&str>) -> IndyResult { debug!("get_credentials >>> wallet_handle: {:?}, filter_json: {:?}", wallet_handle, filter_json); @@ -349,7 +350,7 @@ impl ProverCommandExecutor { } fn get_credential(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, cred_id: &str) -> IndyResult { debug!("get_credentials >>> wallet_handle: {:?}, cred_id: {:?}", wallet_handle, cred_id); @@ -366,7 +367,7 @@ impl ProverCommandExecutor { } fn search_credentials(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, query_json: Option<&str>) -> IndyResult<(i32, usize)> { debug!("search_credentials >>> wallet_handle: {:?}, query_json: {:?}", wallet_handle, query_json); @@ -429,7 +430,7 @@ impl ProverCommandExecutor { } fn get_credentials_for_proof_req(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, proof_request: &ProofRequest) -> IndyResult { debug!("get_credentials_for_proof_req >>> wallet_handle: {:?}, proof_request: {:?}", wallet_handle, proof_request); @@ -471,7 +472,7 @@ impl ProverCommandExecutor { } fn search_credentials_for_proof_req(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, proof_request: &ProofRequest, extra_query: Option<&ProofRequestExtraQuery>) -> IndyResult { debug!("search_credentials_for_proof_req >>> wallet_handle: {:?}, proof_request: {:?}, extra_query: {:?}", wallet_handle, proof_request, extra_query); @@ -550,7 +551,7 @@ impl ProverCommandExecutor { } fn create_proof(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, proof_req: &ProofRequest, requested_credentials: &RequestedCredentials, master_secret_id: &str, @@ -701,7 +702,7 @@ impl ProverCommandExecutor { } fn _query_requested_credentials(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, query_json: &str, predicate_info: Option<&PredicateInfo>, interval: &Option) -> IndyResult> { @@ -758,7 +759,7 @@ impl ProverCommandExecutor { } - fn _wallet_get_master_secret(&self, wallet_handle: i32, key: &str) -> IndyResult { + fn _wallet_get_master_secret(&self, wallet_handle: WalletHandle, key: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &key, &RecordOptions::id_value()) } } diff --git a/libindy/src/commands/crypto.rs b/libindy/src/commands/crypto.rs index 5bcbdb2004..f68497a456 100644 --- a/libindy/src/commands/crypto.rs +++ b/libindy/src/commands/crypto.rs @@ -15,26 +15,27 @@ use std::str; use utils::crypto::base64; use utils::crypto::chacha20poly1305_ietf; use domain::crypto::combo_box::ComboBox; +use api::WalletHandle; pub enum CryptoCommand { CreateKey( - i32, // wallet handle + WalletHandle, KeyInfo, // key info Box) + Send>, ), SetKeyMetadata( - i32, // wallet handle + WalletHandle, String, // verkey String, // metadata Box) + Send>, ), GetKeyMetadata( - i32, // wallet handle + WalletHandle, String, // verkey Box) + Send>, ), CryptoSign( - i32, // wallet handle + WalletHandle, String, // my vk Vec, // msg Box>) + Send>, @@ -46,14 +47,14 @@ pub enum CryptoCommand { Box) + Send>, ), AuthenticatedEncrypt( - i32, // wallet handle + WalletHandle, String, // my vk String, // their vk Vec, // msg Box>) + Send>, ), AuthenticatedDecrypt( - i32, // wallet handle + WalletHandle, String, // my vk Vec, // encrypted msg Box)>) + Send>, @@ -64,7 +65,7 @@ pub enum CryptoCommand { Box>) + Send>, ), AnonymousDecrypt( - i32, // wallet handle + WalletHandle, String, // my vk Vec, // msg Box>) + Send>, @@ -73,12 +74,12 @@ pub enum CryptoCommand { Vec, // plaintext message String, // list of receiver's keys Option, // senders verkey - i32, //wallet handle + WalletHandle, Box>) + Send>, ), UnpackMessage( Vec, // JWE - i32, // wallet handle + WalletHandle, Box>) + Send>, ), } @@ -148,7 +149,7 @@ impl CryptoCommandExecutor { }; } - fn create_key(&self, wallet_handle: i32, key_info: &KeyInfo) -> IndyResult { + fn create_key(&self, wallet_handle: WalletHandle, key_info: &KeyInfo) -> IndyResult { debug!( "create_key >>> wallet_handle: {:?}, key_info: {:?}", wallet_handle, @@ -164,7 +165,7 @@ impl CryptoCommandExecutor { Ok(res) } - fn crypto_sign(&self, wallet_handle: i32, my_vk: &str, msg: &[u8]) -> IndyResult> { + fn crypto_sign(&self, wallet_handle: WalletHandle, my_vk: &str, msg: &[u8]) -> IndyResult> { trace!( "crypto_sign >>> wallet_handle: {:?}, sender_vk: {:?}, msg: {:?}", wallet_handle, my_vk, msg @@ -206,7 +207,7 @@ impl CryptoCommandExecutor { //TODO begin deprecation process this function. It will be replaced by pack fn authenticated_encrypt( &self, - wallet_handle: i32, + wallet_handle: WalletHandle, my_vk: &str, their_vk: &str, msg: &[u8], @@ -237,7 +238,7 @@ impl CryptoCommandExecutor { //TODO begin deprecation process this function. It will be replaced by unpack fn authenticated_decrypt( &self, - wallet_handle: i32, + wallet_handle: WalletHandle, my_vk: &str, msg: &[u8], ) -> IndyResult<(String, Vec)> { @@ -289,7 +290,7 @@ impl CryptoCommandExecutor { } fn anonymous_decrypt(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, my_vk: &str, encrypted_msg: &[u8]) -> IndyResult> { trace!( @@ -314,7 +315,7 @@ impl CryptoCommandExecutor { Ok(res) } - fn set_key_metadata(&self, wallet_handle: i32, verkey: &str, metadata: &str) -> IndyResult<()> { + fn set_key_metadata(&self, wallet_handle: WalletHandle, verkey: &str, metadata: &str) -> IndyResult<()> { debug!( "set_key_metadata >>> wallet_handle: {:?}, verkey: {:?}, metadata: {:?}", wallet_handle, verkey, metadata @@ -334,7 +335,7 @@ impl CryptoCommandExecutor { Ok(()) } - fn get_key_metadata(&self, wallet_handle: i32, verkey: &str) -> IndyResult { + fn get_key_metadata(&self, wallet_handle: WalletHandle, verkey: &str) -> IndyResult { debug!( "get_key_metadata >>> wallet_handle: {:?}, verkey: {:?}", wallet_handle, verkey @@ -362,7 +363,7 @@ impl CryptoCommandExecutor { message: Vec, receivers: &str, sender_vk: Option, - wallet_handle: i32, + wallet_handle: WalletHandle, ) -> IndyResult> { //parse receivers to structs @@ -424,7 +425,7 @@ impl CryptoCommandExecutor { fn _prepare_protected_authcrypt(&self, receiver_list: Vec, sender_vk: &str, - wallet_handle: i32, + wallet_handle: WalletHandle, ) -> IndyResult<(String, chacha20poly1305_ietf::Key)> { let mut encrypted_recipients_struct : Vec = vec![]; @@ -502,7 +503,7 @@ impl CryptoCommandExecutor { }) } - pub fn unpack_msg(&self, jwe_json: Vec, wallet_handle: i32) -> IndyResult> { + pub fn unpack_msg(&self, jwe_json: Vec, wallet_handle: WalletHandle) -> IndyResult> { //serialize JWE to struct let jwe_struct: JWE = serde_json::from_slice(jwe_json.as_slice()).map_err(|err| { err_msg(IndyErrorKind::InvalidStructure, format!( @@ -560,7 +561,7 @@ impl CryptoCommandExecutor { }); } - fn _find_correct_recipient(&self, protected_struct: Protected, wallet_handle: i32) -> IndyResult<(Recipient, bool)>{ + fn _find_correct_recipient(&self, protected_struct: Protected, wallet_handle: WalletHandle) -> IndyResult<(Recipient, bool)>{ for recipient in protected_struct.recipients { let my_key_res = self.wallet_service.get_indy_object::( wallet_handle, @@ -576,7 +577,7 @@ impl CryptoCommandExecutor { return Err(IndyError::from(IndyErrorKind::WalletItemNotFound)); } - fn _unpack_cek_authcrypt(&self, recipient: Recipient, wallet_handle: i32) -> IndyResult<(Option, chacha20poly1305_ietf::Key)> { + fn _unpack_cek_authcrypt(&self, recipient: Recipient, wallet_handle: WalletHandle) -> IndyResult<(Option, chacha20poly1305_ietf::Key)> { let encrypted_key_vec = base64::decode_urlsafe(&recipient.encrypted_key)?; let iv = base64::decode_urlsafe(&recipient.header.iv.unwrap())?; let enc_sender_vk = base64::decode_urlsafe(&recipient.header.sender.unwrap())?; @@ -611,7 +612,7 @@ impl CryptoCommandExecutor { Ok((Some(sender_vk), cek)) } - fn _unpack_cek_anoncrypt(&self, recipient: Recipient, wallet_handle: i32) -> IndyResult<(Option, chacha20poly1305_ietf::Key)> { + fn _unpack_cek_anoncrypt(&self, recipient: Recipient, wallet_handle: WalletHandle) -> IndyResult<(Option, chacha20poly1305_ietf::Key)> { let encrypted_key_vec = base64::decode_urlsafe(&recipient.encrypted_key)?; //get my private key diff --git a/libindy/src/commands/did.rs b/libindy/src/commands/did.rs index 754cb99f7a..6a0a0a3329 100644 --- a/libindy/src/commands/did.rs +++ b/libindy/src/commands/did.rs @@ -18,58 +18,59 @@ use services::ledger::LedgerService; use services::wallet::{RecordOptions, SearchOptions, WalletService}; use utils::crypto::base58; use utils::sequence; +use api::WalletHandle; pub enum DidCommand { CreateAndStoreMyDid( - i32, // wallet handle + WalletHandle, MyDidInfo, // my did info Box) + Send>), ReplaceKeysStart( - i32, // wallet handle + WalletHandle, KeyInfo, // key info String, // did Box) + Send>), ReplaceKeysApply( - i32, // wallet handle + WalletHandle, String, // my did Box) + Send>), StoreTheirDid( - i32, // wallet handle + WalletHandle, TheirDidInfo, // their did info json Box) + Send>), GetMyDidWithMeta( - i32, // wallet handle + WalletHandle, String, // my did Box) + Send>), ListMyDidsWithMeta( - i32, // wallet handle + WalletHandle, Box) + Send>), KeyForDid( i32, // pool handle - i32, // wallet handle + WalletHandle, String, // did (my or their) Box) + Send>), KeyForLocalDid( - i32, // wallet handle + WalletHandle, String, // did (my or their) Box) + Send>), SetEndpointForDid( - i32, // wallet handle + WalletHandle, String, // did Endpoint, // endpoint address and optional verkey Box) + Send>), GetEndpointForDid( - i32, // wallet handle + WalletHandle, i32, // pool handle String, // did Box)>) + Send>), SetDidMetadata( - i32, // wallet handle + WalletHandle, String, // did String, // metadata Box) + Send>), GetDidMetadata( - i32, // wallet handle + WalletHandle, String, // did Box) + Send>), AbbreviateVerkey( @@ -78,13 +79,13 @@ pub enum DidCommand { Box) + Send>), // Internal commands GetNymAck( - i32, // wallet_handle + WalletHandle, IndyResult, // GetNym Result i32, // deferred cmd id ), // Internal commands GetAttribAck( - i32, // wallet_handle + WalletHandle, IndyResult, // GetAttrib Result i32, // deferred cmd id ), @@ -184,7 +185,7 @@ impl DidCommandExecutor { } fn create_and_store_my_did(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, my_did_info: &MyDidInfo) -> IndyResult<(String, String)> { debug!("create_and_store_my_did >>> wallet_handle: {:?}, my_did_info_json: {:?}", wallet_handle, secret!(my_did_info)); @@ -205,7 +206,7 @@ impl DidCommandExecutor { } fn replace_keys_start(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, key_info: &KeyInfo, my_did: &str) -> IndyResult { debug!("replace_keys_start >>> wallet_handle: {:?}, key_info_json: {:?}, my_did: {:?}", wallet_handle, secret!(key_info), my_did); @@ -228,7 +229,7 @@ impl DidCommandExecutor { } fn replace_keys_apply(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, my_did: &str) -> IndyResult<()> { debug!("replace_keys_apply >>> wallet_handle: {:?}, my_did: {:?}", wallet_handle, my_did); @@ -249,7 +250,7 @@ impl DidCommandExecutor { } fn store_their_did(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, their_did_info: &TheirDidInfo) -> IndyResult<()> { debug!("store_their_did >>> wallet_handle: {:?}, their_did_info: {:?}", wallet_handle, their_did_info); @@ -262,7 +263,7 @@ impl DidCommandExecutor { Ok(()) } - fn get_my_did_with_meta(&self, wallet_handle: i32, my_did: &str) -> IndyResult { + fn get_my_did_with_meta(&self, wallet_handle: WalletHandle, my_did: &str) -> IndyResult { debug!("get_my_did_with_meta >>> wallet_handle: {:?}, my_did: {:?}", wallet_handle, my_did); self.crypto_service.validate_did(&my_did)?; @@ -286,7 +287,7 @@ impl DidCommandExecutor { Ok(res) } - fn list_my_dids_with_meta(&self, wallet_handle: i32) -> IndyResult { + fn list_my_dids_with_meta(&self, wallet_handle: WalletHandle) -> IndyResult { debug!("list_my_dids_with_meta >>> wallet_handle: {:?}", wallet_handle); let mut did_search = @@ -325,7 +326,7 @@ impl DidCommandExecutor { fn key_for_did(&self, pool_handle: i32, - wallet_handle: i32, + wallet_handle: WalletHandle, did: String, cb: Box) + Send>) { debug!("key_for_did >>> pool_handle: {:?}, wallet_handle: {:?}, did: {:?}", pool_handle, wallet_handle, did); @@ -359,7 +360,7 @@ impl DidCommandExecutor { } fn key_for_local_did(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, did: &str) -> IndyResult { info!("key_for_local_did >>> wallet_handle: {:?}, did: {:?}", wallet_handle, did); @@ -385,7 +386,7 @@ impl DidCommandExecutor { } fn set_endpoint_for_did(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, did: &str, endpoint: &Endpoint) -> IndyResult<()> { debug!("set_endpoint_for_did >>> wallet_handle: {:?}, did: {:?}, endpoint: {:?}", wallet_handle, did, endpoint); @@ -403,7 +404,7 @@ impl DidCommandExecutor { } fn get_endpoint_for_did(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, pool_handle: i32, did: String, cb: Box)>) + Send>) { @@ -429,7 +430,7 @@ impl DidCommandExecutor { } fn set_did_metadata(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, did: &str, metadata: String) -> IndyResult<()> { debug!("set_did_metadata >>> wallet_handle: {:?}, did: {:?}, metadata: {:?}", wallet_handle, did, metadata); @@ -446,7 +447,7 @@ impl DidCommandExecutor { } fn get_did_metadata(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, did: &str) -> IndyResult { debug!("get_did_metadata >>> wallet_handle: {:?}, did: {:?}", wallet_handle, did); @@ -486,14 +487,14 @@ impl DidCommandExecutor { } fn get_nym_ack(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, get_nym_reply_result: IndyResult, deferred_cmd_id: i32) { let res = self._get_nym_ack(wallet_handle, get_nym_reply_result); self._execute_deferred_command(deferred_cmd_id, res.err()); } - fn _get_nym_ack(&self, wallet_handle: i32, get_nym_reply_result: IndyResult) -> IndyResult<()> { + fn _get_nym_ack(&self, wallet_handle: WalletHandle, get_nym_reply_result: IndyResult) -> IndyResult<()> { trace!("_get_nym_ack >>> wallet_handle: {:?}, get_nym_reply_result: {:?}", wallet_handle, get_nym_reply_result); let get_nym_reply = get_nym_reply_result?; @@ -525,14 +526,14 @@ impl DidCommandExecutor { } fn get_attrib_ack(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, get_attrib_reply_result: IndyResult, deferred_cmd_id: i32) { let res = self._get_attrib_ack(wallet_handle, get_attrib_reply_result); self._execute_deferred_command(deferred_cmd_id, res.err()); } - fn _get_attrib_ack(&self, wallet_handle: i32, get_attrib_reply_result: IndyResult) -> IndyResult<()> { + fn _get_attrib_ack(&self, wallet_handle: WalletHandle, get_attrib_reply_result: IndyResult) -> IndyResult<()> { trace!("_get_attrib_ack >>> wallet_handle: {:?}, get_attrib_reply_result: {:?}", wallet_handle, get_attrib_reply_result); let get_attrib_reply = get_attrib_reply_result?; @@ -600,7 +601,7 @@ impl DidCommandExecutor { } fn _fetch_their_did_from_ledger(&self, - wallet_handle: i32, pool_handle: i32, + wallet_handle: WalletHandle, pool_handle: i32, did: &str, deferred_cmd: DidCommand) { // Defer this command until their did is fetched from ledger. let deferred_cmd_id = self._defer_command(deferred_cmd); @@ -625,7 +626,7 @@ impl DidCommandExecutor { } fn _fetch_attrib_from_ledger(&self, - wallet_handle: i32, pool_handle: i32, + wallet_handle: WalletHandle, pool_handle: i32, did: &str, deferred_cmd: DidCommand) { // Defer this command until their did is fetched from ledger. let deferred_cmd_id = self._defer_command(deferred_cmd); @@ -649,11 +650,11 @@ impl DidCommandExecutor { ))).unwrap(); } - fn _wallet_get_my_did(&self, wallet_handle: i32, my_did: &str) -> IndyResult { + fn _wallet_get_my_did(&self, wallet_handle: WalletHandle, my_did: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &my_did, &RecordOptions::id_value()) } - fn _wallet_get_their_did(&self, wallet_handle: i32, their_did: &str) -> IndyResult { + fn _wallet_get_their_did(&self, wallet_handle: WalletHandle, their_did: &str) -> IndyResult { self.wallet_service.get_indy_object(wallet_handle, &their_did, &RecordOptions::id_value()) } } diff --git a/libindy/src/commands/ledger.rs b/libindy/src/commands/ledger.rs index 1a72be8ede..c85e327d50 100644 --- a/libindy/src/commands/ledger.rs +++ b/libindy/src/commands/ledger.rs @@ -23,11 +23,12 @@ use services::pool::{ use services::wallet::{RecordOptions, WalletService}; use utils::crypto::base58; use utils::crypto::signature_serializer::serialize_signature; +use api::WalletHandle; pub enum LedgerCommand { SignAndSubmitRequest( i32, // pool handle - i32, // wallet handle + WalletHandle, String, // submitter did String, // request json Box) + Send>), @@ -46,12 +47,12 @@ pub enum LedgerCommand { Option, // timeout Box) + Send>), SignRequest( - i32, // wallet handle + WalletHandle, String, // submitter did String, // request json Box) + Send>), MultiSignRequest( - i32, // wallet handle + WalletHandle, String, // submitter did String, // request json Box) + Send>), @@ -184,6 +185,15 @@ pub enum LedgerCommand { GetResponseMetadata( String, // response Box) + Send>), + BuildAuthRuleRequest( + String, // submitter did + String, // auth type + String, // auth action + String, // field + Option, // old value + String, // new value + String, // constraint + Box) + Send>), } pub struct LedgerCommandExecutor { @@ -361,6 +371,10 @@ impl LedgerCommandExecutor { info!(target: "ledger_command_executor", "GetResponseMetadata command received"); cb(self.get_response_metadata(&response)); } + LedgerCommand::BuildAuthRuleRequest(submitter_did, txn_type, action, field, old_value, new_value, constraint, cb) => { + info!(target: "ledger_command_executor", "BuildAuthRuleRequest command received"); + cb(self.build_auth_rule_request(&submitter_did, &txn_type, &action, &field, old_value.as_ref().map(String::as_str), &new_value, &constraint)); + } }; } @@ -375,7 +389,7 @@ impl LedgerCommandExecutor { fn sign_and_submit_request(&self, pool_handle: i32, - wallet_handle: i32, + wallet_handle: WalletHandle, submitter_did: &str, request_json: &str, cb: Box) + Send>) { @@ -389,7 +403,7 @@ impl LedgerCommandExecutor { } fn _sign_request(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, submitter_did: &str, request_json: &str, signature_type: SignatureType) -> IndyResult { @@ -463,7 +477,7 @@ impl LedgerCommandExecutor { } fn sign_request(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, submitter_did: &str, request_json: &str) -> IndyResult { debug!("sign_request >>> wallet_handle: {:?}, submitter_did: {:?}, request_json: {:?}", wallet_handle, submitter_did, request_json); @@ -476,7 +490,7 @@ impl LedgerCommandExecutor { } fn multi_sign_request(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, submitter_did: &str, request_json: &str) -> IndyResult { debug!("multi_sign_request >>> wallet_handle: {:?}, submitter_did: {:?}, request_json: {:?}", wallet_handle, submitter_did, request_json); @@ -890,6 +904,26 @@ impl LedgerCommandExecutor { Ok(res) } + fn build_auth_rule_request(&self, + submitter_did: &str, + txn_type: &str, + action: &str, + field: &str, + old_value: Option<&str>, + new_value: &str, + constraint: &str) -> IndyResult { + debug!("build_auth_rule_request >>> submitter_did: {:?}, txn_type: {:?}, action: {:?}, field: {:?}, \ + old_value: {:?}, new_value: {:?}, constraint: {:?}", submitter_did, txn_type, action, field, old_value, new_value, constraint); + + self.validate_opt_did(Some(submitter_did))?; + + let res = self.ledger_service.build_auth_rule_request(submitter_did, txn_type, action, field, old_value, new_value, constraint)?; + + debug!("build_auth_rule_request <<< res: {:?}", res); + + Ok(res) + } + fn validate_opt_did(&self, did: Option<&str>) -> IndyResult<()> { match did { Some(did) => Ok(self.crypto_service.validate_did(did)?), diff --git a/libindy/src/commands/non_secrets.rs b/libindy/src/commands/non_secrets.rs index b0fe7d5812..86dd2f4305 100644 --- a/libindy/src/commands/non_secrets.rs +++ b/libindy/src/commands/non_secrets.rs @@ -6,49 +6,50 @@ use domain::wallet::Tags; use errors::prelude::*; use services::wallet::{RecordOptions, SearchOptions, WalletRecord, WalletSearch, WalletService}; use utils::sequence; +use api::WalletHandle; pub enum NonSecretsCommand { - AddRecord(i32, // handle + AddRecord(WalletHandle, String, // type String, // id String, // value Option, //tags Box) + Send>), - UpdateRecordValue(i32, // handle + UpdateRecordValue(WalletHandle, String, // type String, // id String, // value Box) + Send>), - UpdateRecordTags(i32, // handle + UpdateRecordTags(WalletHandle, String, // type String, // id Tags, //tags Box) + Send>), - AddRecordTags(i32, // handle + AddRecordTags(WalletHandle, String, // type String, // id Tags, //tags Box) + Send>), - DeleteRecordTags(i32, // handle + DeleteRecordTags(WalletHandle, String, // type String, // id String, //tag names json Box) + Send>), - DeleteRecord(i32, // handle + DeleteRecord(WalletHandle, String, // type String, // id Box) + Send>), - GetRecord(i32, // handle + GetRecord(WalletHandle, String, // type String, // id String, // options json Box) + Send>), - OpenSearch(i32, // handle + OpenSearch(WalletHandle, String, // type String, // query json String, // options json Box) + Send>), - FetchSearchNextRecords(i32, // wallet handle + FetchSearchNextRecords(WalletHandle, i32, // wallet search handle usize, // count Box) + Send>), @@ -115,7 +116,7 @@ impl NonSecretsCommandExecutor { } fn add_record(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, value: &str, @@ -132,7 +133,7 @@ impl NonSecretsCommandExecutor { } fn update_record_value(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, value: &str) -> IndyResult<()> { @@ -148,7 +149,7 @@ impl NonSecretsCommandExecutor { } fn update_record_tags(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, tags: &Tags) -> IndyResult<()> { @@ -164,7 +165,7 @@ impl NonSecretsCommandExecutor { } fn add_record_tags(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, tags: &Tags) -> IndyResult<()> { @@ -180,7 +181,7 @@ impl NonSecretsCommandExecutor { } fn delete_record_tags(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, tag_names_json: &str) -> IndyResult<()> { @@ -199,7 +200,7 @@ impl NonSecretsCommandExecutor { } fn delete_record(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str) -> IndyResult<()> { trace!("delete_record >>> wallet_handle: {:?}, type_: {:?}, id: {:?}", wallet_handle, type_, id); @@ -214,7 +215,7 @@ impl NonSecretsCommandExecutor { } fn get_record(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, id: &str, options_json: &str) -> IndyResult { @@ -236,7 +237,7 @@ impl NonSecretsCommandExecutor { } fn open_search(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, type_: &str, query_json: &str, options_json: &str) -> IndyResult { @@ -259,7 +260,7 @@ impl NonSecretsCommandExecutor { } fn fetch_search_next_records(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, wallet_search_handle: i32, count: usize) -> IndyResult { trace!("fetch_search_next_records >>> wallet_handle: {:?}, wallet_search_handle: {:?}, count: {:?}", wallet_handle, wallet_search_handle, count); diff --git a/libindy/src/commands/pairwise.rs b/libindy/src/commands/pairwise.rs index e6d64bb5d8..7a401122a7 100644 --- a/libindy/src/commands/pairwise.rs +++ b/libindy/src/commands/pairwise.rs @@ -5,28 +5,29 @@ use services::wallet::{RecordOptions, WalletService}; use std::collections::HashMap; use std::rc::Rc; use std::str; +use api::WalletHandle; pub enum PairwiseCommand { PairwiseExists( - i32, // wallet handle + WalletHandle, String, // their_did Box) + Send>), CreatePairwise( - i32, // wallet handle + WalletHandle, String, // their_did String, // my_did Option, // metadata Box) + Send>), ListPairwise( - i32, // wallet handle + WalletHandle, Box) + Send>), GetPairwise( - i32, // wallet handle + WalletHandle, String, // their_did Box) + Send>), SetPairwiseMetadata( - i32, // wallet handle + WalletHandle, String, // their_did Option, // metadata Box) + Send>) @@ -69,7 +70,7 @@ impl PairwiseCommandExecutor { } fn pairwise_exists(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, their_did: &str) -> IndyResult { debug!("pairwise_exists >>> wallet_handle: {:?}, their_did: {:?}", wallet_handle, their_did); @@ -81,7 +82,7 @@ impl PairwiseCommandExecutor { } fn create_pairwise(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, their_did: &str, my_did: &str, metadata: Option<&str>) -> IndyResult<()> { @@ -104,7 +105,7 @@ impl PairwiseCommandExecutor { } fn list_pairwise(&self, - wallet_handle: i32) -> IndyResult { + wallet_handle: WalletHandle) -> IndyResult { debug!("list_pairwise >>> wallet_handle: {:?}", wallet_handle); let mut pairwise_search = @@ -130,7 +131,7 @@ impl PairwiseCommandExecutor { } fn get_pairwise(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, their_did: &str) -> IndyResult { debug!("get_pairwise >>> wallet_handle: {:?}, their_did: {:?}", wallet_handle, their_did); @@ -148,7 +149,7 @@ impl PairwiseCommandExecutor { fn set_pairwise_metadata(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, their_did: &str, metadata: Option<&str>) -> IndyResult<()> { debug!("set_pairwise_metadata >>> wallet_handle: {:?}, their_did: {:?}, metadata: {:?}", wallet_handle, their_did, metadata); diff --git a/libindy/src/commands/payments.rs b/libindy/src/commands/payments.rs index 18119fbc4a..feb5bab243 100644 --- a/libindy/src/commands/payments.rs +++ b/libindy/src/commands/payments.rs @@ -10,6 +10,7 @@ use errors::prelude::*; use services::crypto::CryptoService; use services::payments::{PaymentsMethodCBs, PaymentsService}; use services::wallet::{RecordOptions, WalletService}; +use api::WalletHandle; pub enum PaymentsCommand { RegisterMethod( @@ -17,19 +18,19 @@ pub enum PaymentsCommand { PaymentsMethodCBs, //method callbacks Box) + Send>), CreateAddress( - i32, //wallet_handle + WalletHandle, String, //type String, //config Box) + Send>), CreateAddressAck( i32, //handle - i32, //wallet handle + WalletHandle, IndyResult), ListAddresses( - i32, //wallet handle + WalletHandle, Box) + Send>), AddRequestFees( - i32, //wallet handle + WalletHandle, Option, //submitter did String, //req String, //inputs @@ -47,7 +48,7 @@ pub enum PaymentsCommand { i32, //handle IndyResult), BuildGetPaymentSourcesRequest( - i32, //wallet_handle + WalletHandle, Option, //submitter did String, //payment address Box) + Send>), @@ -62,7 +63,7 @@ pub enum PaymentsCommand { i32, //cmd_handle IndyResult), BuildPaymentReq( - i32, //wallet_handle + WalletHandle, Option, //submitter did String, //inputs String, //outputs @@ -79,7 +80,7 @@ pub enum PaymentsCommand { i32, IndyResult), BuildMintReq( - i32, //wallet_handle + WalletHandle, Option, //submitter did String, //outputs Option, //extra @@ -88,7 +89,7 @@ pub enum PaymentsCommand { i32, IndyResult), BuildSetTxnFeesReq( - i32, //wallet_handle + WalletHandle, Option, //submitter did String, //method String, //fees @@ -97,7 +98,7 @@ pub enum PaymentsCommand { i32, IndyResult), BuildGetTxnFeesReq( - i32, //wallet_handle + WalletHandle, Option, //submitter did String, //method Box) + Send>), @@ -112,7 +113,7 @@ pub enum PaymentsCommand { i32, IndyResult), BuildVerifyPaymentReq( - i32, //wallet_handle + WalletHandle, Option, //submitter_did String, //receipt Box) + Send>), @@ -273,7 +274,7 @@ impl PaymentsCommandExecutor { res } - fn create_address(&self, wallet_handle: i32, type_: &str, config: &str, cb: Box) + Send>) { + fn create_address(&self, wallet_handle: WalletHandle, type_: &str, config: &str, cb: Box) + Send>) { trace!("create_address >>> wallet_handle: {:?}, type_: {:?}, config: {:?}", wallet_handle, type_, config); match self.wallet_service.check(wallet_handle).map_err(map_err_err!()) { Err(err) => return cb(Err(IndyError::from(err))), @@ -284,7 +285,7 @@ impl PaymentsCommandExecutor { trace!("create_address <<<"); } - fn create_address_ack(&self, handle: i32, wallet_handle: i32, result: IndyResult) { + fn create_address_ack(&self, handle: i32, wallet_handle: WalletHandle, result: IndyResult) { trace!("create_address_ack >>> wallet_handle: {:?}, result: {:?}", wallet_handle, result); let total_result: IndyResult = match result { Ok(res) => { @@ -299,7 +300,7 @@ impl PaymentsCommandExecutor { trace!("create_address_ack <<<"); } - fn list_addresses(&self, wallet_handle: i32, cb: Box) + Send>) { + fn list_addresses(&self, wallet_handle: WalletHandle, cb: Box) + Send>) { trace!("list_addresses >>> wallet_handle: {:?}", wallet_handle); match self.wallet_service.check(wallet_handle).map_err(map_err_err!()) { Err(err) => return cb(Err(IndyError::from(err))), @@ -327,7 +328,7 @@ impl PaymentsCommandExecutor { trace!("list_addresses <<<"); } - fn add_request_fees(&self, wallet_handle: i32, submitter_did: Option<&str>, req: &str, inputs: &str, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { + fn add_request_fees(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, req: &str, inputs: &str, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { trace!("add_request_fees >>> wallet_handle: {:?}, submitter_did: {:?}, req: {:?}, inputs: {:?}, outputs: {:?}, extra: {:?}", wallet_handle, submitter_did, req, inputs, outputs, extra); if let Some(did) = submitter_did { @@ -383,7 +384,7 @@ impl PaymentsCommandExecutor { trace!("parse_response_with_fees_ack <<<"); } - fn build_get_payment_sources_request(&self, wallet_handle: i32, submitter_did: Option<&str>, payment_address: &str, cb: Box) + Send>) { + fn build_get_payment_sources_request(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, payment_address: &str, cb: Box) + Send>) { trace!("build_get_payment_sources_request >>> wallet_handle: {:?}, submitter_did: {:?}, payment_address: {:?}", wallet_handle, submitter_did, payment_address); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { @@ -430,7 +431,7 @@ impl PaymentsCommandExecutor { trace!("parse_get_payment_sources_response_ack <<<"); } - fn build_payment_req(&self, wallet_handle: i32, submitter_did: Option<&str>, inputs: &str, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { + fn build_payment_req(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, inputs: &str, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { trace!("build_payment_req >>> wallet_handle: {:?}, submitter_did: {:?}, inputs: {:?}, outputs: {:?}, extra: {:?}", wallet_handle, submitter_did, inputs, outputs, extra); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { @@ -481,7 +482,7 @@ impl PaymentsCommandExecutor { trace!("parse_payment_response_ack <<<"); } - fn build_mint_req(&self, wallet_handle: i32, submitter_did: Option<&str>, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { + fn build_mint_req(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, outputs: &str, extra: Option<&str>, cb: Box) + Send>) { trace!("build_mint_req >>> wallet_handle: {:?}, submitter_did: {:?}, outputs: {:?}, extra: {:?}", wallet_handle, submitter_did, outputs, extra); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { @@ -515,7 +516,7 @@ impl PaymentsCommandExecutor { trace!("build_mint_req_ack <<<"); } - fn build_set_txn_fees_req(&self, wallet_handle: i32, submitter_did: Option<&str>, type_: &str, fees: &str, cb: Box) + Send>) { + fn build_set_txn_fees_req(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, type_: &str, fees: &str, cb: Box) + Send>) { trace!("build_set_txn_fees_req >>> wallet_handle: {:?}, submitter_did: {:?}, type_: {:?}, fees: {:?}", wallet_handle, submitter_did, type_, fees); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { @@ -544,7 +545,7 @@ impl PaymentsCommandExecutor { trace!("build_set_txn_fees_req_ack <<<"); } - fn build_get_txn_fees_req(&self, wallet_handle: i32, submitter_did: Option<&str>, type_: &str, cb: Box) + Send>) { + fn build_get_txn_fees_req(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, type_: &str, cb: Box) + Send>) { trace!("build_get_txn_fees_req >>> wallet_handle: {:?}, submitter_did: {:?}, type_: {:?}", wallet_handle, submitter_did, type_); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { @@ -579,7 +580,7 @@ impl PaymentsCommandExecutor { trace!("parse_get_txn_fees_response_ack <<<"); } - fn build_verify_payment_request(&self, wallet_handle: i32, submitter_did: Option<&str>, receipt: &str, cb: Box) + Send>) { + fn build_verify_payment_request(&self, wallet_handle: WalletHandle, submitter_did: Option<&str>, receipt: &str, cb: Box) + Send>) { trace!("build_verify_payment_request >>> wallet_handle: {:?}, submitter_did: {:?}, receipt: {:?}", wallet_handle, submitter_did, receipt); if let Some(did) = submitter_did { match self.crypto_service.validate_did(did).map_err(map_err_err!()) { diff --git a/libindy/src/commands/wallet.rs b/libindy/src/commands/wallet.rs index f9419ce685..85c9ebb00c 100644 --- a/libindy/src/commands/wallet.rs +++ b/libindy/src/commands/wallet.rs @@ -10,6 +10,7 @@ use services::crypto::CryptoService; use services::wallet::{KeyDerivationData, WalletService}; use utils::crypto::{base58, chacha20poly1305_ietf, randombytes}; use utils::crypto::chacha20poly1305_ietf::Key as MasterKey; +use api::{WalletHandle, CallbackHandle}; type DeriveKeyResult = IndyResult; @@ -47,14 +48,14 @@ pub enum WalletCommand { Credentials, // credentials KeyDerivationData, DeriveKeyResult, // derive_key_result - i32), + CallbackHandle), Open(Config, // config Credentials, // credentials - Box) + Send>), - OpenContinue(i32, // wallet handle + Box) + Send>), + OpenContinue(WalletHandle, DeriveKeyResult<(MasterKey, Option)>, // derive_key_result ), - Close(i32, // handle + Close(WalletHandle, Box) + Send>), Delete(Config, // config Credentials, // credentials @@ -63,15 +64,15 @@ pub enum WalletCommand { Credentials, // credentials Metadata, // credentials DeriveKeyResult, - i32), - Export(i32, // wallet_handle + CallbackHandle), + Export(WalletHandle, ExportConfig, // export config Box) + Send>), - ExportContinue(i32, // wallet_handle + ExportContinue(WalletHandle, ExportConfig, // export config KeyDerivationData, DeriveKeyResult, - i32), + CallbackHandle), Import(Config, // config Credentials, // credentials ExportConfig, // import config @@ -79,7 +80,8 @@ pub enum WalletCommand { ImportContinue(Config, // config Credentials, // credentials DeriveKeyResult<(MasterKey, MasterKey)>, // derive_key_result - i32, // handle + WalletHandle, + CallbackHandle ), GenerateKey(Option, // config Box) + Send>), @@ -97,8 +99,8 @@ macro_rules! get_cb { pub struct WalletCommandExecutor { wallet_service: Rc, crypto_service: Rc, - open_callbacks: RefCell) + Send>>>, - pending_callbacks: RefCell) + Send>>> + open_callbacks: RefCell) + Send>>>, + pending_callbacks: RefCell) + Send>>> } impl WalletCommandExecutor { @@ -167,9 +169,9 @@ impl WalletCommandExecutor { debug!(target: "wallet_command_executor", "Import command received"); self._import(&config, &credentials, &import_config, cb); } - WalletCommand::ImportContinue(config, credential, key_result, wallet_handle) => { + WalletCommand::ImportContinue(config, credential, key_result, wallet_handle, cb_id) => { debug!(target: "wallet_command_executor", "ImportContinue command received"); - self._import_continue(wallet_handle, &config, &credential, key_result); + self._import_continue(cb_id, wallet_handle, &config, &credential, key_result); } WalletCommand::GenerateKey(config, cb) => { debug!(target: "wallet_command_executor", "DeriveKey command received"); @@ -231,7 +233,7 @@ impl WalletCommandExecutor { let key_data = KeyDerivationData::from_passphrase_with_new_salt(&credentials.key, &credentials.key_derivation_method); - let cb_id = ::utils::sequence::get_next_id(); + let cb_id : CallbackHandle = ::utils::sequence::get_next_id(); self.pending_callbacks.borrow_mut().insert(cb_id, cb); let config = config.clone(); @@ -248,7 +250,7 @@ impl WalletCommandExecutor { credentials.clone(), key_data.clone(), master_key_res, - cb_id, + cb_id ))).unwrap(); })) )).unwrap(); @@ -257,7 +259,7 @@ impl WalletCommandExecutor { } fn _create_continue(&self, - cb_id: i32, + cb_id: CallbackHandle, config: &Config, credentials: &Credentials, key_data: KeyDerivationData, @@ -270,7 +272,7 @@ impl WalletCommandExecutor { fn _open(&self, config: &Config, credentials: &Credentials, - cb: Box) + Send>) { + cb: Box) + Send>) { trace!("_open >>> config: {:?}, credentials: {:?}", config, secret!(credentials)); let (wallet_handle, key_derivation_data, rekey_data) = try_cb!(self.wallet_service.open_wallet_prepare(config, credentials), cb); @@ -299,7 +301,7 @@ impl WalletCommandExecutor { trace!("_open <<< res: {:?}", res); } - fn _derive_rekey_and_continue(wallet_handle: i32, key_result: MasterKey, rekey_data: KeyDerivationData) { + fn _derive_rekey_and_continue(wallet_handle: WalletHandle, key_result: MasterKey, rekey_data: KeyDerivationData) { CommandExecutor::instance().send( Command::Wallet(WalletCommand::DeriveKey( rekey_data, @@ -312,7 +314,7 @@ impl WalletCommandExecutor { ).unwrap(); } - fn _send_open_continue(wallet_handle: i32, key_result: DeriveKeyResult<(MasterKey, Option)>) { + fn _send_open_continue(wallet_handle: WalletHandle, key_result: DeriveKeyResult<(MasterKey, Option)>) { CommandExecutor::instance().send( Command::Wallet(WalletCommand::OpenContinue( wallet_handle, @@ -322,7 +324,7 @@ impl WalletCommandExecutor { } fn _open_continue(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, key_result: DeriveKeyResult<(MasterKey, Option)>) { let cb = self.open_callbacks.borrow_mut().remove(&wallet_handle).unwrap(); cb(key_result @@ -330,10 +332,10 @@ impl WalletCommandExecutor { } fn _close(&self, - handle: i32) -> IndyResult<()> { - trace!("_close >>> handle: {:?}", handle); + wallet_handle: WalletHandle) -> IndyResult<()> { + trace!("_close >>> handle: {:?}", wallet_handle); - let res = self.wallet_service.close_wallet(handle)?; + let res = self.wallet_service.close_wallet(wallet_handle)?; trace!("_close <<< res: {:?}", res); Ok(res) @@ -347,7 +349,7 @@ impl WalletCommandExecutor { let (metadata, key_derivation_data) = try_cb!(self.wallet_service.delete_wallet_prepare(&config, &credentials), cb); - let cb_id = ::utils::sequence::get_next_id(); + let cb_id: CallbackHandle = ::utils::sequence::get_next_id(); self.pending_callbacks.borrow_mut().insert(cb_id, cb); let config = config.clone(); @@ -374,7 +376,7 @@ impl WalletCommandExecutor { } fn _delete_continue(&self, - cb_id: i32, + cb_id: CallbackHandle, config: &Config, credentials: &Credentials, metadata: &Metadata, @@ -385,7 +387,7 @@ impl WalletCommandExecutor { } fn _export(&self, - wallet_handle: i32, + wallet_handle: WalletHandle, export_config: &ExportConfig, cb: Box) + Send>) { trace!("_export >>> handle: {:?}, export_config: {:?}", wallet_handle, secret!(export_config)); @@ -416,8 +418,8 @@ impl WalletCommandExecutor { } fn _export_continue(&self, - cb_id: i32, - wallet_handle: i32, + cb_id: CallbackHandle, + wallet_handle: WalletHandle, export_config: &ExportConfig, key_data: KeyDerivationData, key_result: DeriveKeyResult) { @@ -436,7 +438,8 @@ impl WalletCommandExecutor { let (wallet_handle, key_data, import_key_data) = try_cb!(self.wallet_service.import_wallet_prepare(&config, &credentials, &import_config), cb); - self.pending_callbacks.borrow_mut().insert(wallet_handle, cb); + let cb_id : CallbackHandle = ::utils::sequence::get_next_id(); + self.pending_callbacks.borrow_mut().insert(cb_id, cb); let config = config.clone(); let credentials = credentials.clone(); @@ -458,6 +461,7 @@ impl WalletCommandExecutor { credentials.clone(), import_key_result.and_then(|import_key| key_result.map(|key| (import_key, key))), wallet_handle, + cb_id ))).unwrap(); }), )) @@ -470,11 +474,12 @@ impl WalletCommandExecutor { } fn _import_continue(&self, - wallet_handle: i32, + cb_id: CallbackHandle, + wallet_handle: WalletHandle, config: &Config, credential: &Credentials, key_result: DeriveKeyResult<(MasterKey, MasterKey)>) { - let cb = get_cb!(self, wallet_handle); + let cb = get_cb!(self, cb_id); cb(key_result .and_then(|key| self.wallet_service.import_wallet_continue(wallet_handle, &config, &credential, key))) } diff --git a/libindy/src/domain/ledger/auth_rule.rs b/libindy/src/domain/ledger/auth_rule.rs new file mode 100644 index 0000000000..fbd69733d7 --- /dev/null +++ b/libindy/src/domain/ledger/auth_rule.rs @@ -0,0 +1,64 @@ +use serde_json::Value; + +use super::constants::AUTH_RULE; + +#[allow(non_camel_case_types)] +#[derive(Deserialize, Debug, Serialize, PartialEq)] +pub enum AuthAction { + ADD, + EDIT +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[serde(tag = "constraint_id")] +pub enum Constraint { + #[serde(rename = "OR")] + OrConstraint(CombinationConstraint), + #[serde(rename = "AND")] + AndConstraint(CombinationConstraint), + #[serde(rename = "ROLE")] + RoleConstraint(RoleConstraint), +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct RoleConstraint { + pub sig_count: u32, + pub role: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub need_to_be_owner: Option, +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct CombinationConstraint { + pub auth_constraints: Vec +} + +#[derive(Serialize, PartialEq, Debug)] +pub struct AuthRuleOperation { + #[serde(rename = "type")] + pub _type: String, + pub auth_type: String, + pub field: String, + pub auth_action: AuthAction, + #[serde(skip_serializing_if = "Option::is_none")] + pub old_value: Option, + pub new_value: String, + pub constraint: Constraint, +} + +impl AuthRuleOperation { + pub fn new(auth_type: String, field: String, auth_action: AuthAction, + old_value: Option, new_value: String, constraint: Constraint) -> AuthRuleOperation { + AuthRuleOperation { + _type: AUTH_RULE.to_string(), + auth_type, + field, + auth_action, + old_value, + new_value, + constraint, + } + } +} \ No newline at end of file diff --git a/libindy/src/domain/ledger/constants.rs b/libindy/src/domain/ledger/constants.rs index 998a8aab89..26f964bbb7 100644 --- a/libindy/src/domain/ledger/constants.rs +++ b/libindy/src/domain/ledger/constants.rs @@ -17,10 +17,47 @@ pub const GET_REVOC_REG_DEF: &str = "115"; pub const GET_REVOC_REG: &str = "116"; pub const GET_REVOC_REG_DELTA: &str = "117"; pub const GET_VALIDATOR_INFO: &str = "119"; +pub const AUTH_RULE: &str = "120"; pub const GET_DDO: &str = "120";//TODO change number +pub const REQUESTS: [&str; 21] = [NODE, NYM, GET_TXN, ATTRIB, SCHEMA, CRED_DEF, GET_ATTR, GET_NYM, GET_SCHEMA, + GET_CRED_DEF, POOL_UPGRADE, POOL_RESTART, POOL_CONFIG, REVOC_REG_DEF, REVOC_REG_ENTRY, GET_REVOC_REG_DEF, + GET_REVOC_REG, GET_REVOC_REG_DELTA, GET_VALIDATOR_INFO, AUTH_RULE, GET_DDO]; + pub const TRUSTEE: &str = "0"; pub const STEWARD: &str = "2"; pub const TRUST_ANCHOR: &str = "101"; pub const NETWORK_MONITOR: &str = "201"; pub const ROLE_REMOVE: &str = ""; + + +pub fn txn_name_to_code(txn: &str) -> Option<&str> { + if REQUESTS.contains(&txn) { + return Some(txn) + } + + match txn { + "NODE" => Some(NODE), + "NYM" => Some(NYM), + "GET_TXN" => Some(GET_TXN), + "ATTRIB" => Some(ATTRIB), + "SCHEMA" => Some(SCHEMA), + "CRED_DEF" | "CLAIM_DEF" => Some(CRED_DEF), + "GET_ATTR" => Some(GET_ATTR), + "GET_NYM" => Some(GET_NYM), + "GET_SCHEMA" => Some(GET_SCHEMA), + "GET_CRED_DEF" => Some(GET_CRED_DEF), + "POOL_UPGRADE" => Some(POOL_UPGRADE), + "POOL_RESTART" => Some(POOL_RESTART), + "POOL_CONFIG" => Some(POOL_CONFIG), + "REVOC_REG_DEF" => Some(REVOC_REG_DEF), + "REVOC_REG_ENTRY" => Some(REVOC_REG_ENTRY), + "GET_REVOC_REG_DEF" => Some(GET_REVOC_REG_DEF), + "GET_REVOC_REG" => Some(GET_REVOC_REG), + "GET_REVOC_REG_DELTA" => Some(GET_REVOC_REG_DELTA), + "GET_VALIDATOR_INFO" => Some(GET_VALIDATOR_INFO), + "AUTH_RULE" => Some(AUTH_RULE), + "GET_DDO" => Some(GET_DDO), + _ => None + } +} \ No newline at end of file diff --git a/libindy/src/domain/ledger/mod.rs b/libindy/src/domain/ledger/mod.rs index 1748ecee0c..efaefab01c 100644 --- a/libindy/src/domain/ledger/mod.rs +++ b/libindy/src/domain/ledger/mod.rs @@ -12,3 +12,4 @@ pub mod rev_reg; pub mod response; pub mod validator_info; pub mod constants; +pub mod auth_rule; diff --git a/libindy/src/services/ledger/mod.rs b/libindy/src/services/ledger/mod.rs index 2c842b0bd0..ec6c9bb624 100644 --- a/libindy/src/services/ledger/mod.rs +++ b/libindy/src/services/ledger/mod.rs @@ -12,7 +12,7 @@ use domain::anoncreds::revocation_registry_definition::{RevocationRegistryDefini use domain::anoncreds::revocation_registry_delta::{RevocationRegistryDelta, RevocationRegistryDeltaV1}; use domain::anoncreds::schema::{Schema, SchemaV1, MAX_ATTRIBUTES_COUNT}; use domain::ledger::attrib::{AttribOperation, GetAttribOperation}; -use domain::ledger::constants::{GET_VALIDATOR_INFO, NYM, POOL_RESTART, ROLE_REMOVE, STEWARD, TRUST_ANCHOR, TRUSTEE, NETWORK_MONITOR}; +use domain::ledger::constants::{GET_VALIDATOR_INFO, NYM, POOL_RESTART, ROLE_REMOVE, STEWARD, TRUST_ANCHOR, TRUSTEE, NETWORK_MONITOR, txn_name_to_code}; use domain::ledger::cred_def::{CredDefOperation, GetCredDefOperation, GetCredDefReplyResult}; use domain::ledger::ddo::GetDdoOperation; use domain::ledger::node::{NodeOperation, NodeOperationData}; @@ -25,6 +25,7 @@ use domain::ledger::rev_reg_def::{GetRevocRegDefReplyResult, GetRevRegDefOperati use domain::ledger::schema::{GetSchemaOperation, GetSchemaOperationData, GetSchemaReplyResult, SchemaOperation, SchemaOperationData}; use domain::ledger::txn::{GetTxnOperation, LedgerType}; use domain::ledger::validator_info::GetValidatorInfoOperation; +use domain::ledger::auth_rule::*; use errors::prelude::*; pub mod merkletree; @@ -78,6 +79,35 @@ impl LedgerService { Ok(request) } + pub fn build_auth_rule_request(&self, submitter_did: &str, txn_type: &str, action: &str, field: &str, + old_value: Option<&str>, new_value: &str, constraint: &str) -> IndyResult { + info!("build_auth_rule_request >>> submitter_did: {:?}, txn_type: {:?}, action: {:?}, field: {:?}, \ + old_value: {:?}, new_value: {:?}, constraint: {:?}", submitter_did, txn_type, action, field, old_value, new_value, constraint); + + let txn_type = txn_name_to_code(&txn_type) + .ok_or(err_msg(IndyErrorKind::InvalidStructure, format!("Unsupported `txn_type`: {}", txn_type)))?; + + let action = serde_json::from_str::(&format!("\"{}\"", action)) + .map_err(|err| IndyError::from_msg(IndyErrorKind::InvalidStructure, format!("Cannot parse action: {}", err)))?; + + if action == AuthAction::EDIT && old_value.is_none() { + return Err(err_msg(IndyErrorKind::InvalidStructure, "`old_value` must be specified for EDIT auth action")); + } + + let constraint = serde_json::from_str::(constraint) + .map_err(|err| IndyError::from_msg(IndyErrorKind::InvalidStructure, format!("Can not deserialize Constraint: {}", err)))?; + + let operation = AuthRuleOperation::new(txn_type.to_string(), field.to_string(), action, + old_value.map(String::from), new_value.to_string(), constraint); + + let request = Request::build_request(Some(submitter_did), operation) + .to_indy(IndyErrorKind::InvalidState, "AUTH_RULE request json is invalid")?; + + info!("build_auth_rule_request <<< request: {:?}", request); + + Ok(request) + } + pub fn build_get_nym_request(&self, identifier: Option<&str>, dest: &str) -> IndyResult { info!("build_get_nym_request >>> identifier: {:?}, dest: {:?}", identifier, dest); @@ -924,6 +954,130 @@ mod tests { ledger_service.validate_action(&request).unwrap(); } + mod auth_rule { + use super::*; + + const ADD_AUTH_ACTION: &str = "ADD"; + const EDIT_AUTH_ACTION: &str = "EDIT"; + const FIELD: &str = "role"; + const OLD_VALUE: &str = "0"; + const NEW_VALUE: &str = "101"; + + fn _role_constraint() -> Constraint { + Constraint::RoleConstraint(RoleConstraint { + sig_count: 0, + metadata: None, + role: String::new(), + need_to_be_owner: Some(false), + }) + } + + fn _role_constraint_json() -> String { + serde_json::to_string(&_role_constraint()).unwrap() + } + + #[test] + fn build_auth_rule_request_works_for_role_constraint() { + let ledger_service = LedgerService::new(); + + let expected_result = json!({ + "type": AUTH_RULE, + "auth_type": NYM, + "field": FIELD, + "new_value": NEW_VALUE, + "auth_action": AuthAction::ADD, + "constraint": _role_constraint(), + }); + + let request = ledger_service.build_auth_rule_request(IDENTIFIER, NYM, ADD_AUTH_ACTION, FIELD, + None, NEW_VALUE, + &_role_constraint_json()).unwrap(); + check_request(&request, expected_result); + } + + #[test] + fn build_auth_rule_request_works_for_combination_constraints() { + let ledger_service = LedgerService::new(); + + let constraint = Constraint::AndConstraint( + CombinationConstraint { + auth_constraints: vec![ + _role_constraint(), + Constraint::OrConstraint( + CombinationConstraint { + auth_constraints: vec![ + _role_constraint(), _role_constraint(), ] + } + ) + ] + }); + let constraint_json = serde_json::to_string(&constraint).unwrap(); + + let expected_result = json!({ + "type": AUTH_RULE, + "auth_type": NYM, + "field": FIELD, + "new_value": NEW_VALUE, + "auth_action": AuthAction::ADD, + "constraint": constraint, + }); + + let request = ledger_service.build_auth_rule_request(IDENTIFIER, NYM, ADD_AUTH_ACTION, FIELD, + None, NEW_VALUE, + &constraint_json).unwrap(); + + check_request(&request, expected_result); + } + + #[test] + fn build_auth_rule_request_works_for_edit_auth_action() { + let ledger_service = LedgerService::new(); + + let expected_result = json!({ + "type": AUTH_RULE, + "auth_type": NYM, + "field": FIELD, + "old_value": OLD_VALUE, + "new_value": NEW_VALUE, + "auth_action": AuthAction::EDIT, + "constraint": _role_constraint(), + }); + + let request = ledger_service.build_auth_rule_request(IDENTIFIER, NYM, EDIT_AUTH_ACTION, FIELD, + Some(OLD_VALUE), NEW_VALUE, + &_role_constraint_json()).unwrap(); + check_request(&request, expected_result); + } + + #[test] + fn build_auth_rule_request_works_for_edit_auth_action_missed_old_value() { + let ledger_service = LedgerService::new(); + + let res = ledger_service.build_auth_rule_request(IDENTIFIER, NYM, EDIT_AUTH_ACTION, FIELD, + None, NEW_VALUE, + &_role_constraint_json()); + assert_kind!(IndyErrorKind::InvalidStructure, res); + } + + #[test] + fn build_auth_rule_request_works_for_invalid_auth_type() { + let ledger_service = LedgerService::new(); + + let res = ledger_service.build_auth_rule_request(IDENTIFIER, "WRONG", ADD_AUTH_ACTION, FIELD, + None, NEW_VALUE, + &_role_constraint_json()); + assert_kind!(IndyErrorKind::InvalidStructure, res); + } + + #[test] + fn build_auth_rule_request_works_for_invalid_auth_action() { + let ledger_service = LedgerService::new(); + + let res = ledger_service.build_auth_rule_request(IDENTIFIER, NYM, "WRONG", FIELD, None, NEW_VALUE, &_role_constraint_json()); + assert_kind!(IndyErrorKind::InvalidStructure, res); + } + } + fn check_request(request: &str, expected_result: serde_json::Value) { let request: serde_json::Value = serde_json::from_str(request).unwrap(); assert_eq!(request["operation"], expected_result); diff --git a/libindy/src/services/payments.rs b/libindy/src/services/payments.rs index bd993b4ae1..3f3bbaaf0c 100644 --- a/libindy/src/services/payments.rs +++ b/libindy/src/services/payments.rs @@ -6,7 +6,7 @@ use std::ptr::null; use serde_json; -use api::ErrorCode; +use api::{ErrorCode, WalletHandle}; use api::payments::*; use errors::prelude::*; use utils::ctypes; @@ -82,7 +82,7 @@ impl PaymentsService { trace!("register_payment_method <<<"); } - pub fn create_address(&self, cmd_handle: i32, wallet_handle: i32, method_type: &str, config: &str) -> IndyResult<()> { + pub fn create_address(&self, cmd_handle: i32, wallet_handle: WalletHandle, method_type: &str, config: &str) -> IndyResult<()> { trace!("create_address >>> wallet_handle: {:?}, method_type: {:?}, config: {:?}", wallet_handle, method_type, config); let create_address: CreatePaymentAddressCB = self.methods.borrow().get(method_type) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", method_type)))?.create_address; @@ -96,7 +96,7 @@ impl PaymentsService { res } - pub fn add_request_fees(&self, cmd_handle: i32, method_type: &str, wallet_handle: i32, submitter_did: Option<&str>, req: &str, inputs: &str, outputs: &str, extra: Option<&str>) -> IndyResult<()> { + pub fn add_request_fees(&self, cmd_handle: i32, method_type: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, req: &str, inputs: &str, outputs: &str, extra: Option<&str>) -> IndyResult<()> { trace!("add_request_fees >>> method_type: {:?}, wallet_handle: {:?}, submitter_did: {:?}, req: {:?}, inputs: {:?}, outputs: {:?}, extra: {:?}", method_type, wallet_handle, submitter_did, req, inputs, outputs, extra); let add_request_fees: AddRequestFeesCB = self.methods.borrow().get(method_type) @@ -135,7 +135,7 @@ impl PaymentsService { res } - pub fn build_get_payment_sources_request(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>, address: &str) -> IndyResult<()> { + pub fn build_get_payment_sources_request(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, address: &str) -> IndyResult<()> { trace!("build_get_payment_sources_request >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}, address: {:?}", type_, wallet_handle, submitter_did, address); let build_get_payment_sources_request: BuildGetPaymentSourcesRequestCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_get_payment_sources_request; @@ -168,7 +168,7 @@ impl PaymentsService { res } - pub fn build_payment_req(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>, inputs: &str, outputs: &str, extra: Option<&str>) -> IndyResult<()> { + pub fn build_payment_req(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, inputs: &str, outputs: &str, extra: Option<&str>) -> IndyResult<()> { trace!("build_payment_req >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}, inputs: {:?}, outputs: {:?}, extra: {:?}", type_, wallet_handle, submitter_did, inputs, outputs, extra); let build_payment_req: BuildPaymentReqCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_payment_req; @@ -205,7 +205,7 @@ impl PaymentsService { res } - pub fn build_mint_req(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>, outputs: &str, extra: Option<&str>) -> IndyResult<()> { + pub fn build_mint_req(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, outputs: &str, extra: Option<&str>) -> IndyResult<()> { trace!("build_mint_req >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}, outputs: {:?}, extra: {:?}", type_, wallet_handle, submitter_did, outputs, extra); let build_mint_req: BuildMintReqCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_mint_req; @@ -226,7 +226,7 @@ impl PaymentsService { res } - pub fn build_set_txn_fees_req(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>, fees: &str) -> IndyResult<()> { + pub fn build_set_txn_fees_req(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, fees: &str) -> IndyResult<()> { trace!("build_set_txn_fees_req >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}, fees: {:?}", type_, wallet_handle, submitter_did, fees); let build_set_txn_fees_req: BuildSetTxnFeesReqCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_set_txn_fees_req; @@ -245,7 +245,7 @@ impl PaymentsService { res } - pub fn build_get_txn_fees_req(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>) -> IndyResult<()> { + pub fn build_get_txn_fees_req(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>) -> IndyResult<()> { trace!("build_get_txn_fees_req >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}", type_, wallet_handle, submitter_did); let build_get_txn_fees_req: BuildGetTxnFeesReqCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_get_txn_fees_req; @@ -276,7 +276,7 @@ impl PaymentsService { res } - pub fn build_verify_payment_req(&self, cmd_handle: i32, type_: &str, wallet_handle: i32, submitter_did: Option<&str>, receipt: &str) -> IndyResult<()> { + pub fn build_verify_payment_req(&self, cmd_handle: i32, type_: &str, wallet_handle: WalletHandle, submitter_did: Option<&str>, receipt: &str) -> IndyResult<()> { trace!("build_verify_payment_req >>> type_: {:?}, wallet_handle: {:?}, submitter_did: {:?}, receipt: {:?}", type_, wallet_handle, submitter_did, receipt); let build_verify_payment_req: BuildVerifyPaymentReqCB = self.methods.borrow().get(type_) .ok_or(err_msg(IndyErrorKind::UnknownPaymentMethodType, format!("Unknown payment method {}", type_)))?.build_verify_payment_req; @@ -438,7 +438,7 @@ mod cbs { use self::libc::c_char; - pub fn create_address_cb(cmd_handle: i32, wallet_handle: i32) -> Option Option ErrorCode> { send_ack(cmd_handle, Box::new(move |cmd_handle, result| PaymentsCommand::CreateAddressAck(cmd_handle, wallet_handle, result))) diff --git a/libindy/src/services/pool/catchup.rs b/libindy/src/services/pool/catchup.rs index ebb8dc75dc..e63e358bec 100644 --- a/libindy/src/services/pool/catchup.rs +++ b/libindy/src/services/pool/catchup.rs @@ -17,6 +17,7 @@ pub enum CatchupProgress { MerkleTree, ), NotNeeded(MerkleTree), + Restart(MerkleTree), InProgress, } @@ -51,34 +52,68 @@ pub fn check_nodes_responses_on_status(nodes_votes: &HashMap<(String, usize, Opt node_cnt: usize, f: usize, pool_name: &str) -> IndyResult { - if let Some((most_popular_vote, votes_cnt)) = nodes_votes.iter().map(|(key, val)| (key, val.len())).max_by_key(|entry| entry.1) { - let is_consensus_reached = votes_cnt == node_cnt - f; - if is_consensus_reached { - if most_popular_vote.0.eq("timeout") { - return Err(err_msg(IndyErrorKind::PoolTimeout, "Pool timeout")); - } - - return _try_to_catch_up(most_popular_vote, merkle_tree).or_else(|err| { + let (votes, timeout_votes): (HashMap<&(String, usize, Option>), usize>, HashMap<&(String, usize, Option>), usize>) = + nodes_votes + .iter() + .map(|(key, val)| (key, val.len())) + .partition(|((key, _, _), _)| key != "timeout"); + + let most_popular_not_timeout = + votes + .iter() + .max_by_key(|entry| entry.1); + + let timeout_votes = timeout_votes.iter().last(); + + if let Some((most_popular_not_timeout_vote, votes_cnt)) = most_popular_not_timeout { + if *votes_cnt == f + 1 { + return _try_to_catch_up(most_popular_not_timeout_vote, merkle_tree).or_else(|err| { if merkle_tree_factory::drop_cache(pool_name).is_ok() { let merkle_tree = merkle_tree_factory::create(pool_name)?; - _try_to_catch_up(most_popular_vote, &merkle_tree) + _try_to_catch_up(most_popular_not_timeout_vote, &merkle_tree) } else { Err(err) } }); } else { - let reps_cnt: usize = nodes_votes.values().map(|set| set.len()).sum(); - let positive_votes_cnt = votes_cnt + (node_cnt - reps_cnt); - let is_consensus_reachable = positive_votes_cnt < node_cnt - f; - if is_consensus_reachable { - //TODO: maybe we should change the error, but it was made to escape changing of ErrorCode returned to client - return Err(err_msg(IndyErrorKind::PoolTimeout, "No consensus possible")); - } + return _if_consensus_reachable(nodes_votes, node_cnt, *votes_cnt, f, pool_name); + } + } else if let Some((_, votes_cnt)) = timeout_votes { + if *votes_cnt == node_cnt - f { + return _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "Pool timeout")); + } else { + return _if_consensus_reachable(nodes_votes, node_cnt, *votes_cnt, f, pool_name); } } Ok(CatchupProgress::InProgress) } +fn _if_consensus_reachable(nodes_votes: &HashMap<(String, usize, Option>), HashSet>, + node_cnt: usize, + votes_cnt: usize, + f: usize, + pool_name: &str) -> IndyResult { + let reps_cnt: usize = nodes_votes.values().map(HashSet::len).sum(); + let positive_votes_cnt = votes_cnt + (node_cnt - reps_cnt); + let is_consensus_not_reachable = positive_votes_cnt < node_cnt - f; + if is_consensus_not_reachable { + //TODO: maybe we should change the error, but it was made to escape changing of ErrorCode returned to client + _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "No consensus possible")) + } else { + Ok(CatchupProgress::InProgress) + } +} + + +fn _try_to_restart_catch_up(pool_name: &str, err: IndyError) -> IndyResult { + if merkle_tree_factory::drop_cache(pool_name).is_ok() { + let merkle_tree = merkle_tree_factory::create(pool_name)?; + return Ok(CatchupProgress::Restart(merkle_tree)); + } else { + return Err(err); + } +} + fn _try_to_catch_up(ledger_status: &(String, usize, Option>), merkle_tree: &MerkleTree) -> IndyResult { let &(ref target_mt_root, target_mt_size, ref hashes) = ledger_status; let cur_mt_size = merkle_tree.count(); diff --git a/libindy/src/services/pool/events.rs b/libindy/src/services/pool/events.rs index c34f2ca426..f9d0f0987f 100644 --- a/libindy/src/services/pool/events.rs +++ b/libindy/src/services/pool/events.rs @@ -69,6 +69,9 @@ pub enum PoolEvent { usize, //target_mt_size MerkleTree, ), + CatchupRestart( + MerkleTree, + ), CatchupTargetNotFound(IndyError), #[allow(dead_code)] //FIXME PoolOutdated, diff --git a/libindy/src/services/pool/pool.rs b/libindy/src/services/pool/pool.rs index a3731f9ccd..72837fa550 100644 --- a/libindy/src/services/pool/pool.rs +++ b/libindy/src/services/pool/pool.rs @@ -305,6 +305,17 @@ impl> PoolSM { CommandExecutor::instance().send(Command::Pool(pc)).unwrap(); PoolState::Terminated(state.into()) } + PoolEvent::CatchupRestart(merkle_tree) => { + if let Ok((nodes, remotes)) = _get_nodes_and_remotes(&merkle_tree) { + state.networker.borrow_mut().process_event(Some(NetworkerEvent::NodesStateUpdated(remotes))); + state.request_handler = R::new(state.networker.clone(), _get_f(nodes.len()), &vec![], &nodes, None, &pool_name, timeout, extended_timeout); + let ls = _ledger_status(&merkle_tree); + state.request_handler.process_event(Some(RequestEvent::LedgerStatus(ls, None, Some(merkle_tree)))); + PoolState::GettingCatchupTarget(state) + } else { + PoolState::Terminated(state.into()) + } + } PoolEvent::CatchupTargetFound(target_mt_root, target_mt_size, merkle_tree) => { if let Ok((nodes, remotes)) = _get_nodes_and_remotes(&merkle_tree) { state.networker.borrow_mut().process_event(Some(NetworkerEvent::NodesStateUpdated(remotes))); @@ -605,19 +616,22 @@ fn _get_request_handler_with_ledger_status_sent LedgerStatus{ let protocol_version = ProtocolVersion::get(); - let ls = LedgerStatus { + LedgerStatus { txnSeqNo: merkle.count(), merkleRoot: merkle.root_hash().as_slice().to_base58(), ledgerId: 0, ppSeqNo: None, viewNo: None, protocolVersion: if protocol_version > 1 { Some(protocol_version) } else { None }, - }; - - request_handler.process_event(Some(RequestEvent::LedgerStatus(ls, None, Some(merkle)))); - Ok(request_handler) + } } fn _get_nodes_and_remotes(merkle: &MerkleTree) -> IndyResult<(HashMap>, Vec)> { diff --git a/libindy/src/services/pool/request_handler.rs b/libindy/src/services/pool/request_handler.rs index f0f057943c..5f9dba974e 100644 --- a/libindy/src/services/pool/request_handler.rs +++ b/libindy/src/services/pool/request_handler.rs @@ -546,12 +546,20 @@ impl RequestSM { pool_name: &str) -> (RequestState, Option) { let (finished, result) = RequestSM::_process_catchup_target(mt_root, sz, cons_proof, &node_alias, &mut state, f, nodes, pool_name); - if finished { - state.networker.borrow_mut().process_event(Some(NetworkerEvent::CleanTimeout(req_id, None))); - (RequestState::finish(), result) - } else { - state.networker.borrow_mut().process_event(Some(NetworkerEvent::CleanTimeout(req_id, Some(node_alias)))); - (RequestState::CatchupConsensus(state), result) + + match (finished, result) { + (true, result) => { + state.networker.borrow_mut().process_event(Some(NetworkerEvent::CleanTimeout(req_id, None))); + (RequestState::finish(), result) + }, + (false, Some(PoolEvent::CatchupRestart(merkle_tree))) => { + state.networker.borrow_mut().process_event(Some(NetworkerEvent::CleanTimeout(req_id, None))); + (RequestState::CatchupConsensus(state), Some(PoolEvent::CatchupRestart(merkle_tree))) + }, + (false, result) => { + state.networker.borrow_mut().process_event(Some(NetworkerEvent::CleanTimeout(req_id, Some(node_alias)))); + (RequestState::CatchupConsensus(state), result) + } } } @@ -579,6 +587,7 @@ impl RequestSM { &pool_name) { Ok(CatchupProgress::InProgress) => (false, None), Ok(CatchupProgress::NotNeeded(merkle_tree)) => (true, Some(PoolEvent::Synced(merkle_tree))), + Ok(CatchupProgress::Restart(merkle_tree)) => (false, Some(PoolEvent::CatchupRestart(merkle_tree))), Ok(CatchupProgress::ShouldBeStarted(target_mt_root, target_mt_size, merkle_tree)) => (true, Some(PoolEvent::CatchupTargetFound(target_mt_root, target_mt_size, merkle_tree))), Err(err) => (true, Some(PoolEvent::CatchupTargetNotFound(err))), @@ -1501,7 +1510,7 @@ pub mod tests { assert_match!(&RequestState::CatchupConsensus(_), &request_handler.request_wrapper.as_ref().unwrap().state); request_handler.process_event(Some(RequestEvent::LedgerStatus(LedgerStatus::default(), Some("n3".to_string()), Some(MerkleTree::default())))); - assert_match!(&RequestState::CatchupConsensus(_), &request_handler.request_wrapper.as_ref().unwrap().state); + assert_match!(&RequestState::Finish(_), &request_handler.request_wrapper.as_ref().unwrap().state); request_handler.process_event(Some(RequestEvent::LedgerStatus(LedgerStatus::default(), Some("n4".to_string()), Some(MerkleTree::default())))); assert_match!(RequestState::Finish(_), request_handler.request_wrapper.unwrap().state); } diff --git a/libindy/src/services/wallet/mod.rs b/libindy/src/services/wallet/mod.rs index 038f7a35dc..716df35073 100644 --- a/libindy/src/services/wallet/mod.rs +++ b/libindy/src/services/wallet/mod.rs @@ -9,6 +9,7 @@ use named_type::NamedType; use serde_json; use api::wallet::*; + use domain::wallet::{Config, Credentials, ExportConfig, Metadata, MetadataArgon, MetadataRaw, Tags}; use errors::prelude::*; pub use services::wallet::encryption::KeyDerivationData; @@ -21,6 +22,7 @@ use self::storage::{WalletStorage, WalletStorageType}; use self::storage::default::SQLiteStorageType; use self::storage::plugged::PluggedStorageType; use self::wallet::{Keys, Wallet}; +use api::WalletHandle; mod storage; mod encryption; @@ -32,9 +34,9 @@ mod wallet; pub struct WalletService { storage_types: RefCell>>, - wallets: RefCell>>, - pending_for_open: RefCell, Metadata, Option)>>, - pending_for_import: RefCell, chacha20poly1305_ietf::Nonce, usize, Vec, KeyDerivationData)>>, + wallets: RefCell>>, + pending_for_open: RefCell, Metadata, Option)>>, + pending_for_import: RefCell, chacha20poly1305_ietf::Nonce, usize, Vec, KeyDerivationData)>>, } impl WalletService { @@ -177,14 +179,14 @@ impl WalletService { Ok(()) } - pub fn open_wallet_prepare(&self, config: &Config, credentials: &Credentials) -> IndyResult<(i32, KeyDerivationData, Option)> { + pub fn open_wallet_prepare(&self, config: &Config, credentials: &Credentials) -> IndyResult<(WalletHandle, KeyDerivationData, Option)> { trace!("open_wallet >>> config: {:?}, credentials: {:?}", config, secret!(&credentials)); self._is_id_from_config_not_used(config)?; let (storage, metadata, key_derivation_data) = self._open_storage_and_fetch_metadata(config, credentials)?; - let wallet_handle = sequence::get_next_id(); + let wallet_handle = WalletHandle(sequence::get_next_id()); let rekey_data: Option = credentials.rekey.as_ref().map(|ref rekey| KeyDerivationData::from_passphrase_with_new_salt(rekey, &credentials.rekey_derivation_method)); @@ -194,7 +196,7 @@ impl WalletService { Ok((wallet_handle, key_derivation_data, rekey_data)) } - pub fn open_wallet_continue(&self, wallet_handle: i32, master_key: (&MasterKey, Option<&MasterKey>)) -> IndyResult { + pub fn open_wallet_continue(&self, wallet_handle: WalletHandle, master_key: (&MasterKey, Option<&MasterKey>)) -> IndyResult { let (id, storage, metadata, rekey_data) = self.pending_for_open.borrow_mut().remove(&wallet_handle) .ok_or(err_msg(IndyErrorKind::InvalidState, "Open data not found"))?; @@ -227,7 +229,7 @@ impl WalletService { Ok((storage, metadata, key_derivation_data)) } - pub fn close_wallet(&self, handle: i32) -> IndyResult<()> { + pub fn close_wallet(&self, handle: WalletHandle) -> IndyResult<()> { trace!("close_wallet >>> handle: {:?}", handle); match self.wallets.borrow_mut().remove(&handle) { @@ -247,7 +249,7 @@ impl WalletService { } } - pub fn add_record(&self, wallet_handle: i32, type_: &str, name: &str, value: &str, tags: &Tags) -> IndyResult<()> { + pub fn add_record(&self, wallet_handle: WalletHandle, type_: &str, name: &str, value: &str, tags: &Tags) -> IndyResult<()> { match self.wallets.borrow_mut().get_mut(&wallet_handle) { Some(wallet) => wallet.add(type_, name, value, tags) .map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name)), @@ -255,7 +257,7 @@ impl WalletService { } } - pub fn add_indy_object(&self, wallet_handle: i32, name: &str, object: &T, tags: &Tags) + pub fn add_indy_object(&self, wallet_handle: WalletHandle, name: &str, object: &T, tags: &Tags) -> IndyResult where T: ::serde::Serialize + Sized, T: NamedType { let type_ = T::short_type_name(); @@ -266,7 +268,7 @@ impl WalletService { Ok(object_json) } - pub fn update_record_value(&self, wallet_handle: i32, type_: &str, name: &str, value: &str) -> IndyResult<()> { + pub fn update_record_value(&self, wallet_handle: WalletHandle, type_: &str, name: &str, value: &str) -> IndyResult<()> { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => wallet.update(type_, name, value) @@ -275,7 +277,7 @@ impl WalletService { } } - pub fn update_indy_object(&self, wallet_handle: i32, name: &str, object: &T) -> IndyResult where T: ::serde::Serialize + Sized, T: NamedType { + pub fn update_indy_object(&self, wallet_handle: WalletHandle, name: &str, object: &T) -> IndyResult where T: ::serde::Serialize + Sized, T: NamedType { let type_ = T::short_type_name(); match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => { @@ -288,7 +290,7 @@ impl WalletService { } } - pub fn add_record_tags(&self, wallet_handle: i32, type_: &str, name: &str, tags: &Tags) -> IndyResult<()> { + pub fn add_record_tags(&self, wallet_handle: WalletHandle, type_: &str, name: &str, tags: &Tags) -> IndyResult<()> { match self.wallets.borrow_mut().get_mut(&wallet_handle) { Some(wallet) => wallet.add_tags(type_, name, tags) .map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name)), @@ -296,7 +298,7 @@ impl WalletService { } } - pub fn update_record_tags(&self, wallet_handle: i32, type_: &str, name: &str, tags: &Tags) -> IndyResult<()> { + pub fn update_record_tags(&self, wallet_handle: WalletHandle, type_: &str, name: &str, tags: &Tags) -> IndyResult<()> { match self.wallets.borrow_mut().get_mut(&wallet_handle) { Some(wallet) => wallet.update_tags(type_, name, tags) .map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name)), @@ -304,7 +306,7 @@ impl WalletService { } } - pub fn delete_record_tags(&self, wallet_handle: i32, type_: &str, name: &str, tag_names: &[&str]) -> IndyResult<()> { + pub fn delete_record_tags(&self, wallet_handle: WalletHandle, type_: &str, name: &str, tag_names: &[&str]) -> IndyResult<()> { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => wallet.delete_tags(type_, name, tag_names) .map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name)), @@ -312,7 +314,7 @@ impl WalletService { } } - pub fn delete_record(&self, wallet_handle: i32, type_: &str, name: &str) -> IndyResult<()> { + pub fn delete_record(&self, wallet_handle: WalletHandle, type_: &str, name: &str) -> IndyResult<()> { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => wallet.delete(type_, name) .map_err(|err| WalletService::_map_wallet_storage_error(err, type_, name)), @@ -320,11 +322,11 @@ impl WalletService { } } - pub fn delete_indy_record(&self, wallet_handle: i32, name: &str) -> IndyResult<()> where T: NamedType { + pub fn delete_indy_record(&self, wallet_handle: WalletHandle, name: &str) -> IndyResult<()> where T: NamedType { self.delete_record(wallet_handle, &self.add_prefix(T::short_type_name()), name) } - pub fn get_record(&self, wallet_handle: i32, type_: &str, name: &str, options_json: &str) -> IndyResult { + pub fn get_record(&self, wallet_handle: WalletHandle, type_: &str, name: &str, options_json: &str) -> IndyResult { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => wallet.get(type_, name, options_json) @@ -333,12 +335,12 @@ impl WalletService { } } - pub fn get_indy_record(&self, wallet_handle: i32, name: &str, options_json: &str) -> IndyResult where T: NamedType { + pub fn get_indy_record(&self, wallet_handle: WalletHandle, name: &str, options_json: &str) -> IndyResult where T: NamedType { self.get_record(wallet_handle, &self.add_prefix(T::short_type_name()), name, options_json) } // Dirty hack. json must live longer then result T - pub fn get_indy_object(&self, wallet_handle: i32, name: &str, options_json: &str) -> IndyResult where T: ::serde::de::DeserializeOwned, T: NamedType { + pub fn get_indy_object(&self, wallet_handle: WalletHandle, name: &str, options_json: &str) -> IndyResult where T: ::serde::de::DeserializeOwned, T: NamedType { let type_ = T::short_type_name(); let record: WalletRecord = match self.wallets.borrow().get(&wallet_handle) { @@ -354,7 +356,7 @@ impl WalletService { } // Dirty hack. json must live longer then result T - pub fn get_indy_opt_object(&self, wallet_handle: i32, name: &str, options_json: &str) -> IndyResult> where T: ::serde::de::DeserializeOwned, T: NamedType { + pub fn get_indy_opt_object(&self, wallet_handle: WalletHandle, name: &str, options_json: &str) -> IndyResult> where T: ::serde::de::DeserializeOwned, T: NamedType { match self.get_indy_object::(wallet_handle, name, options_json) { Ok(res) => Ok(Some(res)), Err(ref err) if err.kind() == IndyErrorKind::WalletItemNotFound => Ok(None), @@ -362,19 +364,19 @@ impl WalletService { } } - pub fn search_records(&self, wallet_handle: i32, type_: &str, query_json: &str, options_json: &str) -> IndyResult { + pub fn search_records(&self, wallet_handle: WalletHandle, type_: &str, query_json: &str, options_json: &str) -> IndyResult { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => Ok(WalletSearch { iter: wallet.search(type_, query_json, Some(options_json))? }), None => Err(err_msg(IndyErrorKind::InvalidWalletHandle, "Unknown wallet handle")) } } - pub fn search_indy_records(&self, wallet_handle: i32, query_json: &str, options_json: &str) -> IndyResult where T: NamedType { + pub fn search_indy_records(&self, wallet_handle: WalletHandle, query_json: &str, options_json: &str) -> IndyResult where T: NamedType { self.search_records(wallet_handle, &self.add_prefix(T::short_type_name()), query_json, options_json) } #[allow(dead_code)] // TODO: Should we implement getting all records or delete everywhere? - pub fn search_all_records(&self, _wallet_handle: i32) -> IndyResult { + pub fn search_all_records(&self, _wallet_handle: WalletHandle) -> IndyResult { // match self.wallets.borrow().get(&wallet_handle) { // Some(wallet) => wallet.search_all_records(), // None => Err(IndyError::InvalidHandle(wallet_handle.to_string())) @@ -382,7 +384,7 @@ impl WalletService { unimplemented!() } - pub fn upsert_indy_object(&self, wallet_handle: i32, name: &str, object: &T) -> IndyResult + pub fn upsert_indy_object(&self, wallet_handle: WalletHandle, name: &str, object: &T) -> IndyResult where T: ::serde::Serialize + Sized, T: NamedType { if self.record_exists::(wallet_handle, name)? { self.update_indy_object::(wallet_handle, name, object) @@ -391,7 +393,7 @@ impl WalletService { } } - pub fn record_exists(&self, wallet_handle: i32, name: &str) -> IndyResult where T: NamedType { + pub fn record_exists(&self, wallet_handle: WalletHandle, name: &str) -> IndyResult where T: NamedType { match self.wallets.borrow().get(&wallet_handle) { Some(wallet) => match wallet.get(&self.add_prefix(T::short_type_name()), name, &RecordOptions::id()) { @@ -403,14 +405,14 @@ impl WalletService { } } - pub fn check(&self, handle: i32) -> IndyResult<()> { + pub fn check(&self, handle: WalletHandle) -> IndyResult<()> { match self.wallets.borrow().get(&handle) { Some(_) => Ok(()), None => Err(err_msg(IndyErrorKind::InvalidWalletHandle, "Unknown wallet handle")) } } - pub fn export_wallet(&self, wallet_handle: i32, export_config: &ExportConfig, version: u32, key: (&KeyDerivationData, &MasterKey)) -> IndyResult<()> { + pub fn export_wallet(&self, wallet_handle: WalletHandle, export_config: &ExportConfig, version: u32, key: (&KeyDerivationData, &MasterKey)) -> IndyResult<()> { trace!("export_wallet >>> wallet_handle: {:?}, export_config: {:?}, version: {:?}", wallet_handle, secret!(export_config), version); if version != 0 { @@ -448,7 +450,7 @@ impl WalletService { pub fn import_wallet_prepare(&self, config: &Config, credentials: &Credentials, - export_config: &ExportConfig) -> IndyResult<(i32, KeyDerivationData, KeyDerivationData)> { + export_config: &ExportConfig) -> IndyResult<(WalletHandle, KeyDerivationData, KeyDerivationData)> { trace!("import_wallet_prepare >>> config: {:?}, credentials: {:?}, export_config: {:?}", config, secret!(export_config), secret!(export_config)); let exported_file_to_import = @@ -459,7 +461,7 @@ impl WalletService { let (reader, import_key_derivation_data, nonce, chunk_size, header_bytes) = preparse_file_to_import(exported_file_to_import, &export_config.key)?; let key_data = KeyDerivationData::from_passphrase_with_new_salt(&credentials.key, &credentials.key_derivation_method); - let wallet_handle = sequence::get_next_id(); + let wallet_handle = WalletHandle(sequence::get_next_id()); let stashed_key_data = key_data.clone(); @@ -468,7 +470,7 @@ impl WalletService { Ok((wallet_handle, key_data, import_key_derivation_data)) } - pub fn import_wallet_continue(&self, wallet_handle: i32, config: &Config, credentials: &Credentials, key: (MasterKey, MasterKey)) -> IndyResult<()> { + pub fn import_wallet_continue(&self, wallet_handle: WalletHandle, config: &Config, credentials: &Credentials, key: (MasterKey, MasterKey)) -> IndyResult<()> { let (reader, nonce, chunk_size, header_bytes, key_data) = self.pending_for_import.borrow_mut().remove(&wallet_handle).unwrap(); let (import_key, master_key) = key; @@ -735,6 +737,8 @@ mod tests { use std::fs; use std::path::Path; + use api::INVALID_WALLET_HANDLE; + use domain::wallet::KeyDerivationMethod; use utils::environment; use utils::inmem_wallet::InmemWallet; @@ -743,12 +747,12 @@ mod tests { use super::*; impl WalletService { - fn open_wallet(&self, config: &Config, credentials: &Credentials) -> IndyResult { + fn open_wallet(&self, config: &Config, credentials: &Credentials) -> IndyResult { self._is_id_from_config_not_used(config)?; let (storage, metadata, key_derivation_data) = self._open_storage_and_fetch_metadata(config, credentials)?; - let wallet_handle = sequence::get_next_id(); + let wallet_handle = WalletHandle(sequence::get_next_id()); let rekey_data: Option = credentials.rekey.as_ref().map(|ref rekey| KeyDerivationData::from_passphrase_with_new_salt(rekey, &credentials.rekey_derivation_method)); @@ -782,7 +786,7 @@ mod tests { let (reader, import_key_derivation_data, nonce, chunk_size, header_bytes) = preparse_file_to_import(exported_file_to_import, &export_config.key)?; let key_data = KeyDerivationData::from_passphrase_with_new_salt(&credentials.key, &credentials.key_derivation_method); - let wallet_handle = sequence::get_next_id(); + let wallet_handle = WalletHandle(sequence::get_next_id()); let import_key = import_key_derivation_data.calc_master_key()?; let master_key = key_data.calc_master_key()?; @@ -1102,7 +1106,7 @@ mod tests { wallet_service.create_wallet(&_config(), &RAW_CREDENTIAL, (&RAW_KDD, &RAW_MASTER_KEY)).unwrap(); let wallet_handle = wallet_service.open_wallet(&_config(), &RAW_CREDENTIAL).unwrap(); - let res = wallet_service.close_wallet(wallet_handle + 1); + let res = wallet_service.close_wallet(INVALID_WALLET_HANDLE); assert_kind!(IndyErrorKind::InvalidWalletHandle, res); wallet_service.close_wallet(wallet_handle).unwrap(); @@ -1776,10 +1780,10 @@ mod tests { let wallet_service = WalletService::new(); wallet_service.create_wallet(&_config(), &RAW_CREDENTIAL, (&RAW_KDD, &RAW_MASTER_KEY)).unwrap(); - let wallet_handle = wallet_service.open_wallet(&_config(), &RAW_CREDENTIAL).unwrap(); + let _wallet_handle = wallet_service.open_wallet(&_config(), &RAW_CREDENTIAL).unwrap(); let (kdd, master_key) = _export_key_raw(); - let res = wallet_service.export_wallet(wallet_handle + 1, &_export_config_raw(), 0, (&kdd, &master_key)); + let res = wallet_service.export_wallet(INVALID_WALLET_HANDLE, &_export_config_raw(), 0, (&kdd, &master_key)); assert_kind!(IndyErrorKind::InvalidWalletHandle, res); assert!(!_export_file_path().exists()); } diff --git a/libindy/tests/ledger.rs b/libindy/tests/ledger.rs index 3152375244..489211f9a3 100644 --- a/libindy/tests/ledger.rs +++ b/libindy/tests/ledger.rs @@ -549,7 +549,7 @@ mod high_cases { #[test] #[cfg(feature = "local_nodes_pool")] fn indy_attrib_requests_works_for_raw_value() { - let (wallet_handle, pool_handle, did,_) = utils::setup_new_identity(); + let (wallet_handle, pool_handle, did, _) = utils::setup_new_identity(); let attrib_request = ledger::build_attrib_request(&did, &did, @@ -571,7 +571,7 @@ mod high_cases { #[test] #[cfg(feature = "local_nodes_pool")] fn indy_attrib_requests_works_for_hash_value() { - let (wallet_handle, pool_handle, did,_) = utils::setup_new_identity(); + let (wallet_handle, pool_handle, did, _) = utils::setup_new_identity(); let mut ctx = Hasher::new(MessageDigest::sha256()).unwrap(); ctx.update(&ATTRIB_RAW_DATA.as_bytes()).unwrap(); @@ -597,7 +597,7 @@ mod high_cases { #[test] #[cfg(feature = "local_nodes_pool")] fn indy_attrib_requests_works_for_encrypted_value() { - let (wallet_handle, pool_handle, did,_) = utils::setup_new_identity(); + let (wallet_handle, pool_handle, did, _) = utils::setup_new_identity(); let key = secretbox::gen_key(); let nonce = secretbox::gen_nonce(); @@ -623,7 +623,7 @@ mod high_cases { #[test] #[cfg(feature = "local_nodes_pool")] fn indy_get_attrib_requests_works_for_default_submitter_did() { - let (wallet_handle, pool_handle, did,_) = utils::setup_new_identity(); + let (wallet_handle, pool_handle, did, _) = utils::setup_new_identity(); let attrib_request = ledger::build_attrib_request(&did, &did, @@ -1668,6 +1668,160 @@ mod high_cases { assert!(response_metadata["lastSeqNo"].as_u64().is_none()); } } + + mod auth_rule { + use super::*; + + const ADD_AUTH_ACTION: &str = "ADD"; + const EDIT_AUTH_ACTION: &str = "EDIT"; + const FIELD: &str = "role"; + const OLD_VALUE: &str = "0"; + const NEW_VALUE: &str = "101"; + const ROLE_CONSTRAINT: &str = r#"{ + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": false + }"#; + + #[test] + fn indy_build_auth_rule_request_works_for_add_action() { + let expected_result = json!({ + "type": constants::AUTH_RULE, + "auth_type": constants::NYM, + "field": FIELD, + "new_value": NEW_VALUE, + "auth_action": ADD_AUTH_ACTION, + "constraint": json!({ + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": false + }), + }); + + let request = ledger::build_auth_rule_request(DID_TRUSTEE, + constants::NYM, + &ADD_AUTH_ACTION, + FIELD, + None, + NEW_VALUE, + ROLE_CONSTRAINT).unwrap(); + check_request(&request, expected_result); + } + + #[test] + fn indy_build_auth_rule_request_works_for_edit_action() { + let expected_result = json!({ + "type": constants::AUTH_RULE, + "auth_type": constants::NYM, + "field": FIELD, + "old_value": OLD_VALUE, + "new_value": NEW_VALUE, + "auth_action": EDIT_AUTH_ACTION, + "constraint": json!({ + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": false + }), + }); + + let request = ledger::build_auth_rule_request(DID_TRUSTEE, + constants::NYM, + &EDIT_AUTH_ACTION, + FIELD, + Some(OLD_VALUE), + NEW_VALUE, + ROLE_CONSTRAINT).unwrap(); + check_request(&request, expected_result); + } + + + #[test] + fn indy_build_auth_rule_request_works_for_complex_constraint() { + let constraint = r#"{ + "constraint_id": "AND", + "auth_constraints": [ + { + "constraint_id": "ROLE", + "role": "0", + "sig_count": 1, + "need_to_be_owner": false, + "metadata": {} + }, + { + "constraint_id": "OR", + "auth_constraints": [ + { + "constraint_id": "ROLE", + "role": "0", + "sig_count": 1, + "need_to_be_owner": false, + "metadata": {} + }, + { + "constraint_id": "ROLE", + "role": "0", + "sig_count": 1 + } + ] + } + ] + }"#; + + let expected_result = json!({ + "type": constants::AUTH_RULE, + "auth_type": constants::NYM, + "field": FIELD, + "new_value": NEW_VALUE, + "auth_action": ADD_AUTH_ACTION, + "constraint": serde_json::from_str::(constraint).unwrap(), + }); + + let request = ledger::build_auth_rule_request(DID_TRUSTEE, + constants::NYM, + &ADD_AUTH_ACTION, + FIELD, + None, + NEW_VALUE, + constraint).unwrap(); + check_request(&request, expected_result); + } + + #[test] + fn indy_build_auth_rule_request_works_for_invalid_constraint() { + let res = ledger::build_auth_rule_request(DID_TRUSTEE, + constants::NYM, + &ADD_AUTH_ACTION, + FIELD, + None, + NEW_VALUE, + r#"{"field":"value"}"#); + assert_code!(ErrorCode::CommonInvalidStructure, res); + } + + #[test] + #[cfg(feature = "local_nodes_pool")] + fn indy_auth_rule_request_works() { + let (wallet_handle, pool_handle, trustee_did) = utils::setup_trustee(); + + let auth_rule_request = ledger::build_auth_rule_request(&trustee_did, + constants::NYM, + &ADD_AUTH_ACTION, + FIELD, + None, + NEW_VALUE, + ROLE_CONSTRAINT).unwrap(); + let response = ledger::sign_and_submit_request(pool_handle, wallet_handle, &trustee_did, &auth_rule_request).unwrap(); + pool::check_response_type(&response, ResponseType::REPLY); + + utils::tear_down_with_wallet_and_pool(wallet_handle, pool_handle); + } + } } mod medium_cases { @@ -2200,7 +2354,7 @@ mod medium_cases { pool::check_response_type(&cred_def_response, ::utils::types::ResponseType::REPLY); let get_cred_def_request = ledger::build_get_cred_def_request(Some(DID_MY1), &cred_def_id).unwrap(); - let get_cred_def_response = ledger::submit_request(pool_handle, &get_cred_def_request).unwrap(); + let get_cred_def_response = ledger::submit_request_with_retries(pool_handle, &get_cred_def_request, &cred_def_response).unwrap(); let (_, cred_def_json) = ledger::parse_get_cred_def_response(&get_cred_def_response).unwrap(); let _cred_def: CredentialDefinitionV1 = serde_json::from_str(&cred_def_json).unwrap(); diff --git a/libindy/tests/utils/ledger.rs b/libindy/tests/utils/ledger.rs index b0db88179d..b3d1b764f4 100644 --- a/libindy/tests/utils/ledger.rs +++ b/libindy/tests/utils/ledger.rs @@ -187,10 +187,10 @@ pub fn register_transaction_parser_for_sp(txn_type: &str, parse: CustomTransacti let err = unsafe { indy_register_transaction_parser_for_sp(command_handle, - txn_type.as_ptr(), - Some(parse), - Some(free), - cb) + txn_type.as_ptr(), + Some(parse), + Some(free), + cb) }; super::results::result_to_empty(err, receiver) @@ -200,6 +200,16 @@ pub fn get_response_metadata(response: &str) -> Result { ledger::get_response_metadata(response).wait() } +pub fn build_auth_rule_request(submitter_did: &str, + txn_type: &str, + action: &str, + field: &str, + old_value: Option<&str>, + new_value: &str, + constraint: &str, ) -> Result { + ledger::build_auth_rule_request(submitter_did, txn_type, action, field, old_value, new_value, constraint).wait() +} + pub fn post_entities() -> (&'static str, &'static str, &'static str) { lazy_static! { static ref COMMON_ENTITIES_INIT: Once = ONCE_INIT; diff --git a/samples/python/src/getting_started.py b/samples/python/src/getting_started.py index acd263dd88..6f2956247b 100644 --- a/samples/python/src/getting_started.py +++ b/samples/python/src/getting_started.py @@ -1,6 +1,6 @@ import time -from indy import anoncreds, crypto, did, ledger, pool, wallet +from indy import anoncreds, crypto, did, ledger, pool, wallet, blob_storage import json import logging @@ -8,6 +8,7 @@ import argparse import sys from ctypes import * +from os.path import dirname from indy.error import ErrorCode, IndyError @@ -240,7 +241,7 @@ async def run(): job_certificate_cred_def = { 'tag': 'TAG1', 'type': 'CL', - 'config': {"support_revocation": False} + 'config': {"support_revocation": True} } (acme['job_certificate_cred_def_id'], acme['job_certificate_cred_def']) = \ await anoncreds.issuer_create_and_store_credential_def(acme['wallet'], acme['did'], @@ -252,6 +253,26 @@ async def run(): logger.info("\"Acme\" -> Send \"Acme Job-Certificate\" Credential Definition to Ledger") await send_cred_def(acme['pool'], acme['wallet'], acme['did'], acme['job_certificate_cred_def']) + logger.info("\"Acme\" -> Creates Revocation Registry") + acme['tails_writer_config'] = json.dumps({'base_dir': "/tmp/indy_acme_tails", 'uri_pattern': ''}) + tails_writer = await blob_storage.open_writer('default', acme['tails_writer_config']) + (acme['revoc_reg_id'], acme['revoc_reg_def'], acme['revoc_reg_entry']) = \ + await anoncreds.issuer_create_and_store_revoc_reg(acme['wallet'], acme['did'], 'CL_ACCUM', 'TAG1', + acme['job_certificate_cred_def_id'], + json.dumps({'max_cred_num': 5, + 'issuance_type': 'ISSUANCE_ON_DEMAND'}), + tails_writer) + + logger.info("\"Acme\" -> Post Revocation Registry Definition to Ledger") + acme['revoc_reg_def_request'] = await ledger.build_revoc_reg_def_request(acme['did'], acme['revoc_reg_def']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_def_request']) + + logger.info("\"Acme\" -> Post Revocation Registry Entry to Ledger") + acme['revoc_reg_entry_request'] = \ + await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM', + acme['revoc_reg_entry']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_request']) + logger.info("==============================") logger.info("=== Getting Transcript with Faber ==") logger.info("==============================") @@ -276,12 +297,12 @@ async def run(): await anoncreds.issuer_create_credential_offer(faber['wallet'], faber['transcript_cred_def_id']) logger.info("\"Faber\" -> Get key for Alice did") - faber['alic_key_for_faber'] = \ + faber['alice_key_for_faber'] = \ await did.key_for_did(faber['pool'], faber['wallet'], faber['alice_connection_response']['did']) logger.info("\"Faber\" -> Authcrypt \"Transcript\" Credential Offer for Alice") faber['authcrypted_transcript_cred_offer'] = \ - await crypto.auth_crypt(faber['wallet'], faber['key_for_alice'], faber['alic_key_for_faber'], + await crypto.auth_crypt(faber['wallet'], faber['key_for_alice'], faber['alice_key_for_faber'], faber['transcript_cred_offer'].encode('utf-8')) logger.info("\"Faber\" -> Send authcrypted \"Transcript\" Credential Offer to Alice") @@ -298,7 +319,7 @@ async def run(): logger.info("\"Alice\" -> Get \"Faber Transcript\" Credential Definition from Ledger") (alice['faber_transcript_cred_def_id'], alice['faber_transcript_cred_def']) = \ - await get_cred_def(alice['pool'], alice['did_for_faber'], authdecrypted_transcript_cred_offer['cred_def_id']) + await get_cred_def(alice['pool'], alice['did_for_faber'], alice['transcript_cred_def_id']) logger.info("\"Alice\" -> Create \"Transcript\" Credential Request for Faber") (alice['transcript_cred_request'], alice['transcript_cred_request_metadata']) = \ @@ -312,7 +333,14 @@ async def run(): alice['transcript_cred_request'].encode('utf-8')) logger.info("\"Alice\" -> Send authcrypted \"Transcript\" Credential Request to Faber") - alice['transcript_cred_values'] = json.dumps({ + faber['authcrypted_transcript_cred_request'] = alice['authcrypted_transcript_cred_request'] + + logger.info("\"Faber\" -> Authdecrypt \"Transcript\" Credential Request from Alice") + faber['alice_key_for_faber'], faber['transcript_cred_request'], _ = \ + await auth_decrypt(faber['wallet'], faber['key_for_alice'], faber['authcrypted_transcript_cred_request']) + + logger.info("\"Faber\" -> Create \"Transcript\" Credential for Alice") + faber['alice_transcript_cred_values'] = json.dumps({ "first_name": {"raw": "Alice", "encoded": "1139481716457488690172217916278103335"}, "last_name": {"raw": "Garcia", "encoded": "5321642780241790123587902456789123452"}, "degree": {"raw": "Bachelor of Science, Marketing", "encoded": "12434523576212321"}, @@ -321,15 +349,6 @@ async def run(): "year": {"raw": "2015", "encoded": "2015"}, "average": {"raw": "5", "encoded": "5"} }) - faber['authcrypted_transcript_cred_request'] = alice['authcrypted_transcript_cred_request'] - faber['alice_transcript_cred_values'] = alice['transcript_cred_values'] - - logger.info("\"Faber\" -> Authdecrypt \"Transcript\" Credential Request from Alice") - faber['alice_key_for_faber'], faber['transcript_cred_request'], _ = \ - await auth_decrypt(faber['wallet'], faber['key_for_alice'], faber['authcrypted_transcript_cred_request']) - - logger.info("\"Faber\" -> Create \"Transcript\" Credential for Alice") - faber['transcript_cred'], _, _ = \ await anoncreds.issuer_create_credential(faber['wallet'], faber['transcript_cred_offer'], faber['transcript_cred_request'], @@ -444,7 +463,8 @@ async def run(): cred_for_attr5['referent']: cred_for_attr5, cred_for_predicate1['referent']: cred_for_predicate1} - alice['schemas'], alice['cred_defs'], alice['revoc_states'] = \ + alice['schemas_for_job_application'], alice['cred_defs_for_job_application'], \ + alice['revoc_states_for_job_application'] = \ await prover_get_entities_from_ledger(alice['pool'], alice['did_for_acme'], alice['creds_for_job_application_proof'], alice['name']) @@ -466,7 +486,9 @@ async def run(): alice['job_application_proof'] = \ await anoncreds.prover_create_proof(alice['wallet'], alice['job_application_proof_request'], alice['job_application_requested_creds'], alice['master_secret_id'], - alice['schemas'], alice['cred_defs'], alice['revoc_states']) + alice['schemas_for_job_application'], + alice['cred_defs_for_job_application'], + alice['revoc_states_for_job_application']) logger.info("\"Alice\" -> Authcrypt \"Job-Application\" Proof for Acme") alice['authcrypted_job_application_proof'] = \ @@ -480,7 +502,8 @@ async def run(): _, acme['job_application_proof'], decrypted_job_application_proof = \ await auth_decrypt(acme['wallet'], acme['key_for_alice'], acme['authcrypted_job_application_proof']) - acme['schemas'], acme['cred_defs'], acme['revoc_ref_defs'], acme['revoc_regs'] = \ + acme['schemas_for_job_application'], acme['cred_defs_for_job_application'], \ + acme['revoc_ref_defs_for_job_application'], acme['revoc_regs_for_job_application'] = \ await verifier_get_entities_from_ledger(acme['pool'], acme['did'], decrypted_job_application_proof['identifiers'], acme['name']) @@ -497,8 +520,10 @@ async def run(): assert '123-45-6789' == decrypted_job_application_proof['requested_proof']['self_attested_attrs']['attr6_referent'] assert await anoncreds.verifier_verify_proof(acme['job_application_proof_request'], acme['job_application_proof'], - acme['schemas'], acme['cred_defs'], acme['revoc_ref_defs'], - acme['revoc_regs']) + acme['schemas_for_job_application'], + acme['cred_defs_for_job_application'], + acme['revoc_ref_defs_for_job_application'], + acme['revoc_regs_for_job_application']) logger.info("==============================") logger.info("== Apply for the job with Acme - Getting Job-Certificate Credential ==") @@ -555,11 +580,19 @@ async def run(): await auth_decrypt(acme['wallet'], acme['key_for_alice'], acme['authcrypted_job_certificate_cred_request']) logger.info("\"Acme\" -> Create \"Job-Certificate\" Credential for Alice") - - acme['job_certificate_cred'], _, _ = \ + acme['blob_storage_reader_cfg_handle'] = await blob_storage.open_reader('default', acme['tails_writer_config']) + acme['job_certificate_cred'], acme['job_certificate_cred_rev_id'], acme['alice_cert_rev_reg_delta'] = \ await anoncreds.issuer_create_credential(acme['wallet'], acme['job_certificate_cred_offer'], acme['job_certificate_cred_request'], - acme['job_certificate_cred_values'], None, None) + acme['job_certificate_cred_values'], + acme['revoc_reg_id'], + acme['blob_storage_reader_cfg_handle']) + + logger.info("\"Acme\" -> Post Revocation Registry Delta to Ledger") + acme['revoc_reg_entry_req'] = \ + await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM', + acme['alice_cert_rev_reg_delta']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_req']) logger.info("\"Acme\" -> Authcrypt \"Job-Certificate\" Credential for Alice") acme['authcrypted_job_certificate_cred'] = \ @@ -570,13 +603,21 @@ async def run(): alice['authcrypted_job_certificate_cred'] = acme['authcrypted_job_certificate_cred'] logger.info("\"Alice\" -> Authdecrypted \"Job-Certificate\" Credential from Acme") - _, alice['job_certificate_cred'], _ = \ + _, alice['job_certificate_cred'], alice_job_certificate_cred = \ await auth_decrypt(alice['wallet'], alice['key_for_acme'], alice['authcrypted_job_certificate_cred']) + logger.info("\"Alice\" -> Gets RevocationRegistryDefinition for \"Job-Certificate\" Credential from Acme") + alice['acme_revoc_reg_des_req'] = \ + await ledger.build_get_revoc_reg_def_request(alice['did_for_acme'], + alice_job_certificate_cred['rev_reg_id']) + alice['acme_revoc_reg_des_resp'] = await ledger.submit_request(alice['pool'], alice['acme_revoc_reg_des_req']) + (alice['acme_revoc_reg_def_id'], alice['acme_revoc_reg_def_json']) = \ + await ledger.parse_get_revoc_reg_def_response(alice['acme_revoc_reg_des_resp']) + logger.info("\"Alice\" -> Store \"Job-Certificate\" Credential") await anoncreds.prover_store_credential(alice['wallet'], None, alice['job_certificate_cred_request_metadata'], alice['job_certificate_cred'], - alice['acme_job_certificate_cred_def'], None) + alice['acme_job_certificate_cred_def'], alice['acme_revoc_reg_def_json']) logger.info("==============================") logger.info("=== Apply for the loan with Thrift ==") @@ -587,118 +628,133 @@ async def run(): thrift['did_for_alice'], thrift['key_for_alice'], alice['did_for_thrift'], alice['key_for_thrift'], \ thrift['alice_connection_response'] = await onboarding(thrift, alice) - logger.info("==============================") - logger.info("== Apply for the loan with Thrift - Job-Certificate proving ==") - logger.info("------------------------------") - - logger.info("\"Thrift\" -> Create \"Loan-Application-Basic\" Proof Request") - thrift['apply_loan_proof_request'] = json.dumps({ - 'nonce': '123432421212', - 'name': 'Loan-Application-Basic', - 'version': '0.1', - 'requested_attributes': { - 'attr1_referent': { - 'name': 'employee_status', - 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] - } - }, - 'requested_predicates': { - 'predicate1_referent': { - 'name': 'salary', - 'p_type': '>=', - 'p_value': 2000, - 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] + async def apply_loan_basic(): + # This method will be called twice: once with a valid Job-Certificate and + # the second time after the Job-Certificate has been revoked. + logger.info("==============================") + logger.info("== Apply for the loan with Thrift - Job-Certificate proving ==") + logger.info("------------------------------") + + logger.info("\"Thrift\" -> Create \"Loan-Application-Basic\" Proof Request") + thrift['apply_loan_proof_request'] = json.dumps({ + 'nonce': '123432421212', + 'name': 'Loan-Application-Basic', + 'version': '0.1', + 'requested_attributes': { + 'attr1_referent': { + 'name': 'employee_status', + 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] + } }, - 'predicate2_referent': { - 'name': 'experience', - 'p_type': '>=', - 'p_value': 1, - 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] + 'requested_predicates': { + 'predicate1_referent': { + 'name': 'salary', + 'p_type': '>=', + 'p_value': 2000, + 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] + }, + 'predicate2_referent': { + 'name': 'experience', + 'p_type': '>=', + 'p_value': 1, + 'restrictions': [{'cred_def_id': acme['job_certificate_cred_def_id']}] + } + }, + 'non_revoked': {'to': int(time.time())} + }) + + logger.info("\"Thrift\" -> Get key for Alice did") + thrift['alice_key_for_thrift'] = \ + await did.key_for_did(thrift['pool'], thrift['wallet'], thrift['alice_connection_response']['did']) + + logger.info("\"Thrift\" -> Authcrypt \"Loan-Application-Basic\" Proof Request for Alice") + thrift['authcrypted_apply_loan_proof_request'] = \ + await crypto.auth_crypt(thrift['wallet'], thrift['key_for_alice'], thrift['alice_key_for_thrift'], + thrift['apply_loan_proof_request'].encode('utf-8')) + + logger.info("\"Thrift\" -> Send authcrypted \"Loan-Application-Basic\" Proof Request to Alice") + alice['authcrypted_apply_loan_proof_request'] = thrift['authcrypted_apply_loan_proof_request'] + + logger.info("\"Alice\" -> Authdecrypt \"Loan-Application-Basic\" Proof Request from Thrift") + alice['thrift_key_for_alice'], alice['apply_loan_proof_request'], _ = \ + await auth_decrypt(alice['wallet'], alice['key_for_thrift'], alice['authcrypted_apply_loan_proof_request']) + + logger.info("\"Alice\" -> Get credentials for \"Loan-Application-Basic\" Proof Request") + + search_for_apply_loan_proof_request = \ + await anoncreds.prover_search_credentials_for_proof_req(alice['wallet'], + alice['apply_loan_proof_request'], None) + + cred_for_attr1 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'attr1_referent') + cred_for_predicate1 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'predicate1_referent') + cred_for_predicate2 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'predicate2_referent') + + await anoncreds.prover_close_credentials_search_for_proof_req(search_for_apply_loan_proof_request) + + alice['creds_for_apply_loan_proof'] = {cred_for_attr1['referent']: cred_for_attr1, + cred_for_predicate1['referent']: cred_for_predicate1, + cred_for_predicate2['referent']: cred_for_predicate2} + + requested_timestamp = int(json.loads(thrift['apply_loan_proof_request'])['non_revoked']['to']) + alice['schemas_for_loan_app'], alice['cred_defs_for_loan_app'], alice['revoc_states_for_loan_app'] = \ + await prover_get_entities_from_ledger(alice['pool'], alice['did_for_thrift'], + alice['creds_for_apply_loan_proof'], + alice['name'], None, requested_timestamp) + + logger.info("\"Alice\" -> Create \"Loan-Application-Basic\" Proof") + revoc_states_for_loan_app = json.loads(alice['revoc_states_for_loan_app']) + timestamp_for_attr1 = get_timestamp_for_attribute(cred_for_attr1, revoc_states_for_loan_app) + timestamp_for_predicate1 = get_timestamp_for_attribute(cred_for_predicate1, revoc_states_for_loan_app) + timestamp_for_predicate2 = get_timestamp_for_attribute(cred_for_predicate2, revoc_states_for_loan_app) + alice['apply_loan_requested_creds'] = json.dumps({ + 'self_attested_attributes': {}, + 'requested_attributes': { + 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True, 'timestamp': timestamp_for_attr1} + }, + 'requested_predicates': { + 'predicate1_referent': {'cred_id': cred_for_predicate1['referent'], 'timestamp': timestamp_for_predicate1}, + 'predicate2_referent': {'cred_id': cred_for_predicate2['referent'], 'timestamp': timestamp_for_predicate2} } - } - }) - - logger.info("\"Thrift\" -> Get key for Alice did") - thrift['alice_key_for_thrift'] = \ - await did.key_for_did(thrift['pool'], thrift['wallet'], thrift['alice_connection_response']['did']) - - logger.info("\"Thrift\" -> Authcrypt \"Loan-Application-Basic\" Proof Request for Alice") - thrift['authcrypted_apply_loan_proof_request'] = \ - await crypto.auth_crypt(thrift['wallet'], thrift['key_for_alice'], thrift['alice_key_for_thrift'], - thrift['apply_loan_proof_request'].encode('utf-8')) - - logger.info("\"Thrift\" -> Send authcrypted \"Loan-Application-Basic\" Proof Request to Alice") - alice['authcrypted_apply_loan_proof_request'] = thrift['authcrypted_apply_loan_proof_request'] - - logger.info("\"Alice\" -> Authdecrypt \"Loan-Application-Basic\" Proof Request from Thrift") - alice['thrift_key_for_alice'], alice['apply_loan_proof_request'], _ = \ - await auth_decrypt(alice['wallet'], alice['key_for_thrift'], alice['authcrypted_apply_loan_proof_request']) - - logger.info("\"Alice\" -> Get credentials for \"Loan-Application-Basic\" Proof Request") - - search_for_apply_loan_proof_request = \ - await anoncreds.prover_search_credentials_for_proof_req(alice['wallet'], - alice['apply_loan_proof_request'], None) - - cred_for_attr1 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'attr1_referent') - cred_for_predicate1 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'predicate1_referent') - cred_for_predicate2 = await get_credential_for_referent(search_for_apply_loan_proof_request, 'predicate2_referent') - - await anoncreds.prover_close_credentials_search_for_proof_req(search_for_apply_loan_proof_request) - - alice['creds_for_apply_loan_proof'] = {cred_for_attr1['referent']: cred_for_attr1, - cred_for_predicate1['referent']: cred_for_predicate1, - cred_for_predicate2['referent']: cred_for_predicate2} - - alice['schemas'], alice['cred_defs'], alice['revoc_states'] = \ - await prover_get_entities_from_ledger(alice['pool'], alice['did_for_thrift'], - alice['creds_for_apply_loan_proof'], - alice['name']) - - logger.info("\"Alice\" -> Create \"Loan-Application-Basic\" Proof") - alice['apply_loan_requested_creds'] = json.dumps({ - 'self_attested_attributes': {}, - 'requested_attributes': { - 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True} - }, - 'requested_predicates': { - 'predicate1_referent': {'cred_id': cred_for_predicate1['referent']}, - 'predicate2_referent': {'cred_id': cred_for_predicate2['referent']} - } - }) - alice['apply_loan_proof'] = \ - await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_proof_request'], - alice['apply_loan_requested_creds'], alice['master_secret_id'], - alice['schemas'], alice['cred_defs'], alice['revoc_states']) - - logger.info("\"Alice\" -> Authcrypt \"Loan-Application-Basic\" Proof for Thrift") - alice['authcrypted_alice_apply_loan_proof'] = \ - await crypto.auth_crypt(alice['wallet'], alice['key_for_thrift'], alice['thrift_key_for_alice'], - alice['apply_loan_proof'].encode('utf-8')) - - logger.info("\"Alice\" -> Send authcrypted \"Loan-Application-Basic\" Proof to Thrift") - thrift['authcrypted_alice_apply_loan_proof'] = alice['authcrypted_alice_apply_loan_proof'] - - logger.info("\"Thrift\" -> Authdecrypted \"Loan-Application-Basic\" Proof from Alice") - _, thrift['alice_apply_loan_proof'], authdecrypted_alice_apply_loan_proof = \ - await auth_decrypt(thrift['wallet'], thrift['key_for_alice'], thrift['authcrypted_alice_apply_loan_proof']) - - logger.info("\"Thrift\" -> Get Schemas, Credential Definitions and Revocation Registries from Ledger" - " required for Proof verifying") - - thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], thrift['revoc_regs'] = \ - await verifier_get_entities_from_ledger(thrift['pool'], thrift['did'], - authdecrypted_alice_apply_loan_proof['identifiers'], thrift['name']) - - logger.info("\"Thrift\" -> Verify \"Loan-Application-Basic\" Proof from Alice") - assert 'Permanent' == \ - authdecrypted_alice_apply_loan_proof['requested_proof']['revealed_attrs']['attr1_referent']['raw'] - - assert await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'], thrift['alice_apply_loan_proof'], - thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], - thrift['revoc_regs']) - - logger.info("==============================") + }) + alice['apply_loan_proof'] = \ + await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_proof_request'], + alice['apply_loan_requested_creds'], alice['master_secret_id'], + alice['schemas_for_loan_app'], alice['cred_defs_for_loan_app'], + alice['revoc_states_for_loan_app']) + + logger.info("\"Alice\" -> Authcrypt \"Loan-Application-Basic\" Proof for Thrift") + alice['authcrypted_alice_apply_loan_proof'] = \ + await crypto.auth_crypt(alice['wallet'], alice['key_for_thrift'], alice['thrift_key_for_alice'], + alice['apply_loan_proof'].encode('utf-8')) + + logger.info("\"Alice\" -> Send authcrypted \"Loan-Application-Basic\" Proof to Thrift") + thrift['authcrypted_alice_apply_loan_proof'] = alice['authcrypted_alice_apply_loan_proof'] + + logger.info("\"Thrift\" -> Authdecrypted \"Loan-Application-Basic\" Proof from Alice") + _, thrift['alice_apply_loan_proof'], authdecrypted_alice_apply_loan_proof = \ + await auth_decrypt(thrift['wallet'], thrift['key_for_alice'], thrift['authcrypted_alice_apply_loan_proof']) + + logger.info("\"Thrift\" -> Get Schemas, Credential Definitions and Revocation Registries from Ledger" + " required for Proof verifying") + + thrift['schemas_for_loan_app'], thrift['cred_defs_for_loan_app'], thrift['revoc_defs_for_loan_app'], \ + thrift['revoc_regs_for_loan_app'] = \ + await verifier_get_entities_from_ledger(thrift['pool'], thrift['did'], + authdecrypted_alice_apply_loan_proof['identifiers'], + thrift['name'], requested_timestamp) + + logger.info("\"Thrift\" -> Verify \"Loan-Application-Basic\" Proof from Alice") + assert 'Permanent' == \ + authdecrypted_alice_apply_loan_proof['requested_proof']['revealed_attrs']['attr1_referent']['raw'] + + await apply_loan_basic() + + assert await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'], + thrift['alice_apply_loan_proof'], + thrift['schemas_for_loan_app'], + thrift['cred_defs_for_loan_app'], + thrift['revoc_defs_for_loan_app'], + thrift['revoc_regs_for_loan_app']) logger.info("==============================") logger.info("== Apply for the loan with Thrift - Transcript and Job-Certificate proving ==") @@ -749,18 +805,21 @@ async def run(): cred_for_attr2['referent']: cred_for_attr2, cred_for_attr3['referent']: cred_for_attr3} - alice['schemas'], alice['cred_defs'], alice['revoc_states'] = \ + alice['schemas_for_loan_kyc_app'], alice['cred_defs_for_loan_kyc_app'], alice['revoc_states_for_loan_kyc_app'] = \ await prover_get_entities_from_ledger(alice['pool'], alice['did_for_thrift'], - alice['creds_for_apply_loan_kyc_proof'], 'Alice') + alice['creds_for_apply_loan_kyc_proof'], alice['name'],) logger.info("\"Alice\" -> Create \"Loan-Application-KYC\" Proof") - + revoc_states_for_loan_app = json.loads(alice['revoc_states_for_loan_kyc_app']) + timestamp_for_attr1 = get_timestamp_for_attribute(cred_for_attr1, revoc_states_for_loan_app) + timestamp_for_attr2 = get_timestamp_for_attribute(cred_for_attr2, revoc_states_for_loan_app) + timestamp_for_attr3 = get_timestamp_for_attribute(cred_for_attr3, revoc_states_for_loan_app) alice['apply_loan_kyc_requested_creds'] = json.dumps({ 'self_attested_attributes': {}, 'requested_attributes': { - 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True}, - 'attr2_referent': {'cred_id': cred_for_attr2['referent'], 'revealed': True}, - 'attr3_referent': {'cred_id': cred_for_attr3['referent'], 'revealed': True} + 'attr1_referent': {'cred_id': cred_for_attr1['referent'], 'revealed': True, 'timestamp': timestamp_for_attr1}, + 'attr2_referent': {'cred_id': cred_for_attr2['referent'], 'revealed': True, 'timestamp': timestamp_for_attr2}, + 'attr3_referent': {'cred_id': cred_for_attr3['referent'], 'revealed': True, 'timestamp': timestamp_for_attr3} }, 'requested_predicates': {} }) @@ -768,7 +827,8 @@ async def run(): alice['apply_loan_kyc_proof'] = \ await anoncreds.prover_create_proof(alice['wallet'], alice['apply_loan_kyc_proof_request'], alice['apply_loan_kyc_requested_creds'], alice['master_secret_id'], - alice['schemas'], alice['cred_defs'], alice['revoc_states']) + alice['schemas_for_loan_kyc_app'], alice['cred_defs_for_loan_kyc_app'], + alice['revoc_states_for_loan_kyc_app']) logger.info("\"Alice\" -> Authcrypt \"Loan-Application-KYC\" Proof for Thrift") alice['authcrypted_alice_apply_loan_kyc_proof'] = \ @@ -785,9 +845,10 @@ async def run(): logger.info("\"Thrift\" -> Get Schemas, Credential Definitions and Revocation Registries from Ledger" " required for Proof verifying") - thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], thrift['revoc_regs'] = \ + thrift['schemas_for_loan_kyc_app'], thrift['cred_defs_for_loan_kyc_app'], thrift['revoc_defs_for_loan_kyc_app'], \ + thrift['revoc_regs_for_loan_kyc_app'] = \ await verifier_get_entities_from_ledger(thrift['pool'], thrift['did'], - alice_apply_loan_kyc_proof['identifiers'], 'Thrift') + alice_apply_loan_kyc_proof['identifiers'], thrift['name']) logger.info("\"Thrift\" -> Verify \"Loan-Application-KYC\" Proof from Alice") assert 'Alice' == \ @@ -799,8 +860,43 @@ async def run(): assert await anoncreds.verifier_verify_proof(thrift['apply_loan_kyc_proof_request'], thrift['alice_apply_loan_kyc_proof'], - thrift['schemas'], thrift['cred_defs'], thrift['revoc_defs'], - thrift['revoc_regs']) + thrift['schemas_for_loan_kyc_app'], thrift['cred_defs_for_loan_kyc_app'], + thrift['revoc_defs_for_loan_kyc_app'], + thrift['revoc_regs_for_loan_kyc_app']) + + logger.info("==============================") + + logger.info("==============================") + logger.info("== Credential revocation - Acme revokes Alice's Job-Certificate ==") + logger.info("------------------------------") + + logger.info("\"Acme\" - Revoke credential") + acme['alice_cert_rev_reg_delta'] = \ + await anoncreds.issuer_revoke_credential(acme['wallet'], + acme['blob_storage_reader_cfg_handle'], + acme['revoc_reg_id'], + acme['job_certificate_cred_rev_id']) + + logger.info("\"Acme\" - Post RevocationRegistryDelta to Ledger") + acme['revoc_reg_entry_req'] = \ + await ledger.build_revoc_reg_entry_request(acme['did'], acme['revoc_reg_id'], 'CL_ACCUM', + acme['alice_cert_rev_reg_delta']) + await ledger.sign_and_submit_request(acme['pool'], acme['wallet'], acme['did'], acme['revoc_reg_entry_req']) + + logger.info("==============================") + + logger.info("==============================") + logger.info("== Apply for the loan with Thrift again - Job-Certificate proving ==") + logger.info("------------------------------") + + await apply_loan_basic() + + assert not await anoncreds.verifier_verify_proof(thrift['apply_loan_proof_request'], + thrift['alice_apply_loan_proof'], + thrift['schemas_for_loan_app'], + thrift['cred_defs_for_loan_app'], + thrift['revoc_defs_for_loan_app'], + thrift['revoc_regs_for_loan_app']) logger.info("==============================") @@ -890,6 +986,7 @@ async def onboarding(_from, to): return from_to_did, from_to_key, to_from_did, to_from_key, _from['connection_response'] + def wallet_config(operation, wallet_config_str): if not args.storage_type: return wallet_config_str @@ -900,6 +997,7 @@ def wallet_config(operation, wallet_config_str): #print(operation, json.dumps(wallet_config_json)) return json.dumps(wallet_config_json) + def wallet_credentials(operation, wallet_credentials_str): if not args.storage_type: return wallet_credentials_str @@ -909,6 +1007,7 @@ def wallet_credentials(operation, wallet_credentials_str): #print(operation, json.dumps(wallet_credentials_json)) return json.dumps(wallet_credentials_json) + async def get_verinym(_from, from_to_did, from_to_key, to, to_from_did, to_from_key): logger.info("\"{}\" -> Create and store in Wallet \"{}\" new DID".format(to['name'], to['name'])) (to_did, to_key) = await did.create_and_store_my_did(to['wallet'], "{}") @@ -971,7 +1070,14 @@ async def get_credential_for_referent(search_handle, referent): return credentials[0]['cred_info'] -async def prover_get_entities_from_ledger(pool_handle, _did, identifiers, actor): +def get_timestamp_for_attribute(cred_for_attribute, revoc_states): + if cred_for_attribute['rev_reg_id'] in revoc_states: + return int(next(iter(revoc_states[cred_for_attribute['rev_reg_id']]))) + else: + return None + + +async def prover_get_entities_from_ledger(pool_handle, _did, identifiers, actor, timestamp_from=None, timestamp_to=None): schemas = {} cred_defs = {} rev_states = {} @@ -984,13 +1090,38 @@ async def prover_get_entities_from_ledger(pool_handle, _did, identifiers, actor) (received_cred_def_id, received_cred_def) = await get_cred_def(pool_handle, _did, item['cred_def_id']) cred_defs[received_cred_def_id] = json.loads(received_cred_def) - if 'rev_reg_seq_no' in item: - pass # TODO Create Revocation States + if 'rev_reg_id' in item and item['rev_reg_id'] is not None: + # Create Revocations States + logger.info("\"{}\" -> Get Revocation Registry Definition from Ledger".format(actor)) + get_revoc_reg_def_request = await ledger.build_get_revoc_reg_def_request(_did, item['rev_reg_id']) + + get_revoc_reg_def_response = await ledger.submit_request(pool_handle, get_revoc_reg_def_request) + (rev_reg_id, revoc_reg_def_json) = await ledger.parse_get_revoc_reg_def_response(get_revoc_reg_def_response) + + logger.info("\"{}\" -> Get Revocation Registry Delta from Ledger".format(actor)) + if not timestamp_to: timestamp_to = int(time.time()) + get_revoc_reg_delta_request = \ + await ledger.build_get_revoc_reg_delta_request(_did, item['rev_reg_id'], timestamp_from, timestamp_to) + get_revoc_reg_delta_response = \ + await ledger.submit_request(pool_handle, get_revoc_reg_delta_request) + (rev_reg_id, revoc_reg_delta_json, t) = \ + await ledger.parse_get_revoc_reg_delta_response(get_revoc_reg_delta_response) + + tails_reader_config = json.dumps( + {'base_dir': dirname(json.loads(revoc_reg_def_json)['value']['tailsLocation']), + 'uri_pattern': ''}) + blob_storage_reader_cfg_handle = await blob_storage.open_reader('default', tails_reader_config) + + logger.info('%s - Create Revocation State', actor) + rev_state_json = \ + await anoncreds.create_revocation_state(blob_storage_reader_cfg_handle, revoc_reg_def_json, + revoc_reg_delta_json, t, item['cred_rev_id']) + rev_states[rev_reg_id] = {t: json.loads(rev_state_json)} return json.dumps(schemas), json.dumps(cred_defs), json.dumps(rev_states) -async def verifier_get_entities_from_ledger(pool_handle, _did, identifiers, actor): +async def verifier_get_entities_from_ledger(pool_handle, _did, identifiers, actor, timestamp=None): schemas = {} cred_defs = {} rev_reg_defs = {} @@ -1004,8 +1135,23 @@ async def verifier_get_entities_from_ledger(pool_handle, _did, identifiers, acto (received_cred_def_id, received_cred_def) = await get_cred_def(pool_handle, _did, item['cred_def_id']) cred_defs[received_cred_def_id] = json.loads(received_cred_def) - if 'rev_reg_seq_no' in item: - pass # TODO Get Revocation Definitions and Revocation Registries + if 'rev_reg_id' in item and item['rev_reg_id'] is not None: + # Get Revocation Definitions and Revocation Registries + logger.info("\"{}\" -> Get Revocation Definition from Ledger".format(actor)) + get_revoc_reg_def_request = await ledger.build_get_revoc_reg_def_request(_did, item['rev_reg_id']) + + get_revoc_reg_def_response = await ledger.submit_request(pool_handle, get_revoc_reg_def_request) + (rev_reg_id, revoc_reg_def_json) = await ledger.parse_get_revoc_reg_def_response(get_revoc_reg_def_response) + + logger.info("\"{}\" -> Get Revocation Registry from Ledger".format(actor)) + if not timestamp: timestamp = item['timestamp'] + get_revoc_reg_request = \ + await ledger.build_get_revoc_reg_request(_did, item['rev_reg_id'], timestamp) + get_revoc_reg_response = await ledger.submit_request(pool_handle, get_revoc_reg_request) + (rev_reg_id, rev_reg_json, timestamp2) = await ledger.parse_get_revoc_reg_response(get_revoc_reg_response) + + rev_regs[rev_reg_id] = {timestamp2: json.loads(rev_reg_json)} + rev_reg_defs[rev_reg_id] = json.loads(revoc_reg_def_json) return json.dumps(schemas), json.dumps(cred_defs), json.dumps(rev_reg_defs), json.dumps(rev_regs) diff --git a/vcx/ci/libindy.dockerfile b/vcx/ci/libindy.dockerfile index 8ec8fd273f..33d2df18da 100644 --- a/vcx/ci/libindy.dockerfile +++ b/vcx/ci/libindy.dockerfile @@ -41,7 +41,7 @@ RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - \ && apt-get install -y nodejs # Install Rust -ARG RUST_VER="1.31.0" +ARG RUST_VER="1.32.0" ENV RUST_ARCHIVE=rust-${RUST_VER}-x86_64-unknown-linux-gnu.tar.gz ENV RUST_DOWNLOAD_URL=https://static.rust-lang.org/dist/$RUST_ARCHIVE diff --git a/vcx/ci/libvcx.dockerfile b/vcx/ci/libvcx.dockerfile index 0e237d3809..9d913dfc2f 100644 --- a/vcx/ci/libvcx.dockerfile +++ b/vcx/ci/libvcx.dockerfile @@ -4,7 +4,7 @@ ARG uid=1000 RUN useradd -ms /bin/bash -u $uid vcx USER vcx -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.31.0 +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.32.0 ENV PATH /home/vcx/.cargo/bin:$PATH WORKDIR /home/vcx ENV PATH /home/vcx:$PATH diff --git a/vcx/libvcx/build_scripts/ios/mac/mac.01.libindy.setup.sh b/vcx/libvcx/build_scripts/ios/mac/mac.01.libindy.setup.sh index 80775593a2..e05d7df2e5 100755 --- a/vcx/libvcx/build_scripts/ios/mac/mac.01.libindy.setup.sh +++ b/vcx/libvcx/build_scripts/ios/mac/mac.01.libindy.setup.sh @@ -44,7 +44,7 @@ fi if [[ $RUSTUP_VERSION =~ ^'rustup ' ]]; then rustup update - rustup default 1.31.0 + rustup default 1.32.0 rustup component add rls-preview rust-analysis rust-src echo "Using rustc version $(rustc --version)" rustup target remove aarch64-linux-android armv7-linux-androideabi arm-linux-androideabi i686-linux-android x86_64-linux-android diff --git a/vcx/wrappers/ios/vcx/utils/NSError+VcxError.m b/vcx/wrappers/ios/vcx/utils/NSError+VcxError.m index 56095a76fb..9c41e81da4 100644 --- a/vcx/wrappers/ios/vcx/utils/NSError+VcxError.m +++ b/vcx/wrappers/ios/vcx/utils/NSError+VcxError.m @@ -4,6 +4,8 @@ // #import "NSError+VcxError.h" +#import "vcx.h" +#include "vcx.h" static NSString *const VcxErrorDomain = @"VcxErrorDomain"; @@ -13,23 +15,23 @@ + (NSError*) errorFromVcxError:(vcx_error_t) error { NSMutableDictionary *userInfo = [NSMutableDictionary new]; - // if (error != Success) { - // const char * error_json_p; - // indy_get_current_error(&error_json_p); - // - // NSString *errorDetailsJson = [NSString stringWithUTF8String:error_json_p]; - // - // NSError *error; - // NSDictionary *errorDetails = [NSJSONSerialization JSONObjectWithData:[NSData dataWithBytes:[errorDetailsJson UTF8String] - // length:[errorDetailsJson length]] - // options:kNilOptions - // error: &error]; - // - // [userInfo setValue:errorDetails[@"error"] forKey:@"sdk_message"]; - // [userInfo setValue:errorDetails[@"message"] forKey:@"sdk_full_message"]; - // [userInfo setValue:errorDetails[@"cause"] forKey:@"sdk_cause"]; - // [userInfo setValue:errorDetails[@"backtrace"] forKey:@"sdk_backtrace"]; - // } + if (error != Success) { + const char * error_json_p; + vcx_get_current_error(&error_json_p); + + NSString *errorDetailsJson = [NSString stringWithUTF8String:error_json_p]; + + NSError *error; + NSDictionary *errorDetails = [NSJSONSerialization JSONObjectWithData:[NSData dataWithBytes:[errorDetailsJson UTF8String] + length:[errorDetailsJson length]] + options:kNilOptions + error: &error]; + + [userInfo setValue:errorDetails[@"error"] forKey:@"sdk_message"]; + [userInfo setValue:errorDetails[@"message"] forKey:@"sdk_full_message"]; + [userInfo setValue:errorDetails[@"cause"] forKey:@"sdk_cause"]; + [userInfo setValue:errorDetails[@"backtrace"] forKey:@"sdk_backtrace"]; + } return [NSError errorWithDomain:VcxErrorDomain code: error userInfo:userInfo]; } diff --git a/vcx/wrappers/ios/vcx/vcx.framework/Headers/libvcx.h b/vcx/wrappers/ios/vcx/vcx.framework/Headers/libvcx.h index c4c44abc77..9774400399 100644 --- a/vcx/wrappers/ios/vcx/vcx.framework/Headers/libvcx.h +++ b/vcx/wrappers/ios/vcx/vcx.framework/Headers/libvcx.h @@ -68,7 +68,7 @@ vcx_error_t vcx_update_agent_info(vcx_command_handle_t handle, const char *info, const char *vcx_error_c_message(int); const char *vcx_version(); -vcx_error_t vcx_current_error(const char ** error_json_p); +vcx_error_t vcx_get_current_error(const char ** error_json_p); /** * Schema object diff --git a/vcx/wrappers/java/ci/android.dockerfile b/vcx/wrappers/java/ci/android.dockerfile index b4d25ad42c..f05d043939 100644 --- a/vcx/wrappers/java/ci/android.dockerfile +++ b/vcx/wrappers/java/ci/android.dockerfile @@ -21,5 +21,5 @@ RUN yes | .//home/android/android-sdk-linux/tools/bin/sdkmanager "ndk-bundle" RUN echo "android ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers USER android -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.31.0 +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.32.0 ENV PATH /home/android/.cargo/bin:$PATH diff --git a/vcx/wrappers/node/src/vcx.ts b/vcx/wrappers/node/src/vcx.ts index 55e53959ac..8f9aa39fce 100644 --- a/vcx/wrappers/node/src/vcx.ts +++ b/vcx/wrappers/node/src/vcx.ts @@ -1,4 +1,5 @@ import * as ffi from 'ffi' +import * as os from 'os' import * as path from 'path' import { FFIConfiguration, IFFIEntryPoint } from './rustlib' @@ -12,20 +13,28 @@ export interface IVCXRuntimeConfig { // VCXRuntimeConfg is a class that currently only contains a chosen basepath for the .so file // I made it a class just in case we think of more needed configs +const extension = {"darwin": ".dylib", "linux": ".so", "win32": ".dll"} +const libPath = {"darwin": "/usr/local/lib/", "linux": '/usr/lib/', "win32": 'c:\\windows\\system32\\'} + export class VCXRuntime { public readonly ffi: IFFIEntryPoint private _config: IVCXRuntimeConfig constructor (config: IVCXRuntimeConfig = {}) { this._config = config - // initialize FFI + // initialize FFI const libraryPath = this._initializeBasepath() this.ffi = ffi.Library(libraryPath, FFIConfiguration) } private _initializeBasepath = (): string => { - const library = 'libvcx.so' // TODO: FIXME provide better way to resolve library + const platform = os.platform() + // @ts-ignore + const postfix = extension[platform.toLowerCase()] || extension['linux'] + // @ts-ignore + const libDir = libPath[platform.toLowerCase()] || libPath['linux'] + const library = `libvcx${postfix}` const customPath = process.env.LIBVCX_PATH ? process.env.LIBVCX_PATH + library : undefined - return customPath || this._config.basepath || path.resolve(__dirname, '/usr/lib/' + library) + return customPath || this._config.basepath || path.resolve(__dirname, `${libDir}${library}`) } } diff --git a/wrappers/ios/libindy-pod/Indy-demoTests/Case Tests/Ledger/LedgerBuildRequest.mm b/wrappers/ios/libindy-pod/Indy-demoTests/Case Tests/Ledger/LedgerBuildRequest.mm index 690bc320aa..0718ff437a 100644 --- a/wrappers/ios/libindy-pod/Indy-demoTests/Case Tests/Ledger/LedgerBuildRequest.mm +++ b/wrappers/ios/libindy-pod/Indy-demoTests/Case Tests/Ledger/LedgerBuildRequest.mm @@ -641,4 +641,42 @@ - (void)testBuildGetTxnRequest { XCTAssertTrue([getTxnRequest contains:expectedResult], @"getTxnRequest json doesn't contain expectedResult json"); } +// MARK: Auth Rule request + +- (void)testBuildAuthRuleRequestsWorks { + NSDictionary *constraint = @{ + @"sig_count": @(1), + @"role": @"0", + @"constraint_id": @"ROLE", + @"need_to_be_owner": @(false) + }; + + NSDictionary *expectedResult = @{ + @"identifier": [TestUtils trusteeDid], + @"operation": @{ + @"type": @"120", + @"auth_type": @"1", + @"auth_action": @"ADD", + @"field": @"role", + @"new_value": @"101", + @"constraint": constraint, + } + }; + + NSString *requestJson; + ret = [[LedgerUtils sharedInstance] buildAuthRuleRequestWithSubmitterDid:[TestUtils trusteeDid] + txnType:@"NYM" + action:@"ADD" + field:@"role" + oldValue:nil + newValue:@"101" + constraint:[NSDictionary toString:constraint] + outRequest:&requestJson]; + XCTAssertEqual(ret.code, Success, @"LedgerUtils::buildNymRequestWithSubmitterDid() failed!"); + + NSDictionary *request = [NSDictionary fromString:requestJson]; + + XCTAssertTrue([request contains:expectedResult], @"Request doesn't contain expectedResult"); +} + @end diff --git a/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.h b/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.h index b01cba99e1..e08516828d 100644 --- a/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.h +++ b/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.h @@ -179,6 +179,16 @@ requestJson:(NSString *)requestJson resultJson:(NSString **)resultJson; +// MARK: - Auth Rule request +- (NSError *)buildAuthRuleRequestWithSubmitterDid:(NSString *)submitterDid + txnType:(NSString *)txnType + action:(NSString *)action + field:(NSString *)field + oldValue:(NSString *)oldValue + newValue:(NSString *)newValue + constraint:(NSString *)constraint + outRequest:(NSString **)resultJson; + // MARK: - Response Metadata - (NSError *)getResponseMetadata:(NSString *)response responseMetadata:(NSString **)responseMetadata; diff --git a/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.mm b/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.mm index c99724d7fb..5414042ecb 100644 --- a/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.mm +++ b/wrappers/ios/libindy-pod/Indy-demoTests/Test Utils/LedgerUtils.mm @@ -729,6 +729,38 @@ - (NSError *)multiSignRequestWithWalletHandle:(IndyHandle)walletHandle return err; } +- (NSError *)buildAuthRuleRequestWithSubmitterDid:(NSString *)submitterDid + txnType:(NSString *)txnType + action:(NSString *)action + field:(NSString *)field + oldValue:(NSString *)oldValue + newValue:(NSString *)newValue + constraint:(NSString *)constraint + outRequest:(NSString **)resultJson { + XCTestExpectation *completionExpectation = [[XCTestExpectation alloc] initWithDescription:@"completion finished"]; + __block NSError *err = nil; + __block NSString *outJson = nil; + + [IndyLedger buildAuthRuleRequestWithSubmitterDid:submitterDid + txnType:txnType + action:action + field:field + oldValue:oldValue + newValue:newValue + constraint:constraint + completion:^(NSError *error, NSString *json) { + err = error; + outJson = json; + [completionExpectation fulfill]; + }]; + + [self waitForExpectations:@[completionExpectation] timeout:[TestUtils longTimeout]]; + + if (resultJson) {*resultJson = outJson;} + + return err; +} + - (NSError *)getResponseMetadata:(NSString *)response responseMetadata:(NSString **)responseMetadata { XCTestExpectation *completionExpectation = [[XCTestExpectation alloc] initWithDescription:@"completion finished"]; diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h index 66464febda..9a2b09842e 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h @@ -568,7 +568,49 @@ */ + (void)parseGetRevocRegDeltaResponse:(NSString *)getRevocRegDeltaResponse completion:(void (^)(NSError *error, NSString *revocRegDefId, NSString *revocRegDeltaJson, NSNumber *timestamp))completion; - + +// MARK: - Auth Rule request + +/** + Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. + + @param submitterDid DID of the submitter stored in secured Wallet. + @param txnType - ledger transaction alias or associated value. + @param action - type of an action. + Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). + @param field - transaction field. + @param oldValue - old value of a field, which can be changed to a new_value (mandatory for EDIT action). + @param newValue - new value that can be used to fill the field. + @param constraint - set of constraints required for execution of an action in the following format: + { + constraint_id - type of a constraint. + Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + role - role of a user which satisfy to constrain. + sig_count - the number of signatures required to execution action. + need_to_be_owner - if user must be an owner of transaction. + metadata - additional parameters of the constraint. + } + can be combined by + { + 'constraint_id': <"AND" or "OR"> + 'auth_constraints': [, ] + } + + Default ledger auth rules: https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md + + More about AUTH_RULE request: https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule + + @param completion Callback that takes command result as parameter. Returns request result as json. + */ ++ (void)buildAuthRuleRequestWithSubmitterDid:(NSString *)submitterDid + txnType:(NSString *)txnType + action:(NSString *)action + field:(NSString *)field + oldValue:(NSString *)oldValue + newValue:(NSString *)newValue + constraint:(NSString *)constraint + completion:(void (^)(NSError *error, NSString *requestJSON))completion; + /** Parse transaction response to fetch metadata. The important use case for this method is validation of Node's response freshens. diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.mm b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.mm index 2d373124b7..40e7af3125 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.mm +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.mm @@ -706,6 +706,37 @@ + (void)parseGetRevocRegDeltaResponse:(NSString *)getRevocRegDeltaResponse } } ++ (void)buildAuthRuleRequestWithSubmitterDid:(NSString *)submitterDid + txnType:(NSString *)txnType + action:(NSString *)action + field:(NSString *)field + oldValue:(NSString *)oldValue + newValue:(NSString *)newValue + constraint:(NSString *)constraint + completion:(void (^)(NSError *error, NSString *requestJSON))completion { + indy_error_t ret; + + indy_handle_t handle = [[IndyCallbacks sharedInstance] createCommandHandleFor:completion]; + + + ret = indy_build_auth_rule_request(handle, + [submitterDid UTF8String], + [txnType UTF8String], + [action UTF8String], + [field UTF8String], + [oldValue UTF8String], + [newValue UTF8String], + [constraint UTF8String], + IndyWrapperCommonStringCallback); + if (ret != Success) { + [[IndyCallbacks sharedInstance] deleteCommandHandleFor:handle]; + + dispatch_async(dispatch_get_main_queue(), ^{ + completion([NSError errorFromIndyError:ret], nil); + }); + } +} + + (void)getResponseMetadata:(NSString *)response completion:(void (^)(NSError *error, NSString *responseMetadata))completion { indy_error_t ret; diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/LibIndy.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/LibIndy.java index 76daa98324..06470f85ea 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/LibIndy.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/LibIndy.java @@ -70,6 +70,7 @@ public interface API extends Library { public int indy_build_get_revoc_reg_delta_request(int command_handle, String submitter_did, String revoc_reg_def_id, long from, long to, Callback cb); public int indy_parse_get_revoc_reg_delta_response(int command_handle, String get_revoc_reg_delta_response, Callback cb); public int indy_get_response_metadata(int command_handle, String response, Callback cb); + public int indy_build_auth_rule_request(int command_handle, String submitter_did, String txn_type, String action, String field, String old_value, String new_value, String constraint, Callback cb); // did.rs diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java index c936466f7d..0ff187e3ea 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java @@ -1265,4 +1265,68 @@ public static CompletableFuture getResponseMetadata( return future; } + + /** + * Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. + * + * @param submitterDid DID of the submitter stored in secured Wallet. + * @param txnType - ledger transaction alias or associated value. + * @param action - type of an action. + * Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). + * @param field - transaction field. + * @param oldValue - old value of a field, which can be changed to a new_value (mandatory for EDIT action). + * @param newValue - new value that can be used to fill the field. + * @param constraint - set of constraints required for execution of an action in the following format: + * { + * constraint_id - [string] type of a constraint. + * Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + * role - [string] role of a user which satisfy to constrain. + * sig_count - [u32] the number of signatures required to execution action. + * need_to_be_owner - [bool] if user must be an owner of transaction. + * metadata - [object] additional parameters of the constraint. + * } + * can be combined by + * { + * 'constraint_id': "AND" or "OR" + * 'auth_constraints': [[constraint_1], [constraint_2]] + * } + * + * Default ledger auth rules: https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md + * + * More about AUTH_RULE request: https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule + * + * @return A future resolving to a request result as json. + * @throws IndyException Thrown if an error occurs when calling the underlying SDK. + */ + public static CompletableFuture buildAuthRuleRequest( + String submitterDid, + String txnType, + String action, + String field, + String oldValue, + String newValue, + String constraint) throws IndyException { + + ParamGuard.notNullOrWhiteSpace(submitterDid, "submitterDid"); + ParamGuard.notNullOrWhiteSpace(txnType, "txnType"); + ParamGuard.notNullOrWhiteSpace(action, "action"); + + CompletableFuture future = new CompletableFuture(); + int commandHandle = addFuture(future); + + int result = LibIndy.api.indy_build_auth_rule_request( + commandHandle, + submitterDid, + txnType, + action, + field, + oldValue, + newValue, + constraint, + buildRequestCb); + + checkResult(future, result); + + return future; + } } diff --git a/wrappers/java/src/test/java/org/hyperledger/indy/sdk/ledger/AuthRuleRequestsTest.java b/wrappers/java/src/test/java/org/hyperledger/indy/sdk/ledger/AuthRuleRequestsTest.java new file mode 100644 index 0000000000..b71b49d258 --- /dev/null +++ b/wrappers/java/src/test/java/org/hyperledger/indy/sdk/ledger/AuthRuleRequestsTest.java @@ -0,0 +1,65 @@ +package org.hyperledger.indy.sdk.ledger; + +import org.hyperledger.indy.sdk.IndyIntegrationTest; +import org.json.JSONObject; +import org.junit.Test; + +public class AuthRuleRequestsTest extends IndyIntegrationTest { + + private String txnType = "NYM"; + private String field = "role"; + private String newValue = "101"; + private JSONObject constraint = new JSONObject() + .put("sig_count", 1) + .put("role", "0") + .put("constraint_id", "ROLE") + .put("need_to_be_owner", false); + + @Test + public void testBuildAuthRuleRequestWorksForAddAction() throws Exception { + String addAuthAction = "ADD"; + + JSONObject expectedResult = new JSONObject() + .put("identifier", DID) + .put("operation", + new JSONObject() + .put("type", "120") + .put("auth_type", "1") + .put("auth_action", addAuthAction) + .put("field", field) + .put("new_value", newValue) + .put("constraint", constraint) + ); + + String request = Ledger.buildAuthRuleRequest(DID, txnType, addAuthAction, field, null, newValue, constraint.toString()).get(); + + assert (new JSONObject(request).toMap().entrySet() + .containsAll( + expectedResult.toMap().entrySet())); + } + + @Test + public void testBuildAuthRuleRequestWorksForEditAction() throws Exception { + String editAuthAction = "EDIT"; + String oldValue = "0"; + + JSONObject expectedResult = new JSONObject() + .put("identifier", DID) + .put("operation", + new JSONObject() + .put("type", "120") + .put("auth_type", "1") + .put("auth_action", editAuthAction) + .put("field", field) + .put("old_value", oldValue) + .put("new_value", newValue) + .put("constraint", constraint) + ); + + String request = Ledger.buildAuthRuleRequest(DID, txnType, editAuthAction, field, oldValue, newValue, constraint.toString()).get(); + + assert (new JSONObject(request).toMap().entrySet() + .containsAll( + expectedResult.toMap().entrySet())); + } +} diff --git a/wrappers/nodejs/README.md b/wrappers/nodejs/README.md index b7c197aa1b..4c5cdfb010 100644 --- a/wrappers/nodejs/README.md +++ b/wrappers/nodejs/README.md @@ -1748,6 +1748,43 @@ Parse a GET\_REVOC\_REG\_DELTA response to get Revocation Registry Delta in the Errors: `Common*` +#### buildAuthRuleRequest \( submitterDid, txnType, action, field, oldValue, newValue, constraint \) -> request + +Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. + +* `submitterDid`: String - \(Optional\) DID of the read request sender \(if not provided then default Libindy DID will be used\). +* `txnType`: String - ledger transaction alias or associated value. +* `action`: String - type of an action. + * "ADD" - to add a new rule + * "EDIT" - to edit an existing one +* `field`: String - transaction field. +* `oldValue`: String - \(Optional\) old value of a field, which can be changed to a new_value (mandatory for EDIT action). +* `newValue`: String - new value that can be used to fill the field. +* `constraint`: String - set of constraints required for execution of an action in the following format: +``` + { + constraint_id - type of a constraint. + Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + role - role of a user which satisfy to constrain. + sig_count - the number of signatures required to execution action. + need_to_be_owner - if user must be an owner of transaction. + metadata - additional parameters of the constraint. + } +can be combined by + { + 'constraint_id': <"AND" or "OR"> + 'auth_constraints': [, ] + } +``` + +Default ledger auth rules: https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md + +More about AUTH_RULE request: https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule + +* __->__ `request`: Json + +Errors: `Common*` + #### getResponseMetadata \( response \) -> responseMetadata Parse transaction response to fetch metadata. diff --git a/wrappers/nodejs/src/index.js b/wrappers/nodejs/src/index.js index 2614cd899c..b351e21fc1 100644 --- a/wrappers/nodejs/src/index.js +++ b/wrappers/nodejs/src/index.js @@ -541,6 +541,12 @@ indy.parseGetRevocRegDeltaResponse = function parseGetRevocRegDeltaResponse (get return cb.promise } +indy.buildAuthRuleRequest = function buildAuthRuleRequest (submitterDid, txnType, action, field, oldValue, newValue, constraint, cb) { + cb = wrapIndyCallback(cb, fromJson) + capi.buildAuthRuleRequest(submitterDid, txnType, action, field, oldValue, newValue, toJson(constraint), cb) + return cb.promise +} + indy.getResponseMetadata = function getResponseMetadata (response, cb) { cb = wrapIndyCallback(cb, fromJson) capi.getResponseMetadata(toJson(response), cb) diff --git a/wrappers/nodejs/src/indy.cc b/wrappers/nodejs/src/indy.cc index b0a1228d3e..da4698f82d 100644 --- a/wrappers/nodejs/src/indy.cc +++ b/wrappers/nodejs/src/indy.cc @@ -2038,6 +2038,40 @@ NAN_METHOD(parseGetRevocRegDeltaResponse) { delete arg0; } +void buildAuthRuleRequest_cb(indy_handle_t handle, indy_error_t xerr, const char* arg0) { + IndyCallback* icb = IndyCallback::getCallback(handle); + if(icb != nullptr){ + icb->cbString(xerr, arg0); + } +} +NAN_METHOD(buildAuthRuleRequest) { + INDY_ASSERT_NARGS(buildAuthRuleRequest, 8) + INDY_ASSERT_STRING(buildAuthRuleRequest, 0, submitterDid) + INDY_ASSERT_STRING(buildAuthRuleRequest, 1, txnType) + INDY_ASSERT_STRING(buildAuthRuleRequest, 2, action) + INDY_ASSERT_STRING(buildAuthRuleRequest, 3, field) + INDY_ASSERT_STRING(buildAuthRuleRequest, 4, oldValue) + INDY_ASSERT_STRING(buildAuthRuleRequest, 5, newValue) + INDY_ASSERT_STRING(buildAuthRuleRequest, 6, constraint) + INDY_ASSERT_FUNCTION(buildAuthRuleRequest, 7) + const char* arg0 = argToCString(info[0]); + const char* arg1 = argToCString(info[1]); + const char* arg2 = argToCString(info[2]); + const char* arg3 = argToCString(info[3]); + const char* arg4 = argToCString(info[4]); + const char* arg5 = argToCString(info[5]); + const char* arg6 = argToCString(info[6]); + IndyCallback* icb = argToIndyCb(info[7]); + indyCalled(icb, indy_build_auth_rule_request(icb->handle, arg0, arg1, arg2, arg3, arg4, arg5, arg6, buildAuthRuleRequest_cb)); + delete arg0; + delete arg1; + delete arg2; + delete arg3; + delete arg4; + delete arg5; + delete arg6; +} + void getResponseMetadata_cb(indy_handle_t handle, indy_error_t xerr, const char* arg0) { IndyCallback* icb = IndyCallback::getCallback(handle); if(icb != nullptr){ @@ -3115,6 +3149,7 @@ NAN_MODULE_INIT(InitAll) { Nan::Export(target, "parseGetRevocRegResponse", parseGetRevocRegResponse); Nan::Export(target, "buildGetRevocRegDeltaRequest", buildGetRevocRegDeltaRequest); Nan::Export(target, "parseGetRevocRegDeltaResponse", parseGetRevocRegDeltaResponse); + Nan::Export(target, "buildAuthRuleRequest", buildAuthRuleRequest); Nan::Export(target, "getResponseMetadata", getResponseMetadata); Nan::Export(target, "addWalletRecord", addWalletRecord); Nan::Export(target, "updateWalletRecordValue", updateWalletRecordValue); diff --git a/wrappers/nodejs/test/ledger.js b/wrappers/nodejs/test/ledger.js index 28cbb857a9..9883c07e45 100644 --- a/wrappers/nodejs/test/ledger.js +++ b/wrappers/nodejs/test/ledger.js @@ -151,6 +151,17 @@ test('ledger', async function (t) { req = await indy.signRequest(wh, myDid, req) res = await indy.submitAction(pool.handle, req, null, null) + var constraint = { + 'sig_count': 1, + 'metadata': {}, + 'role': '0', + 'constraint_id': 'ROLE', + 'need_to_be_owner': false + } + req = await indy.buildAuthRuleRequest(trusteeDid, 'NYM', 'ADD', 'role', null, '101', constraint) + res = await indy.signAndSubmitRequest(pool.handle, wh, trusteeDid, req) + t.is(res.op, 'REPLY') + await indy.closeWallet(wh) await indy.deleteWallet(walletConfig, walletCredentials) pool.cleanup() diff --git a/wrappers/python/indy/ledger.py b/wrappers/python/indy/ledger.py index 8700d60682..d692192462 100644 --- a/wrappers/python/indy/ledger.py +++ b/wrappers/python/indy/ledger.py @@ -1261,3 +1261,80 @@ async def get_response_metadata(response: str) -> str: res = response_metadata.decode() logger.debug("get_response_metadata: <<< res: %r", res) return res + + +async def build_auth_rule_request(submitter_did: str, + txn_type: str, + action: str, + field: str, + old_value: Optional[str], + new_value: str, + constraint: str) -> str: + """ + Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. + + :param submitter_did: DID of the submitter stored in secured Wallet. + :param txn_type: ledger transaction alias or associated value. + :param action: type of an action. + Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). + :param field: transaction field. + :param old_value: old value of a field, which can be changed to a new_value (mandatory for EDIT action). + :param new_value: new value that can be used to fill the field. + :param constraint: set of constraints required for execution of an action in the following format: + { + constraint_id - type of a constraint. + Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. + role - role of a user which satisfy to constrain. + sig_count - the number of signatures required to execution action. + need_to_be_owner - if user must be an owner of transaction. + metadata - additional parameters of the constraint. + } + can be combined by + { + 'constraint_id': <"AND" or "OR"> + 'auth_constraints': [, ] + } + + Default ledger auth rules: https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md + + More about AUTH_RULE request: https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule + + :return: Request result as json. + """ + + logger = logging.getLogger(__name__) + logger.debug("build_auth_rule_request: >>> submitter_did: %r, txn_type: %r, action: %r, field: %r, " + "old_value: %r, new_value: %r, constraint: %r", + submitter_did, + txn_type, + action, + field, + old_value, + new_value, + constraint) + + if not hasattr(build_auth_rule_request, "cb"): + logger.debug("build_auth_rule_request: Creating callback") + build_auth_rule_request.cb = create_cb(CFUNCTYPE(None, c_int32, c_int32, c_char_p)) + + c_submitter_did = c_char_p(submitter_did.encode('utf-8')) + c_txn_type = c_char_p(txn_type.encode('utf-8')) + c_action = c_char_p(action.encode('utf-8')) + c_field = c_char_p(field.encode('utf-8')) + c_old_value = c_char_p(old_value.encode('utf-8')) if old_value is not None else None + c_new_value = c_char_p(new_value.encode('utf-8')) + c_constraint = c_char_p(constraint.encode('utf-8')) + + request_json = await do_call('indy_build_auth_rule_request', + c_submitter_did, + c_txn_type, + c_action, + c_field, + c_old_value, + c_new_value, + c_constraint, + build_auth_rule_request.cb) + + res = request_json.decode() + logger.debug("build_auth_rule_request: <<< res: %r", res) + return res diff --git a/wrappers/python/tests/ledger/test_build_auth_rule_request.py b/wrappers/python/tests/ledger/test_build_auth_rule_request.py new file mode 100644 index 0000000000..aebe92a5dd --- /dev/null +++ b/wrappers/python/tests/ledger/test_build_auth_rule_request.py @@ -0,0 +1,61 @@ +from indy import ledger +from indy.error import * + +import json +import pytest + +identifier = "Th7MpTaRZVRYnPiabds81Y" +txn_type = "NYM" +add_auth_action = "ADD" +edit_auth_action = "EDIT" +field = 'role' +old_value = '0' +new_value = '101' +constraint = { + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": False +} + + +@pytest.mark.asyncio +async def test_build_auth_rule_request_works_for_add_auth_action(): + expected_request = { + "identifier": identifier, + "operation": { + "type": "120", + "auth_type": "1", + "auth_action": add_auth_action, + "field": field, + "new_value": new_value, + "constraint": constraint + } + } + + request = json.loads( + await ledger.build_auth_rule_request(identifier, txn_type, add_auth_action, field, None, new_value, + json.dumps(constraint))) + assert expected_request.items() <= request.items() + + +@pytest.mark.asyncio +async def test_build_auth_rule_request_works_for_edit_auth_action(): + expected_request = { + "identifier": identifier, + "operation": { + "type": "120", + "auth_type": "1", + "auth_action": edit_auth_action, + "field": field, + "old_value": old_value, + "new_value": new_value, + "constraint": constraint + } + } + + request = json.loads( + await ledger.build_auth_rule_request(identifier, txn_type, edit_auth_action, field, old_value, new_value, + json.dumps(constraint))) + assert expected_request.items() <= request.items() diff --git a/wrappers/rust/indy-sys/src/ledger.rs b/wrappers/rust/indy-sys/src/ledger.rs index f76620284c..7d0b784c51 100644 --- a/wrappers/rust/indy-sys/src/ledger.rs +++ b/wrappers/rust/indy-sys/src/ledger.rs @@ -221,6 +221,17 @@ extern { pub fn indy_get_response_metadata(command_handle: Handle, response: CString, cb: Option) -> Error; + + #[no_mangle] + pub fn indy_build_auth_rule_request(command_handle: Handle, + submitter_did: CString, + txn_type: CString, + action: CString, + field: CString, + old_value: CString, + new_value: CString, + constraint: CString, + cb: Option) -> Error; } pub type CustomTransactionParser = extern fn(reply_from_node: CString, parsed_sp: *mut CString) -> Error; diff --git a/wrappers/rust/src/ledger.rs b/wrappers/rust/src/ledger.rs index 3d8aa37807..cf23e3fe41 100644 --- a/wrappers/rust/src/ledger.rs +++ b/wrappers/rust/src/ledger.rs @@ -85,7 +85,7 @@ fn _submit_action(command_handle: IndyHandle, pool_handle: IndyHandle, request_j let nodes_str = opt_c_str!(nodes); ErrorCode::from(unsafe { - ledger::indy_submit_action(command_handle, pool_handle, request_json.as_ptr(), opt_c_ptr!(nodes, nodes_str), wait_timeout.unwrap_or(-1), cb) + ledger::indy_submit_action(command_handle, pool_handle, request_json.as_ptr(), opt_c_ptr!(nodes, nodes_str), wait_timeout.unwrap_or(-1), cb) }) } @@ -259,7 +259,7 @@ pub fn build_get_txn_request(submitter_did: Option<&str>, ledger_type: Option<&s ResultHandler::str(command_handle, err, receiver) } -fn _build_get_txn_request(command_handle: IndyHandle, submitter_did: Option<&str>, ledger_type: Option<&str>, seq_no: i32, cb: Option) -> ErrorCode { +fn _build_get_txn_request(command_handle: IndyHandle, submitter_did: Option<&str>, ledger_type: Option<&str>, seq_no: i32, cb: Option) -> ErrorCode { let submitter_did_str = opt_c_str!(submitter_did); let ledger_type_str = opt_c_str!(ledger_type); @@ -566,7 +566,7 @@ fn _build_get_validator_info_request(command_handle: IndyHandle, submitter_did: let submitter_did = c_str!(submitter_did); ErrorCode::from(unsafe { - ledger::indy_build_get_validator_info_request(command_handle, submitter_did.as_ptr(), cb) + ledger::indy_build_get_validator_info_request(command_handle, submitter_did.as_ptr(), cb) }) } @@ -890,7 +890,7 @@ pub fn parse_get_revoc_reg_response(get_revoc_reg_response: &str) -> Box) -> ErrorCode { let get_revoc_reg_response = c_str!(get_revoc_reg_response); - ErrorCode::from(unsafe { ledger::indy_parse_get_revoc_reg_response(command_handle,get_revoc_reg_response.as_ptr(), cb) }) + ErrorCode::from(unsafe { ledger::indy_parse_get_revoc_reg_response(command_handle, get_revoc_reg_response.as_ptr(), cb) }) } /// Builds a GET_REVOC_REG_DELTA request. Request to get the delta of the accumulated state of the Revocation Registry. @@ -947,7 +947,7 @@ pub fn parse_get_revoc_reg_delta_response(get_revoc_reg_delta_response: &str) -> fn _parse_get_revoc_reg_delta_response(command_handle: IndyHandle, get_revoc_reg_delta_response: &str, cb: Option) -> ErrorCode { let get_revoc_reg_delta_response = c_str!(get_revoc_reg_delta_response); - ErrorCode::from(unsafe { ledger::indy_parse_get_revoc_reg_delta_response(command_handle,get_revoc_reg_delta_response.as_ptr(), cb) }) + ErrorCode::from(unsafe { ledger::indy_parse_get_revoc_reg_delta_response(command_handle, get_revoc_reg_delta_response.as_ptr(), cb) }) } /// Parse transaction response to fetch metadata. @@ -991,5 +991,71 @@ pub fn get_response_metadata(response: &str) -> Box) -> ErrorCode { let response = c_str!(response); - ErrorCode::from(unsafe { ledger::indy_get_response_metadata(command_handle,response.as_ptr(), cb) }) + ErrorCode::from(unsafe { ledger::indy_get_response_metadata(command_handle, response.as_ptr(), cb) }) } + +/// Builds a AUTH_RULE request. Request to change authentication rules for a ledger transaction. +/// +/// # Arguments +/// * `txn_type`: ledger transaction alias or associated value for which authentication rules will be applied. +/// * `field`: type of an action for which authentication rules will be applied. +/// Can be either "ADD" (to add a new rule) or "EDIT" (to edit an existing one). +/// * `action`: transaction field for which authentication rule will be applied. +/// * `old_value`: old value of a field, which can be changed to a new_value (mandatory for EDIT action). +/// * `new_value`: new value that can be used to fill the field. +/// * `constraint`: set of constraints required for execution of an action in the following format: +/// { +/// constraint_id - type of a constraint. +/// Can be either "ROLE" to specify final constraint or "AND"/"OR" to combine constraints. +/// role - role of a user which satisfy to constrain. +/// sig_count - the number of signatures required to execution action. +/// need_to_be_owner - if user must be an owner of transaction. +/// metadata - additional parameters of the constraint. +/// } +/// can be combined by +/// { +/// 'constraint_id': <"AND" or "OR"> +/// 'auth_constraints': [, ] +/// } +/// +/// # Returns +/// Request result as json. +pub fn build_auth_rule_request(submitter_did: &str, txn_type: &str, action: &str, field: &str, + old_value: Option<&str>, new_value: &str, constraint: &str) -> Box> { + let (receiver, command_handle, cb) = ClosureHandler::cb_ec_string(); + + let err = _build_auth_rule_request(command_handle, submitter_did, txn_type, action, field, old_value, new_value, constraint, cb); + + ResultHandler::str(command_handle, err, receiver) +} + +fn _build_auth_rule_request(command_handle: IndyHandle, + submitter_did: &str, + txn_type: &str, + action: &str, + field: &str, + old_value: Option<&str>, + new_value: &str, + constraint: &str, + cb: Option) -> ErrorCode { + let submitter_did = c_str!(submitter_did); + let txn_type = c_str!(txn_type); + let action = c_str!(action); + let field = c_str!(field); + let new_value = c_str!(new_value); + let constraint = c_str!(constraint); + + let old_value_str = opt_c_str!(old_value); + + ErrorCode::from(unsafe { + ledger::indy_build_auth_rule_request(command_handle, + submitter_did.as_ptr(), + txn_type.as_ptr(), + action.as_ptr(), + field.as_ptr(), + opt_c_ptr!(old_value, old_value_str), + new_value.as_ptr(), + constraint.as_ptr(), + cb) + }) +} \ No newline at end of file diff --git a/wrappers/rust/tests/ledger.rs b/wrappers/rust/tests/ledger.rs index 52c2115f81..fa9211796b 100644 --- a/wrappers/rust/tests/ledger.rs +++ b/wrappers/rust/tests/ledger.rs @@ -1,9 +1,12 @@ -#[macro_use] extern crate serde_json; -#[macro_use] extern crate serde_derive; +#[macro_use] +extern crate serde_json; +#[macro_use] +extern crate serde_derive; extern crate rmp_serde; extern crate byteorder; extern crate indyrs as indy; extern crate futures; + #[allow(unused_variables)] #[allow(unused_macros)] #[allow(dead_code)] @@ -133,7 +136,7 @@ mod test_submit_request { mod test_submit_action { use super::*; - const NODES : &str = "[\"Node1\", \"Node2\"]"; + const NODES: &str = "[\"Node1\", \"Node2\"]"; #[test] #[ignore] // TODO: restore after IS-1027 will be fixed @@ -407,7 +410,7 @@ mod test_build_get_attrib_request { mod test_build_schema_request { use super::*; - const SCHEMA_DATA : &str = r#"{"id":"id","attrNames": ["name", "male"],"name":"gvt2","version":"3.1","ver":"1.0"}"#; + const SCHEMA_DATA: &str = r#"{"id":"id","attrNames": ["name", "male"],"name":"gvt2","version":"3.1","ver":"1.0"}"#; #[test] pub fn build_schema_request_success() { @@ -425,9 +428,9 @@ mod test_build_schema_request { #[cfg(test)] mod test_build_get_schema_request { -use super::*; + use super::*; - const SCHEMA_REQUEST : &str = "5LEV4bTAXntXqmtLFm7yCS:2:bob:1.0"; + const SCHEMA_REQUEST: &str = "5LEV4bTAXntXqmtLFm7yCS:2:bob:1.0"; #[test] pub fn build_get_schema_request_success() { @@ -450,11 +453,11 @@ mod test_parse_get_schema_response { use super::*; - const SCHEMA_NAME : &str = "schema_1234"; - const SCHEMA_DATA : &str = r#"{"id":"schema_id1234","attrNames": ["name", "male"],"name":"schema_1234","version":"1.0","ver":"1.0"}"#; + const SCHEMA_NAME: &str = "schema_1234"; + const SCHEMA_DATA: &str = r#"{"id":"schema_id1234","attrNames": ["name", "male"],"name":"schema_1234","version":"1.0","ver":"1.0"}"#; - fn create_build_schema_request(did : &String) -> String { + fn create_build_schema_request(did: &String) -> String { format!("{}:2:{}:1.0", did, SCHEMA_NAME) } @@ -483,7 +486,7 @@ mod test_parse_get_schema_response { let schema_request = create_build_schema_request(&did); let schema_response = ledger::build_get_schema_request(Some(&did), &schema_request).wait().unwrap(); - let signed_response = ledger::sign_request(wallet.handle, &did,&schema_response).wait().unwrap(); + let signed_response = ledger::sign_request(wallet.handle, &did, &schema_response).wait().unwrap(); let submit_response = ledger::submit_request(pool_handle, &signed_response).wait().unwrap(); let parse_response = ledger::parse_get_schema_response(&submit_response).wait(); @@ -522,7 +525,7 @@ mod test_build_get_ddo_request { mod test_build_get_txn_request { use super::*; - const LEDGER_TYPE : &str = "DOMAIN"; + const LEDGER_TYPE: &str = "DOMAIN"; #[test] pub fn build_get_txn_request_success() { @@ -542,10 +545,10 @@ mod test_build_get_txn_request { mod test_build_cred_def_request { use super::*; - const CRED_DATA : &str = r#"{"ver":"1.0","id":"V4SGRU86Z58d6TV7PBUe6f:3:CL:17:oI6Iokv","schemaId":"17","type":"CL","tag":"oI6Iokv","value":{"primary":{"n":"87178281071734731437690387382922138711010162107879888888538848407132327215161696479014638246148780516059076502007170233816521638866237445955186196199106181664196333035350522256775772678749757516076687671733088043974750225859037634391059057871128952528114293385763258675546471992534732373945591487042489023109902330242980705545998552851661474245748466697559479508930710234503328250288511766352977719334252928597855882930620741923986586828547412389638821815758450532251881301327049927072714545264108360496728434366393519356711418047944068773770531352206244052618670493983767902201878934735288733850555036281883721724473","s":"66794590351311400173440340223561508784777853797981871904981559245334503567545616382611784848902543717386870008558289905316601662574754089209687052710438230437549560386636286514822680768065835610624750399055088116166226098175424830519537908801592274839622946402090491946787040058370552124732885892610142242847158959492000732292603755213902976259446731410240912872744210451254301242220759673686769861789834996124153811460984114732824978048021325148492655982695079333718710090836034672739682282204904856516947015563681443657793597047393731812247221167838278986153621564706820976058621996938916226023920421313258317181056","r":{"height":"37686658568948037814775431903843597441856100114754323955796133079330648476309192012260294209465266635551131504125646637359931844595436529982289207908218765877672222632310887737940054188921134584912244256324727048869497937750475441196124576035922245172355820888415660512858847440533214955712359488689065763607483137995713620001810321655685884305156101062519673602853819411046367019986397235673847881046529391589711229735614071805410066894389088951657447726215788267372471488185033424222037788505918934857840957649277458736101301203881379280675945440723899652144116975079241713669490809165909240425120649887001447597783","sex":"48901017446440182649799731593114947230876351500273905015595989118217119375071111218399500737051508041416910144890371937193478691958771514378058820388069120931504416289663010431000145369715463131882383259114210820041731832960312557792681552574471886139487662967612807830334835729846093444859302732007584479807271091676491277902271511164922767210557187133481955551837663018165430244652992048757580783775433571336475692686259720997931194126203237043117966211161878071070916577025579669942228991696500136399569383974985399786080235342264485395522119939857486145401501612186163491615961653478438596959371113747671419654818","master_secret":"25754344723836699563584283879786692153622691083042382144160949511089528018631287834606498465418239311334501386316618629687258527283908520406207259178795217648481719864528515688115872808761112818709464686844054961387398147908732686218740513751960844653382166618983380191016571483892249629309506399346596975262589381752411984820505602091163687287542251803844421163362364666975191601496467090517324300542321861313152630025880504086070664031524805153571288074723002683472372414034607393588926109015678216745625826790077479058525170476570603845174669212586627449339894597259739762350550126584394404404482135882343197379054","name":"64945144723199018124037264140277156942131666148001245998219662472757345342279533884405888431954009830746588251472121029944168008438815350643138701794229741155411122621890661138856631059298571458398258239896113210276837384872922411226059610000961503919325158321529528085516642820682380678880886510720705463915144189095545183388444662260696183777535832602518582169729325489244691039221691384084009024188000745680035168934609700642727769603625029893488551202843527601643121220149122355960460523112480070939364242895718918315978456710031746858656148388609050488969420517950113219916527876709626082332309036117494497571230","age":"32059832863983999153613274260157019698296212529496396734792926477728751870868732126531732944880026440901943732956875433636855727848522486073745001899386738358016223503298068118020520201025961660332519845724521320552467451946852925824035412812595344753770029091958417300642692814810865758776286263929051571009155820474717897179825570107678882180230319004359558595714472285100325554837250772201405847343231203624749918461130474867722155978675551762505078508248381791048831193422832357874770535478244636601382323151375446307087219224928662366021820679699538198192887109930714869161019271417169222414370495648047714662103"},"rctxt":"38527464755916130963069611882293312815641859720607042775748742527688895624917359948168832828223678535804570958646474457323858571801037955359525462917173252086033591899208879285429574561167189107147758287137082689831331351781369164690717667586513821072095969666206581907243540078811132148136508600170388059098530570400824663936962403223100603326027117401899329035603739144303108339956544437073624926208818126402005595194344120188160525632489014089138283290414616529527375527577666875823786710497945303252443476610222721607664991987281949777517734685590949741562190122640202895612444667451959072089271004850428610427341","z":"31160349078984317779569363785252606468286101126154161634595102825752576352018565401209247600497171866986884547654707390053445672860929599916189762737605262398652714436350679825010487409345252016639698884761154432723648619393558760904141612222413004613912305317054390982133492273484244661652402423836430130022761985374095739624351663212686292616011934960947889676162946869205272435766196882679460333258355511812361345778943009086137697548566706243827603133668933678120765799991107515465495261132740007129958253450651703438567276912235691326876396719017894799034243480316169679472944643292666194979984921170821328746189"}}}"#; + const CRED_DATA: &str = r#"{"ver":"1.0","id":"V4SGRU86Z58d6TV7PBUe6f:3:CL:17:oI6Iokv","schemaId":"17","type":"CL","tag":"oI6Iokv","value":{"primary":{"n":"87178281071734731437690387382922138711010162107879888888538848407132327215161696479014638246148780516059076502007170233816521638866237445955186196199106181664196333035350522256775772678749757516076687671733088043974750225859037634391059057871128952528114293385763258675546471992534732373945591487042489023109902330242980705545998552851661474245748466697559479508930710234503328250288511766352977719334252928597855882930620741923986586828547412389638821815758450532251881301327049927072714545264108360496728434366393519356711418047944068773770531352206244052618670493983767902201878934735288733850555036281883721724473","s":"66794590351311400173440340223561508784777853797981871904981559245334503567545616382611784848902543717386870008558289905316601662574754089209687052710438230437549560386636286514822680768065835610624750399055088116166226098175424830519537908801592274839622946402090491946787040058370552124732885892610142242847158959492000732292603755213902976259446731410240912872744210451254301242220759673686769861789834996124153811460984114732824978048021325148492655982695079333718710090836034672739682282204904856516947015563681443657793597047393731812247221167838278986153621564706820976058621996938916226023920421313258317181056","r":{"height":"37686658568948037814775431903843597441856100114754323955796133079330648476309192012260294209465266635551131504125646637359931844595436529982289207908218765877672222632310887737940054188921134584912244256324727048869497937750475441196124576035922245172355820888415660512858847440533214955712359488689065763607483137995713620001810321655685884305156101062519673602853819411046367019986397235673847881046529391589711229735614071805410066894389088951657447726215788267372471488185033424222037788505918934857840957649277458736101301203881379280675945440723899652144116975079241713669490809165909240425120649887001447597783","sex":"48901017446440182649799731593114947230876351500273905015595989118217119375071111218399500737051508041416910144890371937193478691958771514378058820388069120931504416289663010431000145369715463131882383259114210820041731832960312557792681552574471886139487662967612807830334835729846093444859302732007584479807271091676491277902271511164922767210557187133481955551837663018165430244652992048757580783775433571336475692686259720997931194126203237043117966211161878071070916577025579669942228991696500136399569383974985399786080235342264485395522119939857486145401501612186163491615961653478438596959371113747671419654818","master_secret":"25754344723836699563584283879786692153622691083042382144160949511089528018631287834606498465418239311334501386316618629687258527283908520406207259178795217648481719864528515688115872808761112818709464686844054961387398147908732686218740513751960844653382166618983380191016571483892249629309506399346596975262589381752411984820505602091163687287542251803844421163362364666975191601496467090517324300542321861313152630025880504086070664031524805153571288074723002683472372414034607393588926109015678216745625826790077479058525170476570603845174669212586627449339894597259739762350550126584394404404482135882343197379054","name":"64945144723199018124037264140277156942131666148001245998219662472757345342279533884405888431954009830746588251472121029944168008438815350643138701794229741155411122621890661138856631059298571458398258239896113210276837384872922411226059610000961503919325158321529528085516642820682380678880886510720705463915144189095545183388444662260696183777535832602518582169729325489244691039221691384084009024188000745680035168934609700642727769603625029893488551202843527601643121220149122355960460523112480070939364242895718918315978456710031746858656148388609050488969420517950113219916527876709626082332309036117494497571230","age":"32059832863983999153613274260157019698296212529496396734792926477728751870868732126531732944880026440901943732956875433636855727848522486073745001899386738358016223503298068118020520201025961660332519845724521320552467451946852925824035412812595344753770029091958417300642692814810865758776286263929051571009155820474717897179825570107678882180230319004359558595714472285100325554837250772201405847343231203624749918461130474867722155978675551762505078508248381791048831193422832357874770535478244636601382323151375446307087219224928662366021820679699538198192887109930714869161019271417169222414370495648047714662103"},"rctxt":"38527464755916130963069611882293312815641859720607042775748742527688895624917359948168832828223678535804570958646474457323858571801037955359525462917173252086033591899208879285429574561167189107147758287137082689831331351781369164690717667586513821072095969666206581907243540078811132148136508600170388059098530570400824663936962403223100603326027117401899329035603739144303108339956544437073624926208818126402005595194344120188160525632489014089138283290414616529527375527577666875823786710497945303252443476610222721607664991987281949777517734685590949741562190122640202895612444667451959072089271004850428610427341","z":"31160349078984317779569363785252606468286101126154161634595102825752576352018565401209247600497171866986884547654707390053445672860929599916189762737605262398652714436350679825010487409345252016639698884761154432723648619393558760904141612222413004613912305317054390982133492273484244661652402423836430130022761985374095739624351663212686292616011934960947889676162946869205272435766196882679460333258355511812361345778943009086137697548566706243827603133668933678120765799991107515465495261132740007129958253450651703438567276912235691326876396719017894799034243480316169679472944643292666194979984921170821328746189"}}}"#; #[test] - pub fn test_build_cred_def_request_success(){ + pub fn test_build_cred_def_request_success() { let wallet = Wallet::new(); let (did, _) = did::create_and_store_my_did(wallet.handle, "{}").wait().unwrap(); @@ -560,75 +563,75 @@ mod test_build_cred_def_request { } #[cfg(test)] -mod test_build_get_cred_def_request { - -} +mod test_build_get_cred_def_request {} #[cfg(test)] -mod test_build_node_request { - -} +mod test_build_node_request {} #[cfg(test)] -mod test_build_get_validator_info_request { - -} +mod test_build_get_validator_info_request {} #[cfg(test)] -mod test_build_pool_config_request { - -} +mod test_build_pool_config_request {} #[cfg(test)] -mod test_build_pool_restart_request { - -} +mod test_build_pool_restart_request {} #[cfg(test)] -mod test_build_pool_upgrade_request { - -} +mod test_build_pool_upgrade_request {} #[cfg(test)] -mod test_build_revoc_reg_def_request { - -} +mod test_build_revoc_reg_def_request {} #[cfg(test)] -mod test_build_get_revoc_reg_def_request { - -} +mod test_build_get_revoc_reg_def_request {} #[cfg(test)] -mod test_parse_get_revoc_reg_def_response { - -} +mod test_parse_get_revoc_reg_def_response {} #[cfg(test)] -mod test_build_revoc_reg_entry_request { -} +mod test_build_revoc_reg_entry_request {} #[cfg(test)] -mod test_build_get_revoc_reg_request { - -} +mod test_build_get_revoc_reg_request {} #[cfg(test)] -mod test_parse_get_revoc_reg_response { - -} +mod test_parse_get_revoc_reg_response {} #[cfg(test)] -mod test_build_get_revoc_reg_delta_request { - -} +mod test_build_get_revoc_reg_delta_request {} #[cfg(test)] -mod test_parse_get_revoc_reg_delta_response { +mod test_parse_get_revoc_reg_delta_response {} -} +#[cfg(test)] +mod test_register_transaction_parser_for_sp {} #[cfg(test)] -mod test_register_transaction_parser_for_sp { +mod test_build_auth_rule_request { + use super::*; -} + const DID: &str = "VsKV7grR1BUE29mG2Fm2kX"; + const NYM_AUTH_TYPE: &str = "1"; + const ADD_AUTH_ACTION: &str = "ADD"; + const FIELD: &str = "role"; + const NEW_VALUE: &str = "101"; + const ROLE_CONSTRAINT: &str = r#"{ + "sig_count": 1, + "metadata": {}, + "role": "0", + "constraint_id": "ROLE", + "need_to_be_owner": false + }"#; + + #[test] + pub fn build_auth_rule_request_success() { + let _nym_result = ledger::build_auth_rule_request(DID, + NYM_AUTH_TYPE, + &ADD_AUTH_ACTION, + FIELD, + None, + NEW_VALUE, + ROLE_CONSTRAINT).wait().unwrap(); + } +} \ No newline at end of file