diff --git a/Cargo.lock b/Cargo.lock index 3119149a3..110a39c75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "account-for-display" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "derive_more", @@ -48,10 +48,10 @@ dependencies = [ [[package]] name = "addresses" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", - "bytes 1.1.101", + "bytes 1.1.102", "cap26-models", "core-utils", "derive_more", @@ -242,7 +242,7 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "assert-json" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json-diff", "error", @@ -550,7 +550,7 @@ dependencies = [ [[package]] name = "build-info" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "cargo_toml 0.15.3 (git+https://gitlab.com/lib.rs/cargo_toml?rev=e498c94fc42a660c1ca1a28999ce1d757cfe77fe)", @@ -583,7 +583,7 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "delegate", @@ -622,7 +622,7 @@ dependencies = [ [[package]] name = "cap26-models" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "derive_more", @@ -806,7 +806,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clients" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "async-trait", @@ -866,7 +866,7 @@ checksum = "0d8a42181e0652c2997ae4d217f25b63c5337a52fd2279736e97b832fa0a3cff" [[package]] name = "core-collections" -version = "1.1.101" +version = "1.1.102" dependencies = [ "has-sample-values", "indexmap 2.7.0", @@ -893,7 +893,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-misc" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "core-utils", @@ -913,7 +913,7 @@ dependencies = [ [[package]] name = "core-utils" -version = "1.1.101" +version = "1.1.102" dependencies = [ "error", "iso8601-timestamp", @@ -1139,7 +1139,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "drivers" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -1163,10 +1163,10 @@ dependencies = [ [[package]] name = "ecc" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", - "bytes 1.1.101", + "bytes 1.1.102", "derive_more", "enum-as-inner", "error", @@ -1267,11 +1267,11 @@ dependencies = [ [[package]] name = "encryption" -version = "1.1.101" +version = "1.1.102" dependencies = [ "aes-gcm", "assert-json", - "bytes 1.1.101", + "bytes 1.1.102", "derive_more", "error", "has-sample-values", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "entity-by-address" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "error", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "entity-foundation" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "derive_more", @@ -1372,11 +1372,13 @@ dependencies = [ [[package]] name = "error" -version = "1.1.101" +version = "1.1.102" dependencies = [ + "derive_more", "log", "prelude", "serde_json 1.0.108", + "strum 0.26.1", "thiserror 1.0.50", ] @@ -1429,7 +1431,7 @@ dependencies = [ [[package]] name = "factor-instances-provider" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -1455,9 +1457,9 @@ dependencies = [ [[package]] name = "factors" -version = "1.1.101" +version = "1.1.102" dependencies = [ - "bytes 1.1.101", + "bytes 1.1.102", "cap26-models", "core-collections", "core-misc", @@ -1492,7 +1494,7 @@ dependencies = [ [[package]] name = "factors-supporting-types" -version = "1.1.101" +version = "1.1.102" dependencies = [ "async-trait", "error", @@ -1673,7 +1675,7 @@ dependencies = [ [[package]] name = "gateway-client-and-api" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -1693,7 +1695,7 @@ dependencies = [ [[package]] name = "gateway-models" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "assert-json", @@ -1798,7 +1800,7 @@ dependencies = [ [[package]] name = "has-sample-values" -version = "1.1.101" +version = "1.1.102" dependencies = [ "error", "indexmap 2.7.0", @@ -1809,9 +1811,9 @@ dependencies = [ [[package]] name = "hash" -version = "1.1.101" +version = "1.1.102" dependencies = [ - "bytes 1.1.101", + "bytes 1.1.102", "derive_more", "prelude", "radix-common", @@ -1880,11 +1882,11 @@ source = "git+https://github.com/KokaKiwi/rust-hex/?rev=b2b4370b5bf021b98ee7adc9 [[package]] name = "hierarchical-deterministic" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "bip39", - "bytes 1.1.101", + "bytes 1.1.102", "cap26-models", "derive_more", "ecc", @@ -1926,13 +1928,13 @@ dependencies = [ [[package]] name = "home-cards" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", "async-trait", "base64 0.22.1 (git+https://github.com/marshallpierce/rust-base64.git?rev=e14400697453bcc85997119b874bc03d9601d0af)", - "bytes 1.1.101", + "bytes 1.1.102", "core-utils", "derive_more", "drivers", @@ -1952,7 +1954,7 @@ dependencies = [ [[package]] name = "host-info" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "derive_more", @@ -1999,10 +2001,10 @@ dependencies = [ [[package]] name = "http-client" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", - "bytes 1.1.101", + "bytes 1.1.102", "core-utils", "drivers", "error", @@ -2227,7 +2229,7 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "identified-vec-of" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "derive_more", @@ -2300,7 +2302,7 @@ dependencies = [ [[package]] name = "interactors" -version = "1.1.101" +version = "1.1.102" dependencies = [ "async-trait", "derive_more", @@ -2430,7 +2432,7 @@ dependencies = [ [[package]] name = "key-derivation-traits" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "async-trait", @@ -2448,7 +2450,7 @@ dependencies = [ [[package]] name = "keys-collector" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -2537,7 +2539,7 @@ dependencies = [ [[package]] name = "manifests" -version = "1.1.101" +version = "1.1.102" dependencies = [ "account-for-display", "addresses", @@ -2577,7 +2579,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "metadata" -version = "1.1.101" +version = "1.1.102" dependencies = [ "derive_more", "has-sample-values", @@ -2677,7 +2679,7 @@ dependencies = [ [[package]] name = "network" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", "enum-iterator", @@ -2693,7 +2695,7 @@ dependencies = [ [[package]] name = "next-derivation-index-ephemeral" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "assert-json", @@ -2769,9 +2771,9 @@ dependencies = [ [[package]] name = "numeric" -version = "1.1.101" +version = "1.1.102" dependencies = [ - "bytes 1.1.101", + "bytes 1.1.102", "delegate", "derive_more", "enum-iterator", @@ -3009,7 +3011,7 @@ dependencies = [ [[package]] name = "prelude" -version = "1.1.101" +version = "1.1.102" dependencies = [ "radix-engine", "radix-engine-toolkit", @@ -3044,7 +3046,7 @@ dependencies = [ [[package]] name = "profile" -version = "1.1.101" +version = "1.1.102" dependencies = [ "account-for-display", "addresses", @@ -3088,7 +3090,7 @@ dependencies = [ [[package]] name = "profile-account" -version = "1.1.101" +version = "1.1.102" dependencies = [ "account-for-display", "addresses", @@ -3108,7 +3110,7 @@ dependencies = [ [[package]] name = "profile-account-or-persona" -version = "1.1.101" +version = "1.1.102" dependencies = [ "cap26-models", "derive_more", @@ -3125,7 +3127,7 @@ dependencies = [ [[package]] name = "profile-app-preferences" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "core-misc", @@ -3148,7 +3150,7 @@ dependencies = [ [[package]] name = "profile-base-entity" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "derive_more", @@ -3171,7 +3173,7 @@ dependencies = [ [[package]] name = "profile-gateway" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "assert-json", @@ -3195,7 +3197,7 @@ dependencies = [ [[package]] name = "profile-logic" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "derive_more", @@ -3217,7 +3219,7 @@ dependencies = [ [[package]] name = "profile-persona" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "cap26-models", @@ -3238,7 +3240,7 @@ dependencies = [ [[package]] name = "profile-persona-data" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "assert-json", @@ -3257,7 +3259,7 @@ dependencies = [ [[package]] name = "profile-security-structures" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "cap26-models", @@ -3284,7 +3286,7 @@ dependencies = [ [[package]] name = "profile-state-holder" -version = "1.1.101" +version = "1.1.102" dependencies = [ "derive_more", "error", @@ -3299,7 +3301,7 @@ dependencies = [ [[package]] name = "profile-supporting-types" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "derive_more", @@ -3388,14 +3390,14 @@ dependencies = [ [[package]] name = "radix-connect" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", "assert-json", "async-trait", "base64 0.22.1 (git+https://github.com/marshallpierce/rust-base64.git?rev=e14400697453bcc85997119b874bc03d9601d0af)", - "bytes 1.1.101", + "bytes 1.1.102", "core-misc", "core-utils", "derive_more", @@ -3423,10 +3425,10 @@ dependencies = [ [[package]] name = "radix-connect-models" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", - "bytes 1.1.101", + "bytes 1.1.102", "core-misc", "derive_more", "error", @@ -3910,7 +3912,7 @@ dependencies = [ [[package]] name = "sargon" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -3975,7 +3977,7 @@ dependencies = [ [[package]] name = "sargon-os" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "async-trait", @@ -4002,7 +4004,7 @@ dependencies = [ [[package]] name = "sargon-os-accounts" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -4025,7 +4027,7 @@ dependencies = [ [[package]] name = "sargon-os-factors" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "async-trait", @@ -4045,7 +4047,7 @@ dependencies = [ [[package]] name = "sargon-os-security-center" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "derive_more", @@ -4061,7 +4063,7 @@ dependencies = [ [[package]] name = "sargon-os-signing" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "async-trait", @@ -4087,7 +4089,7 @@ dependencies = [ [[package]] name = "sargon-os-transaction" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "async-std", @@ -4116,7 +4118,7 @@ dependencies = [ [[package]] name = "sargon-uniffi" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", @@ -4173,7 +4175,7 @@ dependencies = [ [[package]] name = "sargon-uniffi-conversion-macros" -version = "1.1.101" +version = "1.1.102" dependencies = [ "proc-macro2", "quote", @@ -4347,7 +4349,7 @@ dependencies = [ [[package]] name = "security-center" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", "assert-json", @@ -4552,7 +4554,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "short-string" -version = "1.1.101" +version = "1.1.102" dependencies = [ "arraystring", "assert-json", @@ -4587,13 +4589,13 @@ dependencies = [ [[package]] name = "signing" -version = "1.1.101" +version = "1.1.102" dependencies = [ "actix-rt", "addresses", "assert-json", "async-trait", - "bytes 1.1.101", + "bytes 1.1.102", "cap26-models", "core-collections", "core-misc", @@ -4625,14 +4627,15 @@ dependencies = [ [[package]] name = "signing-traits" -version = "1.1.101" +version = "1.1.102" dependencies = [ "async-trait", - "bytes 1.1.101", + "bytes 1.1.102", "core-collections", "derive_more", "ecc", "entity-by-address", + "enum-as-inner", "error", "hash", "indexmap 2.7.0", @@ -4790,7 +4793,7 @@ dependencies = [ [[package]] name = "sub-systems" -version = "1.1.101" +version = "1.1.102" dependencies = [ "derive_more", "drivers", @@ -4964,7 +4967,7 @@ dependencies = [ [[package]] name = "time-utils" -version = "1.1.101" +version = "1.1.102" dependencies = [ "iso8601-timestamp", "prelude", @@ -5093,10 +5096,10 @@ dependencies = [ [[package]] name = "transaction-foundation" -version = "1.1.101" +version = "1.1.102" dependencies = [ "assert-json", - "bytes 1.1.101", + "bytes 1.1.102", "derive_more", "has-sample-values", "paste 1.0.14", @@ -5108,10 +5111,10 @@ dependencies = [ [[package]] name = "transaction-models" -version = "1.1.101" +version = "1.1.102" dependencies = [ "addresses", - "bytes 1.1.101", + "bytes 1.1.102", "cargo_toml 0.15.3 (git+https://gitlab.com/lib.rs/cargo_toml?rev=e498c94fc42a660c1ca1a28999ce1d757cfe77fe)", "core-collections", "core-misc", diff --git a/Package.resolved b/Package.resolved index f202b8c0c..af565dc0a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", - "version" : "1.1.4" + "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb", + "version" : "1.1.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-custom-dump", "state" : { - "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", - "version" : "1.3.3" + "revision" : "f01efb26f3a192a0e88dcdb7c3c391ec2fc25d9c", + "version" : "1.3.0" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "a3f634d1a409c7979cabc0a71b3f26ffa9fc8af1", - "version" : "1.4.3" + "revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2", + "version" : "1.1.2" } } ], diff --git a/apple/Sources/Sargon/SargonOS/ThrowingHostInteractor+Static+Shared.swift b/apple/Sources/Sargon/SargonOS/ThrowingHostInteractor+Static+Shared.swift index f2d9c8264..c85aa05f5 100644 --- a/apple/Sources/Sargon/SargonOS/ThrowingHostInteractor+Static+Shared.swift +++ b/apple/Sources/Sargon/SargonOS/ThrowingHostInteractor+Static+Shared.swift @@ -2,15 +2,15 @@ public class ThrowingHostInteractor: HostInteractor { public nonisolated(unsafe) static var shared: HostInteractor = ThrowingHostInteractor() - public func signAuth(request: SargonUniFFI.SignRequestOfAuthIntent) async throws -> SargonUniFFI.SignWithFactorsOutcomeOfAuthIntentHash { + public func signAuth(request: SargonUniFFI.SignRequestOfAuthIntent) async throws -> SargonUniFFI.SignResponseOfAuthIntentHash { throw CommonError.SigningRejected } - public func signTransactions(request: SargonUniFFI.SignRequestOfTransactionIntent) async throws -> SargonUniFFI.SignWithFactorsOutcomeOfTransactionIntentHash { + public func signTransactions(request: SargonUniFFI.SignRequestOfTransactionIntent) async throws -> SargonUniFFI.SignResponseOfTransactionIntentHash { throw CommonError.SigningRejected } - public func signSubintents(request: SargonUniFFI.SignRequestOfSubintent) async throws -> SargonUniFFI.SignWithFactorsOutcomeOfSubintentHash { + public func signSubintents(request: SargonUniFFI.SignRequestOfSubintent) async throws -> SargonUniFFI.SignResponseOfSubintentHash { throw CommonError.SigningRejected } diff --git a/apple/Tests/TestCases/System/ThrowingHostInteractorTests.swift b/apple/Tests/TestCases/System/ThrowingHostInteractorTests.swift new file mode 100644 index 000000000..4f6d6a7d9 --- /dev/null +++ b/apple/Tests/TestCases/System/ThrowingHostInteractorTests.swift @@ -0,0 +1,48 @@ +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class ThrowingHostInteractorTests: TestCase { + typealias SUT = ThrowingHostInteractor + + func testDeriveKeysThrows() async throws { + do { + let _ = try await SUT.shared.deriveKeys( + request: SargonUniFFI.KeyDerivationRequest(derivationPurpose: .creatingNewAccount, perFactorSource: []) + ) + } catch { + XCTAssertEqual(error as? CommonError, CommonError.SigningRejected) + } + } + + func testSignAuthThrows() async throws { + do { + let _ = try await SUT.shared.signAuth( + request: SargonUniFFI.SignRequestOfAuthIntent(factorSourceKind: .device, perFactorSource: []) + ) + } catch { + XCTAssertEqual(error as? CommonError, CommonError.SigningRejected) + } + } + + func testSignTransactionsThrows() async throws { + do { + let _ = try await SUT.shared.signTransactions( + request: SargonUniFFI.SignRequestOfTransactionIntent(factorSourceKind: .device, perFactorSource: []) + ) + } catch { + XCTAssertEqual(error as? CommonError, CommonError.SigningRejected) + } + } + + func testSignSubintentsThrows() async throws { + do { + let _ = try await SUT.shared.signSubintents( + request: SargonUniFFI.SignRequestOfSubintent(factorSourceKind: .device, perFactorSource: []) + ) + } catch { + XCTAssertEqual(error as? CommonError, CommonError.SigningRejected) + } + } +} diff --git a/crates/app/home-cards/Cargo.toml b/crates/app/home-cards/Cargo.toml index 506605245..7bf4ed1a1 100644 --- a/crates/app/home-cards/Cargo.toml +++ b/crates/app/home-cards/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "home-cards" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/app/key-derivation-traits/Cargo.toml b/crates/app/key-derivation-traits/Cargo.toml index 919be5b9a..b4d608ad5 100644 --- a/crates/app/key-derivation-traits/Cargo.toml +++ b/crates/app/key-derivation-traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "key-derivation-traits" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/app/radix-connect-models/Cargo.toml b/crates/app/radix-connect-models/Cargo.toml index 32641836c..9c25876f9 100644 --- a/crates/app/radix-connect-models/Cargo.toml +++ b/crates/app/radix-connect-models/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "radix-connect-models" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/app/radix-connect/Cargo.toml b/crates/app/radix-connect/Cargo.toml index d29e6da4c..efd175ce8 100644 --- a/crates/app/radix-connect/Cargo.toml +++ b/crates/app/radix-connect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "radix-connect" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/app/security-center/Cargo.toml b/crates/app/security-center/Cargo.toml index 792634637..aa804fe7e 100644 --- a/crates/app/security-center/Cargo.toml +++ b/crates/app/security-center/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "security-center" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/app/signing-traits/Cargo.toml b/crates/app/signing-traits/Cargo.toml index 69c0d204c..9b60088dc 100644 --- a/crates/app/signing-traits/Cargo.toml +++ b/crates/app/signing-traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "signing-traits" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] @@ -8,6 +8,7 @@ edition = "2021" prelude = { workspace = true } error = { workspace = true } ecc = { workspace = true } +enum-as-inner = { workspace = true } core-collections = { workspace = true } bytes = { workspace = true } hash = { workspace = true } diff --git a/crates/app/signing-traits/src/host_interaction/factor_outcome.rs b/crates/app/signing-traits/src/host_interaction/factor_outcome.rs new file mode 100644 index 000000000..67dabdfe8 --- /dev/null +++ b/crates/app/signing-traits/src/host_interaction/factor_outcome.rs @@ -0,0 +1,207 @@ +use crate::prelude::*; + +/// The outcome of the signing process for each factor source as collected by the `SignInteractor`. +#[derive( + Clone, PartialEq, Eq, enum_as_inner::EnumAsInner, derive_more::Debug, +)] +pub enum FactorOutcome { + /// The user successfully signed with the factor source, the associated + /// value `produced_signatures` contains the produced signatures and any relevant metadata. + #[debug("Signed: {:#?}", produced_signatures)] + Signed { + produced_signatures: IndexSet>, + }, + + /// The factor source got neglected, either due to user explicitly skipping + /// or due to failure + #[debug("Neglected")] + Neglected(NeglectedFactor), +} + +impl HasSampleValues for FactorOutcome { + fn sample() -> Self { + let signature = HDSignature::::sample(); + + Self::signed(IndexSet::just(signature)).unwrap() + } + + fn sample_other() -> Self { + Self::skipped(FactorSourceIDFromHash::sample_other()) + } +} + +impl FactorOutcome { + pub fn signed( + produced_signatures: IndexSet>, + ) -> Result { + if produced_signatures.is_empty() { + return Err(CommonError::ExpectedNonEmptyCollection); + } + + let factor_source_id = &produced_signatures + .first() + .expect("Should have at least one signature") + .factor_source_id(); + + if produced_signatures + .iter() + .any(|s| s.factor_source_id() != *factor_source_id) + { + return Err(CommonError::FactorOutcomeSignedFactorSourceIDMismatch); + } + + Ok(FactorOutcome::Signed { + produced_signatures, + }) + } + + pub fn failure(factor: FactorSourceIDFromHash) -> Self { + FactorOutcome::Neglected(NeglectedFactor::new( + NeglectFactorReason::Failure, + factor, + )) + } + + pub fn skipped(factor: FactorSourceIDFromHash) -> Self { + FactorOutcome::Neglected(NeglectedFactor::new( + NeglectFactorReason::UserExplicitlySkipped, + factor, + )) + } + + pub fn irrelevant(factor: FactorSourceIDFromHash) -> Self { + FactorOutcome::Neglected(NeglectedFactor::new( + NeglectFactorReason::Irrelevant, + factor, + )) + } + + pub fn factor_source_id(&self) -> FactorSourceIDFromHash { + match self { + FactorOutcome::Signed { + produced_signatures, + } => { + let signature = produced_signatures + .first() + .expect("Should have at least one signature"); + + signature.factor_source_id() + } + FactorOutcome::Neglected(neglected_factor) => { + neglected_factor.factor_source_id() + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = FactorOutcome; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + pub fn test_signed() { + let signature = HDSignature::sample(); + let signatures = IndexSet::just(signature.clone()); + + let sut = SUT::signed(signatures.clone()).unwrap(); + + assert_eq!(sut.as_signed().unwrap().clone(), signatures); + } + + #[test] + pub fn test_signed_no_signatures_failure() { + let result = SUT::signed(IndexSet::new()); + + assert_eq!(result, Err(CommonError::ExpectedNonEmptyCollection)); + } + + #[test] + pub fn test_signed_different_factor_source_id_failure() { + let result = SUT::signed( + IndexSet::from([ + HDSignature::fake_sign_by_looking_up_mnemonic_amongst_samples( + HDSignatureInput::new( + TransactionIntentHash::sample(), + OwnedFactorInstance::new( + AddressOfAccountOrPersona::sample(), + HierarchicalDeterministicFactorInstance::sample_mainnet_tx_account( + Hardened::from_local_key_space_unsecurified(0u32).unwrap(), + FactorSourceIDFromHash::sample_device(), + ), + ) + ) + ), + HDSignature::fake_sign_by_looking_up_mnemonic_amongst_samples( + HDSignatureInput::new( + TransactionIntentHash::sample(), + OwnedFactorInstance::new( + AddressOfAccountOrPersona::sample(), + HierarchicalDeterministicFactorInstance::sample_mainnet_tx_account( + Hardened::from_local_key_space_unsecurified(1u32).unwrap(), + FactorSourceIDFromHash::sample_device_other(), + ), + ) + ) + ) + ]) + ); + + assert_eq!( + result, + Err(CommonError::FactorOutcomeSignedFactorSourceIDMismatch) + ); + } + + #[test] + pub fn test_failure() { + let factor_source_id = FactorSourceIDFromHash::sample(); + + let sut = SUT::failure(factor_source_id); + + assert_eq!(sut.as_neglected().unwrap().content, factor_source_id); + assert_eq!( + sut.as_neglected().unwrap().reason, + NeglectFactorReason::Failure + ); + } + + #[test] + pub fn test_skipped() { + let factor_source_id = FactorSourceIDFromHash::sample(); + + let sut = SUT::skipped(factor_source_id); + + assert_eq!(sut.as_neglected().unwrap().content, factor_source_id); + assert_eq!( + sut.as_neglected().unwrap().reason, + NeglectFactorReason::UserExplicitlySkipped + ); + } + + #[test] + pub fn test_irrelevant() { + let factor_source_id = FactorSourceIDFromHash::sample(); + + let sut = SUT::irrelevant(factor_source_id); + + assert_eq!(sut.as_neglected().unwrap().content, factor_source_id); + assert_eq!( + sut.as_neglected().unwrap().reason, + NeglectFactorReason::Irrelevant + ); + } +} diff --git a/crates/app/signing-traits/src/host_interaction/mod.rs b/crates/app/signing-traits/src/host_interaction/mod.rs index 73512add7..1a0067d5e 100644 --- a/crates/app/signing-traits/src/host_interaction/mod.rs +++ b/crates/app/signing-traits/src/host_interaction/mod.rs @@ -1,11 +1,13 @@ +mod factor_outcome; +mod per_factor_source_input; mod sign_interactor; mod sign_request; mod sign_response; -mod sign_with_factors_outcome; mod transaction_sign_request_input; +pub use factor_outcome::*; +pub use per_factor_source_input::*; pub use sign_interactor::*; pub use sign_request::*; pub use sign_response::*; -pub use sign_with_factors_outcome::*; pub use transaction_sign_request_input::*; diff --git a/crates/app/signing-traits/src/host_interaction/per_factor_source_input.rs b/crates/app/signing-traits/src/host_interaction/per_factor_source_input.rs new file mode 100644 index 000000000..bafb3300e --- /dev/null +++ b/crates/app/signing-traits/src/host_interaction/per_factor_source_input.rs @@ -0,0 +1,61 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PerFactorSourceInput { + /// The factor source which the interactor should request signatures with + pub factor_source_id: FactorSourceIDFromHash, + + /// A set of transactions to sign, with multiple derivations paths. + pub per_transaction: IndexSet>, + + /// A collection of transactions which would be invalid if the user skips + /// signing with this factor source. + pub invalid_transactions_if_neglected: + IndexSet>, +} + +impl PerFactorSourceInput { + pub fn new( + factor_source_id: FactorSourceIDFromHash, + per_transaction: IndexSet>, + invalid_transactions_if_neglected: IndexSet< + InvalidTransactionIfNeglected, + >, + ) -> Self { + Self { + factor_source_id, + per_transaction, + invalid_transactions_if_neglected, + } + } +} + +impl HasSampleValues for PerFactorSourceInput +where + S::Payload: HasSampleValues, + S::ID: HasSampleValues, +{ + fn sample() -> Self { + Self::new( + FactorSourceIDFromHash::sample(), + IndexSet::from_iter(vec![ + TransactionSignRequestInput::::sample(), + TransactionSignRequestInput::::sample_other(), + ]), + IndexSet::new(), + ) + } + + fn sample_other() -> Self { + Self::new( + FactorSourceIDFromHash::sample_ledger(), + IndexSet::from_iter(vec![ + TransactionSignRequestInput::::sample(), + TransactionSignRequestInput::::sample_other(), + ]), + IndexSet::just( + InvalidTransactionIfNeglected::::sample_other(), + ), + ) + } +} diff --git a/crates/app/signing-traits/src/host_interaction/sign_interactor.rs b/crates/app/signing-traits/src/host_interaction/sign_interactor.rs index 22683530b..99a6f0218 100644 --- a/crates/app/signing-traits/src/host_interaction/sign_interactor.rs +++ b/crates/app/signing-traits/src/host_interaction/sign_interactor.rs @@ -10,5 +10,5 @@ pub trait SignInteractor: Send + Sync { async fn sign( &self, request: SignRequest, - ) -> Result>; + ) -> Result>; } diff --git a/crates/app/signing-traits/src/host_interaction/sign_request.rs b/crates/app/signing-traits/src/host_interaction/sign_request.rs index 59561b247..7e6c6a687 100644 --- a/crates/app/signing-traits/src/host_interaction/sign_request.rs +++ b/crates/app/signing-traits/src/host_interaction/sign_request.rs @@ -4,17 +4,10 @@ use crate::prelude::*; pub struct SignRequest { pub factor_source_kind: FactorSourceKind, - /// Per factor source, a set of transactions to sign, with - /// multiple derivations paths. - pub per_factor_source: IndexMap< - FactorSourceIDFromHash, - IndexSet>, - >, - - /// A collection of transactions which would be invalid if the user skips - /// signing with this factor source. - pub invalid_transactions_if_neglected: - IndexSet>, + /// Per factor source, a set of inputs that contain information on what signables need signing, + /// and what signables will fail if such factor source is neglected. + pub per_factor_source: + IndexMap>, } impl SignRequest { @@ -26,10 +19,7 @@ impl SignRequest { factor_source_kind: FactorSourceKind, per_factor_source: IndexMap< FactorSourceIDFromHash, - IndexSet>, - >, - invalid_transactions_if_neglected: IndexSet< - InvalidTransactionIfNeglected, + PerFactorSourceInput, >, ) -> Self { assert!( @@ -47,7 +37,6 @@ impl SignRequest { Self { factor_source_kind, per_factor_source, - invalid_transactions_if_neglected, } } @@ -55,7 +44,33 @@ impl SignRequest { self.per_factor_source.keys().cloned().collect() } - pub fn factor_source_kind(&self) -> FactorSourceKind { + pub fn invalid_transactions_if_all_factors_neglected( + &self, + ) -> IndexSet> { + let mut invalid_transactions_for_all_factors = + IndexSet::>::new(); + + self.per_factor_source.values().for_each(|input| { + invalid_transactions_for_all_factors + .extend(input.invalid_transactions_if_neglected.clone()) + }); + + invalid_transactions_for_all_factors + } + + pub fn invalid_transactions_if_factor_neglected( + &self, + factor_source_id: &FactorSourceIDFromHash, + ) -> IndexSet> { + self.per_factor_source + .get(factor_source_id) + .map_or(IndexSet::new(), |i| { + i.invalid_transactions_if_neglected.clone() + }) + } + + #[allow(unused)] + pub(crate) fn factor_source_kind(&self) -> FactorSourceKind { self.factor_source_kind } } @@ -70,12 +85,8 @@ where FactorSourceKind::sample(), IndexMap::just(( FactorSourceIDFromHash::sample(), - IndexSet::from_iter(vec![ - TransactionSignRequestInput::sample(), - TransactionSignRequestInput::sample_other(), - ]), + PerFactorSourceInput::sample(), )), - IndexSet::new(), ) } @@ -84,14 +95,8 @@ where FactorSourceKind::sample_other(), IndexMap::just(( FactorSourceIDFromHash::sample_ledger(), - IndexSet::from_iter(vec![ - TransactionSignRequestInput::sample(), - TransactionSignRequestInput::sample_other(), - ]), + PerFactorSourceInput::sample_other(), )), - IndexSet::just( - InvalidTransactionIfNeglected::::sample_other(), - ), ) } } @@ -118,7 +123,7 @@ mod tests { expected = "Invalid input, per_factor_source must not be empty, this is a programmer error." )] fn panics_if_per_factor_source_is_empty() { - SUT::new(FactorSourceKind::Device, IndexMap::new(), IndexSet::new()); + SUT::new(FactorSourceKind::Device, IndexMap::new()); } #[test] @@ -130,11 +135,8 @@ mod tests { FactorSourceKind::ArculusCard, IndexMap::just(( FactorSourceIDFromHash::sample(), - IndexSet::just( - TransactionSignRequestInput::::sample(), - ), + PerFactorSourceInput::::sample(), )), - IndexSet::new(), ); } } diff --git a/crates/app/signing-traits/src/host_interaction/sign_response.rs b/crates/app/signing-traits/src/host_interaction/sign_response.rs index 631b43ae3..8c3e57c76 100644 --- a/crates/app/signing-traits/src/host_interaction/sign_response.rs +++ b/crates/app/signing-traits/src/host_interaction/sign_response.rs @@ -1,33 +1,8 @@ use crate::prelude::*; -/// The response of a batch signing request, either a PolyFactor or MonoFactor signing -/// request, matters not, because the goal is to have signed all transactions with -/// enough keys (derivation paths) needed for it to be valid when submitted to the -/// Radix network. #[derive(Clone, PartialEq, Eq, derive_more::Debug)] -#[debug("SignResponse {{ signatures: {:#?} }}", signatures.values().map(|f| format!("{:#?}", f)).join(", "))] pub struct SignResponse { - pub signatures: IndexMap>>, -} - -impl SignResponse { - pub fn new( - signatures: IndexMap>>, - ) -> Self { - Self { signatures } - } - - pub fn with_signatures(signatures: IndexSet>) -> Self { - let signatures = signatures - .into_iter() - .into_group_map_by(|x| x.factor_source_id()); - Self::new( - signatures - .into_iter() - .map(|(k, v)| (k, IndexSet::from_iter(v))) - .collect(), - ) - } + pub per_factor_outcome: IndexMap>, } impl HasSampleValues for SignResponse { @@ -38,28 +13,94 @@ impl HasSampleValues for SignResponse { .owned_factor_instance .value .factor_source_id; - Self::new(IndexMap::just(( + + Self::signed(IndexMap::just(( factor_source_id, IndexSet::just(hd_signature), ))) + .unwrap() } fn sample_other() -> Self { - let hd_signature = HDSignature::sample_other(); - let factor_source_id = hd_signature - .input - .owned_factor_instance - .value - .factor_source_id; - Self::new(IndexMap::just(( - factor_source_id, - IndexSet::just(hd_signature), - ))) + Self::user_skipped_factors(IndexSet::just( + FactorSourceIDFromHash::sample_other(), + )) + } +} + +impl SignResponse { + #[allow(unused)] + pub fn new_from_outcomes( + outcomes: IndexMap>, + ) -> Result { + if outcomes + .iter() + .any(|(id, outcome)| *id != outcome.factor_source_id()) + { + return Err(CommonError::FactorOutcomeSignedFactorSourceIDMismatch); + } + + Ok(Self { + per_factor_outcome: outcomes, + }) + } + + #[allow(unused)] + pub fn signed( + produced_signatures: IndexMap< + FactorSourceIDFromHash, + IndexSet>, + >, + ) -> Result { + let signed_outcomes = produced_signatures + .iter() + .map(|(id, signatures)| { + let outcome = FactorOutcome::signed(signatures.clone())?; + Ok((*id, outcome)) + }) + .collect::)>>>()?; + + Ok(Self { + per_factor_outcome: IndexMap::from_iter(signed_outcomes), + }) + } + + #[allow(unused)] + pub(crate) fn failure_with_factors( + ids: IndexSet, + ) -> Self { + Self { + per_factor_outcome: IndexMap::from_iter( + ids.iter().map(|id| (*id, FactorOutcome::failure(*id))), + ), + } + } + + pub fn user_skipped_factors(ids: IndexSet) -> Self { + Self { + per_factor_outcome: IndexMap::from_iter( + ids.iter().map(|id| (*id, FactorOutcome::skipped(*id))), + ), + } + } + + pub fn irrelevant(factor_sources_of_kind: &FactorSourcesOfKind) -> Self { + let ids = factor_sources_of_kind + .factor_sources() + .into_iter() + .map(|f| *f.factor_source_id().as_hash().unwrap()) // TODO ask that + .collect_vec(); + + Self { + per_factor_outcome: IndexMap::from_iter( + ids.iter().map(|id| (*id, FactorOutcome::irrelevant(*id))), + ), + } } } #[cfg(test)] -mod tests { +mod test { use super::*; #[allow(clippy::upper_case_acronyms)] @@ -75,4 +116,20 @@ mod tests { fn inequality() { assert_ne!(SUT::sample(), SUT::sample_other()); } + + #[test] + fn test_new_from_outcomes_fails_due_to_id_mismatch() { + let outcome = FactorOutcome::sample(); + + let wrong_outcome_paring = IndexMap::from([ + (outcome.factor_source_id(), outcome.clone()), + (FactorSourceIDFromHash::sample_ledger(), outcome), + ]); + let result = SUT::new_from_outcomes(wrong_outcome_paring); + + assert_eq!( + Err(CommonError::FactorOutcomeSignedFactorSourceIDMismatch), + result + ); + } } diff --git a/crates/app/signing-traits/src/host_interaction/sign_with_factors_outcome.rs b/crates/app/signing-traits/src/host_interaction/sign_with_factors_outcome.rs deleted file mode 100644 index 12fe842e1..000000000 --- a/crates/app/signing-traits/src/host_interaction/sign_with_factors_outcome.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::prelude::*; - -#[derive(Clone, PartialEq, Eq, derive_more::Debug)] -pub enum SignWithFactorsOutcome { - /// The user successfully signed with the factor source(s), the associated - /// value contains the produces signatures and any relevant metadata. - #[debug("Signed: {:#?}", produced_signatures)] - Signed { - produced_signatures: SignResponse, - }, - - /// The factor source got neglected, either due to user explicitly skipping - /// or due to failure - #[debug("Neglected")] - Neglected(NeglectedFactors), -} - -impl HasSampleValues - for SignWithFactorsOutcome -{ - fn sample() -> Self { - Self::signed(SignResponse::sample()) - } - - fn sample_other() -> Self { - Self::Neglected(NeglectedFactors::sample_other()) - } -} - -impl SignWithFactorsOutcome { - #[allow(unused)] - pub fn signed(produced_signatures: SignResponse) -> Self { - Self::Signed { - produced_signatures, - } - } - - pub fn failure_with_factors(ids: IndexSet) -> Self { - Self::Neglected(NeglectedFactors::new( - NeglectFactorReason::Failure, - ids, - )) - } - - pub fn user_skipped_factors(ids: IndexSet) -> Self { - Self::Neglected(NeglectedFactors::new( - NeglectFactorReason::UserExplicitlySkipped, - ids, - )) - } - - pub fn user_skipped_factor(id: FactorSourceIDFromHash) -> Self { - Self::user_skipped_factors(IndexSet::from_iter([id])) - } - - pub fn irrelevant(factor_sources_of_kind: &FactorSourcesOfKind) -> Self { - Self::Neglected(NeglectedFactors::new( - NeglectFactorReason::Irrelevant, - factor_sources_of_kind - .factor_sources() - .into_iter() - .map(|f| *f.factor_source_id().as_hash().unwrap()) - .collect(), - )) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = SignWithFactorsOutcome; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/app/signing-traits/src/lib.rs b/crates/app/signing-traits/src/lib.rs index ac419c151..889e003ba 100644 --- a/crates/app/signing-traits/src/lib.rs +++ b/crates/app/signing-traits/src/lib.rs @@ -38,13 +38,13 @@ pub mod prelude { pub(crate) use std::fmt::Debug; pub(crate) use std::sync::Arc; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { pub(crate) use std::collections::HashSet; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/app/signing-traits/src/testing/interactors/test_sign_interactor.rs b/crates/app/signing-traits/src/testing/interactors/test_sign_interactor.rs index 795e6142b..888117862 100644 --- a/crates/app/signing-traits/src/testing/interactors/test_sign_interactor.rs +++ b/crates/app/signing-traits/src/testing/interactors/test_sign_interactor.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub struct TestSignInteractor { - pub simulated_user: SimulatedUser, + pub(crate) simulated_user: SimulatedUser, } impl TestSignInteractor { @@ -23,10 +23,12 @@ impl SignInteractor for TestSignInteractor { async fn sign( &self, request: SignRequest, - ) -> Result> { + ) -> Result> { self.simulated_user.spy_on_request_before_handled( request.factor_source_kind(), - request.invalid_transactions_if_neglected.clone(), + request + .invalid_transactions_if_all_factors_neglected() + .clone(), ); let ids = request .per_factor_source @@ -35,44 +37,41 @@ impl SignInteractor for TestSignInteractor { .collect::>(); if self.should_simulate_failure(ids.clone()) { - return Ok(SignWithFactorsOutcome::failure_with_factors(ids)); + return Ok(SignResponse::failure_with_factors(ids)); } - match self - .simulated_user - .sign_or_skip(request.invalid_transactions_if_neglected.clone()) - { + match self.simulated_user.sign_or_skip( + request + .invalid_transactions_if_all_factors_neglected() + .clone(), + ) { SigningUserInput::Sign => { - let signatures = request + let per_factor_outcome = IndexMap::from_iter(request .per_factor_source .iter() - .flat_map(|(_, v)| { - v.iter() + .map(|(id, input)| { + let signatures = input.per_transaction + .iter() .flat_map(|x| { x.signature_inputs() .iter() - .map(|y| unsafe { HDSignature::produced_signing_with_input(y.clone())}) + .map(|y| + unsafe { + HDSignature::produced_signing_with_input(y.clone()) + } + ) .collect_vec() }) - .collect::>>() - }) - .collect::>>(); + .collect::>>(); - let signatures = signatures - .into_iter() - .into_group_map_by(|x| x.factor_source_id()); - let response = SignResponse::new( - signatures - .into_iter() - .map(|(k, v)| (k, IndexSet::from_iter(v))) - .collect(), - ); + (*id, signatures) + })); - Ok(SignWithFactorsOutcome::signed(response)) + SignResponse::signed(per_factor_outcome) } SigningUserInput::Skip => { - Ok(SignWithFactorsOutcome::user_skipped_factors(ids)) + Ok(SignResponse::user_skipped_factors(ids)) } SigningUserInput::Reject => Err(CommonError::SigningRejected), diff --git a/crates/app/signing-traits/src/types/mod.rs b/crates/app/signing-traits/src/types/mod.rs index b97136931..95cde91e7 100644 --- a/crates/app/signing-traits/src/types/mod.rs +++ b/crates/app/signing-traits/src/types/mod.rs @@ -1,9 +1,9 @@ mod hd_signature; mod hd_signature_input; -mod neglected_factor_instance; +mod neglected_factor; mod owned_types; pub use hd_signature::*; pub use hd_signature_input::*; -pub use neglected_factor_instance::*; +pub use neglected_factor::*; pub use owned_types::*; diff --git a/crates/app/signing-traits/src/types/neglected_factor_instance.rs b/crates/app/signing-traits/src/types/neglected_factor.rs similarity index 90% rename from crates/app/signing-traits/src/types/neglected_factor_instance.rs rename to crates/app/signing-traits/src/types/neglected_factor.rs index b3280e14c..6f36d8954 100644 --- a/crates/app/signing-traits/src/types/neglected_factor_instance.rs +++ b/crates/app/signing-traits/src/types/neglected_factor.rs @@ -1,6 +1,5 @@ -use std::fmt::Formatter; - use crate::prelude::*; +use std::fmt::Formatter; /// A neglected factor, with a reason. #[derive(Clone, PartialEq, Eq, Hash)] @@ -64,21 +63,18 @@ impl HasSampleValues for NeglectedFactorInstance { /// ID to some neglected factor source, with the reason why it was neglected (skipped/failed) pub type NeglectedFactor = AbstractNeglectedFactor; -/// IDs to some neglected factor source, with the reason why they were neglected (skipped/failed) -pub type NeglectedFactors = - AbstractNeglectedFactor>; - -impl HasSampleValues for NeglectedFactors { +impl HasSampleValues for NeglectedFactor { fn sample() -> Self { Self::new( NeglectFactorReason::UserExplicitlySkipped, - IndexSet::just(FactorSourceIDFromHash::sample()), + FactorSourceIDFromHash::sample(), ) } + fn sample_other() -> Self { Self::new( NeglectFactorReason::Failure, - IndexSet::just(FactorSourceIDFromHash::sample_other()), + FactorSourceIDFromHash::sample_other(), ) } } diff --git a/crates/app/signing/Cargo.toml b/crates/app/signing/Cargo.toml index 9aae39ce8..efb5d6742 100644 --- a/crates/app/signing/Cargo.toml +++ b/crates/app/signing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "signing" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/app/signing/src/collector/mod.rs b/crates/app/signing/src/collector/mod.rs index d056dc78e..0b15d8d9c 100644 --- a/crates/app/signing/src/collector/mod.rs +++ b/crates/app/signing/src/collector/mod.rs @@ -5,9 +5,11 @@ mod signatures_collector_dependencies; mod signatures_collector_preprocessor; mod signatures_collector_state; mod signing_finish_early_strategy; +mod support_poly_sign; pub use extractor_of_instances_required_to_sign_transactions::*; pub(crate) use signatures_collector_preprocessor::*; +pub(crate) use support_poly_sign::*; pub use signatures_collecting_continuation::*; pub use signatures_collector::*; diff --git a/crates/app/signing/src/collector/signatures_collector.rs b/crates/app/signing/src/collector/signatures_collector.rs index d2b3668ef..a229c554c 100644 --- a/crates/app/signing/src/collector/signatures_collector.rs +++ b/crates/app/signing/src/collector/signatures_collector.rs @@ -190,7 +190,7 @@ impl SignaturesCollector { "Neglecting all factors of kind: {} since they are all irrelevant (all TX referencing those factors have already failed)", factor_sources_of_kind.kind ); - self.process_batch_response(SignWithFactorsOutcome::irrelevant( + self.process_batch_response(SignResponse::irrelevant( factor_sources_of_kind, )); true @@ -209,57 +209,77 @@ impl SignaturesCollector { &factor_sources_of_kind.kind ); - let kind = factor_sources_of_kind.kind; - if kind == FactorSourceKind::Device { - debug!("Creating poly request for interactor"); - let request = - self.request_for_parallel_interactor(factor_sources_of_kind); - if !request.invalid_transactions_if_neglected.is_empty() { - info!( - "If factors {:?} are neglected, invalid TXs: {:?}", - request.per_factor_source.keys(), - request.invalid_transactions_if_neglected - ) - } - debug!("Dispatching poly request to interactor: {:?}", request); - let response = self.dependencies.interactor.sign(request).await?; - debug!("Got response from poly interactor: {:?}", response); - self.process_batch_response(response); + if factor_sources_of_kind.kind.support_poly_sign() { + self.sign_poly(factor_sources_of_kind).await } else { - let factor_sources = factor_sources_of_kind.factor_sources(); - for factor_source in factor_sources { - // Prepare the request for the interactor - debug!("Creating mono request for interactor"); - let factor_source_id = - factor_source.factor_source_id().as_hash().cloned().expect( - "Signature Collector only works with HD FactorSources.", - ); + self.sign_mono(factor_sources_of_kind).await + } + } - let request = - self.request_for_serial_interactor(kind, &factor_source_id); + async fn sign_poly( + &self, + factor_sources_of_kind: &FactorSourcesOfKind, + ) -> Result<()> { + debug!("Creating poly request for interactor"); + let request = self.request_for_poly_sign(factor_sources_of_kind); - if !request.invalid_transactions_if_neglected.is_empty() { - info!( - "If factor {:?} are neglected, invalid TXs: {:?}", - factor_source_id, - request.invalid_transactions_if_neglected - ) - } + let invalid_transactions = + request.invalid_transactions_if_all_factors_neglected(); + if !invalid_transactions.is_empty() { + info!( + "If factors {:?} are neglected, invalid TXs: {:?}", + request.per_factor_source.keys(), + invalid_transactions + ) + } + debug!("Dispatching poly request to interactor: {:?}", request); + let response = self.dependencies.interactor.sign(request).await?; + debug!("Got response from poly interactor: {:?}", response); + self.process_batch_response(response); - debug!("Dispatching mono request to interactor: {:?}", request); - // Produce the results from the interactor - let response = - self.dependencies.interactor.sign(request).await?; - debug!("Got response from mono interactor: {:?}", response); + Ok(()) + } - // Report the results back to the collector - self.process_batch_response(response); + async fn sign_mono( + &self, + factor_sources_of_kind: &FactorSourcesOfKind, + ) -> Result<()> { + let factor_sources = factor_sources_of_kind.factor_sources(); + for factor_source in factor_sources { + // Prepare the request for the interactor + debug!("Creating mono request for interactor"); + let factor_source_id = + factor_source.factor_source_id().as_hash().cloned().expect( + "Signature Collector only works with HD FactorSources.", + ); - if self.continuation() == FinishEarly { - break; - } + let request = self.request_for_mono_sign( + factor_sources_of_kind.kind, + &factor_source_id, + ); + + let invalid_transactions = request + .invalid_transactions_if_factor_neglected(&factor_source_id); + if !invalid_transactions.is_empty() { + info!( + "If factor {:?} are neglected, invalid TXs: {:?}", + factor_source_id, invalid_transactions + ) + } + + debug!("Dispatching mono request to interactor: {:?}", request); + // Produce the results from the interactor + let response = self.dependencies.interactor.sign(request).await?; + debug!("Got response from mono interactor: {:?}", response); + + // Report the results back to the collector + self.process_batch_response(response); + + if self.continuation() == FinishEarly { + break; } } + Ok(()) } @@ -281,7 +301,7 @@ impl SignaturesCollector { Ok(()) } - fn input_for_interactor( + fn per_transaction_input( &self, factor_source_id: &FactorSourceIDFromHash, ) -> IndexSet> { @@ -293,15 +313,15 @@ impl SignaturesCollector { .expect( "SignaturesCollectorState lock should not have been poisoned.", ) - .input_for_interactor(factor_source_id) + .per_transaction_input(factor_source_id) } - fn request_for_serial_interactor( + fn request_for_mono_sign( &self, factor_source_kind: FactorSourceKind, factor_source_id: &FactorSourceIDFromHash, ) -> SignRequest { - let batch_signing_request = self.input_for_interactor(factor_source_id); + let per_transaction = self.per_transaction_input(factor_source_id); let invalid_transactions_if_neglected = self .invalid_transactions_if_neglected_factor_sources(IndexSet::just( @@ -310,14 +330,19 @@ impl SignaturesCollector { .into_iter() .collect::>(); + let per_factor_source_input = PerFactorSourceInput::new( + *factor_source_id, + per_transaction, + invalid_transactions_if_neglected, + ); + SignRequest::new( factor_source_kind, - IndexMap::just((*factor_source_id, batch_signing_request)), - invalid_transactions_if_neglected, + IndexMap::just((*factor_source_id, per_factor_source_input)), ) } - fn request_for_parallel_interactor( + fn request_for_poly_sign( &self, factor_sources_of_kind: &FactorSourcesOfKind, ) -> SignRequest { @@ -330,26 +355,24 @@ impl SignaturesCollector { ) }) .collect::>(); - let per_factor_source = factor_source_ids - .clone() - .iter() - .map(|fid| (*fid, self.input_for_interactor(fid))) - .collect::>, - >>(); let invalid_transactions_if_neglected = self .invalid_transactions_if_neglected_factor_sources( - factor_source_ids, + factor_source_ids.clone(), ); + let per_factor_source = factor_source_ids.iter().map(|id| { + let per_transaction = self.per_transaction_input(id); + + (*id, PerFactorSourceInput::new( + *id, + per_transaction, + invalid_transactions_if_neglected.clone(), + )) + }).collect::>>(); + // Prepare the request for the interactor - SignRequest::new( - factor_sources_of_kind.kind, - per_factor_source, - invalid_transactions_if_neglected, - ) + SignRequest::new(factor_sources_of_kind.kind, per_factor_source) } fn invalid_transactions_if_neglected_factor_sources( @@ -367,7 +390,7 @@ impl SignaturesCollector { .invalid_transactions_if_neglected_factors(factor_source_ids) } - fn process_batch_response(&self, response: SignWithFactorsOutcome) { + fn process_batch_response(&self, response: SignResponse) { let state = self .state .write() @@ -1060,7 +1083,14 @@ mod tests { let outcome = collector.collect_signatures().await.unwrap(); assert!(outcome.successful()); let signatures = outcome.all_signatures(); - assert_eq!(signatures.len(), 2); + assert_eq!( + signatures.len(), + if FactorSourceKind::Device.support_poly_sign() { + 2 + } else { + 1 + } + ); assert!(signatures .into_iter() diff --git a/crates/app/signing/src/collector/support_poly_sign.rs b/crates/app/signing/src/collector/support_poly_sign.rs new file mode 100644 index 000000000..6277566ec --- /dev/null +++ b/crates/app/signing/src/collector/support_poly_sign.rs @@ -0,0 +1,31 @@ +use crate::prelude::*; + +/// Indicates entities that can enable poly sign. +/// `FactorSourceKind`s are the only entities that can be used on mono or poly sign. +/// Poly sign is designed to be used with factor kinds that can provide signatures with a single +/// authorisation. +/// +/// One prime example of that is `FactorSourceKind::Device` which can provide +/// signatures for many device factor sources with a single biometrics request. Although disabled +/// for now. +pub(crate) trait SupportPolySign { + fn support_poly_sign(&self) -> bool; +} + +impl SupportPolySign for FactorSourceKind { + fn support_poly_sign(&self) -> bool { + false + } +} + +mod test { + #[allow(unused_imports)] + use super::*; + + #[test] + pub fn all_kinds_supporting_mono_sign() { + assert!(FactorSourceKind::all() + .iter() + .all(|kind| { kind.support_poly_sign() == false })) + } +} diff --git a/crates/app/signing/src/petition_types/petitions.rs b/crates/app/signing/src/petition_types/petitions.rs index ca787d4b1..73f30c554 100644 --- a/crates/app/signing/src/petition_types/petitions.rs +++ b/crates/app/signing/src/petition_types/petitions.rs @@ -167,7 +167,7 @@ impl Petitions { /// `should_neglect_factors_due_to_irrelevant` from SignatureCollector main /// loop, i.e. we should not have called this method from SignaturesCollector /// if `should_neglect_factors_due_to_irrelevant` returned true. - pub(crate) fn input_for_interactor( + pub(crate) fn per_transaction_input( &self, factor_source_id: &FactorSourceIDFromHash, ) -> IndexSet> { @@ -209,36 +209,28 @@ impl Petitions { ) } - pub(crate) fn process_batch_response( - &self, - response: SignWithFactorsOutcome, - ) { - match response { - SignWithFactorsOutcome::Signed { - produced_signatures, - } => { - for (k, v) in produced_signatures.signatures.clone().iter() { - info!("Signed with {} (#{} signatures)", k, v.len()); + pub(crate) fn process_batch_response(&self, response: SignResponse) { + response + .per_factor_outcome + .iter() + .for_each(|(id, outcome)| match outcome { + FactorOutcome::Signed { + produced_signatures, + .. + } => { + info!( + "Signed with {} (#{} signatures)", + id.clone(), + produced_signatures.len() + ); + produced_signatures + .iter() + .for_each(|s| self.add_signature(s)); } - produced_signatures - .signatures - .values() - .flatten() - .for_each(|s| self.add_signature(s)); - } - SignWithFactorsOutcome::Neglected(neglected_factors) => { - let reason = neglected_factors.reason; - for neglected_factor_source_id in - neglected_factors.content.iter() - { - info!("Neglected {}", neglected_factor_source_id); - self.neglect_factor_source_with_id(NeglectedFactor::new( - reason, - *neglected_factor_source_id, - )) + FactorOutcome::Neglected(neglected_factor) => { + self.neglect_factor_source_with_id(neglected_factor.clone()) } - } - } + }) } #[allow(unused)] diff --git a/crates/common/build-info/Cargo.toml b/crates/common/build-info/Cargo.toml index 127f93ce5..50654801b 100644 --- a/crates/common/build-info/Cargo.toml +++ b/crates/common/build-info/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "build-info" -version = "1.1.101" +version = "1.1.102" edition = "2021" build = "build.rs" diff --git a/crates/common/bytes/Cargo.toml b/crates/common/bytes/Cargo.toml index 92315e559..befd3a33f 100644 --- a/crates/common/bytes/Cargo.toml +++ b/crates/common/bytes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bytes" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/entity-foundation/Cargo.toml b/crates/common/entity-foundation/Cargo.toml index 02700b113..020e58f39 100644 --- a/crates/common/entity-foundation/Cargo.toml +++ b/crates/common/entity-foundation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "entity-foundation" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/host-info/Cargo.toml b/crates/common/host-info/Cargo.toml index 5693e996f..f1eba7e1a 100644 --- a/crates/common/host-info/Cargo.toml +++ b/crates/common/host-info/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "host-info" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/identified-vec-of/Cargo.toml b/crates/common/identified-vec-of/Cargo.toml index 4d072fa16..ed8d90b18 100644 --- a/crates/common/identified-vec-of/Cargo.toml +++ b/crates/common/identified-vec-of/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identified-vec-of" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/metadata/Cargo.toml b/crates/common/metadata/Cargo.toml index 5fbbd337c..af9db05c3 100644 --- a/crates/common/metadata/Cargo.toml +++ b/crates/common/metadata/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "metadata" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/network/Cargo.toml b/crates/common/network/Cargo.toml index 14a637e3b..e2f3b10ff 100644 --- a/crates/common/network/Cargo.toml +++ b/crates/common/network/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "network" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/numeric/Cargo.toml b/crates/common/numeric/Cargo.toml index 8d4c1f235..067aed60f 100644 --- a/crates/common/numeric/Cargo.toml +++ b/crates/common/numeric/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "numeric" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/common/short-string/Cargo.toml b/crates/common/short-string/Cargo.toml index 4d33804e9..13d01fff7 100644 --- a/crates/common/short-string/Cargo.toml +++ b/crates/common/short-string/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "short-string" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/assert-json/Cargo.toml b/crates/core/assert-json/Cargo.toml index dd070d579..1c0cb389e 100644 --- a/crates/core/assert-json/Cargo.toml +++ b/crates/core/assert-json/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "assert-json" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/collections/Cargo.toml b/crates/core/collections/Cargo.toml index fa58a677a..31d381b2c 100644 --- a/crates/core/collections/Cargo.toml +++ b/crates/core/collections/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "core-collections" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/error/Cargo.toml b/crates/core/error/Cargo.toml index bd5460949..83c067f29 100644 --- a/crates/core/error/Cargo.toml +++ b/crates/core/error/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "error" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] @@ -15,3 +15,5 @@ prelude = { workspace = true } log = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } +derive_more = { workspace = true } +strum = { workspace = true } diff --git a/crates/core/error/src/common_error.rs b/crates/core/error/src/common_error.rs index 4acbdf2f1..884aac4b1 100644 --- a/crates/core/error/src/common_error.rs +++ b/crates/core/error/src/common_error.rs @@ -1,3 +1,4 @@ +use crate::prelude::*; use thiserror::Error as ThisError; pub type Result = std::result::Result; @@ -222,7 +223,7 @@ pub enum CommonError { #[error("Failed to access secure storage due to \"{error_message}\" for key {} ", key)] SecureStorageAccessError { key: String, - error_kind: String, + error_kind: SecureStorageAccessErrorKind, error_message: String, } = 10063, @@ -849,6 +850,9 @@ pub enum CommonError { #[error("SecurityStateAccessController address mismatch")] SecurityStateAccessControllerAddressMismatch = 10244, + + #[error("Not all signatures are produced with the same factor source.")] + FactorOutcomeSignedFactorSourceIDMismatch = 10245, } impl CommonError { diff --git a/crates/core/error/src/lib.rs b/crates/core/error/src/lib.rs index 95fbf3394..5e074a05a 100644 --- a/crates/core/error/src/lib.rs +++ b/crates/core/error/src/lib.rs @@ -2,7 +2,9 @@ #![feature(core_intrinsics)] mod common_error; +mod secure_storage_access_error_kind; pub mod prelude { pub use crate::common_error::*; + pub use crate::secure_storage_access_error_kind::*; } diff --git a/crates/system/drivers/src/drivers/secure_storage_driver/support/secure_storage_access_error_kind.rs b/crates/core/error/src/secure_storage_access_error_kind.rs similarity index 94% rename from crates/system/drivers/src/drivers/secure_storage_driver/support/secure_storage_access_error_kind.rs rename to crates/core/error/src/secure_storage_access_error_kind.rs index 232f08580..c1b4f7efb 100644 --- a/crates/system/drivers/src/drivers/secure_storage_driver/support/secure_storage_access_error_kind.rs +++ b/crates/core/error/src/secure_storage_access_error_kind.rs @@ -1,5 +1,3 @@ -use crate::prelude::*; - /// An error kind that might be returned during access to secure storage driver. These errors are /// android specific and are defined [here](https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt#constants_1) /// Hosts, can print the error message provided by the system, and can ignore the error if @@ -64,16 +62,6 @@ pub enum SecureStorageAccessErrorKind { NoDeviceCredential, } -impl HasSampleValues for SecureStorageAccessErrorKind { - fn sample() -> Self { - SecureStorageAccessErrorKind::HardwareUnavailable - } - - fn sample_other() -> Self { - SecureStorageAccessErrorKind::UnableToProcess - } -} - impl SecureStorageAccessErrorKind { pub fn is_manual_cancellation(&self) -> bool { self == &SecureStorageAccessErrorKind::UserCancelled diff --git a/crates/core/has-sample-values/Cargo.toml b/crates/core/has-sample-values/Cargo.toml index 8fa7b1299..2f1e41b8c 100644 --- a/crates/core/has-sample-values/Cargo.toml +++ b/crates/core/has-sample-values/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "has-sample-values" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/has-sample-values/src/has_sample_values.rs b/crates/core/has-sample-values/src/has_sample_values.rs index 4588ab2c7..9356cc944 100644 --- a/crates/core/has-sample-values/src/has_sample_values.rs +++ b/crates/core/has-sample-values/src/has_sample_values.rs @@ -15,6 +15,16 @@ impl HasSampleValues for CommonError { } } +impl HasSampleValues for SecureStorageAccessErrorKind { + fn sample() -> Self { + SecureStorageAccessErrorKind::HardwareUnavailable + } + + fn sample_other() -> Self { + SecureStorageAccessErrorKind::UnableToProcess + } +} + impl HasSampleValues for String { fn sample() -> Self { "sample".to_string() diff --git a/crates/core/misc/Cargo.toml b/crates/core/misc/Cargo.toml index 4271add16..49288dfdf 100644 --- a/crates/core/misc/Cargo.toml +++ b/crates/core/misc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "core-misc" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/prelude/Cargo.toml b/crates/core/prelude/Cargo.toml index 2a8ac7b6f..25feb9729 100644 --- a/crates/core/prelude/Cargo.toml +++ b/crates/core/prelude/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prelude" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/time-utils/Cargo.toml b/crates/core/time-utils/Cargo.toml index 6516a2f08..3d788d696 100644 --- a/crates/core/time-utils/Cargo.toml +++ b/crates/core/time-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "time-utils" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/core/utils/Cargo.toml b/crates/core/utils/Cargo.toml index 988508d5f..08982d40d 100644 --- a/crates/core/utils/Cargo.toml +++ b/crates/core/utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "core-utils" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/addresses/Cargo.toml b/crates/crypto/addresses/Cargo.toml index 9a731f6ab..5cbb1bd53 100644 --- a/crates/crypto/addresses/Cargo.toml +++ b/crates/crypto/addresses/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "addresses" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/cap26-models/Cargo.toml b/crates/crypto/cap26-models/Cargo.toml index e467717fd..7656c6572 100644 --- a/crates/crypto/cap26-models/Cargo.toml +++ b/crates/crypto/cap26-models/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap26-models" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/cap26-models/src/lib.rs b/crates/crypto/cap26-models/src/lib.rs index 456320e6c..e52b35939 100644 --- a/crates/crypto/cap26-models/src/lib.rs +++ b/crates/crypto/cap26-models/src/lib.rs @@ -13,6 +13,8 @@ pub mod prelude { pub(crate) use serde_repr::{Deserialize_repr, Serialize_repr}; pub(crate) use strum::FromRepr; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { @@ -20,8 +22,6 @@ pub mod prelude { pub(crate) use serde_json::json; pub(crate) use std::collections::BTreeSet; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/crypto/ecc/Cargo.toml b/crates/crypto/ecc/Cargo.toml index 29de2def6..b875dc35d 100644 --- a/crates/crypto/ecc/Cargo.toml +++ b/crates/crypto/ecc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ecc" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/encryption/Cargo.toml b/crates/crypto/encryption/Cargo.toml index 9ccccf1b6..d5678ae01 100644 --- a/crates/crypto/encryption/Cargo.toml +++ b/crates/crypto/encryption/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "encryption" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/hash/Cargo.toml b/crates/crypto/hash/Cargo.toml index 4535d9116..230667ea3 100644 --- a/crates/crypto/hash/Cargo.toml +++ b/crates/crypto/hash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hash" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/crypto/hd/Cargo.toml b/crates/crypto/hd/Cargo.toml index 6b363513a..4740e6c33 100644 --- a/crates/crypto/hd/Cargo.toml +++ b/crates/crypto/hd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hierarchical-deterministic" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/factors/factors/Cargo.toml b/crates/factors/factors/Cargo.toml index 075ed63a7..f71d8dfa0 100644 --- a/crates/factors/factors/Cargo.toml +++ b/crates/factors/factors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "factors" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/factors/instances-provider/Cargo.toml b/crates/factors/instances-provider/Cargo.toml index 8bf1ef6ec..aa50aac76 100644 --- a/crates/factors/instances-provider/Cargo.toml +++ b/crates/factors/instances-provider/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "factor-instances-provider" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/factors/keys-collector/Cargo.toml b/crates/factors/keys-collector/Cargo.toml index 5df605a95..4911e29a5 100644 --- a/crates/factors/keys-collector/Cargo.toml +++ b/crates/factors/keys-collector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "keys-collector" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/factors/next-derivation-index-ephemeral/Cargo.toml b/crates/factors/next-derivation-index-ephemeral/Cargo.toml index 999045300..38711e8f0 100644 --- a/crates/factors/next-derivation-index-ephemeral/Cargo.toml +++ b/crates/factors/next-derivation-index-ephemeral/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "next-derivation-index-ephemeral" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/factors/supporting-types/Cargo.toml b/crates/factors/supporting-types/Cargo.toml index fcb9a1652..d255ebdc2 100644 --- a/crates/factors/supporting-types/Cargo.toml +++ b/crates/factors/supporting-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "factors-supporting-types" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/gateway/client-and-api/Cargo.toml b/crates/gateway/client-and-api/Cargo.toml index 0c00c2f39..5646da8d9 100644 --- a/crates/gateway/client-and-api/Cargo.toml +++ b/crates/gateway/client-and-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gateway-client-and-api" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/gateway/models/Cargo.toml b/crates/gateway/models/Cargo.toml index 1f1d7b929..998e4d6ee 100644 --- a/crates/gateway/models/Cargo.toml +++ b/crates/gateway/models/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gateway-models" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/profile/logic/logic_SPLIT_ME/Cargo.toml b/crates/profile/logic/logic_SPLIT_ME/Cargo.toml index d1430d439..61e58227b 100644 --- a/crates/profile/logic/logic_SPLIT_ME/Cargo.toml +++ b/crates/profile/logic/logic_SPLIT_ME/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-logic" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/account-for-display/Cargo.toml b/crates/profile/models/account-for-display/Cargo.toml index bf3d329d3..7970daa7f 100644 --- a/crates/profile/models/account-for-display/Cargo.toml +++ b/crates/profile/models/account-for-display/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "account-for-display" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/account-or-persona/Cargo.toml b/crates/profile/models/account-or-persona/Cargo.toml index 2b634e841..76237a8d6 100644 --- a/crates/profile/models/account-or-persona/Cargo.toml +++ b/crates/profile/models/account-or-persona/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-account-or-persona" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/account/Cargo.toml b/crates/profile/models/account/Cargo.toml index a8293da0e..7099367d0 100644 --- a/crates/profile/models/account/Cargo.toml +++ b/crates/profile/models/account/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-account" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/app-preferences/Cargo.toml b/crates/profile/models/app-preferences/Cargo.toml index 5ba237013..e3944c90e 100644 --- a/crates/profile/models/app-preferences/Cargo.toml +++ b/crates/profile/models/app-preferences/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-app-preferences" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/base-entity/Cargo.toml b/crates/profile/models/base-entity/Cargo.toml index da174c5ed..cece0c87a 100644 --- a/crates/profile/models/base-entity/Cargo.toml +++ b/crates/profile/models/base-entity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-base-entity" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/gateway/Cargo.toml b/crates/profile/models/gateway/Cargo.toml index fb8c393bd..25ad11c5b 100644 --- a/crates/profile/models/gateway/Cargo.toml +++ b/crates/profile/models/gateway/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-gateway" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/persona-data/Cargo.toml b/crates/profile/models/persona-data/Cargo.toml index 372c5a520..59f99ad49 100644 --- a/crates/profile/models/persona-data/Cargo.toml +++ b/crates/profile/models/persona-data/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-persona-data" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/persona/Cargo.toml b/crates/profile/models/persona/Cargo.toml index 39537ed62..d1b2fd0a8 100644 --- a/crates/profile/models/persona/Cargo.toml +++ b/crates/profile/models/persona/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-persona" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/profile_SPLIT_ME/Cargo.toml b/crates/profile/models/profile_SPLIT_ME/Cargo.toml index 86a386e70..77c98f774 100644 --- a/crates/profile/models/profile_SPLIT_ME/Cargo.toml +++ b/crates/profile/models/profile_SPLIT_ME/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/profile/models/security-structures/Cargo.toml b/crates/profile/models/security-structures/Cargo.toml index 76bad37c7..e9296d9b6 100644 --- a/crates/profile/models/security-structures/Cargo.toml +++ b/crates/profile/models/security-structures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-security-structures" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/models/supporting-types/Cargo.toml b/crates/profile/models/supporting-types/Cargo.toml index 56de26b6c..062605a62 100644 --- a/crates/profile/models/supporting-types/Cargo.toml +++ b/crates/profile/models/supporting-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-supporting-types" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/profile/traits/entity-by-address/Cargo.toml b/crates/profile/traits/entity-by-address/Cargo.toml index f654e6364..69be564cd 100644 --- a/crates/profile/traits/entity-by-address/Cargo.toml +++ b/crates/profile/traits/entity-by-address/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "entity-by-address" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/sargon/Cargo.toml b/crates/sargon/Cargo.toml index 5ef3f62b0..bf1b2c9db 100644 --- a/crates/sargon/Cargo.toml +++ b/crates/sargon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon" -version = "1.1.101" +version = "1.1.102" edition = "2021" resolver = "2" # features enabled in integration test diff --git a/crates/sargon/tests/integration/main.rs b/crates/sargon/tests/integration/main.rs index 082f11664..6ce53f612 100644 --- a/crates/sargon/tests/integration/main.rs +++ b/crates/sargon/tests/integration/main.rs @@ -384,14 +384,12 @@ mod integration_tests { transactions_to_sign: &IndexSet< TransactionSignRequestInput, >, - ) -> SignWithFactorsOutcome { - if request.invalid_transactions_if_neglected.is_empty() { - return SignWithFactorsOutcome::Neglected( - NeglectedFactors::new( - NeglectFactorReason::UserExplicitlySkipped, - IndexSet::just(factor_source_id), - ), - ); + ) -> Result> { + if request + .invalid_transactions_if_factor_neglected(&factor_source_id) + .is_empty() + { + return Ok(FactorOutcome::skipped(factor_source_id)); } let signatures = transactions_to_sign @@ -405,11 +403,7 @@ mod integration_tests { }) .collect::>>(); - SignWithFactorsOutcome::Signed { - produced_signatures: SignResponse::with_signatures( - signatures, - ), - } + FactorOutcome::signed(signatures) } } @@ -768,43 +762,27 @@ mod integration_tests { async fn sign( &self, request: SignRequest, - ) -> Result> - { - let mut signatures = - IndexSet::>::new(); + ) -> Result> { + let mut per_factor_outcome = IndexMap::< + FactorSourceIDFromHash, + FactorOutcome, + >::new(); for (factor_source_id, inputs) in request.per_factor_source.iter() { - let result = self - .sign_mono(*factor_source_id, &request, inputs) - .await; + let outcome = self + .sign_mono( + *factor_source_id, + &request, + &inputs.per_transaction, + ) + .await?; - match result { - SignWithFactorsOutcome::Signed { - produced_signatures, - } => { - signatures.extend( - produced_signatures - .signatures - .into_iter() - .flat_map(|(_, xs)| xs) - .collect::>(), - ); - } - SignWithFactorsOutcome::Neglected(_) => { - return Ok(SignWithFactorsOutcome::Neglected( - NeglectedFactors::new( - NeglectFactorReason::UserExplicitlySkipped, - request.factor_source_ids(), - ), - )); - } - } + per_factor_outcome.insert(*factor_source_id, outcome); } - Ok(SignWithFactorsOutcome::signed( - SignResponse::with_signatures(signatures), - )) + + SignResponse::new_from_outcomes(per_factor_outcome) } } diff --git a/crates/system/clients/clients/Cargo.toml b/crates/system/clients/clients/Cargo.toml index d4477077d..677a9e3d5 100644 --- a/crates/system/clients/clients/Cargo.toml +++ b/crates/system/clients/clients/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clients" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/system/clients/http/Cargo.toml b/crates/system/clients/http/Cargo.toml index 87488bbd9..d769ea819 100644 --- a/crates/system/clients/http/Cargo.toml +++ b/crates/system/clients/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http-client" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/drivers/Cargo.toml b/crates/system/drivers/Cargo.toml index 3d15587eb..e43e59b55 100644 --- a/crates/system/drivers/Cargo.toml +++ b/crates/system/drivers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drivers" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/system/drivers/src/drivers/secure_storage_driver/support/mod.rs b/crates/system/drivers/src/drivers/secure_storage_driver/support/mod.rs index 0247ff8d2..42cb13564 100644 --- a/crates/system/drivers/src/drivers/secure_storage_driver/support/mod.rs +++ b/crates/system/drivers/src/drivers/secure_storage_driver/support/mod.rs @@ -1,7 +1,5 @@ -mod secure_storage_access_error_kind; mod secure_storage_key; mod test; -pub use secure_storage_access_error_kind::*; pub use secure_storage_key::*; pub use test::*; diff --git a/crates/system/interactors/Cargo.toml b/crates/system/interactors/Cargo.toml index 59472f09d..a9c6ab962 100644 --- a/crates/system/interactors/Cargo.toml +++ b/crates/system/interactors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interactors" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/interactors/src/testing/test_use_factor_sources_interactors.rs b/crates/system/interactors/src/testing/test_use_factor_sources_interactors.rs index a402496f5..f694a2364 100644 --- a/crates/system/interactors/src/testing/test_use_factor_sources_interactors.rs +++ b/crates/system/interactors/src/testing/test_use_factor_sources_interactors.rs @@ -30,7 +30,7 @@ impl SignInteractor for TestUseFactorSourcesInteractors { async fn sign( &self, request: SignRequest, - ) -> Result> { + ) -> Result> { self.transaction_signing.sign(request).await } } @@ -40,7 +40,7 @@ impl SignInteractor for TestUseFactorSourcesInteractors { async fn sign( &self, request: SignRequest, - ) -> Result> { + ) -> Result> { self.subintent_signing.sign(request).await } } @@ -60,7 +60,7 @@ impl SignInteractor for TestUseFactorSourcesInteractors { async fn sign( &self, request: SignRequest, - ) -> Result> { + ) -> Result> { self.auth_signing.sign(request).await } } diff --git a/crates/system/os/accounts/Cargo.toml b/crates/system/os/accounts/Cargo.toml index 1436074f9..a3a0f9e39 100644 --- a/crates/system/os/accounts/Cargo.toml +++ b/crates/system/os/accounts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os-accounts" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/accounts/src/lib.rs b/crates/system/os/accounts/src/lib.rs index 007f1a99b..c74fa67c2 100644 --- a/crates/system/os/accounts/src/lib.rs +++ b/crates/system/os/accounts/src/lib.rs @@ -12,13 +12,13 @@ pub mod prelude { pub(crate) use sargon_os::prelude::*; pub(crate) use indexmap::IndexMap; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { pub(crate) use std::sync::Arc; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/system/os/factors/Cargo.toml b/crates/system/os/factors/Cargo.toml index 51bd9be26..1c5e5dd87 100644 --- a/crates/system/os/factors/Cargo.toml +++ b/crates/system/os/factors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os-factors" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/os/Cargo.toml b/crates/system/os/os/Cargo.toml index 2958dcb46..206b7c739 100644 --- a/crates/system/os/os/Cargo.toml +++ b/crates/system/os/os/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/os/src/lib.rs b/crates/system/os/os/src/lib.rs index aef326192..0fab765ae 100644 --- a/crates/system/os/os/src/lib.rs +++ b/crates/system/os/os/src/lib.rs @@ -41,11 +41,11 @@ pub mod prelude { pub(crate) use std::collections::HashSet; pub(crate) use std::sync::Arc; + #[cfg(test)] + pub(crate) use testing::*; + #[cfg(test)] mod testing { pub(crate) use serde_json::json; } - - #[cfg(test)] - pub(crate) use testing::*; } diff --git a/crates/system/os/security-center/Cargo.toml b/crates/system/os/security-center/Cargo.toml index 1816c97d5..bfe09b179 100644 --- a/crates/system/os/security-center/Cargo.toml +++ b/crates/system/os/security-center/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os-security-center" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/security-center/src/lib.rs b/crates/system/os/security-center/src/lib.rs index d4f2740e5..e85b29959 100644 --- a/crates/system/os/security-center/src/lib.rs +++ b/crates/system/os/security-center/src/lib.rs @@ -6,6 +6,8 @@ pub mod prelude { pub(crate) use error::prelude::*; pub(crate) use sargon_os::prelude::*; pub(crate) use security_center::prelude::*; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { @@ -13,8 +15,6 @@ pub mod prelude { pub(crate) use has_sample_values::prelude::*; pub(crate) use std::sync::Arc; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/system/os/signing/Cargo.toml b/crates/system/os/signing/Cargo.toml index de6206a50..456e30b8d 100644 --- a/crates/system/os/signing/Cargo.toml +++ b/crates/system/os/signing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os-signing" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/signing/src/lib.rs b/crates/system/os/signing/src/lib.rs index 133494bc2..ab44780a0 100644 --- a/crates/system/os/signing/src/lib.rs +++ b/crates/system/os/signing/src/lib.rs @@ -11,6 +11,8 @@ pub mod prelude { pub(crate) use signing_traits::prelude::*; pub(crate) use std::sync::Arc; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { @@ -21,8 +23,6 @@ pub mod prelude { pub(crate) use profile_logic::prelude::*; pub(crate) use radix_connect_models::prelude::*; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/system/os/transaction/Cargo.toml b/crates/system/os/transaction/Cargo.toml index 942edb11e..addb42a88 100644 --- a/crates/system/os/transaction/Cargo.toml +++ b/crates/system/os/transaction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-os-transaction" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/os/transaction/src/lib.rs b/crates/system/os/transaction/src/lib.rs index 95aad6bdf..88ebab454 100644 --- a/crates/system/os/transaction/src/lib.rs +++ b/crates/system/os/transaction/src/lib.rs @@ -17,13 +17,13 @@ pub mod prelude { pub(crate) use signing_traits::prelude::*; pub(crate) use radix_engine_interface::prelude::MetadataValue as ScryptoMetadataValue; + #[cfg(test)] + pub(crate) use testing::*; #[cfg(test)] mod testing { pub(crate) use std::sync::Arc; } - #[cfg(test)] - pub(crate) use testing::*; } pub use prelude::*; diff --git a/crates/system/profile-state-holder/Cargo.toml b/crates/system/profile-state-holder/Cargo.toml index f5aa5ede4..82d27464f 100644 --- a/crates/system/profile-state-holder/Cargo.toml +++ b/crates/system/profile-state-holder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profile-state-holder" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/system/sub-systems/Cargo.toml b/crates/system/sub-systems/Cargo.toml index 85afab7ea..aad9b46e5 100644 --- a/crates/system/sub-systems/Cargo.toml +++ b/crates/system/sub-systems/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sub-systems" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/transaction/foundation/Cargo.toml b/crates/transaction/foundation/Cargo.toml index 82db18e5c..a3941d8dd 100644 --- a/crates/transaction/foundation/Cargo.toml +++ b/crates/transaction/foundation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "transaction-foundation" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/transaction/manifests/Cargo.toml b/crates/transaction/manifests/Cargo.toml index a1bb733fe..d23040410 100644 --- a/crates/transaction/manifests/Cargo.toml +++ b/crates/transaction/manifests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "manifests" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/transaction/models/Cargo.toml b/crates/transaction/models/Cargo.toml index 718e4e984..7cf9baa0d 100644 --- a/crates/transaction/models/Cargo.toml +++ b/crates/transaction/models/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "transaction-models" -version = "1.1.101" +version = "1.1.102" edition = "2021" diff --git a/crates/uniffi/conversion-macros/Cargo.toml b/crates/uniffi/conversion-macros/Cargo.toml index 84077e792..bd8d795f7 100644 --- a/crates/uniffi/conversion-macros/Cargo.toml +++ b/crates/uniffi/conversion-macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sargon-uniffi-conversion-macros" -version = "1.1.101" +version = "1.1.102" edition = "2021" [dependencies] diff --git a/crates/uniffi/uniffi_SPLIT_ME/Cargo.toml b/crates/uniffi/uniffi_SPLIT_ME/Cargo.toml index 918568d0e..2b7c3a4ae 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/Cargo.toml +++ b/crates/uniffi/uniffi_SPLIT_ME/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon-uniffi" -version = "1.1.101" +version = "1.1.102" edition = "2021" build = "build.rs" diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/core/error/common_error.rs b/crates/uniffi/uniffi_SPLIT_ME/src/core/error/common_error.rs index f166cd2df..43b479159 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/core/error/common_error.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/core/error/common_error.rs @@ -225,7 +225,7 @@ pub enum CommonError { #[error("Failed to access secure storage due to \"{error_message}\" for key {} ", key)] SecureStorageAccessError { key: String, - error_kind: String, + error_kind: SecureStorageAccessErrorKind, error_message: String, } = 10063, @@ -852,6 +852,9 @@ pub enum CommonError { #[error("SecurityStateAccessController address mismatch")] SecurityStateAccessControllerAddressMismatch = 10244, + + #[error("Not all signatures are produced with the same factor source.")] + FactorOutcomeSignedFactorSourceIDMismatch = 10245, } #[uniffi::export] diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/factor_outcome.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/factor_outcome.rs new file mode 100644 index 000000000..bd29e1209 --- /dev/null +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/factor_outcome.rs @@ -0,0 +1,71 @@ +use crate::prelude::*; +use paste::paste; + +macro_rules! decl_factor_outcome { + ( + struct_name: $struct_name:ident, + internal_struct_name: $internal_struct_name:ident, + signature: $signature:ident, + new_signed: $new_signed:ident, + new_failure: $new_failure:ident, + new_skipped: $new_skipped:ident, + ) => { + /// The outcome of the signing process for each factor source as collected by the `SignInteractor`. + #[derive(Clone, PartialEq, Eq, InternalConversion, uniffi::Enum)] + pub enum $struct_name { + /// The user successfully signed with the factor source, the associated + /// value contains the produced signatures and any relevant metadata. + Signed { + produced_signatures: Vec<$signature>, + }, + + /// The factor source got neglected, either due to user explicitly skipping + /// or due to failure + Neglected(NeglectedFactor), + } + + #[uniffi::export] + pub fn $new_signed(produced_signatures: Vec<$signature>) -> Result<$struct_name> { + $internal_struct_name::signed( + sargon::IndexSet::from_iter( + produced_signatures.into_iter().map(|sig| sig.into_internal()) + ) + ).into_result() + } + + #[uniffi::export] + pub fn $new_failure(factor_source_id: FactorSourceIDFromHash) -> $struct_name { + $internal_struct_name::failure( + factor_source_id.into_internal() + ).into() + } + + #[uniffi::export] + pub fn $new_skipped(factor_source_id: FactorSourceIDFromHash) -> $struct_name { + $internal_struct_name::skipped( + factor_source_id.into_internal() + ).into() + } + }; + ($signable_id:ty) => { + paste! { + use sargon::[< $signable_id >] as [< Internal $signable_id >]; + + type [< InternalFactorOutcomeOf $signable_id >] = + sargon::FactorOutcome<[< Internal $signable_id >]>; + + decl_factor_outcome!( + struct_name: [< FactorOutcomeOf $signable_id >], + internal_struct_name: [< InternalFactorOutcomeOf $signable_id >], + signature: [< HDSignatureOf $signable_id >], + new_signed: [< new_signed_factor_outcome_of_ $signable_id:snake >], + new_failure: [< new_failure_factor_outcome_of_ $signable_id:snake >], + new_skipped: [< new_skipped_factor_outcome_of_ $signable_id:snake >], + ); + } + }; +} + +decl_factor_outcome!(TransactionIntentHash); +decl_factor_outcome!(SubintentHash); +decl_factor_outcome!(AuthIntentHash); diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/mod.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/mod.rs index d4fbf93d1..c62a38f0e 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/mod.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/mod.rs @@ -1,25 +1,27 @@ mod authentication; +mod factor_outcome; mod hd_signature; mod hd_signature_input; mod intent_signature_of_owner; mod invalid_transaction_if_neglected; -mod neglected_factors; +mod neglected_factor; +mod per_factor_outcome; +mod per_factor_source_input; mod sign_request; mod sign_response; -mod sign_with_factors_outcome; mod signatures_per_fractor_source; mod transaction_sign_request_input; -mod transactions_to_sign_per_factor_source; pub use authentication::*; +pub use factor_outcome::*; pub use hd_signature::*; pub use hd_signature_input::*; pub use intent_signature_of_owner::*; pub use invalid_transaction_if_neglected::*; -pub use neglected_factors::*; +pub use neglected_factor::*; +pub use per_factor_outcome::*; +pub use per_factor_source_input::*; pub use sign_request::*; pub use sign_response::*; -pub use sign_with_factors_outcome::*; pub use signatures_per_fractor_source::*; pub use transaction_sign_request_input::*; -pub use transactions_to_sign_per_factor_source::*; diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factors.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factor.rs similarity index 65% rename from crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factors.rs rename to crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factor.rs index 03e0f2c24..956cc3e87 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factors.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/neglected_factor.rs @@ -1,36 +1,36 @@ use crate::prelude::*; use sargon::FactorSourceIDFromHash as InternalFactorSourceIDFromHash; use sargon::NeglectFactorReason as InternalNeglectFactorReason; -type InternalNeglectedFactors = - sargon::AbstractNeglectedFactor>; +type InternalNeglectedFactor = + sargon::AbstractNeglectedFactor; #[derive(Clone, PartialEq, Eq, uniffi::Record)] -pub struct NeglectedFactors { +pub struct NeglectedFactor { /// The reason why this factor was neglected. pub reason: NeglectFactorReason, /// The neglected factors - pub factors: Vec, + pub factor: FactorSourceIDFromHash, } -impl NeglectedFactors { - pub fn into_internal(&self) -> InternalNeglectedFactors { +impl NeglectedFactor { + pub fn into_internal(&self) -> InternalNeglectedFactor { self.clone().into() } } -impl From for NeglectedFactors { - fn from(value: InternalNeglectedFactors) -> Self { +impl From for NeglectedFactor { + fn from(value: InternalNeglectedFactor) -> Self { Self { reason: value.reason.into(), - factors: value.content.into_iter().map(|id| id.into()).collect(), + factor: value.content.into(), } } } -impl From for InternalNeglectedFactors { - fn from(value: NeglectedFactors) -> Self { - Self::new(value.reason.into_internal(), value.factors.into_internal()) +impl From for InternalNeglectedFactor { + fn from(value: NeglectedFactor) -> Self { + Self::new(value.reason.into_internal(), value.factor.into_internal()) } } diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_outcome.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_outcome.rs new file mode 100644 index 000000000..d4e13031b --- /dev/null +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_outcome.rs @@ -0,0 +1,39 @@ +use crate::prelude::*; +use paste::paste; + +macro_rules! decl_per_factor_outcome { + ( + struct_name: $struct_name:ident, + factor_outcome: $factor_outcome:ident + ) => { + #[derive(Clone, PartialEq, Eq, uniffi::Record)] + pub struct $struct_name { + pub factor_source_id: FactorSourceIDFromHash, + pub outcome: $factor_outcome, + } + + impl $struct_name { + pub fn new( + factor_source_id: FactorSourceIDFromHash, + outcome: $factor_outcome, + ) -> Self { + Self { + factor_source_id, + outcome, + } + } + } + }; + ($signable_id:ty) => { + paste! { + decl_per_factor_outcome!( + struct_name: [< PerFactorOutcomeOf $signable_id >], + factor_outcome: [< FactorOutcomeOf $signable_id >] + ); + } + }; +} + +decl_per_factor_outcome!(TransactionIntentHash); +decl_per_factor_outcome!(SubintentHash); +decl_per_factor_outcome!(AuthIntentHash); diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_source_input.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_source_input.rs new file mode 100644 index 000000000..4d1e62af6 --- /dev/null +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/per_factor_source_input.rs @@ -0,0 +1,62 @@ +use crate::prelude::*; +use paste::paste; + +macro_rules! decl_per_factor_source_input { + ( + struct_name: $struct_name:ident, + per_transaction: $per_transaction:ident, + invalid_transaction_if_neglected: $invalid_transaction_if_neglected:ident, + ) => { + #[derive(Clone, PartialEq, Eq, uniffi::Record)] + pub struct $struct_name { + /// The factor source which the interactor should request signatures with + pub factor_source_id: FactorSourceIDFromHash, + + /// A set of transactions to sign, with multiple derivations paths. + pub per_transaction: Vec<$per_transaction>, + + /// A collection of transactions which would be invalid if the user skips + /// signing with this factor source. + pub invalid_transactions_if_neglected: Vec<$invalid_transaction_if_neglected> + } + + impl $struct_name { + pub fn new( + factor_source_id: FactorSourceIDFromHash, + per_transaction: Vec<$per_transaction>, + invalid_transactions_if_neglected: Vec<$invalid_transaction_if_neglected> + ) -> Self { + Self { + factor_source_id, + per_transaction, + invalid_transactions_if_neglected + } + } + } + }; + (signable: $signable:ty, signable_id: $signable_id:ty) => { + paste! { + use sargon::[< $signable >] as [< Internal $signable >]; + use sargon::[< $signable_id >] as [< Internal $signable_id >]; + + decl_per_factor_source_input!( + struct_name: [< PerFactorSourceInputOf $signable >], + per_transaction: [< TransactionSignRequestInputOf $signable >], + invalid_transaction_if_neglected: [< InvalidTransactionIfNeglectedOf $signable_id >], + ); + } + }; +} + +decl_per_factor_source_input!( + signable: TransactionIntent, + signable_id: TransactionIntentHash +); +decl_per_factor_source_input!( + signable: Subintent, + signable_id: SubintentHash +); +decl_per_factor_source_input!( + signable: AuthIntent, + signable_id: AuthIntentHash +); diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_request.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_request.rs index ea42e7d64..6a791bdf4 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_request.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_request.rs @@ -5,21 +5,18 @@ macro_rules! decl_sign_request { ( struct_name: $struct_name:ident, internal_struct_name: $internal_struct_name:ident, - per_factor_source: $per_factor_source:ident, - invalid_transactions_if_neglected: $invalid_transactions_if_neglected:ident, + per_factor_source_input: $per_factor_source_input:ident, + internal_per_factor_source_input: $internal_per_factor_source_input:ident, + new_sample: $new_sample:ident, + new_sample_other: $new_sample_other:ident, ) => { #[derive(Clone, PartialEq, Eq, uniffi::Record)] pub struct $struct_name { pub factor_source_kind: FactorSourceKind, - /// Per factor source, a set of transactions to sign, with - /// multiple derivations paths. - pub per_factor_source: Vec<$per_factor_source>, - - /// A collection of transactions which would be invalid if the user skips - /// signing with this factor source. - pub invalid_transactions_if_neglected: - Vec<$invalid_transactions_if_neglected>, + /// Per factor source, a set of inputs that contain information on what signables need signing, + /// and what signables will fail if such factor source is neglected. + pub per_factor_source: Vec<$per_factor_source_input>, } impl $struct_name { @@ -35,18 +32,20 @@ macro_rules! decl_sign_request { per_factor_source: value .per_factor_source .into_iter() - .map(|(id, transactions)| { - $per_factor_source::new( + .map(|(id, input)| { + $per_factor_source_input::new( id.into(), - transactions.into_iter().map(|t| t.into()).collect() + input.per_transaction + .into_iter() + .map(|t| t.into()) + .collect(), + input.invalid_transactions_if_neglected + .into_iter() + .map(|t| t.into()) + .collect() ) }) .collect(), - invalid_transactions_if_neglected: value - .invalid_transactions_if_neglected - .into_iter() - .map(|t| t.into()) - .collect(), } } } @@ -62,36 +61,54 @@ macro_rules! decl_sign_request { .map(|item| { ( item.factor_source_id.into_internal(), - item.transactions - .iter() - .map(|t| t.into_internal()) - .collect(), + $internal_per_factor_source_input::new( + item.factor_source_id.into_internal(), + item.per_transaction + .iter() + .map(|t| t.into_internal()) + .collect(), + item.invalid_transactions_if_neglected + .iter() + .map(|t| t.into_internal()) + .collect() + ) + ) }) .collect(), - invalid_transactions_if_neglected: value - .invalid_transactions_if_neglected - .iter() - .map(|t| t.into_internal()) - .collect(), } } } + #[uniffi::export] + pub fn $new_sample() -> $struct_name { + $internal_struct_name::sample().into() + } + + #[uniffi::export] + pub fn $new_sample_other() -> $struct_name { + $internal_struct_name::sample_other().into() + } + decl_conversion_tests_for!($struct_name); }; (signable: $signable:ty, signable_id: $signable_id:ty) => { paste! { use sargon::[< $signable >] as [< Internal $signable >]; + use sargon::[< $signable_id >] as [< Internal $signable_id >]; type [< InternalSignRequestOf $signable >] = sargon::SignRequest<[< Internal $signable >]>; + type [< InternalPerFactorSourceInputOf $signable >] = + sargon::PerFactorSourceInput<[< Internal $signable >]>; decl_sign_request!( struct_name: [< SignRequestOf $signable >], internal_struct_name: [< InternalSignRequestOf $signable >], - per_factor_source: [< TransactionToSignPerFactorSourceOf $signable >], - invalid_transactions_if_neglected: [< InvalidTransactionIfNeglectedOf $signable_id >], + per_factor_source_input: [< PerFactorSourceInputOf $signable >], + internal_per_factor_source_input: [< InternalPerFactorSourceInputOf $signable >], + new_sample: [< new_sign_request_of_ $signable:snake _sample >], + new_sample_other: [< new_sign_request_of_ $signable:snake _sample_other >], ); } }; diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_response.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_response.rs index 7ae89d899..965344c80 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_response.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_response.rs @@ -1,19 +1,18 @@ use crate::prelude::*; use paste::paste; +use sargon::IndexMap; macro_rules! decl_sign_response { ( struct_name: $struct_name:ident, internal_struct_name: $internal_struct_name:ident, - per_factor_source: $per_factor_source:ident, + per_factor_outcome: $per_factor_outcome:ident, + new_from_outcomes: $new_from_outcomes:ident, + new_from_skipping_factors: $new_from_skipping_factors:ident, ) => { - /// The response of a batch signing request, either a PolyFactor or MonoFactor signing - /// request, matters not, because the goal is to have signed all transactions with - /// enough keys (derivation paths) needed for it to be valid when submitted to the - /// Radix network. #[derive(Clone, PartialEq, Eq, uniffi::Record)] pub struct $struct_name { - pub per_factor_source: Vec<$per_factor_source>, + pub per_factor_outcome: Vec<$per_factor_outcome>, } impl $struct_name { @@ -25,34 +24,52 @@ macro_rules! decl_sign_response { impl From<$internal_struct_name> for $struct_name { fn from(value: $internal_struct_name) -> Self { Self { - per_factor_source: value.signatures + per_factor_outcome: value + .per_factor_outcome .into_iter() - .map(|(id, hd_signatures)| { - $per_factor_source::new( - id.into(), - hd_signatures.into_iter().map(|s| s.into()).collect() - ) - }) - .collect() + .map(|(id, outcome)| $per_factor_outcome::new(id.into(), outcome.into())) + .collect(), } } } impl From<$struct_name> for $internal_struct_name { fn from(value: $struct_name) -> Self { - Self::new( - IndexMap::from_iter(value.per_factor_source.into_iter().map( - |item| { - ( - item.factor_source_id.into_internal(), - item.hd_signatures.into_internal() - ) - }, - )), - ) + Self { + per_factor_outcome: sargon::IndexMap::from_iter( + value.per_factor_outcome.into_iter().map(|item| { + let factor_source_id = item.factor_source_id.into_internal(); + let internal_outcome = item.outcome.into_internal(); + (factor_source_id, internal_outcome) + }), + ), + } } } + + #[uniffi::export] + pub fn $new_from_outcomes( + outcomes: Vec<$per_factor_outcome> + ) -> Result<$struct_name> { + $internal_struct_name::new_from_outcomes( + sargon::IndexMap::from_iter(outcomes.into_iter().map(|item| { + let factor_source_id = item.factor_source_id.into_internal(); + let internal_outcome = item.outcome.into_internal(); + (factor_source_id, internal_outcome) + })) + ).into_result() + } + + #[uniffi::export] + pub fn $new_from_skipping_factors( + factors: Vec + ) -> $struct_name { + $internal_struct_name::user_skipped_factors( + sargon::IndexSet::from_iter(factors.iter().map(|f| f.into_internal())) + ).into() + } + decl_conversion_tests_for!($struct_name); }; ($signable_id:ty) => { @@ -65,7 +82,9 @@ macro_rules! decl_sign_response { decl_sign_response!( struct_name: [< SignResponseOf $signable_id >], internal_struct_name: [< InternalSignResponseOf $signable_id >], - per_factor_source: [< SignaturesPerFactorSourceOf $signable_id >], + per_factor_outcome: [< PerFactorOutcomeOf $signable_id >], + new_from_outcomes: [< new_sign_response_of_ $signable_id:snake _from_outcomes >], + new_from_skipping_factors: [< new_sign_response_of_ $signable_id:snake _from_skipping_factors >], ); } }; diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_with_factors_outcome.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_with_factors_outcome.rs deleted file mode 100644 index 51c7ae346..000000000 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/sign_with_factors_outcome.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::prelude::*; -use paste::paste; - -macro_rules! decl_sign_with_factors_outcome { - ( - struct_name: $struct_name:ident, - sign_response: $sign_response:ident, - ) => { - /// A batch of keys (derivation paths) all being factor instances of a HDFactorSource - /// with id `factor_source_id` to sign a single transaction with, which hash - /// is `intent_hash`. - #[derive(Clone, PartialEq, Eq, InternalConversion, uniffi::Enum)] - pub enum $struct_name { - /// The user successfully signed with the factor source(s), the associated - /// value contains the produces signatures and any relevant metadata. - Signed { produced_signatures: $sign_response }, - - /// The factor source got neglected, either due to user explicitly skipping - /// or due to failure - Neglected(NeglectedFactors), - } - }; - ($signable_id:ty) => { - paste! { - use sargon::[< $signable_id >] as [< Internal $signable_id >]; - - type [< InternalSignWithFactorsOutcomeOf $signable_id >] = - sargon::SignWithFactorsOutcome<[< Internal $signable_id >]>; - - decl_sign_with_factors_outcome!( - struct_name: [< SignWithFactorsOutcomeOf $signable_id >], - sign_response: [< SignResponseOf $signable_id >], - ); - } - }; -} - -decl_sign_with_factors_outcome!(TransactionIntentHash); -decl_sign_with_factors_outcome!(SubintentHash); -decl_sign_with_factors_outcome!(AuthIntentHash); diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/signing/transactions_to_sign_per_factor_source.rs b/crates/uniffi/uniffi_SPLIT_ME/src/signing/transactions_to_sign_per_factor_source.rs deleted file mode 100644 index 25a036653..000000000 --- a/crates/uniffi/uniffi_SPLIT_ME/src/signing/transactions_to_sign_per_factor_source.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::prelude::*; -use paste::paste; - -#[derive(Clone, PartialEq, Eq, uniffi::Record)] -pub struct TransactionToSignPerFactorSource { - pub factor_source_id: FactorSourceIDFromHash, - - pub transactions: Vec, -} - -impl TransactionToSignPerFactorSource { - pub fn new( - factor_source_id: FactorSourceIDFromHash, - transactions: Vec, - ) -> Self { - Self { - factor_source_id, - transactions, - } - } -} - -macro_rules! decl_transaction_to_sign_per_factor_source { - ( - struct_name: $struct_name:ident, - per_transaction: $per_transaction:ident, - ) => { - #[derive(Clone, PartialEq, Eq, uniffi::Record)] - pub struct $struct_name { - pub factor_source_id: FactorSourceIDFromHash, - - pub transactions: Vec<$per_transaction>, - } - - impl $struct_name { - pub fn new( - factor_source_id: FactorSourceIDFromHash, - transactions: Vec<$per_transaction>, - ) -> Self { - Self { - factor_source_id, - transactions, - } - } - } - }; - (signable: $signable:ty, signable_id: $signable_id:ty) => { - paste! { - - decl_transaction_to_sign_per_factor_source!( - struct_name: [< TransactionToSignPerFactorSourceOf $signable >], - per_transaction: [< TransactionSignRequestInputOf $signable >], - ); - } - }; -} - -decl_transaction_to_sign_per_factor_source!( - signable: TransactionIntent, - signable_id: TransactionIntentHash -); -decl_transaction_to_sign_per_factor_source!( - signable: Subintent, - signable_id: SubintentHash -); -decl_transaction_to_sign_per_factor_source!( - signable: AuthIntent, - signable_id: AuthIntentHash -); diff --git a/crates/uniffi/uniffi_SPLIT_ME/src/system/interactors/host_interactor.rs b/crates/uniffi/uniffi_SPLIT_ME/src/system/interactors/host_interactor.rs index 7389bef40..c8a36ac58 100644 --- a/crates/uniffi/uniffi_SPLIT_ME/src/system/interactors/host_interactor.rs +++ b/crates/uniffi/uniffi_SPLIT_ME/src/system/interactors/host_interactor.rs @@ -15,13 +15,13 @@ use sargon::UseFactorSourcesInteractor as InternalUseFactorSourcesInteractor; type InternalSignRequestForTransactionIntent = sargon::SignRequest; type InternalSignWithFactorsOutcomeForTransactionIntent = - sargon::SignWithFactorsOutcome; + sargon::SignResponse; type InternalSignRequestForSubintent = sargon::SignRequest; type InternalSignWithFactorsOutcomeForSubintent = - sargon::SignWithFactorsOutcome; + sargon::SignResponse; type InternalSignRequestForAuthIntent = sargon::SignRequest; type InternalSignWithFactorsOutcomeForAuthIntent = - sargon::SignWithFactorsOutcome; + sargon::SignResponse; /// Sargon os #[uniffi::export(with_foreign)] @@ -30,12 +30,12 @@ pub trait HostInteractor: Send + Sync + std::fmt::Debug { async fn sign_transactions( &self, request: SignRequestOfTransactionIntent, - ) -> Result; + ) -> Result; async fn sign_subintents( &self, request: SignRequestOfSubintent, - ) -> Result; + ) -> Result; async fn derive_keys( &self, @@ -45,7 +45,7 @@ pub trait HostInteractor: Send + Sync + std::fmt::Debug { async fn sign_auth( &self, request: SignRequestOfAuthIntent, - ) -> Result; + ) -> Result; } #[derive(Debug)] diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/AccountForDisplay.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/AccountForDisplay.kt new file mode 100644 index 000000000..1167ea792 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/AccountForDisplay.kt @@ -0,0 +1,9 @@ +package com.radixdlt.sargon.extensions + +import com.radixdlt.sargon.Account +import com.radixdlt.sargon.AccountForDisplay +import com.radixdlt.sargon.newAccountForDisplayFromAccount + +fun AccountForDisplay.Companion.from(account: Account) = newAccountForDisplayFromAccount( + account = account +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/FactorOutcome.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/FactorOutcome.kt new file mode 100644 index 000000000..2fc9600de --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/FactorOutcome.kt @@ -0,0 +1,58 @@ +package com.radixdlt.sargon.extensions + +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.FactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.FactorOutcomeOfSubintentHash +import com.radixdlt.sargon.FactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.HdSignatureOfAuthIntentHash +import com.radixdlt.sargon.HdSignatureOfSubintentHash +import com.radixdlt.sargon.HdSignatureOfTransactionIntentHash +import com.radixdlt.sargon.newFailureFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.newFailureFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.newFailureFactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.newSignedFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.newSignedFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.newSignedFactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.newSkippedFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.newSkippedFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.newSkippedFactorOutcomeOfTransactionIntentHash + +@Throws(CommonException::class) +fun FactorOutcomeOfTransactionIntentHash.Companion.signed( + producedSignatures: List +): FactorOutcomeOfTransactionIntentHash = newSignedFactorOutcomeOfTransactionIntentHash( + producedSignatures = producedSignatures +) + +@Throws(CommonException::class) +fun FactorOutcomeOfSubintentHash.Companion.signed( + producedSignatures: List +): FactorOutcomeOfSubintentHash = newSignedFactorOutcomeOfSubintentHash( + producedSignatures = producedSignatures +) + +@Throws(CommonException::class) +fun FactorOutcomeOfAuthIntentHash.Companion.signed( + producedSignatures: List +): FactorOutcomeOfAuthIntentHash = newSignedFactorOutcomeOfAuthIntentHash( + producedSignatures = producedSignatures +) + +fun FactorOutcomeOfTransactionIntentHash.Companion.skipped( + factorSourceId: FactorSourceIdFromHash +): FactorOutcomeOfTransactionIntentHash = newSkippedFactorOutcomeOfTransactionIntentHash( + factorSourceId = factorSourceId +) + +fun FactorOutcomeOfSubintentHash.Companion.skipped( + factorSourceId: FactorSourceIdFromHash +): FactorOutcomeOfSubintentHash = newSkippedFactorOutcomeOfSubintentHash( + factorSourceId = factorSourceId +) + +fun FactorOutcomeOfAuthIntentHash.Companion.skipped( + factorSourceId: FactorSourceIdFromHash +): FactorOutcomeOfAuthIntentHash = newSkippedFactorOutcomeOfAuthIntentHash( + factorSourceId = factorSourceId +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/SignResponse.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/SignResponse.kt new file mode 100644 index 000000000..fa2ec6d49 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/SignResponse.kt @@ -0,0 +1,27 @@ +package com.radixdlt.sargon.extensions + +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.PerFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.PerFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.PerFactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.SignResponseOfAuthIntentHash +import com.radixdlt.sargon.SignResponseOfSubintentHash +import com.radixdlt.sargon.SignResponseOfTransactionIntentHash +import com.radixdlt.sargon.newSignResponseOfAuthIntentHashFromOutcomes +import com.radixdlt.sargon.newSignResponseOfSubintentHashFromOutcomes +import com.radixdlt.sargon.newSignResponseOfTransactionIntentHashFromOutcomes + +@Throws(CommonException::class) +fun SignResponseOfTransactionIntentHash.Companion.fromOutcomes( + outcomes: List +) = newSignResponseOfTransactionIntentHashFromOutcomes(outcomes = outcomes) + +@Throws(CommonException::class) +fun SignResponseOfSubintentHash.Companion.fromOutcomes( + outcomes: List +) = newSignResponseOfSubintentHashFromOutcomes(outcomes = outcomes) + +@Throws(CommonException::class) +fun SignResponseOfAuthIntentHash.Companion.fromOutcomes( + outcomes: List +) = newSignResponseOfAuthIntentHashFromOutcomes(outcomes = outcomes) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/driver/AndroidBiometricAuthorizationDriver.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/driver/AndroidBiometricAuthorizationDriver.kt index 493ca7930..d557c9026 100644 --- a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/driver/AndroidBiometricAuthorizationDriver.kt +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/driver/AndroidBiometricAuthorizationDriver.kt @@ -40,7 +40,7 @@ class BiometricsFailure( key: SecureStorageKey ): CommonException = CommonException.SecureStorageAccessException( key = secureStorageKeyIdentifier(key), - errorKind = secureStorageAccessErrorKindToString(kind = when (errorCode) { + errorKind = when (errorCode) { BiometricPrompt.ERROR_CANCELED -> SecureStorageAccessErrorKind.CANCELLED BiometricPrompt.ERROR_HW_NOT_PRESENT -> SecureStorageAccessErrorKind.HARDWARE_NOT_PRESENT BiometricPrompt.ERROR_HW_UNAVAILABLE -> SecureStorageAccessErrorKind.HARDWARE_UNAVAILABLE @@ -55,7 +55,7 @@ class BiometricsFailure( BiometricPrompt.ERROR_USER_CANCELED -> SecureStorageAccessErrorKind.USER_CANCELLED BiometricPrompt.ERROR_VENDOR -> SecureStorageAccessErrorKind.VENDOR else -> throw CommonException.Unknown() - }), + }, errorMessage = errorMessage.orEmpty() ) diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/FactorOutcome.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/FactorOutcome.kt new file mode 100644 index 000000000..cda8e70f2 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/FactorOutcome.kt @@ -0,0 +1,74 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.FactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.FactorOutcomeOfSubintentHash +import com.radixdlt.sargon.FactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.NeglectedFactor +import com.radixdlt.sargon.extensions.signed +import kotlin.jvm.Throws + +sealed interface FactorOutcome { + + /** + * The user successfully signed with the factor source, the associated + * value contains the produced signatures and any relevant metadata. + */ + data class Signed( + val producedSignatures: List> + ) : FactorOutcome + + /** + * The factor source got neglected, either due to user explicitly skipping + * or due to failure + */ + data class Neglected( + val factor: NeglectedFactor + ) : FactorOutcome +} + +@Throws(CommonException::class) +internal fun FactorOutcome.intoSargon() = when (this) { + is FactorOutcome.Signed -> intoSargon() + is FactorOutcome.Neglected -> intoSargon() +} + +@Throws(CommonException::class) +internal fun FactorOutcome.intoSargon() = when (this) { + is FactorOutcome.Signed -> intoSargon() + is FactorOutcome.Neglected -> intoSargon() +} + +@Throws(CommonException::class) +internal fun FactorOutcome.intoSargon() = when (this) { + is FactorOutcome.Signed -> intoSargon() + is FactorOutcome.Neglected -> intoSargon() +} + + +@Throws(CommonException::class) +internal fun FactorOutcome.Signed.intoSargon() = + FactorOutcomeOfTransactionIntentHash.signed( + producedSignatures = producedSignatures.map { it.intoSargon() } + ) + +@Throws(CommonException::class) +internal fun FactorOutcome.Signed.intoSargon() = + FactorOutcomeOfSubintentHash.signed( + producedSignatures = producedSignatures.map { it.intoSargon() } + ) + + +internal fun FactorOutcome.Signed.intoSargon() = + FactorOutcomeOfAuthIntentHash.signed( + producedSignatures = producedSignatures.map { it.intoSargon() } + ) + +internal fun FactorOutcome.Neglected.intoSargon() = + FactorOutcomeOfTransactionIntentHash.Neglected(v1 = factor) + +internal fun FactorOutcome.Neglected.intoSargon() = + FactorOutcomeOfSubintentHash.Neglected(v1 = factor) + +internal fun FactorOutcome.Neglected.intoSargon() = + FactorOutcomeOfAuthIntentHash.Neglected(v1 = factor) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignature.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignature.kt new file mode 100644 index 000000000..a2fef84e1 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignature.kt @@ -0,0 +1,38 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.HdSignatureOfAuthIntentHash +import com.radixdlt.sargon.HdSignatureOfSubintentHash +import com.radixdlt.sargon.HdSignatureOfTransactionIntentHash +import com.radixdlt.sargon.SignatureWithPublicKey + +data class HdSignature ( + /** + * The input used to produce this `HDSignature` + */ + val input: HdSignatureInput, + /** + * The ECDSA/EdDSA signature produced by the private key of the + * `owned_hd_factor_instance.public_key`, + * derived by the HDFactorSource identified by + * `owned_hd_factor_ + * instance.factor_s + * ource_id` and which + * was derived at `owned_hd_factor_instance.derivation_path`. + */ + val signature: SignatureWithPublicKey +) + +internal fun HdSignature.intoSargon() = HdSignatureOfTransactionIntentHash( + input = input.intoSargon(), + signature = signature +) + +internal fun HdSignature.intoSargon() = HdSignatureOfSubintentHash( + input = input.intoSargon(), + signature = signature +) + +internal fun HdSignature.intoSargon() = HdSignatureOfAuthIntentHash( + input = input.intoSargon(), + signature = signature +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignatureInput.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignatureInput.kt new file mode 100644 index 000000000..1a6a2d264 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/HDSignatureInput.kt @@ -0,0 +1,38 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.HdSignatureInputOfAuthIntentHash +import com.radixdlt.sargon.HdSignatureInputOfSubintentHash +import com.radixdlt.sargon.HdSignatureInputOfTransactionIntentHash +import com.radixdlt.sargon.HdSignatureOfTransactionIntentHash +import com.radixdlt.sargon.OwnedFactorInstance + +data class HdSignatureInput ( + /** + * Hash which was signed. + */ + val payloadId: ID, + /** + * The account or identity address of the entity which signed the hash, + * with expected public key and with derivation path to derive PrivateKey + * with. + */ + val ownedFactorInstance: OwnedFactorInstance +) + +internal fun HdSignatureInput.intoSargon() + = HdSignatureInputOfTransactionIntentHash( + payloadId = payloadId.value, + ownedFactorInstance = ownedFactorInstance + ) + +internal fun HdSignatureInput.intoSargon() + = HdSignatureInputOfSubintentHash( + payloadId = payloadId.value, + ownedFactorInstance = ownedFactorInstance +) + +internal fun HdSignatureInput.intoSargon() + = HdSignatureInputOfAuthIntentHash( + payloadId = payloadId.value, + ownedFactorInstance = ownedFactorInstance +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/InvalidTransactionIfNeglected.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/InvalidTransactionIfNeglected.kt new file mode 100644 index 000000000..d7db4a0f7 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/InvalidTransactionIfNeglected.kt @@ -0,0 +1,42 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.AddressOfAccountOrPersona +import com.radixdlt.sargon.InvalidTransactionIfNeglectedOfAuthIntentHash +import com.radixdlt.sargon.InvalidTransactionIfNeglectedOfSubintentHash +import com.radixdlt.sargon.InvalidTransactionIfNeglectedOfTransactionIntentHash + +/** + * A list of entities which would fail in a transaction if we would + * neglect certain factor source, either by user explicitly skipping + * it or if implicitly neglected due to failure. + */ +data class InvalidTransactionIfNeglected( + /** + * The intent hash of the transaction which would be invalid if a + * certain factor source would be neglected, either if user + * explicitly skipped it or implicitly neglected due to failure. + */ + val signableId: ID, + /** + * The entities in the transaction which would fail auth. + */ + val entitiesWhichWouldFailAuth: List +) + +internal fun InvalidTransactionIfNeglectedOfTransactionIntentHash.into() + = InvalidTransactionIfNeglected( + signableId = Signable.ID.Transaction(value = signableId), + entitiesWhichWouldFailAuth = entitiesWhichWouldFailAuth + ) + +internal fun InvalidTransactionIfNeglectedOfSubintentHash.into() + = InvalidTransactionIfNeglected( + signableId = Signable.ID.Subintent(value = signableId), + entitiesWhichWouldFailAuth = entitiesWhichWouldFailAuth + ) + +internal fun InvalidTransactionIfNeglectedOfAuthIntentHash.into() + = InvalidTransactionIfNeglected( + signableId = Signable.ID.Auth(value = signableId), + entitiesWhichWouldFailAuth = entitiesWhichWouldFailAuth + ) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorOutcome.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorOutcome.kt new file mode 100644 index 000000000..3b662c794 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorOutcome.kt @@ -0,0 +1,32 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.PerFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.PerFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.PerFactorOutcomeOfTransactionIntentHash +import kotlin.jvm.Throws + +data class PerFactorOutcome( + val factorSourceId: FactorSourceIdFromHash, + val outcome: FactorOutcome +) + +@Throws(CommonException::class) +fun PerFactorOutcome.intoSargon() = + PerFactorOutcomeOfTransactionIntentHash( + factorSourceId = factorSourceId, + outcome = outcome.intoSargon() + ) + +@Throws(CommonException::class) +fun PerFactorOutcome.intoSargon() = PerFactorOutcomeOfSubintentHash( + factorSourceId = factorSourceId, + outcome = outcome.intoSargon() +) + +@Throws(CommonException::class) +fun PerFactorOutcome.intoSargon() = PerFactorOutcomeOfAuthIntentHash( + factorSourceId = factorSourceId, + outcome = outcome.intoSargon() +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorSourceInput.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorSourceInput.kt new file mode 100644 index 000000000..97338198b --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/PerFactorSourceInput.kt @@ -0,0 +1,40 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.PerFactorSourceInputOfAuthIntent +import com.radixdlt.sargon.PerFactorSourceInputOfSubintent +import com.radixdlt.sargon.PerFactorSourceInputOfTransactionIntent + +data class PerFactorSourceInput( + /** + * The factor source which the interactor should request signatures with + */ + val factorSourceId: FactorSourceIdFromHash, + /** + * A set of transactions to sign, with multiple derivations paths. + */ + val perTransaction: List>, + /** + * A collection of transactions which would be invalid if the user skips + * signing with this factor source. + */ + val invalidTransactionsIfNeglected: List> +) + +fun PerFactorSourceInputOfTransactionIntent.into() = PerFactorSourceInput( + factorSourceId = factorSourceId, + perTransaction = perTransaction.map { it.into() }, + invalidTransactionsIfNeglected = invalidTransactionsIfNeglected.map { it.into() } +) + +fun PerFactorSourceInputOfSubintent.into() = PerFactorSourceInput( + factorSourceId = factorSourceId, + perTransaction = perTransaction.map { it.into() }, + invalidTransactionsIfNeglected = invalidTransactionsIfNeglected.map { it.into() } +) + +fun PerFactorSourceInputOfAuthIntent.into() = PerFactorSourceInput( + factorSourceId = factorSourceId, + perTransaction = perTransaction.map { it.into() }, + invalidTransactionsIfNeglected = invalidTransactionsIfNeglected.map { it.into() } +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignRequest.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignRequest.kt new file mode 100644 index 000000000..ace0936e6 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignRequest.kt @@ -0,0 +1,28 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.FactorSourceKind +import com.radixdlt.sargon.SignRequestOfAuthIntent +import com.radixdlt.sargon.SignRequestOfSubintent +import com.radixdlt.sargon.SignRequestOfTransactionIntent + +data class SignRequest( + val factorSourceKind: FactorSourceKind, + val perFactorSource: List> +) { + companion object +} + +fun SignRequestOfTransactionIntent.into() = SignRequest( + factorSourceKind = factorSourceKind, + perFactorSource = perFactorSource.map { it.into() } +) + +fun SignRequestOfSubintent.into() = SignRequest( + factorSourceKind = factorSourceKind, + perFactorSource = perFactorSource.map { it.into() } +) + +fun SignRequestOfAuthIntent.into() = SignRequest( + factorSourceKind = factorSourceKind, + perFactorSource = perFactorSource.map { it.into() } +) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignResponse.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignResponse.kt new file mode 100644 index 000000000..51717bd6b --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/SignResponse.kt @@ -0,0 +1,30 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.SignResponseOfAuthIntentHash +import com.radixdlt.sargon.SignResponseOfSubintentHash +import com.radixdlt.sargon.SignResponseOfTransactionIntentHash +import com.radixdlt.sargon.extensions.fromOutcomes +import kotlin.jvm.Throws + +data class SignResponse( + val perFactorOutcome: List> +) + +@Throws(CommonException::class) +fun SignResponse.intoSargon() + = SignResponseOfTransactionIntentHash.fromOutcomes( + outcomes = perFactorOutcome.map { it.intoSargon() } + ) + +@Throws(CommonException::class) +fun SignResponse.intoSargon() + = SignResponseOfSubintentHash.fromOutcomes( + outcomes = perFactorOutcome.map { it.intoSargon() } + ) + +@Throws(CommonException::class) +fun SignResponse.intoSargon() + = SignResponseOfAuthIntentHash.fromOutcomes( + outcomes = perFactorOutcome.map { it.intoSargon() } + ) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/Signable.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/Signable.kt new file mode 100644 index 000000000..e0aa4a458 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/Signable.kt @@ -0,0 +1,107 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.AuthIntent +import com.radixdlt.sargon.AuthIntentHash +import com.radixdlt.sargon.CompiledSubintent +import com.radixdlt.sargon.CompiledTransactionIntent +import com.radixdlt.sargon.Hash +import com.radixdlt.sargon.SubintentHash +import com.radixdlt.sargon.TransactionIntent +import com.radixdlt.sargon.TransactionIntentHash +import com.radixdlt.sargon.extensions.compile +import com.radixdlt.sargon.extensions.decompile +import com.radixdlt.sargon.extensions.hash + +/** + * A "kotlinised" representation of sargon's Signable trait and its implementations. + */ +sealed interface Signable { + + fun getPayload(): Payload + + fun getId(): ID + + fun hash(): Hash = getId().hash() + + data class Transaction( + val value: TransactionIntent + ) : Signable { + override fun getPayload(): Payload = Payload.Transaction( + value = value.compile() + ) + + override fun getId(): ID = ID.Transaction( + value = value.hash() + ) + } + + data class Subintent( + val value: com.radixdlt.sargon.Subintent + ) : Signable { + override fun getPayload(): Payload = Payload.Subintent( + value = value.compile() + ) + + override fun getId(): ID = ID.Subintent( + value = value.hash() + ) + } + + data class Auth( + val value: AuthIntent + ) : Signable { + override fun getPayload(): Payload = Payload.Auth( + value = value + ) + + override fun getId(): ID = ID.Auth( + value = value.hash() + ) + } + + sealed interface Payload { + + fun getSignable(): Signable + + data class Transaction( + val value: CompiledTransactionIntent + ) : Payload { + override fun getSignable(): Signable = Transaction(value.decompile()) + } + + data class Subintent( + val value: CompiledSubintent + ) : Payload { + override fun getSignable(): Signable = Subintent(value.decompile()) + } + + data class Auth( + val value: AuthIntent + ) : Payload { + override fun getSignable(): Signable = Signable.Auth(value) + } + } + + sealed interface ID { + + fun hash(): Hash + + data class Transaction( + val value: TransactionIntentHash + ) : ID { + override fun hash(): Hash = value.hash + } + + data class Subintent( + val value: SubintentHash + ) : ID { + override fun hash(): Hash = value.hash + } + + data class Auth( + val value: AuthIntentHash + ) : ID { + override fun hash(): Hash = value.payload.hash() + } + } +} \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/TransactionSignRequestInput.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/TransactionSignRequestInput.kt new file mode 100644 index 000000000..a1af45dae --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/os/signing/TransactionSignRequestInput.kt @@ -0,0 +1,44 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.OwnedFactorInstance +import com.radixdlt.sargon.TransactionSignRequestInputOfAuthIntent +import com.radixdlt.sargon.TransactionSignRequestInputOfSubintent +import com.radixdlt.sargon.TransactionSignRequestInputOfTransactionIntent + +data class TransactionSignRequestInput ( + /** + * Payload to sign + */ + val payload: SP, + /** + * ID of factor to use to sign + */ + val factorSourceId: FactorSourceIdFromHash, + /** + * The derivation paths to use to derive the private keys to sign with. The + * `factor_source_id` of each item must match `factor_source_id`. + */ + val ownedFactorInstances: List +) + +internal fun TransactionSignRequestInputOfTransactionIntent.into() + = TransactionSignRequestInput( + payload = Signable.Payload.Transaction(value = payload), + factorSourceId = factorSourceId, + ownedFactorInstances = ownedFactorInstances + ) + +internal fun TransactionSignRequestInputOfSubintent.into() + = TransactionSignRequestInput( + payload = Signable.Payload.Subintent(value = payload), + factorSourceId = factorSourceId, + ownedFactorInstances = ownedFactorInstances + ) + +internal fun TransactionSignRequestInputOfAuthIntent.into() + = TransactionSignRequestInput( + payload = Signable.Payload.Auth(value = payload), + factorSourceId = factorSourceId, + ownedFactorInstances = ownedFactorInstances + ) \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/AccountForDisplaySample.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/AccountForDisplaySample.kt new file mode 100644 index 000000000..7fd9d12e1 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/AccountForDisplaySample.kt @@ -0,0 +1,14 @@ +package com.radixdlt.sargon.samples + +import com.radixdlt.sargon.AccountForDisplay +import com.radixdlt.sargon.annotation.UsesSampleValues +import com.radixdlt.sargon.newAccountForDisplaySample +import com.radixdlt.sargon.newAccountForDisplaySampleOther + +@UsesSampleValues +val AccountForDisplay.Companion.sample: Sample + get() = object : Sample { + override fun invoke(): AccountForDisplay = newAccountForDisplaySample() + + override fun other(): AccountForDisplay = newAccountForDisplaySampleOther() + } \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/FactorSourceIdFromHashSample.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/FactorSourceIdFromHashSample.kt new file mode 100644 index 000000000..fb3c6a114 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/FactorSourceIdFromHashSample.kt @@ -0,0 +1,15 @@ +package com.radixdlt.sargon.samples + +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.annotation.UsesSampleValues +import com.radixdlt.sargon.newFactorSourceIdFromHashSample +import com.radixdlt.sargon.newFactorSourceIdFromHashSampleOther + +@UsesSampleValues +val FactorSourceIdFromHash.Companion.sample: Sample + get() = object : Sample { + override fun invoke(): FactorSourceIdFromHash = newFactorSourceIdFromHashSample() + + override fun other(): FactorSourceIdFromHash = newFactorSourceIdFromHashSampleOther() + + } \ No newline at end of file diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/SignRequestSample.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/SignRequestSample.kt new file mode 100644 index 000000000..791590a58 --- /dev/null +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/samples/SignRequestSample.kt @@ -0,0 +1,46 @@ +package com.radixdlt.sargon.samples + +import com.radixdlt.sargon.annotation.UsesSampleValues +import com.radixdlt.sargon.newSignRequestOfAuthIntentSample +import com.radixdlt.sargon.newSignRequestOfAuthIntentSampleOther +import com.radixdlt.sargon.newSignRequestOfSubintentSample +import com.radixdlt.sargon.newSignRequestOfSubintentSampleOther +import com.radixdlt.sargon.newSignRequestOfTransactionIntentSample +import com.radixdlt.sargon.newSignRequestOfTransactionIntentSampleOther +import com.radixdlt.sargon.os.signing.SignRequest +import com.radixdlt.sargon.os.signing.Signable.ID +import com.radixdlt.sargon.os.signing.Signable.Payload +import com.radixdlt.sargon.os.signing.into + +@UsesSampleValues +val SignRequest.Companion.sampleTransactionIntent: Sample> + get() = object : Sample> { + override fun invoke(): SignRequest + = newSignRequestOfTransactionIntentSample().into() + + + override fun other(): SignRequest + = newSignRequestOfTransactionIntentSampleOther().into() + } + +@UsesSampleValues +val SignRequest.Companion.sampleSubintent: Sample> + get() = object : Sample> { + override fun invoke(): SignRequest + = newSignRequestOfSubintentSample().into() + + + override fun other(): SignRequest + = newSignRequestOfSubintentSampleOther().into() + } + +@UsesSampleValues +val SignRequest.Companion.sampleAuthIntent: Sample> + get() = object : Sample> { + override fun invoke(): SignRequest + = newSignRequestOfAuthIntentSample().into() + + + override fun other(): SignRequest + = newSignRequestOfAuthIntentSampleOther().into() + } \ No newline at end of file diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/AccountForDisplayTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/AccountForDisplayTest.kt new file mode 100644 index 000000000..167eeaf05 --- /dev/null +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/AccountForDisplayTest.kt @@ -0,0 +1,28 @@ +package com.radixdlt.sargon + +import com.radixdlt.sargon.extensions.from +import com.radixdlt.sargon.samples.Sample +import com.radixdlt.sargon.samples.sample +import com.radixdlt.sargon.samples.sampleMainnet +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class AccountForDisplayTest: SampleTestable { + override val samples: List> + get() = listOf(AccountForDisplay.sample) + + @Test + fun testFromAccount() { + val account = Account.sampleMainnet() + + assertEquals( + AccountForDisplay( + address = account.address, + displayName = account.displayName, + appearanceId = account.appearanceId + ), + AccountForDisplay.from(account) + ) + + } +} \ No newline at end of file diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/TransactionManifestTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/TransactionManifestTest.kt index 46df2aa3a..bdc51bc4a 100644 --- a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/TransactionManifestTest.kt +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/TransactionManifestTest.kt @@ -580,7 +580,7 @@ class TransactionManifestTest : SampleTestable { val summary = TransactionManifest.sample().summary assertEquals( listOf(AccountAddress.sampleMainnet()), - summary?.addressesOfAccountsWithdrawnFrom + summary.addressesOfAccountsWithdrawnFrom ) } diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/interactor/FakeHostInteractor.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/interactor/FakeHostInteractor.kt index 75104a31a..7ed095f99 100644 --- a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/interactor/FakeHostInteractor.kt +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/interactor/FakeHostInteractor.kt @@ -7,18 +7,18 @@ import com.radixdlt.sargon.KeyDerivationResponse import com.radixdlt.sargon.SignRequestOfAuthIntent import com.radixdlt.sargon.SignRequestOfSubintent import com.radixdlt.sargon.SignRequestOfTransactionIntent -import com.radixdlt.sargon.SignWithFactorsOutcomeOfAuthIntentHash -import com.radixdlt.sargon.SignWithFactorsOutcomeOfSubintentHash -import com.radixdlt.sargon.SignWithFactorsOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.SignResponseOfAuthIntentHash +import com.radixdlt.sargon.SignResponseOfSubintentHash +import com.radixdlt.sargon.SignResponseOfTransactionIntentHash class FakeHostInteractor: HostInteractor { override suspend fun signTransactions( request: SignRequestOfTransactionIntent - ): SignWithFactorsOutcomeOfTransactionIntentHash { + ): SignResponseOfTransactionIntentHash { throw CommonException.SigningRejected() } - override suspend fun signSubintents(request: SignRequestOfSubintent): SignWithFactorsOutcomeOfSubintentHash { + override suspend fun signSubintents(request: SignRequestOfSubintent): SignResponseOfSubintentHash { throw CommonException.SigningRejected() } @@ -26,7 +26,7 @@ class FakeHostInteractor: HostInteractor { throw CommonException.SigningRejected() } - override suspend fun signAuth(request: SignRequestOfAuthIntent): SignWithFactorsOutcomeOfAuthIntentHash { + override suspend fun signAuth(request: SignRequestOfAuthIntent): SignResponseOfAuthIntentHash { throw CommonException.SigningRejected() } diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignRequestTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignRequestTest.kt new file mode 100644 index 000000000..84d9f8bad --- /dev/null +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignRequestTest.kt @@ -0,0 +1,72 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.samples.sampleAuthIntent +import com.radixdlt.sargon.samples.sampleSubintent +import com.radixdlt.sargon.samples.sampleTransactionIntent +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Test + +class SignRequestTest { + + @Test + fun testEqualityForTransactionIntent() { + assertEquals( + SignRequest.sampleTransactionIntent(), + SignRequest.sampleTransactionIntent(), + ) + assertEquals( + SignRequest.sampleTransactionIntent.other(), + SignRequest.sampleTransactionIntent.other(), + ) + } + + @Test + fun testInequalityForTransactionIntent() { + assertNotEquals( + SignRequest.sampleTransactionIntent(), + SignRequest.sampleTransactionIntent.other(), + ) + } + + @Test + fun testEqualityForSubintent() { + assertEquals( + SignRequest.sampleSubintent(), + SignRequest.sampleSubintent(), + ) + assertEquals( + SignRequest.sampleSubintent.other(), + SignRequest.sampleSubintent.other(), + ) + } + + @Test + fun testInequalityForSubintent() { + assertNotEquals( + SignRequest.sampleSubintent(), + SignRequest.sampleSubintent.other(), + ) + } + + @Test + fun testEqualityForAuthIntent() { + assertEquals( + SignRequest.sampleAuthIntent(), + SignRequest.sampleAuthIntent(), + ) + assertEquals( + SignRequest.sampleAuthIntent.other(), + SignRequest.sampleAuthIntent.other(), + ) + } + + @Test + fun testInequalityForAuthIntent() { + assertNotEquals( + SignRequest.sampleAuthIntent(), + SignRequest.sampleAuthIntent.other(), + ) + } + +} \ No newline at end of file diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignResponseTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignResponseTest.kt new file mode 100644 index 000000000..66e0219b9 --- /dev/null +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignResponseTest.kt @@ -0,0 +1,446 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.Account +import com.radixdlt.sargon.AddressOfAccountOrPersona +import com.radixdlt.sargon.AuthIntent +import com.radixdlt.sargon.CommonException +import com.radixdlt.sargon.EntitySecurityState +import com.radixdlt.sargon.FactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.FactorOutcomeOfSubintentHash +import com.radixdlt.sargon.FactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.FactorSourceIdFromHash +import com.radixdlt.sargon.HdSignatureInputOfAuthIntentHash +import com.radixdlt.sargon.HdSignatureInputOfSubintentHash +import com.radixdlt.sargon.HdSignatureInputOfTransactionIntentHash +import com.radixdlt.sargon.HdSignatureOfAuthIntentHash +import com.radixdlt.sargon.HdSignatureOfSubintentHash +import com.radixdlt.sargon.HdSignatureOfTransactionIntentHash +import com.radixdlt.sargon.HierarchicalDeterministicFactorInstance +import com.radixdlt.sargon.HierarchicalDeterministicPublicKey +import com.radixdlt.sargon.MnemonicWithPassphrase +import com.radixdlt.sargon.NeglectFactorReason +import com.radixdlt.sargon.NeglectedFactor +import com.radixdlt.sargon.OwnedFactorInstance +import com.radixdlt.sargon.PerFactorOutcomeOfAuthIntentHash +import com.radixdlt.sargon.PerFactorOutcomeOfSubintentHash +import com.radixdlt.sargon.PerFactorOutcomeOfTransactionIntentHash +import com.radixdlt.sargon.SignResponseOfAuthIntentHash +import com.radixdlt.sargon.SignResponseOfSubintentHash +import com.radixdlt.sargon.SignResponseOfTransactionIntentHash +import com.radixdlt.sargon.Subintent +import com.radixdlt.sargon.TransactionIntent +import com.radixdlt.sargon.extensions.hash +import com.radixdlt.sargon.extensions.publicKey +import com.radixdlt.sargon.extensions.sign +import com.radixdlt.sargon.extensions.signed +import com.radixdlt.sargon.extensions.skipped +import com.radixdlt.sargon.samples.sample +import com.radixdlt.sargon.samples.sampleMainnet +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class SignResponseTest { + + private val mnemonic = MnemonicWithPassphrase.sample() + private val account = Account.sampleMainnet() + private val ownedFactorInstance = OwnedFactorInstance( + owner = AddressOfAccountOrPersona.Account(account.address), + factorInstance = (account.securityState as EntitySecurityState.Unsecured) + .value + .transactionSigning + ) + + @Test + fun testSignResponseForTransactionIntentHashSigned() { + val factorSourceId = ownedFactorInstance.factorInstance.factorSourceId + val payload = TransactionIntent.sample() + val signature = mnemonic.sign( + hash = payload.hash().hash, + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = factorSourceId, + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Transaction(payload.hash()), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample.other() + ) + ) + ) + ) + ) + + assertEquals( + SignResponseOfTransactionIntentHash( + perFactorOutcome = listOf( + PerFactorOutcomeOfTransactionIntentHash( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcomeOfTransactionIntentHash.signed( + producedSignatures = listOf( + HdSignatureOfTransactionIntentHash( + input = HdSignatureInputOfTransactionIntentHash( + payloadId = payload.hash(), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcomeOfTransactionIntentHash( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcomeOfTransactionIntentHash.skipped( + FactorSourceIdFromHash.sample.other() + ) + ) + ) + ), + signResponse.intoSargon(), + ) + } + + @Test + fun testSignResponseForSubintentHashSigned() { + val factorSourceId = ownedFactorInstance.factorInstance.factorSourceId + val payload = Subintent.sample() + val signature = mnemonic.sign( + hash = payload.hash().hash, + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = factorSourceId, + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Subintent(payload.hash()), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample.other() + ) + ) + ) + ) + ) + + assertEquals( + SignResponseOfSubintentHash( + perFactorOutcome = listOf( + PerFactorOutcomeOfSubintentHash( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcomeOfSubintentHash.signed( + producedSignatures = listOf( + HdSignatureOfSubintentHash( + input = HdSignatureInputOfSubintentHash( + payloadId = payload.hash(), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcomeOfSubintentHash( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcomeOfSubintentHash.skipped( + FactorSourceIdFromHash.sample.other() + ) + ) + ) + ), + signResponse.intoSargon(), + ) + } + + @Test + fun testSignResponseForAuthIntentHashSigned() { + val factorSourceId = ownedFactorInstance.factorInstance.factorSourceId + val payload = AuthIntent.sample() + val signature = mnemonic.sign( + hash = payload.hash().payload.hash(), + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = factorSourceId, + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Auth(payload.hash()), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample.other() + ) + ) + ) + ) + ) + + assertEquals( + SignResponseOfAuthIntentHash( + perFactorOutcome = listOf( + PerFactorOutcomeOfAuthIntentHash( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcomeOfAuthIntentHash.signed( + producedSignatures = listOf( + HdSignatureOfAuthIntentHash( + input = HdSignatureInputOfAuthIntentHash( + payloadId = payload.hash(), + ownedFactorInstance = ownedFactorInstance + ), + signature = signature + ) + ) + ) + ), + PerFactorOutcomeOfAuthIntentHash( + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcomeOfAuthIntentHash.skipped( + FactorSourceIdFromHash.sample.other() + ) + ) + ) + ), + signResponse.intoSargon(), + ) + } + + @Test + fun testSignResponseForTransactionIntentHashInvalidOutcomesWhenSigned() { + val payload = TransactionIntent.sample() + val signature = mnemonic.sign( + hash = payload.hash().hash, + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Transaction(payload.hash()), + ownedFactorInstance = OwnedFactorInstance( + owner = ownedFactorInstance.owner, + factorInstance = HierarchicalDeterministicFactorInstance( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + publicKey = ownedFactorInstance.factorInstance.publicKey + ) + ) + ), + signature = signature + ) + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } + + @Test + fun testSignResponseForTransactionIntentHashInvalidOutcomesWhenNeglected() { + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample() + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } + + @Test + fun testSignResponseForSubintentHashInvalidOutcomesWhenSigned() { + val payload = Subintent.sample() + val signature = mnemonic.sign( + hash = payload.hash().hash, + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Subintent(payload.hash()), + ownedFactorInstance = OwnedFactorInstance( + owner = ownedFactorInstance.owner, + factorInstance = HierarchicalDeterministicFactorInstance( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + publicKey = ownedFactorInstance.factorInstance.publicKey + ) + ) + ), + signature = signature + ) + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } + + @Test + fun testSignResponseForSubintentHashInvalidOutcomesWhenNeglected() { + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample() + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } + + @Test + fun testSignResponseForAuthIntentHashInvalidOutcomesWhenSigned() { + val payload = AuthIntent.sample() + val signature = mnemonic.sign( + hash = payload.hash().payload.hash(), + path = ownedFactorInstance.factorInstance.publicKey.derivationPath + ) + + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + factorSourceId = FactorSourceIdFromHash.sample(), + outcome = FactorOutcome.Signed( + producedSignatures = listOf( + HdSignature( + input = HdSignatureInput( + payloadId = Signable.ID.Auth(payload.hash()), + ownedFactorInstance = OwnedFactorInstance( + owner = ownedFactorInstance.owner, + factorInstance = HierarchicalDeterministicFactorInstance( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + publicKey = ownedFactorInstance.factorInstance.publicKey + ) + ) + ), + signature = signature + ) + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } + + @Test + fun testSignResponseForAuthIntentHashInvalidOutcomesWhenNeglected() { + val signResponse = SignResponse( + perFactorOutcome = listOf( + PerFactorOutcome( + // Using a different factor source id + factorSourceId = FactorSourceIdFromHash.sample.other(), + outcome = FactorOutcome.Neglected( + factor = NeglectedFactor( + reason = NeglectFactorReason.USER_EXPLICITLY_SKIPPED, + factor = FactorSourceIdFromHash.sample() + ) + ) + ), + ) + ) + + val error = runCatching { + signResponse.intoSargon() + }.exceptionOrNull() + + assertTrue(error is CommonException.FactorOutcomeSignedFactorSourceIdMismatch) + } +} \ No newline at end of file diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignableTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignableTest.kt new file mode 100644 index 000000000..b38440f97 --- /dev/null +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/signing/SignableTest.kt @@ -0,0 +1,159 @@ +package com.radixdlt.sargon.os.signing + +import com.radixdlt.sargon.AuthIntent +import com.radixdlt.sargon.AuthIntentHash +import com.radixdlt.sargon.CompiledSubintent +import com.radixdlt.sargon.CompiledTransactionIntent +import com.radixdlt.sargon.Subintent +import com.radixdlt.sargon.SubintentHash +import com.radixdlt.sargon.TransactionIntent +import com.radixdlt.sargon.TransactionIntentHash +import com.radixdlt.sargon.extensions.compile +import com.radixdlt.sargon.extensions.decompile +import com.radixdlt.sargon.extensions.hash +import com.radixdlt.sargon.samples.sample +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class SignableTest { + + @Nested + inner class IDTest { + @Test + fun transactionIntent() { + val intentHash = TransactionIntentHash.sample() + val id = Signable.ID.Transaction(intentHash) + + assertEquals( + intentHash.hash, + id.hash() + ) + } + + @Test + fun subintent() { + val intentHash = SubintentHash.sample() + val id = Signable.ID.Subintent(intentHash) + + assertEquals( + intentHash.hash, + id.hash() + ) + } + + @Test + fun authIntent() { + val intentHash = AuthIntentHash.sample() + val id = Signable.ID.Auth(intentHash) + + assertEquals( + intentHash.payload.hash(), + id.hash() + ) + } + } + + + @Nested + inner class PayloadTest { + @Test + fun transactionIntent() { + val compiledIntent = CompiledTransactionIntent.sample() + val payload = Signable.Payload.Transaction(compiledIntent) + + assertEquals( + Signable.Transaction(compiledIntent.decompile()), + payload.getSignable() + ) + } + + @Test + fun subintent() { + val compiledIntent = CompiledSubintent.sample() + val payload = Signable.Payload.Subintent(compiledIntent) + + assertEquals( + Signable.Subintent(compiledIntent.decompile()), + payload.getSignable() + ) + } + + @Test + fun authIntent() { + val intentHash = AuthIntent.sample() + val payload = Signable.Payload.Auth(intentHash) + + assertEquals( + Signable.Auth(intentHash), + payload.getSignable() + ) + } + } + + @Nested + inner class SignableTest { + @Test + fun transactionIntent() { + val intent = TransactionIntent.sample() + val signable = Signable.Transaction(intent) + + assertEquals( + Signable.Payload.Transaction(intent.compile()), + signable.getPayload(), + ) + + assertEquals( + Signable.ID.Transaction(intent.hash()), + signable.getId() + ) + + assertEquals( + intent.hash().hash, + signable.hash() + ) + } + + @Test + fun subintent() { + val intent = Subintent.sample() + val signable = Signable.Subintent(intent) + + assertEquals( + Signable.Payload.Subintent(intent.compile()), + signable.getPayload(), + ) + + assertEquals( + Signable.ID.Subintent(intent.hash()), + signable.getId() + ) + + assertEquals( + intent.hash().hash, + signable.hash() + ) + } + + @Test + fun authIntent() { + val intent = AuthIntent.sample() + val signable = Signable.Auth(intent) + + assertEquals( + Signable.Payload.Auth(intent), + signable.getPayload(), + ) + + assertEquals( + Signable.ID.Auth(intent.hash()), + signable.getId() + ) + + assertEquals( + intent.hash().payload.hash(), + signable.hash() + ) + } + } +} \ No newline at end of file