Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUT-11: SIG_ALL sign aggregated message all inputs and ouptuts #149

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 33 additions & 24 deletions 11.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The recipient who owns the private key of the public key `Secret.data` can spend
}
```

#### Signature scheme
### Signature scheme

To spend a token locked with `P2PK`, the spender needs to include signatures in the spent proofs. We use `libsecp256k1`'s serialized 64 byte Schnorr signatures on the SHA256 hash of the message to sign. The message to sign is the field `Proof.secret` in the inputs. If indicated by `Secret.tags.sigflag` in the inputs, outputs might also require signatures on the message `BlindedMessage.B_`.

Expand All @@ -61,28 +61,31 @@ More complex spending conditions can be defined in the tags in `Proof.tags`. All
Supported tags are:

- `sigflag: <str>` determines whether outputs have to be signed as well
- `n_sigs: <int>` specifies the minimum number of valid signatures expected
- `n_sigs: <int>` is the minimum number of valid signatures required
- `pubkeys: <hex_str>` are additional public keys that can provide signatures (*allows multiple entries*)
- `locktime: <int>` is the Unix timestamp of when the lock expires
- `refund: <hex_str>` are optional refund public keys that can exclusively spend after `locktime` (*allows multiple entries*)

**Note:** The tag serialization type is `[<str>, <str>, ...]` but some tag values are `int`. Wallets and mints must cast types appropriately for de/serialization.

#### Signature flags
Signature flags are defined in the tag `Secret.tags['sigflag']`. Currently, there are two signature flags.

### Signature flags
Signature flags are defined in the tag `Secret.tags['sigflag']`. There are two signature flags.


- `SIG_INPUTS` requires valid signatures on all inputs. It is the default signature flag and will be applied even if the `sigflag` tag is absent.
- `SIG_ALL` requires valid signatures on all inputs and on all outputs.

The signature flag `SIG_ALL` is enforced if at least one of the `Proofs` have the flag `SIG_ALL`. Otherwise, `SIG_INPUTS` is enforced.
If one input has the signature flag `SIG_ALL`, then all of them are required to have the flag `SIG_ALL` and same `Secret.data` and `Secret.tags` (see [here](#sig_inputs)), otherwise an error is returned.

`SIG_INPUTS` is only enforced if no input is `SIG_ALL`.

#### Signature
#### Signature flag `SIG_INPUTS`

Signatures must be provided in the field `Proof.witness.signatures` for each `Proof` which is an input. If the signature flag `SIG_ALL` is enforced, signatures must also be provided for every output in its field `BlindedMessage.witness.signatures`.
`SIG_INPUTS` means that each `Proof` (input) requires it's own signature. The signature is provided in the `Proof.witness` field of each input separately. The format of the [witness](#witness-format) is defined later on.

##### Signed inputs
A `Proof` (an input) with a signature `P2PKWitness.signatures` on `secret` is the JSON (see [NUT-00][00]):
A signed `Proof` where `witness` includes a signature on `secret` is the JSON (see [NUT-00][00]):
```json
{
"amount": <int>,
Expand All @@ -93,34 +96,40 @@ A `Proof` (an input) with a signature `P2PKWitness.signatures` on `secret` is th
}
```

The `secret` of each input is **signed as a string**.
The `secret` field is **signed as a string**.

##### Signed outputs
A `BlindedMessage` (an output) with a signature `P2PKWitness.signatures` on `B_` is the JSON (see [NUT-00][00]):
#### Signature flag `SIG_ALL`

```json
{
"amount": <int>,
"B_": <hex_str>,
"witness": <P2PKWitness | str> // Signatures on "B_"
}
```
`SIG_ALL` is enforced only if the following conditions are met:

The `B_` of each output is **signed as bytes** which comes from the original hex string.
- If one input has the signature flag `SIG_ALL`, all other inputs MUST have the same `Secret.data` and `Secret.tags`, and by extension, also be `SIG_ALL`.
- If one or more inputs differ from this, an error is returned.

If this condition is met, the `SIG_ALL` flag is enforced and a single signature per signing public key is required for the whole transaction. All signatures by the signing public keys MUST be provided in the `Proof.witness` of the first input of the transaction.

##### Message aggregation

To provide a signature for a `SIG_ALL` transaction, the owner (or owners) of one of the signing public keys must concatenate the `secret` field of all `Proofs` (inputs), and the `B_` fields of all `BlindedMessages` (outputs, see [NUT-00][00]) to a single message string in the order they appear in the transaction. This string concatenated is then hashed and signed (see [Signature scheme](#signature-scheme)).

If a transaction has `n` inputs and `m` outputs, the message to sign becomes:
```
msg = secret_0 || ... || secret_n || B_0 || ... || B_m
```
Here, `||` denotes string concatenation. The `B_` of each output is **a hex string**.

##### Witness format
#### Witness format

`P2PKWitness` is a serialized JSON string of the form
Signatures are stored in `P2PKWitness` objects and are provided in either each `Proof.witness` of all inputs separately (for `SIG_INPUTS`) or only in the first input of the transaction (for `SIG_ALL`). `P2PKWitness` is a serialized JSON string of the form
```json
{
"signatures": <Array[<hex_str>]>
}
```

The `signatures` are an array of signatures in hex.
The `signatures` are an array of signatures in hex and correspond to the signatures by one or multiple signing public keys.

### Multisig
If the tag `n_sigs` is a positive integer, the mint will also consider signatures from public keys specified in the `pubkeys` tag additional to the public key in `Secret.data`. If the number of valid signatures is greater or equal to the number specified in `n_sigs`, the transaction is valid.
If the tag `n_sigs` is a positive integer, the mint will also consider signatures from public keys specified in the `pubkeys` tag additional to the public key in `Secret.data`. If the number of valid signatures is greater or equal to the number specified in `n_sigs`, the transaction is valid. The individual signatures are provided in an array of strings in the `P2PKWitness` object.

Expressed as an "n-of-m" scheme, `n = n_sigs` is the number of required signatures and `m = 1 ("data" field) + len(pubkeys tag)` is the number of public keys that could sign.

Expand All @@ -132,7 +141,7 @@ If the `locktime` is in the past and a tag `refund` is present, the `Proof` is s

#### Complex Example

This is an example `secret` that locks a `Proof` with a Pay-to-Pubkey (P2PK) condition that requires 2-of-3 signatures from the public keys in the `data` field and the `pubkeys` tag. If the `timelock` has passed, the `Proof` becomes spendable with a single signature from the public key in the `refund` tag. The signature flag `sigflag` indicates that signatures are necessary on the `inputs` and the `outputs` of a transaction.
This is an example `Secret` that locks a `Proof` with a Pay-to-Pubkey (P2PK) condition that requires 2-of-3 signatures from the public keys in the `data` field and the `pubkeys` tag. If the `timelock` has passed, the `Proof` becomes spendable with a single signature from the public key in the `refund` tag. The signature flag `sigflag` indicates that signatures are necessary on the `inputs` and the `outputs` of the transaction this `Proof` is spent by.

```json
[
Expand Down