Skip to content
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

M-of-N-like sporks #2288

Merged
merged 25 commits into from
Sep 30, 2018
Merged

M-of-N-like sporks #2288

merged 25 commits into from
Sep 30, 2018

Conversation

gladcow
Copy link

@gladcow gladcow commented Sep 12, 2018

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 value 1, while 2 other signers broadcast value 2 (and nTimeSigned field has the same value, see details later), this spork is treated as it has value 0.

    Also, several possible signers require new logic for nTimeSigned field processing. In old implementation this field is used to separate old non-actual spork messages from the new spork messages (spork message with bigger nTimeSigned overwrites 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 moment t1 signers 1, 2, 3 broadcast spork with value 1 and spork switches to this value. After that at moment t2 signers 4 and 5 tries to switch this spork to value 2, 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 nTimeSigned field 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 less nTimeSigned field 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 renamed nTimeSigned field to nBroadcastID to reflect its new meaning.

@gladcow gladcow changed the title M-of-N-like sporks [WIP] M-of-N-like sporks Sep 12, 2018
@nmarley
Copy link

nmarley commented Sep 13, 2018

Thanks for this implementation @gladcow!

I'm not sure it makes sense to try and coordinate on nTimeSigned or rename to use a broadcast field -- or rather, I think that adds extra, un-needed complexity. The nTimeSigned should be when the signer signed the spork, and we track spork ID/Values per-spork-signer instead. The latest nTimeSigned value over-writes the value for that spork ID for that given signer only.

At moment t1 signers 1, 2, 3 broadcast spork with value 1 and spork switches to this value. After that at moment t2 signers 4 and 5 tries to switch this spork to value 2, 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.

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.

@gladcow
Copy link
Author

gladcow commented Sep 13, 2018

@nmarley ,

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?

Let's consider what a some usual node has in this situation. It has spork messages from signers 1-3 with spork value 1 (and "old" times in nTimeSigned) and messages from signers 4 and 5 with spork value 2 (and "new" times in nTimeSigned). How could this node understand that current spork value is 2, not 1? The only way is to compare nTimeSigned field of all this messages.

@gladcow gladcow changed the title [WIP] M-of-N-like sporks M-of-N-like sporks Sep 13, 2018
@UdjinM6
Copy link

UdjinM6 commented Sep 13, 2018

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 strSporkAddress to mapSporkAddresses <int, string> e.g. [(0, "Xsomeaddr1"), (1, "Xsomeaddr2), ..]. You could then make -sporkaddr accept format like signerid:signeraddr e.g. 0:Xsomeaddr1 and instead of encoding every addr in one string in cmd line you could submit multiple -sporkaddr just like we do for -connect or -addnode. This would also require extending spork message with smth like nSignierID though but I think it should be feasible to implement upgrade logic here.

@nmarley
Copy link

nmarley commented Sep 13, 2018

How could this node understand that current spork value is 2, not 1? The only way is to compare nTimeSigned field of all this messages.

nTimeSigned should be different for all signers. The node uses the spork with the same ID / value for the threshold signers. If the threshold is below majority (e.g. 2 of 5), then it should honor majority signers, e.g. if 2 spork signers send 10004 / 1000, then the other 3 send 10004 / 3000, then the majority signed value should take the "active" slot.

This would also require extending spork message with smth like nSignierID though but I think it should be feasible to implement upgrade logic here.

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:

https://github.com/nmarley/dash/blob/a5b2df7a86da7c7a789b607be5a194ec9ae4abb2/src/spork.cpp#L431

@UdjinM6
Copy link

UdjinM6 commented Sep 13, 2018

@nmarley Ah, good point re pubkey recovery!

@gladcow
Copy link
Author

gladcow commented Sep 13, 2018

nTimeSigned should be different for all signers. The node uses the spork with the same ID / value for the threshold signers. If the threshold is below majority (e.g. 2 of 5), then it should honor majority signers, e.g. if 2 spork signers send 10004 / 1000, then the other 3 send 10004 / 3000, then the majority signed value should take the "active" slot.

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 1000 in 2017 and should become 3000 in 2018. But usual node in 2018 has 5 messages in its memory: 3 messages from 2017 and 2 from 2018, that's why switching to 3000 in 2018 fails.

It is not attempt to simply defend the written code, it is a real problem I've encountered during testing.)
There are 2 ways to resolve this issue: special processing for nTimeSigned field and using of crypto m-of-n threshold signature like @codablock has described in #2221. In the second way the spork sending process for the signers is different from the original process, so I've decided to use the first way.

@nmarley
Copy link

nmarley commented Sep 13, 2018

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 1000 in 2017 and should become 3000 in 2018. But usual node in 2018 has 5 messages in its memory: 3 messages from 2017 and 2 from 2018, that's why switching to 3000 in 2018 fails.

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?

@gladcow
Copy link
Author

gladcow commented Sep 14, 2018

@nmarley , right, it solves the problem, but I've thought that 1-of-N (or 2-of-N at least) case is important for this functionality (we can have emergency need to switch some spork value, so any additional communications among signers can be too slow). Am I wrong? What is the most possible configuration for many signers we plan to use?

@UdjinM6
Copy link

UdjinM6 commented Sep 14, 2018

I'd say let's start with implementing the simplest/smallest possible one aka 2of3 first and see how it goes.

@gladcow
Copy link
Author

gladcow commented Sep 14, 2018

So, to make it clear, what should be changed:

  1. Remove processing of small Ms, M always is greater than N/2.
  2. Remove new processing of the nTimeSigned field, calc current spork value with majority of signers.
  3. Get KeyID from message signature like in @nmarley implementation, not by comparing signature with all signer addresses like in current implementation (I'm using address as signer unique id already, only calculation of the KeyID is different).
  4. Change -setsporkaddress syntax to don't set all addresses in one string.

Have I missed something?

@UdjinM6
Copy link

UdjinM6 commented Sep 14, 2018

@gladcow sounds good (assuming (4) means ", support providing multiple -sporkaddrs instead")

@gladcow gladcow changed the title M-of-N-like sporks [WIP] M-of-N-like sporks Sep 14, 2018
@gladcow gladcow changed the title [WIP] M-of-N-like sporks M-of-N-like sporks Sep 17, 2018
@gladcow
Copy link
Author

gladcow commented Sep 17, 2018

Rebased and fixed, re-review, please.

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about all the nits! See inline

src/init.cpp Outdated
if (!sporkManager.SetSporkAddress(GetArg("-sporkaddr", Params().SporkAddress())))
return InitError(_("Invalid spork address specified with -sporkaddr"));
std::vector<std::string> vSporkAddresses;
if(mapMultiArgs.count("-sporkaddr")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if( -> if (

src/init.cpp Outdated
} else {
vSporkAddresses = Params().SporkAddresses();
}
for(const auto& address: vSporkAddresses) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for( -> for (

}
for(const auto& address: vSporkAddresses) {
if (!sporkManager.SetSporkAddress(address))
return InitError(_("Invalid spork address specified with -sporkaddr"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.


int minsporkkeys = GetArg("-minsporkkeys", Params().MinSporkKeys());
if(!sporkManager.SetMinSporkKeys(minsporkkeys))
return InitError(_("Invalid minimum number of spork signers specified with -minsporkkeys"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, same line or braces. Also if( -> if (

src/spork.cpp Outdated
{
LOCK(cs);
if (!mapSporksActive.count(sporkID))
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

above, same line or braces

src/spork.cpp Outdated
// check if any value has enough signer votes
int max_count = 0;
for (const auto& value_data: value_counts) {
if(value_data.second >= nMinSporkKeys) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if( -> if (

src/spork.cpp Outdated
@@ -55,41 +81,54 @@ void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CD
if(!chainActive.Tip()) return;
strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
}

CKeyID signer;
if(!(spork.GetSignerKeyID(signer, IsSporkActive(SPORK_6_NEW_SIGS))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if( -> if (

src/spork.cpp Outdated
} else {
LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
r = 4070908800ULL; // 2099-1-1 i.e. off by default
if(SporkValueIsActive(nSporkID, r)){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if( -> if (

)){ -> )) {

src/spork.cpp Outdated
if (mapSporksActive.count(nSporkID))
return mapSporksActive[nSporkID].nValue;
int64_t r = -1;
if(SporkValueIsActive(nSporkID, r)){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if( -> if (

)){ -> )) {

src/spork.cpp Outdated
return r < GetAdjustedTime();
}

// grab the value of the spork on the network, or the default
int64_t CSporkManager::GetSporkValue(int nSporkID)
{
LOCK(cs);
if (mapSporksActive.count(nSporkID))
return mapSporksActive[nSporkID].nValue;
int64_t r = -1;
Copy link
Member

@PastaPastaPasta PastaPastaPasta Sep 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a better name ¯\(ツ)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.)

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see inline comments

Test logic for several signer keys usage for spork broadcast.

We set 5 possible keys for sporks signing and set minimum
required signers to 2. We check 1 siger can't set the spork
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be updated to match actual test logic.

# first and second signers set spork value
self.set_test_spork_state(self.nodes[0], 1)
self.set_test_spork_state(self.nodes[1], 1)
# spork change requires at least 2 signers
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

src/spork.cpp Outdated
}

LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
r = 4070908800ULL; // 2099-1-1 i.e. off by default
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to set r here, could just return false instead.

src/spork.h Outdated
CKey sporkPrivKey;

bool SporkValueIsActive(int sporkID, int64_t& activeValue) const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming: nSporkID and nActiveValueRet (same in cpp)

src/spork.cpp Outdated
int max_count = 0;
for (const auto& value_data: value_counts) {
if (value_data.second >= nMinSporkKeys) {
if (value_data.second > max_count) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/spork.cpp Outdated
if (!mapSporksActive.count(sporkID)) return false;

// calc how many values we have and how many signers vote for every value
std::map<int64_t, int> value_counts;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: mapValueCounts

src/spork.cpp Outdated
@@ -55,41 +81,54 @@ void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CD
if(!chainActive.Tip()) return;
strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
}

CKeyID signer;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: keyIDSigner

src/spork.cpp Outdated
}
spork.Relay(connman);

//does a task if needed
ExecuteSpork(spork.nSporkID, spork.nValue);
int64_t activeValue = 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nActiveValue

src/spork.cpp Outdated
@@ -129,10 +168,15 @@ bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime());

if(spork.Sign(sporkPrivKey, IsSporkActive(SPORK_6_NEW_SIGS))) {
CKeyID signer;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keyIDSigner

if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
READWRITE(vchSig);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the hash. Not sure if this is going to work (without being banned), will test.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

if (!(s.GetType() & SER_GETHASH)) {
READWRITE(vchSig);
}
READWRITE(vchSig);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

src/spork.cpp Outdated
{
CPubKey pubkeyFromSig;
if (fSporkSixActive) {
if (!pubkeyFromSig.RecoverCompact(GetHash(), vchSig)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use a (modified) GetSignatureHash() here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.(

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now, will try to fix it this way.)

@gladcow
Copy link
Author

gladcow commented Sep 26, 2018

Rebased and fixed, re-review, please

@gladcow
Copy link
Author

gladcow commented Sep 26, 2018

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)

@thephez
Copy link
Collaborator

thephez commented Sep 26, 2018

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:

https://github.com/nmarley/dash/blob/a5b2df7a86da7c7a789b607be5a194ec9ae4abb2/src/spork.cpp#L431

@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.

@UdjinM6
Copy link

UdjinM6 commented Sep 26, 2018

Pls rebase again and bump CSporkManager::SERIALIZATION_VERSION_STRING

@gladcow
Copy link
Author

gladcow commented Sep 26, 2018

Rebased, serialization version is bumped.

@UdjinM6 UdjinM6 added this to the 12.4 milestone Sep 26, 2018
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

src/spork.cpp Outdated
if (!itActive->second.CheckSignature(sporkPubKeyID, true)) {
mapSporksByHash.erase(itActive->second.GetHash());
mapSporksActive.erase(itActive++);
auto signer_msg_pair = itActive->second.begin();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is confusing here imo, would prefer smth like itSignerPair or smth like that.

src/spork.cpp Outdated
continue;
}
}
signer_msg_pair++;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use pre-increment here i.e. ++signer_msg_pair; (or rather ++itSignerPair; considering the previous comment)

src/spork.cpp Outdated

} else if (strCommand == NetMsgType::GETSPORKS) {
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) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer a bit more self-describing name e.g. signerSporkPair

src/spork.cpp Outdated
{
int maxKeysNumber = setSporkPubKeyIDs.size();
if ((minSporkKeys <= maxKeysNumber / 2) || (minSporkKeys > maxKeysNumber)) {
LogPrintf("CSporkManager::SetSporkAddress -- Invalid min spork signers number: %d\n", minSporkKeys);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/SetSporkAddress /SetMinSporkKeys/

src/spork.cpp Outdated
@@ -88,41 +127,60 @@ void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CD
if(!chainActive.Tip()) return;
strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
}

CKeyID keyIDSigner;
if (!(spork.GetSignerKeyID(keyIDSigner, IsSporkActive(SPORK_6_NEW_SIGS))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer this rewritten via ||

src/spork.cpp Outdated
@@ -162,10 +220,15 @@ bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime());

if(spork.Sign(sporkPrivKey, IsSporkActive(SPORK_6_NEW_SIGS))) {
CKeyID keyIDSigner;
if (!(spork.GetSignerKeyID(keyIDSigner, IsSporkActive(SPORK_6_NEW_SIGS) && setSporkPubKeyIDs.count(keyIDSigner)))) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer this rewritten via ||

@gladcow
Copy link
Author

gladcow commented Sep 27, 2018

Fixed the issues

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly tested ACK

Copy link

@nmarley nmarley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK, 👍 Good work @gladcow !

Seems like an easy transition from legacy to multiple keys, by:

  1. Adding multiple keys by default in 12.4 release (with legacy key being 1 of them) and only requiring 1-of-1
  2. Coordinating and signing sporks with other (non-legacy) keys during that period
  3. 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 ?

@UdjinM6
Copy link

UdjinM6 commented Sep 29, 2018

Not sure I understand it right @nmarley. I was thinking of using another spork to activate new keys smth like

// add new keys to chainparams, bump min required
if (IsSporkActive(SPORK_X_MULTIKEYSPORK) {
    // use chainparams
} else {
    // ignore chainparams, use the first (i.e. legacy) key only and minkey == 1
}

So first activate SPORK_X_MULTIKEYSPORK with the legacy key and then re-sign needed sporks with multiple keys.

@nmarley
Copy link

nmarley commented Sep 29, 2018

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?

@UdjinM6
Copy link

UdjinM6 commented Sep 30, 2018

@nmarley I don't think the logic for spork activation here is compatible with multiple keys + 1-of-1 case. But we could probably "downgrade" the activation code with some hardcoded 1-of-1 backwards compatible logic and a legacy spork address i.e. smth like "if signed by the legacy spork address, assume that's the right value and ignore everything else for now". Is that what you are proposing?

@nmarley
Copy link

nmarley commented Sep 30, 2018

@UdjinM6 Yes Kinda. The more I think of it, the more I don't like trying to shoehorn the legacy spork key into the multiple spork signers logic. But I was thinking something like you suggested, namely:

  1. Look at multi signers logic first. Otherwise:
  2. If legacy spork signed it, then consider it valid.

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.

@nmarley
Copy link

nmarley commented Sep 30, 2018

edit: Actually, not exactly this:

if signed by the legacy spork address, assume that's the right value and ignore everything else for now

More like, prefer multi signer logic first, and then fall back to legacy spork key.

@UdjinM6
Copy link

UdjinM6 commented Sep 30, 2018

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 :)

@UdjinM6 UdjinM6 merged commit f7ab6c4 into dashpay:develop Sep 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants