diff --git a/11.md b/11.md index 63584cb9..651921eb 100644 --- a/11.md +++ b/11.md @@ -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_`. @@ -61,28 +61,31 @@ More complex spending conditions can be defined in the tags in `Proof.tags`. All Supported tags are: - `sigflag: ` determines whether outputs have to be signed as well -- `n_sigs: ` specifies the minimum number of valid signatures expected +- `n_sigs: ` is the minimum number of valid signatures required - `pubkeys: ` are additional public keys that can provide signatures (*allows multiple entries*) - `locktime: ` is the Unix timestamp of when the lock expires - `refund: ` are optional refund public keys that can exclusively spend after `locktime` (*allows multiple entries*) **Note:** The tag serialization type is `[, , ...]` 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": , @@ -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": , - "B_": , - "witness": // 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": ]> } ``` -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. @@ -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 [