{"id":"474f2a906fb2671e103b3dd8ca346015d51920301733859401274addd410cc12","pubkey":"d1678e7ef965374bbea308a1215609a78376dc158277a7d657680f9d5efd5c38","created_at":1781018153,"kind":30817,"tags":[["d","nut-11"],["title","NUT-11: Pay to Public Key (P2PK)"],["summary","Locking ecash to a public key, redeemable only against a Schnorr signature from the key that owns it."],["s","optional"],["t","cashu"],["t","ecash"],["t","nut"],["alt","A specification: NUT-11: Pay to Public Key (P2PK)"],["client","openspecs-import"],["published_at","1697225320"],["proxy","https://github.com/cashubtc/nuts/blob/a845dfc998abae501fc3419592d53dc995d34b12/11.md","web"],["x","f32ca0208e6ef181312a3056e51802cf2de8527d20d5cd3b724a30bce2de6ca9"]],"content":"# NUT-11: Pay to Public Key (P2PK)\n\n`optional`\n\n`depends on: NUT-10, NUT-08`\n\n`extended by: NUT-28`\n\n---\n\nThis NUT describes Pay-to-Public-Key (P2PK) which is one kind of spending condition based on [NUT-10][10]'s well-known `Secret`. Using P2PK, we can lock ecash Proofs (see [NUT-00][00]) to a receiver's ECC public key and require a Schnorr signature with the corresponding private key to unlock the ecash. The spending condition is enforced by the mint.\n\nCaution: If the mint does not support this type of spending condition, Proofs may be treated as regular anyone-can-spend Proofs. Applications need to make sure to check whether the mint supports a specific kind of spending condition by checking the mint's [NUT-06][06] info endpoint.\n\n## Basic Case\n\n[NUT-10][10] Secret `kind: P2PK`\n\nIf for a `Proof`, `Proof.secret` is a `Secret` of kind `P2PK`, the proof must be unlocked by providing a witness `Proof.witness` and one or more valid signatures in the array `Proof.witness.signatures`.\n\nIn the basic case, when spending a locked Proof, the mint requires one valid Schnorr signature in `Proof.witness.signatures` on `Proof.secret` by the public key in `Proof.secret.data`.\n\nTo give a concrete example of the basic case, to mint a locked Proof we first create a P2PK `Secret` that reads:\n\n```json\n[\n  \"P2PK\",\n  {\n    \"nonce\": \"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\n    \"data\": \"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\n    \"tags\": [[\"sigflag\", \"SIG_INPUTS\"]]\n  }\n]\n```\n\nHere, `Secret.data` is the public key of the recipient of the locked ecash. We serialize this `Secret` to a string in `Proof.secret` and get a blind signature by the mint that is stored in `Proof.C` (see [NUT-03][03]).\n\nThe recipient who owns the private key of the public key `Secret.data` can spend this proof by providing a signature on the serialized `Proof.secret` string that is then added to `Proof.witness.signatures`:\n\n```json\n{\n  \"amount\": 1,\n  \"secret\": \"[\\\"P2PK\\\",{\\\"nonce\\\":\\\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\\\",\\\"data\\\":\\\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\\\",\\\"tags\\\":[[\\\"sigflag\\\",\\\"SIG_INPUTS\\\"]]}]\",\n  \"C\": \"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\",\n  \"id\": \"009a1f293253e41e\",\n  \"witness\": \"{\\\"signatures\\\":[\\\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\\\"]}\"\n}\n```\n\n## Signature scheme\n\nTo spend Proofs locked with `P2PK`, the spender needs to include signatures in the Proofs used as \"inputs\" for the spending operation. 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, unless otherwise indicated by the `Secret.tags.sigflag` in the inputs, as detailed below.\n\nAn ecash spending operation like [swap][03] and [melt][05] can have multiple inputs and outputs. If we have more than one locked input, we either provide signatures in each input individually (for `SIG_INPUTS`) or only in the first input for the entire transaction (for `SIG_ALL`). The inputs are the `Proofs` provided in the `inputs` field and the outputs are the `BlindedMessages` in the `outputs` field in the request body (see `PostMeltRequest` in [NUT-05][05] and `PostSwapRequest` in [NUT-03][03]).\n\n> [!NOTE]\n>\n> The field `Proof.secret` contains escaped JSON for transport. The message to sign MUST be constructed using the **unescaped** secret string, eg: [\"P2PK\",{\"nonce\":\"c7f280eb55c1e856...\n\n### Witness format\n\nSignatures 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\n\n```json\n{\n  \"signatures\": <Array[<hex_str>]>\n}\n```\n\nThe `signatures` are an array of signatures in hex and correspond to the signatures by one or more signing public keys.\n\n## Tags\n\nMore complex spending conditions can be defined in the tags in `Secret.tags`. All tags are optional. Tags are arrays with two or more strings being `[\"key\", \"value1\", \"value2\", ...]`. We denote a specific tag in a proof by its `key`.\n\nSupported tags are:\n\n- `sigflag: <str_enum[SIG_FLAG]>` sets the [signature flag](#signature-flag)\n- `pubkeys: <hex_str>` are additional public keys (together with the one in the `data` field of the secret) that can provide signatures (_allows multiple entries_)\n- `n_sigs: <int>` specifies the minimum number of [Locktime Multisig](#locktime-multisig) public keys providing valid signatures\n- `locktime: <int>` is the Unix timestamp of when the lock expires\n- `refund: <hex_str>` are additional public keys that can provide signatures after `locktime` (_allows multiple entries_)\n- `n_sigs_refund: <int>` specifies the minimum number of [Refund Multisig](#refund-multisig) public keys providing valid signatures after `locktime` expires\n\nEach of the above tags may appear exactly **ONCE** in a P2PK secret. If a tag appears more than once, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.\n\nIf `n_sigs` or `n_sigs_refund` is not a positive integer, or exceeds the total number of keys in its pathway, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.\n\n> [!NOTE]\n>\n> The tag serialization type is `[<str>, <str>, ...]` but some tag values are `int`. Wallets and mints must cast types appropriately for de/serialization.\n\n## Signature flag\n\nSignature flags are defined in the tag `Secret.tags['sigflag']`. Currently, there are two signature flags.\n\n- `SIG_INPUTS` requires valid signatures on all inputs independently. It is the default signature flag and will be applied if the `sigflag` tag is absent.\n- `SIG_ALL` requires valid signatures on all inputs and on all outputs of a transaction.\n\nIf any one input has the signature flag `SIG_ALL`, then all inputs are required to have the same kind, the flag `SIG_ALL` and the same `Secret.data` and `Secret.tags`, otherwise an error is returned.\n\n`SIG_INPUTS` is only enforced if no input is `SIG_ALL`.\n\nIf a P2PK secret has any other signature flag value, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.\n\n### Signature flag `SIG_INPUTS`\n\n`SIG_INPUTS` means that each `Proof` (input) requires its own signature. The signature is provided in the `Proof.witness` field of each input separately. The format of the [witness](#witness-format) was defined earlier on.\n\n#### Signed inputs\n\nA `Proof` (an input) with a signature `P2PKWitness.signatures` on `secret` is the JSON (see [NUT-00][00]):\n\n```json\n{\n  \"amount\": <int>,\n  \"secret\": <str>,\n  \"C\": <hex_str>,\n  \"id\": <str>,\n  \"witness\": <P2PKWitness | str> // Signatures on \"secret\"\n}\n```\n\nThe `secret` field is **signed as a string**.\n\n### Signature flag `SIG_ALL`\n\n`SIG_ALL` is enforced only if the following conditions are met:\n\n- 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`.\n- If one or more inputs differ from this, an error is returned.\n\nIf this condition is met, the `SIG_ALL` flag is enforced and only **the first input of a transaction requires a witness** that covers all other inputs and outputs of the transaction. All signatures by the signing public keys MUST be provided in the `Proof.witness` of the first input of the transaction.\n\n#### Message aggregation for `SIG_ALL`\n\nThe message to be signed depends on the type of transaction containing an input with signature flag `SIG_ALL`.\n\n##### Aggregation for `swap`\n\nA swap contains `inputs` and `outputs` (see [NUT-03][03]). To provide a valid signature, the owner (or owners) of the signing public keys must concatenate the `secret` and `C` fields of all `Proofs` (inputs) with the `amount` and `B_` fields of all `BlindedMessages` (outputs, see [NUT-00][00]) to a single message string in the order they appear in the transaction. This concatenated string is then hashed and signed (see [Signature scheme](#signature-scheme)).\n\nIf a swap transaction has `n` inputs and `m` outputs, the message to sign becomes:\n\n```\nmsg = secret_0 || C_0 || ... || secret_n || C_n || amount_0 ||  B_0 || ... || amount_m || B_m\n```\n\nHere, `||` denotes string concatenation. The `C` of each input and `B_` of each output are **hex strings** and `amount` is a UTF-encoded string.\n\n##### Aggregation for `melt`\n\nFor a melt transaction, the message to sign is composed of all the inputs, the quote ID being paid, and the [NUT-08][08] blank `outputs`.\n\nIf a melt transaction has `n` inputs, `m` blank outputs, and a quote ID `quote_id`, the message to sign becomes:\n\n```\nmsg = secret_0 || C_0 || ... || secret_n || C_n || amount_0 || B_0 || ... || amount_m || B_m || quote_id\n```\n\nHere, `||` denotes string concatenation. The `C` of each input and `B_` of each output are **hex strings** and `amount` is a UTF-encoded string.\n\n## Locktime Tag\n\nThe `locktime` tag signals which set of locking rules the mint should apply. There are three possible states the `locktime` tag can represent:\n\n### Permanent Lock\n\nIf the `locktime` tag is not present, or is not a valid unix time, the lock is considered \"permanent\".\n\n[Locktime Multisig](#locktime-multisig) conditions apply if the `pubkeys` tag is present, [Basic Case](#basic-case) conditions if not.\n\n### Active Lock\n\nIf the `locktime` tag is a valid unix time and the mint's local clock is less than `locktime`, the lock is \"active\".\n\n[Locktime Multisig](#locktime-multisig) conditions apply if the `pubkeys` tag is present, [Basic Case](#basic-case) conditions if not.\n\n### Expired Lock\n\nIf the `locktime` tag is a valid unix time and the mint's local clock is greater than `locktime`, the lock has \"expired\".\n\nBoth [Locktime Multisig](#locktime-multisig) and [Refund Multisig](#refund-multisig) conditions apply if the `refund` tag is present, otherwise the proof is considered unlocked and spendable without a witness signature.\n\n> [!NOTE]\n> A `Proof` is considered spendable by anyone if it only requires a `secret` and a valid unblinded signature `C` to be spent (which is the default case in [NUT-00][00]).\n\n## Multisig\n\nCashu offers two multi-signature pathways: `Locktime MultiSig` and `Refund MultiSig`, which are activated depending on the status of the proof's `locktime` tag.\n\n> [!NOTE]\n> Each pathway has a self-contained set of conditions which must be satisfied for that pathway to be valid. You cannot mix/blend conditions between pathways.\n\n### Locktime MultiSig\n\nLocktime Multisig extends the [Basic Case](#basic-case) by allowing proofs to be locked to multiple public keys. The additional locking public keys are stored in the `pubkeys` tag.\n\nIf the `pubkeys` tag is present, the `Proof` is spendable only if a valid signature is given by **at least ONE** of the public keys contained in the `Secret.data` field or the `pubkeys` tag.\n\nIf the `n_sigs` tag is a positive integer, the mint will require at least `n_sigs` of those public keys to provide a valid signature.\n\nIf the number of public keys with valid signatures is greater or equal to the number specified in `n_sigs` (or `1` if `n_sigs` is not present), the transaction is valid. The signatures are provided in an array of strings in the `P2PKWitness` object.\n\nExpressed as an \"n-of-m\" scheme, `n = n_sigs` is the number of required signatures and `m = 1 (data field) + count(pubkeys tag keys)` is the total number of public keys that _could_ sign.\n\n> [!CAUTION]\n>\n> Because Schnorr signatures are non-deterministic (due to auxiliary random data), we expect a minimum number of unique public keys with valid signatures instead of expecting a minimum number of signatures.\n\n### Refund MultiSig\n\nRefund Multisig allows proofs to be _additionally spendable_ by a separate set of public keys once the `locktime` has expired. These public keys are stored in the `refund` tag, and can include keys previously listed in `data` or `pubkeys`.\n\n[Locktime Multisig](#locktime-multisig) conditions continue to apply, and the proof can continue to be spent according to Locktime Multisig rules.\n\n**In addition**, the `Proof` can be spent if a valid signature is given by **at least ONE** of the public keys contained in the `refund` tag.\n\nIf the `n_sigs_refund` tag is a positive integer, the mint will require at least `n_sigs_refund` of those `refund` public keys to provide a valid signature.\n\nIf the number of `refund` public keys with valid signatures is greater or equal to the number specified in `n_sigs_refund` (or `1` if `n_sigs_refund` is not present), the transaction is valid. The signatures are provided in an array of strings in the `P2PKWitness` object.\n\nExpressed as an \"n-of-m\" scheme, `n = n_sigs_refund` is the number of required signatures and `m = count(refund tag keys)` is the total number of `refund` keys that _could_ sign.\n\n> [!CAUTION]\n>\n> Because Schnorr signatures are non-deterministic (due to auxiliary random data), we expect a minimum number of unique public keys with valid signatures instead of expecting a minimum number of signatures.\n\n### Complex Example\n\nThis 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.\n\nIf the `locktime` has passed, the `Proof` continues to be spendable with 2-of-3 signatures from the public keys in the `data` field and the `pubkeys` tag. But now it **ALSO** becomes spendable with a single signature from any ONE of the public keys in the `refund` tag.\n\nThe signature flag `sigflag` indicates that signatures are necessary on the `inputs` and the `outputs` of the transaction this `Proof` is spent by.\n\n```json\n[\n  \"P2PK\",\n  {\n    \"nonce\": \"da62796403af76c80cd6ce9153ed3746\",\n    \"data\": \"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e\",\n    \"tags\": [\n      [\"sigflag\", \"SIG_ALL\"],\n      [\"n_sigs\", \"2\"],\n      [\"locktime\", \"1689418329\"],\n      [\n        \"refund\",\n        \"033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e\",\n        \"02e2aeb97f47690e3c418592a5bcda77282d1339a3017f5558928c2441b7731d50\"\n      ],\n      [\n        \"pubkeys\",\n        \"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\",\n        \"023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54\"\n      ]\n    ]\n  }\n]\n```\n\n## Public Key Canonicalisation\n\nPublic keys **MUST** use the [compressed Secp256k1 public key format](https://learnmeabitcoin.com/technical/public-key#public-key-format).\n\nEach key **MUST** appear at most **ONCE** per [multi-signature](#Multisig) pathway. The same key **MAY** appear in both pathways.\n\nKeys are compared using their lowercase x-coordinate (`02` or `03` y-parity prefix ignored).\n\nExample: the following keys are considered equivalent because they all have the same lowercase x-coordinate, so can all be Schnorr signed using the same secret key:\n\n```json\n02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904 // 02 lowercase\n02698C4E2B5F9534CD0687D87513C759790CF829AA5739184A3E3735471FBDA904 // 02 uppercase\n02698C4e2B5f9534cD0687d87513C759790cF829aa5739184a3e3735471fbda904 // 02 mixed case\n03698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904 // 03 lowercase\n03698C4E2B5F9534CD0687D87513C759790CF829AA5739184A3E3735471FBDA904 // 03 uppercase\n03698C4e2B5f9534cD0687d87513C759790cF829aa5739184a3e3735471fbda904 // 03 mixed case\n```\n\nExample: key duplicated in main pathway (malformed):\n\n```\n\"data\": \"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\",\n\"pubkeys\": \"03698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\"\n```\n\nExample: key allowed in both pathways:\n\n```\n\"data\": \"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\",\n\"refund\": \"03698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904\"\n```\n\nIf a pathway contains a duplicate key, the P2PK secret is malformed and the Proof **MUST** be rejected as unspendable.\n\n## Use cases\n\nThe following use cases are unlocked using P2PK:\n\n- Publicly post locked ecash that can only be redeemed by the intended receiver\n- Final offline-receiver payments that can't be double-spent when combined with an offline signature check mechanism like DLEQ proofs\n- Receiver of locked ecash can defer and batch multiple mint round trips for receiving proofs (requires DLEQ)\n- Ecash that is owned by multiple people via the multisignature abilities\n- Atomic swaps when used in combination with the locktime feature\n\n## Mint info setting\n\nThe [NUT-06][06] `MintMethodSetting` indicates support for this feature:\n\n```json\n{\n  \"11\": {\n    \"supported\": true\n  }\n}\n```\n\n[00]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crqqpd6d9\n[01]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crznyve7g\n[02]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cry0t0uzl\n[03]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crxuwwl3j\n[04]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crg74fknc\n[05]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cr2dsg4q4\n[06]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crv3ltsuz\n[07]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crwz62n00\n[08]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crs4q9zck\n[09]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595crjx9yptm\n[10]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnqx5tye7\n[11]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnz43282n\n[12]: nostr:naddr1qvzqqqrcvypzp5t83el0jefhfwl2xz9py9tqnfurwmwptqnh5lt9w6q0n4006hpcqqrxuat595cnyf7fzky\n","sig":"065e3bbc86654cc640015d2b8bd2dc8cb56226640c1f463b13602345114d5e9ac79f22f487f3d99457c80598a86bd8ec0d34a7b884364fc2414a6618fd841322"}