Skip to content
Snippets Groups Projects

create RFC DUBP V12

All threads resolved!
Merged Éloïs requested to merge dubp_v12 into master
All threads resolved!
+ 2895
0
 
# DUBP - DUniter Blockchain Protocol
 
 
## Contents
 
 
* [Contents](#contents)
 
* [Introduction](#introduction)
 
* [Conventions](#conventions)
 
* [Documents](#documents)
 
* [Signatures](#signatures)
 
* [Formats](#formats)
 
* [Public key](#public-key)
 
* [Identity](#identity)
 
* [Revocation](#revocation)
 
* [Certification](#certification)
 
* [Membership](#membership)
 
* [Block](#block)
 
* [Transaction](#transaction)
 
* [Peer](#peer)
 
* [Variables](#variables)
 
* [Protocol parameters](#protocol-parameters)
 
* [Computed variables](#computed-variables)
 
* [Processing](#processing)
 
* [Block](#block-1)
 
* [Local validation](#local-validation)
 
* [Global validation](#global-validation)
 
* [Peer](#peer-1)
 
* [Transaction](#transaction-1)
 
* [Implementations](#implementations)
 
* [References](#references)
 
 
## Vocabulary
 
 
Word | Description
 
--------------------- | -------------
 
DUBP | Acronym for *DUniter Blockchain Protocol*. A set of rules to create Duniter based currencies.
 
Signature | The cryptographical act of certifying a document using a private key.
 
WoT | Acronym for *Web of Trust*. A groupment of individuals recognizing each other's identity through public keys and certification mechanisms
 
UD | Acronym for *Universal Dividend*. Means money issuance **directly** and **exclusively** by and to WoT members
 
realtime | The time of real life (the habitual time).
 
blocktime | The realtime recorded by a block.
 
 
## Introduction
 
 
DUBP aims at defining a data format, an interpretation of it and processing rules in order to build coherent free currency systems in a P2P environment. DUBP is to be understood as an *abstract* protocol since it defines currency parameters and rules about them, but not their value which is implementation specific.
 
 
This document describes DUBP in a bottom-up logic, so you will find first the details of the protocol (data format) to end with general protocol requirements.
 
 
## Conventions
 
 
### Documents
 
 
#### Line endings
 
 
Please note **very carefully** that every document's line **ENDS with a newline character**, *Unix-style*, that is to say `<LF>`.
 
 
This is a *very important information* as every document is subject to hashes, and Windows-style endings won't produce the expected hashes.
 
 
#### Numbering
 
 
[Block](#block) numbering starts from `0`. That is, first block is `BLOCK#0`.
 
 
#### Block identifiers
 
 
There are 2 kinds of [Block](#block) identifiers:
 
 
##### `BLOCK_ID`
 
 
It is the number of the block. Its format is `INTEGER`, so it is a positive or zero integer.
 
 
##### `BLOCK_UID`
 
It is the concatenation of the `BLOCK_ID` of a block and hash. Its format is `BLOCK_ID-HASH`.
 
 
##### Examples
 
 
The block ID of the root block is `0`. Its UID *might be* `0-883228A23F8A75B342E912DF439738450AE94F5D`.
 
The block ID of the block#433 is `433`. Its UID *might be* `433-FB11681FC1B3E36C9B7A74F4DDE2D07EC2637957`.
 
 
> Note that it says "might be" because the hash is not known in advance.
 
 
#### Currency name
 
 
A valid currency name is composed of alphanumeric characters, spaces, `-` or `_` and has a length of 2 to 50 characters.
 
 
#### Dates
 
 
For any document using a date field, targeted date is to be understood as **UTC+0** reference.
 
 
#### Integer
 
 
Any integer field has a maximum length of 19 digits.
 
 
### Signatures
 
 
#### Format
 
 
Signatures follow the [Ed25519 pattern](http://en.wikipedia.org/wiki/EdDSA), and are written under [Base64](http://en.wikipedia.org/wiki/Base64) encoding.
 
 
Here is an example of an expected signature:
 
 
H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==
 
 
Please note: In Ğ1 and Ğ1-test networks, the V10 and V11 blocks and some transactions in these blocks are signed with a buggy version of TweetNaCl: [version 20131229](http://tweetnacl.cr.yp.to/software.html). As a result, some V10 and V11 blocks have an invalid signature (as well as some transactions in V10 or V11 blocks).
 
 
#### Line endings
 
 
No new line character exists in a signature. However, a signature may be followed by a new line character, hence denoting the end of the signature.
 
 
## Formats
 
 
This section deals with the various data formats used by DUBP.
 
 
### Public key
 
 
#### Definition
 
 
A public key is to be understood as an [Ed25519](http://en.wikipedia.org/wiki/EdDSA) public key.
 
 
Its format is a [Base58](http://en.wikipedia.org/wiki/Base58) string of 43 or 44 characters, such as the following:
 
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
 
A public key is always paired with a private key, which DUBP will never deal with. DUBP only deals with public keys and signatures.
 
 
### Identity
 
 
#### Definition
 
 
Issuing an identity is the act of creating a link between a *public key* and *an arbitrary identity*. In DUBP, this link is done through the signature of an identity string by the private key corresponding to the public key. It is exactly like saying:
 
 
> « This identity refers to me ! »
 
 
#### Identity unique ID
 
DUBP does not rely on any particular identity format, which remains implementation free. Identity simply has to be a string of length between 2 and 100 characters avoiding usage of line ending characters.
 
 
In this document *identifier*, `UserID`, `USER_ID` and `uid` will be indifferently used to refer to this identity string.
 
 
#### Format
 
 
An identity is a *signed* document containing the identifier:
 
 
```yml
 
Version: 10
 
Type: Identity
 
Currency: CURRENCY_NAME
 
Issuer: PUBLIC_KEY
 
UniqueID: USER_ID
 
Timestamp: BLOCK_UID
 
```
 
 
Here, `USER_ID` has to be replaced with a valid identifier, `PUBLIC_KEY` with a valid public key and `BLOCK_UID` with a valid block unique ID. This document **is what signature is based upon**.
 
 
The whole identity document is then:
 
 
```yml
 
Version: 10
 
Type: Identity
 
Currency: CURRENCY_NAME
 
Issuer: PUBLIC_KEY
 
UniqueID: USER_ID
 
Timestamp: BLOCK_UID
 
SIGNATURE
 
```
 
 
Where:
 
 
* `CURRENCY_NAME` is a valid currency name
 
* `USER_ID` is a valid user identity string
 
* `PUBLIC_KEY` is a valid public key (base58 format)
 
* `BLOCK_UID` refers to a [block unique ID], and represents a time reference.
 
* `SIGNATURE` is a signature
 
 
So the identity issuance is the act of saying:
 
 
> « I attest, today, that this identity refers to me. »
 
 
##### Example
 
 
A valid identity:
 
 
```yml
 
Version: 10
 
Type: Identity
 
Currency: beta_brousouf
 
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
UniqueID: lolcat
 
Timestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 
J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 
```
 
 
### Revocation
 
 
 
#### Definition
 
 
An identity revocation is the act, for a given public key's owner, to revoke an identity they created for representing themself. Doing a self-revocation is exactly like saying:
 
 
> « This identity I created makes no sense anymore. It has to be definitively locked. »
 
 
#### Use case
 
 
Its goal is only to inform that a created identity was either made by mistake, or contained a mistake, may have been compromised (private key stolen, lost...), or because for some reason you want to make yourself another identity.
 
 
#### Format
 
 
A revocation is a *signed* document gathering the identity informations to revoke:
 
 
```yml
 
Version: 10
 
Type: Revocation
 
Currency: CURRENCY_NAME
 
Issuer: PUBLIC_KEY
 
IdtyUniqueID: USER_ID
 
IdtyTimestamp: BLOCK_UID
 
IdtySignature: IDTY_SIGNATURE
 
REVOCATION_SIGNATURE
 
```
 
 
Where:
 
 
* `REVOCATION_SIGNATURE` is the signature over the document, `REVOCATION_SIGNATURE` excluded.
 
 
#### Example
 
 
If we have the following identity:
 
 
```yml
 
Version: 10
 
Type: Identity
 
Currency: beta_brousouf
 
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
UniqueID: lolcat
 
Timestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 
J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 
```
 
 
A valid revocation could be:
 
 
```yml
 
Version: 10
 
Type: Revocation
 
Currency: beta_brousouf
 
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
IdtyUniqueID: lolcat
 
IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 
IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 
SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
 
```
 
 
> The revocation actually *contains* the identity, so a program receiving a Revocation can extract the Identity and check the validity of its signature before processing the Revocation document.
 
 
### Certification
 
 
#### Definition
 
 
A *certification* in DUBP refers to the document *certifying* that someone else's identity is to consider as the unique reference to a living individual.
 
 
#### Format
 
 
A certification has the following format:
 
 
```yml
 
Version: 10
 
Type: Certification
 
Currency: CURRENCY_NAME
 
Issuer: PUBLIC_KEY
 
IdtyIssuer: IDTY_ISSUER
 
IdtyUniqueID: USER_ID
 
IdtyTimestamp: BLOCK_UID
 
IdtySignature: IDTY_SIGNATURE
 
CertTimestamp: BLOCK_UID
 
CERTIFIER_SIGNATURE
 
```
 
 
Where:
 
 
* `BLOCK_UID` refers to a block unique ID.
 
* `CERTIFIER_SIGNATURE` is the signature of the *certifier*.
 
 
#### Inline format
 
 
Certifications may exist in an *inline format*, which describes the certification in a simple line. Here is the general structure:
 
 
PUBKEY_FROM:PUBKEY_TO:BLOCK_ID:SIGNATURE
 
 
Where:
 
 
* `PUBKEY_FROM` is the certification public key
 
* `PUBKEY_TO` is the public key whose identity is being certified
 
* `BLOCK_ID` is the certification time reference
 
* `SIGNATURE` is the certification signature
 
 
> Note: BLOCK_UID is not required in the inline format, since this format aims at being used in the context of a blockchain, where block_uid can be deduced.
 
 
#### Example
 
 
If we have the following complete self-certification:
 
 
```yml
 
Version: 10
 
Type: Identity
 
Currency: beta_brousouf
 
Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
UniqueID: lolcat
 
Timestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 
J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 
```
 
 
A valid certification could be:
 
 
```yml
 
Version: 10
 
Type: Certification
 
Currency: beta_brousouf
 
Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
 
IdtyIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
IdtyUniqueID: lolcat
 
IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 
IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 
CertTimestamp: 36-1076F10A7397715D2BEE82579861999EA1F274AC
 
SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
 
```
 
 
### Membership
 
 
In DUBP, a member is represented by a public key they are supposed to own. To be integrated in a WoT, the newcomer owner of the key *has to express their will* to integrate the WoT.
 
 
This step is done by issuing the following document:
 
 
```yml
 
Version: VERSION
 
Type: Membership
 
Currency: CURRENCY_NAME
 
Issuer: ISSUER
 
Block: M_BLOCK_UID
 
Membership: MEMBERSHIP_TYPE
 
UserID: USER_ID
 
CertTS: BLOCK_UID
 
```
 
 
followed by a signature of the `Issuer`.
 
 
#### Fields details
 
 
Field | Description
 
----- | -----------
 
`Version` | Denotes the current structure version.
 
`Type` | Type of the document.
 
`Currency` | Contains the name of the currency.
 
`Issuer` | The public key of the issuer.
 
`Block` | Block number and hash. Value is used to target a blockchain and precise time reference for membership's time validity.
 
`Membership` | Membership message. Value is either `IN` or `OUT` to express whether a member wishes to opt-in or opt-out the community.
 
`UserID` | Identity to use for this public key
 
`CertTS` | Identity's block UID
 
 
#### Validity
 
 
A [Membership](#membership) is to be considered having valid format if:
 
 
* `Version` equals `10`
 
* `Type` equals `Membership` value.
 
* `Currency` is a valid currency name
 
* `Issuer` is a public key
 
* `Membership` matches either `IN` or `OUT` value
 
* `Block` starts with an integer value, followed by a dash and an uppercased SHA1 string
 
* `UserID` is a non-empty string
 
* `CertTS` is a valid block UID
 
 
### Transaction
 
 
#### Definition
 
 
Transaction is the support of money: it allows to materialize coins' ownership.
 
 
#### Money ownership
 
 
Money ownership **IS NOT** limited to members of the Community. Any owner (an individual or an organization) of a public key may own money: it only requires the key to match `Ouputs` of a transaction.
 
 
#### Transfering money
 
 
Obviously, coins a sender does not own CANNOT be sent by them. That is why a transaction refers to other transactions, to prove that the sender actually owns the coins they want to send.
 
 
#### Format
 
 
A transaction is defined by the following format:
 
 
```yml
 
Version: VERSION
 
Type: Transaction
 
Currency: CURRENCY_NAME
 
Blockstamp: BLOCK_UID
 
Locktime: INTEGER
 
Issuers:
 
PUBLIC_KEY
 
...
 
Inputs:
 
INPUT
 
...
 
Unlocks:
 
UNLOCK
 
...
 
Outputs:
 
AMOUNT:BASE:CONDITIONS
 
...
 
Comment: COMMENT
 
SIGNATURES
 
...
 
```
 
 
Here is a description of each field:
 
 
Field | Description
 
----- | -----------
 
`Version` | denotes the current structure version
 
`Type` | type of the document
 
`Currency` | contains the name of the currency
 
`Blockstamp` | a block reference as timestamp of the transaction
 
`Locktime` | waiting delay to be included in the blockchain
 
`Issuers` | a list of public keys
 
`Inputs` | a list of money sources
 
`Unlocks` | a list of values justifying inputs consumption
 
`Outputs` | a list of amounts and conditions to unlock them
 
`Comment` | a comment to write on the transaction
 
 
#### Validity
 
 
A Transaction structure is considered *valid* if:
 
 
* Field `Version` equals `10`.
 
* Field `Type` equals `Transaction`.
 
* Field `Currency` is not empty.
 
* Field `Blockstamp` is a block UID
 
* Field `Locktime` is an integer
 
* Field `Issuers` is a multiline field whose lines are public keys.
 
* Field `Inputs` is a multiline field whose lines match either:
 
* `AMOUNT:BASE:D:PUBLIC_KEY:BLOCK_ID` format
 
* `AMOUNT:BASE:T:T_HASH:T_INDEX` format
 
* Field `Unlocks` is a multiline field whose lines follow `INDEX:UL_CONDITIONS` format:
 
* `IN_INDEX` must be an integer value
 
* `UL_CONDITIONS` must be a valid [Input Condition](#input-condition)
 
* Field `Outputs` is a multiline field whose lines follow `AMOUNT:BASE:CONDITIONS` format:
 
* `AMOUNT` must be an integer value
 
* `BASE` must be an integer value
 
* `CONDITIONS` must be a valid [Output Condition](#output-condition)
 
* Field `Comment` is a string of maximum 255 characters, exclusively composed of alphanumeric characters, space, `-`, `_`, `:`, `/`, `;`, `*`, `[`, `]`, `(`, `)`, `?`, `!`, `^`, `+`, `=`, `@`, `&`, `~`, `#`, `{`, `}`, `|`, `\`, `<`, `>`, `%`, `.`. Must be present even if empty.
 
 
#### Input condition
 
 
It is a suite of values each separated by a space "` `". Values must be either a `SIG(INDEX)` or `HXH(INTEGER)`.
 
 
If no values are provided, the valid input condition is an empty string.
 
 
##### Input condition examples
 
 
* `SIG(0)`
 
* `XHX(73856837)`
 
* `SIG(0) SIG(2) XHX(343)`
 
* `SIG(0) SIG(2) SIG(4) SIG(6)`
 
 
#### Output condition
 
 
It follows a machine-readable BNF grammar composed of
 
 
* `(` and `)` characters
 
* `&&` and `||` operators
 
* `SIG(PUBLIC_KEY)`, `XHX(SHA256_HASH)`, `CLTV(INTEGER)`, `CSV(INTEGER)` functions
 
* ` ` space
 
 
**An empty condition or a condition fully composed of spaces is considered an invalid output condition**.
 
 
Also, the maximum length of a condition is 1000 characters. // TODO: OK?
 
 
##### Output condition examples
 
 
* `SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd)`
 
* `(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) && XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6))`
 
* `(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) && CSV(3600))`
 
* `(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) && (CLTV(1489677041) || CSV(3600)))`
 
* `(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) && XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6)))`
 
 
#### Condition matching
 
 
Considering a transaction `TX` consuming some money: each unlock line `UNLOCK` of TX tries to unlock the input `INPUT = TX.Inputs[IN_INDEX]` refering to an ouput `Output` whose value is:
 
 
If `INPUT.TYPE = 'T'`:
 
 
Output = TRANSACTIONS_IN_BLOCKCHAIN_OR_IN_LOCAL_BLOCK[T_HASH].OUPUTS[T_INDEX]
 
 
If `INPUT.TYPE = 'D'`:
 
 
Output = BLOCKCHAIN[BLOCK_ID].Dividend[PUBLIC_KEY]
 
 
Let's name:
 
 
* `CONDITIONS` the conditions of a given output `Output`
 
* `NB_FUNCTIONS` the number of functions present in `CONDITIONS` (read from left to right)
 
* `NB_PARAMETERS` the number of parameters present in `UNLOCK`, each separated by a space (read from left to right)
 
 
Then, an `UNLOCK` line is considered *successful* if:
 
 
* `Output` exists
 
* `NB_PARAMETERS <= NB_FUNCTIONS`
 
* Each `UNLOCK` parameter returns TRUE
 
* `CONDITIONS` evaluated globally with `(`, `)`, `&&`, `||` and its locking functions returns TRUE
 
 
##### Example 1
 
 
TX1:
 
 
```yml
 
Outputs:
 
50:2:XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)
 
```
 
 
Is resolved by TX2:
 
 
```yml
 
Unlocks:
 
0:XHX(1872767826647264)
 
```
 
 
Because `XHX(1872767826647264) = 8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB` (this will be explained in the next section).
 
 
##### Example 2
 
 
TX1:
 
 
```yml
 
Outputs:
 
50:2:SIG(DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo)
 
```
 
 
Is resolved by TX2:
 
 
```yml
 
Issuers:
 
HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd
 
DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo
 
[...]
 
Unlocks:
 
0:SIG(1)
 
```
 
 
Because `SIG(1)` refers to the signature `DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo`, considering that signature `DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo` is good over TX2.
 
 
#### Locking and unlocking functions
 
 
The *locking* functions are present under `Outputs` field.
 
 
The *unlocking* functions are present under `Unlocks` field.
 
 
##### SIG function
 
 
###### Definition
 
 
Lock:
 
 
SIG(PUBKEY_A)
 
 
Unlock:
 
 
SIG(INDEX)
 
 
###### Condition
 
 
Lock `SIG(PUBKEY_A)` returns TRUE if it exists a parameter `SIG(INDEX)` returning TRUE where `Issuers[INDEX] == PUBKEY_A`.
 
 
Unlock `SIG(INDEX)` returns TRUE if `Signatures[INDEX]` is a valid valid signature of TX against `Issuers[INDEX]` public key.
 
 
###### Description
 
 
This function is a control over the signature.
 
 
* in an `Output` of a transaction TX1, `SIG(PUBKEY_A)` requires from a future transaction TX2 unlocking the output to give as parameter a valid signature of TX2 by `PUBKEY_A`
 
* if TX2 does not give `SIG(INDEX)` parameter as [matching parameter](#condition-matching), the condition fails
 
* if TX2's `Issuers[INDEX]` does not equal `PUBKEY_A`, the condition fails
 
* if TX2's `SIG(INDEX)` does not return TRUE, the condition fails
 
* in an `Unlock` of TX2, `SIG(INDEX)` returns TRUE if `Signatures[INDEX]` is a valid signature of TX2 against `Issuers[INDEX]`
 
 
So if we have, in TX1:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
[...]
 
Outputs
 
25:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
```
 
 
Then the `25` units can be spent *exclusively* in a future transaction TX2 which looks like:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
[...]
 
Issuers:
 
BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g
 
Inputs:
 
25:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0
 
Unlocks:
 
0:SIG(0)
 
```
 
 
Where:
 
 
* `SIG(0)` refers to the signature of `Issuers[0]`, here `BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g`
 
* `6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3` is the hash of TX1.
 
 
The necessary condition `SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)` is matched here if **both**:
 
 
* the sufficient proof `SIG(0)` is a valid signature of TX2 against `Issuers[0]` public key
 
* `Issuers[0] = BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g`
 
 
 
##### XHX function
 
 
###### Definition
 
 
Lock:
 
 
XHX(HASH)
 
 
Unlock:
 
 
XHX(PASSWORD)
 
 
###### Condition
 
 
`XHX(HASH)` returns true if it exists a parameter `XHX(PASSWORD)` where `SHA256(PASSWORD) = HASH`.
 
 
`XHX(PASSWORD)` returns true if it exists a locking function `XHX(HASH)` where `SHA256(PASSWORD) = HASH` in the conditions.
 
 
###### Description
 
 
This function is a password control.
 
 
So if we have, in TX1:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
[...]
 
Outputs
 
25:2:XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)
 
```
 
 
Then the `25` units can be spent *exclusively* in a future transaction TX2 which looks like:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
[...]
 
Issuers:
 
BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g
 
Inputs:
 
55:1:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0
 
Unlocks:
 
0:XHX(1872767826647264)
 
```
 
 
Where:
 
 
* `6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3` is the hash of TX1.
 
 
Then `XHX(PASSWORD)` returns TRUE if it exists `XHX(HASH)` in the output's conditions of TX1, with the relation `SHA256(PASSWORD) = HASH`.
 
 
Here `XHX(1872767826647264)` returns TRUE if it exists `XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)` in the output's conditions of TX1, as it matches the link `SHA256(1872767826647264) = 8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB`.
 
 
#### Example 1
 
 
Key `HsLShA` sending 25 coins to key `BYfWYF` using 1 source transaction written in block #3.
 
 
```yml
 
Version: 10
 
Type: Transaction
 
Currency: beta_brousouf
 
Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
Locktime: 0
 
Issuers:
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
Inputs:
 
30:0:T:8361C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:3
 
Unlocks:
 
0:SIG(0)
 
Outputs:
 
25:0:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
5:0:SIG(HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY)
 
Comment: First transaction
 
```
 
 
Signatures (fake here):
 
 
42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 
 
#### Example 2
 
 
Key `HsLShA` sending 30 coins (base 2) to key `BYfWYF` using 2 sources transaction written in blocks #65 and #77 + 1 UD from block #88.
 
 
```yml
 
Version: 10
 
Type: Transaction
 
Currency: beta_brousouf
 
Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
Locktime: 0
 
Issuers:
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
Inputs:
 
6:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0
 
20:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:10
 
40:1:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:88
 
Unlocks:
 
0:SIG(0)
 
1:SIG(0)
 
2:SIG(0)
 
Outputs:
 
30:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
Comment:
 
```
 
 
Signatures (fake here):
 
 
42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 
 
#### Example 3
 
 
Key `HsLShA`, `CYYjHs` and `9WYHTa` sending 235 coins to key `BYfWYF` using 4 sources transaction + 2 UD from same block #46.
 
 
```yml
 
Version: 10
 
Type: Transaction
 
Currency: beta_brousouf
 
Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
Locktime: 0
 
Issuers:
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
 
9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB
 
Inputs:
 
40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
 
70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
 
20:2:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:46
 
70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
 
20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
 
15:2:D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
 
Unlocks:
 
0:SIG(0)
 
1:XHX(7665798292)
 
2:SIG(0)
 
3:SIG(0) SIG(2)
 
4:SIG(0) SIG(1) SIG(2)
 
5:SIG(2)
 
Outputs:
 
120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
 
49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
 
Comment: -----@@@----- (why not this comment?)
 
```
 
 
Signatures (fakes here):
 
 
42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 
2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
 
2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 
 
##### CLTV function
 
 
###### Definition
 
 
Lock:
 
 
CLVT(TIMESTAMP)
 
 
Unlock: no unlocking function.
 
 
###### Condition
 
 
`CLVT(TIMESTAMP)` returns true if and only if the transaction including block's `MedianTime >= TIMESTAMP`.
 
 
###### Description
 
 
This function locks an output in the future, which will be unlocked at a given date.
 
 
So if we have, in TX1:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
[...]
 
Outputs
 
25:2:CLTV(1489677041)
 
```
 
 
So here, the `25` units can be spent *exclusively* in a block whose `MedianTime >= 1489677041`
 
 
`CLTV`'s parameter must be an integer with a length between `1` and `10` chars.
 
 
##### CSV function
 
 
###### Definition
 
 
Lock:
 
 
CSV(DELAY)
 
 
Unlock: no unlocking function.
 
 
We define `TxTime` as the `MedianTime` of the block referenced by the transaction's `Blockstamp` field.
 
 
###### Condition
 
 
`CSV(DELAY)` returns true if and only if the transaction including block's `MedianTime - TxTime >= DELAY`.
 
 
###### Description
 
 
This function locks an output in the future, which will be unlocked after the given amount of time has elapsed.
 
 
So if we have, in TX1:
 
 
```yml
 
Version: 10
 
Type: Transaction
 
Currency: beta_brousouf
 
Blockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
[...]
 
Outputs
 
25:2:CSV(3600)
 
```
 
 
Then the `25` units can be spent *exclusively* in a block whose `MedianTime - TxTime >= 3600`.
 
 
`CSV`'s parameter must be an integer with a length between `1` and `8` chars.
 
 
#### Compact format
 
 
A transaction may be described with a more compact format, to be used in a [Block](#block) document. The general format is:
 
 
```yml
 
TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_UNLOCKS:NB_OUTPUTS:HAS_COMMENT:LOCKTIME
 
BLOCKSTAMP
 
PUBLIC_KEY
 
...
 
INPUT
 
...
 
UNLOCK
 
...
 
OUTPUT
 
...
 
COMMENT
 
SIGNATURE
 
...
 
```
 
 
Here is an example compacting [example 2](#example-2) from above:
 
 
```yml
 
TX:10:1:3:1:0:0
 
204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
6:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0
 
20:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:10
 
40:1:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:88
 
0:SIG(0)
 
1:SIG(0)
 
2:SIG(0)
 
30:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 
```
 
 
Here is an example compacting [example 3](#example-3) from above:
 
 
```yml
 
TX:10:3:6:3:1:0
 
204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B
 
HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
 
9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB
 
40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
 
70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
 
20:2D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:46
 
70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
 
20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
 
15:2:D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
 
0:SIG(0)
 
1:XHX(7665798292)
 
2:SIG(0)
 
3:SIG(0) SIG(2)
 
4:SIG(0) SIG(1) SIG(2)
 
5:SIG(2)
 
120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 
146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
 
49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
 
-----@@@----- (why not this comment?)
 
42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 
2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
 
2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 
```
 
 
### Block
 
 
A Block is a document gathering both:
 
 
* [Public key](#publickey) data in order to build a Web Of Trust (WoT) representation
 
* [Transaction](#transaction) data to identify money units & ownership
 
 
but also other informations like:
 
 
* time reference (calendar time)
 
* UD value for money issuance
 
 
#### Structure
 
 
```yml
 
Version: VERSION
 
Type: Block
 
Currency: CURRENCY
 
Number: BLOCK_ID
 
PoWMin: NUMBER_OF_ZEROS
 
Time: GENERATED_ON
 
MedianTime: MEDIAN_DATE
 
UniversalDividend: DIVIDEND_AMOUNT
 
UnitBase: UNIT_BASE
 
Issuer: ISSUER_KEY
 
IssuersFrame: ISSUERS_FRAME
 
IssuersFrameVar: ISSUERS_FRAME_VAR
 
DifferentIssuersCount: ISSUER_KEY
 
PreviousHash: PREVIOUS_HASH
 
PreviousIssuer: PREVIOUS_ISSUER_KEY
 
Parameters: PARAMETERS
 
MembersCount: WOT_MEM_COUNT
 
Identities:
 
PUBLIC_KEY:SIGNATURE:I_BLOCK_UID:USER_ID
 
...
 
Joiners:
 
PUBLIC_KEY:SIGNATURE:M_BLOCK_UID:I_BLOCK_UID:USER_ID
 
...
 
Actives:
 
PUBLIC_KEY:SIGNATURE:M_BLOCK_UID:I_BLOCK_UID:USER_ID
 
...
 
Leavers:
 
PUBLIC_KEY:SIGNATURE:M_BLOCK_UID:I_BLOCK_UID:USER_ID
 
...
 
Revoked:
 
PUBLIC_KEY:SIGNATURE
 
...
 
Excluded:
 
PUBLIC_KEY
 
...
 
Certifications:
 
PUBKEY_FROM:PUBKEY_TO:BLOCK_ID:SIGNATURE
 
...
 
Transactions:
 
COMPACT_TRANSACTION
 
...
 
InnerHash: BLOCK_HASH
 
Nonce: NONCE
 
BOTTOM_SIGNATURE
 
```
 
 
Field | Data | Mandatory?
 
--------------------- | ------------------------------------------------- | ------------
 
Version | The document version | Always
 
Type | The document type | Always
 
Currency | The currency name | Always
 
Number | The block number | Always
 
PoWMin | The current minimum PoW difficulty | Always
 
Time | Time of generation | Always
 
MedianTime | Median date | Always
 
UniversalDividend | Universal Dividend amount | **Optional**
 
UnitBase | Universal Dividend unit base (power of 10) | Always in V3, **Optional** in V2
 
Issuer | This block's issuer's public key | Always
 
IssuersFrame | The size of the frame to look for issuers | Always
 
IssuersFrameVar | A counter to increment/decrement IssuersFrame | Always
 
DifferentIssuersCount | The count of unique issuers of blocks | Always
 
PreviousHash | Previous block fingerprint (SHA256) | from Block#1
 
PreviousIssuer | Previous block issuer's public key | from Block#1
 
Parameters | Currency parameters. | **Block#0 only**
 
MembersCount | Number of members in the WoT, this block included | Always
 
Identities | New identities in the WoT | Always
 
Joiners | `IN` memberships | Always
 
Actives | `IN` memberships (renewal) | Always
 
Leavers | `OUT` memberships | Always
 
Revoked | Revocation documents | Always
 
Excluded | Exluded members' public key | Always
 
Transactions | A list of compact transactions | Always
 
InnerHash | The hash value of the block's inner content | Always
 
Nonce | An arbitrary nonce value | Always
 
BlockHash | Hash from `InnerHash: ` to `SIGNATURE` | Virtual
 
BlockSize | Hash from `InnerHash: ` to `SIGNATURE` | Virtual
 
 
##### BlockSize
 
 
The block size is defined as the number of lines in multiline fields (`Identities`, `Joiners`, `Actives`, `Leavers`, `Revoked`, `Certifications`, `Transactions`) **except** `Excluded` field.
 
 
For example:
 
 
* 1 new identity + 1 joiner + 2 certifications = 4 lines sized block
 
* 1 new identity + 1 joiner + 2 certifications + 5 lines transaction = 9 lines sized block
 
 
#### Coherence
 
 
To be valid, a block must match the following rules:
 
 
##### Format
 
 
* `Version`, `Nonce`, `Number`, `PoWMin`, `Time`, `MedianTime`, `MembersCount`, `UniversalDividend`, `UnitBase`, `IssuersFrame`, `IssuersFrameVar` and `DifferentIssuersCount` are integer values
 
* `Currency` is a valid currency name
 
* `PreviousHash` is an uppercased SHA256 hash
 
* `Issuer` and `PreviousIssuer` are [Public keys](#publickey)
 
* `Identities` is a multiline field composed for each line of:
 
* `PUBLIC_KEY` : a [Public key](#publickey)
 
* `SIGNATURE` : a [Signature](#signature)
 
* `BLOCK_UID` : a block UID
 
* `USER_ID` : an identifier
 
* `Joiners`, `Actives` and `Leavers` are multiline fields composed for each line of:
 
* `PUBLIC_KEY` : a [Public key](#publickey)
 
* `SIGNATURE` : a [Signature](#signature)
 
* `M_BLOCK_UID` : a block UID
 
* `I_BLOCK_UID` : a block UID
 
* `USER_ID` : an identifier
 
* `Revoked` is a multiline field composed for each line of:
 
* `SIGNATURE` : a [Signature](#signature)
 
* `USER_ID` : an identifier
 
* `Excluded` is a multiline field composed for each line of:
 
* `PUBLIC_KEY` : a [Public key](#publickey)
 
* `Certifications` is a multiline field composed for each line of:
 
* `PUBKEY_FROM` : a [Public key](#publickey) doing the certification
 
* `PUBKEY_TO` : a [Public key](#publickey) being certified
 
* `BLOCK_ID` : a positive integer
 
* `SIGNATURE` : a [Signature](#signature) of the certification
 
* `Transactions` is a multiline field composed of [compact transactions](#compact-format)
 
* `Parameters` is a simple line field, composed of 1 float, 12 integers and 1 last float all separated by a colon `:`, and representing [currency parameters](#protocol-parameters) (a.k.a Protocol parameters, but valued for a given currency):
 
 
c:dt:ud0:sigPeriod:sigStock:sigWindow:sigValidity:sigQty:idtyWindow:msWindow:xpercent:msValidity:stepMax:medianTimeBlocks:avgGenTime:dtDiffEval:percentRot:udTime0:udReevalTime0:dtReeval
 
 
The document must be ended with a `BOTTOM_SIGNATURE` [Signature](#signature).
 
 
##### Data
 
 
* `Version` equals `10` or `11` or `12`
 
* `Type` equals `Block`
 
 
### Peer
 
 
DUBP uses P2P networks to manage community and money data. Since only members can write to the Blockchain, it is important to have authenticated peers so newly validated blocks can be efficiently sent to them, without any ambiguity.
 
 
For that purpose, DUBP defines a peering table containing, for a given node's public key:
 
 
* a currency name
 
* a list of endpoints to contact the node
 
 
This link is made through a document called *Peer* whose format is described below.
 
 
#### Structure
 
 
```yml
 
Version: VERSION
 
Type: Peer
 
Currency: CURRENCY_NAME
 
Issuer: NODE_PUBLICKEY
 
Block: BLOCK
 
Endpoints:
 
END_POINT_1
 
END_POINT_2
 
END_POINT_3
 
[...]
 
```
 
 
With the signature attached, this document certifies that this public key is owned by this server at given network endpoints.
 
 
The aggregation of all *Peer* documents is called the *peering table*, and allows to authentify addresses of all nodes identified by their public keys.
 
 
#### Fields details
 
 
Field | Description
 
----- | -----------
 
`Version` | denotes the current structure version.
 
`Type` | the document type.
 
`Currency` | contains the name of the currency.
 
`Issuer` | the node's public key.
 
`Block` | block number and hash. Value is used to target a blockchain and precise time reference.
 
`Endpoints` | a list of endpoints to interact with the node
 
 
`Endpoints` has a particular structure. It is made up of at least one line, with each line following this format:
 
 
PROTOCOL_NAME[ OPTIONS]
 
[...]
 
 
For example, the first written Duniter peering protocol is BASIC_MERKLED_API, which defines an HTTP API. An endpoint of such protocol would look like:
 
 
BASIC_MERKLED_API[ DNS][ IPv4][ IPv6] PORT
 
 
Where :
 
 
Field | Description
 
----- | -----------
 
`DNS` | is the dns name to access the node.
 
`IPv4` | is the IPv4 address to access the node.
 
`IPv6` | is the IPv6 address to access the node.
 
`PORT` | is the port of the address to access the node.
 
 
#### Coherence
 
 
To be valid, a peer document must match the following rules:
 
 
##### Format
 
 
* `Version` equals `10`
 
* `Type` equals `Peer`
 
* `Currency` is a valid currency name
 
* `PublicKey` is a [Public key](#publickey)
 
* `Endpoints` is a multiline field
 
 
The document must be ended with a `BOTTOM_SIGNATURE` [Signature](#signature).
 
 
#### Example
 
 
```yml
 
Version: 10
 
Type: Peer
 
Currency: beta_brousouf
 
PublicKey: HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 
Block: 8-1922C324ABC4AF7EF7656734A31F5197888DDD52
 
Endpoints:
 
BASIC_MERKLED_API some.dns.name 88.77.66.55 2001:0db8:0000:85a3:0000:0000:ac1f 9001
 
BASIC_MERKLED_API some.dns.name 88.77.66.55 2001:0db8:0000:85a3:0000:0000:ac1f 9002
 
OTHER_PROTOCOL 88.77.66.55 9001
 
```
 
 
## Variables
 
 
### Protocol parameters
 
 
Parameter | Goal
 
----------- | ----
 
c | The %growth of the UD every `[dt]` period
 
dt | Time period between two UD.
 
dtReeval | Time period between two re-evaluation of the UD.
 
ud0 | UD(0), i.e. initial Universal Dividend
 
udTime0 | Time of first UD.
 
udReevalTime0 | Time of first reevaluation of the UD.
 
sigPeriod | Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero.
 
msPeriod | Minimum delay between 2 memberships of a same issuer, in seconds. Must be positive or zero.
 
sigReplay | Minimum delay between 2 certifications of a same issuer to a same receiver, in seconds. Equals to `msPeriod`.
 
sigStock | Maximum quantity of active certifications made by member.
 
sigWindow | Maximum delay a certification can wait before being expired for non-writing.
 
sigValidity | Maximum age of an active signature (in seconds)
 
sigQty | Minimum quantity of signatures to be part of the WoT
 
idtyWindow | Maximum delay an identity can wait before being expired for non-writing.
 
msWindow | Maximum delay a membership can wait before being expired for non-writing.
 
xpercent | Minimum percent of sentries to reach to match the distance rule
 
msValidity | Maximum age of an active membership (in seconds)
 
stepMax | Maximum distance between each WoT member and a newcomer
 
medianTimeBlocks | Number of blocks used for calculating median time.
 
avgGenTime | The average time for writing 1 block (wished time)
 
dtDiffEval | The number of blocks required to evaluate again `PoWMin` value
 
percentRot | The percent of previous issuers to reach for personalized difficulty
 
txWindow | `= 3600 * 24 * 7`. Maximum delay a transaction can wait before being expired for non-writing.
 
 
### Computed variables
 
 
Variable | Meaning
 
--------- | ----
 
members | Synonym of `members(t = now)`, `wot(t)`, `community(t)` targeting the keys whose last active (non-expired) membership is either in `Joiners` or `Actives`.
 
maxGenTime | `= CEIL(avgGenTime * 1.189)`
 
minGenTime | `= FLOOR(avgGenTime / 1.189)`
 
minSpeed | 1 / maxGenTime
 
maxSpeed | 1 / minGenTime
 
maxAcceleration | `= CEIL(maxGenTime * medianTimeBlocks)`
 
 
## Processing
 
 
### Block
 
 
A Block can be accepted only if it respects a set of rules, here divided in 2 parts : *local* and *global*.
 
 
#### Local validation
 
 
Local validation verifies the coherence of a well-formatted block, without any other context than the block itself.
 
 
##### Version
 
 
Rule:
 
 
HEAD.version == 10 || HEAD.version == 11
 
 
##### InnerHash
 
 
* `InnerHash` is the SHA256 hash of the whole fields from `Version:` to `\InnerHash`, the 'InnerHash' string being excluded from the hash computation.
 
 
##### Nonce
 
 
* `Nonce` value may be any zero or positive integer. This field is a special field allowing for document hash to change for proof-of-work computation.
 
 
##### Proof of work
 
 
To be valid, the BlockHash must start with a specific number of zeros. Locally, this hash must start with at least `NB_ZEROS` zeros:
 
 
REMAINDER = PowMin % 16
 
NB_ZEROS = (PowMin - REMAINDER) / 16
 
 
##### PreviousHash
 
 
* `PreviousHash` must be present if the value of the `Number` field is greater than `0`.
 
* `PreviousHash` must not be present if the value of the `Number` field is equal to `0`.
 
 
##### PreviousIssuer
 
 
* `PreviousIssuer` must be present if the value of the `Number` field is greater than `0`.
 
* `PreviousIssuer` must not be present if the value of the `Number` field is equal to `0`.
 
 
##### Parameters
 
 
* `Parameters` must be present if the value of the `Number` field is equal to `0`.
 
* `Parameters` must not be present if the value of the `Number` field is greater than `0`.
 
 
##### Universal Dividend
 
 
If HEAD.number == 0, HEAD.dividend must equal `null`.
 
 
##### UnitBase
 
 
* Block V2:
 
* If `UniversalDividend` field is present, `UnitBase` must be present too.
 
* Block V3:
 
* The field is always present.
 
* For root block, `UnitBase` must equal `0`.
 
 
##### Signature
 
 
* A block must have a valid signature over the content from `Hash: ` to `Nonce: NONCE\n`, where the associated public key is the block's `Issuer` field.
 
 
Please note: In Ğ1 and Ğ1-test networks, the V10 and V11 blocks and some transactions in these blocks are signed with a buggy version of TweetNaCl: [version 20131229](http://tweetnacl.cr.yp.to/software.html). As a result, some V10 and V11 blocks have an invalid signature (as well as some transactions in V10 or V11 blocks).
 
A complete list of invalid signatures in each currency will be compiled when the Ğ1 and Ğ1-test networks have successfully migrated to DUBP v12.
 
 
##### Dates
 
 
* A block must have its `Time` field be between [`MedianTime` ; `MedianTime` + `maxAcceleration`].
 
* Root block's `Time` & `MedianTime` must be equal.
 
 
##### Identities
 
 
A block cannot contain identities whose signature does not match the identity's content
 
 
##### Memberships (Joiners, Actives, Leavers)
 
 
A block cannot contain memberships whose signature does not match the membership's content
 
 
##### Revoked
 
 
A block cannot contain revocations whose signature does not match the revocation's content
 
 
##### Transactions
 
 
* A transaction in compact format cannot measure more than 100 lines
 
* A transaction must have at least 1 source
 
* A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count.
 
* A transaction **must** have signatures matching its content **for each issuer**
 
* Transaction's version must be equal to `10`.
 
* Signatures count must be the same as issuers count
 
* Signatures are ordered by issuer
 
* Signatures are made over the transaction's content, signatures excepted
 
 
Please note: In Ğ1 and Ğ1-test networks, some transactions in V10 and V11 blocks have an invalid signature. A complete list of invalid signatures in each currency will be compiled when the Ğ1 and Ğ1-test networks have successfully migrated to DUBP v12.
 
 
##### INDEX GENERATION
 
 
##### Identities
 
 
Each identity produces 2 new entries:
 
 
IINDEX (
 
op = 'CREATE'
 
uid = USER_ID
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
member = true
 
wasMember = true
 
kick = false
 
)
 
 
MINDEX (
 
op = 'CREATE'
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
expired_on = 0
 
expires_on = MedianTime + msValidity
 
revokes_on = MedianTime + msValidity*2
 
chainable_on = MedianTime + msPeriod
 
type = 'JOIN'
 
revoked_on = null
 
leaving = false
 
)
 
 
##### Joiners
 
 
Each join whose `PUBLIC_KEY` **does not match** a local MINDEX `CREATE, PUBLIC_KEY` produces 2 new entries:
 
 
IINDEX (
 
op = 'UPDATE'
 
uid = null
 
pub = PUBLIC_KEY
 
created_on = null
 
written_on = BLOCKSTAMP
 
member = true
 
wasMember = null
 
kick = null
 
)
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
expired_on = 0
 
expires_on = MedianTime + msValidity
 
revokes_on = MedianTime + msValidity*2
 
chainable_on = MedianTime + msPeriod
 
type = 'JOIN'
 
revoked_on = null
 
leaving = null
 
)
 
 
##### Actives
 
 
Each active produces 1 new entry:
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
expires_on = MedianTime + msValidity
 
revokes_on = MedianTime + msValidity*2
 
chainable_on = MedianTime + msPeriod
 
type = 'RENEW'
 
revoked_on = null
 
leaving = null
 
)
 
 
##### Leavers
 
 
Each leaver produces 1 new entry:
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
type = 'LEAVE'
 
expires_on = null
 
revokes_on = null
 
revoked_on = null
 
leaving = true
 
)
 
 
##### Revoked
 
 
Each revocation produces 1 new entry:
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = PUBLIC_KEY
 
created_on = BLOCK_UID
 
written_on = BLOCKSTAMP
 
type = 'REV'
 
expires_on = null
 
revokes_on = null
 
revoked_on = MedianTime
 
revocation = REVOCATION_SIG
 
leaving = false
 
)
 
 
##### Excluded
 
 
Each exclusion produces 1 new entry:
 
 
IINDEX (
 
op = 'UPDATE'
 
uid = null
 
pub = PUBLIC_KEY
 
created_on = null
 
written_on = BLOCKSTAMP
 
member = false
 
wasMember = null
 
kick = false
 
)
 
 
##### Certifications
 
 
Each certification produces 1 new entry:
 
 
CINDEX (
 
op = 'CREATE'
 
issuer = PUBKEY_FROM
 
receiver = PUBKEY_TO
 
created_on = BLOCK_ID
 
written_on = BLOCKSTAMP
 
sig = SIGNATURE
 
expires_on = MedianTime + sigValidity
 
chainable_on = MedianTime + sigPeriod
 
replayable_on = MedianTime + sigReplay
 
expired_on = 0
 
)
 
 
##### Sources
 
 
Each transaction input produces 1 new entry:
 
 
SINDEX (
 
op = 'UPDATE'
 
tx = TRANSACTION_HASH
 
identifier = INPUT_IDENTIFIER
 
pos = INPUT_INDEX
 
created_on = TX_BLOCKSTAMP
 
written_on = BLOCKSTAMP
 
amount = INPUT_AMOUNT
 
base = INPUT_BASE
 
conditions = null
 
consumed = true
 
)
 
 
Each transaction output produces 1 new entry:
 
 
SINDEX (
 
op = 'CREATE'
 
tx = TRANSACTION_HASH
 
identifier = TRANSACTION_HASH
 
pos = OUTPUT_INDEX_IN_TRANSACTION
 
written_on = BLOCKSTAMP
 
written_time = MedianTime
 
amount = OUTPUT_AMOUNT
 
base = OUTPUT_BASE
 
locktime = LOCKTIME
 
conditions = OUTPUT_CONDITIONS
 
consumed = false
 
)
 
 
##### INDEX RULES
 
 
###### UserID and PublicKey unicity
 
 
* The local IINDEX has a unicity constraint on `USER_ID`.
 
* The local IINDEX has a unicity constraint on `PUBLIC_KEY`.
 
* Each local IINDEX `op = 'CREATE'` operation must match a single local MINDEX `op = 'CREATE', pub = PUBLIC_KEY` operation.
 
 
> Functionally: UserID and public key must be unique in a block, an each new identity must have an opt-in document attached.
 
 
###### Membership unicity
 
 
* The local MINDEX has a unicity constraint on `PUBLIC_KEY`
 
 
> Functionally: a user has only 1 status change allowed per block.
 
 
###### Revocation implies exclusion
 
 
* Each local MINDEX ̀`op = 'UPDATE', revoked_on != null` operations must match a single local IINDEX `op = 'UPDATE', pub = PUBLIC_KEY, member = false` operation.
 
 
> Functionally: a revoked member must be immediately excluded.
 
 
###### Certifications
 
 
* The local CINDEX has a unicity constraint on `PUBKEY_FROM, PUBKEY_TO`
 
* The local CINDEX has a unicity constraint on `PUBKEY_FROM`, except for block#0
 
* The local CINDEX must not match a MINDEX operation on `PUBLIC_KEY = PUBKEY_FROM, member = false` or `PUBLIC_KEY = PUBKEY_FROM, leaving = true`
 
 
> Functionally:
 
>
 
> * a block cannot have 2 identical certifications (A -> B)
 
> * a block cannot have 2 certifications from a same public key, except in block#0
 
> * a block cannot have a certification to a leaver or an excluded
 
 
###### Sources
 
 
* The local SINDEX has a unicity constraint on `UPDATE, IDENTIFIER, POS`
 
* The local SINDEX has a unicity constraint on `CREATE, IDENTIFIER, POS`
 
 
> Functionally:
 
> * a same source cannot be consumed twice by the block
 
> * a same output cannot be produced twice by block
 
>
 
> But a source can be both created and consumed in the same block, so a *chain of transactions* can be stored at once.
 
 
##### Double-spending control
 
 
Definitions:
 
 
For each SINDEX unique `tx`:
 
 
* **inputs** are the SINDEX row matching `UPDATE, tx`
 
* **outputs** are the SINDEX row matching `CREATE, tx`
 
 
> Functionally: we gather the sources for each transaction, in order to check them.
 
 
###### CommonBase
 
 
Each input has an `InputBase`, and each output has an `OutputBase`. These bases are to be called `AmountBase`.
 
 
The `CommonBase` is the lowest base value among all `AmountBase` of the transaction.
 
 
For any amount comparison, the respective amounts must be translated into `CommonBase` using the following rule:
 
 
```
 
AMOUNT(CommonBase) = AMOUNT(AmountBase) x POW(10, AmountBase - CommonBase)
 
```
 
 
So if a transaction only carries amounts with the same `AmountBase`, no conversion is required. But if a transaction carries:
 
 
* input_0 of value 45 with `AmountBase = 5`
 
* input_1 of value 75 with `AmountBase = 5`
 
* input_2 of value 3 with `AmountBase = 6`
 
* output_0 of value 15 with `AmountBase = 6`
 
 
Then the output value has to be converted before being compared:
 
 
```
 
CommonBase = 5
 
 
output_0(5) = output_0(6) x POW(10, 6 - 5)
 
output_0(5) = output_0(6) x POW(10, 1)
 
output_0(5) = output_0(6) x 10
 
output_0(5) = 15 x 10
 
output_0(5) = 150
 
 
input_0(5) = input_0(5)
 
input_0(5) = 45
 
 
input_1(5) = input_1(5)
 
input_1(5) = 75
 
 
input_2(5) = input_2(6) x POW(10, 6 - 5)
 
input_2(5) = input_2(6) x POW(10, 1)
 
input_2(5) = input_2(6) x 10
 
input_2(5) = 3 x 10
 
input_2(5) = 30
 
```
 
 
The equality of inputs and outputs is then verified because:
 
 
```
 
output_0(5) = 150
 
input_0(5) = 45
 
input_1(5) = 75
 
input_2(5) = 30
 
 
output_0(5) = input_0(5) + input_1(5) + input_2(5)
 
150 = 45 + 75 + 30
 
TRUE
 
```
 
 
###### Amounts
 
 
* *Def.*: `InputBaseSum` is the sum of amounts with the same `InputBase`.
 
* *Def.*: `OutputBaseSum` is the sum of amounts with the same `OutputBase`.
 
* *Def.*: `BaseDelta = OutputBaseSum - InputBaseSum`, expressed in `CommonBase`
 
* *Rule*: For each `OutputBase`:
 
* if `BaseDelta > 0`, then it must be inferior or equal to the sum of all preceding `BaseDelta`
 
* *Rule*: The sum of all inputs in `CommonBase` must equal the sum of all outputs in `CommonBase`
 
 
> Functionally: we cannot create nor lose money through transactions. We can only transfer coins we own.
 
> Functionally: also, we cannot convert a superiod unit base into a lower one.
 
 
##### Transactions chaining max depth
 
 
FUNCTION `getTransactionDepth(txHash, LOCAL_DEPTH)`:
 
 
INPUTS = LOCAL_SINDEX[op='UPDATE',tx=txHash]
 
DEPTH = LOCAL_DEPTH
 
 
FOR EACH `INPUT` OF `INPUTS`
 
CONSUMED = LOCAL_SINDEX[op='CREATE',identifier=INPUT.identifier,pos=INPUT.pos]
 
IF (CONSUMED != NULL)
 
IF (LOCAL_DEPTH < 5)
 
DEPTH = MAX(DEPTH, getTransactionDepth(CONSUMED.tx, LOCAL_DEPTH +1)
 
ELSE
 
DEPTH++
 
END_IF
 
END_IF
 
END_FOR
 
 
RETURN DEPTH
 
 
END_FUNCTION
 
 
Then:
 
 
maxTxChainingDepth = 0
 
 
For each `TX_HASH` of `UNIQ(PICK(LOCAL_SINDEX, 'tx))`:
 
 
maxTxChainingDepth = MAX(maxTxChainingDepth, getTransactionDepth(TX_HASH, 0))
 
 
Rule:
 
 
maxTxChainingDepth <= 5
 
 
#### Global validation
 
 
Global validation verifies the coherence of a locally-validated block, in the context of the whole blockchain, including the block.
 
 
##### INDEX GENERATION
 
 
###### Definitions
 
 
Here are some references that will be used in next sections.
 
 
BINDEX references:
 
 
* *HEAD*: the BINDEX top entry (generated for incoming block, precisely)
 
* *HEAD~1*: the BINDEX 1<sup>st</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - 1`)
 
* *HEAD~n*: the BINDEX n<sup>th</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - n`)
 
* *HEAD~n[field=value, ...]*: the BINDEX entry at *HEAD~n* if it fulfills the condition, null otherwise
 
* *HEAD~n..m[field=value, ...]*: the BINDEX entries between *HEAD~n* and *HEAD~m*, included, where each entry fulfills the condition
 
* *HEAD~n.property*: get a BINDEX entry property. Ex.: `HEAD~1.hash` looks at the hash of the entry preceding HEAD.
 
* *(HEAD~n..m).property*: get all the values of *property* in BINDEX for entries between *HEAD~n* and *HEAD~m*, included.
 
* *(HEAD~<n>..<m>).property*: same, but <n> and <m> are variables to be computed
 
 
Function references:
 
 
* *COUNT* returns the number of values in a list of values
 
* *AVG* computes the average value in a list of values, floor rounded.
 
* *MEDIAN* computes the median value in a list of values
 
* *MAX* computes the maximum value in a list of values
 
 
> If values count is even, the median is computed over the 2 centered values by an arithmetical median on them, *NOT* rounded.
 
 
* *UNIQ* returns a list of the unique values in a list of values
 
* *PICK* returns a list of the values by picking a particular property on each record
 
* *INTEGER_PART* return the integer part of a number
 
* *FIRST* return the first element in a list of values matching the given condition
 
* *REDUCE* merges a set of elements into a single one, by extending the non-null properties from each record into the resulting record.
 
* *REDUCE_BY* merges a set of elements into a new set, where each new element is the reduction of the first set sharing a given key.
 
* *CONCAT* concatenates two sets of elements into a new one
 
 
> If there is no elements, all its properties are `null`.
 
 
* *NUMBER* get the number part of blockstamp
 
* *HASH* get the hash part of blockstamp
 
 
###### HEAD
 
 
The block produces 1 new entry:
 
 
BINDEX (
 
version = Version
 
size = BlockSize
 
hash = BlockHash
 
issuer = Issuer
 
time = Time
 
number = null
 
currency = null
 
previousHash = null
 
previousIssuer = null
 
membersCount = null
 
issuersCount = null
 
issuersFrame = null
 
issuersFrameVar = null
 
issuerDiff = null
 
avgBlockSize = null
 
medianTime = null
 
dividend = null
 
mass = null
 
massReeval = null
 
unitBase = null
 
powMin = PowMin
 
udTime = null
 
diffTime = null
 
speed = null
 
)
 
 
This entry will be refered to as *HEAD* in the following sections, and is added on the top of the BINDEX.
 
 
###### BR_G01 - HEAD.number
 
 
If HEAD~1 is defined:
 
 
HEAD.number = HEAD~1.number + 1
 
 
Else:
 
 
HEAD.number = 0
 
 
###### BR_G02 - HEAD.previousHash
 
 
If `HEAD.number > 0`:
 
 
HEAD.previousHash = HEAD~1.hash
 
 
Else:
 
 
HEAD.previousHash = null
 
 
###### BR_G99 - HEAD.currency
 
 
If `HEAD.number > 0`:
 
 
HEAD.currency = HEAD~1.currency
 
 
Else:
 
 
HEAD.currency = null
 
 
###### BR_G03 - HEAD.previousIssuer
 
 
If `HEAD.number > 0`:
 
 
HEAD.previousIssuer = HEAD~1.issuer
 
 
Else:
 
 
HEAD.previousIssuer = null
 
 
###### BR_G100 - HEAD.issuerIsMember
 
 
If `HEAD.number > 0`:
 
 
HEAD.issuerIsMember = REDUCE(GLOBAL_IINDEX[pub=HEAD.issuer]).member
 
 
Else:
 
 
HEAD.issuerIsMember = REDUCE(LOCAL_IINDEX[pub=HEAD.issuer]).member
 
 
###### BR_G04 - HEAD.issuersCount
 
 
If `HEAD.number == 0`:
 
 
HEAD.issuersCount = 0
 
 
Else:
 
 
HEAD.issuersCount = COUNT(UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer))
 
 
###### BR_G05 - HEAD.issuersFrame
 
 
If `HEAD.number == 0`:
 
 
HEAD.issuersFrame = 1
 
 
Else if `HEAD~1.issuersFrameVar > 0`:
 
 
HEAD.issuersFrame = HEAD~1.issuersFrame + 1
 
 
Else if `HEAD~1.issuersFrameVar < 0`:
 
 
HEAD.issuersFrame = HEAD~1.issuersFrame - 1
 
 
Else:
 
 
HEAD.issuersFrame = HEAD~1.issuersFrame
 
 
###### BR_G06 - HEAD.issuersFrameVar
 
 
If `HEAD.number == 0`:
 
 
HEAD.issuersFrameVar = 0
 
 
Else if `HEAD~1.issuersFrameVar > 0`:
 
 
HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) - 1
 
 
Else if `HEAD~1.issuersFrameVar < 0`:
 
 
HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) + 1
 
 
Else:
 
 
HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount)
 
 
 
###### BR_G07 - HEAD.avgBlockSize
 
 
HEAD.avgBlockSize = AVG((HEAD~1..<HEAD.issuersCount>).size)
 
 
###### BR_G08 - HEAD.medianTime
 
 
If `HEAD.number > 0`:
 
 
HEAD.medianTime = MEDIAN((HEAD~1..<MIN(medianTimeBlocks, HEAD.number)>).time)
 
 
Else:
 
 
HEAD.medianTime = HEAD.time
 
 
###### BR_G09 - HEAD.diffNumber
 
 
If `HEAD.number == 0`:
 
 
HEAD.diffNumber = HEAD.number + dtDiffEval
 
 
Else if `HEAD~1.diffNumber <= HEAD.number`:
 
 
HEAD.diffNumber = HEAD~1.diffNumber + dtDiffEval
 
 
Else:
 
 
HEAD.diffNumber = HEAD~1.diffNumber
 
 
###### BR_G10 - HEAD.membersCount
 
 
If `HEAD.number == 0`:
 
 
HEAD.membersCount = COUNT(LOCAL_IINDEX[member=true])
 
 
Else:
 
 
HEAD.membersCount = HEAD~1.membersCount + COUNT(LOCAL_IINDEX[member=true]) - COUNT(LOCAL_IINDEX[member=false])
 
 
###### BR_G11 - HEAD.udTime and HEAD.udReevalTime
 
 
If `HEAD.number == 0`:
 
 
HEAD.udTime = udTime0
 
 
Else if `HEAD~1.udTime <= HEAD.medianTime`:
 
 
HEAD.udTime = HEAD~1.udTime + dt
 
 
Else:
 
 
HEAD.udTime = HEAD~1.udTime
 
 
EndIf
 
 
If `HEAD.number == 0`:
 
 
HEAD.udReevalTime = udReevalTime0
 
 
Else if `HEAD~1.udReevalTime <= HEAD.medianTime`:
 
 
HEAD.udReevalTime = HEAD~1.udReevalTime + dtReeval
 
 
Else:
 
 
HEAD.udReevalTime = HEAD~1.udReevalTime
 
 
EndIf
 
 
###### BR_G12 - HEAD.unitBase
 
 
If `HEAD.number == 0`:
 
 
HEAD.unitBase = 0
 
 
Else:
 
 
HEAD.unitBase = HEAD~1.unitBase
 
 
###### BR_G13 - HEAD.dividend and HEAD.new_dividend
 
 
If `HEAD.number == 0`:
 
 
HEAD.dividend = ud0
 
 
Else If `HEAD.udReevalTime != HEAD~1.udReevalTime`:
 
 
HEAD.dividend = CEIL(HEAD_1.dividend + c² * CEIL(HEAD~1.massReeval / POW(10, HEAD~1.unitbase)) / HEAD.membersCount)
 
 
Else:
 
 
HEAD.dividend = HEAD~1.dividend
 
 
EndIf
 
 
If `HEAD.number == 0`:
 
 
HEAD.new_dividend = null
 
 
Else If `HEAD.udTime != HEAD~1.udTime`:
 
 
HEAD.new_dividend = HEAD.dividend
 
 
Else:
 
 
HEAD.new_dividend = null
 
 
###### BR_G14 - HEAD.dividend and HEAD.unitbase and HEAD.new_dividend
 
 
If `HEAD.dividend >= 1000000` :
 
 
HEAD.dividend = CEIL(HEAD.dividend / 10)
 
HEAD.new_dividend = HEAD.dividend
 
HEAD.unitBase = HEAD.unitBase + 1
 
 
###### BR_G15 - HEAD.mass and HEAD.massReeval
 
 
If `HEAD.number == 0`:
 
 
HEAD.mass = 0
 
 
Else if `HEAD.udTime != HEAD~1.udTime`:
 
 
HEAD.mass = HEAD~1.mass + HEAD.dividend * POWER(10, HEAD.unitBase) * HEAD.membersCount
 
 
Else:
 
 
HEAD.mass = HEAD~1.mass
 
 
EndIf
 
 
If `HEAD.number == 0`:
 
 
HEAD.massReeval = 0
 
 
Else if `HEAD.udReevalTime != HEAD~1.udReevalTime`:
 
 
HEAD.massReeval = HEAD~1.mass
 
 
Else:
 
 
HEAD.massReeval = HEAD~1.massReeval
 
 
EndIf
 
 
> Functionnally: the UD is reevaluated on the preceding monetary mass (important!)
 
 
###### BR_G16 - HEAD.speed
 
 
If `HEAD.number == 0`:
 
 
speed = 0
 
 
Else:
 
 
range = MIN(dtDiffEval, HEAD.number)
 
elapsed = (HEAD.medianTime - HEAD~<range>.medianTime)
 
 
EndIf
 
 
If `elapsed == 0`:
 
 
speed = 100
 
 
Else:
 
speed = range / elapsed
 
 
###### BR_G17 - HEAD.powMin
 
 
If `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed AND (HEAD~1.powMin + 2) % 16 == 0`:
 
 
HEAD.powMin = HEAD~1.powMin + 2
 
 
Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed`:
 
 
HEAD.powMin = HEAD~1.powMin + 1
 
 
Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed AND (HEAD~1.powMin) % 16 == 0`:
 
 
HEAD.powMin = MAX(0, HEAD~1.powMin - 2)
 
 
Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed`:
 
 
HEAD.powMin = MAX(0, HEAD~1.powMin - 1)
 
 
Else if `HEAD.number > 0`:
 
 
HEAD.powMin = HEAD~1.powMin
 
 
###### BR_G18 - HEAD.powZeros and HEAD.powRemainder
 
 
If `HEAD.number == 0`:
 
 
nbPersonalBlocksInFrame = 0
 
medianOfBlocksInFrame = 1
 
 
Else:
 
 
blocksOfIssuer = HEAD~1..<HEAD~1.issuersFrame>[issuer=HEAD.issuer]
 
nbPersonalBlocksInFrame = COUNT(blocksOfIssuer)
 
blocksPerIssuerInFrame = MAP(
 
UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer)
 
=> ISSUER: COUNT(HEAD~1..<HEAD~1.issuersFrame>[issuer=ISSUER]))
 
medianOfBlocksInFrame = MEDIAN(blocksPerIssuerInFrame)
 
 
EndIf
 
 
If `nbPersonalBlocksInFrame == 0`:
 
 
nbPreviousIssuers = 0
 
nbBlocksSince = 0
 
 
Else:
 
 
last = FIRST(blocksOfIssuer)
 
nbPreviousIssuers = last.issuersCount
 
nbBlocksSince = HEAD~1.number - last.number
 
 
EndIf
 
 
PERSONAL_EXCESS = MAX(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1)
 
PERSONAL_HANDICAP = FLOOR(LN(1 + PERSONAL_EXCESS) / LN(1.189))
 
HEAD.issuerDiff = MAX [ HEAD.powMin ; HEAD.powMin * FLOOR (percentRot * nbPreviousIssuers / (1 + nbBlocksSince)) ] + PERSONAL_HANDICAP
 
 
If `(HEAD.issuerDiff + 1) % 16 == 0`:
 
 
HEAD.issuerDiff = HEAD.issuerDiff + 1
 
 
EndIf
 
 
Finally:
 
 
HEAD.powRemainder = HEAD.issuerDiff % 16
 
HEAD.powZeros = (HEAD.issuerDiff - HEAD.powRemainder) / 16
 
 
###### Local IINDEX augmentation
 
 
####### BR_G19 - ENTRY.age
 
 
For each ENTRY in local IINDEX where `op = 'CREATE'`:
 
 
REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
 
 
If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
 
 
ENTRY.age = 0
 
 
Else if `REF_BLOC != null`:
 
 
ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
 
 
Else:
 
 
ENTRY.age = conf.idtyWindow + 1
 
 
EndIf
 
 
###### BR_G20 - Identity UserID unicity
 
 
For each ENTRY in local IINDEX:
 
 
If `op = 'CREATE'`:
 
 
ENTRY.uidUnique = COUNT(GLOBAL_IINDEX[uid=ENTRY.uid) == 0
 
 
Else:
 
 
ENTRY.uidUnique = true
 
 
###### BR_G21 - Identity pubkey unicity
 
 
For each ENTRY in local IINDEX:
 
 
If `op = 'CREATE'`:
 
 
ENTRY.pubUnique = COUNT(GLOBAL_IINDEX[pub=ENTRY.pub) == 0
 
 
Else:
 
 
ENTRY.pubUnique = true
 
 
####### BR_G33 - ENTRY.excludedIsMember
 
 
For each ENTRY in local IINDEX where `member != false`:
 
 
ENTRY.excludedIsMember = true
 
 
For each ENTRY in local IINDEX where `member == false`:
 
 
ENTRY.excludedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
 
 
####### BR_G35 - ENTRY.isBeingKicked
 
 
For each ENTRY in local IINDEX where `member != false`:
 
 
ENTRY.isBeingKicked = false
 
 
For each ENTRY in local IINDEX where `member == false`:
 
 
ENTRY.isBeingKicked = true
 
 
####### BR_G36 - ENTRY.hasToBeExcluded
 
 
For each ENTRY in local IINDEX:
 
 
ENTRY.hasToBeExcluded = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).kick
 
 
###### Local MINDEX augmentation
 
 
####### BR_G22 - ENTRY.age
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
 
 
If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
 
 
ENTRY.age = 0
 
 
Else if `REF_BLOC != null`:
 
 
ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
 
 
Else:
 
 
ENTRY.age = conf.msWindow + 1
 
 
EndIf
 
 
####### BR_G23 - ENTRY.numberFollowing
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
created_on = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).created_on
 
 
If `created_on != null`:
 
 
ENTRY.numberFollowing = NUMBER(ENTRY.created_ON) > NUMBER(created_on)
 
 
Else:
 
 
ENTRY.numberFollowing = true
 
 
EndIf
 
 
For each ENTRY in local MINDEX where `revoked_on != null`:
 
 
ENTRY.numberFollowing = true
 
 
####### BR_G24 - ENTRY.distanceOK
 
 
For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`:
 
 
dSen = CEIL(HEAD.membersCount ^ (1 / stepMax))
 
 
GRAPH = SET(LOCAL_CINDEX, 'issuer', 'receiver') + SET(GLOBAL_CINDEX, 'issuer', 'receiver')
 
SENTRIES = SUBSET(GRAPH, dSen, 'issuer')
 
 
ENTRY.distanceOK = EXISTS_PATH(xpercent, SENTRIES, GRAPH, ENTRY.pub, stepMax)
 
 
> Functionally: checks if it exists, for at least `xpercent`% of the sentries, a path using GLOBAL_CINDEX + LOCAL_CINDEX leading to the key `PUBLIC_KEY` with a maximum count of `[stepMax]` hops.
 
 
For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`:
 
 
ENTRY.distanceOK = true
 
 
####### BR_G25 - ENTRY.onRevoked
 
 
For each ENTRY in local MINDEX:
 
 
ENTRY.onRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null
 
 
####### BR_G26 - ENTRY.joinsTwice
 
 
For each ENTRY in local MINDEX where `op = 'UPDATE', expired_on = 0`:
 
 
ENTRY.joinsTwice = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member == true
 
 
> This rule ensures that someone who is in the `Joiners` field isn't already a member.
 
 
####### BR_G27 - ENTRY.enoughCerts
 
 
For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`:
 
 
If `HEAD.version >= 12`
 
 
BLOCKCHAIN = REDUCE_BY(GLOBAL_CINDEX[receiver=ENTRY.pub], 'issuer')[expired_on=0]
 
INCOMING = LOCAL_CINDEX[receiver=ENTRY.pub]
 
UNIQUE_ISSUERS = UNIQ(CONCAT(
 
PICK(BLOCKCHAIN, 'issuer'),
 
PICK(INCOMING, 'issuer')))
 
ENTRY.enoughCerts = COUNT(UNIQUE_ISSUERS) >= sigQty
 
 
else
 
 
ENTRY.enoughCerts = COUNT(GLOBAL_CINDEX[receiver=ENTRY.pub,expired_on=0]) + COUNT(LOCAL_CINDEX[receiver=ENTRY.pub,expired_on=0]) >= sigQty
 
 
> Functionally: any member or newcomer needs `[sigQty]` certifications coming *to* him to be in the WoT
 
 
For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`:
 
 
ENTRY.enoughCerts = true
 
 
####### BR_G28 - ENTRY.leaverIsMember
 
 
For each ENTRY in local MINDEX where `type == 'LEAVE'`:
 
 
ENTRY.leaverIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
 
 
For each ENTRY in local MINDEX where `type != 'LEAVE'`:
 
 
ENTRY.leaverIsMember = true
 
 
####### BR_G29 - ENTRY.activeIsMember
 
 
For each ENTRY in local MINDEX where `type == 'ACTIVE'`:
 
 
ENTRY.activeIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
 
 
For each ENTRY in local MINDEX where `type != 'ACTIVE'`:
 
 
ENTRY.activeIsMember = true
 
 
####### BR_G30 - ENTRY.revokedIsMember
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
ENTRY.revokedIsMember = true
 
 
For each ENTRY in local MINDEX where `revoked_on != null`:
 
 
ENTRY.revokedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member
 
 
####### BR_G31 - ENTRY.alreadyRevoked
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
ENTRY.alreadyRevoked = false
 
 
For each ENTRY in local MINDEX where `revoked_on != null`:
 
 
ENTRY.alreadyRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null
 
 
####### BR_G32 - ENTRY.revocationSigOK
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
ENTRY.revocationSigOK = true
 
 
For each ENTRY in local MINDEX where `revoked_on != null`:
 
 
ENTRY.revocationSigOK = SIG_CHECK_REVOKE(REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]), ENTRY)
 
 
####### BR_G34 - ENTRY.isBeingRevoked
 
 
For each ENTRY in local MINDEX where `revoked_on == null`:
 
 
ENTRY.isBeingRevoked = false
 
 
For each ENTRY in local MINDEX where `revoked_on != null`:
 
 
ENTRY.isBeingRevoked = true
 
 
####### BR_G107 - ENTRY.unchainables
 
 
If `HEAD.number > 0 AND ENTRY.revocation == null`:
 
 
ENTRY.unchainables = COUNT(GLOBAL_MINDEX[issuer=ENTRY.issuer, chainable_on > HEAD~1.medianTime]))
 
 
###### Local CINDEX augmentation
 
 
For each ENTRY in local CINDEX:
 
 
####### BR_G37 - ENTRY.age
 
 
REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)]
 
 
If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
 
 
ENTRY.age = 0
 
 
Else if `REF_BLOC != null`:
 
 
ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
 
 
Else:
 
 
ENTRY.age = conf.sigWindow + 1
 
 
EndIf
 
 
####### BR_G38 - ENTRY.unchainables
 
 
If `HEAD.number > 0`:
 
 
ENTRY.unchainables = COUNT(GLOBAL_CINDEX[issuer=ENTRY.issuer, chainable_on > HEAD~1.medianTime]))
 
 
####### BR_G39 - ENTRY.stock
 
 
ENTRY.stock = COUNT(REDUCE_BY(GLOBAL_CINDEX[issuer=ENTRY.issuer], 'receiver', 'created_on')[expired_on=0])
 
 
####### BR_G40 - ENTRY.fromMember
 
 
ENTRY.fromMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.issuer]).member
 
 
####### BR_G41 - ENTRY.toMember
 
 
ENTRY.toMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]).member
 
 
####### BR_G42 - ENTRY.toNewcomer
 
 
ENTRY.toNewcomer = COUNT(LOCAL_IINDEX[member=true,pub=ENTRY.receiver]) > 0
 
 
####### BR_G43 - ENTRY.toLeaver
 
 
ENTRY.toLeaver = REDUCE(GLOBAL_MINDEX[pub=ENTRY.receiver]).leaving
 
 
####### BR_G44 - ENTRY.isReplay
 
 
reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0]
 
 
If `count(reducable) == 0`:
 
 
ENTRY.isReplay = false
 
 
Else:
 
 
ENTRY.isReplay = reduce(reducable).expired_on == 0
 
 
####### BR_G44.2 - ENTRY.isReplayable
 
 
If `HEAD.number > 0 && HEAD~1.version > 10` :
 
 
reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0]
 
 
If `count(reducable) == 0`:
 
 
ENTRY.isReplayable = true
 
 
Else:
 
 
ENTRY.isReplayable = reduce(reducable).replayable_on < HEAD~1.medianTime
 
Else:
 
 
ENTRY.isReplayable = false
 
 
EndIf
 
 
####### BR_G45 - ENTRY.sigOK
 
 
ENTRY.sigOK = SIG_CHECK_CERT(REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]), ENTRY)
 
 
###### Local SINDEX augmentation
 
 
####### BR_G102 - ENTRY.age
 
 
For each ENTRY in local SINDEX where `op = 'UPDATE'`:
 
 
REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.hash)>[hash=HASH(ENTRY.created_on)]
 
 
If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`:
 
 
ENTRY.age = 0
 
 
Else if `REF_BLOC != null`:
 
 
ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime
 
 
Else:
 
 
ENTRY.age = conf.txWindow + 1
 
 
EndIf
 
 
####### BR_G46 - ENTRY.available and ENTRY.conditions
 
 
For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
 
 
INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
If COUNT(INPUT_ENTRIES) == 0 Then
 
INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
EndIf
 
INPUT = REDUCE(INPUT_ENTRIES)
 
ENTRY.conditions = INPUT.conditions
 
ENTRY.available = INPUT.consumed == false
 
 
####### BR_G47 - ENTRY.isLocked
 
 
INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
If COUNT(INPUT_ENTRIES) == 0 Then
 
INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
EndIf
 
INPUT = REDUCE(INPUT_ENTRIES)
 
ENTRY.isLocked = TX_SOURCE_UNLOCK(INPUT.conditions, ENTRY)
 
 
####### BR_G48 - ENTRY.isTimeLocked
 
 
INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
If COUNT(INPUT_ENTRIES) == 0 Then
 
INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
 
EndIf
 
INPUT = REDUCE(INPUT_ENTRIES)
 
ENTRY.isTimeLocked = ENTRY.written_time - INPUT.written_time < ENTRY.locktime
 
 
##### Rules
 
 
Each rule returns true by default, unless **at least one test returns `false`**.
 
 
###### BR_G49 - Version
 
 
Rule:
 
 
If `HEAD.number > 0`:
 
 
HEAD.version == (HEAD~1.version OR HEAD~1.version + 1)
 
 
###### BR_G50 - Block size
 
 
Rule:
 
 
If `HEAD.number > 0`:
 
 
HEAD.size < MAX(500 ; CEIL(1.10 * HEAD.avgBlockSize))
 
 
###### BR_G98 - Currency
 
 
Rule:
 
 
If `HEAD.number > 0`:
 
 
Currency = HEAD.currency
 
 
###### BR_G51 - Number
 
 
Rule:
 
 
Number = HEAD.number
 
 
###### BR_G52 - PreviousHash
 
 
Rule:
 
 
PreviousHash = HEAD.previousHash
 
 
###### BR_G53 - PreviousIssuer
 
 
Rule:
 
 
PreviousHash = HEAD.previousHash
 
 
###### BR_G101 - Issuer
 
 
Rule:
 
 
HEAD.issuerIsMember == true
 
 
###### BR_G54 - DifferentIssuersCount
 
 
Rule:
 
 
DifferentIssuersCount = HEAD.issuersCount
 
 
###### BR_G55 - IssuersFrame
 
 
Rule:
 
 
IssuersFrame = HEAD.issuersFrame
 
 
###### BR_G56 - IssuersFrameVar
 
 
Rule:
 
 
IssuersFrameVar = HEAD.issuersFrameVar
 
 
###### BR_G57 - MedianTime
 
 
Rule:
 
 
MedianTime = HEAD.medianTime
 
 
###### BR_G58 - UniversalDividend
 
 
Rule:
 
 
UniversalDividend = HEAD.new_dividend
 
 
###### BR_G59 - UnitBase
 
 
Rule:
 
 
UnitBase = HEAD.unitBase
 
 
###### BR_G60 - MembersCount
 
 
Rule:
 
 
MembersCount = HEAD.membersCount
 
 
###### BR_G61 - PowMin
 
 
Rule:
 
 
If `HEAD.number > 0`:
 
 
PowMin = HEAD.powMin
 
 
###### BR_G62 - Proof-of-work
 
 
Rule: the proof is considered valid if:
 
 
* `HEAD.hash` starts with at least `HEAD.powZeros` zeros
 
* `HEAD.hash`'s `HEAD.powZeros + 1`th character is:
 
* between `[0-F]` if `HEAD.powRemainder = 0`
 
* between `[0-E]` if `HEAD.powRemainder = 1`
 
* between `[0-D]` if `HEAD.powRemainder = 2`
 
* between `[0-C]` if `HEAD.powRemainder = 3`
 
* between `[0-B]` if `HEAD.powRemainder = 4`
 
* between `[0-A]` if `HEAD.powRemainder = 5`
 
* between `[0-9]` if `HEAD.powRemainder = 6`
 
* between `[0-8]` if `HEAD.powRemainder = 7`
 
* between `[0-7]` if `HEAD.powRemainder = 8`
 
* between `[0-6]` if `HEAD.powRemainder = 9`
 
* between `[0-5]` if `HEAD.powRemainder = 10`
 
* between `[0-4]` if `HEAD.powRemainder = 11`
 
* between `[0-3]` if `HEAD.powRemainder = 12`
 
* between `[0-2]` if `HEAD.powRemainder = 13`
 
* between `[0-1]` if `HEAD.powRemainder = 14`
 
 
> N.B.: it is not possible to have HEAD.powRemainder = 15
 
 
###### BR_G63 - Identity writability
 
 
Rule:
 
 
ENTRY.age <= [idtyWindow]
 
 
###### BR_G64 - Membership writability
 
 
Rule:
 
 
ENTRY.age <= [msWindow]
 
 
###### BR_G108 - Membership period
 
 
Rule:
 
 
ENTRY.unchainables == 0
 
 
###### BR_G65 - Certification writability
 
 
Rule:
 
 
ENTRY.age <= [sigWindow]
 
 
###### BR_G66 - Certification stock
 
 
Rule:
 
 
ENTRY.stock <= sigStock
 
 
###### BR_G67 - Certification period
 
 
Rule:
 
 
ENTRY.unchainables == 0
 
 
###### BR_G68 - Certification from member
 
 
Rule:
 
 
If `HEAD.number > 0`:
 
 
ENTRY.fromMember == true
 
 
###### BR_G69 - Certification to member or newcomer
 
 
Rule:
 
 
ENTRY.toMember == true
 
OR
 
ENTRY.toNewcomer == true
 
 
###### BR_G70 - Certification to non-leaver
 
 
Rule:
 
 
ENTRY.toLeaver == false
 
 
###### BR_G71 - Certification replay
 
 
Rule:
 
 
ENTRY.isReplay == false || ENTRY.isReplayable == true
 
 
###### BR_G72 - Certification signature
 
 
Rule:
 
 
ENTRY.sigOK == true
 
 
###### BR_G73 - Identity UserID unicity
 
 
Rule:
 
 
ENTRY.uidUnique == true
 
 
###### BR_G74 - Identity pubkey unicity
 
 
Rule:
 
 
ENTRY.pubUnique == true
 
 
###### BR_G75 - Membership succession
 
 
Rule:
 
 
ENTRY.numberFollowing == true
 
 
###### BR_G76 - Membership distance check
 
 
Rule:
 
 
ENTRY.distanceOK == true
 
 
###### BR_G77 - Membership on revoked
 
 
Rule:
 
 
ENTRY.onRevoked == false
 
 
###### BR_G78 - Membership joins twice
 
 
Rule:
 
 
ENTRY.joinsTwice == false
 
 
###### BR_G79 - Membership enough certifications
 
 
Rule:
 
 
ENTRY.enoughCerts == true
 
 
###### BR_G80 - Membership leaver
 
 
Rule:
 
 
ENTRY.leaverIsMember == true
 
 
###### BR_G81 - Membership active
 
 
Rule:
 
 
ENTRY.activeIsMember == true
 
 
###### BR_G82 - Revocation by a member
 
 
Rule:
 
 
ENTRY.revokedIsMember == true
 
 
###### BR_G83 - Revocation singleton
 
 
Rule:
 
 
ENTRY.alreadyRevoked == false
 
 
###### BR_G84 - Revocation signature
 
 
Rule:
 
 
ENTRY.revocationSigOK == true
 
 
###### BR_G85 - Excluded is a member
 
 
Rule:
 
 
ENTRY.excludedIsMember == true
 
 
###### BR_G86 - Excluded contains exclatly those to be kicked
 
 
Rule:
 
 
For each `REDUCE_BY(GLOBAL_IINDEX[kick=true], 'pub') as TO_KICK`:
 
 
REDUCED = REDUCE(GLOBAL_IINDEX[pub=TO_KICK.pub])
 
 
If `REDUCED.kick` then:
 
 
COUNT(LOCAL_MINDEX[pub=REDUCED.pub,isBeingKicked=true]) == 1
 
 
Rule:
 
 
For each `IINDEX[member=false] as ENTRY`:
 
 
ENTRY.hasToBeExcluded = true
 
 
###### BR_G103 - Transaction writability
 
 
Rule:
 
 
ENTRY.age <= [txWindow]
 
 
###### BR_G87 - Input is available
 
 
For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
 
 
Rule:
 
 
ENTRY.available == true
 
 
###### BR_G88 - Input is unlocked
 
 
For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
 
 
Rule:
 
 
ENTRY.isLocked == false
 
 
###### BR_G89 - Input is time unlocked
 
 
For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
 
 
Rule:
 
 
ENTRY.isTimeLocked == false
 
 
###### BR_G90 - Output base
 
 
For each `LOCAL_SINDEX[op='CREATE'] as ENTRY`:
 
 
Rule:
 
 
ENTRY.unitBase <= HEAD~1.unitBase
 
 
##### Post-rules INDEX augmentation
 
 
###### BR_G91 - Dividend
 
 
If `HEAD.new_dividend != null`:
 
 
For each `REDUCE_BY(GLOBAL_IINDEX[member=true], 'pub') as IDTY` then if `IDTY.member`, add a new LOCAL_SINDEX entry:
 
 
SINDEX (
 
op = 'CREATE'
 
identifier = IDTY.pub
 
pos = HEAD.number
 
written_on = BLOCKSTAMP
 
written_time = MedianTime
 
amount = HEAD.dividend
 
base = HEAD.unitBase
 
locktime = null
 
conditions = REQUIRE_SIG(MEMBER.pub)
 
consumed = false
 
)
 
 
For each `LOCAL_IINDEX[member=true] as IDTY` add a new LOCAL_SINDEX entry:
 
 
SINDEX (
 
op = 'CREATE'
 
identifier = IDTY.pub
 
pos = HEAD.number
 
written_on = BLOCKSTAMP
 
written_time = MedianTime
 
amount = HEAD.dividend
 
base = HEAD.unitBase
 
locktime = null
 
conditions = REQUIRE_SIG(MEMBER.pub)
 
consumed = false
 
)
 
 
###### BR_G106 - Low accounts
 
 
Set:
 
 
ACCOUNTS = UNIQ(GLOBAL_SINDEX, 'conditions')
 
 
For each `ACCOUNTS as ACCOUNT` then:
 
 
Set:
 
 
ALL_SOURCES = CONCAT(GLOBAL_SINDEX[conditions=ACCOUNT.conditions], LOCAL_SINDEX[conditions=ACCOUNT.conditions])
 
SOURCES = REDUCE_BY(ALL_SOURCES, 'identifier', 'pos')[consumed=false]
 
BALANCE = SUM(MAP(SOURCES => SRC: SRC.amount * POW(10, SRC.base)))
 
 
If `BALANCE < 100 * POW(10, HEAD.unitBase)`, then for each `SOURCES AS SRC` add a new LOCAL_SINDEX entry:
 
 
SINDEX (
 
op = 'UPDATE'
 
identifier = SRC.identifier
 
pos = SRC.pos
 
written_on = BLOCKSTAMP
 
written_time = MedianTime
 
consumed = true
 
)
 
 
###### BR_G92 - Certification expiry
 
 
For each `GLOBAL_CINDEX[expires_on<=HEAD.medianTime] as CERT`, add a new LOCAL_CINDEX entry:
 
 
If `reduce(GLOBAL_CINDEX[issuer=CERT.issuer,receiver=CERT.receiver,created_on=CERT.created_on]).expired_on == 0`:
 
 
CINDEX (
 
op = 'UPDATE'
 
issuer = CERT.issuer
 
receiver = CERT.receiver
 
created_on = CERT.created_on
 
expired_on = HEAD.medianTime
 
)
 
 
###### BR_G93 - Membership expiry
 
 
For each `REDUCE_BY(GLOBAL_MINDEX[expires_on<=HEAD.medianTime AND revokes_on>HEAD.medianTime], 'pub') as POTENTIAL` then consider `REDUCE(GLOBAL_MINDEX[pub=POTENTIAL.pub]) AS MS`.
 
 
If `(MS.expired_on == null OR MS.expired_on == 0) AND MS.expires_on > HEAD.medianTime`, add a new LOCAL_MINDEX entry:
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = MS.pub
 
written_on = BLOCKSTAMP
 
expired_on = HEAD.medianTime
 
)
 
 
###### BR_G94 - Exclusion by membership
 
 
For each `LOCAL_MINDEX[expired_on!=0] as MS`, add a new LOCAL_IINDEX entry:
 
 
IINDEX (
 
op = 'UPDATE'
 
pub = MS.pub
 
written_on = BLOCKSTAMP
 
kick = true
 
)
 
 
###### BR_G95 - Exclusion by certification
 
 
For each `LOCAL_CINDEX[expired_on!=0] as CERT`:
 
 
Set:
 
 
CURRENT_VALID_CERTS = REDUCE_BY(GLOBAL_CINDEX[receiver=CERT.receiver], 'issuer', 'receiver', 'created_on')[expired_on=0]
 
 
If `COUNT(CURRENT_VALID_CERTS) + COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on=0]) - COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on!=0]) < sigQty`, add a new LOCAL_IINDEX entry:
 
 
IINDEX (
 
op = 'UPDATE'
 
pub = CERT.receiver
 
written_on = BLOCKSTAMP
 
kick = true
 
)
 
 
###### BR_G96 - Implicit revocation
 
 
For each `GLOBAL_MINDEX[revokes_on<=HEAD.medianTime,revoked_on=null] as MS`:
 
 
REDUCED = REDUCE(GLOBAL_MINDEX[pub=MS.pub])
 
 
If `REDUCED.revokes_on<=HEAD.medianTime AND REDUCED.revoked_on==null`, add a new LOCAL_MINDEX entry:
 
 
MINDEX (
 
op = 'UPDATE'
 
pub = MS.pub
 
written_on = BLOCKSTAMP
 
revoked_on = HEAD.medianTime
 
)
 
 
###### BR_G104 - Membership expiry date correction
 
 
For each `LOCAL_MINDEX[type='JOIN'] as MS`:
 
 
MS.expires_on = MS.expires_on - MS.age
 
MS.revokes_on = MS.revokes_on - MS.age
 
 
For each `LOCAL_MINDEX[type='ACTIVE'] as MS`:
 
 
MS.expires_on = MS.expires_on - MS.age
 
MS.revokes_on = MS.revokes_on - MS.age
 
 
###### BR_G105 - Certification expiry date correction
 
 
For each `LOCAL_CINDEX as CERT`:
 
 
CERT.expires_on = CERT.expires_on - CERT.age
 
 
##### BR_G97 - Final INDEX operations
 
 
If all the rules [BR_G49 ; BR_G90] pass, then all the LOCAL INDEX values (IINDEX, MINDEX, CINDEX, SINDEX, BINDEX) have to be appended to the GLOBAL INDEX.
 
 
### Peer
 
 
#### Global validation
 
 
##### Block
 
 
* `Block` field must target an existing block in the blockchain, or target special block `0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855`.
 
 
#### Interpretation
 
 
* A Peer document SHOULD NOT be interpreted if its `Block` field is anterior to previously recorded Peer document for a same `PublicKey` key.
 
 
### Transactions
 
 
#### Local coherence
 
 
* A transaction must be signed by all of its `Issuers`
 
* Signatures are to be associated to each `Issuers` according to their apparition order
 
* A transaction must have at least 1 issuer
 
* A transaction must have at least 1 source
 
* A transaction must have at least 1 recipient
 
* A transaction must have at least 1 signature
 
* A transaction must have exactly the same number of signatures and issuers
 
* A transaction's indexes' (`Inputs` field) value must be less than or equal to the `Issuers` line count
 
* A transaction must have its issuers appear at least once in the `Inputs` field, where an issuer is linked to `INDEX` by its position in the `Issuers` field. First issuer is `INDEX = 0`.
 
* A transaction's `Inputs` amount sum must be equal to the `Ouputs` amount sum.
 
* A transaction cannot have two identical `Inputs`
 
 
## Implementations
 
 
### APIs
 
 
DUBP does not impose any particular APIs to deal with DUBP data.
 
For inter-node communication, see [DUniter Network Protocol (DUNP)](https://git.duniter.org/nodes/common/doc#duniter-network-protocol-dunp) protocol.
 
 
## References
 
 
* [Relative Theory of the Money](http://en.trm.creationmonetaire.info), the theoretical reference behind Universal Dividend
 
* [OpenUDC](http://www.openudc.org/), the inspiration project of Duniter
 
* [Bitcoin](https://github.com/bitcoin/bitcoin), the well known crypto-currency system
Loading