From 5f8e8aeb3625a006e4828d6c8d9b97b864f34dbc Mon Sep 17 00:00:00 2001 From: kirahsapong <102400653+kirahsapong@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:38:24 -0700 Subject: [PATCH] fix offering and add protocol field (#58) * fix offering and add protocol field * add tests back * remove tests.xml and add to gitignore --- .gitignore | 3 +- Sources/tbDEX/Protocol/Models/Resource.swift | 49 ++------------- .../Protocol/Models/Resources/Offering.swift | 25 -------- Tests/tbDEXTestVectors/tbdex-spec | 2 +- .../Models/Resources/OfferingTests.swift | 62 +++++++------------ 5 files changed, 32 insertions(+), 109 deletions(-) diff --git a/.gitignore b/.gitignore index e96b292..c4d9948 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ Package.resolved *.pyc .docc-build .vscode -Utilities/InstalledSwiftPMConfiguration/config.json \ No newline at end of file +Utilities/InstalledSwiftPMConfiguration/config.json +tests.xml \ No newline at end of file diff --git a/Sources/tbDEX/Protocol/Models/Resource.swift b/Sources/tbDEX/Protocol/Models/Resource.swift index 86a388b..6651fab 100644 --- a/Sources/tbDEX/Protocol/Models/Resource.swift +++ b/Sources/tbDEX/Protocol/Models/Resource.swift @@ -15,40 +15,12 @@ public struct Resource: Codable, Equatable { public let data: D /// Signature that verifies the authenticity and integrity of the Resource - public private(set) var signature: String? - - /// Default Initializer - init( - from: String, - data: D - ) { - let now = Date() - self.metadata = ResourceMetadata( - id: TypeID(prefix: data.kind().rawValue)!, - kind: data.kind(), - from: from, - createdAt: now, - updatedAt: now - ) - self.data = data - self.signature = nil - } + public let signature: String? private func digest() throws -> Data { try CryptoUtils.digest(data: data, metadata: metadata) } - public mutating func sign(did: BearerDID, keyAlias: String? = nil) throws { - self.signature = try JWS.sign( - did: did, - payload: try digest(), - options: .init( - detached: true, - verificationMethodID: keyAlias - ) - ) - } - public func verify() async throws -> Bool { return try await JWS.verify( compactJWS: signature, @@ -91,19 +63,8 @@ public struct ResourceMetadata: Codable, Equatable { /// The time at which the resource was last updated public let updatedAt: Date? - - /// Default Initializer - init( - id: TypeID, - kind: ResourceKind, - from: String, - createdAt: Date, - updatedAt: Date? = nil - ) { - self.id = id - self.kind = kind - self.from = from - self.createdAt = createdAt - self.updatedAt = updatedAt - } + + /// Version of the protocol in use (x.x format). Must be consistent with all other messages in a given exchange + public let `protocol`: String + } diff --git a/Sources/tbDEX/Protocol/Models/Resources/Offering.swift b/Sources/tbDEX/Protocol/Models/Resources/Offering.swift index 4538e25..7c054c4 100644 --- a/Sources/tbDEX/Protocol/Models/Resources/Offering.swift +++ b/Sources/tbDEX/Protocol/Models/Resources/Offering.swift @@ -52,17 +52,6 @@ public struct CurrencyDetails: Codable, Equatable { /// Maximum amount of currency that the offer is valid for public let maxAmount: String? - /// Default initializer - init( - currencyCode: String, - minAmount: String? = nil, - maxAmount: String? = nil - ) { - self.currencyCode = currencyCode - self.minAmount = minAmount - self.maxAmount = maxAmount - } - } /// Details about payment methods within an Offering @@ -77,18 +66,4 @@ public struct PaymentMethod: Codable, Equatable { /// A JSON Schema containing the fields that need to be collected in order to use this payment method public let requiredPaymentDetails: AnyCodable? - /// The fee expressed in the currency's sub units to make use of this payment method - public let fee: String? - - /// Default initializer - init( - kind: String, - requiredPaymentDetails: AnyCodable? = nil, - fee: String? = nil - ) { - self.kind = kind - self.requiredPaymentDetails = requiredPaymentDetails - self.fee = fee - } - } diff --git a/Tests/tbDEXTestVectors/tbdex-spec b/Tests/tbDEXTestVectors/tbdex-spec index 5f9ddb7..b4523ba 160000 --- a/Tests/tbDEXTestVectors/tbdex-spec +++ b/Tests/tbDEXTestVectors/tbdex-spec @@ -1 +1 @@ -Subproject commit 5f9ddb7007c98164391a12f5b8f4f2bbfdc00fac +Subproject commit b4523ba1c0cf04d4c07055a574f29bfe7e57a9ac diff --git a/Tests/tbDEXTests/Protocol/Models/Resources/OfferingTests.swift b/Tests/tbDEXTests/Protocol/Models/Resources/OfferingTests.swift index 72962f4..e78aca4 100644 --- a/Tests/tbDEXTests/Protocol/Models/Resources/OfferingTests.swift +++ b/Tests/tbDEXTests/Protocol/Models/Resources/OfferingTests.swift @@ -4,49 +4,35 @@ import XCTest @testable import tbDEX final class OfferingTests: XCTestCase { + + func test_parseOffering() throws { + if let offering = try parsedOffering() { + XCTAssertEqual(offering.metadata.kind, ResourceKind.offering) + } else { + XCTFail("Offering is not a parsed offering") + } - func test_init() { - let offering = createOffering(from: "pfi") - - XCTAssertEqual(offering.metadata.id.prefix, "offering") - XCTAssertEqual(offering.metadata.from, "pfi") - XCTAssertEqual(offering.data.description, "test offering") - XCTAssertEqual(offering.data.payoutUnitsPerPayinUnit, "1") - XCTAssertEqual(offering.data.payinCurrency.currencyCode, "AUD") - XCTAssertEqual(offering.data.payoutCurrency.currencyCode, "BTC") } - func test_signAndVerifySuccess() async throws { - let did = try DIDJWK.create(keyManager: InMemoryKeyManager()) - var offering = createOffering(from: did.uri) - - XCTAssertNil(offering.signature) - try offering.sign(did: did) - XCTAssertNotNil(offering.signature) - let isValid = try await offering.verify() - XCTAssertTrue(isValid) - } - - func test_verifyWithoutSigningFailure() async throws { - let did = try DIDJWK.create(keyManager: InMemoryKeyManager()) - let offering = createOffering(from: did.uri) - - await XCTAssertThrowsErrorAsync(try await offering.verify()) + func test_verifyOfferingIsValid() async throws { + if let offering = try parsedOffering() { + XCTAssertNotNil(offering.signature) + XCTAssertNotNil(offering.data) + XCTAssertNotNil(offering.metadata) + let isValid = try await offering.verify() + XCTAssertTrue(isValid) + } else { + XCTFail("Offering is not a parsed offering") + } } - private func createOffering(from: String) -> Offering { - Offering( - from: from, - data: .init( - description: "test offering", - payoutUnitsPerPayinUnit: "1", - payinCurrency: .init(currencyCode: "AUD"), - payoutCurrency: .init(currencyCode: "BTC"), - payinMethods: [], - payoutMethods: [], - requiredClaims: [:] - ) - ) + private func parsedOffering() throws -> Offering? { + let offeringJson = "{\"metadata\":{\"from\":\"did:dht:77em1f968c1gzwrrb15cgkzjxg8rft67ebxj6gjkocnz5p8sdniy\",\"protocol\":\"1.0\",\"kind\":\"offering\",\"id\":\"offering_01hrqn6ph3f00asxqvx46capbw\",\"createdAt\":\"2024-03-11T21:02:55.523Z\"},\"data\":{\"description\":\"Selling BTC for USD\",\"payinCurrency\":{\"currencyCode\":\"USD\",\"minAmount\":\"0.0\",\"maxAmount\":\"999999.99\"},\"payoutCurrency\":{\"currencyCode\":\"BTC\",\"maxAmount\":\"999526.11\"},\"payoutUnitsPerPayinUnit\":\"0.00003826\",\"payinMethods\":[{\"kind\":\"DEBIT_CARD\",\"requiredPaymentDetails\":{\"$schema\":\"http://json-schema.org/draft-07/schema\",\"type\":\"object\",\"properties\":{\"cardNumber\":{\"type\":\"string\",\"description\":\"The 16-digit debit card number\",\"minLength\":16,\"maxLength\":16},\"expiryDate\":{\"type\":\"string\",\"description\":\"The expiry date of the card in MM/YY format\",\"pattern\":\"^(0[1-9]|1[0-2])\\\\/([0-9]{2})$\"},\"cardHolderName\":{\"type\":\"string\",\"description\":\"Name of the cardholder as it appears on the card\"},\"cvv\":{\"type\":\"string\",\"description\":\"The 3-digit CVV code\",\"minLength\":3,\"maxLength\":3}},\"required\":[\"cardNumber\",\"expiryDate\",\"cardHolderName\",\"cvv\"],\"additionalProperties\":false}}],\"payoutMethods\":[{\"kind\":\"BTC_ADDRESS\",\"requiredPaymentDetails\":{\"$schema\":\"http://json-schema.org/draft-07/schema\",\"type\":\"object\",\"properties\":{\"btcAddress\":{\"type\":\"string\",\"description\":\"your Bitcoin wallet address\"}},\"required\":[\"btcAddress\"],\"additionalProperties\":false}}],\"requiredClaims\":{\"id\":\"7ce4004c-3c38-4853-968b-e411bafcd945\",\"input_descriptors\":[{\"id\":\"bbdb9b7c-5754-4f46-b63b-590bada959e0\",\"constraints\":{\"fields\":[{\"path\":[\"$.type\"],\"filter\":{\"type\":\"string\",\"const\":\"YoloCredential\"}}]}}]}},\"signature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpkaHQ6NzdlbTFmOTY4YzFnendycmIxNWNna3pqeGc4cmZ0NjdlYnhqNmdqa29jbno1cDhzZG5peSMwIn0..puQwdTvi4KTfKedA6CXdHHldztoQ8udUrQrGmw1wvWfYW3ilMB8myoD3ATw7NGlt1NuizJ80i4ufZArgGrTiAA\"}" + let parsedResource = try AnyResource.parse(offeringJson) + guard case let .offering(parsedOffering) = parsedResource else { + return nil + } + return parsedOffering } }