<commit_before># uCoin HTTP API

## Contents

* [Contents](#contents)
* [Overview](#overview)
* [Merkle URLs](#merkle-urls)
* [API](#api)
  * [node/](#node)
      * [summary](#nodesummary)
  * [wot/](#wot)
      * [add](#wotadd)
      * [certify](#wotcertify)
      * [revoke](#wotrevoke)
      * [lookup/[search]](#wotlookupsearch)
      * [requirements/[PUBKEY]](#wotrequirementspubkey)
      * [certifiers-of/[search]](#wotcertifiers-ofsearch)
      * [certified-by/[search]](#wotcertified-bysearch)
  * [currency/](#currency)
      * [parameters](#currencyparameters)
  * [blockchain/](#blockchain)
      * [parameters](#blockchainparameters)
      * [membership](#blockchainmembership)
      * [memberships/[search]](#blockchainmembershipssearch)
      * [block](#blockchainblock)
      * [block/[number]](#blockchainblocknumber)
      * [blocks/[count]/[from]](#blockchainblockscountfrom)
      * [current](#blockchaincurrent)
      * [hardship/[PUBKEY]](#blockchainhardshippubkey)
      * [difficulties](#blockchaindifficulties)
      * [with/](#blockchainwith)
          * [newcomers](#blockchainwithnewcomers)
          * [certs](#blockchainwithcerts)
          * [actives](#blockchainwithactives)
          * [leavers](#blockchainwithleavers)
          * [excluded](#blockchainwithexcluded)
          * [ud](#blockchainwithud)
          * [tx](#blockchainwithtx)
      * [branches](#blockchainbranches)
  * [network/](#network)
      * [peers](#networkpeers)
      * [peering](#networkpeering)
      * [peering/peers (GET)](#networkpeeringpeers-get)
      * [peering/peers (POST)](#networkpeeringpeers-post)
  * [tx/](#tx)
      * [process](#txprocess)
      * [sources/[pubkey]](#txsourcespubkey)
      * [history/[pubkey]](#txhistorypubkey)
      * [history/[pubkey]/blocks/[from]/[to]](#txhistorypubkeyblocksfromto)
      * [history/[pubkey]/times/[from]/[to]](#txhistorypubkeytimesfromto)
  * [ud/](#ud)
      * [history/[pubkey]](#udhistorypubkey)
  * [ws/](#ws)
      * [block](#wsblock)
      * [peer](#wspeer)

## Overview

Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit uCoin specificities.

    http[s]://Node[:port]/...
    |-- wot/
    |   |-- add
    |   |-- certify
    |   |-- revoke
    |   |-- requirements/[pubkey]
    |   |-- certifiers-of/[uid|pubkey]
    |   |-- certified-by/[uid|pubkey]
    |   |-- members
    |   `-- lookup
    |-- blockchain/
    |   |-- parameters
    |   |-- membership
    |   |-- with/
    |       |-- newcomers
    |       |-- certs
    |       |-- joiners
    |       |-- actives
    |       |-- leavers
    |       |-- excluded
    |       |-- ud
    |       `-- tx
    |   |-- hardship
    |   |   `-- [PUBKEY]
    |   |-- block
    |   |   `-- [NUMBER]
    |   |-- difficulties
    |   `-- current
    |-- network/
    |   |-- peers
    |   `-- peering
    |       `-- peers
    |-- tx/
    |   |-- process
    |   |-- sources
    |   `-- history
    |-- ud/
    |   `-- history
    `-- ws/
        |-- block
        `-- peer

## Merkle URLs

Merkle URL is a special kind of URL applicable for resources:

* `network/peering/peers (GET)`

Such kind of URL returns Merkle tree hashes informations. In uCoin, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect.

For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. uCoin Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them.

Here is an example of members Merkle tree with 5 members (taken from [Tree Hash EXchange format (THEX)](http://web.archive.org/web/20080316033726/http://www.open-content.net/specs/draft-jchapweske-thex-02.html)):

                       ROOT=H(H+E)
                        /        \
                       /          \
                 H=H(F+G)          E
                /       \           \
               /         \           \
        F=H(A+B)         G=H(C+D)     E
        /     \           /     \      \
       /       \         /       \      \
      A         B       C         D      E


    Note: H() is some hash function

Where A,B,C,D,E are already hashed data.

With such a tree structure, uCoin consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.

Hence it is quite easy for anyone who wants to check if a `Z` member joined the uCoin community as it would alter the `E` branch of the tree:

                        ROOT'=H(H+E')
                        /            \
                       /              \
                 H=H(F+G)              E'
                /       \               \
               /         \               \
        F=H(A+B)          G=H(C+D)       E'=H(E+Z)
        /     \           /     \         /     \
       /       \         /       \       /       \
      A         B       C         D     E         Z

`ROOT` changed to `ROOT'`, `E` to `E'`, but `H` did not. The whole `E'` branch should be updated with the proper new data.

For that purpose, Merkle URL defines different parameters and results:

**Parameters**

Parameter | Description
--------- | -----------
`leaves`  | Defines wether or not leaves hashes should be returned too. Defaults to `false`.
`leaf`    | Hash of a leaf whose content should be returned. Ignore `leaves` parameter.

**Returns**

Merkle URL result with `leaves=false`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "<KEY>"
}
```

Merkle URL result with `leaves=true`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "<KEY>",
  "leaves": [
    "32096C2E0EFF33D844EE6D675407ACE18289357D",
    "50C9E8D5FC98727B4BBC93CF5D64A68DB647F04F",
    "6DCD4CE23D88E2EE9568BA546C007C63D9131C1B",
    "AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC",
    "E0184ADEDF913B076626646D3F52C3B49C39AD6D"
  ]
}
```

Merkle URL result with `leaf=AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "6513D6A1582DAE614D8A3B364BF3C64C513D236B",
  "leaf": {
    "hash": "AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC",
    "value": // JSON value (object, string, int, ...)
  }
}
```

### uCoin Merkle trees leaves

Each tree manages different data, and has a different goal. Hence, each tree has its own rules on how are generated and sorted tree leaves.
Here is a summup of such rules:


Merkle URL             | Leaf                      | Sort
---------------------- | --------------------------| -------------
`network/peers (GET)`    | Hash of the peers' pubkey | By hash string sort, ascending.

#### Unicity

It has to be noted that **possible conflict exists** for leaves, as every leaf is hash, but is rather unlikely.

## API

### node/*

#### `node/summary`
**Goal**

GET technical informations about this peer.

**Parameters**

*None*.

**Returns**

Technical informations about the node.
```json
{
  "ucoin": {
    "software": "ucoind",
    "version": "0.10.3",
    "forkWindowSize": 10
  }
}
```

### wot/*

#### `wot/add`


**Goal**

POST [Identity](./Protocol.md#identity) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`identity` | The raw identity. | POST

**Returns**

The available validated data for this public key.
```json
{
  "pubkey": "<KEY>",
  "uids": [
    {
      "uid": "udid2;c;TOCQUEVILLE;FRANCOIS-XAVIER-ROBE;1989-07-14;e+48.84+002.30;0;",
      "meta": {
        "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8"
      },
      "self": "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci",
      "others": [
        {
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "22-2E910FCCCEE008C4B978040CA68211C2395C84C3E6BFB432A267384ED8CD22E5"
          },
          "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
        }
      ]
    }
  ]
}
```

#### `wot/certify`


**Goal**

POST [Certification](./Protocol.md#certification) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`cert` | The raw certification. | POST

**Returns**

The available validated data for this public key.
```json
{
  "issuer": "<KEY>",
  "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8",
  "sig": "<KEY>",
  "target": {
    "issuer": "<KEY>",
    "uid": "johnsnow",
    "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8",
    "sig": "<KEY>",
  }
}
```

#### `wot/revoke`


**Goal**

Remove an identity from Identity pool.

> N.B.: An identity **written in the blockchain cannot be removed**.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`revocation` | The raw revocation. | POST

**Returns**

True if operation went well. An HTTP error otherwise with body as error message.
```json
{
  "result": true
}
```

#### `wot/lookup/[search]`


**Goal**

GET [Public key](./Protocol.md#publickey) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | A string of data to look for (public key, uid). | URL

**Returns**

A list of public key data matching search string (may not return all results, check `partial` flag which is set to `false` if all results are here, ` true` otherwise).
```json
{
  "partial": false,
  "results": [
    {
      "pubkey": "<KEY>",
      "uids": [
        {
          "uid": "udid2;c;TOCQUEVILLE;FRANCOIS-XAVIER-ROBE;1989-07-14;e+48.84+002.30;0;",
          "meta": {
            "timestamp": "56-97A56CCE04A1B7A03264ADE09545B262CBE65E62DDA481B7D7C89EB4669F5435"
          },
          "self": "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci",
          "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==",
          "revoked": false,
          "others": [
            {
              "pubkey": "<KEY>",
              "meta": {
                "timestamp": "32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD"
              },
              "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
            }
          ]
        }
      ],
      "signed": [
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "33-AB30D958EE5CB75186972286ED3F4686B8A1C2CD"
          },
          "revocation_sig": "CK6UDDJM3d0weE1RVtzFJnw/+J507lPAtspleHc59T4+N1tzQj1RRGWrzPiTknCjnCO6SxBSJX0B+MIUWrpNAw==",
          "revoked": false,
          "signature": "Xbr7qhyGNCmLoVuuKnKIbrdmtCvb9VBIEY19izUNwA5nufsjNm8iEsBTwKWOo0lq5O1+AAPMnht8cm2JjMq8AQ=="
        },
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "978-B38F54242807DFA1A12F17E012D355D8DB92CA6E947FC5D147F131B30C639163"
          },
          "revocation_sig": "a7SFapoVaXq27NU+wZj4afmxp0SbwLGqLJih8pfX6TRKPvNp/V93fbKixbqg10cwa1CadNenztxq3ZgOivqADw==",
          "revoked": false,
          "signature": "HU9VPwC4EqPJwATPuyUJM7HLjfig5Ke1CKonL9Q78n5/uNSL2hkgE9Pxsor8CCJfkwCxh66NjGyqnGYqZnQMAg=="
        },
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "76-0DC977717C49E69A78A67C6A1526EC17ED380BC68F0C38D290A954471A1349B7"
          },
          "revocation_sig": "h8D/dx/z5K2dx06ktp7fnmLRdxkdV5wRkJgnmEvKy2k55mM2RyREpHfD7t/1CC5Ew+UD0V9N27PfaoLxZc1KCQ==",
          "revoked": true,
          "signature": "6S3x3NwiHB2QqYEY79x4wCUYHcDctbazfxIyxejs38V1uRAl4DuC8R3HJUfD6wMSiWKPqbO+td+8ZMuIn0L8AA=="
        },
        {
          "uid": "cat",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "63-999677597FC04E6148860AE888A2E1942DF0E1E732C31500BA8EFF07F06FEC0C"
          },
          "revocation_sig": "bJyoM2Tz4hltVXkLvYHOOmLP4qqh2fx7aMLkS5q0cMoEg5AFER3iETj13uoFyhz8yiAKESyAZSDjjQwp8A1QDw==",
          "revoked": false,
          "signature": "AhgblSOdxUkLwpUN9Ec46St3JGaw2jPyDn/mLcR4j3EjKxUOwHBYqqkxcQdRz/6K4Qo/xMa941MgUp6NjNbKBA=="
        }
      ]
    }
  ]
}
```

#### `wot/members`


**Goal**

GET the list of current Web of Trust members.

**Parameters**

*None*.

**Returns**

A list of public key + uid.
```json
{
  "results": [
    { "pubkey": "<KEY>", "uid": "cat" },
    { "pubkey": "<KEY>", "uid": "tac" },
    { "pubkey": "<KEY>", "uid": "toc" }
  ]
}
```

#### `wot/requirements/[pubkey]`


**Goal**

GET requirements to be filled by pubkey to become a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`pbkey` | Public key to check. | URL

**Returns**

A list of identities matching this pubkey and the requirements of each identities to become a member.

> If the pubkey is matching a member, only one identity may be displayed: the one which is a member.

```json
{
  "pubkey": "<KEY>",
  "identities": [{
      "uid": "tobi",
      "meta": {
        "timestamp": "12-20504546F37853625C1E695B757D93CFCC6E494069D53F73748E428947933E45"
      },
      "outdistanced": true,
      "certifications": 2,
      "membershipMissing": true
    }
    ...
  ]
}
```

#### `wot/certifiers-of/[search]`


**Goal**

GET [Certification](./Protocol.md#certification) data over a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL

**Returns**

A list of certifications issued to the member by other members (or who used to be members), with `written` data indicating wether the certification is written in the blockchain or not.

Each certification also has:

* a `isMember` field to indicate wether the issuer of the certification is still a member or not.
* a `written` field to indicate the block where the certification was written (or null if not written yet).

```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "isMember": true,
  sigDate: 1421787461,
  "certifications": [
    {
      "pubkey": "<KEY>",
      "uid": "certifier uid",
      "cert_time": {
        "block": 88,
        "medianTime": 1509991044
      },
      sigDate: 1421787461,
      "written": {
        "number": 872768,
        "hash": "D30978C9D6C5A348A8188603F039423D90E50DC5"
      },
      "isMember": true,
      "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL<KEY>"
    },
    ...
  ]
}
```

#### `wot/certified-by/[search]`


**Goal**

GET [Certification](./Protocol.md#certification) data over a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL

**Returns**

A list of certifications issued by the member to other members (or who used to be members), with `written` data indicating wether the certification is written in the blockchain or not.

Each certification also has:

* a `isMember` field to indicate wether the issuer of the certification is still a member or not.
* a `written` field to indicate the block where the certification was written (or null if not written yet).
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "isMember": true,
  sigDate: 1421787461,
  "certifications": [
    {
      "pubkey": "<KEY>",
      "uid": "certifier uid",
      "cert_time": {
        "block": 88,
        "medianTime": 1509991044
      },
      sigDate: 1421787461,
      "written": {
        "number": 872768,
        "hash": "D30978C9D6C5A348A8188603F039423D90E50DC5"
      },
      "isMember": true,
      "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
    },
    ...
  ]
}
```

#### `wot/identity-of/[search]`


**Goal**

GET identity data written for a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* we want see the attached identity. | URL

**Returns**

Identity data written in the blockchain.
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "sigDate": "21-EB18A8D89256EA80195990C91AD399B798F92EE8187F775DF7F4823C46E61F00"
}
```

### currency/*

#### `currency/parameters`

**Goal**

GET the blockchain parameters used by this node.

**Parameters**

*None*.

**Returns**

The synchronization parameters.
```json
{
  currency: "beta_brouzouf",
  c: 0.01,
  dt: 302400,
  ud0: 100,
  sigPeriod: 7200,
  sigStock: 45,
  sigWindow: 604800,
  sigValidity: 2629800,
  sigQty: 3,
  idtyWindow: 604800,
  msWindow: 604800,
  xpercent: 5,
  msValidity: 2629800,
  stepMax: 3,
  medianTimeBlocks: 11,
  avgGenTime: 600,
  dtDiffEval: 10,
  blocksRot: 20,
  percentRot: 0.67
}
```

### blockchain/*

#### `blockchain/parameters`

**Goal**

Shortcut to [/currency/parameters](#currency-parameters).

Parameters meaning is described under [Protocol parameters](./Protocol.md#protocol-parameters).

#### `blockchain/membership`


**Goal**

POST a [Membership](./Protocol.md#membership) document.

**Parameters**

Name | Value | Method
---- | ----- | ------
`membership` | The membership document (with signature). | POST

**Returns**

The posted membership request.
```json
{
  "signature": "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==",
  "membership": {
    "version": "2",
    "currency": "beta_brouzouf",
    "issuer": "<KEY>",
    "membership": "IN",
    "sigDate": 1390739944,
    "uid": "superman63"
  }
}
```

#### `blockchain/memberships/[search]`


**Goal**

GET [Membership](./Protocol.md#membership) data written for a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* we want see the memberships. | URL

**Returns**

A list of memberships issued by the *member* and written in the blockchain.
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "sigDate": 1390739944,
  "memberships": [
    {
	  "version": "2",
	  "currency": "beta_brouzouf",
	  "membership": "IN",
	  "blockNumber": 678,
	  "blockHash": "000007936DF3CC32BFCC1023D1258EC9E485D474",
	  "written_number": null
	},
    ...
  ]
}
```

#### `blockchain/block`

**Goal**

POST a new block to add to the blockchain.

**Parameters**

Name               | Value                          | Method
------------------ | ------------------------------ | ------
`block`             | The raw block to be added     | POST
`signature`         | Signature of the raw block    | POST

**Returns**

The promoted block if successfully added to the blockchain (see [block/[number]](#blockchainblocknumber) return object).

#### `blockchain/block/[NUMBER]`

**Goal**

GET the promoted block whose number `NUMBER`.

**Parameters**

Name               | Value                                                         | Method
------------------ | ------------------------------------------------------------- | ------
`NUMBER`           | The promoted block number (integer value) we want to see.  | URL

**Returns**

The promoted block if it exists (otherwise return HTTP 404).
```json
{
  "version": 2,
  "currency": "beta_brouzouf",
  "nonce": 28,
  "inner_hash": "FD09B0F7CEC5A575CA6E528DC4C854B612AE77B7283F48E0D28677F5C9C9D0DD",
  "number": 1,
  "time": 1408996317,
  "medianTime": 1408992543,
  "dividend": 254,
  "monetaryMass": 18948,
  "issuer": "<KEY>",
  "previousHash": "0009A7A62703F976F683BBA500FC0CB832B8220D",
  "previousIssuer": "<KEY>",
  "membersCount": 4,
  "hash": "0000F40BDC0399F2E84000468628F50A122B5F16",
  "identities": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX:1409007070:udid2;c;CAT;LOL;2000-04-19;e+43.70-079.42;0;"
  ],
  "joiners": [
"9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk:1505004141"
  ],
  "leavers": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk:1505004141"
  ],
  "revoked": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX"
  ],
  "excluded": [
    "<KEY>"
  ],
  "certifications": [
    "CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:1505900000:<KEY>"
  ],
  "transactions": [
    {
      "signatures": [
        "<KEY>
        "<KEY>",
        "<KEY>"
      ],
        "version": 2,
        "currency": "beta_brouzouf",
        "issuers": [
          "<KEY>",
          "<KEY>",
          "<KEY>"
        ],
        "inputs": [
          "T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0",
          "T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:0",<commit_msg>Fix #376 HTTP APi doc for Leavers<commit_after># uCoin HTTP API

## Contents

* [Contents](#contents)
* [Overview](#overview)
* [Merkle URLs](#merkle-urls)
* [API](#api)
  * [node/](#node)
      * [summary](#nodesummary)
  * [wot/](#wot)
      * [add](#wotadd)
      * [certify](#wotcertify)
      * [revoke](#wotrevoke)
      * [lookup/[search]](#wotlookupsearch)
      * [requirements/[PUBKEY]](#wotrequirementspubkey)
      * [certifiers-of/[search]](#wotcertifiers-ofsearch)
      * [certified-by/[search]](#wotcertified-bysearch)
  * [currency/](#currency)
      * [parameters](#currencyparameters)
  * [blockchain/](#blockchain)
      * [parameters](#blockchainparameters)
      * [membership](#blockchainmembership)
      * [memberships/[search]](#blockchainmembershipssearch)
      * [block](#blockchainblock)
      * [block/[number]](#blockchainblocknumber)
      * [blocks/[count]/[from]](#blockchainblockscountfrom)
      * [current](#blockchaincurrent)
      * [hardship/[PUBKEY]](#blockchainhardshippubkey)
      * [difficulties](#blockchaindifficulties)
      * [with/](#blockchainwith)
          * [newcomers](#blockchainwithnewcomers)
          * [certs](#blockchainwithcerts)
          * [actives](#blockchainwithactives)
          * [leavers](#blockchainwithleavers)
          * [excluded](#blockchainwithexcluded)
          * [ud](#blockchainwithud)
          * [tx](#blockchainwithtx)
      * [branches](#blockchainbranches)
  * [network/](#network)
      * [peers](#networkpeers)
      * [peering](#networkpeering)
      * [peering/peers (GET)](#networkpeeringpeers-get)
      * [peering/peers (POST)](#networkpeeringpeers-post)
  * [tx/](#tx)
      * [process](#txprocess)
      * [sources/[pubkey]](#txsourcespubkey)
      * [history/[pubkey]](#txhistorypubkey)
      * [history/[pubkey]/blocks/[from]/[to]](#txhistorypubkeyblocksfromto)
      * [history/[pubkey]/times/[from]/[to]](#txhistorypubkeytimesfromto)
  * [ud/](#ud)
      * [history/[pubkey]](#udhistorypubkey)
  * [ws/](#ws)
      * [block](#wsblock)
      * [peer](#wspeer)

## Overview

Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit uCoin specificities.

    http[s]://Node[:port]/...
    |-- wot/
    |   |-- add
    |   |-- certify
    |   |-- revoke
    |   |-- requirements/[pubkey]
    |   |-- certifiers-of/[uid|pubkey]
    |   |-- certified-by/[uid|pubkey]
    |   |-- members
    |   `-- lookup
    |-- blockchain/
    |   |-- parameters
    |   |-- membership
    |   |-- with/
    |       |-- newcomers
    |       |-- certs
    |       |-- joiners
    |       |-- actives
    |       |-- leavers
    |       |-- excluded
    |       |-- ud
    |       `-- tx
    |   |-- hardship
    |   |   `-- [PUBKEY]
    |   |-- block
    |   |   `-- [NUMBER]
    |   |-- difficulties
    |   `-- current
    |-- network/
    |   |-- peers
    |   `-- peering
    |       `-- peers
    |-- tx/
    |   |-- process
    |   |-- sources
    |   `-- history
    |-- ud/
    |   `-- history
    `-- ws/
        |-- block
        `-- peer

## Merkle URLs

Merkle URL is a special kind of URL applicable for resources:

* `network/peering/peers (GET)`

Such kind of URL returns Merkle tree hashes informations. In uCoin, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect.

For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. uCoin Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them.

Here is an example of members Merkle tree with 5 members (taken from [Tree Hash EXchange format (THEX)](http://web.archive.org/web/20080316033726/http://www.open-content.net/specs/draft-jchapweske-thex-02.html)):

                       ROOT=H(H+E)
                        /        \
                       /          \
                 H=H(F+G)          E
                /       \           \
               /         \           \
        F=H(A+B)         G=H(C+D)     E
        /     \           /     \      \
       /       \         /       \      \
      A         B       C         D      E


    Note: H() is some hash function

Where A,B,C,D,E are already hashed data.

With such a tree structure, uCoin consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.

Hence it is quite easy for anyone who wants to check if a `Z` member joined the uCoin community as it would alter the `E` branch of the tree:

                        ROOT'=H(H+E')
                        /            \
                       /              \
                 H=H(F+G)              E'
                /       \               \
               /         \               \
        F=H(A+B)          G=H(C+D)       E'=H(E+Z)
        /     \           /     \         /     \
       /       \         /       \       /       \
      A         B       C         D     E         Z

`ROOT` changed to `ROOT'`, `E` to `E'`, but `H` did not. The whole `E'` branch should be updated with the proper new data.

For that purpose, Merkle URL defines different parameters and results:

**Parameters**

Parameter | Description
--------- | -----------
`leaves`  | Defines wether or not leaves hashes should be returned too. Defaults to `false`.
`leaf`    | Hash of a leaf whose content should be returned. Ignore `leaves` parameter.

**Returns**

Merkle URL result with `leaves=false`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "6513D6A1582DAE614D8A3B364BF3C64C513D236B"
}
```

Merkle URL result with `leaves=true`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "6513D6A1582DAE614D8A3B364BF3C64C513D236B",
  "leaves": [
    "32096C2E0EFF33D844EE6D675407ACE18289357D",
    "50C9E8D5FC98727B4BBC93CF5D64A68DB647F04F",
    "6DCD4CE23D88E2EE9568BA546C007C63D9131C1B",
    "AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC",
    "E0184ADEDF913B076626646D3F52C3B49C39AD6D"
  ]
}
```

Merkle URL result with `leaf=AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC`.
```json
{
  "depth": 3,
  "nodesCount": 6,
  "leavesCount": 5,
  "root": "6513D6A1582DAE614D8A3B364BF3C64C513D236B",
  "leaf": {
    "hash": "AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC",
    "value": // JSON value (object, string, int, ...)
  }
}
```

### uCoin Merkle trees leaves

Each tree manages different data, and has a different goal. Hence, each tree has its own rules on how are generated and sorted tree leaves.
Here is a summup of such rules:


Merkle URL             | Leaf                      | Sort
---------------------- | --------------------------| -------------
`network/peers (GET)`    | Hash of the peers' pubkey | By hash string sort, ascending.

#### Unicity

It has to be noted that **possible conflict exists** for leaves, as every leaf is hash, but is rather unlikely.

## API

### node/*

#### `node/summary`
**Goal**

GET technical informations about this peer.

**Parameters**

*None*.

**Returns**

Technical informations about the node.
```json
{
  "ucoin": {
    "software": "ucoind",
    "version": "0.10.3",
    "forkWindowSize": 10
  }
}
```

### wot/*

#### `wot/add`


**Goal**

POST [Identity](./Protocol.md#identity) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`identity` | The raw identity. | POST

**Returns**

The available validated data for this public key.
```json
{
  "pubkey": "<KEY>",
  "uids": [
    {
      "uid": "udid2;c;TOCQUEVILLE;FRANCOIS-XAVIER-ROBE;1989-07-14;e+48.84+002.30;0;",
      "meta": {
        "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8"
      },
      "self": "<KEY>",
      "others": [
        {
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "22-2E910FCCCEE008C4B978040CA68211C2395C84C3E6BFB432A267384ED8CD22E5"
          },
          "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
        }
      ]
    }
  ]
}
```

#### `wot/certify`


**Goal**

POST [Certification](./Protocol.md#certification) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`cert` | The raw certification. | POST

**Returns**

The available validated data for this public key.
```json
{
  "issuer": "<KEY>",
  "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8",
  "sig": "<KEY>",
  "target": {
    "issuer": "<KEY>",
    "uid": "johnsnow",
    "timestamp": "44-76522E321B3380B058DB6D9E66121705EEA63610869A7C5B3E701CF6AF2D55A8",
    "sig": "<KEY>",
  }
}
```

#### `wot/revoke`


**Goal**

Remove an identity from Identity pool.

> N.B.: An identity **written in the blockchain cannot be removed**.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`revocation` | The raw revocation. | POST

**Returns**

True if operation went well. An HTTP error otherwise with body as error message.
```json
{
  "result": true
}
```

#### `wot/lookup/[search]`


**Goal**

GET [Public key](./Protocol.md#publickey) data.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | A string of data to look for (public key, uid). | URL

**Returns**

A list of public key data matching search string (may not return all results, check `partial` flag which is set to `false` if all results are here, ` true` otherwise).
```json
{
  "partial": false,
  "results": [
    {
      "pubkey": "<KEY>",
      "uids": [
        {
          "uid": "udid2;c;TOCQUEVILLE;FRANCOIS-XAVIER-ROBE;1989-07-14;e+48.84+002.30;0;",
          "meta": {
            "timestamp": "56-97A56CCE04A1B7A03264ADE09545B262CBE65E62DDA481B7D7C89EB4669F5435"
          },
          "self": "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci",
          "revocation_sig": "<KEY>
          "revoked": false,
          "others": [
            {
              "pubkey": "<KEY>",
              "meta": {
                "timestamp": "32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD"
              },
              "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
            }
          ]
        }
      ],
      "signed": [
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "33-AB30D958EE5CB75186972286ED3F4686B8A1C2CD"
          },
          "revocation_sig": "CK6UDDJM3d0weE1RVtzFJnw/+J507lPAtspleHc59T4+N1tzQj1RRGWrzPiTknCjnCO6SxBSJX0B+MIUWrpNAw==",
          "revoked": false,
          "signature": "Xbr7qhyGNCmLoVuuKnKIbrdmtCvb9VBIEY19izUNwA5nufsjNm8iEsBTwKWOo0lq5O1+AAPMnht8cm2JjMq8AQ=="
        },
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "978-B38F54242807DFA1A12F17E012D355D8DB92CA6E947FC5D147F131B30C639163"
          },
          "revocation_sig": "a7SFapoVaXq27NU+wZj4afmxp0SbwLGqLJih8pfX6TRKPvNp/V93fbKixbqg10cwa1CadNenztxq3ZgOivqADw==",
          "revoked": false,
          "signature": "HU9VPwC4EqPJwATPuyUJM7HLjfig5Ke1CKonL9Q78n5/uNSL2hkgE9Pxsor8CCJfkwCxh66NjGyqnGYqZnQMAg=="
        },
        {
          "uid": "snow",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "76-0DC977717C49E69A78A67C6A1526EC17ED380BC68F0C38D290A954471A1349B7"
          },
          "revocation_sig": "h8D/dx/z5K2dx06ktp7fnmLRdxkdV5wRkJgnmEvKy2k55mM2RyREpHfD7t/1CC5Ew+UD0V9N27PfaoLxZc1KCQ==",
          "revoked": true,
          "signature": "6S3x3NwiHB2QqYEY79x4wCUYHcDctbazfxIyxejs38V1uRAl4DuC8R3HJUfD6wMSiWKPqbO+td+8ZMuIn0L8AA=="
        },
        {
          "uid": "cat",
          "pubkey": "<KEY>",
          "meta": {
            "timestamp": "63-999677597FC04E6148860AE888A2E1942DF0E1E732C31500BA8EFF07F06FEC0C"
          },
          "revocation_sig": "bJyoM2Tz4hltVXkLvYHOOmLP4qqh2fx7aMLkS5q0cMoEg5AFER3iETj13uoFyhz8yiAKESyAZSDjjQwp8A1QDw==",
          "revoked": false,
          "signature": "AhgblSOdxUkLwpUN9Ec46St3JGaw2jPyDn/mLcR4j3EjKxUOwHBYqqkxcQdRz/6K4Qo/xMa941MgUp6NjNbKBA=="
        }
      ]
    }
  ]
}
```

#### `wot/members`


**Goal**

GET the list of current Web of Trust members.

**Parameters**

*None*.

**Returns**

A list of public key + uid.
```json
{
  "results": [
    { "pubkey": "<KEY>", "uid": "cat" },
    { "pubkey": "<KEY>", "uid": "tac" },
    { "pubkey": "<KEY>", "uid": "toc" }
  ]
}
```

#### `wot/requirements/[pubkey]`


**Goal**

GET requirements to be filled by pubkey to become a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`pbkey` | Public key to check. | URL

**Returns**

A list of identities matching this pubkey and the requirements of each identities to become a member.

> If the pubkey is matching a member, only one identity may be displayed: the one which is a member.

```json
{
  "pubkey": "<KEY>",
  "identities": [{
      "uid": "tobi",
      "meta": {
        "timestamp": "12-20504546F37853625C1E695B757D93CFCC6E494069D53F73748E428947933E45"
      },
      "outdistanced": true,
      "certifications": 2,
      "membershipMissing": true
    }
    ...
  ]
}
```

#### `wot/certifiers-of/[search]`


**Goal**

GET [Certification](./Protocol.md#certification) data over a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL

**Returns**

A list of certifications issued to the member by other members (or who used to be members), with `written` data indicating wether the certification is written in the blockchain or not.

Each certification also has:

* a `isMember` field to indicate wether the issuer of the certification is still a member or not.
* a `written` field to indicate the block where the certification was written (or null if not written yet).

```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "isMember": true,
  sigDate: 1421787461,
  "certifications": [
    {
      "pubkey": "<KEY>",
      "uid": "certifier uid",
      "cert_time": {
        "block": 88,
        "medianTime": 1509991044
      },
      sigDate: 1421787461,
      "written": {
        "number": 872768,
        "hash": "D30978C9D6C5A348A8188603F039423D90E50DC5"
      },
      "isMember": true,
      "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
    },
    ...
  ]
}
```

#### `wot/certified-by/[search]`


**Goal**

GET [Certification](./Protocol.md#certification) data over a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL

**Returns**

A list of certifications issued by the member to other members (or who used to be members), with `written` data indicating wether the certification is written in the blockchain or not.

Each certification also has:

* a `isMember` field to indicate wether the issuer of the certification is still a member or not.
* a `written` field to indicate the block where the certification was written (or null if not written yet).
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "isMember": true,
  sigDate: 1421787461,
  "certifications": [
    {
      "pubkey": "<KEY>",
      "uid": "certifier uid",
      "cert_time": {
        "block": 88,
        "medianTime": 1509991044
      },
      sigDate: 1421787461,
      "written": {
        "number": 872768,
        "hash": "D30978C9D6C5A348A8188603F039423D90E50DC5"
      },
      "isMember": true,
      "signature": "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r"
    },
    ...
  ]
}
```

#### `wot/identity-of/[search]`


**Goal**

GET identity data written for a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* we want see the attached identity. | URL

**Returns**

Identity data written in the blockchain.
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "sigDate": "21-EB18A8D89256EA80195990C91AD399B798F92EE8187F775DF7F4823C46E61F00"
}
```

### currency/*

#### `currency/parameters`

**Goal**

GET the blockchain parameters used by this node.

**Parameters**

*None*.

**Returns**

The synchronization parameters.
```json
{
  currency: "beta_brouzouf",
  c: 0.01,
  dt: 302400,
  ud0: 100,
  sigPeriod: 7200,
  sigStock: 45,
  sigWindow: 604800,
  sigValidity: 2629800,
  sigQty: 3,
  idtyWindow: 604800,
  msWindow: 604800,
  xpercent: 5,
  msValidity: 2629800,
  stepMax: 3,
  medianTimeBlocks: 11,
  avgGenTime: 600,
  dtDiffEval: 10,
  blocksRot: 20,
  percentRot: 0.67
}
```

### blockchain/*

#### `blockchain/parameters`

**Goal**

Shortcut to [/currency/parameters](#currency-parameters).

Parameters meaning is described under [Protocol parameters](./Protocol.md#protocol-parameters).

#### `blockchain/membership`


**Goal**

POST a [Membership](./Protocol.md#membership) document.

**Parameters**

Name | Value | Method
---- | ----- | ------
`membership` | The membership document (with signature). | POST

**Returns**

The posted membership request.
```json
{
  "signature": "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==",
  "membership": {
    "version": "2",
    "currency": "beta_brouzouf",
    "issuer": "<KEY>",
    "membership": "IN",
    "sigDate": 1390739944,
    "uid": "superman63"
  }
}
```

#### `blockchain/memberships/[search]`


**Goal**

GET [Membership](./Protocol.md#membership) data written for a member.

**Parameters**

Name  | Value | Method
----  | ----- | ------
`search` | Public key or uid of a *member* we want see the memberships. | URL

**Returns**

A list of memberships issued by the *member* and written in the blockchain.
```json
{
  "pubkey": "<KEY>",
  "uid": "user identifier",
  "sigDate": 1390739944,
  "memberships": [
    {
	  "version": "2",
	  "currency": "beta_brouzouf",
	  "membership": "IN",
	  "blockNumber": 678,
	  "blockHash": "000007936DF3CC32BFCC1023D1258EC9E485D474",
	  "written_number": null
	},
    ...
  ]
}
```

#### `blockchain/block`

**Goal**

POST a new block to add to the blockchain.

**Parameters**

Name               | Value                          | Method
------------------ | ------------------------------ | ------
`block`             | The raw block to be added     | POST
`signature`         | Signature of the raw block    | POST

**Returns**

The promoted block if successfully added to the blockchain (see [block/[number]](#blockchainblocknumber) return object).

#### `blockchain/block/[NUMBER]`

**Goal**

GET the promoted block whose number `NUMBER`.

**Parameters**

Name               | Value                                                         | Method
------------------ | ------------------------------------------------------------- | ------
`NUMBER`           | The promoted block number (integer value) we want to see.  | URL

**Returns**

The promoted block if it exists (otherwise return HTTP 404).
```json
{
  "version": 2,
  "currency": "beta_brouzouf",
  "nonce": 28,
  "inner_hash": "FD09B0F7CEC5A575CA6E528DC4C854B612AE77B7283F48E0D28677F5C9C9D0DD",
  "number": 1,
  "time": 1408996317,
  "medianTime": 1408992543,
  "dividend": 254,
  "monetaryMass": 18948,
  "issuer": "<KEY>",
  "previousHash": "0009A7A62703F976F683BBA500FC0CB832B8220D",
  "previousIssuer": "<KEY>",
  "membersCount": 4,
  "hash": "0000F40BDC0399F2E84000468628F50A122B5F16",
  "identities": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:crowlin"
  ],
  "joiners": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:crowlin"
  ],
  "leavers": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:crowlin"
  ],
  "revoked": [
    "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX"
  ],
  "excluded": [
    "<KEY>"
  ],
  "certifications": [
    "CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:1505900000:2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk"
  ],
  "transactions": [
    {
      "signatures": [
        "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw=="
        "2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX",
        "<KEY>"
      ],
        "version": 2,
        "currency": "beta_brouzouf",
        "issuers": [
          "<KEY>",
          "<KEY>",
          "<KEY>"
        ],
        "inputs": [
          "T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0",
          "T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:0",
{'fraction_non_alphanumeric': 0.15141308109355173, 'fraction_numerical': 0.08162417810589456, 'mean_word_length': 2.6656236786469343, 'pattern_counts': {'":': 348, '<': 71, '<?xml version=': 0, '>': 75, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2432940', 'n_tokens_mistral': 18860, 'n_tokens_neox': 16611, 'n_words': 3203}