NUT-XX: Get quotes by pubkeys - #341
Conversation
robwoodgate
left a comment
There was a problem hiding this comment.
The signature scheme is a bit worrying, but otherwise just some nit/questions for clarity.
Co-authored-by: Rob Woodgate <robwoodgate@users.noreply.github.com>
Co-authored-by: Rob Woodgate <robwoodgate@users.noreply.github.com>
|
|
||
| ```json | ||
| { | ||
| "29": { |
There was a problem hiding this comment.
This needs a new NUT number, NUT-29 is already defined in NUT-29: Batched Minting
Same thing here
|
I've opened a PR with the updated signature logic and test vectors here: #363 |
| } | ||
| ``` | ||
|
|
||
| Where `MintQuoteResponse` is the quote response type defined in [NUT-04][04]. |
There was a problem hiding this comment.
Presumably, each quote will be the shape specified for the method it represents? So maybe a better wording is something like:
| Where `MintQuoteResponse` is the quote response type defined in [NUT-04][04]. | |
| Where `MintQuoteResponse` is the quote response type defined in [NUT-04][04], or an extension of it based on the method the quote relates to. |
|
Suggestion: drop |
|
Suggestion: bound the pubkey count. The mint must verify every signature before it can tell whether the caller is entitled to anything, so request length decides how much work an anonymous caller can demand. Add a MUST to limit it, error 11017 on overflow, and an optional |
|
Suggestion: let the request bound the response. The query returns a pubkey's full quote history in every state, with no way to request a subset so the response grows unbounded with the number of quotes a key accumulates. Two optional request fields fix it: mintable quotes ( |
Previously discussed and mint side prefers to keep:
Good idea. I wonder if instead of yet-another-mintinfo-flag, we just cap it in the NUT spec to (say) 100?
In an ideal world, pubkeys would not be constantly reused for privacy, but there is no bound in the real world. I wonder if a mint returning just NON-EXPIRED and mintable quotes ( |
Fine with me. Before we pick a number perhaps we should do some load testing to help inform our choice.
I agree in principle that we shouldn't build it before we need it. My use case does a lot more volume than the rest of the cashu ecosystem. This is why Hashpool has been useful in the past for load testing cdk. I plan to rotate locking keys on a timer at the miner wallet, not implemented yet. I could lean on vardiff or progressive mint fees to limit the number of quotes the mint processes, the latter is probably the best solution (market solutions ftw). Lots of knobs I can turn to shift complexity outside of the cashu protocol definition. |
For cross-reference, this PR would solve the pubkey bound issue in practice (mints already cap array lengths using the value internally): #425 |
There is another issue of not only max pubkey but max quotes per pubkey. If a wallet creates many quotes for a pubkey and then does a look up of just one pubkey but over time has generated many quotes on the mint this could be an expensive look up for the mint especially if we add logic like is mintable etc. We don't have anyway to limit this right now. |
Good point. We would probably have to limit the length of the returned arrays as well. |
|
@thesimplekid @robwoodgate are both implementation PRs gtg? |
CTS: Yes, just need to gate the mint info once nut number is finalized and add integration tests once a mint release supports it. So available pretty quickly once nut and cdk merge it |
|
We can assign a number for this thing? |
|
Cross link: |
We can't just limit it though other wise quotes will be lost if a user only has the pubkey, but i don't see a great solution other then limiting the number of quotes that can be created for a pubkey or adding pagination to the look up by pubkey both not good. |
Is there an issue if the mint simply rejects a create mint quote request if it uses a pubkey that has been used more than the max_array_inputs number of times? Then it can always return a max number of quotes, no pagination |
There was a problem hiding this comment.
This still needs replay protection to stop an onlooker following the lifecycle of a pubkey forever. Suggest we include timestamp in the request, signed over, and the mint with its NTP server clock can accept or reject it +/- a defined window.
This avoids needing an extra challenge request, and also avoids mint needing to store or track nonces
Request:
{
"pubkeys": [
"03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac",
"02a4c1d2f3e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2"
],
"timestamp": 1788825600,
"signatures": [
"d9be080b33179387e504bb6991ea41ae0dd715e28b01ce9f63d57198a095bb2f...",
"3f2a8c1e5b7d9f0a2c4e6b8d1f3a5c7e9b0d2f4a6c8e1b3d5f7a9c0e2b4d6f8a..."
]
}Signature scheme:
For each pubkey P_i in the request, the wallet computes:
msg_i = len32(P_i) || P_i || len32(t) || t
where:
P_i = the 33-byte compressed public key
t = the timestamp as an 8-byte big-endian unsigned integer
(Unix seconds)
len32 = the byte length of the following field as a 4-byte
big-endian unsigned integer
|| = byte concatenation
The wallet signs:
sig_i = BIP340_sign(k_i, TaggedHash("Cashu_QuoteLookup_v1", msg_i))
where k_i is the private key for P_i and TaggedHash is the BIP-340
tagged hash: SHA256(SHA256(tag) || SHA256(tag) || msg) with the tag
as UTF-8 bytes.
signatures[i] MUST be the signature for pubkeys[i] (same order,
same length). All signatures in one request commit to the same
timestamp.
The mint MUST reject the request if:
- |now - timestamp| > TIMESTAMP_WINDOW (suggested: 300 seconds),
checked before any signature verification; or
- the pubkeys and signatures arrays differ in length; or
- any signature fails BIP-340 verification against the
x-coordinate of its pubkey.
Rejection is all-or-nothing; a stale timestamp returns a distinct
error code so wallets can retry with a fresh timestamp rather than
treating it as an authorization failure.
Implementations
supersedes #329
Get NUT-20 quotes by
pubkeys. Requires signatures to prove possession of the corresponding private keys.