M-of-N-like sporks#2288
Conversation
|
Thanks for this implementation @gladcow! I'm not sure it makes sense to try and coordinate on
I don't understand this. What do you mean by "because votes of first 3 signers don't allow any node to calculate current spork value" ? Can you explain this in another way? I don't think the intention is to require signing and broadcasting at a specific time or timestamp, but rather, sporks are updated per-signer and when the threshold number is reached where ID / Value match, then the active spork switches to that value. |
|
@nmarley ,
Let's consider what a some usual node has in this situation. It has spork messages from signers 1-3 with spork value |
|
I think I see what's going on here with switching from time to sequence id - it's basically an attempt to make sure everyone is signing the same message I guess. But I don't see why it would matter if signatures are not combined (in a crypto way like e.g. in BLS). To distinguish messages signed by different spork key holders I'd rather switch from |
I initially was trying to do something like this, but I don't think it's necessary (is an arbitrary unique ID field that doesn't have to be honored by all nodes and is superfluous). We can just use the sporkAddr as the unique ID instead. The spork signer is identified by reading the signature and recovering the pubkey / hash160, something like: |
|
@nmarley Ah, good point re pubkey recovery! |
Looks like I can't explain it clearly.) This 3 signers and other 2 signers participate in different votings really, for example first 3 signers vote in 2017 year and last 2 signers vote in 2018. And spork should be It is not attempt to simply defend the written code, it is a real problem I've encountered during testing.) |
Ah, ok, thanks for the explanation. I think I understand now. In this case, just make the threshold value require a majority, e.g. if 5 total spork keys exist, require at least 3 of them. That should solve the problem, right? |
|
@nmarley , right, it solves the problem, but I've thought that |
|
I'd say let's start with implementing the simplest/smallest possible one aka 2of3 first and see how it goes. |
|
So, to make it clear, what should be changed:
Have I missed something? |
|
@gladcow sounds good (assuming (4) means ", support providing multiple |
|
Rebased and fixed, re-review, please. |
PastaPastaPasta
left a comment
There was a problem hiding this comment.
Sorry about all the nits! See inline
There was a problem hiding this comment.
bodies of an if should either be on the same line or in brackets
If an if only has a single-statement then-clause, it can appear on the same line as the if, without braces. In every other case, braces are required, and the then and else clauses must appear correctly indented on a new line.
There was a problem hiding this comment.
same as above, same line or braces. Also if( -> if (
There was a problem hiding this comment.
above, same line or braces
There was a problem hiding this comment.
if( -> if (
)){ -> )) {
There was a problem hiding this comment.
if( -> if (
)){ -> )) {
There was a problem hiding this comment.
I'd prefer a better name ¯\(ツ)/¯
There was a problem hiding this comment.
@PastaPastaPasta , I'm writing wrong ifs automatically, I think it is bad practice to break codestyle guide, so it is good you are pointing this errors to me, I'm sorry that I'm making them so often.)
There was a problem hiding this comment.
This should be updated to match actual test logic.
There was a problem hiding this comment.
No need to set r here, could just return false instead.
There was a problem hiding this comment.
Naming: nSporkID and nActiveValueRet (same in cpp)
There was a problem hiding this comment.
Given the conditions in SetMinSporkKeys there can be only one item with value_data.second >= nMinSporkKeys i.e. there is no need for max_count and if, you can simply set activeValue and return true; here. Or even better, you could check this in previous loop (line 40) and drop this one completely.
There was a problem hiding this comment.
This changes the hash. Not sure if this is going to work (without being banned), will test.
There was a problem hiding this comment.
Yep, I'm being banned on testnet. Should probably change GetSignatureHash() to hash everything but vchSig and use it instead of GetHash() in GetSignerKeyID() (for activated spork6 only).
There was a problem hiding this comment.
Yep, I'm being banned on testnet. Should probably change GetSignatureHash() to hash everything but vchSig and use it instead of GetHash() in GetSignerKeyID() (for activated spork6 only).
There was a problem hiding this comment.
Should use a (modified) GetSignatureHash() here.
There was a problem hiding this comment.
Hash should include signature for this functionality because of the fact that signature is the only thing that is different in messages from different signers. If we don't include the signature the messages from other signers are not broadcasted after the message from the first signer. So it looks like I should add some update code compatible with the old version.(
There was a problem hiding this comment.
I know :) But this is only true for p2p part, not for the sign/verify part which can use whatever hash it needs, that's basically the purpose of GetSignatureHash().
There was a problem hiding this comment.
Ah, I see now, will try to fix it this way.)
|
Rebased and fixed, re-review, please |
|
Contains changes from #2313 (I missed them), but also stops to serialize pubkey ids, pubkeys should be hardcoded or read from options, not from spork cache ( = from previous dashd run) |
@nmarley A little late and probably not relevant at this point in time, but my understanding is that this type of pubkey recovery cannot be done with BLS. Perhaps something to think about in the future. |
|
Pls rebase again and bump |
…e synced at moment
|
Rebased, serialization version is bumped. |
UdjinM6
left a comment
There was a problem hiding this comment.
I have a couple of (small) change requests, mostly about readability, see inline commits. Otherwise looks good and seems to be working 👍 Will test a bit more.
| if (!itActive->second.CheckSignature(sporkPubKeyID, true)) { | ||
| mapSporksByHash.erase(itActive->second.GetHash()); | ||
| mapSporksActive.erase(itActive++); | ||
| auto signer_msg_pair = itActive->second.begin(); |
There was a problem hiding this comment.
The name is confusing here imo, would prefer smth like itSignerPair or smth like that.
| continue; | ||
| } | ||
| } | ||
| signer_msg_pair++; |
There was a problem hiding this comment.
Should use pre-increment here i.e. ++signer_msg_pair; (or rather ++itSignerPair; considering the previous comment)
| LOCK(cs); // make sure to not lock this together with cs_main | ||
| for (const auto& pair : mapSporksActive) { | ||
| connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, pair.second)); | ||
| for (const auto& signer_spork: pair.second) { |
There was a problem hiding this comment.
Would prefer a bit more self-describing name e.g. signerSporkPair
| { | ||
| int maxKeysNumber = setSporkPubKeyIDs.size(); | ||
| if ((minSporkKeys <= maxKeysNumber / 2) || (minSporkKeys > maxKeysNumber)) { | ||
| LogPrintf("CSporkManager::SetSporkAddress -- Invalid min spork signers number: %d\n", minSporkKeys); |
| } | ||
|
|
||
| CKeyID keyIDSigner; | ||
| if (!(spork.GetSignerKeyID(keyIDSigner, IsSporkActive(SPORK_6_NEW_SIGS)) |
|
|
||
| if(spork.Sign(sporkPrivKey, IsSporkActive(SPORK_6_NEW_SIGS))) { | ||
| CKeyID keyIDSigner; | ||
| if (!(spork.GetSignerKeyID(keyIDSigner, IsSporkActive(SPORK_6_NEW_SIGS) && setSporkPubKeyIDs.count(keyIDSigner)))) { |
|
Fixed the issues |
nmarley
left a comment
There was a problem hiding this comment.
utACK, 👍 Good work @gladcow !
Seems like an easy transition from legacy to multiple keys, by:
- Adding multiple keys by default in 12.4 release (with legacy key being 1 of them) and only requiring 1-of-1
- Coordinating and signing sporks with other (non-legacy) keys during that period
- The next release after that can remove or replace the default spork key (since sporks now signed by multiple keys), and require majority.
Is this what you had in mind for rollout @UdjinM6 ?
|
Not sure I understand it right @nmarley. I was thinking of using another spork to activate new keys smth like So first activate SPORK_X_MULTIKEYSPORK with the legacy key and then re-sign needed sporks with multiple keys. |
|
Wouldn't that be more complicated? Seems to me like the method I outlined would be easier, and it doesn't require another spork (which would only be used for this purpose). Are there any downsides to that method? |
|
@nmarley I don't think the logic for spork activation here is compatible with |
|
@UdjinM6
Something like: nmarley/dash@pr2288-rebased...spork-legacy-logic Should be separate PR if we go that route. I think this particular PR should be good though as-is, since it introduces the main multi signers logic and there could be alternative ways to activate, as we've discussed a couple already. |
|
edit: Actually, not exactly this:
More like, prefer multi signer logic first, and then fall back to legacy spork key. |
|
Need to think more about it but I agree that new keys and migration logic is smth for another PR. Let's merge this as is first :) |
See issue #2221
allow multiple hardcoded spork addresses (N) to be specified in chainparams/cmd-line/config (same signers for all sporks);
allow multiple messages with the same id/value signed by different spork addresses;
(de)activate any spork only when at least M similar message with the same id/value signed by different signers were received (the same M for all sporks);
"M-of-N" spork implementation requires spork signers consensus achieved with some external communication and doesn't offer any facilities for such consensus achievement. If this consensus is not achived, the spork is treated as it has default value. For example, if sporks are configured as "at least 2 signers of 5 possible" and some spork has default value
0, and 2 signers broadcast this spork value1, while 2 other signers broadcast value2(andnTimeSignedfield has the same value, see details later), this spork is treated as it has value0.Also, several possible signers require new logic for
nTimeSignedfield processing. In old implementation this field is used to separate old non-actual spork messages from the new spork messages (spork message with biggernTimeSignedoverwrites message with smaller one). We need similar logic in new implementation. For example, we have the same configuration like in previous example, 2-of-5-signers. At momentt1signers 1, 2, 3 broadcast spork with value1and spork switches to this value. After that at momentt2signers 4 and 5 tries to switch this spork to value2, and fail to do this, because votes of first 3 signers don't allow any node to calculate current spork value, and spork goes to default value. We can't also simply overwrite all "old" spork messages of other signers because of the fact that in this case just one signer can "rallback" several other signer's messages.To resolve this issue we can require all signers to use the same value of the
nTimeSignedfield to treat their messages as a part of the same "spork broadcast" and allow such signer's groups to overwrite messages of other signer's groups with lessnTimeSignedfield value if this group size is bigger or equal to the minimum configured value (M in M-of-N). It is difficult to use current time for this goal as it was before, because signer's clocks are not synchronized and signers can broadcast their messages at slightly different moments of time. I think it is better to use increasing counter instead and allow signers to set its value directly. Also it is better to allow spork signers to use some default way to calculate this counter value for their convenience and I have added such calculation, but this calculation is not a facility for achieving of signers consensus, so it doesn't resolve all possible situations and sometimes signers should set counter value explicitly. Also, I've renamednTimeSignedfield tonBroadcastIDto reflect its new meaning.