diff --git a/README.md b/README.md index b665ba68..c25163d8 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ dependencies { ## JSON Schemas -the [tbdex]() repo acts as the source of truth for all json schemas. For this reason, The `tbdex` repo is a git +the [tbdex]() repo acts as the source of truth for all json schemas and test vectors. For this reason, the `tbdex` repo is a git submodule of this repo. By default, `git clone` does not actually check out the submodule's files. Using `--recurse-submodules` option when cloning automatically initializes, fetches, and does a checkout of the appropriate commit for the submodule. @@ -41,7 +41,7 @@ If you've already cloned the repo without `--recurse-submodules`, you can do the git submodule update --init ``` -copying the schemas into the procotol package's `resources` directory can be done by running `./gradlew syncSchemas` +copying the schemas and test vectors into the protocol package's `resources` directory can be done by running `./gradlew syncSchemas` and `./gradlew syncTestVectors` respectively. # Other Docs diff --git a/protocol/build.gradle.kts b/protocol/build.gradle.kts index 3dc02462..538fcbc9 100644 --- a/protocol/build.gradle.kts +++ b/protocol/build.gradle.kts @@ -35,9 +35,14 @@ dependencies { tasks { register("syncSchemas", Sync::class) { - from("../tbdex/json-schemas") + from("../tbdex/hosted/json-schemas") into("./src/main/resources") } + + register("syncTestVectors", Sync::class) { + from("../tbdex/hosted/test-vectors/protocol/vectors") + into("./src/test/resources/test-vectors") + } } tasks.test { diff --git a/protocol/src/main/kotlin/tbdex/sdk/protocol/Validator.kt b/protocol/src/main/kotlin/tbdex/sdk/protocol/Validator.kt index 3d1bf8ca..ad2e58fe 100644 --- a/protocol/src/main/kotlin/tbdex/sdk/protocol/Validator.kt +++ b/protocol/src/main/kotlin/tbdex/sdk/protocol/Validator.kt @@ -3,9 +3,11 @@ package tbdex.sdk.protocol import com.fasterxml.jackson.databind.JsonNode import com.networknt.schema.JsonSchema import com.networknt.schema.JsonSchemaFactory +import com.networknt.schema.SchemaValidatorsConfig import com.networknt.schema.SpecVersion import tbdex.sdk.protocol.models.MessageKind import tbdex.sdk.protocol.models.ResourceKind +import java.net.URI /** * Thrown by [Validator.validate]. @@ -19,15 +21,28 @@ class ValidatorException(message: String, val errors: List = listOf()) : */ object Validator { private val schemaMap = mutableMapOf() + private val config = SchemaValidatorsConfig() /** * Initializes the validator by loading schemas for messages and resources. */ init { + // Translate external URIs into internal resource URIs + config.addUriTranslator { uri: URI -> + val uriStr = uri.toString() + val prefix = "https://tbdex.dev/" + if (uriStr.startsWith(prefix)) { + val resourceName = uriStr.substring(prefix.length) + val resourceUri = object {}.javaClass.getResource("/$resourceName")?.toURI() + return@addUriTranslator resourceUri + } + return@addUriTranslator uri + } + val factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7) val definitionsStream = object {}.javaClass.getResourceAsStream("/definitions.json") - factory.getSchema(definitionsStream) + factory.getSchema(definitionsStream, config) val schemaNames = listOf("message" to "message.schema.json", "resource" to "resource.schema.json") + MessageKind.entries.map { it.name to "${it.name}.schema.json" } + @@ -36,7 +51,7 @@ object Validator { for (schemaName in schemaNames) { val (name, fileName) = schemaName val schemaStream = object {}.javaClass.getResourceAsStream("/$fileName") - schemaMap[name] = factory.getSchema(schemaStream) + schemaMap[name] = factory.getSchema(schemaStream, config) } } diff --git a/protocol/src/main/resources/close.schema.json b/protocol/src/main/resources/close.schema.json index c508e403..d4577d2b 100644 --- a/protocol/src/main/resources/close.schema.json +++ b/protocol/src/main/resources/close.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/close.schema.json", + "$id": "https://tbdex.dev/close.schema.json", "type": "object", "additionalProperties": false, "properties": { diff --git a/protocol/src/main/resources/definitions.json b/protocol/src/main/resources/definitions.json index fee2e863..7443d2f1 100644 --- a/protocol/src/main/resources/definitions.json +++ b/protocol/src/main/resources/definitions.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/definitions.json", + "$id": "https://tbdex.dev/definitions.json", "type": "object", "definitions": { "did": { diff --git a/protocol/src/main/resources/index.html b/protocol/src/main/resources/index.html deleted file mode 100644 index 01b56a14..00000000 --- a/protocol/src/main/resources/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - JSON Schemas - - - -
-

JSON Schemas

-
-
-

Resources

- -
- -
-

Messages

- -
- -
-

Shared

- -
- - - \ No newline at end of file diff --git a/protocol/src/main/resources/message.schema.json b/protocol/src/main/resources/message.schema.json index 6fdfb756..a23a1a3a 100644 --- a/protocol/src/main/resources/message.schema.json +++ b/protocol/src/main/resources/message.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/message.schema.json", + "$id": "https://tbdex.dev/message.schema.json", "definitions": { "MessageMetadata": { "type": "object", @@ -10,7 +10,7 @@ "description": "The sender's DID" }, "to": { - "$ref": "https://tbdex.dev/json-schemas/definitions.json#/definitions/did", + "$ref": "https://tbdex.dev/definitions.json#/definitions/did", "description": "The recipient's DID" }, "kind": { diff --git a/protocol/src/main/resources/offering.schema.json b/protocol/src/main/resources/offering.schema.json index bdd708fd..2659967c 100644 --- a/protocol/src/main/resources/offering.schema.json +++ b/protocol/src/main/resources/offering.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/offering.schema.json", + "$id": "https://tbdex.dev/offering.schema.json", "definitions": { "CurrencyDetails": { "type": "object", diff --git a/protocol/src/main/resources/order.schema.json b/protocol/src/main/resources/order.schema.json index d8fcef39..5ace8afb 100644 --- a/protocol/src/main/resources/order.schema.json +++ b/protocol/src/main/resources/order.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/order.schema.json", + "$id": "https://tbdex.dev/order.schema.json", "type": "object", "additionalProperties": false, "properties": {} diff --git a/protocol/src/main/resources/orderstatus.schema.json b/protocol/src/main/resources/orderstatus.schema.json index bbdc6e30..9aa20be3 100644 --- a/protocol/src/main/resources/orderstatus.schema.json +++ b/protocol/src/main/resources/orderstatus.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/orderstatus.schema.json", + "$id": "https://tbdex.dev/orderstatus.schema.json", "type": "object", "required": [ "orderStatus" diff --git a/protocol/src/main/resources/quote.schema.json b/protocol/src/main/resources/quote.schema.json index e4ffbe59..57ab134a 100644 --- a/protocol/src/main/resources/quote.schema.json +++ b/protocol/src/main/resources/quote.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/quote.schema.json", + "$id": "https://tbdex.dev/quote.schema.json", "definitions": { "QuoteDetails": { "type": "object", diff --git a/protocol/src/main/resources/reputation.schema.json b/protocol/src/main/resources/reputation.schema.json index 3e7550e9..9e08fbfe 100644 --- a/protocol/src/main/resources/reputation.schema.json +++ b/protocol/src/main/resources/reputation.schema.json @@ -1,4 +1,4 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/reputation.schema.json" + "$id": "https://tbdex.dev/reputation.schema.json" } \ No newline at end of file diff --git a/protocol/src/main/resources/resource.schema.json b/protocol/src/main/resources/resource.schema.json index d4158e83..2d90cc41 100644 --- a/protocol/src/main/resources/resource.schema.json +++ b/protocol/src/main/resources/resource.schema.json @@ -1,13 +1,13 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/resource.schema.json", + "$id": "https://tbdex.dev/resource.schema.json", "type": "object", "properties": { "metadata": { "type": "object", "properties": { "from": { - "$ref": "https://tbdex.dev/json-schemas/definitions.json#/definitions/did", + "$ref": "https://tbdex.dev/definitions.json#/definitions/did", "description": "The PFI's DID" }, "kind": { diff --git a/protocol/src/main/resources/rfq.schema.json b/protocol/src/main/resources/rfq.schema.json index 0a331cff..a8b9cb87 100644 --- a/protocol/src/main/resources/rfq.schema.json +++ b/protocol/src/main/resources/rfq.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://tbdex.dev/json-schemas/rfq.schema.json", + "$id": "https://tbdex.dev/rfq.schema.json", "definitions": { "SelectedPaymentMethod": { "type": "object", diff --git a/protocol/src/test/kotlin/tbdex/sdk/protocol/ResourceVectorTest.kt b/protocol/src/test/kotlin/tbdex/sdk/protocol/ResourceVectorTest.kt index ea6b948c..34af7796 100644 --- a/protocol/src/test/kotlin/tbdex/sdk/protocol/ResourceVectorTest.kt +++ b/protocol/src/test/kotlin/tbdex/sdk/protocol/ResourceVectorTest.kt @@ -1,27 +1,29 @@ package tbdex.sdk.protocol +import com.fasterxml.jackson.databind.JsonNode import tbdex.sdk.protocol.models.Offering import tbdex.sdk.protocol.models.Resource import tbdex.sdk.protocol.serialization.Json import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertNotNull class ResourceVectorTest { @Test - fun `parse offering`() { - val serializedOffering = TestVectors.offering() - val offering = Resource.parse(serializedOffering) - assertIs(offering) + fun `parse-offering json`() { + val vector = TestVectors.getVector("parse-offering.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } - @Test - fun `serialized offering matches original`() { - val serializedOffering = TestVectors.offering() - val offering = Resource.parse(serializedOffering) - val serializedOffering2 = Json.stringify(offering) + private inline fun testNonErrorTestVector(vector: JsonNode) { + val input = vector["input"].textValue() + assertNotNull(input) - assertEquals(serializedOffering, serializedOffering2) - } + val tbDEXMessage = Resource.parse(input) + assertIs(tbDEXMessage) + assertEquals(vector["output"], Json.jsonMapper.readTree(tbDEXMessage.toString())) + } } \ No newline at end of file diff --git a/protocol/src/test/kotlin/tbdex/sdk/protocol/TbdexTestVectorsMessageParse.kt b/protocol/src/test/kotlin/tbdex/sdk/protocol/TbdexTestVectorsMessageParse.kt index fbaa7195..18b4acd7 100644 --- a/protocol/src/test/kotlin/tbdex/sdk/protocol/TbdexTestVectorsMessageParse.kt +++ b/protocol/src/test/kotlin/tbdex/sdk/protocol/TbdexTestVectorsMessageParse.kt @@ -1,5 +1,8 @@ package tbdex.sdk.protocol +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.junit.jupiter.api.Test import tbdex.sdk.protocol.models.Close import tbdex.sdk.protocol.models.Message @@ -10,67 +13,59 @@ import tbdex.sdk.protocol.models.Rfq import tbdex.sdk.protocol.serialization.Json import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertNotNull class TbdexTestVectorsMessageParse { @Test - fun `parse rfq`() { - testParsing(TestVectors.rfq()) + fun `parse-close json`() { + val vector = TestVectors.getVector("parse-close.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } @Test - fun `serialize rfq`() { - testSerialisation(TestVectors.rfq()) - } - @Test - fun `parse quote`() { - testParsing(TestVectors.quote()) + fun `parse-order json`() { + val vector = TestVectors.getVector("parse-order.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } @Test - fun `serialize quote`() { - testSerialisation(TestVectors.quote()) - } - @Test - fun `parse order`() { - testParsing(TestVectors.order()) + fun `parse-orderstatus json`() { + val vector = TestVectors.getVector("parse-orderstatus.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } - @Test - fun `serialize order`() { - testSerialisation(TestVectors.order()) - } - @Test - fun `parse order status`() { - testParsing(TestVectors.orderStatus()) - } @Test - fun `serialize order status`() { - testSerialisation(TestVectors.orderStatus()) - } - @Test - fun `parse close`() { - testParsing(TestVectors.close()) + fun `parse-quote json`() { + val vector = TestVectors.getVector("parse-quote.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } @Test - fun `serialize close`() { - testSerialisation(TestVectors.close()) + fun `parse-rfq json`() { + val vector = TestVectors.getVector("parse-rfq.json") + assertNotNull(vector) + testNonErrorTestVector(vector) } - /** - * Test parse, validate, and verify on the [vectorString]. - */ - private inline fun testParsing(vectorString: String) { - val tbDEXMessage = Message.parse(vectorString) - assertIs(tbDEXMessage) - } + private inline fun testNonErrorTestVector(vector: JsonNode) { + val input = vector["input"].textValue() + assertNotNull(input) - private fun testSerialisation(original: String) { - val tbDEXMessage = Message.parse(original) - val serialized = Json.stringify(tbDEXMessage) + val tbDEXMessage = Message.parse(input) + assertIs(tbDEXMessage) - assertEquals(original, serialized) + assertEquals(vector["output"], Json.jsonMapper.readTree(tbDEXMessage.toString())) } + // When we create test vectors with `error: true` + // private fun testErrorTestVector(vector: JsonNode) { + // val input = vector["input"].textValue() + // assertNotNull(input) + // assertThrows(Message.parse(vector["input"]) + // } } \ No newline at end of file diff --git a/protocol/src/test/kotlin/tbdex/sdk/protocol/TestVectors.kt b/protocol/src/test/kotlin/tbdex/sdk/protocol/TestVectors.kt index 04c924ce..5f4acc4d 100644 --- a/protocol/src/test/kotlin/tbdex/sdk/protocol/TestVectors.kt +++ b/protocol/src/test/kotlin/tbdex/sdk/protocol/TestVectors.kt @@ -6,16 +6,25 @@ import tbdex.sdk.protocol.serialization.Json object TestVectors { val vectors = readVectors() - fun readVectors(): JsonNode { + fun readVectors(): MutableMap { val loader = Thread.currentThread().contextClassLoader - val vectorsJson = loader.getResourceAsStream("testVectors.json")?.bufferedReader()?.readText()!! - return Json.jsonMapper.readTree(vectorsJson) + val vectors = mutableMapOf(); + val vectorFiles = arrayOf( + "parse-close.json", + "parse-offering.json", + "parse-order.json", + "parse-orderstatus.json", + "parse-quote.json", + "parse-rfq.json" + ) + for (vectorFile in vectorFiles) { + val vectorJson = loader.getResourceAsStream("test-vectors/$vectorFile")?.bufferedReader()?.readText()!! + vectors[vectorFile] = Json.jsonMapper.readTree(vectorJson) + } + return vectors } - fun offering() = vectors["resources"]["offering"].toString() - fun rfq() = vectors["messages"]["rfq"].toString() - fun quote() = vectors["messages"]["quote"].toString() - fun order() = vectors["messages"]["order"].toString() - fun orderStatus() = vectors["messages"]["orderStatus"].toString() - fun close() = vectors["messages"]["close"].toString() + fun getVector(vectorFile: String): JsonNode? { + return vectors[vectorFile] + } } \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-close.json b/protocol/src/test/resources/test-vectors/parse-close.json new file mode 100644 index 00000000..6a4370aa --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-close.json @@ -0,0 +1,19 @@ +{ + "description": "Close parses from string", + "input": "{\"metadata\":{\"kind\":\"close\",\"to\":\"did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC\",\"from\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"id\":\"close_7zzzzzzpvqf2480048h0001jm6\",\"exchangeId\":\"rfq_01hdh7fqvpevq8003pxr001a45\",\"createdAt\":\"2023-10-24T16:15:04.037Z\"},\"data\":{\"reason\":\"test reason\"},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..TDtaxXdl1Bljuft8bZlvxTXTK472fKOia12kG_mQA7UhGTVIfwO9cuDCS_86EZHPMhkAvYdOnIiUodouZVba_A\"}", + "output": { + "metadata": { + "kind": "close", + "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", + "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "id": "close_7zzzzzzpvqf2480048h0001jm6", + "exchangeId": "rfq_01hdh7fqvpevq8003pxr001a45", + "createdAt": "2023-10-24T16:15:04.037Z" + }, + "data": { + "reason": "test reason" + }, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..TDtaxXdl1Bljuft8bZlvxTXTK472fKOia12kG_mQA7UhGTVIfwO9cuDCS_86EZHPMhkAvYdOnIiUodouZVba_A" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-offering.json b/protocol/src/test/resources/test-vectors/parse-offering.json new file mode 100644 index 00000000..89aaf914 --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-offering.json @@ -0,0 +1,109 @@ +{ + "description": "can parse and serialize an Offering", + "input": "{\"metadata\":{\"kind\":\"offering\",\"from\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"id\":\"offering_01hdh7fyftfkv8002wyr000nj5\",\"createdAt\":\"2023-10-24T16:15:03.662Z\",\"updatedAt\":\"2023-10-24T16:15:03.662Z\"},\"data\":{\"description\":\"A sample offering\",\"payoutUnitsPerPayinUnit\":\"1\",\"payoutCurrency\":{\"currencyCode\":\"USDC\"},\"payinCurrency\":{\"currencyCode\":\"AUD\",\"minSubunits\":\"1\",\"maxSubunits\":\"10000\"},\"payinMethods\":[{\"kind\":\"BTC_ADDRESS\",\"requiredPaymentDetails\":{\"$schema\":\"http://json-schema.org/draft-07/schema\",\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"phoneNumber\":{\"minLength\":12,\"pattern\":\"^+2547[0-9]{8}$\",\"description\":\"Mobile Money account number of the Recipient\",\"type\":\"string\",\"title\":\"Phone Number\",\"maxLength\":12},\"accountHolderName\":{\"pattern\":\"^[A-Za-zs'-]+$\",\"description\":\"Name of the account holder as it appears on the Mobile Money account\",\"type\":\"string\",\"title\":\"Account Holder Name\",\"maxLength\":32}},\"required\":[\"accountNumber\",\"accountHolderName\"]}}],\"payoutMethods\":[{\"kind\":\"MOMO\",\"requiredPaymentDetails\":{\"$schema\":\"http://json-schema.org/draft-07/schema\",\"additionalProperties\":false,\"type\":\"object\",\"properties\":{\"phoneNumber\":{\"minLength\":12,\"pattern\":\"^+2547[0-9]{8}$\",\"description\":\"Mobile Money account number of the Recipient\",\"type\":\"string\",\"title\":\"Phone Number\",\"maxLength\":12},\"accountHolderName\":{\"pattern\":\"^[A-Za-zs'-]+$\",\"description\":\"Name of the account holder as it appears on the Mobile Money account\",\"type\":\"string\",\"title\":\"Account Holder Name\",\"maxLength\":32}},\"required\":[\"accountNumber\",\"accountHolderName\"]}}],\"requiredClaims\":{\"id\":\"test-pd-id\",\"name\":\"simple PD\",\"purpose\":\"pd for testing\",\"input_descriptors\":[{\"id\":\"whatever\",\"purpose\":\"id for testing\",\"constraints\":{\"fields\":[{\"path\":[\"$.credentialSubject.btcAddress\"]}]}}]}},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..MyaMT4LZAlkLj4w9LZMqQLsaklhHlsrob60p1XmKMKg249kweyXPGABpEnvKD_65_1s1RjdyKlEotgQT15xAYw\"}", + "output": { + "metadata": { + "kind": "offering", + "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "id": "offering_01hdh7fyftfkv8002wyr000nj5", + "createdAt": "2023-10-24T16:15:03.662Z", + "updatedAt": "2023-10-24T16:15:03.662Z" + }, + "data": { + "description": "A sample offering", + "payoutUnitsPerPayinUnit": "1", + "payoutCurrency": { + "currencyCode": "USDC" + }, + "payinCurrency": { + "currencyCode": "AUD", + "minSubunits": "1", + "maxSubunits": "10000" + }, + "payinMethods": [ + { + "kind": "BTC_ADDRESS", + "requiredPaymentDetails": { + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "type": "object", + "properties": { + "phoneNumber": { + "minLength": 12, + "pattern": "^+2547[0-9]{8}$", + "description": "Mobile Money account number of the Recipient", + "type": "string", + "title": "Phone Number", + "maxLength": 12 + }, + "accountHolderName": { + "pattern": "^[A-Za-zs'-]+$", + "description": "Name of the account holder as it appears on the Mobile Money account", + "type": "string", + "title": "Account Holder Name", + "maxLength": 32 + } + }, + "required": [ + "accountNumber", + "accountHolderName" + ] + } + } + ], + "payoutMethods": [ + { + "kind": "MOMO", + "requiredPaymentDetails": { + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "type": "object", + "properties": { + "phoneNumber": { + "minLength": 12, + "pattern": "^+2547[0-9]{8}$", + "description": "Mobile Money account number of the Recipient", + "type": "string", + "title": "Phone Number", + "maxLength": 12 + }, + "accountHolderName": { + "pattern": "^[A-Za-zs'-]+$", + "description": "Name of the account holder as it appears on the Mobile Money account", + "type": "string", + "title": "Account Holder Name", + "maxLength": 32 + } + }, + "required": [ + "accountNumber", + "accountHolderName" + ] + } + } + ], + "requiredClaims": { + "id": "test-pd-id", + "name": "simple PD", + "purpose": "pd for testing", + "input_descriptors": [ + { + "id": "whatever", + "purpose": "id for testing", + "constraints": { + "fields": [ + { + "path": [ + "$.credentialSubject.btcAddress" + ] + } + ] + } + } + ] + } + }, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..MyaMT4LZAlkLj4w9LZMqQLsaklhHlsrob60p1XmKMKg249kweyXPGABpEnvKD_65_1s1RjdyKlEotgQT15xAYw" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-order.json b/protocol/src/test/resources/test-vectors/parse-order.json new file mode 100644 index 00000000..c73fe41a --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-order.json @@ -0,0 +1,17 @@ +{ + "description": "Order parses from string", + "input": "{\"metadata\":{\"kind\":\"order\",\"to\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"from\":\"did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC\",\"id\":\"order_01hdh7fpzjeh980014a8000t9d\",\"exchangeId\":\"rfq_7zzzzzzqvhffw8002vz0000mwp\",\"createdAt\":\"2023-10-24T16:15:04.032Z\"},\"data\":{},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..FLzrqCirBzDFix3AuIpv-1FtGSUNq7v6ckUQjzrGc5JLDSggS8P-MdVJ1gW5SVCusZCRhkjk6UX4qQXSFZ4X8w\"}", + "output": { + "metadata": { + "kind": "order", + "to": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "from": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", + "id": "order_01hdh7fpzjeh980014a8000t9d", + "exchangeId": "rfq_7zzzzzzqvhffw8002vz0000mwp", + "createdAt": "2023-10-24T16:15:04.032Z" + }, + "data": {}, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..FLzrqCirBzDFix3AuIpv-1FtGSUNq7v6ckUQjzrGc5JLDSggS8P-MdVJ1gW5SVCusZCRhkjk6UX4qQXSFZ4X8w" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-orderstatus.json b/protocol/src/test/resources/test-vectors/parse-orderstatus.json new file mode 100644 index 00000000..47b3f6a0 --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-orderstatus.json @@ -0,0 +1,19 @@ +{ + "description": "Order Status parses from string", + "input": "{\"metadata\":{\"kind\":\"orderstatus\",\"to\":\"did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC\",\"from\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"id\":\"orderstatus_01hdh7fzvffj3r005wgw000ac4\",\"exchangeId\":\"rfq_01hdh7fqvzesf8002pbr001x7t\",\"createdAt\":\"2023-10-24T16:15:04.034Z\"},\"data\":{\"orderStatus\":\"PENDING\"},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..dSZEztUIOWMfb8fLuJSL5A9DblEH7ROszTUztv1b-21dgKEZMCZCAPeju2MEEIObtv7SKvWopenL7IqtP2RpVQ\"}", + "output": { + "metadata": { + "kind": "orderstatus", + "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", + "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "id": "orderstatus_01hdh7fzvffj3r005wgw000ac4", + "exchangeId": "rfq_01hdh7fqvzesf8002pbr001x7t", + "createdAt": "2023-10-24T16:15:04.034Z" + }, + "data": { + "orderStatus": "PENDING" + }, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..dSZEztUIOWMfb8fLuJSL5A9DblEH7ROszTUztv1b-21dgKEZMCZCAPeju2MEEIObtv7SKvWopenL7IqtP2RpVQ" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-quote.json b/protocol/src/test/resources/test-vectors/parse-quote.json new file mode 100644 index 00000000..ce8b0165 --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-quote.json @@ -0,0 +1,39 @@ +{ + "description": "Quote parses from string", + "input": "{\"metadata\":{\"kind\":\"quote\",\"to\":\"did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC\",\"from\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"id\":\"quote_7zzzzzzpyrfy2r004fgm0013c7\",\"exchangeId\":\"rfq_7zzzzzzzztfxx8001ff8000ex2\",\"createdAt\":\"2023-10-24T16:15:04.024Z\"},\"data\":{\"expiresAt\":\"2023-10-25T16:15:04.024Z\",\"payin\":{\"currencyCode\":\"AUD\",\"amountSubunits\":\"1000\",\"feeSubunits\":\"1\"},\"payout\":{\"currencyCode\":\"BTC\",\"amountSubunits\":\"12\",\"feeSubunits\":\"2\"},\"paymentInstructions\":{\"payin\":{\"link\":\"https://block.xyz\",\"instruction\":\"payin instruction\"},\"payout\":{\"link\":\"https://block.xyz\",\"instruction\":\"payout instruction\"}}},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..B2n8TL3Tk_Bq0uf8l-1IIj_p44fuLghihElbrF493N1b1TqexdHqkhUuevdh5DKsTi3DgiQyHd8Vijwnz6Ap_A\"}", + "output": { + "metadata": { + "kind": "quote", + "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", + "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "id": "quote_7zzzzzzpyrfy2r004fgm0013c7", + "exchangeId": "rfq_7zzzzzzzztfxx8001ff8000ex2", + "createdAt": "2023-10-24T16:15:04.024Z" + }, + "data": { + "expiresAt": "2023-10-25T16:15:04.024Z", + "payin": { + "currencyCode": "AUD", + "amountSubunits": "1000", + "feeSubunits": "1" + }, + "payout": { + "currencyCode": "BTC", + "amountSubunits": "12", + "feeSubunits": "2" + }, + "paymentInstructions": { + "payin": { + "link": "https://block.xyz", + "instruction": "payin instruction" + }, + "payout": { + "link": "https://block.xyz", + "instruction": "payout instruction" + } + } + }, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..B2n8TL3Tk_Bq0uf8l-1IIj_p44fuLghihElbrF493N1b1TqexdHqkhUuevdh5DKsTi3DgiQyHd8Vijwnz6Ap_A" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/test-vectors/parse-rfq.json b/protocol/src/test/resources/test-vectors/parse-rfq.json new file mode 100644 index 00000000..6f61df3f --- /dev/null +++ b/protocol/src/test/resources/test-vectors/parse-rfq.json @@ -0,0 +1,36 @@ +{ + "description": "RFQ parses from string", + "input": "{\"metadata\":{\"kind\":\"rfq\",\"to\":\"did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg\",\"from\":\"did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC\",\"id\":\"rfq_7zzzzzzyzte028007g0g001xdc\",\"exchangeId\":\"rfq_7zzzzzzyzte028007g0g001xdc\",\"createdAt\":\"2023-10-24T16:15:04.010Z\"},\"data\":{\"offeringId\":\"offering_01hdh7fyzzek48003ms0001bv2\",\"payinSubunits\":\"1000\",\"payinMethod\":{\"kind\":\"BTC_ADDRESS\",\"paymentDetails\":{\"address\":\"123456\"}},\"payoutMethod\":{\"kind\":\"MOMO\",\"paymentDetails\":{\"phoneNumber\":\"+254712345678\",\"accountHolderName\":\"Alfred Holder\"}},\"claims\":[\"presentation submission\"]},\"signature\":\"eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..Y9gKs87CGalvHxlChHsL2fx2wtlPbY2-tBDciz7rSKRJkWmeIdyu-7NxwUdVMlQfJ58PTLoYPknXUKL8Hc0v4w\"}", + "output": { + "metadata": { + "kind": "rfq", + "to": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", + "from": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", + "id": "rfq_7zzzzzzyzte028007g0g001xdc", + "exchangeId": "rfq_7zzzzzzyzte028007g0g001xdc", + "createdAt": "2023-10-24T16:15:04.010Z" + }, + "data": { + "offeringId": "offering_01hdh7fyzzek48003ms0001bv2", + "payinSubunits": "1000", + "payinMethod": { + "kind": "BTC_ADDRESS", + "paymentDetails": { + "address": "123456" + } + }, + "payoutMethod": { + "kind": "MOMO", + "paymentDetails": { + "phoneNumber": "+254712345678", + "accountHolderName": "Alfred Holder" + } + }, + "claims": [ + "presentation submission" + ] + }, + "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..Y9gKs87CGalvHxlChHsL2fx2wtlPbY2-tBDciz7rSKRJkWmeIdyu-7NxwUdVMlQfJ58PTLoYPknXUKL8Hc0v4w" + }, + "error": false +} \ No newline at end of file diff --git a/protocol/src/test/resources/testVectors.json b/protocol/src/test/resources/testVectors.json deleted file mode 100644 index 906bfa2a..00000000 --- a/protocol/src/test/resources/testVectors.json +++ /dev/null @@ -1,215 +0,0 @@ -{ - "resources": { - "offering": { - "metadata": { - "kind": "offering", - "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "id": "offering_01hdh7fyftfkv8002wyr000nj5", - "createdAt": "2023-10-24T16:15:03.662Z", - "updatedAt": "2023-10-24T16:15:03.662Z" - }, - "data": { - "description": "A sample offering", - "payoutUnitsPerPayinUnit": "1", - "payoutCurrency": { - "currencyCode": "USDC" - }, - "payinCurrency": { - "currencyCode": "AUD", - "minSubunits": "1", - "maxSubunits": "10000" - }, - "payinMethods": [ - { - "kind": "BTC_ADDRESS", - "requiredPaymentDetails": { - "$schema": "http://json-schema.org/draft-07/schema", - "additionalProperties": false, - "type": "object", - "properties": { - "phoneNumber": { - "minLength": 12, - "pattern": "^+2547[0-9]{8}$", - "description": "Mobile Money account number of the Recipient", - "type": "string", - "title": "Phone Number", - "maxLength": 12 - }, - "accountHolderName": { - "pattern": "^[A-Za-zs'-]+$", - "description": "Name of the account holder as it appears on the Mobile Money account", - "type": "string", - "title": "Account Holder Name", - "maxLength": 32 - } - }, - "required": [ - "accountNumber", - "accountHolderName" - ] - } - } - ], - "payoutMethods": [ - { - "kind": "MOMO", - "requiredPaymentDetails": { - "$schema": "http://json-schema.org/draft-07/schema", - "additionalProperties": false, - "type": "object", - "properties": { - "phoneNumber": { - "minLength": 12, - "pattern": "^+2547[0-9]{8}$", - "description": "Mobile Money account number of the Recipient", - "type": "string", - "title": "Phone Number", - "maxLength": 12 - }, - "accountHolderName": { - "pattern": "^[A-Za-zs'-]+$", - "description": "Name of the account holder as it appears on the Mobile Money account", - "type": "string", - "title": "Account Holder Name", - "maxLength": 32 - } - }, - "required": [ - "accountNumber", - "accountHolderName" - ] - } - } - ], - "requiredClaims": { - "id": "test-pd-id", - "name": "simple PD", - "purpose": "pd for testing", - "input_descriptors": [ - { - "id": "whatever", - "purpose": "id for testing", - "constraints": { - "fields": [ - { - "path": [ - "$.credentialSubject.btcAddress" - ] - } - ] - } - } - ] - } - }, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..MyaMT4LZAlkLj4w9LZMqQLsaklhHlsrob60p1XmKMKg249kweyXPGABpEnvKD_65_1s1RjdyKlEotgQT15xAYw" - } - }, - "messages": { - "rfq": { - "metadata": { - "kind": "rfq", - "to": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "from": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", - "id": "rfq_7zzzzzzyzte028007g0g001xdc", - "exchangeId": "rfq_7zzzzzzyzte028007g0g001xdc", - "createdAt": "2023-10-24T16:15:04.010Z" - }, - "data": { - "offeringId": "offering_01hdh7fyzzek48003ms0001bv2", - "payinSubunits": "1000", - "payinMethod": { - "kind": "BTC_ADDRESS", - "paymentDetails": { - "address": "123456" - } - }, - "payoutMethod": { - "kind": "MOMO", - "paymentDetails": { - "phoneNumber": "+254712345678", - "accountHolderName": "Alfred Holder" - } - }, - "claims": [ - "presentation submission" - ] - }, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..Y9gKs87CGalvHxlChHsL2fx2wtlPbY2-tBDciz7rSKRJkWmeIdyu-7NxwUdVMlQfJ58PTLoYPknXUKL8Hc0v4w" - }, - "quote": { - "metadata": { - "kind": "quote", - "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", - "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "id": "quote_7zzzzzzpyrfy2r004fgm0013c7", - "exchangeId": "rfq_7zzzzzzzztfxx8001ff8000ex2", - "createdAt": "2023-10-24T16:15:04.024Z" - }, - "data": { - "expiresAt": "2023-10-25T16:15:04.024Z", - "payin": { - "currencyCode": "AUD", - "amountSubunits": "1000", - "feeSubunits": "1" - }, - "payout": { - "currencyCode": "BTC", - "amountSubunits": "12", - "feeSubunits": "2" - }, - "paymentInstructions": { - "payin": { - "link": "https://block.xyz", - "instruction": "payin instruction" - }, - "payout": { - "link": "https://block.xyz", - "instruction": "payout instruction" - } - } - }, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..B2n8TL3Tk_Bq0uf8l-1IIj_p44fuLghihElbrF493N1b1TqexdHqkhUuevdh5DKsTi3DgiQyHd8Vijwnz6Ap_A" - }, - "order": { - "metadata": { - "kind": "order", - "to": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "from": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", - "id": "order_01hdh7fpzjeh980014a8000t9d", - "exchangeId": "rfq_7zzzzzzqvhffw8002vz0000mwp", - "createdAt": "2023-10-24T16:15:04.032Z" - }, - "data": {}, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaXpmUVlFaUI4ZHFhYzk2UDZTSFl0VUNDc1k3Z1NjaTRRZWFlYmdzZnoxVkMjelEzc2hpemZRWUVpQjhkcWFjOTZQNlNIWXRVQ0NzWTdnU2NpNFFlYWViZ3NmejFWQyIsImFsZyI6IkVTMjU2SyJ9..FLzrqCirBzDFix3AuIpv-1FtGSUNq7v6ckUQjzrGc5JLDSggS8P-MdVJ1gW5SVCusZCRhkjk6UX4qQXSFZ4X8w" - }, - "orderStatus": { - "metadata": { - "kind": "orderstatus", - "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", - "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "id": "orderstatus_01hdh7fzvffj3r005wgw000ac4", - "exchangeId": "rfq_01hdh7fqvzesf8002pbr001x7t", - "createdAt": "2023-10-24T16:15:04.034Z" - }, - "data": { - "orderStatus": "PENDING" - }, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..dSZEztUIOWMfb8fLuJSL5A9DblEH7ROszTUztv1b-21dgKEZMCZCAPeju2MEEIObtv7SKvWopenL7IqtP2RpVQ" - }, - "close": { - "metadata": { - "kind": "close", - "to": "did:key:zQ3shizfQYEiB8dqac96P6SHYtUCCsY7gSci4Qeaebgsfz1VC", - "from": "did:key:zQ3shhwZ18aebLvYzCDdkkUH78z6RXec843BQc9hybsMRSoxg", - "id": "close_7zzzzzzpvqf2480048h0001jm6", - "exchangeId": "rfq_01hdh7fqvpevq8003pxr001a45", - "createdAt": "2023-10-24T16:15:04.037Z" - }, - "data": { - "reason": "test reason" - }, - "signature": "eyJraWQiOiJkaWQ6a2V5OnpRM3NoaHdaMThhZWJMdll6Q0Rka2tVSDc4ejZSWGVjODQzQlFjOWh5YnNNUlNveGcjelEzc2hod1oxOGFlYkx2WXpDRGRra1VINzh6NlJYZWM4NDNCUWM5aHlic01SU294ZyIsImFsZyI6IkVTMjU2SyJ9..TDtaxXdl1Bljuft8bZlvxTXTK472fKOia12kG_mQA7UhGTVIfwO9cuDCS_86EZHPMhkAvYdOnIiUodouZVba_A" - } - } -} \ No newline at end of file diff --git a/tbdex b/tbdex new file mode 160000 index 00000000..4540cec0 --- /dev/null +++ b/tbdex @@ -0,0 +1 @@ +Subproject commit 4540cec0f38e3908f5cc6c2e5860524acf158551