Skip to content

Commit

Permalink
fix: change how to handle services according to spec change
Browse files Browse the repository at this point in the history
  • Loading branch information
beatt83 committed Feb 12, 2024
1 parent ab36cb7 commit 7a061c0
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 148 deletions.
7 changes: 2 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# This workflow will build a Swift project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift

name: Swift
name: PR

on:
pull_request:
Expand All @@ -12,7 +9,7 @@ concurrency:
cancel-in-progress: true

jobs:
build:
test:

runs-on: macos-13

Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/swift-libp2p/swift-multibase.git", .upToNextMajor(from: "0.0.1")),
.package(url: "https://github.com/swift-libp2p/swift-bases.git", .upToNextMajor(from: "0.0.3")),
.package(url: "https://github.com/beatt83/didcore-swift.git", .upToNextMinor(from: "1.0.1"))
.package(url: "https://github.com/beatt83/didcore-swift.git", .upToNextMinor(from: "1.1.0"))
],
targets: [
.target(
Expand Down
6 changes: 3 additions & 3 deletions Sources/PeerDID/Models/PeerDID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ public struct PeerDID {
.map { String($0.dropFirst()) }
}

public var algo2Service: String? {
guard algo == ._2 else { return nil }
public var algo2Service: [String] {
guard algo == ._2 else { return [] }
return allAttributes
.filter { $0.hasPrefix(Algorithm.Algo2Prefix.service.rawValue) }
.first.map { String($0.dropFirst()) }
.map { String($0.dropFirst()) }
}
}
8 changes: 5 additions & 3 deletions Sources/PeerDID/PeerDID/PeerDIDHelper+ResolveAlgo2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension PeerDIDHelper {
public func resolvePeerDIDAlgo2(peerDID: PeerDID, format: VerificationMaterialFormat) throws -> DIDDocument {
let authenticationKeys = peerDID.algo2AuthenticationKeys
let agreementKeys = peerDID.algo2KeyAgreementKeys
let service = peerDID.algo2Service
let services = peerDID.algo2Service

var keyIdCount = 1
let authenticationVerificationMethods = try authenticationKeys
Expand All @@ -40,12 +40,14 @@ extension PeerDIDHelper {
return method
}

let documentService = try service.map { try decodedPeerDIDService(did: peerDID.string, serviceString: $0) }
let documentServices = try services.enumerated().map {
try decodedPeerDIDService(did: peerDID.string, serviceString: $0.element, index: $0.offset)
}

return DIDDocument(
id: peerDID.string,
verificationMethods: authenticationVerificationMethods + agreementVerificationMethods,
services: documentService ?? []
services: documentServices
)
}
}
138 changes: 65 additions & 73 deletions Sources/PeerDID/PeerDID/PeerDIDHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public struct PeerDIDHelper {
.map { try PeerDIDHelper().createMultibaseEncnumbasis(material: $0) }
.map { "V\($0)" }

let encodedServiceStr = try PeerDIDHelper().encodePeerDIDServices(services: services)
let methodId = (["2"] + encodedAgreementsStrings + encodedAuthenticationsStrings + [encodedServiceStr])
let encodedServiceStrs = try services.map { try PeerDIDHelper().encodePeerDIDServices(service: $0) }
let methodId = (["2"] + encodedAgreementsStrings + encodedAuthenticationsStrings + encodedServiceStrs)
.compactMap { $0 }
.joined(separator: ".")

Expand Down Expand Up @@ -72,7 +72,7 @@ public extension PeerDIDHelper {
ecnumbasis: String,
format: VerificationMaterialFormat
) throws -> PeerDIDVerificationMaterial {
let (base, decodedMultibase) = try BaseEncoding.decode(ecnumbasis)
let (_, decodedMultibase) = try BaseEncoding.decode(ecnumbasis)
let (codec, decodedMulticodec) = try Multicodec().fromMulticodec(value: decodedMultibase)

try decodedMulticodec.validateKeyLength()
Expand Down Expand Up @@ -130,6 +130,43 @@ extension PeerDIDHelper {

struct PeerDIDService: Codable {

struct ServiceEndpoint: Codable {
let uri: String
let r: [String]? // Routing keys
let a: [String]? // Accept
}

let t: String // Type
let s: ServiceEndpoint // Service Endpoint

init(from: DIDDocument.Service) throws {
self.t = from.type
guard
let dic = from.serviceEndpoint.value as? [String: Any],
let uri = dic["uri"] as? String
else {
throw PeerDIDError.invalidPeerDIDService
}
self.s = .init(
uri: uri,
r: dic["routing_keys"] as? [String],
a: dic["accept"] as? [String]
)
}

func toDIDDocumentService(did: String, index: Int) throws -> DIDDocument.Service {
return .init(
id: "\(did)#\(t.lowercased())-\(index+1)",
type: t,
serviceEndpoint: AnyCodable(
dictionaryLiteral: ("uri", s.uri), ("accept", s.a ?? []), ("routing_keys", s.r ?? [])
)
)
}
}

struct PeerDIDServiceLegacy: Codable {

let t: String // Type
let s: String // Service Endpoint
let r: [String]? // Routing keys
Expand All @@ -144,79 +181,37 @@ extension PeerDIDHelper {

init(from: DIDDocument.Service) throws {
self.t = from.type
self.s = try from.serviceEndpoint.getJsonString() ?? ""
self.r = from.routingKeys
self.a = from.accept
guard
let uri = from.serviceEndpoint.value as? String
else {
throw PeerDIDError.invalidPeerDIDService
}
self.s = uri
self.r = []
self.a = []
}

func toDIDDocumentService(did: String, index: Int) throws -> DIDDocument.Service {
let serviceEndpoint: DIDDocument.Service.ServiceEndpoint
if let s = try? parseServiceEndpoint(serviceEndpoint: s) {
serviceEndpoint = s
} else {
serviceEndpoint = try parseServiceEndpoint(serviceEndpoint: "\"\(s)\"")
}
var serviceEndpoint: [String: Any] = ["uri": s]
a.map { serviceEndpoint["accept"] = $0 }
r.map { serviceEndpoint["routing_keys"] = $0 }

return .init(
id: "\(did)#\(t.lowercased())-\(index+1)",
type: t,
serviceEndpoint: serviceEndpoint,
routingKeys: r,
accept: a
serviceEndpoint: AnyCodable(dictionaryLiteral: ("uri", s), ("accept", a ?? []), ("routing_keys", r ?? []))
)
}

private func parseServiceEndpoint(serviceEndpoint: String) throws -> DIDDocument.Service.ServiceEndpoint {
guard let serviceData = serviceEndpoint.data(using: .utf8) else {
throw PeerDIDError.somethingWentWrong
}
return try JSONDecoder().decode(DIDDocument.Service.ServiceEndpoint.self, from: serviceData)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: PeerDIDHelper.PeerDIDService.CodingKeys.self)
try container.encode(self.s, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.s)
try container.encode(self.r, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.r)
try container.encodeIfPresent(self.a, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.a)
try container.encodeIfPresent(self.t, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.t)
}

enum CodingKeys: CodingKey {
case t
case s
case r
case a
}

init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<PeerDIDHelper.PeerDIDService.CodingKeys> = try decoder.container(keyedBy: PeerDIDHelper.PeerDIDService.CodingKeys.self)
self.t = try container.decode(String.self, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.t)
self.s = try container.decode(String.self, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.s)
self.r = try container.decodeIfPresent([String].self, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.r)
self.a = try container.decodeIfPresent([String].self, forKey: PeerDIDHelper.PeerDIDService.CodingKeys.a)
}
}

public func encodePeerDIDServices(services: [DIDDocument.Service]) throws -> String? {
guard !services.isEmpty else { return nil }
public func encodePeerDIDServices(service: DIDDocument.Service) throws -> String {
let encoder = JSONEncoder.peerDIDEncoder()

let parsingStr: String

if services.count > 1 {
let peerDidServices = try services.map { try PeerDIDService(from: $0) }
guard let jsonStr = String(data: try encoder.encode(peerDidServices), encoding: .utf8) else {
throw PeerDIDError.somethingWentWrong
}
parsingStr = jsonStr
} else if let service = services.first {
let peerDIDService = try PeerDIDService(from: service)
guard let jsonStr = String(data: try encoder.encode(peerDIDService), encoding: .utf8) else {
throw PeerDIDError.somethingWentWrong
}
parsingStr = jsonStr
} else {
throw PeerDIDError.somethingWentWrong // This should never happen since we handle all the cases
let peerDIDService = try PeerDIDService(from: service)
guard let jsonStr = String(data: try encoder.encode(peerDIDService), encoding: .utf8) else {
throw PeerDIDError.somethingWentWrong
}
let parsingStr = jsonStr

let parsedService = parsingStr
.replacingOccurrences(of: "[\n\t\\s]*", with: "", options: .regularExpression)
Expand All @@ -229,7 +224,7 @@ extension PeerDIDHelper {
return "S\(encodedService)"
}

public func decodedPeerDIDService(did: String, serviceString: String) throws -> [DIDDocument.Service] {
public func decodedPeerDIDService(did: String, serviceString: String, index: Int) throws -> DIDDocument.Service {
guard
let serviceBase64Data = Data(base64URLEncoded: serviceString),
let serviceStr = String(data: serviceBase64Data, encoding: .utf8)
Expand All @@ -246,20 +241,17 @@ extension PeerDIDHelper {
let decoder = JSONDecoder()
if
let service = try? decoder.decode(
PeerDIDService.self,
PeerDIDServiceLegacy.self,
from: peerDIDServiceData
)
{
return [try service.toDIDDocumentService(did: did, index: 0)]
return try service.toDIDDocumentService(did: did, index: index)
} else {
let services = try decoder.decode(
[PeerDIDService].self,
let service = try decoder.decode(
PeerDIDService.self,
from: peerDIDServiceData
)

return try Dictionary(grouping: services, by: \.t)
.mapValues { try $0.enumerated().map { try $0.element.toDIDDocumentService(did: did, index: $0.offset) } }
.flatMap { $0.value }
return try service.toDIDDocumentService(did: did, index: index)
}
}
}
Expand All @@ -283,7 +275,7 @@ extension DIDDocument.VerificationMethod {
)
}
}
extension DIDDocument.Service.ServiceEndpoint {
extension DIDDocument.Service {

func getJsonString() throws -> String? {
let encoder = JSONEncoder.peerDIDEncoder()
Expand Down
21 changes: 10 additions & 11 deletions Tests/PeerDIDTests/CreatePeerDIDAlgo2Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ final class CreatePeerDIDAlgo2Tests: XCTestCase {
let validService = DIDDocument.Service(
id: "test",
type: "DIDCommMessaging",
serviceEndpoint: .string("https://example.com/endpoint"),
routingKeys: ["did:example:somemediator#somekey"],
accept: nil
serviceEndpoint: AnyCodable(
dictionaryLiteral: ("uri","https://example.com/endpoint"), ("routing_keys", ["did:example:somemediator#somekey"]))
)

func testCreatePeerDIDAlgo2Base58() throws {
Expand All @@ -79,12 +78,12 @@ final class CreatePeerDIDAlgo2Tests: XCTestCase {
services: [validService]
)

XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9", peerDID.string)
XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ", peerDID.string)

XCTAssertTrue(peerDID.algo2KeyAgreementKeys.contains("z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg"))
XCTAssertTrue(peerDID.algo2Service?.contains("eyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9") ?? false)
XCTAssertTrue(peerDID.algo2Service.contains("eyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ"))
}

func testCreatePeerDIDAlgo2Multibase() throws {
Expand All @@ -94,12 +93,12 @@ final class CreatePeerDIDAlgo2Tests: XCTestCase {
services: [validService]
)

XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9", peerDID.string)
XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ", peerDID.string)

XCTAssertTrue(peerDID.algo2KeyAgreementKeys.contains("z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg"))
XCTAssertTrue(peerDID.algo2Service?.contains("eyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9") ?? false)
XCTAssertTrue(peerDID.algo2Service.contains("eyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ"))
}

func testCreatePeerDIDAlgo2JWK() throws {
Expand All @@ -109,12 +108,12 @@ final class CreatePeerDIDAlgo2Tests: XCTestCase {
services: [validService]
)

XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9", peerDID.string)
XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ", peerDID.string)

XCTAssertTrue(peerDID.algo2KeyAgreementKeys.contains("z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"))
XCTAssertTrue(peerDID.algo2AuthenticationKeys.contains("z6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg"))
XCTAssertTrue(peerDID.algo2Service?.contains("eyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJcImh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnRcIiIsInQiOiJkbSJ9") ?? false)
XCTAssertTrue(peerDID.algo2Service.contains("eyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ"))
}

func testCreatePeerDIDAlgo2MultipleServices() throws {
Expand All @@ -124,9 +123,9 @@ final class CreatePeerDIDAlgo2Tests: XCTestCase {
services: [validService, validService]
)

XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SW3siciI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJzIjoiXCJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50XCIiLCJ0IjoiZG0ifSx7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwicyI6IlwiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludFwiIiwidCI6ImRtIn1d", peerDID.string)
XCTAssertEqual("did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.SeyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ.SeyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ", peerDID.string)

XCTAssertTrue(peerDID.algo2Service?.contains("W3siciI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJzIjoiXCJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50XCIiLCJ0IjoiZG0ifSx7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwicyI6IlwiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludFwiIiwidCI6ImRtIn1d") ?? false)
XCTAssertTrue(peerDID.algo2Service.contains("eyJzIjp7InIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwidXJpIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCJ9LCJ0IjoiZG0ifQ"))
}

func testCreatePeerDIDAlgo2NoServices() throws {
Expand Down
Loading

0 comments on commit 7a061c0

Please sign in to comment.