diff --git a/Sources/MobileCoinClient+Deprecated.swift b/Sources/MobileCoinClient+Deprecated.swift index 4fde3919..219f9868 100644 --- a/Sources/MobileCoinClient+Deprecated.swift +++ b/Sources/MobileCoinClient+Deprecated.swift @@ -211,7 +211,7 @@ extension MobileCoinClient { @available(*, deprecated, message: """ - Use the new `prepareTransaction(...)` that accepts a 32-byte rngSeed. + Use the `prepareTransaction(...)` that accepts a MobileCoinRng instead of RngSeed ``` public func prepareTransaction( @@ -219,42 +219,35 @@ extension MobileCoinClient { memoType: MemoType = .recoverable, amount: Amount, fee: UInt64, - rngSeed: RngSeed, + rng: MobileCoinRng, completion: @escaping ( Result ) -> Void ) { ``` - - this function creates a 32-byte seed by combining the data from 4 calls to rng.next() """) public func prepareTransaction( to recipient: PublicAddress, memoType: MemoType = .recoverable, amount: Amount, fee: UInt64, - rng: MobileCoinRng, + rngSeed: RngSeed, completion: @escaping ( Result ) -> Void ) { - guard let rngSeed = rng.generateRngSeed() else { - completion(.failure( - TransactionPreparationError.invalidInput("Could not create 32 byte RNG seed"))) - return - } prepareTransaction( to: recipient, memoType: memoType, amount: amount, fee: fee, - rngSeed: rngSeed, + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed), completion: completion) } @available(*, deprecated, message: """ - Use the new `prepareTransaction(...)` that accepts a 32-byte rngSeed. + Use the `prepareTransaction(...)` that accepts a MobileCoinRng instead of an RngSeed ``` public func prepareTransaction( @@ -262,7 +255,7 @@ extension MobileCoinClient { memoType: MemoType = .recoverable, amount: Amount, feeLevel: FeeLevel = .minimum, - rngSeed: RngSeed, + rng: MobileCoinRng, completion: @escaping ( Result ) -> Void @@ -276,22 +269,17 @@ extension MobileCoinClient { memoType: MemoType = .recoverable, amount: Amount, feeLevel: FeeLevel = .minimum, - rng: MobileCoinRng, + rngSeed: RngSeed, completion: @escaping ( Result ) -> Void ) { - guard let rngSeed = rng.generateRngSeed() else { - completion(.failure( - TransactionPreparationError.invalidInput("Could not create 32-byte RNG seed"))) - return - } prepareTransaction( to: recipient, memoType: memoType, amount: amount, feeLevel: feeLevel, - rngSeed: rngSeed, + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed), completion: completion) } diff --git a/Sources/MobileCoinClient.swift b/Sources/MobileCoinClient.swift index 6f397abf..307b1b37 100644 --- a/Sources/MobileCoinClient.swift +++ b/Sources/MobileCoinClient.swift @@ -203,7 +203,7 @@ public final class MobileCoinClient { memoType: memoType, amount: amount, fee: fee, - rngSeed: RngSeed(), + rng: MobileCoinChaCha20Rng(), completion: completion) } @@ -212,11 +212,16 @@ public final class MobileCoinClient { memoType: MemoType = .recoverable, amount: Amount, fee: UInt64, - rngSeed: RngSeed, + rng: MobileCoinRng, completion: @escaping ( Result ) -> Void ) { + guard let rngSeed = rng.generateRngSeed() else { + completion(.failure( + TransactionPreparationError.invalidInput("Could not create 32 byte RNG seed"))) + return + } Account.TransactionOperations( account: accountLock, fogMerkleProofService: serviceProvider.fogMerkleProofService, @@ -252,7 +257,7 @@ public final class MobileCoinClient { memoType: memoType, amount: amount, feeLevel: feeLevel, - rngSeed: RngSeed(), + rng: MobileCoinChaCha20Rng(), completion: completion) } @@ -261,11 +266,16 @@ public final class MobileCoinClient { memoType: MemoType = .recoverable, amount: Amount, feeLevel: FeeLevel = .minimum, - rngSeed: RngSeed, + rng: MobileCoinRng, completion: @escaping ( Result ) -> Void ) { + guard let rngSeed = rng.generateRngSeed() else { + completion(.failure( + TransactionPreparationError.invalidInput("Could not create 32-byte RNG seed"))) + return + } Account.TransactionOperations( account: accountLock, fogMerkleProofService: serviceProvider.fogMerkleProofService, diff --git a/Tests/Integration/Transaction/TransactionIdempotenceTests.swift b/Tests/Integration/Transaction/TransactionIdempotenceTests.swift index f6e62df1..03e0926f 100644 --- a/Tests/Integration/Transaction/TransactionIdempotenceTests.swift +++ b/Tests/Integration/Transaction/TransactionIdempotenceTests.swift @@ -11,14 +11,62 @@ enum IdempotenceTestError: Error { class TransactionIdempotenceTests: XCTestCase { - func testIdempotenceSubmissionFailure() throws { + func testAndroidIdempotenceOutputPublicKeyMatch() throws { let description = "Testing idempotence submission failure" try testSupportedProtocols(description: description) { - try idempotenceSubmissionFailure(transportProtocol: $0, expectation: $1) + try androidIdempotenceOutputPublicKeyMatch(transportProtocol: $0, expectation: $1) } } - func idempotenceSubmissionFailure( + func androidIdempotenceOutputPublicKeyMatch( + transportProtocol: TransportProtocol, + expectation expect: XCTestExpectation + ) throws { + let rngSeedHex = + "676f746f2068747470733a2f2f6275792e6d6f62696c65636f696e2e636f6d00" + let recipientAddressHex = """ + 0a220a2048d9e6aa836d7aa57f7a9da9709603d8f5071faff4908c86ed829e68c0129f63\ + 12220a207ee52b7741a8b9f3701ab716fa361483b03ddcaeecb64ba64355a3411c87d43d + """ + let androidTxOutputPublicKeyHex = + "d47d4f6525cee846a47106e92de3e63e5b3cb677b8ae4df7efd667e2bad15719" + + let client = try IntegrationTestFixtures.createMobileCoinClient( + accountIndex: 1, + using: transportProtocol) + let rngSeed = try XCTUnwrap(RngSeed(try XCTUnwrap(Data(hexEncoded: rngSeedHex)))) + let recipientAddressData = try XCTUnwrap(Data(hexEncoded: recipientAddressHex)) + let recipient = try XCTUnwrap(PublicAddress(serializedData: recipientAddressData)) + let androidTxOutputPublicKey = try XCTUnwrap(Data(hexEncoded: androidTxOutputPublicKeyHex)) + let amt = Amount(mob: 100) + + client.updateBalances { + guard $0.successOrFulfill(expectation: expect) != nil else { return } + client.prepareTransaction( + to: recipient, + memoType: .unused, + amount: amt, + fee: IntegrationTestFixtures.fee, + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed) + ) { result in + guard let payload = result.successOrFulfill(expectation: expect) else { + XCTFail("Invalid payload from prepareTransaction") + return + } + XCTAssertEqual(payload.payloadTxOutContext.txOutPublicKey, androidTxOutputPublicKey) + expect.fulfill() + } + } + } + + func testIdempotenceDoubleSubmissionFailure() throws { + let description = "Testing idempotence submission failure" + try testSupportedProtocols(description: description) { + try idempotenceDoubleSubmissionFailure(transportProtocol: $0, expectation: $1) + } + } + + func idempotenceDoubleSubmissionFailure( transportProtocol: TransportProtocol, expectation expect: XCTestExpectation ) throws { @@ -77,7 +125,7 @@ class TransactionIdempotenceTests: XCTestCase { memoType: .unused, amount: amt, fee: IntegrationTestFixtures.fee, - rngSeed: rngSeed + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed) ) { result in guard let payload = result.successOrFulfill(expectation: expect) else { return @@ -145,7 +193,7 @@ class TransactionIdempotenceTests: XCTestCase { memoType: .unused, amount: amt, fee: IntegrationTestFixtures.fee, - rngSeed: rngSeed + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed) ) { result in guard let transaction1 = result.successOrFulfill(expectation: expect) else { return @@ -155,7 +203,7 @@ class TransactionIdempotenceTests: XCTestCase { memoType: .unused, amount: amt, fee: IntegrationTestFixtures.fee, - rngSeed: rngSeed + rng: MobileCoinChaCha20Rng(rngSeed: rngSeed) ) { guard let transaction2 = $0.successOrFulfill(expectation: expect) else { return