diff --git a/devops/indy-pool/Dockerfile b/devops/indy-pool/Dockerfile index 6405d86dd..d3e3494c2 100644 --- a/devops/indy-pool/Dockerfile +++ b/devops/indy-pool/Dockerfile @@ -51,7 +51,6 @@ RUN echo "deb https://repo.sovrin.org/deb xenial $indy_stream" >> /etc/apt/sourc RUN apt-get update && apt-get install -y --no-install-recommends \ indy-plenum=${indy_plenum_ver} \ indy-node=${indy_node_ver} \ - indy-anoncreds=${indy_anoncreds_ver} \ python3-indy-crypto=${python3_indy_crypto_ver} \ libindy-crypto=${indy_crypto_ver} \ sovtoken=${token_ver} \ diff --git a/libsovtoken/src/logic/address.rs b/libsovtoken/src/logic/address.rs index 5842bab38..1a7bd68e9 100644 --- a/libsovtoken/src/logic/address.rs +++ b/libsovtoken/src/logic/address.rs @@ -272,6 +272,14 @@ pub mod address_tests { assert_eq!(address_bytes.len() - vk_bytes.len(), ADDRESS_CHECKSUM_LEN); } + #[test] + fn test_unqualified_address_from_verkey_invalid_verkey() { + let vk_bytes = rand_bytes(VERKEY_LEN - 1); + let verkey = vk_bytes.into_base58(); + let err = unqualified_address_from_verkey(&verkey).unwrap_err(); + assert_eq!(ErrorCode::CommonInvalidStructure, err); + } + #[test] fn test_verkey_invalid_address_length_long_and_short() { validate_address_invalid_verkey_len(30); @@ -288,6 +296,13 @@ pub mod address_tests { assert_eq!(ErrorCode::CommonInvalidStructure, error); } + #[test] + fn test_address_from_unqualified_address() { + let unqualified_address = gen_random_base58_address(); + let address = address_from_unqualified_address(&unqualified_address).unwrap(); + assert_eq!(format!("pay:sov:{}", unqualified_address), address); + } + #[test] fn test_verkey_from_qualified_address() { let verkey = gen_random_base58_verkey(); @@ -327,6 +342,14 @@ pub mod address_tests { assert_eq!(ErrorCode::CommonInvalidStructure, error); } + #[test] + fn test_unqualified_address() { + let address = gen_random_base58_address(); + let qualified_address = format!("pay:sov:{}", address); + let unqualified_address = unqualified_address_from_address(&qualified_address).unwrap(); + assert_eq!(address, unqualified_address); + } + #[test] fn test_unqualified_address_invalid_qualifier() { let address = gen_random_base58_address(); @@ -375,4 +398,23 @@ pub mod address_tests { assert_eq!(validate_address(&fa).unwrap(), verkeys[i]) } } + + #[test] + fn string_to_txo_works() { + let txo_str = "txo:sov:fkjZEd8eTBnYJsw7m7twMph3UYD7j2SoWcDM45DkmRx8eq2SkQnzxoLxyMT1RBAat9x86MwXNJH88Pxf9u7JsM5m8ApXn3bvgbtS5cegZzNp7WmMSpWL"; + let result_txo = string_to_txo(txo_str).unwrap(); + assert_eq!(TXO {address:"pay:sov:iTQzpdRdugkJ2gLD5vW5c159dncSL9jbAtu3WfPcb8qWD9bUd".to_string(), seq_no: 1}, result_txo); + } + + #[test] + fn string_to_txo_fail_for_no_qualifier() { + let txo_str = "fkjZEd8eTBnYJsw7m7twMph3UYD7j2SoWcDM45DkmRx8eq2SkQnzxoLxyMT1RBAat9x86MwXNJH88Pxf9u7JsM5m8ApXn3bvgbtS5cegZzNp7WmMSpWL"; + assert!(string_to_txo(txo_str).is_err()); + } + + #[test] + fn string_to_txo_fail_for_invalid_txo() { + let txo_str = "txo:sov:fkjZEd8eTBnYJsw7m"; + assert!(string_to_txo(txo_str).is_err()); + } } diff --git a/libsovtoken/src/logic/api_internals/add_request_fees.rs b/libsovtoken/src/logic/api_internals/add_request_fees.rs index a4b08c4ca..9d9c81525 100644 --- a/libsovtoken/src/logic/api_internals/add_request_fees.rs +++ b/libsovtoken/src/logic/api_internals/add_request_fees.rs @@ -249,21 +249,57 @@ mod test_deserialize_inputs { #[test] fn deserialize_inputs_invalid_inputs_json() { + // no `address` field let invalid_json = json_c_pointer!([ { - "addres": "pay:sov:d0kitWxupHvZ4i0NHJhoj79RcUeyt3YlwAc8Hbcy87iRLSZC", + "add": "pay:sov:d0kitWxupHvZ4i0NHJhoj79RcUeyt3YlwAc8Hbcy87iRLSZC", "seqNo": 4 } ]); error_deserialize_inputs_inputs(invalid_json, ErrorCode::CommonInvalidStructure); + + // negative `seqNo` + let invalid_json = json_c_pointer!([ + { + "address": "pay:sov:d0kitWxupHvZ4i0NHJhoj79RcUeyt3YlwAc8Hbcy87iRLSZC", + "seqNo": -4 + } + ]); + error_deserialize_inputs_inputs(invalid_json, ErrorCode::CommonInvalidStructure); + + // no `seqNo` field + let invalid_json = json_c_pointer!([ + { + "address": "pay:sov:d0kitWxupHvZ4i0NHJhoj79RcUeyt3YlwAc8Hbcy87iRLSZC" + } + ]); + error_deserialize_inputs_inputs(invalid_json, ErrorCode::CommonInvalidStructure); } #[test] fn deserialize_inputs_invalid_outputs_json() { + // no `address` field + let invalid_json = json_c_pointer!([ + { + "add": "pay:sov:ql33nBkjGw6szxPT6LLRUIejn9TZAYk", + "amount": 10 + } + ]); + error_deserialize_inputs_ouputs(invalid_json, ErrorCode::CommonInvalidStructure); + + // negative `amount` + let invalid_json = json_c_pointer!([ + { + "recipient": "pay:sov:ql33nBkjGw6szxPT6LLRUIejn9TZAYk", + "amount": -10 + } + ]); + error_deserialize_inputs_ouputs(invalid_json, ErrorCode::CommonInvalidStructure); + + // no `amount` field let invalid_json = json_c_pointer!([ { - "address": "pay:sov:ql33nBkjGw6szxPT6LLRUIejn9TZAYkVRPd0QJzfJ8FdhZWs", - "amount": "10" + "recipient": "pay:sov:ql33nBkjGw6szxPT6LLRUIejn9TZAYk", } ]); error_deserialize_inputs_ouputs(invalid_json, ErrorCode::CommonInvalidStructure); diff --git a/libsovtoken/src/logic/payments.rs b/libsovtoken/src/logic/payments.rs index c0f4a9826..6deec115e 100644 --- a/libsovtoken/src/logic/payments.rs +++ b/libsovtoken/src/logic/payments.rs @@ -17,13 +17,13 @@ use logic::address; and in testing environments its anything else as long as it implements CryptoAPI */ -pub struct CreatePaymentHandler where T : CryptoAPI { - injected_api : T +pub struct CreatePaymentHandler where T: CryptoAPI { + injected_api: T } impl CreatePaymentHandler { - pub fn new(api_handler : T) -> Self { - CreatePaymentHandler { injected_api : api_handler } + pub fn new(api_handler: T) -> Self { + CreatePaymentHandler { injected_api: api_handler } } /** @@ -45,11 +45,11 @@ impl CreatePaymentHandler { pay:sov:{32 byte address}{4 byte checksum} */ pub fn create_payment_address_async(&self, - wallet_id: i32, - config: PaymentAddressConfig, - mut cb : F) -> ErrorCode where F: FnMut(String, ErrorCode) + Send { + wallet_id: i32, + config: PaymentAddressConfig, + mut cb: F) -> ErrorCode where F: FnMut(String, ErrorCode) + Send { - let cb_closure = move | err: ErrorCode, verkey : String | { + let cb_closure = move |err: ErrorCode, verkey: String| { let res = if ErrorCode::Success == err { trace!("got verkey from self.injected_api.indy_create_key_async {}", secret!(&verkey)); address::qualified_address_from_verkey(&verkey) @@ -82,11 +82,10 @@ impl CreatePaymentHandler { mod payments_tests { extern crate log; - use std::sync::mpsc::{channel}; + use std::sync::mpsc::channel; use std::time::Duration; use logic::address::*; use logic::address::address_tests::gen_random_base58_verkey; - use utils::random::rand_string; use utils::constants::general::PAYMENT_ADDRESS_QUALIFIER; use utils::base58::FromBase58; @@ -111,10 +110,9 @@ mod payments_tests { } - static VALID_SEED_LEN: usize = 32; static WALLET_ID: i32 = 10; - fn validate_address(address : String) { + fn validate_address(address: String) { // got our result, if its correct, it will look something like this: // pay:sov:gzidfrdJtvgUh4jZTtGvTZGU5ebuGMoNCbofXGazFa91234 // break it up into the individual parts we expect to find and @@ -131,54 +129,25 @@ mod payments_tests { // a fully formatted address is returned. #[test] fn success_create_payment_with_seed_returns_address() { - - let seed = rand_string(VALID_SEED_LEN); + let seed = "000000000000000000000000Address1".to_string(); let config: PaymentAddressConfig = PaymentAddressConfig { seed }; - let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler{}); - - let address = match handler.create_payment_address(WALLET_ID, config) { - Ok(s) => s, - Err(_) => "".to_string(), - }; - - // got our result, if its correct, it will look something like this: - // pay:sov:gzidfrdJtvgUh4jZTtGvTZGU5ebuGMoNCbofXGazFa91234 - // break it up into the individual parts we expect to find and - // test the validity of the parts - let qualifer = &address[..ADDRESS_QUAL_LEN]; - let result_address = &address[ADDRESS_QUAL_LEN..]; - - assert_eq!(PAYMENT_ADDRESS_QUALIFIER, qualifer, "PAYMENT_ADDRESS_QUALIFIER, not found"); - assert_eq!(VERKEY_LEN + ADDRESS_CHECKSUM_LEN, result_address.from_base58().unwrap().len(), "address is not 36 bytes"); - assert_eq!(VERKEY_LEN, result_address.from_base58_check().unwrap().len(), "verkey is not 32 bytes"); + let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler {}); + let address = handler.create_payment_address(WALLET_ID, config).unwrap(); + validate_address(address); } // This is the happy path test when seed provided is empty. Expectation is a // a fully formatted address is returned. #[test] fn success_create_payment_with_no_seed_returns_address() { - let seed = String::new(); let config: PaymentAddressConfig = PaymentAddressConfig { seed }; - let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler{}); - let address = match handler.create_payment_address(WALLET_ID, config){ - Ok(s) => s, - Err(_) => "".to_string(), - }; - - // got our result, if its correct, it will look something like this: - // pay:sov:gzidfrdJtvgUh4jZTtGvTZGU5ebuGMoNCbofXGazFa91234 - // break it up into the individual parts we expect to find and - // test the validity of the parts - let qualifer = &address[..ADDRESS_QUAL_LEN]; - let result_address = &address[ADDRESS_QUAL_LEN..]; - - assert_eq!(PAYMENT_ADDRESS_QUALIFIER, qualifer, "PAYMENT_ADDRESS_QUALIFIER, not found"); - assert_eq!(VERKEY_LEN + ADDRESS_CHECKSUM_LEN, result_address.from_base58().unwrap().len(), "address is not 36 bytes"); - assert_eq!(VERKEY_LEN, result_address.from_base58_check().unwrap().len(), "verkey is not 32 bytes"); + let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler {}); + let address = handler.create_payment_address(WALLET_ID, config).unwrap(); + validate_address(address); } // Happy path test assumes the CB is valid and it is successfully called @@ -187,11 +156,11 @@ mod payments_tests { let seed = String::new(); let config: PaymentAddressConfig = PaymentAddressConfig { seed }; - let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler{}); + let handler = CreatePaymentHandler::new(CreatePaymentSDKMockHandler {}); let (sender, receiver) = channel(); - let cb_closure = move | address : String, err: ErrorCode | { + let cb_closure = move |address: String, err: ErrorCode| { if err != ErrorCode::Success { sender.send(false).unwrap(); diff --git a/libsovtoken/tests/add_fees_for_attrib_test.rs b/libsovtoken/tests/add_fees_for_attrib_test.rs index 5047c12e2..124e43060 100644 --- a/libsovtoken/tests/add_fees_for_attrib_test.rs +++ b/libsovtoken/tests/add_fees_for_attrib_test.rs @@ -49,6 +49,8 @@ pub fn build_and_submit_attrib_with_fees() { assert_eq!(parsed_utxos[0].amount, 9); assert_eq!(parsed_utxos[0].recipient, addresses[0]); + std::thread::sleep(std::time::Duration::from_millis(100)); + let get_attrib_resp = send_get_attrib_req(&wallet, pool_handle, dids[0], dids[0], Some("endpoint")); let data = get_data_from_attrib_reply(get_attrib_resp); assert_eq!(ATTRIB_RAW_DATA, data); @@ -80,6 +82,8 @@ pub fn build_and_submit_attrib_with_fees_and_no_change() { let parsed_utxos: Vec = serde_json::from_str(&parsed_resp).unwrap(); assert_eq!(parsed_utxos.len(), 0); + std::thread::sleep(std::time::Duration::from_millis(100)); + let get_attrib_resp = send_get_attrib_req(&wallet, pool_handle, dids[0], dids[0], Some("endpoint")); let data = get_data_from_attrib_reply(get_attrib_resp); assert_eq!(ATTRIB_RAW_DATA_2, data); @@ -186,6 +190,8 @@ pub fn build_and_submit_attrib_with_fees_double_spend() { assert_eq!(parsed_utxos[0].amount, 9); assert_eq!(parsed_utxos[0].recipient, addresses[0]); + std::thread::sleep(std::time::Duration::from_millis(100)); + let get_attrib_resp = send_get_attrib_req(&wallet, pool_handle, dids[0], dids[0], Some("endpoint")); let data = get_data_from_attrib_reply(get_attrib_resp); assert_eq!(ATTRIB_RAW_DATA, data); diff --git a/libsovtoken/tests/build_add_fees_txn_handler_test.rs b/libsovtoken/tests/add_request_fees_handler_test.rs similarity index 58% rename from libsovtoken/tests/build_add_fees_txn_handler_test.rs rename to libsovtoken/tests/add_request_fees_handler_test.rs index decb8b4c9..7d21fe9b3 100644 --- a/libsovtoken/tests/build_add_fees_txn_handler_test.rs +++ b/libsovtoken/tests/add_request_fees_handler_test.rs @@ -7,8 +7,6 @@ extern crate indyrs as indy; #[macro_use] extern crate lazy_static; -use indy::future::Future; - pub mod utils; use sovtoken::utils::results::ResultHandler; @@ -20,7 +18,7 @@ use sovtoken::{ErrorCode, IndyHandle}; use utils::wallet::Wallet; -fn call_add_fees(wallet_handle: IndyHandle, inputs: String, outputs: String, extra: Option, request: String) -> Result { +fn call_add_request_fees(wallet_handle: IndyHandle, inputs: String, outputs: String, extra: Option, request: String) -> Result { let (receiver, command_handle, cb) = callbacks::cb_ec_string(); let did = "mydid1"; @@ -89,7 +87,7 @@ fn test_add_fees_to_request_valid() { } }); - let result = call_add_fees( + let result = call_add_request_fees( wallet.handle, inputs.to_string(), outputs.to_string(), @@ -100,16 +98,11 @@ fn test_add_fees_to_request_valid() { assert_eq!(expected_fees_request.to_string(), result); } -#[test] // TODO: look carefully on changes -fn test_add_fees_to_request_valid_from_libindy() { +#[test] +fn test_add_fees_to_request_works_for_invalid_request() { let (wallet, input_address) = init_wallet_with_address(); - let did = "Th7MpTaRZVRYnPiabds81Y"; - let fake_request = json!({ - "operation": { - "type": "3" - } - }); + let fake_request = "INVALID REQUEST"; let txo = TXO { address: input_address, seq_no: 1 }; @@ -120,95 +113,102 @@ fn test_add_fees_to_request_valid_from_libindy() { "amount": 20, }]); - let expected_fees_request = json!({ - "fees": [ - [ - { - "address": "iTQzpdRdugkJ2gLD5vW5c159dncSL9jbAtu3WfPcb8qWD9bUd", - "seqNo": 1 - } - ], - [ - { - "address": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "amount": 20 - } - ], - ["64wPLDPMjGxgqTdrNTZFa9CK4NtvBx7eLJkgnjW3JchRGyMUr29tjkAiHCTnhLtkdW81k5BtBiiqM2tkaMB2eouv"] - ], - "operation": { - "type": "3" - } - }); + let err = call_add_request_fees( + wallet.handle, + inputs.to_string(), + outputs.to_string(), + None, + fake_request.to_string() + ).unwrap_err(); + + assert_eq!(err, ErrorCode::CommonInvalidStructure) +} + +#[test] +fn build_add_fees_to_request_works_for_invalid_utxo() { + sovtoken::api::sovtoken_init(); + let wallet = Wallet::new(); - let (req, method) = indy::payments::add_request_fees( + let fake_request = json!({ + "operation": { + "type": "3" + } + }).to_string(); + + let inputs = json!(["txo:sov:1234"]).to_string(); + + let outputs = json!([{ + "recipient": "pay:sov:dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", + "amount": 20, + }]).to_string(); + + let err = call_add_request_fees( wallet.handle, - Some(did), - &fake_request.to_string(), - &inputs.to_string(), - &outputs.to_string(), + inputs.to_string(), + outputs.to_string(), None, - ).wait().unwrap(); + fake_request.to_string() + ).unwrap_err(); - assert_eq!("sov", method); - assert_eq!(expected_fees_request.to_string(), req); + assert_eq!(err, ErrorCode::CommonInvalidStructure) } #[test] -fn test_add_fees_to_request_valid_from_libindy_for_not_owned_payment_address() { - let wallet_1 = utils::wallet::Wallet::new(); - let wallet_2 = utils::wallet::Wallet::new(); - - let setup = utils::setup::Setup::new(&wallet_1, utils::setup::SetupConfig { - num_addresses: 1, - num_trustees: 4, - num_users: 0, - mint_tokens: Some(vec![30]), - fees: None, - }); - let addresses = &setup.addresses; - let pool_handle = setup.pool_handle; - let dids = setup.trustees.dids(); +fn test_add_fees_to_request_works_for_invalid_receipt() { + let (wallet, input_address) = init_wallet_with_address(); let fake_request = json!({ - "operation": { - "type": "3" - } + "operation": { + "type": "3" + } }); - let utxo = utils::payment::get_utxo::get_first_utxo_txo_for_payment_address(&wallet_1, pool_handle, dids[0], &addresses[0]); + let txo = TXO { address: input_address, seq_no: 1 }; - let inputs = json!([utxo]); + let inputs = json!([txo.to_libindy_string().unwrap()]); let outputs = json!([{ - "recipient": addresses[0], + "recipient": "pay:sov:1234", "amount": 20, }]); - let err = indy::payments::add_request_fees(wallet_2.handle, Some(dids[0]), &fake_request.to_string(), &inputs.to_string(), &outputs.to_string(), None).wait().unwrap_err(); - assert_eq!(err.error_code, ErrorCode::WalletItemNotFound); + let err = call_add_request_fees( + wallet.handle, + inputs.to_string(), + outputs.to_string(), + None, + fake_request.to_string() + ).unwrap_err(); + + assert_eq!(err, ErrorCode::CommonInvalidStructure) } #[test] -fn build_add_fees_to_request_works_for_invalid_utxo() { - sovtoken::api::sovtoken_init(); - let wallet = Wallet::new(); - let (did, _) = indy::did::create_and_store_my_did(wallet.handle, &json!({"seed": "000000000000000000000000Trustee1"}).to_string()).wait().unwrap(); +fn test_add_fees_to_request_works_for_invalid_amount() { + let (wallet, input_address) = init_wallet_with_address(); let fake_request = json!({ "operation": { "type": "3" } - }).to_string(); + }); - let inputs = json!(["txo:sov:1234"]).to_string(); + let txo = TXO { address: input_address, seq_no: 1 }; + + let inputs = json!([txo.to_libindy_string().unwrap()]); let outputs = json!([{ - "recipient": "pay:sov:dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "amount": 20, - }]).to_string(); + "recipient": "pay:sov:1234", + "amount": -20, + }]); - let err = indy::payments::add_request_fees(wallet.handle, Some(&did), &fake_request, &inputs, &outputs, None).wait().unwrap_err(); + let err = call_add_request_fees( + wallet.handle, + inputs.to_string(), + outputs.to_string(), + None, + fake_request.to_string() + ).unwrap_err(); - assert_eq!(err.error_code, ErrorCode::CommonInvalidStructure) + assert_eq!(err, ErrorCode::CommonInvalidStructure) } \ No newline at end of file diff --git a/libsovtoken/tests/build_fees_txn_handler_test.rs b/libsovtoken/tests/build_fees_txn_handler_test.rs index 6da5e92d9..d309d115a 100644 --- a/libsovtoken/tests/build_fees_txn_handler_test.rs +++ b/libsovtoken/tests/build_fees_txn_handler_test.rs @@ -10,61 +10,66 @@ extern crate bs58; use libc::c_char; use std::ffi::CString; use std::ptr; -use std::sync::mpsc::{Receiver}; -use std::time::Duration; use sovtoken::ErrorCode; use sovtoken::utils::ffi_support; use sovtoken::utils::test::callbacks; +use sovtoken::utils::results::ResultHandler; + +mod utils; -pub mod utils; -use utils::payment::fees; use utils::setup::{Setup, SetupConfig}; use utils::wallet::Wallet; +use utils::payment::fees; // ***** HELPER METHODS ***** -extern "C" fn empty_create_payment_callback(_command_handle: i32, _err: i32, _mint_req_json: *const c_char) -> i32 { - return ErrorCode::Success as i32; -} +fn build_set_fees(wallet_handle: i32, did: Option<&str>, fees_json: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); -// ***** HELPER TEST DATA ***** + let did = did.map(ffi_support::c_pointer_from_str).unwrap_or(std::ptr::null()); + let fees = ffi_support::c_pointer_from_str(fees_json); -const COMMAND_HANDLE:i32 = 10; -const WALLET_ID:i32 = 10; -static INVALID_OUTPUT_JSON: &'static str = r#"{"totally" : "Not a Number", "bobby" : "DROP ALL TABLES"}"#; -const CB : Option i32 > = Some(empty_create_payment_callback); -static FAKE_DID : &'static str = "Enfru5LNlA2CnA5n4Hfze"; + let ec = sovtoken::api::build_set_txn_fees_handler(command_handle, wallet_handle, did, fees, cb); + return ResultHandler::one(ErrorCode::from(ec), receiver); +} -fn call_set_fees(did: &str, fees_json: serde_json::value::Value) -> (ErrorCode, Receiver<(ErrorCode, String)>) { +fn build_get_fees(wallet_handle: i32, did: Option<&str>) -> Result { let (receiver, command_handle, cb) = callbacks::cb_ec_string(); - let did_pointer = ffi_support::c_pointer_from_str(did); - let fees_pointer = ffi_support::c_pointer_from_string(fees_json.to_string()); + let did = did.map(ffi_support::c_pointer_from_str).unwrap_or(std::ptr::null()); - let ec = sovtoken::api::build_set_txn_fees_handler(command_handle, WALLET_ID, did_pointer, fees_pointer, cb); + let ec = sovtoken::api::build_get_txn_fees_handler(command_handle, wallet_handle, did, cb); - return (ErrorCode::from(ec), receiver); + return ResultHandler::one(ErrorCode::from(ec), receiver); } +// ***** HELPER TEST DATA ***** +const COMMAND_HANDLE: i32 = 10; +const WALLET_ID: i32 = 10; +static INVALID_OUTPUT_JSON: &'static str = r#"{"totally" : "Not a Number", "bobby" : "DROP ALL TABLES"}"#; +const CB: Option i32> = Some(utils::callbacks::empty_callback); +static FAKE_DID: &'static str = "Enfru5LNlA2CnA5n4Hfze"; + + // the build_fees_txn_handler requires a callback and this test ensures that we // receive an error when no callback is provided #[test] -fn errors_with_no_call_back() { - let return_error = sovtoken::api::build_set_txn_fees_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(),ptr::null(), None); +fn add_fees_errors_with_no_call_back() { + let return_error = sovtoken::api::build_set_txn_fees_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), ptr::null(), None); assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting Callback for 'build_fees_txn_handler'"); } // the build fees txn handler method requires an outputs_json parameter and this test ensures that // a error is returned when no config is provided #[test] -fn errors_with_no_fees_json() { - let return_error = sovtoken::api::build_set_txn_fees_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(),ptr::null(), CB); +fn add_fees_errors_with_no_fees_json() { + let return_error = sovtoken::api::build_set_txn_fees_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), ptr::null(), CB); assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting outputs_json for 'build_fees_txn_handler'"); } #[test] -fn errors_with_invalid_fees_json() { +fn add_fees_errors_with_invalid_fees_json() { let fees_str = CString::new(INVALID_OUTPUT_JSON).unwrap(); let fees_str_ptr = fees_str.as_ptr(); let return_error = sovtoken::api::build_set_txn_fees_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), fees_str_ptr, CB); @@ -72,22 +77,26 @@ fn errors_with_invalid_fees_json() { } #[test] -fn add_fees_invalid_json_key() { +fn add_fees_invalid_did() { let fees = json!({ "1000": 4, - "XFER_PUBLIC": 13, - }); + "20001": 13, + }).to_string(); - let (ec, receiver) = call_set_fees(FAKE_DID, fees); - let received = receiver.recv_timeout(Duration::from_millis(300)); + let err = build_set_fees(WALLET_ID, Some(FAKE_DID), &fees).unwrap_err(); + assert_eq!(ErrorCode::CommonInvalidStructure, err); +} - assert_eq!(ErrorCode::CommonInvalidStructure, ec); - assert!(received.is_err()); +#[test] +fn add_fees_invalid_fees() { + let fees = "1000"; + let did = bs58::encode("1234567890123456").into_string(); + let err = build_set_fees(WALLET_ID, Some(&did), fees).unwrap_err(); + assert_eq!(ErrorCode::CommonInvalidStructure, err); } #[test] fn add_fees_json() { - sovtoken::api::sovtoken_init(); let fees = json!({ "3": 6, "20001": 12 @@ -98,19 +107,31 @@ fn add_fees_json() { }); let did = bs58::encode("1234567890123456").into_string(); - let (ec_initial, receiver) = call_set_fees(&did, fees); - let (ec_callback, fees_request) = receiver.recv().unwrap(); + let fees_request = build_set_fees(WALLET_ID, Some(&did), &fees.to_string()).unwrap(); let request_value: serde_json::value::Value = serde_json::from_str(&fees_request).unwrap(); - assert_eq!(ErrorCode::Success, ec_initial); - assert_eq!(ErrorCode::Success, ec_callback); + assert_eq!(&expected_operation, request_value.get("operation").unwrap()); +} + +#[test] +fn build_get_fees_req() { + let expected_operation = json!({ + "type": "20001", + }); + + let did = bs58::encode("1234567890123456").into_string(); + let get_fees_request = build_get_fees(WALLET_ID, Some(&did)).unwrap(); + + let request_value: serde_json::value::Value = serde_json::from_str(&get_fees_request).unwrap(); + assert_eq!(&expected_operation, request_value.get("operation").unwrap()); } #[test] fn add_fees_json_for_any_key() { sovtoken::api::sovtoken_init(); + let wallet = Wallet::new(); let fees = json!({ "3": 6, "TXN_ALIAS": 12, @@ -122,13 +143,9 @@ fn add_fees_json_for_any_key() { }); let did = bs58::encode("1234567890123456").into_string(); - let (ec_initial, receiver) = call_set_fees(&did, fees); - let (ec_callback, fees_request) = receiver.recv().unwrap(); + let fees_request = build_set_fees(wallet.handle, Some(&did), &fees.to_string()).unwrap(); let request_value: serde_json::value::Value = serde_json::from_str(&fees_request).unwrap(); - - assert_eq!(ErrorCode::Success, ec_initial); - assert_eq!(ErrorCode::Success, ec_callback); assert_eq!(&expected_operation, request_value.get("operation").unwrap()); } @@ -159,7 +176,7 @@ pub fn build_and_submit_set_fees() { assert_eq!(current_fees_value["100"].as_u64().unwrap(), 1); let fees = json!({ - "202": 0, + "100": 0, "101": 0 }).to_string(); @@ -198,11 +215,10 @@ pub fn build_and_submit_set_fees_with_names() { }).to_string(); fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, Some(dids[0])); - } #[test] -pub fn build_and_submit_set_fees_with_empty_did() { +pub fn build_and_submit_get_fees_with_empty_did() { let payment_method = sovtoken::utils::constants::general::PAYMENT_METHOD_NAME; let wallet = Wallet::new(); let setup = Setup::new(&wallet, SetupConfig { @@ -233,5 +249,4 @@ pub fn build_and_submit_set_fees_with_empty_did() { }).to_string(); fees::set_fees(pool_handle, wallet.handle, &payment_method, &fees, &dids, Some(dids[0])); - } \ No newline at end of file diff --git a/libsovtoken/tests/build_get_utxo_request_handler_test.rs b/libsovtoken/tests/build_get_utxo_request_handler_test.rs index 2436048a9..e489c6521 100644 --- a/libsovtoken/tests/build_get_utxo_request_handler_test.rs +++ b/libsovtoken/tests/build_get_utxo_request_handler_test.rs @@ -3,15 +3,89 @@ #[macro_use] extern crate lazy_static; extern crate sovtoken; extern crate indyrs as indy; +extern crate libc; +extern crate bs58; + +use libc::c_char; +use std::ptr; use indy::future::Future; -mod utils; +pub mod utils; + use utils::wallet::Wallet; use utils::setup::{Setup, SetupConfig}; use sovtoken::logic::address::strip_qualifier_from_address; use sovtoken::logic::address::verkey_from_unqualified_address; +use sovtoken::utils::results::ResultHandler; +use sovtoken::utils::test::callbacks; +use sovtoken::utils::ffi_support::c_pointer_from_str; +use sovtoken::{ErrorCode, IndyHandle}; + +// ***** HELPER METHODS ***** +fn build_get_payment_sources_request(wallet_handle: IndyHandle, did: &str, payment_address: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let error_code = sovtoken::api::build_get_utxo_request_handler( + command_handle, + wallet_handle, + c_pointer_from_str(did), + c_pointer_from_str(payment_address), + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} + +fn parse_get_payment_sources_response(res: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let error_code = sovtoken::api::parse_get_utxo_response_handler( + command_handle, + c_pointer_from_str(res), + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} + +// ***** HELPER TEST DATA ***** +const COMMAND_HANDLE: i32 = 10; +const WALLET_ID: i32 = 10; +const CB: Option i32> = Some(utils::callbacks::empty_callback); +const ADDRESS: &str = "pay:sov:dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q"; + +// the build_fees_txn_handler requires a callback and this test ensures that we +// receive an error when no callback is provided +#[test] +fn get_utxo_errors_with_no_call_back() { + let return_error = sovtoken::api::build_get_utxo_request_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), ptr::null(), None); + assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting Callback for 'build_get_utxo_request_handler'"); +} + +// the build fees txn handler method requires an outputs_json parameter and this test ensures that +// a error is returned when no config is provided +#[test] +fn get_utxo_errors_with_no_payment_address() { + let return_error = sovtoken::api::build_get_utxo_request_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), ptr::null(), CB); + assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting outputs_json for 'build_fees_txn_handler'"); +} + +#[test] +fn build_get_utxo_json() { + let did = bs58::encode("1234567890123456").into_string(); + let expected_operation = json!({ + "type": "10002", + "address": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q" + }); + + let request = build_get_payment_sources_request(WALLET_ID, &did, &ADDRESS).unwrap(); + + let request_value: serde_json::value::Value = serde_json::from_str(&request).unwrap(); + + assert_eq!(&expected_operation, request_value.get("operation").unwrap()); +} #[test] pub fn build_and_submit_get_utxo_request() { @@ -27,9 +101,9 @@ pub fn build_and_submit_get_utxo_request() { let pool_handle = setup.pool_handle; let dids = setup.trustees.dids(); - let (get_utxo_req, payment_method) = indy::payments::build_get_payment_sources_request(wallet.handle, Some(dids[0]), &payment_addresses[0]).wait().unwrap(); + let get_utxo_req = build_get_payment_sources_request(wallet.handle, dids[0], &payment_addresses[0]).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_get_payment_sources_response(&payment_method, &res).wait().unwrap(); + let res = parse_get_payment_sources_response(&res).unwrap(); let res_parsed: Vec = serde_json::from_str(&res).unwrap(); assert_eq!(res_parsed.len(), 1); @@ -52,9 +126,9 @@ pub fn build_and_submit_get_utxo_request_no_utxo() { let pool_handle = setup.pool_handle; let dids = setup.trustees.dids(); - let (get_utxo_req, payment_method) = indy::payments::build_get_payment_sources_request(wallet.handle, Some(dids[0]), &payment_addresses[0]).wait().unwrap(); + let get_utxo_req = build_get_payment_sources_request(wallet.handle, dids[0], &payment_addresses[0]).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_get_payment_sources_response(&payment_method, &res).wait().unwrap(); + let res = parse_get_payment_sources_response(&res).unwrap(); let res_parsed: Vec = serde_json::from_str(&res).unwrap(); assert_eq!(res_parsed.len(), 0); @@ -73,8 +147,8 @@ pub fn payment_address_is_identifier() { let payment_addresses = &setup.addresses; let dids = setup.trustees.dids(); - let (get_utxo_req, _) = indy::payments::build_get_payment_sources_request(wallet.handle, Some(dids[0]), &payment_addresses[0]).wait().unwrap(); - let req : serde_json::Value = serde_json::from_str(&get_utxo_req).unwrap(); + let get_utxo_req = build_get_payment_sources_request(wallet.handle, dids[0], &payment_addresses[0]).unwrap(); + let req: serde_json::Value = serde_json::from_str(&get_utxo_req).unwrap(); let identifier = req.as_object().unwrap().get("identifier").unwrap().as_str().unwrap(); let unqualified_addr = strip_qualifier_from_address(&payment_addresses[0]); let unqualified_addr = verkey_from_unqualified_address(&unqualified_addr).unwrap(); diff --git a/libsovtoken/tests/build_mint_txn_handler_test.rs b/libsovtoken/tests/build_mint_txn_handler_test.rs index 63041f612..581e9ef2a 100644 --- a/libsovtoken/tests/build_mint_txn_handler_test.rs +++ b/libsovtoken/tests/build_mint_txn_handler_test.rs @@ -17,7 +17,7 @@ use std::ffi::CString; use indy::future::Future; -use sovtoken::ErrorCode; +use sovtoken::{ErrorCode, IndyHandle}; use sovtoken::utils::ffi_support::{str_from_char_ptr, c_pointer_from_str}; use sovtoken::utils::constants::txn_types::MINT_PUBLIC; use sovtoken::utils::constants::txn_fields::OUTPUTS; @@ -25,6 +25,8 @@ use sovtoken::logic::parsers::common::ResponseOperations; use sovtoken::utils::json_conversion::JsonDeserialize; use sovtoken::logic::config::output_mint_config::MintRequest; use sovtoken::logic::request::Request; +use sovtoken::utils::results::ResultHandler; +use sovtoken::utils::test::callbacks; mod utils; @@ -33,10 +35,27 @@ use utils::parse_mint_response::ParseMintResponse; use utils::setup::{Setup, SetupConfig}; // ***** HELPER METHODS ***** +fn build_mint_req(wallet_handle: IndyHandle, did: Option<&str>, outputs: &str, extra: Option<&str>) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let did = did.map(c_pointer_from_str).unwrap_or(std::ptr::null()); + let extra = extra.map(c_pointer_from_str).unwrap_or(std::ptr::null()); + + let error_code = sovtoken::api::build_mint_txn_handler( + command_handle, + wallet_handle, + did, + c_pointer_from_str(outputs), + extra, + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} // ***** HELPER TEST DATA ***** -const COMMAND_HANDLE:i32 = 10; +const COMMAND_HANDLE: i32 = 10; static INVALID_OUTPUT_JSON: &'static str = r#"{"totally" : "Not a Number", "bobby" : "DROP ALL TABLES"}"#; static VALID_OUTPUT_JSON: &'static str = r#"[{"recipient":"pay:sov:dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", "amount":10}]"#; @@ -63,7 +82,7 @@ fn errors_with_no_outputs_json() { let return_error = sovtoken::api::build_mint_txn_handler(COMMAND_HANDLE, 1, ptr::null(), ptr::null(), ptr::null(), Some(cb_no_json)); assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting outputs_json for 'build_mint_txn_handler'"); - unsafe { assert!(! CALLBACK_CALLED) } + unsafe { assert!(!CALLBACK_CALLED) } } // // the mint txn handler method requires a valid JSON format (format is described @@ -81,11 +100,11 @@ fn errors_with_invalid_outputs_json() { let outputs_str_ptr = outputs_str.as_ptr(); let return_error = sovtoken::api::build_mint_txn_handler(COMMAND_HANDLE, 1, ptr::null(), outputs_str_ptr, ptr::null(), Some(cb_invalid_json)); assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting Valid JSON for 'build_mint_txn_handler'"); - unsafe { assert!(! CALLBACK_CALLED) } + unsafe { assert!(!CALLBACK_CALLED) } } #[test] -fn valid_output_json() { +fn valid_output_json() { sovtoken::api::sovtoken_init(); static mut CALLBACK_CALLED: bool = false; extern "C" fn valid_output_json_cb(command_handle: i32, error_code: i32, mint_request: *const c_char) -> i32 { @@ -93,7 +112,7 @@ fn valid_output_json() { assert_eq!(command_handle, COMMAND_HANDLE); assert_eq!(error_code, ErrorCode::Success as i32); let mint_request_json_string = str_from_char_ptr(mint_request).unwrap(); - let mint_request_json_value : serde_json::Value = serde_json::from_str(mint_request_json_string).unwrap(); + let mint_request_json_value: serde_json::Value = serde_json::from_str(mint_request_json_string).unwrap(); let mint_operation = mint_request_json_value .get("operation") .unwrap(); @@ -122,47 +141,13 @@ fn valid_output_json() { ptr::null(), Some(valid_output_json_cb) ); - + assert_eq!(return_error, ErrorCode::Success as i32, "Expecting Valid JSON for 'build_mint_txn_handler'"); unsafe { assert!(CALLBACK_CALLED); } } -#[test] // TODO: look carefully on changes -fn valid_output_json_from_libindy() { - sovtoken::api::sovtoken_init(); - let did = "Th7MpTaRZVRYnPiabds81Y"; - let wallet = Wallet::new(); - let outputs_str = VALID_OUTPUT_JSON; - - let (req, payment_method) = indy::payments::build_mint_req( - wallet.handle, - Some(did), - outputs_str, - None, - ).wait().unwrap(); - - let mint_request_json_value : serde_json::Value = serde_json::from_str(&req).unwrap(); - let mint_operation = mint_request_json_value - .get("operation") - .unwrap(); - - let expected = json!({ - "type": MINT_PUBLIC, - OUTPUTS: [ - { - "address": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "amount": 10 - } - ] - }); - - - assert_eq!("sov", payment_method); - assert_eq!(mint_operation, &expected); -} - #[test] pub fn build_and_submit_mint_txn_works() { let wallet = Wallet::new(); @@ -193,12 +178,12 @@ pub fn build_and_submit_mint_txn_works() { } ]).to_string(); - let (mint_req, _) = indy::payments::build_mint_req( + let mint_req = build_mint_req( wallet.handle, Some(dids[0]), &output_json, None, - ).wait().unwrap(); + ).unwrap(); trace!("{:?}", &mint_req); @@ -248,12 +233,12 @@ pub fn build_and_submit_mint_txn_works_with_empty_did() { } ]).to_string(); - let (mint_req, _) = indy::payments::build_mint_req( + let mint_req = build_mint_req( wallet.handle, Some(&dids[0]), &output_json, None, - ).wait().unwrap(); + ).unwrap(); trace!("{:?}", &mint_req); @@ -303,12 +288,12 @@ pub fn build_and_submit_mint_txn_works_for_double_send_mint() { } ]).to_string(); - let (mint_req, _) = indy::payments::build_mint_req( + let mint_req = build_mint_req( wallet.handle, Some(dids[0]), &output_json, None - ).wait().unwrap(); + ).unwrap(); trace!("{:?}", &mint_req); @@ -355,12 +340,12 @@ fn mint_10_billion_tokens() { "amount": tokens, }]).to_string(); - let (mint_req, _) = indy::payments::build_mint_req( + let mint_req = build_mint_req( wallet.handle, Some(dids[0]), &output_json, None - ).wait().unwrap(); + ).unwrap(); trace!("{:?}", &mint_req); diff --git a/libsovtoken/tests/build_payment_req_handler_test.rs b/libsovtoken/tests/build_payment_req_handler_test.rs index 16aff400a..8f4b7d651 100644 --- a/libsovtoken/tests/build_payment_req_handler_test.rs +++ b/libsovtoken/tests/build_payment_req_handler_test.rs @@ -16,10 +16,10 @@ use indy::future::Future; use sovtoken::logic::address; use sovtoken::logic::parsers::common::TXO; -use sovtoken::ErrorCode; +use sovtoken::{ErrorCode, IndyHandle}; use sovtoken::utils::constants::txn_types::XFER_PUBLIC; use sovtoken::utils::results::ResultHandler; -use sovtoken::utils::ffi_support::c_pointer_from_string; +use sovtoken::utils::ffi_support::{c_pointer_from_string, c_pointer_from_str}; use sovtoken::utils::test::callbacks; mod utils; @@ -27,19 +27,44 @@ use utils::wallet::Wallet; use utils::setup::{SetupConfig, Setup}; -// ***** HELPER METHODS ***** -extern "C" fn empty_create_payment_callback(_command_handle_: i32, _err: i32, _payment_req: *const c_char) -> i32 { - return ErrorCode::Success as i32; -} - // ***** HELPER TEST DATA ***** const COMMAND_HANDLE:i32 = 10; static INVALID_OUTPUT_JSON: &'static str = r#"{"totally" : "Not a Number", "bobby" : "DROP ALL TABLES"}"#; static VALID_OUTPUT_JSON: &'static str = r#"{"outputs":[["AesjahdahudgaiuNotARealAKeyygigfuigraiudgfasfhja",10]]}"#; const WALLET_HANDLE:i32 = 0; -const CB : Option i32 > = Some(empty_create_payment_callback); +const CB : Option i32 > = Some(utils::callbacks::empty_callback); + +// ***** HELPER METHODS ***** +fn build_payment_req(wallet_handle: IndyHandle, did: &str, inputs: &str, outputs: &str, extra: Option) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let extra = extra.map(c_pointer_from_string).unwrap_or(std::ptr::null()); + + let error_code = sovtoken::api::build_payment_req_handler( + command_handle, + wallet_handle, + c_pointer_from_str(did), + c_pointer_from_str(inputs), + c_pointer_from_str(outputs), + extra, + cb + ); + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} + +fn parse_payment_response(response: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let error_code = sovtoken::api::parse_payment_response_handler( + command_handle, + c_pointer_from_str(response), + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} fn generate_payment_addresses(wallet: &Wallet) -> (Vec, Vec) { let seeds = vec![ @@ -65,11 +90,10 @@ fn generate_payment_addresses(wallet: &Wallet) -> (Vec, Vec) { } fn get_resp_for_payment_req(pool_handle: i32, wallet_handle: i32, did: &str, - inputs: &str, outputs: &str) -> Result { - let (req, method) = indy::payments::build_payment_req(wallet_handle, - Some(did), inputs, outputs, None).wait().unwrap(); + inputs: &str, outputs: &str) -> Result { + let req = build_payment_req(wallet_handle, did, inputs, outputs, None).unwrap(); let res = indy::ledger::submit_request(pool_handle, &req).wait().unwrap(); - indy::payments::parse_payment_response(&method, &res).wait() + parse_payment_response(&res) } // ***** UNIT TESTS **** @@ -181,87 +205,15 @@ fn success_signed_request() { ] }); - let (receiver, command_handle, cb) = callbacks::cb_ec_string(); - trace!("Calling build_payment_req"); - let error_code = sovtoken::api::build_payment_req_handler( - command_handle, + let request_string = build_payment_req( wallet.handle, - c_pointer_from_string(did.clone()), - c_pointer_from_string(inputs.to_string()), - c_pointer_from_string(outputs.to_string()), - ptr::null(), - cb - ); - - assert_eq!(ErrorCode::from(error_code), ErrorCode::Success); - - let request_string = ResultHandler::one(ErrorCode::Success, receiver).unwrap(); - - let request: serde_json::value::Value = serde_json::from_str(&request_string).unwrap(); - debug!("Received request {:?}", request); - - assert_eq!(&expected_operation, request.get("operation").unwrap()); - assert_eq!(&did, request.get("identifier").unwrap().as_str().unwrap()); - assert!(request.get("reqId").is_some()); -} - -#[test] // TODO: look carefully on changes -fn success_signed_request_from_libindy() { - - sovtoken::api::sovtoken_init(); - - let did = String::from("Th7MpTaRZVRYnPiabds81Y"); - - let wallet = Wallet::new(); - debug!("wallet id = {:?}", wallet.handle); - - let (payment_addresses, addresses) = generate_payment_addresses(&wallet); - - let txo_1 = TXO { address: payment_addresses[0].clone(), seq_no: 1 }.to_libindy_string().unwrap(); - let txo_2 = TXO { address: payment_addresses[1].clone(), seq_no: 1 }.to_libindy_string().unwrap(); - - let inputs = json!([ - txo_1, txo_2 - ]); - - let outputs = json!([ - { - "recipient": payment_addresses[2], - "amount": 10 - }, - { - "recipient": payment_addresses[3], - "amount": 22 - } - ]); - - let expected_operation = json!({ - "type": XFER_PUBLIC, - "inputs": [ - {"address": addresses[0], "seqNo": 1}, - {"address": addresses[1], "seqNo": 1}, - ], - "outputs": [ - {"address": addresses[2], "amount": 10}, - {"address": addresses[3], "amount": 22}, - ], - "signatures": [ - "bnuZUPAq5jgpqvaQBzXKBQ973yCpjL1pkqJjiBtVPybpzzKGnPv3uE3VufBVZtR6hq2y55b8MSJpPFVMqskBy3m", - "4HpwuknWrSpJCs2qXEMZA1kbAsP9WxJFaoHq1cH7W3yxLg5R2fHV8QPdY5Hz2bgDmGkRitLaPa3HbF65kTxNpNTe" - ] - }); - - trace!("Calling build_payment_req"); - - let (request_string, _) = indy::payments::build_payment_req( - wallet.handle, - Some(&did), + &did, &inputs.to_string(), &outputs.to_string(), - None, - ).wait().unwrap(); + None + ).unwrap(); let request: serde_json::value::Value = serde_json::from_str(&request_string).unwrap(); debug!("Received request {:?}", request); @@ -334,7 +286,6 @@ fn success_signed_request_from_libindy_no_identifier() { let ident = bs58::encode(ident).into_string(); assert_eq!(&ident, request.get("identifier").unwrap().as_str().unwrap()); assert!(request.get("reqId").is_some()); - } #[test] @@ -423,7 +374,7 @@ pub fn build_and_submit_payment_req_incorrect_funds() { ]).to_string(); let res = get_resp_for_payment_req(pool_handle, wallet.handle, dids[0], &inputs, &outputs_1).unwrap_err(); - assert_eq!(res.error_code, ErrorCode::PaymentInsufficientFundsError); + assert_eq!(res, ErrorCode::PaymentInsufficientFundsError); let outputs_2 = json!([ { @@ -437,7 +388,7 @@ pub fn build_and_submit_payment_req_incorrect_funds() { ]).to_string(); let res = get_resp_for_payment_req(pool_handle, wallet.handle, dids[0], &inputs, &outputs_2).unwrap_err(); - assert_eq!(res.error_code, ErrorCode::PaymentExtraFundsError); + assert_eq!(res, ErrorCode::PaymentExtraFundsError); } #[test] @@ -473,7 +424,7 @@ pub fn build_and_submit_payment_req_with_spent_utxo() { "amount": 20 }]).to_string(); let err = get_resp_for_payment_req(pool_handle, wallet.handle, dids[0], &inputs, &outputs).unwrap_err(); - assert_eq!(err.error_code, ErrorCode::PaymentSourceDoesNotExistError); + assert_eq!(err, ErrorCode::PaymentSourceDoesNotExistError); //utxo should stay unspent! let utxos = utils::payment::get_utxo::send_get_utxo_request(&wallet, pool_handle, dids[0], &addresses[0]); @@ -497,8 +448,8 @@ pub fn build_payment_with_invalid_utxo() { } ]).to_string(); - let err = indy::payments::build_payment_req(wallet.handle, Some(&did), &inputs, &outputs, None).wait().unwrap_err(); - assert_eq!(err.error_code, ErrorCode::CommonInvalidStructure); + let err = build_payment_req(wallet.handle, &did, &inputs, &outputs, None).unwrap_err(); + assert_eq!(err, ErrorCode::CommonInvalidStructure); } pub fn build_payment_req_for_not_owned_payment_address() { diff --git a/libsovtoken/tests/build_verify_req_test.rs b/libsovtoken/tests/build_verify_req_test.rs index 7744e0c9e..7e92cb498 100644 --- a/libsovtoken/tests/build_verify_req_test.rs +++ b/libsovtoken/tests/build_verify_req_test.rs @@ -8,19 +8,73 @@ use std::{thread, time}; use indy::future::Future; -use sovtoken::ErrorCode; -use sovtoken::logic::parsers::common::TXO; - mod utils; + +use sovtoken::{ErrorCode, IndyHandle}; +use sovtoken::logic::parsers::common::TXO; +use sovtoken::utils::results::ResultHandler; +use sovtoken::utils::test::callbacks; +use sovtoken::utils::ffi_support::c_pointer_from_str; use utils::wallet::Wallet; -use utils::setup::{Setup, SetupConfig}; +use utils::setup::{Setup, SetupConfig}; fn sleep(msec: u64) { let ms = time::Duration::from_millis(msec); thread::sleep(ms); } +fn build_verify_payment_req(wallet_handle: IndyHandle, did: Option<&str>, txo: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let did = did.map(c_pointer_from_str).unwrap_or(std::ptr::null()); + + let error_code = sovtoken::api::build_verify_req_handler( + command_handle, + wallet_handle, + did, + c_pointer_from_str(txo), + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} + +fn parse_verify_payment_response(response: &str) -> Result { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let error_code = sovtoken::api::parse_verify_response_handler( + command_handle, + c_pointer_from_str(response), + cb + ); + + return ResultHandler::one(ErrorCode::from(error_code), receiver); +} + +#[test] +fn build_verify_payment_request() { + let txo = "txo:sov:3x42qH8UkJac1BuorqjSEvuVjvYkXk8sUAqoVPn1fGCwjLPquu4CndzBHBQ5hX6RSmDVnXGdMPrnWDUN5S1ty4YQP87hW8ubMSzu9M56z1FbAQV6aMSX5h"; + let expected_operation = json!({ + "type": "3", + "ledgerId": 1001, + "data": 28 + }); + + let request = build_verify_payment_req(1, None, txo).unwrap(); + + let request_value: serde_json::value::Value = serde_json::from_str(&request).unwrap(); + + assert_eq!(&expected_operation, request_value.get("operation").unwrap()); +} + +#[test] +fn build_verify_payment_for_invalid_txo() { + let txo = "txo:sov:3x42qH8"; + let res = build_verify_payment_req(1, None, txo).unwrap_err(); + assert_eq!(ErrorCode::CommonInvalidStructure, res); +} + #[test] pub fn build_and_submit_verify_on_mint() { let wallet = Wallet::new(); @@ -39,9 +93,9 @@ pub fn build_and_submit_verify_on_mint() { //We need to wait a little before trying to verify txn sleep(1000); - let (get_utxo_req, payment_method) = indy::payments::build_verify_payment_req(wallet.handle, Some(dids[0]), &txo).wait().unwrap(); + let get_utxo_req = build_verify_payment_req(wallet.handle, Some(dids[0]), &txo).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_verify_payment_response(&payment_method, &res).wait().unwrap(); + let res = parse_verify_payment_response(&res).unwrap(); let res_parsed: serde_json::Value = serde_json::from_str(&res).unwrap(); assert!(res_parsed.as_object().unwrap().get("sources").unwrap().as_array().unwrap().is_empty()); @@ -66,9 +120,9 @@ pub fn build_and_submit_verify_on_mint_with_empty_did() { //We need to wait a little before trying to verify txn sleep(1000); - let (get_utxo_req, payment_method) = indy::payments::build_verify_payment_req(wallet.handle, None, &txo).wait().unwrap(); + let get_utxo_req = build_verify_payment_req(wallet.handle, None, &txo).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_verify_payment_response(&payment_method, &res).wait().unwrap(); + let res = parse_verify_payment_response(&res).unwrap(); let res_parsed: serde_json::Value = serde_json::from_str(&res).unwrap(); assert!(res_parsed.as_object().unwrap().get("sources").unwrap().as_array().unwrap().is_empty()); @@ -109,9 +163,9 @@ pub fn build_and_submit_verify_on_xfer() { //We need to wait a little before trying to verify txn sleep(1000); - let (get_utxo_req, payment_method) = indy::payments::build_verify_payment_req(wallet.handle, Some(dids[0]), &new_utxo).wait().unwrap(); + let get_utxo_req = build_verify_payment_req(wallet.handle, Some(dids[0]), &new_utxo).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_verify_payment_response(&payment_method, &res).wait().unwrap(); + let res = parse_verify_payment_response(&res).unwrap(); let res_parsed: serde_json::Value = serde_json::from_str(&res).unwrap(); assert_eq!(res_parsed.as_object().unwrap().get("sources").unwrap().as_array().unwrap().get(0).unwrap().as_str().unwrap(), txo); @@ -157,9 +211,9 @@ pub fn build_and_submit_verify_on_fees() { //We need to wait a little before trying to verify txn sleep(1000); - let (get_utxo_req, payment_method) = indy::payments::build_verify_payment_req(wallet.handle, Some(dids[0]), &new_utxo).wait().unwrap(); + let get_utxo_req = build_verify_payment_req(wallet.handle, Some(dids[0]), &new_utxo).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let res = indy::payments::parse_verify_payment_response(&payment_method, &res).wait().unwrap(); + let res = parse_verify_payment_response(&res).unwrap(); let res_parsed: serde_json::Value = serde_json::from_str(&res).unwrap(); assert_eq!(res_parsed.as_object().unwrap().get("sources").unwrap().as_array().unwrap().get(0).unwrap().as_str().unwrap(), txo); @@ -185,11 +239,11 @@ pub fn build_and_submit_verify_req_for_unexistant_utxo() { //We need to wait a little before trying to verify txn sleep(1000); - let (get_utxo_req, payment_method) = indy::payments::build_verify_payment_req(wallet.handle, Some(dids[0]), &txo).wait().unwrap(); + let get_utxo_req = build_verify_payment_req(wallet.handle, Some(dids[0]), &txo).unwrap(); let res = indy::ledger::sign_and_submit_request(pool_handle, wallet.handle, dids[0], &get_utxo_req).wait().unwrap(); - let err = indy::payments::parse_verify_payment_response(&payment_method, &res).wait().unwrap_err(); + let err = parse_verify_payment_response(&res).unwrap_err(); - assert_eq!(err.error_code, ErrorCode::PaymentSourceDoesNotExistError); + assert_eq!(err, ErrorCode::PaymentSourceDoesNotExistError); } #[test] @@ -200,7 +254,7 @@ fn build_verify_req_works_for_invalid_utxo() { let receipt = "txo:sov:1234"; - let err = indy::payments::build_verify_payment_req(wallet.handle, Some(&did), receipt).wait().unwrap_err(); + let err = build_verify_payment_req(wallet.handle, Some(&did), receipt).unwrap_err(); - assert_eq!(err.error_code, ErrorCode::CommonInvalidStructure) + assert_eq!(err, ErrorCode::CommonInvalidStructure) } \ No newline at end of file diff --git a/libsovtoken/tests/create_payment_addres_handler_tests.rs b/libsovtoken/tests/create_payment_addres_handler_tests.rs new file mode 100644 index 000000000..b748c814c --- /dev/null +++ b/libsovtoken/tests/create_payment_addres_handler_tests.rs @@ -0,0 +1,123 @@ +//! +//! tests for Payment related functions + + +extern crate bs58; +extern crate libc; + +#[macro_use] extern crate log; +#[macro_use] extern crate serde_json; +#[macro_use] extern crate serde_derive; +#[macro_use] extern crate lazy_static; + +extern crate indyrs as indy; // lib-sdk project +extern crate sovtoken; + +use std::ptr; +use std::ffi::CString; +use std::time::Duration; + +use sovtoken::logic::config::payment_address_config::PaymentAddressConfig; +use sovtoken::logic::address::unqualified_address_from_address; +use sovtoken::utils::test::callbacks; +use sovtoken::ErrorCode; + +mod utils; + +// ***** HELPER TEST DATA ***** +const WALLET_ID: i32 = 99; +const COMMAND_HANDLE: i32 = 1; +const TIMEOUT_SECONDS: u64 = 20; +static INVALID_CONFIG_JSON: &'static str = r#"{ "horrible" : "only on tuedays"}"#; + + +// ***** HELPER METHODS ***** + +fn create_payment_address(wallet: &utils::wallet::Wallet, config: PaymentAddressConfig) -> String { + let (receiver, command_handle, cb) = callbacks::cb_ec_string(); + + let config_str = config.serialize_to_cstring().unwrap(); + let config_str_ptr = config_str.as_ptr(); + + let return_error = sovtoken::api::create_payment_address_handler(command_handle, wallet.handle, config_str_ptr, cb); + + assert_eq!(ErrorCode::Success, ErrorCode::from(return_error), "api call to create_payment_address_handler failed"); + + let (err, payment_address) = receiver.recv_timeout(Duration::from_secs(TIMEOUT_SECONDS)).unwrap(); + + assert_eq!(ErrorCode::Success, err, "Expected Success"); + + return payment_address; +} + + +// ***** UNIT TESTS ***** + +// the create payment requires a callback and this test ensures we have +// receive error when no callback is provided +#[test] +fn errors_with_no_callback() { + let return_error = sovtoken::api::create_payment_address_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), None); + assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting Callback for 'create_payment_address_handler'"); +} + + +// the create payment method requires a config parameter and this test ensures that +// a error is returned when no config is provided +#[test] +fn errors_with_no_config() { + let return_error = sovtoken::api::create_payment_address_handler(COMMAND_HANDLE, WALLET_ID, ptr::null(), Some(utils::callbacks::empty_callback)); + assert_eq!(return_error, ErrorCode::CommonInvalidStructure as i32, "Expecting Config for 'create_payment_address_handler'"); +} + + +// the create payment method requires a valid JSON format (format is described +// in create_payment_address_handler description). When invalid json is sent, +// default empty is used instead +#[test] +fn success_with_invalid_config_json() { + let config_str = CString::new(INVALID_CONFIG_JSON).unwrap(); + let config_str_ptr = config_str.as_ptr(); + + let return_error = sovtoken::api::create_payment_address_handler(COMMAND_HANDLE, WALLET_ID, config_str_ptr, Some(utils::callbacks::empty_callback)); + + assert_eq!(return_error, ErrorCode::Success as i32, "Expecting Valid JSON for 'create_payment_address_handler'"); +} + +// this test passes valid parameters. The callback is invoked and a valid payment address +// is returned in the call back. The payment address format is described in +// create_payment_address_handler +#[test] +fn successfully_creates_payment_address_with_no_seed() { + debug!("logging started for successfully_creates_payment_address_with_no_seed"); + + let wallet = utils::wallet::Wallet::new(); + + let config: PaymentAddressConfig = PaymentAddressConfig { seed: String::new() }; + + let payment_address = create_payment_address(&wallet, config); + + debug!("******* got address of {}", payment_address); + let unqual_address = unqualified_address_from_address(&payment_address).unwrap(); + assert_eq!(bs58::decode(unqual_address).into_vec().unwrap().len(), 36); +} + +// this test passes a valid seed value for the key. The callback is invoked and an expected valid +// payment address is returned in the call back. The payment address format is described in +// create_payment_address_handler +#[test] +fn successfully_creates_payment_address_with_seed() { + trace!("logging started for successfully_creates_payment_address_with_seed"); + + let config: PaymentAddressConfig = PaymentAddressConfig { seed: "00000000000000000000000000000000".to_string() }; + + let wallet = utils::wallet::Wallet::new(); + + let payment_address = create_payment_address(&wallet, config); + + let expected_payment_address = "pay:sov:DB3eBYTCr9NvNVZNp1GwV12iDfqftoGrDKqBedRZV4SgdeTbi"; + + debug!("******* got address of {}", payment_address); + + assert_eq!(expected_payment_address, payment_address, "callback did not receive expected payment address"); +} \ No newline at end of file diff --git a/libsovtoken/tests/parse_payment_response_test.rs b/libsovtoken/tests/parse_payment_response_test.rs deleted file mode 100644 index de1bc9569..000000000 --- a/libsovtoken/tests/parse_payment_response_test.rs +++ /dev/null @@ -1,95 +0,0 @@ -extern crate indyrs as indy; -extern crate libc; -extern crate serde_json; -extern crate sovtoken; - -use indy::future::Future; - -use sovtoken::ErrorCode; - -static PARSE_PAYMENT_RESPONSE_JSON: &'static str = r#"{ - "op": "REPLY", - "protocolVersion": 2, - "result": - { - "txn": - { - "data": - { - "inputs": - [ - { - "address": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "seqNo": 1 - } - ], - "outputs": - [ - { - "address": "2jS4PHWQJKcawRxdW6GVsjnZBa1ecGdCssn7KhWYJZGTXgL7Es", - "amount": 13 - }, - { - "address": "24xHHVDRq97Hss5BxiTciEDsve7nYNx1pxAMi9RAvcWMouviSY", - "amount": 13 - }, - { - "address": "mNYFWv9vvoQVCVLrSpbU7ZScthjNJMQxMs3gREQrwcJC1DsG5", - "amount": 13 - }, - { - "address": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "amount": 1 - } - ] - }, - "metadata": - { - "digest": "228af6a0c773cbbd575bf4e16f9144c2eaa615fa81fdcc3d06b83e20a92e5989", - "from": "6baBEYA94sAphWBA5efEsaA6X2wCdyaH7PXuBtv2H5S1", - "reqId": 152968241 - }, - "protocolVersion": 2, - "type": "10001" - }, - "reqSignature": - { - "type": "ED25519", - "values": - [ - { - "from": "dctKSXBbv2My3TGGUgTFjkxu1A9JM3Sscd5FydY4dkxnfwA7q", - "value": "4fFVD1HSVLaVdMpjHU168eviqWDxKrWYx1fRxw4DDLjg4XZXwya7UdcvVty81pYFcng244tS36WbshCeznC8ZN5Z" - } - ] - }, - "txnMetadata": - { - "seqNo": 2, - "txnTime": 1529682415 - }, - "ver": "1", - "auditPath": ["5NtSQUXaZvETP1KEWi8LaxSb9gGa2Qj31xKQoimNxCAT"], - "rootHash": "GJFwiQt9r7n25PqM1oXBtRceXCeoqoCBcJmRH1c8fVTs" - } -}"#; - -#[test] -pub fn parse_payment_response_works() { - sovtoken::api::sovtoken_init(); - let resp = indy::payments::parse_payment_response("sov", PARSE_PAYMENT_RESPONSE_JSON).wait().unwrap(); - let resp: Vec = serde_json::from_str(&resp).unwrap(); - assert_eq!(resp.len(), 4); - for utxo in resp { - utxo["recipient"].as_str().unwrap(); - utxo["receipt"].as_str().unwrap(); - utxo["amount"].as_u64().unwrap(); - } -} - -#[test] -pub fn parse_payment_response_works_for_invalid() { - sovtoken::api::sovtoken_init(); - let resp = indy::payments::parse_payment_response("sov", "123").wait().unwrap_err(); - assert_eq!(resp.error_code, ErrorCode::CommonInvalidStructure); -} \ No newline at end of file diff --git a/libsovtoken/tests/utils/callbacks.rs b/libsovtoken/tests/utils/callbacks.rs new file mode 100644 index 000000000..8c577dd95 --- /dev/null +++ b/libsovtoken/tests/utils/callbacks.rs @@ -0,0 +1,9 @@ +extern crate libc; + +use self::libc::c_char; + +use sovtoken::ErrorCode; + +pub extern "C" fn empty_callback(_command_handle: i32, _err: i32, _req_json: *const c_char) -> i32 { + return ErrorCode::Success as i32; +} \ No newline at end of file diff --git a/libsovtoken/tests/utils/mod.rs b/libsovtoken/tests/utils/mod.rs index 1fa13082d..f3748a5c7 100644 --- a/libsovtoken/tests/utils/mod.rs +++ b/libsovtoken/tests/utils/mod.rs @@ -6,6 +6,7 @@ Without this, we are warned of all unused code in each integration test. */ pub mod anoncreds; +pub mod callbacks; pub mod did; pub mod environment; pub mod ledger;