Skip to content
Snippets Groups Projects
Unverified Commit 03ab8015 authored by Éloïs's avatar Éloïs
Browse files

[enh] formalization of all json data with explicit typing

parent 83671c54
No related branches found
No related tags found
1 merge request!4RFC 4 : Duniter WS2P API v1
......@@ -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": [
{
"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": [
{
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*
"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
},
IDTY2,
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)
},
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment