diff --git a/rfc/0004_ws2p_v1.md b/rfc/0004_ws2p_v1.md index dd99566b9e8543b2d3d1430b10a0ae6b3db8826e..5460ad9cc84c27905a5df1e0a3063721feda1c38 100644 --- a/rfc/0004_ws2p_v1.md +++ b/rfc/0004_ws2p_v1.md @@ -5,6 +5,8 @@ This document details the current specifications of WS2P v1 as they are already ## Contents * [Contents](#contents) +* [Conventions](#conventions) + * [JSON format](#json-format) * [What is WS2P ?](#what-is-ws2p-) * [WS2P Endpoints](#ws2p-endpoints) * [Endpoint format](#endpoint-format) @@ -26,6 +28,28 @@ This document details the current specifications of WS2P v1 as they are already * [getRequirementsPending](#getrequirementspending) * [List of error messages](#list-of-error-messages) +## Conventions + +### JSON format + +All data in JSON format is presented in this RFC with explicit typing. + +Types list : + + Number(field description), + String(field description), + Bool(field description), + Object(field description) + +Example : + + { + "pubkey": String(identity pubkey in base58), + "expires_on": Number(timestamp), + "wasMember": Bool(true if the identity has already been a member at least once), + "pendingMembership" : Object(pending membership document) + ] + ## What is WS2P ? @@ -119,15 +143,15 @@ for each connection attempt, the process is as follows: JSON format : { - auth: 'CONNECT', - pub: "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx", - challenge: "28170b84-3468-4c21-806e-cf6457d43298055df085-8e5e-43cd-9907-e5c4f9de5bc7", - sig: "wIa/gohWYcJUt10xgsMAjlBiMYhxu2DOKDJdPiEFVB3OVynFvPPW4S/gGZQE7vlxzplSHUE3dCSWfrtjGtlGCw==" + "auth": String("CONNECT"), + "pub": String("D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"), + "challenge": String("28170b84-3468-4c21-806e-cf6457d43298055df085-8e5e-43cd-9907-e5c4f9de5bc7"), + "sig": String("wIa/gohWYcJUt10xgsMAjlBiMYhxu2DOKDJdPiEFVB3OVynFvPPW4S/gGZQE7vlxzplSHUE3dCSWfrtjGtlGCw==") } -pub : Local node public key -challenge: random string -sig: ed25519 signature of the RAW format message in base64. +pub := Local node public key +challenge := random string +sig := ed25519 signature of the RAW format message in base64. RAW format : `WS2P:CONNECT:currency_name:pub:challenge` @@ -138,13 +162,13 @@ RAW format of the example message above : `WS2P:CONNECT:g1:D9D2zaJoWYWveii1JRYLV JSON format : { - auth: 'ACK', - pub: "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx", - sig: "wIa/gohWYcJUt10xgsMAjlBiMYhxu2DOKDJdPiEFVB3OVynFvPPW4S/gGZQE7vlxzplSHUE3dCSWfrtjGtlGCw==" + "auth": String("ACK"), + "pub": String("D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"), + "sig": String("wIa/gohWYcJUt10xgsMAjlBiMYhxu2DOKDJdPiEFVB3OVynFvPPW4S/gGZQE7vlxzplSHUE3dCSWfrtjGtlGCw==") } -pub : Local node public key -sig: ed25519 signature of the RAW format message in base64. +pub := Local node public key +sig := ed25519 signature of the RAW format message in base64. raw format : `WS2P:ACK:currency_name:pub:challenge` @@ -158,12 +182,10 @@ The challenge is not retransmitted in json format of ACK messages because the re JSON format : { - auth: 'OK', - sig + "auth: String("OK"), + "sig": String(ed25519 signature of the RAW format message in base64) } -sig: ed25519 signature of the RAW format message in base64. - raw format : `WS2P:OK:currency_name:pub:challenge` _**Be careful**, this time each one signs his message OK with his own challenge that he sent in his message CONNECT. @@ -208,8 +230,8 @@ Each new head received from the same node overwrites the previous head in memory Accepted since duniter-ts **v1.6.0** { - message : "API:MESSAGE_TYPE:PUBKEY:BLOCKSTAMP", - sig : "ZkatBTCYlp1KC/AS2TcDUYmxsWo0SaIDgkTZnhJzT2HU2OdJTqYr5s5JA+8iGCf0Qml8UgiwidscAEyeEl+WBg=="" + "message": String("API:MESSAGE_TYPE:PUBKEY:BLOCKSTAMP"), + "sig": String("ZkatBTCYlp1KC/AS2TcDUYmxsWo0SaIDgkTZnhJzT2HU2OdJTqYr5s5JA+8iGCf0Qml8UgiwidscAEyeEl+WBg=="") } #### message field @@ -228,8 +250,8 @@ sig: ed25519 signature of the message field in base64. Accepted since duniter-ts **v1.6.9** { - message : "API:MESSAGE_TYPE:1:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX, - sig :"MY90zXICfbYhLlz8VrL4HWPkphZEFR+bT2JWsoKdDMadgn0R0ZjsowDsnlfNqX4F4qeWeFoxhvdVgTO9VSghCA==" + "message": String("API:MESSAGE_TYPE:1:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX"), + "sig": String("MY90zXICfbYhLlz8VrL4HWPkphZEFR+bT2JWsoKdDMadgn0R0ZjsowDsnlfNqX4F4qeWeFoxhvdVgTO9VSghCA==") } #### message field @@ -253,11 +275,11 @@ Accepted since duniter-ts **v1.6.9**, relayed fully since duniter-ts **v1.6.15** _Nodes with versions lower than 1.6.15 bounce a degraded version of the head, only `message` and `sig` fields are relayed._ { - message : "API:HEAD:1:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX, - sig : "TPh2A3NS8cHj8yrJk1Yeldx2H6bPEp46cFAGZXKfxJcNgXL2sWrlirhIOlp8pkUFSrwDawWY1zO1jlgUqMvlAg==", - messageV2: "API:HEAD:2:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX:FREE_MEMBER_ROOM:FREE_MIRROR_ROOM", - sigV2: "ta1lRrWsjGcYHcLdS75JgEW5B8ByRetFVUVVpakKNJBirhRe8HcYUHEOM7xj/+gUQGGOit6Gm5Q/lsvfsngWAQ==", - step: 0 + "message": String("API:HEAD:1:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX"), + "sig": String("TPh2A3NS8cHj8yrJk1Yeldx2H6bPEp46cFAGZXKfxJcNgXL2sWrlirhIOlp8pkUFSrwDawWY1zO1jlgUqMvlAg=="), + "messageV2": String("API:HEAD:2:PUBKEY:BLOCKSTAMP:WS2PID:SOFTWARE:SOFT_VERSION:POW_PREFIX:FREE_MEMBER_ROOM:FREE_MIRROR_ROOM"), + "sigV2": String("ta1lRrWsjGcYHcLdS75JgEW5B8ByRetFVUVVpakKNJBirhRe8HcYUHEOM7xj/+gUQGGOit6Gm5Q/lsvfsngWAQ=="), + "step": Number(0) } #### message & messageV2 fields @@ -315,8 +337,8 @@ All documents are sent in json format, the body format of the request is always { body: { - name: DOCUMENT_TYPE_ID, - [DOCUMENT_TYPE_NAME]: DOCUMENT_IN_JSON_FORMAT + name: Number(DOCUMENT_TYPE_ID), + "DOCUMENT_TYPE_NAME": Object(DOCUMENT) } } @@ -335,18 +357,18 @@ With the exception of the peer format detailed below, the json format of each do ### Peer format { - "version": 10, - "currency": CURRENCY_NAME, + "version": Number(10), + "currency": String(CURRENCY_NAME), "endpoints": [ - ENDPOINT1, - ENDPOINT2, - ENDPOINT3 + String(ENDPOINT_1), + String(ENDPOINT_2), + ... ], - "status": "UP", - "block": BLOCKSTAMP, - "signature": SIGNATURE, - "raw": RAW_FORMAT, - "pubkey": PUBKEY + "status": String("UP"), + "block": String(BLOCKSTAMP), + "signature": String(SIGNATURE), + "raw": String(RAW_FORMAT), + "pubkey": String(PUBKEY) } Real example : @@ -413,16 +435,16 @@ If all conditions are satisfied, then the document is saved in the local bdd of Specific queries can be sent via ws2p to obtain specific blocks or sandbox data. -Each type of query has a unique identifier called "name" (Realy it's an integer). +Each type of query has a unique identifier called "name". ### getCurrent JSON Message : { - reqId: REQUESTS_UNIQUE_ID, + reqId: String(REQUESTS_UNIQUE_ID), body: { - name: "CURRENT", + name: String("CURRENT"), params: {} } } @@ -433,15 +455,15 @@ it allows the requester node to identify to which query the answers it receives JSON response to success : { - resId: REQUESTS_UNIQUE_ID, - body: BLOCK_IN_JSON_FORMAT + resId: String(REQUESTS_UNIQUE_ID), + body: Object(BLOCK_IN_JSON_FORMAT) } JSON error response : { - resId: REQUESTS_UNIQUE_ID, - err: error_message_in_string_format + resId: String(REQUESTS_UNIQUE_ID), + err: String(error message) } ### getBlock @@ -449,11 +471,11 @@ JSON error response : JSON Message : { - reqId: REQUESTS_UNIQUE_ID, + reqId: String(REQUESTS_UNIQUE_ID), body: { - name: "BLOCK_BY_NUMBER", + name: String("BLOCK_BY_NUMBER"), params: { - number: BLOCK_NUMBER + number: Number(BLOCK_NUMBER) } } } @@ -464,15 +486,15 @@ it allows the requester node to identify to which query the answers it receives JSON response to success : { - resId: REQUESTS_UNIQUE_ID, - body: BLOCK_IN_JSON_FORMAT + resId: String(REQUESTS_UNIQUE_ID), + body: Object(BLOCK) } JSON error response : { - resId: REQUESTS_UNIQUE_ID, - err: error_message_in_string_format + resId: String(REQUESTS_UNIQUE_ID), + err: String(error message) } ### getBlocks @@ -484,8 +506,8 @@ JSON Message : body: { name: "BLOCKS_CHUNK", params: { - count: *number of blocks requested*, - fromNumber: *number of the 1st block of the requested interval* + count: Number(number of blocks requested), + fromNumber: Number(number of the 1st block of the requested interval) } } } @@ -496,10 +518,10 @@ it allows the requester node to identify to which query the answers it receives JSON response to success : { - resId: REQUESTS_UNIQUE_ID, + resId: String(REQUESTS_UNIQUE_ID), body: [ - BLOCK_1_IN_JSON_FORMAT, - BLOCK_2_IN_JSON_FORMAT, + Object(BLOCK_1), + Object(BLOCK_2), ... ] } @@ -507,8 +529,8 @@ JSON response to success : JSON error response : { - resId: REQUESTS_UNIQUE_ID, - err: *error message in string format* + resId: String(REQUESTS_UNIQUE_ID), + err: String(error message) } ### getRequirementsPending @@ -518,11 +540,11 @@ Requests the "requirements" of all identities that have received at least `minCe JSON Message : { - reqId: REQUESTS_UNIQUE_ID, + reqId: String(REQUEST_UNIQUE_ID), body: { - name: "WOT_REQUIREMENTS_OF_PENDING", + name: String("WOT_REQUIREMENTS_OF_PENDING"), params: { - minCert: *integer* + minCert: Number() } } } @@ -533,44 +555,89 @@ it allows the requester node to identify to which query the answers it receives JSON response to success : { - resId: REQUESTS_UNIQUE_ID, - body: { - identities: [ + "resId": REQUESTS_UNIQUE_ID, + "body": { + "identities": [ { - hash: IDTY1_HASH, - member: *boolean*, - wasMember: *boolean*, - pubkey: IDTY1_PUBKEY, - uid: IDTY1_UID, - buid: IDTY1_BUID, - sig: IDTY1_SIG, - revocation_sig: IDTY1_REVOCATION_SIG, - revoked: *boolean*, - revoked_on: *integer* + "certifications": [ + { + "expiresIn": Number(timestamp), + "from": String(issuer pubkey in base58), + "timestamp": Number(), + "to": String(receiver pubkey in base58) + }), + ... + ] + "expired": Bool(), + "isSentry": Bool(), + "membershipExpiresIn": Number(), + "membershipPendingExpiresInm": Number(), + "meta": { + "timestamp": String(identity document creation blockstamp) + }, + "outdistanced": Bool(), + "pendingCerts": [ + { + "block": Number(doubloon with "block_number" field), + "block_hash": String(certification document creation block hash), + "block_number": Number(certification document creation block number), + "blockstamp": String(certification document creation blockstamp), + "expired": Number(is always zero), + "expires_on": Number(timestamp), + "from": String(issuer pubkey in base58), + "linked": Bool(is always false), + "sig": String(certification document signature in base64), + "target": String(hash of the target identity), + "to": String(receiver pubkey in base58), + "written": Bool(is always false), + "written_block": is always Null, + "written_hash": is always Null + }, + Object(PEDNIGN_CERT_2), + ... + ], + "pendingMemberships": [ + { + "block": String(doubloon with "blockstamp" field), + "blockHash": String(membership document creation block hash), + "blockNumber": Number(99156), + "blockstamp": String(membership document creation blockstamp), + "certts": String(doubloon with "blockstamp" field), + "expired": Null, + "expires_on": Number(timestamp), + "fpr": String(doubloon with "blockHash" field), + "idtyHash": String(hash of the target identity), + "issuer": String(issuer pubkey in base58), + "membership": String(user request type : "IN" or "OUT"), + "number": Number(doubloon with "blockNumber" field), + "sig": String(membership document signature in base64), + "signature": String(doubloon with "sig" field), + "type": String(doubloon with "membership" field), + "userid": String(), + "written": Bool(is always false), + "written_number": Null + }, + ... + ], + "pubkey": String(identity pubkey in base58), + "revocation_sig": String(revocation document signature in base64, empty where the identity is not revoked), + "revoked": Bool(), + "revoked_on": Number(?), + "sig":String(identity document signature in base64), + "uid": String(), + "wasMember": Bool(true if the identity has already been a member at least once) }, - IDTY2, + Object(IDTY_2), ... ] } } -Fields details: -hash := sha256 of the identity document. -member := `true` if the identity is a member, `false` otherwise. -wasMember := `true` if the identity has already been a member at least once in the past, `false` otherwise. -pubkey := public key ed25519 of the identity -uid := Human name of identity -buid := identity document creation blockstamp -sig := signature of the identity document -revocation_sig := signature of revocation document (empty string if the revocation document has not been published) -revoked := `true` if the member has been revoked, `false` otherwise. -revoked_on := Blockstamp when identity was revoked, or `0` if identity has not yet been revoked. - JSON error response : { - resId: REQUESTS_UNIQUE_ID, - err: *error message in string format* + resId: String(REQUESTS_UNIQUE_ID), + err: String(error message) } ### List of error messages