> This document is currently being written and thus it's not complete. Specifications may change in the future.
# Abstract
## Abstract
In the current Duniter protocol input and output conditions are stored in machine-readable BNF text format. This is good for human readability, but it requires parsing and takes place due to its textual format.
To lower blocksize and transactions weights, or to allow more complex conditions; it could be preferable to use a more simple binary format which need little or no parsing, close to memory and that can be converted back to text format. It can also allow to add new features without hard-forking the protocol.
1.[Parameter group format](#25-parameter-group-format)
1.[Examples](#3-examples)
1.[Simple signature](#31-simple-signature)
1.[2 out of 3 signatures (simple)](#32-2-out-of-3-signatures-simple)
1.[2 out of 3 signatures (merkelized + includes)](#33-2-out-of-3-signatures-merkelized-includes)
## 1. Basic script structure
### 1.1. Scripts as ASTs
An output script allow to define conditions on how funds can be spent. The most basic case is to lock funds with a given public key, and requiring a signature (made with the associated private key) to use them as input of a new transaction.
Since we define a *condition*, we can express it as a *boolean function*.
...
...
@@ -51,19 +55,20 @@ We want to have multiple ways of spending an output, such as multi-signatures or
For a classic 1-signature lock, an AST could look like :
```
```text
SIG
|
-------------
PublicKey Signature
```
-`SIG` would be an operator verifying the association between the signature and the public key.
-`PublicKey` is stored in the script.
-`Signature` is provided as an input when unlocking funds.
If we want a *1 out of 2 signatures*, we could have :
```
```txt
OR
|
----------------------------
...
...
@@ -77,7 +82,7 @@ If we want a *1 out of 2 signatures*, we could have :
For a *2 out of 3 signatures*, we could have :
```
```txt
GREAT_EQUAL
-----------
SUM 2
...
...
@@ -92,7 +97,7 @@ For a *2 out of 3 signatures*, we could have :
-`SUM` would be an operator computing the sum of its leaves.
-`GREAT_EQ` would be an operator comparing if the *left leaf* is *greater or equal* to the *right leaf*.
## 1.2. Signatures (`SIG`)
### 1.2. Signatures (`SIG`)
We want our system to cover as many cases as possible. Currently the Duniter protocol use only [Ed25519 signatures][ed25519], but it could in the future use othe schemes such as [Schnorr signatures][schnorr].
...
...
@@ -113,17 +118,17 @@ With these requirements we have :
- An operator capable of providing data of any length.
- An operator capable of fetching data outside of script.
## 1.3 Unlock inputs (`INPUT`)
### 1.3 Unlock inputs (`INPUT`)
When a user want to spend an output, it must provide inputs making the lock script returns true.
They are stored as list of pairs of indexes and ASTs.
A script can use an `INPUT(Index)` operator returning the input.
## 1.4. Avoid redudancies (`INCLUDE`)
### 1.4. Avoid redudancies (`INCLUDE`)
Some parts of a script may required in multiple places the same computed result. To avoid redudancies, we allow to provide an script with multiple ASTs. Each AST is executed in order and only the output of the last one is taken as the result. An AST can use the result of an above script with an `INCLUDE` operator. With this structure it's not possible to have recursion or cycles which avoid infinite loops and Turing-Completeness. All ASTs are grouped into a single structure (an **AST group** or a **script**).
It could be bad for privacy to expose a complete script with all of the possibilities of spending an output. With merkelized scripts, we allow the user to provide only the hash of unused branches which can be used to prove it's the same script while not providing its complete definition.
...
...
@@ -133,9 +138,9 @@ Because we are using groups, we can define the group hash as `GHASH(Group) = SHA
Then we can define a `EXEC_AST_HASH` operator taking the group and it's `GHASH`. It will first verify that the hash is correct, then execute it.
# 2. Binary format
## 2. Binary format
## 2.1. Document syntax
### 2.1. Document syntax
- Operators are written in **CAPS**.
- Arguments are written as `<Name:Type>`.
...
...
@@ -149,90 +154,108 @@ The script is invalid if operator arguments have wrong sizes.
> These rules only apply for this document and won't appear in the binary format. They just set rules to respect at script validation and execution.
## 2.2. Values
### 2.2. Values
All values are raw arrays of bytes. Their meaning depends of their usage and with which operators they'll be used.
There is an alias `B` for boolean values of size `1`. Value `0` means `False`, and any other value means `True`. Boolean operators however **must** return `0` or `1`. This allow to easily *sum* boolean results together.
## 2.3. Operators
### 2.3. Operators
We store an operator as a **1 byte** value representing its type :
### 2.3.1. Language operators (`[0x00;0x0f]`)
#### 2.3.1. Language operators (`[0x00;0x0f]`)
-`0x00` or `VALUE:Size <Size:1> <Value:Size>`
Return given `Value` of given `Size`.
`Value` must have a size of `Size` bytes or the script is unreadable and thus invalid.
-`0x01` or `INPUT:? <Index:1>`
Return an input parameters as an *AST*.
It this input don't exist, `Undefined` is returned.
If this signature don't exist, `Undefined` is returned.
-`0x03` or `INCLUDE:? <Index:1>`
Return the `VALUE` returned by another script.
-`0x04` or `AST_HASH:32 <Hash:32>`
Return `Undefined`.
It's hash is the provided `Hash`.
-`0x05` or `EXEC_AST_HASH:? <Hash:32> <Script:AST:1>`
Return script result `VALUE` is the hash is correct, `Undefined` otherwise.
-`0x06` or `RESIZE:Size <Size:1> <Value:AST:?>`
Resize the value by truncating or adding padding (`0x00`).
### 2.3.2. Document data (`[0x10;0x2f]`)
#### 2.3.2. Document data (`[0x10;0x2f]`)
-`0x10` or `FETCH_SIG:32 <Index:1>`
Return the signature (hash) from the document signatures. This hash will be used to retrieve the real signature by the `SIG` operator.
-`0x11` or `FETCH_LOCKTIME:8`
Return the timestamp of the locking transaction.
-`0x12` or `FETCH_UNLOCKTIME:8`
Return the timestamp of the (new) unlocking transaction.
### 2.3.3. Boolean operators (`[0x30;0x3f]`)
#### 2.3.3. Boolean operators (`[0x30;0x3f]`)
-`0x30` or `NOT:B <Script:AST:B>`
Return `False` if `Script` returns true, `True` otherwise.
-`0x31` or `NORM:B``<Script:AST:B>`
Return `False` if `Script` returns false, `True(1)` otherwise.
Allow to normalise any `True(...)` value to `True(1)`.
-`0x32` or `OR:B <Count:1> <Script0:AST:B> <Script1:AST:B> ...`
Return `True` if at least one child returns `True`, `False` otherwise.
`Undefined` values are considered `False`.
-`0x33` or `AND:B <Count:1> <Script0:AST:B> <Script1:AST:B> ...`
Return `False` if at least one child returns `False`, `True` otherwise.
-`0x34` or `XOR:B <Script0:AST:B> <Script1:AST:B>`
Return `True` if one but not both returns `True`.
### 2.3.4. Arithmetic operators (`[0x40;0x6f]`)
#### 2.3.4. Arithmetic operators (`[0x40;0x6f]`)
-`0x40` or `EQUAL:B <Script0:AST:?> <Script1:AST:?>`
`True` if values are equal, `False` otherwise.
-`0x41` or `NEQUAL:B <Script0:AST:?> <Script1:AST:?>`
`True` if values are not equal, `False` otherwise.
-`0x42` or `LESS:B <Script0:AST:?> <Script1:AST:?>`
`True` if `Script0` return value is less than `Script1` return value.
-`0x43` or `GREAT:B <Script0:AST:?> <Script1:AST:?>`
`True` if `Script0` return value is greater than `Script1` return value.
-`0x44` or `LESS_EQUAL:B <Script0:AST:?> <Script1:AST:?>`
`True` if `Script0` return value is less or equal than `Script1` return value.
-`0x45` or `GREAT_EQUAL:B <Script0:AST:?> <Script1:AST:?>`
`True` if `Script0` return value is greater or equal than `Script1` return value.
-`0x50` or `SUM:?1 <Count:1> <Script0:AST:?1> <Script1:AST:?1> ...`
Return the sum of given values and discard any overflow. If you don't want overflows, try to `RESIZE` values first. `Undefined` values are ignored.
### 2.3.5. Locks (`[0x70;0x9f]`)
#### 2.3.5. Locks (`[0x70;0x9f]`)
-`0x70` or `PASS:B <PasswordHash:32> <Password:AST:32>`
Return `True` if `PasswordHash = SHA256(Password)`.
`Password` must be 32 bytes long, so the original password should be hashed a first time to have `Password`, then a second time to have `PasswordHash`.
-`0x71` or `SIG:B <Cryptosystem:2> <PublicKeyHash:32> <SignatureHash:AST:32>`
Verify the signature of the transaction with the public key.
To avoid dynamic sizes, only the hash is provided and will allow to fetch the real signature.
## 2.4. AST group format
### 2.4. AST group format
An **AST group** is a list of ASTs of a fixed size.
```
```txt
3
<AST 0>
<AST 1>
...
...
@@ -245,11 +268,11 @@ The last AST must return a **1 byte** boolean value (`0` is `False`, `True` othe
**This is the structure storing the unlocking conditions of an ouput.**
## 2.5. Parameter group format
### 2.5. Parameter group format
A **parameter group** is a map of **AST groups** associated with their *own parameter group*.
```
```txt
3 (3 elements)
0 <Parameter group for 0> <Group 0>
1 <Parameter group for 1> <Group 1>
...
...
@@ -260,15 +283,15 @@ This structure allow groups to be simple values or complex merkelized scripts wi
> It can become very complicated, there are examples below.
# 3. Examples
## 3. Examples
## 3.1. Simple signature
### 3.1. Simple signature
> All `< >` in these examples are placeholders and must be replaced with appropriate data.
Lock :
```
```txt
1 (1 ast)
SIG
0 (if 0 corresponds to Ed25519)
...
...
@@ -276,18 +299,16 @@ Lock :
INPUT 0 (signature on 32 bytes)
```
Lock in hex :
```
```txt
01
71 00 <PublicKeyHash> 01 00
```
Unlocking :
```
```txt
1 (1 parameter)
1 (parameter 1)
0 (no parameters in this group)
...
...
@@ -297,7 +318,7 @@ Unlocking :
Unlocking in hex :
```
```txt
01
01
00
...
...
@@ -305,11 +326,11 @@ Unlocking in hex :
10 00
```
## 3.2. 2 out of 3 signatures (simple)
### 3.2. 2 out of 3 signatures (simple)
Lock :
```
```txt
1
GREAT_EQUAL
SUM 3
...
...
@@ -321,7 +342,7 @@ Lock :
Lock in hex :
```
```txt
01
45
50 03
...
...
@@ -333,7 +354,7 @@ Lock in hex :
Unlocking :
```
```txt
2 (2 parameters)
0 (parameter 0)
0 (no parameters in this group)
...
...
@@ -348,7 +369,7 @@ Unlocking :
Unlocking in hex :
```
```txt
02
00
00
...
...
@@ -360,13 +381,13 @@ Unlocking in hex :
10 01
```
# 3.3. 2 out of 3 signatures (merkelized + includes)
## 3.3. 2 out of 3 signatures (merkelized + includes)
> Node : This one is overcomplicated and is provided as an example of what it's possible to do. However it's very simple compared to the far more complex contracts you can make with that.
Original Merkelized AST :
```
```txt
4 (4 asts)
SIG 0 <PKHash0> INPUT 0 (sig 0)
...
...
@@ -385,7 +406,7 @@ Original Merkelized AST :
Original Merkelized AST in hex :
```
```txt
04
71 00 <PKHash0> 01 00
71 00 <PKHash1> 01 01
...
...
@@ -402,7 +423,7 @@ Let's consider `<MHASH>` as the hash of the AST above.
Lock :
```
```txt
1 (1 ast)
EXEC_AST_HASH (execute correct script in param 0)
<MHASH>
...
...
@@ -411,17 +432,17 @@ Lock :
Lock in hex :
```
```txt
01
05 <MHASH> 01 00
```
> This lock is contained in the transaction, and the merkelized AST need to be stored offchain.
It can then be only known by the participants and used partialy at unlock as shown below.
> It can then be only known by the participants and used partialy at unlock as shown below.