diff --git a/CHANGELOG.md b/CHANGELOG.md index 875341da..27af9123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 transactions. - `Synchronizer.proposeTransfer` - `Synchronizer.proposeShielding` + - `Synchronizer.proposeFulfillingPaymentUri` - `Synchronizer.createProposedTransactions` - `WalletBalanceFixture` class with mock values that are supposed to be used only for testing purposes - `Memo.countLength(memoString: String)` to count memo length in bytes diff --git a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt index 0fef4da1..23d84dba 100644 --- a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt +++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/Backend.kt @@ -28,6 +28,11 @@ interface Backend { memo: ByteArray? = byteArrayOf() ): ProposalUnsafe + suspend fun proposeTransferFromUri( + account: Int, + uri: String + ): ProposalUnsafe + suspend fun proposeShielding( account: Int, shieldingThreshold: Long, diff --git a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt index 82f5f0bc..9b937cc3 100644 --- a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt +++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt @@ -285,6 +285,22 @@ class RustBackend private constructor( ) } + override suspend fun proposeTransferFromUri( + account: Int, + uri: String + ): ProposalUnsafe = + withContext(SdkDispatchers.DATABASE_IO) { + ProposalUnsafe.parse( + proposeTransferFromUri( + dataDbFile.absolutePath, + account, + uri, + networkId = networkId, + useZip317Fees = IS_USE_ZIP_317_FEES + ) + ) + } + override suspend fun proposeTransfer( account: Int, to: String, @@ -573,6 +589,16 @@ class RustBackend private constructor( networkId: Int ) + @JvmStatic + @Suppress("LongParameterList") + private external fun proposeTransferFromUri( + dbDataPath: String, + account: Int, + uri: String, + networkId: Int, + useZip317Fees: Boolean + ): ByteArray + @JvmStatic @Suppress("LongParameterList") private external fun proposeTransfer( diff --git a/backend-lib/src/main/rust/lib.rs b/backend-lib/src/main/rust/lib.rs index 02861c03..3082f0fb 100644 --- a/backend-lib/src/main/rust/lib.rs +++ b/backend-lib/src/main/rust/lib.rs @@ -1402,6 +1402,49 @@ fn zip317_helper( ) } +#[no_mangle] +pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_proposeTransferFromUri<'local>( + mut env: JNIEnv<'local>, + _: JClass<'local>, + db_data: JString<'local>, + account: jint, + payment_uri: JString<'local>, + network_id: jint, + use_zip317_fees: jboolean, +) -> jbyteArray { + let res = catch_unwind(&mut env, |env| { + let _span = tracing::info_span!("RustBackend.proposeTransfer").entered(); + let network = parse_network(network_id as u32)?; + let mut db_data = wallet_db(env, network, db_data)?; + let account = account_id_from_jint(account)?; + let payment_uri = utils::java_string_to_rust(env, &payment_uri); + + let input_selector = zip317_helper(None, use_zip317_fees); + + let request = TransactionRequest::from_uri(&network, &payment_uri) + .map_err(|e| format_err!("Error creating transaction request: {:?}", e))?; + + let proposal = propose_transfer::<_, _, _, Infallible>( + &mut db_data, + &network, + account, + &input_selector, + request, + ANCHOR_OFFSET, + ) + .map_err(|e| format_err!("Error creating transaction proposal: {}", e))?; + + utils::rust_bytes_to_java( + &env, + Proposal::from_standard_proposal(&network, &proposal) + .encode_to_vec() + .as_ref(), + ) + .map(|arr| arr.into_raw()) + }); + unwrap_exc_or(&mut env, res, ptr::null_mut()) +} + #[no_mangle] pub extern "C" fn Java_cash_z_ecc_android_sdk_internal_jni_RustBackend_proposeTransfer<'local>( mut env: JNIEnv<'local>, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt index 1da81a75..9e80eda1 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt @@ -559,6 +559,19 @@ class SdkSynchronizer private constructor( account ) + /** + * Creates a proposal for fulfilling a payment ZIP-321 URI + * + * @param account the account from which to transfer funds. + * @param uri a ZIP-321 compliant payment URI String + * + * @return the proposal or an exception + */ + @Throws(TransactionEncoderException::class) + override suspend fun proposeFulfillingPaymentUri( + account: Account, + uri: String + ): Proposal = txManager.proposeTransferFromUri(account, uri) @Throws(TransactionEncoderException::class) override suspend fun proposeTransfer( account: Account, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt index 4bc740f7..bdeed89b 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Synchronizer.kt @@ -186,6 +186,19 @@ interface Synchronizer { memo: String = "" ): Proposal + /** + * Creates a proposal for fulfilling a payment ZIP-321 URI + * + * @param account the account from which to transfer funds. + * @param uri a ZIP-321 compliant payment URI String + * + * @return the proposal or an exception + */ + suspend fun proposeFulfillingPaymentUri( + account: Account, + uri: String + ): Proposal + /** * Creates a proposal for shielding any transparent funds received by the given account. * diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt index 42861be8..7a5897f5 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt @@ -24,6 +24,11 @@ internal interface TypesafeBackend { recoverUntil: BlockHeight? ): UnifiedSpendingKey + suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal + @Suppress("LongParameterList") suspend fun proposeTransfer( account: Account, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt index 1eb811f3..2cae4734 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt @@ -35,6 +35,17 @@ internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBacke ) } + override suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal = + Proposal.fromUnsafe( + backend.proposeTransferFromUri( + account.value, + uri + ) + ) + @Suppress("LongParameterList") override suspend fun proposeTransfer( account: Account, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManager.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManager.kt index fcc9b3e2..dc0830cc 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManager.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManager.kt @@ -35,6 +35,19 @@ internal interface OutboundTransactionManager { account: Account ): EncodedTransaction + /** + * Creates a proposal for transferring funds from a ZIP-321 compliant payment URI + * + * @param account the account from which to transfer funds. + * @param uri a ZIP-321 compliant payment URI + * + * @return the proposal or an exception + */ + suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal + /** * Creates a proposal for transferring funds to the given recipient. * diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManagerImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManagerImpl.kt index 77404e56..513b4d12 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManagerImpl.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/OutboundTransactionManagerImpl.kt @@ -43,6 +43,18 @@ internal class OutboundTransactionManagerImpl( } } + /** + * Creates a proposal for transferring funds from a ZIP-321 compliant payment URI + * + * @param account the account from which to transfer funds. + * @param uri a ZIP-321 compliant payment URI + * + * @return the proposal or an exception + */ + override suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal = encoder.proposeTransferFromUri(account, uri) override suspend fun proposeTransfer( account: Account, recipient: String, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoder.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoder.kt index c0aa8cea..a9e5c74f 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoder.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoder.kt @@ -40,6 +40,19 @@ internal interface TransactionEncoder { memo: ByteArray? = byteArrayOf() ): EncodedTransaction + /** + * Creates a proposal for transferring from a valid ZIP-321 Payment URI string + * + * @param account the account from which to transfer funds. + * @param uri a valid ZIP-321 Payment URI string + * + * @return the proposal or an exception + */ + suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal + /** * Creates a proposal for transferring funds to the given recipient. * diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt index 33efc2b1..a800f8e8 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt @@ -68,6 +68,36 @@ internal class TransactionEncoderImpl( ?: throw TransactionEncoderException.TransactionNotFoundException(transactionId) } + /** + * Creates a proposal for transferring from a valid ZIP-321 Payment URI string + * + * @param account the account from which to transfer funds. + * @param uri a valid ZIP-321 Payment URI string + * + * @return the proposal or an exception + */ + override suspend fun proposeTransferFromUri( + account: Account, + uri: String + ): Proposal { + Twig.debug { + "creating proposal from URI: $uri" + } + + @Suppress("TooGenericExceptionCaught") + return try { + backend.proposeTransferFromUri( + account, + uri + ) + } catch (t: Throwable) { + Twig.debug(t) { "Caught exception while creating proposal from URI String." } + throw t + }.also { result -> + Twig.debug { "result of proposeTransferFromUri: $result" } + } + } + override suspend fun proposeTransfer( account: Account, recipient: String,