Skip to content

Commit

Permalink
Merge #2421: [Refactor] MN operator key migration to BLS
Browse files Browse the repository at this point in the history
d3843cd [Refactor] No need to get the publickey of the active masternode (random-zebra)
483f509 [Refactor] De-duplicate operator BLS key parsing in rpc (random-zebra)
855c094 [Build] Fix CMake builds with chiabls/relic includes (random-zebra)
77ec208 [Tests] Fix TierTwo functional tests with BLS operator key (random-zebra)
dd46f72 [TierTwo] Migration of DMN operator key to BLS (random-zebra)
1dfdcc1 [RPC] Implement generateblskeypair() RPC command (random-zebra)
619c0f8 scripted-diff: Rename keyIDOperator to pubKeyOperator (random-zebra)
b0a7fa5 [QA] Add tests for fbv messages signed with BLS keys (random-zebra)
dd0fb01 [TierTwo] Add functions to sign/verify messages with BLS (random-zebra)

Pull request description:

  This builds on top of:

  - [x] #2363
  - [x] #2419
  - [x] #2420

  As per title, introduce BLS keys/signatures for messages signed by the masternode operator (which is a requirement for [DIP 6](https://github.com/dashpay/dips/blob/master/dip-0006.md), and subsequent upgrades).

  - a new RPC command `generateblskeypair` returns a new publickey/secretkey BLS keypair.
  - `CDeterministicMNState::keyIDOperator` is now a `CBLSLazyPublicKey` (instead of a `CKeyID`), and it's renamed `pubKeyOperator`.
  - `-mnoperatorprivatekey` flag takes a BLS secret key in hex format.
  - `CActiveMasternodeInfo` stores a `CBLSPublicKey`/`CBLSSecretKey`
  - the BLS signature byte vector is serialized for legacy messages (mnw, fbv) in the compatibility code.

ACKs for top commit:
  furszy:
    great, ACK d3843cd
  Fuzzbawls:
    ACK d3843cd

Tree-SHA512: 1caf49615283d10b356caa049b57091dc70805c0bf2c11645d560c81223ce0d9936a46096cc4871c5ec25164518ebdf259d0f23a8c5bca39f2e436cc9e954519
  • Loading branch information
furszy committed Oct 8, 2021
2 parents 04015f7 + d3843cd commit 295dabf
Show file tree
Hide file tree
Showing 25 changed files with 627 additions and 319 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ target_include_directories(WALLET_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/secp256k1/include
${CMAKE_CURRENT_SOURCE_DIR}/src/univalue/include
${CMAKE_CURRENT_SOURCE_DIR}/src/leveldb/include
${CMAKE_CURRENT_SOURCE_DIR}/src/chiabls/src
${relic_SOURCE_DIR}/include
${relic_BINARY_DIR}/include
${BerkeleyDB_INCLUDE_DIRS}
)

Expand Down
57 changes: 32 additions & 25 deletions src/activemasternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "activemasternode.h"

#include "addrman.h"
#include "bls/bls_wrapper.h"
#include "evo/providertx.h"
#include "masternode-sync.h"
#include "masternode.h"
Expand Down Expand Up @@ -54,19 +55,19 @@ std::string CActiveDeterministicMasternodeManager::GetStatus() const

OperationResult CActiveDeterministicMasternodeManager::SetOperatorKey(const std::string& strMNOperatorPrivKey)
{

LOCK(cs_main); // Lock cs_main so the node doesn't perform any action while we setup the Masternode
LogPrintf("Initializing deterministic masternode...\n");
if (strMNOperatorPrivKey.empty()) {
return errorOut("ERROR: Masternode operator priv key cannot be empty.");
}
if (!CMessageSigner::GetKeysFromSecret(strMNOperatorPrivKey, info.keyOperator, info.keyIDOperator)) {
if (!info.keyOperator.SetHexStr(strMNOperatorPrivKey)) {
return errorOut(_("Invalid mnoperatorprivatekey. Please see the documentation."));
}
info.pubKeyOperator = info.keyOperator.GetPublicKey();
return OperationResult(true);
}

OperationResult CActiveDeterministicMasternodeManager::GetOperatorKey(CKey& key, CKeyID& keyID, CDeterministicMNCPtr& dmn) const
OperationResult CActiveDeterministicMasternodeManager::GetOperatorKey(CBLSSecretKey& key, CDeterministicMNCPtr& dmn) const
{
if (!IsReady()) {
return errorOut("Active masternode not ready");
Expand All @@ -75,12 +76,11 @@ OperationResult CActiveDeterministicMasternodeManager::GetOperatorKey(CKey& key,
if (!dmn) {
return errorOut(strprintf("Active masternode %s not registered or PoSe banned", info.proTxHash.ToString()));
}
if (info.keyIDOperator != dmn->pdmnState->keyIDOperator) {
if (info.pubKeyOperator != dmn->pdmnState->pubKeyOperator.Get()) {
return errorOut("Active masternode operator key changed or revoked");
}
// return keys
// return key
key = info.keyOperator;
keyID = info.keyIDOperator;
return OperationResult(true);
}

Expand Down Expand Up @@ -119,7 +119,7 @@ void CActiveDeterministicMasternodeManager::Init()

CDeterministicMNList mnList = deterministicMNManager->GetListAtChainTip();

CDeterministicMNCPtr dmn = mnList.GetMNByOperatorKey(info.keyIDOperator);
CDeterministicMNCPtr dmn = mnList.GetMNByOperatorKey(info.pubKeyOperator);
if (!dmn) {
// MN not appeared on the chain yet
return;
Expand Down Expand Up @@ -192,7 +192,7 @@ void CActiveDeterministicMasternodeManager::UpdatedBlockTip(const CBlockIndex* p
return;
}

if (newDmn->pdmnState->keyIDOperator != oldDmn->pdmnState->keyIDOperator) {
if (newDmn->pdmnState->pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
// MN operator key changed or revoked
Reset(MASTERNODE_OPERATOR_KEY_CHANGED);
return;
Expand Down Expand Up @@ -466,32 +466,39 @@ void CActiveMasternode::GetKeys(CKey& _privKeyMasternode, CPubKey& _pubKeyMaster
_pubKeyMasternode = pubKeyMasternode;
}

bool GetActiveMasternodeKeys(CKey& key, CKeyID& keyID, CTxIn& vin)
bool GetActiveDMNKeys(CBLSSecretKey& key, CTxIn& vin)
{
if (activeMasternodeManager == nullptr) {
return error("%s: Active Masternode not initialized", __func__);
}
CDeterministicMNCPtr dmn;
auto res = activeMasternodeManager->GetOperatorKey(key, dmn);
if (!res) {
return error("%s: %s", __func__, res.getError());
}
vin = CTxIn(dmn->collateralOutpoint);
return true;
}

bool GetActiveMasternodeKeys(CTxIn& vin, Optional<CKey>& key, CBLSSecretKey& blsKey)
{
if (activeMasternodeManager != nullptr) {
// deterministic mn
CDeterministicMNCPtr dmn;
auto res = activeMasternodeManager->GetOperatorKey(key, keyID, dmn);
if (!res) {
LogPrint(BCLog::MNBUDGET,"%s: %s\n", __func__, res.getError());
return false;
}
vin = CTxIn(dmn->collateralOutpoint);
return true;
key = nullopt;
return GetActiveDMNKeys(blsKey, vin);
}

// legacy mn
if (activeMasternode.vin == nullopt) {
LogPrint(BCLog::MNBUDGET,"%s: Active Masternode not initialized\n", __func__);
return false;
return error("%s: Active Masternode not initialized", __func__);
}
if (activeMasternode.GetStatus() != ACTIVE_MASTERNODE_STARTED) {
LogPrint(BCLog::MNBUDGET,"%s: MN not started (%s)\n", __func__, activeMasternode.GetStatusMessage());
return false;
return error("%s: MN not started (%s)", __func__, activeMasternode.GetStatusMessage());
}
CPubKey mnPubKey;
activeMasternode.GetKeys(key, mnPubKey);
keyID = mnPubKey.GetID();
vin = *activeMasternode.vin;
CKey sk;
CPubKey pk;
activeMasternode.GetKeys(sk, pk);
key = Optional<CKey>(sk);
blsKey.Reset();
return true;
}
14 changes: 9 additions & 5 deletions src/activemasternode.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "wallet/wallet.h"

class CActiveDeterministicMasternodeManager;
class CBLSPublicKey;
class CBLSSecretKey;

#define ACTIVE_MASTERNODE_INITIAL 0 // initial state
#define ACTIVE_MASTERNODE_SYNC_IN_PROCESS 1
Expand All @@ -28,8 +30,8 @@ extern CActiveDeterministicMasternodeManager* activeMasternodeManager;
struct CActiveMasternodeInfo
{
// Keys for the active Masternode
CKeyID keyIDOperator;
CKey keyOperator;
CBLSPublicKey pubKeyOperator;
CBLSSecretKey keyOperator;
// Initialized while registering Masternode
uint256 proTxHash{UINT256_ZERO};
CService service;
Expand Down Expand Up @@ -63,7 +65,7 @@ class CActiveDeterministicMasternodeManager : public CValidationInterface
OperationResult SetOperatorKey(const std::string& strMNOperatorPrivKey);
// If the active masternode is ready, and the keyID matches with the registered one,
// return private key, keyID, and pointer to dmn.
OperationResult GetOperatorKey(CKey& key, CKeyID& keyID, CDeterministicMNCPtr& dmn) const;
OperationResult GetOperatorKey(CBLSSecretKey& key, CDeterministicMNCPtr& dmn) const;
void SetNullProTx() { info.proTxHash = UINT256_ZERO; }

const CActiveMasternodeInfo* GetInfo() const { return &info; }
Expand Down Expand Up @@ -116,7 +118,9 @@ class CActiveMasternode
void GetKeys(CKey& privKeyMasternode, CPubKey& pubKeyMasternode);
};

// Compatibility code: get keys for either legacy or deterministic masternode
bool GetActiveMasternodeKeys(CKey& key, CKeyID& keyID, CTxIn& vin);
// Compatibility code: get vin and keys for either legacy or deterministic masternode
bool GetActiveMasternodeKeys(CTxIn& vin, Optional<CKey>& key, CBLSSecretKey& blsKey);
// Get active masternode BLS operator keys for DMN
bool GetActiveDMNKeys(CBLSSecretKey& key, CTxIn& vin);

#endif
23 changes: 17 additions & 6 deletions src/budget/budgetmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,10 @@ void CBudgetManager::VoteOnFinalizedBudgets()
}

// Get the active masternode (operator) key
CKey mnKey; CKeyID mnKeyID; CTxIn mnVin;
if (!GetActiveMasternodeKeys(mnKey, mnKeyID, mnVin)) {
CTxIn mnVin;
Optional<CKey> mnKey{nullopt};
CBLSSecretKey blsKey;
if (!GetActiveMasternodeKeys(mnVin, mnKey, blsKey)) {
return;
}

Expand Down Expand Up @@ -543,9 +545,18 @@ void CBudgetManager::VoteOnFinalizedBudgets()
// Sign finalized budgets
for (const uint256& budgetHash: vBudgetHashes) {
CFinalizedBudgetVote vote(mnVin, budgetHash);
if (!vote.Sign(mnKey, mnKeyID)) {
LogPrintf("%s: Failure to sign budget %s", __func__, budgetHash.ToString());
continue;
if (mnKey != nullopt) {
// Legacy MN
if (!vote.Sign(*mnKey, mnKey->GetPubKey().GetID())) {
LogPrintf("%s: Failure to sign budget %s\n", __func__, budgetHash.ToString());
continue;
}
} else {
// DMN
if (!vote.Sign(blsKey)) {
LogPrintf("%s: Failure to sign budget %s with DMN\n", __func__, budgetHash.ToString());
continue;
}
}
std::string strError = "";
if (!UpdateFinalizedBudget(vote, NULL, strError)) {
Expand Down Expand Up @@ -1110,7 +1121,7 @@ bool CBudgetManager::ProcessFinalizedBudgetVote(CFinalizedBudgetVote& vote, CNod
if (dmn) {
const std::string& mn_protx_id = dmn->proTxHash.ToString();

if (!vote.CheckSignature(dmn->pdmnState->keyIDOperator)) {
if (!vote.CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) {
err = strprintf("invalid fbvote sig from dmn: %s", mn_protx_id);
return state.DoS(100, false, REJECT_INVALID, "bad-fbvote-sig", false, err);
}
Expand Down
30 changes: 15 additions & 15 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ std::string CDeterministicMNState::ToString() const
operatorPayoutAddress = EncodeDestination(dest);
}

return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, ownerAddress=%s, operatorAddress=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)",
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, ownerAddress=%s, operatorPubKey=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)",
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
EncodeDestination(keyIDOwner), EncodeDestination(keyIDOperator), EncodeDestination(keyIDVoting), addr.ToStringIPPort(), payoutAddress, operatorPayoutAddress);
EncodeDestination(keyIDOwner), pubKeyOperator.Get().ToString(), EncodeDestination(keyIDVoting), addr.ToStringIPPort(), payoutAddress, operatorPayoutAddress);
}

void CDeterministicMNState::ToJson(UniValue& obj) const
Expand All @@ -54,7 +54,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const
obj.pushKV("PoSeBanHeight", nPoSeBanHeight);
obj.pushKV("revocationReason", nRevocationReason);
obj.pushKV("ownerAddress", EncodeDestination(keyIDOwner));
obj.pushKV("operatorAddress", keyIDOperator == CKeyID() ? "" : EncodeDestination(keyIDOperator));
obj.pushKV("operatorPubKey", pubKeyOperator.Get().ToString());
obj.pushKV("votingAddress", EncodeDestination(keyIDVoting));

CTxDestination dest1;
Expand Down Expand Up @@ -122,10 +122,10 @@ CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash)
return dmn;
}

CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CKeyID& keyID)
CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CBLSPublicKey& pubKey)
{
for (const auto& p : mnMap) {
if (p.second->pdmnState->keyIDOperator == keyID) {
if (p.second->pdmnState->pubKeyOperator.Get() == pubKey) {
return p.second;
}
}
Expand Down Expand Up @@ -396,8 +396,8 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
if (HasUniqueProperty(dmn->pdmnState->addr)) {
throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate address %s", __func__, dmn->pdmnState->addr.ToStringIPPort())));
}
if (HasUniqueProperty(dmn->pdmnState->keyIDOwner) || HasUniqueProperty(dmn->pdmnState->keyIDOperator)) {
throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate key (%s or %s)", __func__, EncodeDestination(dmn->pdmnState->keyIDOwner), EncodeDestination(dmn->pdmnState->keyIDOperator))));
if (HasUniqueProperty(dmn->pdmnState->keyIDOwner) || HasUniqueProperty(dmn->pdmnState->pubKeyOperator)) {
throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate key (%s or %s)", __func__, EncodeDestination(dmn->pdmnState->keyIDOwner), dmn->pdmnState->pubKeyOperator.Get().ToString())));
}

mnMap = mnMap.set(dmn->proTxHash, dmn);
Expand All @@ -407,7 +407,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
AddUniqueProperty(dmn, dmn->pdmnState->addr);
}
AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
AddUniqueProperty(dmn, dmn->pdmnState->keyIDOperator);
AddUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);

if (fBumpTotalCount) {
// nTotalRegisteredCount acts more like a checkpoint, not as a limit,
Expand All @@ -430,7 +430,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMNCPtr& oldDmn, const CD

UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr);
UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner);
UpdateUniqueProperty(dmn, oldState->keyIDOperator, pdmnState->keyIDOperator);
UpdateUniqueProperty(dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator);
}

void CDeterministicMNList::UpdateMN(const uint256& proTxHash, const CDeterministicMNStateCPtr& pdmnState)
Expand Down Expand Up @@ -462,7 +462,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
DeleteUniqueProperty(dmn, dmn->pdmnState->addr);
}
DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOperator);
DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);

mnMap = mnMap.erase(proTxHash);
mnInternalIdMap = mnInternalIdMap.erase(dmn->GetInternalId());
Expand Down Expand Up @@ -657,7 +657,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
if (newList.HasUniqueProperty(pl.keyIDOwner)) {
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-owner-key");
}
if (newList.HasUniqueProperty(pl.keyIDOperator)) {
if (newList.HasUniqueProperty(pl.pubKeyOperator)) {
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key");
}

Expand Down Expand Up @@ -702,7 +702,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C

if (newState->nPoSeBanHeight != -1) {
// only revive when all keys are set
if (!newState->keyIDOperator.IsNull() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
if (newState->pubKeyOperator.Get().IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
newState->nPoSePenalty = 0;
newState->nPoSeBanHeight = -1;
newState->nPoSeRevivedHeight = nHeight;
Expand Down Expand Up @@ -730,16 +730,16 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
if (newList.HasUniqueProperty(pl.keyIDOperator) && newList.GetUniquePropertyMN(pl.keyIDOperator)->proTxHash != pl.proTxHash) {
if (newList.HasUniqueProperty(pl.pubKeyOperator) && newList.GetUniquePropertyMN(pl.pubKeyOperator)->proTxHash != pl.proTxHash) {
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->keyIDOperator != pl.keyIDOperator) {
if (newState->pubKeyOperator.Get() != pl.pubKeyOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
}
newState->keyIDOperator = pl.keyIDOperator;
newState->pubKeyOperator.Set(pl.pubKeyOperator);
newState->keyIDVoting = pl.keyIDVoting;
newState->scriptPayout = pl.scriptPayout;

Expand Down
15 changes: 8 additions & 7 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define PIVX_DETERMINISTICMNS_H

#include "arith_uint256.h"
#include "bls/bls_wrapper.h"
#include "dbwrapper.h"
#include "evo/evodb.h"
#include "evo/providertx.h"
Expand Down Expand Up @@ -39,7 +40,7 @@ class CDeterministicMNState
uint256 confirmedHashWithProRegTxHash;

CKeyID keyIDOwner;
CKeyID keyIDOperator;
CBLSLazyPublicKey pubKeyOperator;
CKeyID keyIDVoting;
CService addr;
CScript scriptPayout;
Expand All @@ -50,7 +51,7 @@ class CDeterministicMNState
explicit CDeterministicMNState(const ProRegPL& pl)
{
keyIDOwner = pl.keyIDOwner;
keyIDOperator = pl.keyIDOperator;
pubKeyOperator.Set(pl.pubKeyOperator);
keyIDVoting = pl.keyIDVoting;
addr = pl.addr;
scriptPayout = pl.scriptPayout;
Expand All @@ -70,7 +71,7 @@ class CDeterministicMNState
READWRITE(obj.confirmedHash);
READWRITE(obj.confirmedHashWithProRegTxHash);
READWRITE(obj.keyIDOwner);
READWRITE(obj.keyIDOperator);
READWRITE(obj.pubKeyOperator);
READWRITE(obj.keyIDVoting);
READWRITE(obj.addr);
READWRITE(obj.scriptPayout);
Expand All @@ -79,7 +80,7 @@ class CDeterministicMNState

void ResetOperatorFields()
{
keyIDOperator = CKeyID();
pubKeyOperator.Set(CBLSPublicKey());
addr = CService();
scriptOperatorPayout = CScript();
nRevocationReason = ProUpRevPL::REASON_NOT_SPECIFIED;
Expand Down Expand Up @@ -119,7 +120,7 @@ class CDeterministicMNStateDiff
Field_confirmedHash = 0x0040,
Field_confirmedHashWithProRegTxHash = 0x0080,
Field_keyIDOwner = 0x0100,
Field_keyIDOperator = 0x0200,
Field_pubKeyOperator = 0x0200,
Field_keyIDVoting = 0x0400,
Field_addr = 0x0800,
Field_scriptPayout = 0x1000,
Expand All @@ -136,7 +137,7 @@ class CDeterministicMNStateDiff
DMN_STATE_DIFF_LINE(confirmedHash) \
DMN_STATE_DIFF_LINE(confirmedHashWithProRegTxHash) \
DMN_STATE_DIFF_LINE(keyIDOwner) \
DMN_STATE_DIFF_LINE(keyIDOperator) \
DMN_STATE_DIFF_LINE(pubKeyOperator) \
DMN_STATE_DIFF_LINE(keyIDVoting) \
DMN_STATE_DIFF_LINE(addr) \
DMN_STATE_DIFF_LINE(scriptPayout) \
Expand Down Expand Up @@ -360,7 +361,7 @@ class CDeterministicMNList
}
CDeterministicMNCPtr GetMN(const uint256& proTxHash) const;
CDeterministicMNCPtr GetValidMN(const uint256& proTxHash) const;
CDeterministicMNCPtr GetMNByOperatorKey(const CKeyID& keyID);
CDeterministicMNCPtr GetMNByOperatorKey(const CBLSPublicKey& pubKey);
CDeterministicMNCPtr GetMNByCollateral(const COutPoint& collateralOutpoint) const;
CDeterministicMNCPtr GetValidMNByCollateral(const COutPoint& collateralOutpoint) const;
CDeterministicMNCPtr GetMNByService(const CService& service) const;
Expand Down
Loading

0 comments on commit 295dabf

Please sign in to comment.