From 8ba16d51f255ae6ccd5c696c1151879a32160a06 Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Mon, 3 Jul 2023 12:36:19 +0700 Subject: [PATCH 1/3] fix: do not use did parser from sdk 1.4 --- .../iohk/atala/castor/core/model/did/DID.scala | 5 +++++ .../atala/castor/core/model/did/PrismDID.scala | 16 ++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala index 2678bc3f20..e0e5a34d85 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala @@ -30,3 +30,8 @@ final case class DID( ) { override def toString: String = s"did:${method.value}:${methodSpecificId.value}" } + +object DID { + // TODO: implement + def fromString(s: String): Either[String, DID] = ??? +} diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala index 22324e29ec..0972fc99d6 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala @@ -56,18 +56,14 @@ object PrismDID extends ProtoModelHelper { } def fromString(s: String): Either[String, PrismDID] = { - // Only reuse code in Did.fromString not PrismDid.fromString from 1.4 SDK - // because of uncertainty around keeping prism-identity up-to-date - // as the protobuf definition evolves - Try(io.iohk.atala.prism.identity.Did.Companion.fromString(s)).toEither.left - .map(_.getMessage) + DID.fromString(s) .flatMap { did => - if (did.getMethod.toString == PRISM_METHOD.value) Right(did) - else Left(s"Expected DID to have method ${PRISM_METHOD.value}, but got \"${did.getMethod.toString}\" instead") + if (did.method.toString == PRISM_METHOD.value) Right(did) + else Left(s"Expected DID to have method ${PRISM_METHOD.value}, but got \"${did.method.toString}\" instead") } - .flatMap { (did: io.iohk.atala.prism.identity.Did) => - val canonicalMatchGroups = CANONICAL_SUFFIX_REGEX.findAllMatchIn(did.getMethodSpecificId.toString).toList - val longFormMatchGroups = LONG_FORM_SUFFIX_REGEX.findAllMatchIn(did.getMethodSpecificId.toString).toList + .flatMap { did => + val canonicalMatchGroups = CANONICAL_SUFFIX_REGEX.findAllMatchIn(did.methodSpecificId.toString).toList + val longFormMatchGroups = LONG_FORM_SUFFIX_REGEX.findAllMatchIn(did.methodSpecificId.toString).toList (canonicalMatchGroups, longFormMatchGroups) match { case (Nil, longFormPattern :: Nil) => From 86a04819fecc0be25cc1ce0408bcd830763e67fc Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Mon, 3 Jul 2023 16:32:08 +0700 Subject: [PATCH 2/3] fix: migrate to regex parsing instead of sdk 1.4 --- .../atala/castor/core/model/did/DID.scala | 58 ++++++++++++++----- .../castor/core/model/did/PrismDID.scala | 14 ++--- .../atala/castor/core/model/did/DIDSpec.scala | 26 +++++++++ 3 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala index e0e5a34d85..2976308135 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DID.scala @@ -1,37 +1,63 @@ package io.iohk.atala.castor.core.model.did -import io.iohk.atala.prism.identity as prismIdentity +import scala.util.matching.Regex -import scala.util.Try - -final case class DIDMethod private[did] (value: String) { - override def toString: String = value -} +opaque type DIDMethod = String object DIDMethod { - def fromString(s: String): Try[DIDMethod] = { - Try(DIDMethod(prismIdentity.DidMethod.Companion.fromString(s).toString)) + + private val DID_METHOD_REGEX: Regex = """^[a-z0-9]+$""".r + + extension (method: DIDMethod) { + def toString: String = method } -} -final case class DIDMethodSpecificId private[did] (value: String) { - override def toString: String = value + def fromStringUnsafe(s: String): DIDMethod = s + + def fromString(s: String): Either[String, DIDMethod] = + DID_METHOD_REGEX + .findFirstMatchIn(s) + .toRight(s"The DID method $s does not conform to the syntax") + .map(_ => s) + } +opaque type DIDMethodSpecificId = String + object DIDMethodSpecificId { - def fromString(s: String): Try[DIDMethodSpecificId] = { - Try(DIDMethodSpecificId(prismIdentity.DidMethodSpecificId.Companion.fromString(s).toString)) + + private val DID_METHOD_ID_REGEX: Regex = + """^(([A-Za-z0-9_\-\.]|(%[0-9A-F]{2}))*:)*([A-Za-z0-9_\-\.]|(%[0-9A-F]{2}))+$""".r + + extension (id: DIDMethodSpecificId) { + def toString: String = id } + + def fromStringUnsafe(s: String): DIDMethodSpecificId = s + + def fromString(s: String): Either[String, DIDMethodSpecificId] = + DID_METHOD_ID_REGEX + .findFirstMatchIn(s) + .toRight(s"The DID method specific id $s does not conform to the syntax") + .map(_ => s) + } final case class DID( method: DIDMethod, methodSpecificId: DIDMethodSpecificId ) { - override def toString: String = s"did:${method.value}:${methodSpecificId.value}" + override def toString: String = s"did:$method:$methodSpecificId" } object DID { - // TODO: implement - def fromString(s: String): Either[String, DID] = ??? + def fromString(s: String): Either[String, DID] = { + for { + _ <- Either.cond(s.startsWith("did:"), (), "DID syntax must start with 'did:' prefix") + methodRaw = s.drop("did:".length).takeWhile(_ != ':') + methodSpecificIdRaw = s.drop("did:".length).dropWhile(_ != ':').drop(1) + method <- DIDMethod.fromString(methodRaw) + methodSpecificId <- DIDMethodSpecificId.fromString(methodSpecificIdRaw) + } yield DID(method, methodSpecificId) + } } diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala index 0972fc99d6..eaa88d7e16 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PrismDID.scala @@ -6,7 +6,6 @@ import io.iohk.atala.prism.protos.node_models import io.iohk.atala.prism.protos.node_models.AtalaOperation.Operation import io.iohk.atala.shared.models.Base64UrlString import io.iohk.atala.shared.models.HexString - import scala.util.Try import scala.util.matching.Regex @@ -26,7 +25,7 @@ sealed trait PrismDID { } object PrismDID extends ProtoModelHelper { - val PRISM_METHOD: DIDMethod = DIDMethod("prism") + val PRISM_METHOD: DIDMethod = DIDMethod.fromStringUnsafe("prism") val CANONICAL_SUFFIX_REGEX: Regex = "^([0-9a-f]{64}$)".r val LONG_FORM_SUFFIX_REGEX: Regex = "^([0-9a-f]{64}):([A-Za-z0-9_-]+$)".r @@ -56,10 +55,11 @@ object PrismDID extends ProtoModelHelper { } def fromString(s: String): Either[String, PrismDID] = { - DID.fromString(s) + DID + .fromString(s) .flatMap { did => - if (did.method.toString == PRISM_METHOD.value) Right(did) - else Left(s"Expected DID to have method ${PRISM_METHOD.value}, but got \"${did.method.toString}\" instead") + if (did.method == PRISM_METHOD) Right(did) + else Left(s"Expected DID to have method $PRISM_METHOD, but got \"${did.method.toString}\" instead") } .flatMap { did => val canonicalMatchGroups = CANONICAL_SUFFIX_REGEX.findAllMatchIn(did.methodSpecificId.toString).toList @@ -102,7 +102,7 @@ object PrismDID extends ProtoModelHelper { } final case class CanonicalPrismDID private[did] (stateHash: HexString) extends PrismDID { - override val suffix: DIDMethodSpecificId = DIDMethodSpecificId.fromString(stateHash.toString).get + override val suffix: DIDMethodSpecificId = DIDMethodSpecificId.fromStringUnsafe(stateHash.toString) } final case class LongFormPrismDID private[did] (atalaOperation: node_models.AtalaOperation) extends PrismDID { @@ -114,7 +114,7 @@ final case class LongFormPrismDID private[did] (atalaOperation: node_models.Atal override val suffix: DIDMethodSpecificId = { val encodedState = Base64UrlString.fromByteArray(atalaOperation.toByteArray).toStringNoPadding - DIDMethodSpecificId.fromString(s"${stateHash.toString}:${encodedState}").get + DIDMethodSpecificId.fromStringUnsafe(s"${stateHash.toString}:${encodedState}") } def createOperation: Either[String, PrismDIDOperation.Create] = { diff --git a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala new file mode 100644 index 0000000000..87a8aca04e --- /dev/null +++ b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala @@ -0,0 +1,26 @@ +package io.iohk.atala.castor.core.model.did + +import zio.* +import zio.test.* +import zio.test.Assertion.* + +object DIDSpec extends ZIOSpecDefault { + + override def spec = suite("DID")(fromStringSpec) + + // TODO: check this https://github.com/w3c/did-test-suite + private val fromStringSpec = suite("DID.fromString")( + test("parse valid PRISM did") { + // val did = "did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb" + // did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdi + val did = + "did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdi" + val parsed = DID.fromString(did) + println(parsed) + println(parsed.map(_.method)) + println(parsed.map(_.methodSpecificId)) + assert(parsed)(isRight(anything)) + } + ) @@ TestAspect.tag("dev") + +} From b1a20667b942721ec30eb0c3ae2bb0c0d967990d Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Tue, 4 Jul 2023 16:35:26 +0700 Subject: [PATCH 3/3] tests: add did parser tests --- .../atala/castor/core/model/did/DIDSpec.scala | 193 ++++++++++++++++-- 1 file changed, 180 insertions(+), 13 deletions(-) diff --git a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala index 87a8aca04e..b5cbb49104 100644 --- a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala +++ b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/DIDSpec.scala @@ -3,24 +3,191 @@ package io.iohk.atala.castor.core.model.did import zio.* import zio.test.* import zio.test.Assertion.* +import io.iohk.atala.castor.core.util.GenUtils object DIDSpec extends ZIOSpecDefault { - override def spec = suite("DID")(fromStringSpec) + override def spec = suite("DID")(fromStringSpec, fromStringSpecFrom14Sdk) - // TODO: check this https://github.com/w3c/did-test-suite private val fromStringSpec = suite("DID.fromString")( - test("parse valid PRISM did") { - // val did = "did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb" - // did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdi - val did = - "did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdi" - val parsed = DID.fromString(did) - println(parsed) - println(parsed.map(_.method)) - println(parsed.map(_.methodSpecificId)) - assert(parsed)(isRight(anything)) + test("parse valid PRISM DID that breaks sdk 1.4 parser") { + val poisonDid = + "did:prism:c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb33d5:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdid8lQ6lJciigcytFPb7TiiGjoKE2h0dHBzOi8vZm9vLmJhci5jb20SDUxpbmtlZERvbWFpbnMaFGh0dHBzOi8vZm9vLmJhci5jb20vGjgKEmh0dHBzOi8vdXBkYXRlLmNvbRINTGlua2VkRG9tYWlucxoTaHR0cHM6Ly91cGRhdGUuY29tLxo4ChJodHRwczovL3JlbW92ZS5jb20SDUxpbmtlZERvbWFpbnMaE2h0dHBzOi8vcmVtb3ZlLmNvbS8" + val parsed = DID.fromString(poisonDid) + assert(parsed.map(_.toString))(isRight(equalTo(poisonDid))) && + assert(parsed.map(_.method.toString))(isRight(equalTo("prism"))) && + assert(parsed.map(_.methodSpecificId.toString))( + isRight( + equalTo( + "c191d4dfe2806d59df4632f78d38f80dfbfbd88187804783717e29116ffb33d5:Cu0CCuoCEjoKBmF1dGgtMRAESi4KCXNlY3AyNTZrMRIhAz3656yBEIzdpZuG3yYn8Npoty3_qQhIbpOC8QmdVnpeEj8KC2Fzc2VydGlvbi0xEAJKLgoJc2VjcDI1NmsxEiECbZrA1SxPTpIEl9VBY4a6hGqaPJbJE7v8U5b236bCbWUSOwoHbWFzdGVyMBABSi4KCXNlY3AyNTZrMRIhA0BNFWJYhLqaVji9EYfIvdid8lQ6lJciigcytFPb7TiiGjoKE2h0dHBzOi8vZm9vLmJhci5jb20SDUxpbmtlZERvbWFpbnMaFGh0dHBzOi8vZm9vLmJhci5jb20vGjgKEmh0dHBzOi8vdXBkYXRlLmNvbRINTGlua2VkRG9tYWlucxoTaHR0cHM6Ly91cGRhdGUuY29tLxo4ChJodHRwczovL3JlbW92ZS5jb20SDUxpbmtlZERvbWFpbnMaE2h0dHBzOi8vcmVtb3ZlLmNvbS8" + ) + ) + ) + }, + test("parse any valid long-form PRISM DID") { + check(GenUtils.createOperation) { operation => + val prismDID = PrismDID.buildLongFormFromOperation(operation) + val did = prismDID.toString + val parsed = DID.fromString(did) + assert(parsed.map(_.toString))(isRight(equalTo(did))) + } + }, + test("parse any valid short-form PRISM DID") { + check(GenUtils.createOperation) { operation => + val prismDID = PrismDID.buildLongFormFromOperation(operation) + val did = prismDID.asCanonical.toString + val parsed = DID.fromString(did) + assert(parsed.map(_.toString))(isRight(equalTo(did))) + } + }, + test("parse all DIDs from https://github.com/w3c/did-test-suite.git") { + /* + * Getting test inputs + * + * git clone https://github.com/w3c/did-test-suite.git + * cd did-test-suite + * cd packages/did-core-test-server/suites/implementations + * find . -name 'did-*.json' -exec bash -c "cat {} | jq '.dids' | grep 'did:'" \; + */ + val dids = Seq( + "did:3:kjzl6cwe1jw145m7jxh4jpa6iw1ps3jcjordpo81e0w04krcpz8knxvg5ygiabd", + "did:algo:56da1708-eead-4e2d-9558-f53d684003fd", + "did:art:enq:f045c5c7d50145b65ca2702c38b4e2d46658293c", + "did:cheqd:mainnet:zF7rhDBfUt9d1gJPjx7s1JXfUY7oVWkY", + "did:ebsi:znHeZWvhAK2FK2Dk1jXNe7m", + "did:elem:ropsten:EiBVk9F3eLf2u9xwLJ91-vTIXD-B7Q4m3iGhCbB2OyRiwQ", + "did:ethr:0x26bf14321004e770e7a8b080b7a526d8eed8b388", + "did:example:123", + "did:ion:EiCUAQbYJzzCY1zL8KYmTu8MxCkFwG_cjRcZI2bRpwDQkQ:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJzaWdfMDY0YmViY2MiLCJwdWJsaWNLZXlKd2siOnsiY3J2Ijoic2VjcDI1NmsxIiwia3R5IjoiRUMiLCJ4IjoibHV2TG1tbEc0NFZmcWx1aFh3SWpTdVQxVEwxc0VfMTFnQ0RlazJQbXVpbyIsInkiOiJ0WURKWmFpWDdBN2tydmNhcEFnS0pGNjd6ejVKUzF3bjRGdWNqa09kUUVvIn0sInB1cnBvc2VzIjpbImF1dGhlbnRpY2F0aW9uIiwiYXNzZXJ0aW9uTWV0aG9kIl0sInR5cGUiOiJFY2RzYVNlY3AyNTZrMVZlcmlmaWNhdGlvbktleTIwMTkifV0sInNlcnZpY2VzIjpbeyJpZCI6ImxpbmtlZGRvbWFpbnMiLCJzZXJ2aWNlRW5kcG9pbnQiOnsib3JpZ2lucyI6WyJodHRwczovL2FkbWluLXRlc3RzLWRvbWFpbi5jb20vIl19LCJ0eXBlIjoiTGlua2VkRG9tYWlucyJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQi13MlVfbW5aRUVRWmRoNUNsVWszMERBdjZrTXNMT05TSWdxaGp5WEluLUEifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaURyNk1nVWtSSm9maGVqTW96VXVoSXBSX0tFa3J1WlZGaDlheHVzS2I5cnFRIiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlBS1VxUmJTTkJKSHFUQTljbXJ1MnBtQkVvd2tIVVFVLW9CS3Q3NWQ3QU8wQSJ9fQ", + "did:is:PMW1Ks7h4brpN8FdDVLwhPDKJ7LdA7mVdd", + "did:jnctn:187c4af8932a444a9e9503fb96cb672f", + "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7", + "did:key:z5TcCQtximJCYYLLmpUhydMUfyppwqQFveNQcrmLxYqbCvDrrcu9rVrHwNZEN37CWMUBRd8xgEyPighrGMMmX8NWTnSPUuWPPeFyUhLmkgA1Vqgm3eQYHF4ye7WrkB7jYcWoa68oHQNuSzw6ezgebFtt27uvJG4yjdat8Wj1e2qPMjsR63xQbmNdDTQ4zi8GDz8EwVAgu", + "did:key:z6LSn9Ah7d33uokFv2pg66BMN5UY72WtPE6eFjGXrA4mPcCp", + "did:key:z6MkjPrEBMHGuJubLZ5HWf2jBreAuh7onKCA6BknWXYHLxjS", + "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + "did:key:z6MktZw8HgaRUoG8S9asnmDKQL458uEhuuNT9U2UK5cT6Tmh", + "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9", + "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169", + "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv", + "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", + "did:key:zQ3shwNhfEjorJrrKpqvBvNRV35NfGmVWdx2rNmQCRR58Sfpf", + "did:key:zUC7LbYAQUjoTVSJyieL3cxpbdA1QjWdqqtFMDoMRg4qkZtQWRrrd4LLVCboCd5xbxET3gNM6ALinG57wBZo5VoQ3AokhE9qpJehX4SHdsDJUGa9u3z22PEGLd1fBwzzLhTkJmV", + "did:kilt:04siJtc4dYq2gPre8Xj6KJcSjVAdi1gmjctUzjf3AwrtNnhvy", + "did:kilt:14siJtc4dYq2gPre8Xj6KJcSjVAdi1gmjctUzjf3AwrtNnhvy", + "did:lit:AEZ87t1bi5bRxmVh3ksMUi", + "did:monid:1fb352353ff51248c5104b407f9c04c3666627fcf5a167d693c9fc84b75964e2", + "did:nft:eip155.1_erc721.0xb300a43751601bd54ffee7de35929537b28e1488_2", + "did:onion:fscst5exmlmr262byztwz4kzhggjlzumvc2ndvgytzoucr2tkgxf7mid", + "did:orb:bafkreiazah4qrybzyapmrmk2dhldz24vfmavethcrgcoq7qhic63zz55ru:EiAag4cmgxAE2isL5HG3mxjS7WRq4l-xyyTgULCAcEHQQQ", + "did:orb:bafkreibcsubh3ifub7gletz27hcdyhwvrhlh5mwfth2m5fbasqua6yalay:EiA2ZtZqXjKZt-yf19ersmaCYm-gJEnlixrfk0Mi61ETTg", + "did:orb:bafkreihp4inweep4py7gw4j7hej5mqlbwa7br4u7mtrfxr5khfwpu3qu3m:EiB2tmdM_oWwjXj6AmVLm0RFa_8XKZHipOpNGpEODIVN8Q", + "did:orb:interim:EiAQ1HmY03Cx4OMhiuYHl8q-B1JYlkT1Wns-dhhccUIl5g:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJhZGQtcHVibGljLWtleXMiLCJwdWJsaWNLZXlzIjpbeyJpZCI6ImNyZWF0ZUtleSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6InJILURtZmNkRVZERi1vNm80ellxdjl2YlhPcFFFcDd3RC1XUHFDbl9ELXciLCJ5IjoidnBiRGNQX2YwS1JoRW02Sm93Y0oxbWlNTldJRXo2YWVnRHFDek80WXNxSSJ9LCJwdXJwb3NlcyI6WyJhdXRoZW50aWNhdGlvbiJdLCJ0eXBlIjoiSnNvbldlYktleTIwMjAifSx7ImlkIjoiYXV0aCIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJFZDI1NTE5Iiwia3R5IjoiT0tQIiwieCI6IjEzNkZDRjJTSEZNMUZ6aWlJYXJwNEI1RzkxUVNnNHB1dGFhSWg1VEdXREEiLCJ5IjoiIn0sInB1cnBvc2VzIjpbImFzc2VydGlvbk1ldGhvZCJdLCJ0eXBlIjoiRWQyNTUxOVZlcmlmaWNhdGlvbktleTIwMTgifV19LHsiYWN0aW9uIjoiYWRkLXNlcnZpY2VzIiwic2VydmljZXMiOlt7ImlkIjoiZGlkY29tbSIsInByaW9yaXR5IjowLCJyZWNpcGllbnRLZXlzIjpbIjZLWjZLQkZLZDQ3d3FEMlhnelJwTDZ0RGlxQkNnQzg1eEttbzhVRHExZjVSIl0sInJvdXRpbmdLZXlzIjpbIjhrNUY0bXVQN0s3NmVtZHdQNWlDOHJlRVlWb1NiWmdMM2FudmFXOTdUTm1yIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHBzOi8vaHViLmV4YW1wbGUuY29tLy5pZGVudGl0eS9kaWQ6ZXhhbXBsZTowMTIzNDU2Nzg5YWJjZGVmLyIsInR5cGUiOiJkaWQtY29tbXVuaWNhdGlvbiJ9XX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlEVXVaSFEwOENXRGVBTmJyc3VSeHh3M2V5bXNucFdNbzJ0TXQ3QUNlUUNIUSJ9LCJzdWZmaXhEYXRhIjp7ImFuY2hvck9yaWdpbiI6Imh0dHBzOi8vb3JiLmRvbWFpbjEuY29tL3NlcnZpY2VzL29yYiIsImRlbHRhSGFzaCI6IkVpQUluS05tb0d1WDJVajI1aGFCNDdGQlF4aGpmb0lJYzc3Y2h6N0p0enJXdVEiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUNIOWF3WHZQUFZZdVBneEw2WUFQX3FaeUktMzdxclcwQkdFT2o5cnJWbHd3In19", + "did:orb:interim:EiCYgffdSsqLTXT6PRYLPr6vvgn9PVecJ5nFUGh9hXgOxQ", + "did:orb:interim:EiDHdXhNm7LuCqxo4JvAwKYKiFmpf85YFswAovxTxI_y4Q", + "did:orb:ipfs:QmS4ZME5uEPtQ2DFDwhSZYtLxzFxCYjJ6kC7o3ypwanzFm:EiACG5GI9dK1fjnCMYMA6ZFhtP75HVhunEuqW-XDCAU7Ew", + "did:orb:ipfs:QmfJFePqcopDUYttpvWgec9LKeJhnwh4UjhwUJz5ZcRUqM:EiDwFxa7ooPvKDTqpemH-R-H0pNX9VzUEUzk8AZsMCf9pg", + "did:orb:ipfs:QmfX6CHk7AC43Xq9iFK9XzgH3a7kJeAn3ewWZxEcqur2wE:EiCnmXoUEEP-04kELpPiF7Ss5GesCCedfTgRPA30SJO5KQ", + "did:orb:ipfs:bafkreiacr3ga6zilvzatpcixq5mz4uvgld7yedutgcssvnmql44o6rc7yy:EiB2k0ytmo-qi_M7jGocxvj4P9D6VQJGl6gRy4f6-UUpTw", + "did:orb:webcas:testnet.orb.local:bafkreihdnftiso5b7bzmhhi65nzsutbcuv6mtrmuquzoqlrk7joyer45uq:EiARiEOCLK3GnRVHA_yF92tX3aoSJAVqW1bh7Enre1iDXw", + "did:photon:EiDS68FUZqv0da57WLI_t9Gl5TYGNxvWR3PGgRk9oXx85Q", + "did:pkh:btc:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6", + "did:pkh:celo:0xa0ae58da58dfa46fa55c3b86545e7065f90ff011", + "did:pkh:doge:DH5yaieqoZN36fDVciNyRueRGvGLR3mr7L", + "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", + "did:pkh:sol:CKg5d12Jhpej1JqtmxLJgaFqqeYjxgPqToJ4LBdvG9Ev", + "did:pkh:tz:tz1YwA1FwpgLtc1G8DKbbZ6e6PTb1dQMRn5x", + "did:polygon:0xBCFdE12C425E4CbDb45226Fe51F89F2d99667d3E", + "did:schema:public-ipfs:xsd:QmUQAxKQ5sbWWrcBZzwkThktfUGZvuPQyTrqMzb3mZnLE5", + "did:sov:mattr-dev:3WhAjtBidfhGbiAyNQBxPP", + "did:ssb:ed25519:f_6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU", + "did:trust:tc:dev:id:GvMM3dpmWH6mRhGK88Ykdh", + "did:tz:delphinet:tz1WvvbEGpBXGeTVbLiR6DYBe1izmgiYuZbq", + "did:tz:tz1YwA1FwpgLtc1G8DKbbZ6e6PTb1dQMRn5x", + "did:tz:tz2BFTyPeYRzxd5aiBchbXN3WCZhx7BqbMBq", + "did:tz:tz3agP9LGe2cXmKQyYn6T68BHKjjktDbbSWX", + "did:unisot:test:mtF5XVLJvXEeffY8fo2eUfpXqs9CqQzpj7", + "did:v1:nym:z6Mkh18zyRvTikTTYwi3p4S8kQNqLkExDamQHERxeB34AMvL", + "did:vaa:2H9XwzRXZ1o5ZwSoYDEZn24eHXcQ", + "did:web:demo.spruceid.com:2021:07:08", + "did:web:did.actor:healthcare:doctor:robert", + "did:web:did.actor:mike", + "did:web:did.actor:mike", + "did:web:evernym.com", + "did:web:kyledenhartog.com", + "did:web:or13.github.io:deno-did-pm", + "did:webkey:ssh:demo.spruceid.com:2021:07:14:keys", + ) + val parsed = dids.map(DID.fromString) + assert(parsed)(forall(isRight(anything))) && + assert(parsed.flatMap(_.toOption).map(_.toString))(equalTo(dids)) + }, + test("reject empty string") { + val did = "" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) + }, + test("reject did without method specific id section") { + val did = "did:testmethod" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) + }, + test("reject empty method specific id") { + val did = "did:testmethod:" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) + }, + test("reject method specific id ':'") { + val did = "did:testmethod::" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) } - ) @@ TestAspect.tag("dev") + ) + private val fromStringSpecFrom14Sdk = suite("DID.fromString - 1.4 sdk cases")( + test("simple") { + val did = "did:testmethod:testmethodspecificid" + val parsed = DID.fromString(did) + assert(parsed.map(_.method.toString()))(isRight(equalTo("testmethod"))) && + assert(parsed.map(_.methodSpecificId.toString()))(isRight(equalTo("testmethodspecificid"))) + }, + test("multi-section") { + val did = "did:testmethod:section1:section2" + val parsed = DID.fromString(did) + assert(parsed.map(_.method.toString()))(isRight(equalTo("testmethod"))) && + assert(parsed.map(_.methodSpecificId.toString()))(isRight(equalTo("section1:section2"))) + }, + test("special characters") { + val did = "did:testmethod123:foo.sec_1:foo.sec-2" + val parsed = DID.fromString(did) + assert(parsed.map(_.method.toString()))(isRight(equalTo("testmethod123"))) && + assert(parsed.map(_.methodSpecificId.toString()))(isRight(equalTo("foo.sec_1:foo.sec-2"))) + }, + test("empty section") { + val did = "did:testmethod::section2" + val parsed = DID.fromString(did) + assert(parsed.map(_.method.toString()))(isRight(equalTo("testmethod"))) && + assert(parsed.map(_.methodSpecificId.toString()))(isRight(equalTo(":section2"))) + }, + test("fail on non-did scheme") { + val did = "nondid:testmethod:section1:section2" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) + }, + test("fail on invalid character") { + val did1 = "did:a^bc:test" + val did2 = "did:abc:te^st" + val parsed1 = DID.fromString(did1) + val parsed2 = DID.fromString(did2) + assert(parsed1)(isLeft) && + assert(parsed2)(isLeft) + }, + test("fail on empty method name") { + val did = "did::section1:section2" + val parsed = DID.fromString(did) + assert(parsed)(isLeft) + } + ) }