-
Notifications
You must be signed in to change notification settings - Fork 283
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
wallet: derive all keys in generateNonce for multisig #767
wallet: derive all keys in generateNonce for multisig #767
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great solution, elegant code patch. I wonder if this should be published as (maybe very short) HIP since it's an application-level standard we'll need for interoperability with new HNS wallets. In addition, such a HIP may also want to begin to describe some kind of PSBT format that includes bid values from the tx author to the tx signers. Hopefully we have a future where big multisig corporations are bidding huge amounts on names and this kind of convinience would be helpful.
c595a40
to
5e5ab24
Compare
👍 Will have a draft ready along with Bob's multisig PR |
5e5ab24
to
1064f61
Compare
It just occurred to me that this is also a potentially breaking change. A multisig party may have already placed a bid with their own key's nonce and instead of the group key like you've implemented. Attempting to recover that wallet using I'm not sure what the prettiest code would be but to be totally covered I think the logic for |
I looked for all places
So yes, it's a breaking change when:
Storing both nonces in db doubles what's stored on disk for all current multisig wallets as well as those created in the future. Option 1:
Option 2:
Option 3:
I like the first one. Was thinking of a wallet migration or similar, but it wouldn't work here because it affects new wallets, whereas migrations are for existing ones. |
I like the direction of option 1, just adding a We may even just want to do this without a flag (by default) since it is a bit of an edge case anyway. That would just be for rpc/http
I'm not too worried about storing a few extra hashes in the DB for now. Truth is, the wallet DB stores a ridiculous amount of extra data (e.g. everyone else's bids on auctions you participated in, future bids on re-opened names, etc) and pruning this garbage will be another big task that will increase in urgency as time goes on... |
4f0a4ba
to
efda192
Compare
Okay so now the way it works is:
Normal behavior (makeBid, etc.):
Importnonce RPC/HTTP behavior (
|
lib/wallet/wallet.js
Outdated
*/ | ||
|
||
async generateNonce(nameHash, address, value) { | ||
async generateNonce(nameHash, address, value, forAllKeys) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love how this function maybe returns an array and maybe returns a single value.
What if we...
- rename this function
generateNonces()
and it always returns an array - re-add new function
generateNonce()
which callsgenerateNonces()
and only returns first element
this way, existing generateNonce()
calls don't have to be modified. I think if we did the same with generateBlind()
-> generateBlinds()
then we'd only need to directly call the "plural" versions from the rpc, right? That would cover the edge case.
The http call is kinda funny because it doesn't add anything to db, its just like a utility if a user wants to see their nonce? Does Bob wallet use that endpoint anywhere? It almost makes more sense to change that get
to a put
and also call generateBlind()
same as the rpc call. That would make this PR a breaking change, though.
That would make this PR a breaking change, though.
but actually thats ok because we're going to call this 5.0.0 anyway i think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't like it either. Now it has generateNonce(s)
and generateBlind(s)
. But generateNonce
cannot call its plural method [0] because the order of the nonces/blinds is different:
- hsd behavior has been own public key everywhere
- now we switch to smallest public key
So generateNonce has the new style (smallest) whereas generateNonces returns first based on own key so that there's no breaking change.
The http call is kinda funny because it doesn't add anything to db ... That would make this PR a breaking change, though.
I don't think Bob uses that remote call since it always has access to the wallet directly. But since it's breaking, can be kept aside for a different PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, each commit here undos a lot of code from the previous commit so might be easier to just look at overall PR changes. Will squash if the code makes sense now.
d1615bc
to
18328f9
Compare
lib/wallet/rpc.js
Outdated
// Return only first blind (based on own key) | ||
// to stay backward-compatible | ||
return blinds[0].toString('hex'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eh, backwards-compat isnt so precious here, lets
return blinds.map(b => b.toString('hex'));
It'll be a breaking change I guess, if you only have one key youll get an array with a single item but I'm ok with that. Just needs to go in the changelog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now everything (internal methods, RPC, HTTP) all use smallest key and blinds and nonces are arrays. Added to CHANGELOG.
// but blinds for all key are saved in db | ||
assert(await bob.txdb.hasBlind(expectedBlinds.alice)); | ||
assert(await bob.txdb.hasBlind(expectedBlinds.bob)); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should also try to verify that both alice and bob can create a valid reveal even if the nonce is not based on their own key. That might even be best in wallet-rpc-test.js
since thats the command thats been set up to cover the edge case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a multisig auction test in wallet-rpc-test
from OPEN to CLOSED, including a failed no-blind reveal and importnonce that makes it work.
lib/wallet/wallet.js
Outdated
const publicKeys = await this._getNoncePublicKeys(address, value); | ||
|
||
// Use smallest public key | ||
publicKeys.sort(Buffer.compare); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API tries to be side-channel resistant where possible, so you could use const {safeCompare} = require('bcrypto/lib/safe')
does it matter in the pk case ? I think it is not necessary.
Also after reviewing, I think safeCompare
is not what I thought it was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think can we can avoid duplicate blind as well as additional flags in RPC/API by utilizing almost unused flags in account.
Right now flags have 7 bits unused, we can take 2nd bit for this. We can introduce new flag in account - e.g. this.multisigBlinds = true
Existing wallets in the db, will have this flag set to 0 - meaning old behaviour. New wallets will default to new flag. This adds overhead to techdebt, but avoids all API changes. This does not even need migration. Can be released with minor.
@nodech as discussed, this doesn't solve anything because after this is merged, we can't guarantee everyone in every multisig party is running updated software. So in a recovery scenario (with |
lib/wallet/http.js
Outdated
@@ -1029,7 +1029,8 @@ class HTTP extends Server { | |||
} | |||
|
|||
const nameHash = rules.hashName(name); | |||
const nonce = await req.wallet.generateNonce(nameHash, address, bid); | |||
const nonces = await req.wallet.generateNonces(nameHash, address, bid); | |||
const nonce = nonces[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure but this endpoint might need to return an array as well. Let's take a look at bob's repair bid functionality. IIRC, we use the HTTP endpoint (which does not save anything to DB) to generate blinds until we get the right one, then call importnonce once with the correct values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bob always has access to the wallet and calls generateNonce
(not generateBlind
that saves blind). It doesn't depend on RPC/HTTP at all:
const wallet = await this.node.wdb.get(this.name);
...
const nonce = await wallet.generateNonce(nameHash, from, value);
const blind = Rules.blind(value, nonce).toString('hex');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh just saw that the actual repair does use the HTTP API:
https://github.com/kyokan/bob-wallet//blob/master/app/pages/Auction/RepairBid.js#L46-L50
I think we can make Bob use the wallet directly (similar to previous comment) to not depend on HTTP at all,
or will need to expose generateNonces
plural (requires changes to hs-client also).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New test is great! just nits left
bf56b6a
to
637c85b
Compare
Addressed all comments and cleaned up commits. |
Breaking change for RPC importnonce and HTTP "Regenerate Nonce"
18d77ed
to
0b5fb98
Compare
0b5fb98
to
5f48556
Compare
Multisig accounts will generate different nonce given the same nameHash, p2sh address and value as only "own" public key is used. This PR derives public keys from all keys and picks the lexicographically smallest one so all parties generate the same nonce. Implements @pinheadmz's suggestion: https://t.me/hns_tech/70247
Tests
2 tests are added, one for regular pubkeyhash accounts and another for 2-of-2 multisig. Assertions compare to specific hardcoded values to ensure that this PR is not a breaking change (at least for regular wallets).
Running the test on master passes the first (pubkeyhash account), but fails on the second (multisig) because alice and bob generate different nonces.