diff --git a/CMakeLists.txt b/CMakeLists.txt index e320cf9d1e556..9390797978e69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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} ) diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index e43c2fb0932d0..72814f5c14b11 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -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" @@ -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"); @@ -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); } @@ -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; @@ -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; @@ -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& 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(sk); + blsKey.Reset(); return true; } diff --git a/src/activemasternode.h b/src/activemasternode.h index c85381b3c233b..8a67e939a9811 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -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 @@ -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; @@ -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; } @@ -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& key, CBLSSecretKey& blsKey); +// Get active masternode BLS operator keys for DMN +bool GetActiveDMNKeys(CBLSSecretKey& key, CTxIn& vin); #endif diff --git a/src/budget/budgetmanager.cpp b/src/budget/budgetmanager.cpp index e9cd28cb7c720..2c388fdfed998 100644 --- a/src/budget/budgetmanager.cpp +++ b/src/budget/budgetmanager.cpp @@ -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 mnKey{nullopt}; + CBLSSecretKey blsKey; + if (!GetActiveMasternodeKeys(mnVin, mnKey, blsKey)) { return; } @@ -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)) { @@ -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); } diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index a535372258aa3..60b1226df60b2 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -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 @@ -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; @@ -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; } } @@ -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); @@ -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, @@ -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) @@ -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()); @@ -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"); } @@ -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; @@ -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(*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; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index a761a6d48f918..1609aa9cae849 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -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" @@ -39,7 +40,7 @@ class CDeterministicMNState uint256 confirmedHashWithProRegTxHash; CKeyID keyIDOwner; - CKeyID keyIDOperator; + CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; CService addr; CScript scriptPayout; @@ -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; @@ -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); @@ -79,7 +80,7 @@ class CDeterministicMNState void ResetOperatorFields() { - keyIDOperator = CKeyID(); + pubKeyOperator.Set(CBLSPublicKey()); addr = CService(); scriptOperatorPayout = CScript(); nRevocationReason = ProUpRevPL::REASON_NOT_SPECIFIED; @@ -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, @@ -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) \ @@ -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; diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 5016b403846d9..4f098ca3c8891 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -53,6 +53,15 @@ static bool CheckHashSig(const Payload& pl, const CKeyID& keyID, CValidationStat return true; } +template +static bool CheckHashSig(const Payload& pl, const CBLSPublicKey& pubKey, CValidationState& state) +{ + if (!pl.sig.VerifyInsecure(pubKey, ::SerializeHash(pl))) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false); + } + return true; +} + template static bool CheckStringSig(const Payload& pl, const CKeyID& keyID, CValidationState& state) { @@ -83,8 +92,7 @@ static bool CheckCollateralOut(const CTxOut& out, const ProRegPL& pl, CValidatio // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) // this check applies to internal and external collateral, but internal collaterals are not necessarely a P2PKH if (collateralDestRet == CTxDestination(pl.keyIDOwner) || - collateralDestRet == CTxDestination(pl.keyIDVoting) || - collateralDestRet == CTxDestination(pl.keyIDOperator)) { + collateralDestRet == CTxDestination(pl.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); } // check collateral amount @@ -113,9 +121,12 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); } - if (pl.keyIDOwner.IsNull() || pl.keyIDOperator.IsNull() || pl.keyIDVoting.IsNull()) { + if (pl.keyIDOwner.IsNull() || pl.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } + if (!pl.pubKeyOperator.IsValid()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-key-invalid"); + } // we may support other kinds of scripts later, but restrict it for now if (!pl.scriptPayout.IsPayToPublicKeyHash()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); @@ -131,8 +142,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid } // don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(pl.keyIDOwner) || - payoutDest == CTxDestination(pl.keyIDVoting) || - payoutDest == CTxDestination(pl.keyIDOperator)) { + payoutDest == CTxDestination(pl.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); } @@ -202,7 +212,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid if (mnList.HasUniqueProperty(pl.keyIDOwner)) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-owner-key"); } - if (mnList.HasUniqueProperty(pl.keyIDOperator)) { + if (mnList.HasUniqueProperty(pl.pubKeyOperator)) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key"); } } @@ -230,8 +240,8 @@ std::string ProRegPL::ToString() const CTxDestination dest; std::string payee = ExtractDestination(scriptPayout, dest) ? EncodeDestination(dest) : "unknown"; - return strprintf("ProRegPL(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, operatorAddress=%s, votingAddress=%s, scriptPayout=%s)", - nVersion, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(keyIDOwner), EncodeDestination(keyIDOperator), EncodeDestination(keyIDVoting), payee); + return strprintf("ProRegPL(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, operatorPubKey=%s, votingAddress=%s, scriptPayout=%s)", + nVersion, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(keyIDOwner), pubKeyOperator.ToString(), EncodeDestination(keyIDVoting), payee); } void ProRegPL::ToJson(UniValue& obj) const @@ -243,7 +253,7 @@ void ProRegPL::ToJson(UniValue& obj) const obj.pushKV("collateralIndex", (int)collateralOutpoint.n); obj.pushKV("service", addr.ToString()); obj.pushKV("ownerAddress", EncodeDestination(keyIDOwner)); - obj.pushKV("operatorAddress", EncodeDestination(keyIDOperator)); + obj.pushKV("operatorPubKey", pubKeyOperator.ToString()); obj.pushKV("votingAddress", EncodeDestination(keyIDVoting)); CTxDestination dest1; @@ -307,7 +317,7 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa } // we can only check the signature if pindexPrev != nullptr and the MN is known - if (!CheckHashSig(pl, mn->pdmnState->keyIDOperator, state)) { + if (!CheckHashSig(pl, mn->pdmnState->pubKeyOperator.Get(), state)) { // pass the state returned by the function above return false; } @@ -357,8 +367,8 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); } - if (pl.keyIDOperator.IsNull()) { - return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-key-null"); + if (!pl.pubKeyOperator.IsValid()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-key-invalid"); } if (pl.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-voting-key-null"); @@ -375,7 +385,7 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal } // don't allow reuse of payee key for other keys - if (payoutDest == CTxDestination(pl.keyIDVoting) || payoutDest == CTxDestination(pl.keyIDOperator)) { + if (payoutDest == CTxDestination(pl.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); } @@ -413,13 +423,12 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest"); } if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || - collateralTxDest == CTxDestination(pl.keyIDVoting) || - collateralTxDest == CTxDestination(pl.keyIDOperator)) { + collateralTxDest == CTxDestination(pl.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); } - if (mnList.HasUniqueProperty(pl.keyIDOperator)) { - auto otherDmn = mnList.GetUniquePropertyMN(pl.keyIDOperator); + if (mnList.HasUniqueProperty(pl.pubKeyOperator)) { + auto otherDmn = mnList.GetUniquePropertyMN(pl.pubKeyOperator); if (pl.proTxHash != otherDmn->proTxHash) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); } @@ -440,8 +449,8 @@ std::string ProUpRegPL::ToString() const CTxDestination dest; std::string payee = ExtractDestination(scriptPayout, dest) ? EncodeDestination(dest) : "unknown"; - return strprintf("ProUpRegPL(nVersion=%d, proTxHash=%s, operatorAddress=%s, votingAddress=%s, payoutAddress=%s)", - nVersion, proTxHash.ToString(), EncodeDestination(keyIDOperator), EncodeDestination(keyIDVoting), payee); + return strprintf("ProUpRegPL(nVersion=%d, proTxHash=%s, operatorPubKey=%s, votingAddress=%s, payoutAddress=%s)", + nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(keyIDVoting), payee); } void ProUpRegPL::ToJson(UniValue& obj) const @@ -455,7 +464,7 @@ void ProUpRegPL::ToJson(UniValue& obj) const if (ExtractDestination(scriptPayout, dest)) { obj.pushKV("payoutAddress", EncodeDestination(dest)); } - obj.pushKV("operatorAddress", EncodeDestination(keyIDOperator)); + obj.pushKV("operatorPubKey", pubKeyOperator.ToString()); obj.pushKV("inputsHash", inputsHash.ToString()); } @@ -491,7 +500,7 @@ bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal if (!dmn) return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); - if (!CheckHashSig(pl, dmn->pdmnState->keyIDOperator, state)) { + if (!CheckHashSig(pl, dmn->pdmnState->pubKeyOperator.Get(), state)) { // pass the state returned by the function above return false; } diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 058d0c81d9551..8dd6c06b20aa2 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -5,6 +5,7 @@ #ifndef PIVX_PROVIDERTX_H #define PIVX_PROVIDERTX_H +#include "bls/bls_wrapper.h" #include "primitives/transaction.h" #include "consensus/validation.h" @@ -29,7 +30,7 @@ class ProRegPL COutPoint collateralOutpoint{UINT256_ZERO, (uint32_t)-1}; // if hash is null, we refer to a ProRegTx output CService addr; CKeyID keyIDOwner; - CKeyID keyIDOperator; + CBLSPublicKey pubKeyOperator; CKeyID keyIDVoting; CScript scriptPayout; uint16_t nOperatorReward{0}; @@ -47,7 +48,7 @@ class ProRegPL READWRITE(obj.collateralOutpoint); READWRITE(obj.addr); READWRITE(obj.keyIDOwner); - READWRITE(obj.keyIDOperator); + READWRITE(obj.pubKeyOperator); READWRITE(obj.keyIDVoting); READWRITE(obj.scriptPayout); READWRITE(obj.nOperatorReward); @@ -79,14 +80,14 @@ class ProUpServPL CService addr; CScript scriptOperatorPayout; uint256 inputsHash; // replay protection - std::vector vchSig; + CBLSSignature sig; public: SERIALIZE_METHODS(ProUpServPL, obj) { READWRITE(obj.nVersion, obj.proTxHash, obj.addr, obj.scriptOperatorPayout, obj.inputsHash); if (!(s.GetType() & SER_GETHASH)) { - READWRITE(obj.vchSig); + READWRITE(obj.sig); } } @@ -105,7 +106,7 @@ class ProUpRegPL uint16_t nVersion{CURRENT_VERSION}; // message version uint256 proTxHash; uint16_t nMode{0}; // only 0 supported for now - CKeyID keyIDOperator; + CBLSPublicKey pubKeyOperator; CKeyID keyIDVoting; CScript scriptPayout; uint256 inputsHash; // replay protection @@ -114,7 +115,7 @@ class ProUpRegPL public: SERIALIZE_METHODS(ProUpRegPL, obj) { - READWRITE(obj.nVersion, obj.proTxHash, obj.nMode, obj.keyIDOperator, obj.keyIDVoting, obj.scriptPayout, obj.inputsHash); + READWRITE(obj.nVersion, obj.proTxHash, obj.nMode, obj.pubKeyOperator, obj.keyIDVoting, obj.scriptPayout, obj.inputsHash); if (!(s.GetType() & SER_GETHASH)) { READWRITE(obj.vchSig); } @@ -145,14 +146,14 @@ class ProUpRevPL uint256 proTxHash; uint16_t nReason{REASON_NOT_SPECIFIED}; uint256 inputsHash; // replay protection - std::vector vchSig; + CBLSSignature sig; public: SERIALIZE_METHODS(ProUpRevPL, obj) { READWRITE(obj.nVersion, obj.proTxHash, obj.nReason, obj.inputsHash); if (!(s.GetType() & SER_GETHASH)) { - READWRITE(obj.vchSig); + READWRITE(obj.sig); } } diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index e98249afff8e9..82cc5a672038b 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -473,46 +473,37 @@ void CMasternodePayments::ProcessMessageMasternodePayments(CNode* pfrom, std::st } // See if this winner was signed with a dmn or a legacy masternode - bool fDeterministic{false}; - Optional mnKeyID = nullopt; + bool is_valid_sig = false; auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(winner.vinMasternode.prevout); if (dmn) { - fDeterministic = true; - mnKeyID = Optional(dmn->pdmnState->keyIDOperator); + // DMN: Check BLS signature + is_valid_sig = winner.CheckSignature(dmn->pdmnState->pubKeyOperator.Get()); } else { + // Legacy masternode const CMasternode* pmn = mnodeman.Find(winner.vinMasternode.prevout); - if (pmn) { - mnKeyID = Optional(pmn->pubKeyMasternode.GetID()); - } - } - - // Check not found MN - if (mnKeyID == nullopt) { - // If it's a DMN, the node is misbehaving. - if (fDeterministic) { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), 20); - } else { - // If it's not a DMN, then it could be a non-synced MN. - // Let's try to request the MN to the node. - // (the AskForMN() will only broadcast it every 10 min). + if (pmn == nullptr) { + // it could be a non-synced masternode. ask for the mnb + LogPrint(BCLog::MASTERNODE, "mnw - unknown masternode %s\n", winner.vinMasternode.prevout.hash.ToString()); mnodeman.AskForMN(pfrom, winner.vinMasternode); + return; } - return; + is_valid_sig = winner.CheckSignature(pmn->pubKeyMasternode.GetID()); } - if (!winner.CheckSignature(*mnKeyID)) { + if (!is_valid_sig) { LogPrint(BCLog::MASTERNODE, "%s : mnw - invalid signature\n", __func__); LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return; } - if (masternodePayments.AddWinningMasternode(winner)) { - winner.Relay(); - masternodeSync.AddedMasternodeWinner(winner.GetHash()); + if (!masternodePayments.AddWinningMasternode(winner)) { + return; } + + winner.Relay(); + masternodeSync.AddedMasternodeWinner(winner.GetHash()); } } @@ -719,6 +710,8 @@ void CMasternodePayments::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB void CMasternodePayments::ProcessBlock(int nBlockHeight) { + LogPrintf("%s: Processing block %d\n", __func__, nBlockHeight); + // No more mnw messages after transition to DMN if (deterministicMNManager->LegacyMNObsolete()) { return; @@ -726,8 +719,10 @@ void CMasternodePayments::ProcessBlock(int nBlockHeight) if (!fMasterNode) return; // Get the active masternode (operator) key - CKey mnKey; CKeyID mnKeyID; CTxIn mnVin; - if (!GetActiveMasternodeKeys(mnKey, mnKeyID, mnVin)) { + CTxIn mnVin; + Optional mnKey{nullopt}; + CBLSSecretKey blsKey; + if (!GetActiveMasternodeKeys(mnVin, mnKey, blsKey)) { return; } @@ -735,12 +730,12 @@ void CMasternodePayments::ProcessBlock(int nBlockHeight) int n = mnodeman.GetMasternodeRank(mnVin, nBlockHeight - 100); if (n == -1) { - LogPrint(BCLog::MASTERNODE, "CMasternodePayments::ProcessBlock - Unknown Masternode\n"); + LogPrintf("%s: ERROR: active masternode is not registered yet\n", __func__); return; } if (n > MNPAYMENTS_SIGNATURES_TOTAL) { - LogPrint(BCLog::MASTERNODE, "CMasternodePayments::ProcessBlock - Masternode not in the top %d (%d)\n", MNPAYMENTS_SIGNATURES_TOTAL, n); + LogPrintf("%s: active masternode not in the top %d (%d)\n", __func__, MNPAYMENTS_SIGNATURES_TOTAL, n); return; } @@ -756,20 +751,30 @@ void CMasternodePayments::ProcessBlock(int nBlockHeight) MasternodeRef pmn = mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, true, nCount); if (pmn == nullptr) { - LogPrint(BCLog::MASTERNODE,"%s: Failed to find masternode to pay\n", __func__); + LogPrintf("%s: Failed to find masternode to pay\n", __func__); return; } CMasternodePaymentWinner newWinner(mnVin, nBlockHeight); newWinner.AddPayee(pmn->GetPayeeScript()); - if (!newWinner.Sign(mnKey, mnKeyID)) { - LogPrintf("%s: Failed to sign masternode winner\n", __func__); - return; + if (mnKey != nullopt) { + // Legacy MN + if (!newWinner.Sign(*mnKey, mnKey->GetPubKey().GetID())) { + LogPrintf("%s: Failed to sign masternode winner\n", __func__); + return; + } + } else { + // DMN + if (!newWinner.Sign(blsKey)) { + LogPrintf("%s: Failed to sign masternode winner with DMN\n", __func__); + return; + } } if (!AddWinningMasternode(newWinner)) { return; } newWinner.Relay(); + LogPrintf("%s: Relayed winner %s\n", __func__, newWinner.GetHash().ToString()); nLastBlockHeight = nBlockHeight; } diff --git a/src/messagesigner.cpp b/src/messagesigner.cpp index 0d2c667d09974..507c392bac421 100644 --- a/src/messagesigner.cpp +++ b/src/messagesigner.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "bls/bls_wrapper.h" #include "hash.h" #include "key_io.h" #include "messagesigner.h" @@ -45,6 +46,11 @@ bool CMessageSigner::SignMessage(const std::string& strMessage, std::vector& vchSigRet, const CBLSSecretKey& key) +{ + return CHashSigner::SignHash(GetMessageHash(strMessage), key, vchSigRet); +} + bool CMessageSigner::VerifyMessage(const CPubKey& pubkey, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet) { return VerifyMessage(pubkey.GetID(), vchSig, strMessage, strErrorRet); @@ -55,11 +61,25 @@ bool CMessageSigner::VerifyMessage(const CKeyID& keyID, const std::vector& vchSig, const std::string& strMessage) +{ + return CHashSigner::VerifyHash(GetMessageHash(strMessage), pk, vchSig); +} + bool CHashSigner::SignHash(const uint256& hash, const CKey& key, std::vector& vchSigRet) { return key.SignCompact(hash, vchSigRet); } +bool CHashSigner::SignHash(const uint256& hash, const CBLSSecretKey& key, std::vector& vchSigRet) +{ + if (!key.IsValid()) { + return false; + } + vchSigRet = key.Sign(hash).ToByteVector(); + return true; +} + bool CHashSigner::VerifyHash(const uint256& hash, const CPubKey& pubkey, const std::vector& vchSig, std::string& strErrorRet) { return VerifyHash(hash, pubkey.GetID(), vchSig, strErrorRet); @@ -83,6 +103,11 @@ bool CHashSigner::VerifyHash(const uint256& hash, const CKeyID& keyID, const std return true; } +bool CHashSigner::VerifyHash(const uint256& hash, const CBLSPublicKey& pk, const std::vector& vchSig) +{ + return CBLSSignature(vchSig).VerifyInsecure(pk, hash); +} + /** CSignedMessage Class * Functions inherited by network signed-messages */ @@ -116,6 +141,17 @@ bool CSignedMessage::Sign(const std::string strSignKey) return Sign(key, pubkey.GetID()); } +bool CSignedMessage::Sign(const CBLSSecretKey& sk) +{ + nMessVersion = MessageVersion::MESS_VER_HASH; + + if(!CHashSigner::SignHash(GetSignatureHash(), sk, vchSig)) { + return error("%s : SignHash() failed", __func__); + } + + return true; +} + bool CSignedMessage::CheckSignature(const CKeyID& keyID) const { std::string strError = ""; @@ -129,6 +165,16 @@ bool CSignedMessage::CheckSignature(const CKeyID& keyID) const return CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError); } +bool CSignedMessage::CheckSignature(const CBLSPublicKey& pk) const +{ + // Only MESS_VER_HASH allowed + if (nMessVersion != MessageVersion::MESS_VER_HASH) { + return false; + } + + return CHashSigner::VerifyHash(GetSignatureHash(), pk, vchSig); +} + std::string CSignedMessage::GetSignatureBase64() const { return EncodeBase64(vchSig); diff --git a/src/messagesigner.h b/src/messagesigner.h index ceef3f9095b35..41784991b9c2c 100644 --- a/src/messagesigner.h +++ b/src/messagesigner.h @@ -7,6 +7,10 @@ #define MESSAGESIGNER_H #include "key.h" +#include "primitives/transaction.h" // for CTxIn + +class CBLSPublicKey; +class CBLSSecretKey; enum MessageVersion { MESS_VER_STRMESS = 0, // old format @@ -25,10 +29,14 @@ class CMessageSigner static uint256 GetMessageHash(const std::string& strMessage); /// Sign the message, returns true if successful static bool SignMessage(const std::string& strMessage, std::vector& vchSigRet, const CKey& key); + /// Sign the message with BLS key, returns true if successful + static bool SignMessage(const std::string& strMessage, std::vector& vchSigRet, const CBLSSecretKey& key); /// Verify the message signature, returns true if successful static bool VerifyMessage(const CPubKey& pubkey, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet); /// Verify the message signature, returns true if successful static bool VerifyMessage(const CKeyID& keyID, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet); + /// Verify the message BLS signature, returns true if successful + static bool VerifyMessage(const CBLSPublicKey& pk, const std::vector& vchSig, const std::string& strMessage); }; /** Helper class for signing hashes and checking their signatures @@ -38,14 +46,19 @@ class CHashSigner public: /// Sign the hash, returns true if successful static bool SignHash(const uint256& hash, const CKey& key, std::vector& vchSigRet); + /// Sign the hash with BLS key, returns true if successful + static bool SignHash(const uint256& hash, const CBLSSecretKey& key, std::vector& vchSigRet); /// Verify the hash signature, returns true if successful static bool VerifyHash(const uint256& hash, const CPubKey& pubkey, const std::vector& vchSig, std::string& strErrorRet); /// Verify the hash signature, returns true if successful static bool VerifyHash(const uint256& hash, const CKeyID& keyID, const std::vector& vchSig, std::string& strErrorRet); + /// Verify the hash BLS signature, returns true if successful + static bool VerifyHash(const uint256& hash, const CBLSPublicKey& pk, const std::vector& vchSig); }; /** Base Class for all signed messages on the network */ + class CSignedMessage { protected: @@ -74,6 +87,10 @@ class CSignedMessage void SetVchSig(const std::vector& vchSigIn) { vchSig = vchSigIn; } std::vector GetVchSig() const { return vchSig; } std::string GetSignatureBase64() const; + + // Sign-Verify with BLS + bool Sign(const CBLSSecretKey& sk); + bool CheckSignature(const CBLSPublicKey& pk) const; }; #endif diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index bd2e0ac3b05c1..ff44c92b3c83d 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -177,6 +177,9 @@ target_include_directories(qt_stuff PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/leveldb/include ${CMAKE_SOURCE_DIR}/src/univalue/include ${CMAKE_SOURCE_DIR}/src/secp256k1/include + ${CMAKE_SOURCE_DIR}/src/chiabls/src + ${relic_SOURCE_DIR}/include + ${relic_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/pivx ${CMAKE_CURRENT_SOURCE_DIR}/pivx/settings diff --git a/src/rpc/budget.cpp b/src/rpc/budget.cpp index 8b235608e50f5..c5d2a9d51923b 100644 --- a/src/rpc/budget.cpp +++ b/src/rpc/budget.cpp @@ -247,13 +247,31 @@ struct MnKeyData { std::string mnAlias; const COutPoint* collateralOut; - CKey key; + MnKeyData() = delete; MnKeyData(const std::string& _mnAlias, const COutPoint* _collateralOut, const CKey& _key): mnAlias(_mnAlias), collateralOut(_collateralOut), - key(_key) + key(_key), + use_bls(false) + {} + MnKeyData(const std::string& _mnAlias, const COutPoint* _collateralOut, const CBLSSecretKey& _key): + mnAlias(_mnAlias), + collateralOut(_collateralOut), + blsKey(_key), + use_bls(true) {} + + bool Sign(CSignedMessage* msg) const + { + return use_bls ? msg->Sign(blsKey) + : msg->Sign(key, key.GetPubKey().GetID()); + } + +private: + CKey key; + CBLSSecretKey blsKey; + bool use_bls; // whether to use a CKey (mbv) or blsKey (fbv, mnw) to sign }; typedef std::list mnKeyList; @@ -264,7 +282,7 @@ static UniValue voteProposal(const uint256& propHash, const CBudgetVote::VoteDir int success = 0; for (const auto& k : mnKeys) { CBudgetVote vote(CTxIn(*k.collateralOut), propHash, nVote); - if (!vote.Sign(k.key, k.key.GetPubKey().GetID())) { + if (!k.Sign(&vote)) { resultsObj.push_back(packErrorRetStatus(k.mnAlias, "Failure to sign.")); failed++; continue; @@ -288,7 +306,7 @@ static UniValue voteFinalBudget(const uint256& budgetHash, int success = 0; for (const auto& k : mnKeys) { CFinalizedBudgetVote vote(CTxIn(*k.collateralOut), budgetHash); - if (!vote.Sign(k.key, k.key.GetPubKey().GetID())) { + if (!k.Sign(&vote)) { resultsObj.push_back(packErrorRetStatus(k.mnAlias, "Failure to sign.")); failed++; continue; @@ -352,7 +370,7 @@ static mnKeyList getMNKeysForActiveMasternode(UniValue& resultsObj) } // Deterministic masternodes -static mnKeyList getDMNKeys(CWallet* const pwallet, const Optional& mnAliasFilter, bool fFinal, UniValue& resultsObj, int& failed) +static mnKeyList getDMNVotingKeys(CWallet* const pwallet, const Optional& mnAliasFilter, bool fFinal, UniValue& resultsObj, int& failed) { if (!pwallet) { throw JSONRPCError(RPC_IN_WARMUP, "Wallet (with voting key) not found."); @@ -368,7 +386,7 @@ static mnKeyList getDMNKeys(CWallet* const pwallet, const Optional& if (!mnFilter) { resultsObj.push_back(packErrorRetStatus(*mnAliasFilter, "Invalid or unknown proTxHash")); failed++; - return mnKeyList(); + return {}; } } @@ -379,13 +397,19 @@ static mnKeyList getDMNKeys(CWallet* const pwallet, const Optional& mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { bool filtered = mnFilter && dmn->proTxHash == mnFilter->proTxHash; if (!mnFilter || filtered) { - const CKeyID& mnKeyID = fFinal ? dmn->pdmnState->keyIDOperator : dmn->pdmnState->keyIDVoting; + if (fFinal) { + // We should never get here. BLS operator key (for active mn) is needed. + throw JSONRPCError(RPC_MISC_ERROR, "Finalized budget voting is allowed only locally, from the masternode"); + } + // Get voting key from the wallet CKey mnKey; - if (pwallet->GetKey(mnKeyID, mnKey)) { + if (pwallet->GetKey(dmn->pdmnState->keyIDVoting, mnKey)) { mnKeys.emplace_back(dmn->proTxHash.ToString(), &dmn->collateralOutpoint, mnKey); } else if (filtered) { resultsObj.push_back(packErrorRetStatus(*mnAliasFilter, strprintf( - "Private key for voting address %s not known by this wallet", EncodeDestination(mnKeyID)))); + "Private key for voting address %s not known by this wallet", + EncodeDestination(dmn->pdmnState->keyIDVoting))) + ); failed++; } } @@ -397,28 +421,34 @@ static mnKeyList getDMNKeys(CWallet* const pwallet, const Optional& static mnKeyList getDMNKeysForActiveMasternode(UniValue& resultsObj) { // local node must be a masternode - if (!activeMasternodeManager) + if (!activeMasternodeManager) { throw JSONRPCError(RPC_MISC_ERROR, _("This is not a deterministic masternode. 'local' option disabled.")); + } - CKey dmnKey; CKeyID dmnKeyID; CDeterministicMNCPtr dmn; - auto res = activeMasternodeManager->GetOperatorKey(dmnKey, dmnKeyID, dmn); + CBLSSecretKey sk; CDeterministicMNCPtr dmn; + auto res = activeMasternodeManager->GetOperatorKey(sk, dmn); if (!res) { resultsObj.push_back(packErrorRetStatus("local", res.getError())); return {}; } - return {MnKeyData("local", &dmn->collateralOutpoint, dmnKey)}; + return {MnKeyData("local", &dmn->collateralOutpoint, sk)}; } // vote on proposal (finalized budget, if fFinal=true) with all possible keys or a single mn (mnAliasFilter) +// Note: for DMNs only proposal voting is allowed with the voting key +// (finalized budget voting requires the operator BLS key) static UniValue mnBudgetVoteInner(CWallet* const pwallet, bool fLegacyMN, const uint256& budgetHash, bool fFinal, const CBudgetVote::VoteDirection& nVote, const Optional& mnAliasFilter) { + if (fFinal && !fLegacyMN) { + throw JSONRPCError(RPC_MISC_ERROR, "Finalized budget voting is allowed only locally, from the masternode"); + } UniValue resultsObj(UniValue::VARR); int failed = 0; mnKeyList mnKeys = fLegacyMN ? getMNKeys(mnAliasFilter, resultsObj, failed) - : getDMNKeys(pwallet, mnAliasFilter, fFinal, resultsObj, failed); + : getDMNVotingKeys(pwallet, mnAliasFilter, fFinal, resultsObj, failed); if (mnKeys.empty()) { return packVoteReturnValue(resultsObj, 0, failed); diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 1e8602bf8a9f3..040beb8f76eca 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -132,7 +132,7 @@ static inline bool filterMasternode(const UniValue& dmno, const std::string& str || (filter(dmno["collateralHash"].get_str(), strFilter)) || (filter(dmno["collateralAddress"].get_str(), strFilter)) || (filter(dmno["dmnstate"]["ownerAddress"].get_str(), strFilter)) - || (filter(dmno["dmnstate"]["operatorAddress"].get_str(), strFilter)) + || (filter(dmno["dmnstate"]["operatorPubKey"].get_str(), strFilter)) || (filter(dmno["dmnstate"]["votingAddress"].get_str(), strFilter)); } @@ -703,7 +703,7 @@ UniValue getmasternodestatus(const JSONRPCRequest& request) } const CActiveMasternodeInfo* amninfo = activeMasternodeManager->GetInfo(); UniValue mnObj(UniValue::VOBJ); - auto dmn = deterministicMNManager->GetListAtChainTip().GetMNByOperatorKey(amninfo->keyIDOperator); + auto dmn = deterministicMNManager->GetListAtChainTip().GetMNByOperatorKey(amninfo->pubKeyOperator); if (dmn) { dmn->ToJson(mnObj); } diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 7ba0885d49adf..594014b674d9d 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -3,6 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "activemasternode.h" +#include "bls/bls_wrapper.h" #include "core_io.h" #include "destination_io.h" #include "evo/deterministicmns.h" @@ -35,8 +37,8 @@ enum ProRegParam { collateralIndex, ipAndPort_register, ipAndPort_update, - operatorAddress_register, - operatorAddress_update, + operatorPubKey_register, + operatorPubKey_update, operatorPayoutAddress_register, operatorPayoutAddress_update, operatorReward, @@ -69,23 +71,24 @@ static const std::map mapParamHelp = { "%d. \"ipAndPort\" (string, required) IP and port in the form \"IP:PORT\".\n" " If set to an empty string, the currently active ip is reused.\n" }, - {operatorAddress_register, - "%d. \"operatorAddress\" (string, required) The operator key address. The private key does not have to be known by your wallet.\n" - " If set to an empty string, ownerAddr will be used.\n" + {operatorPubKey_register, + "%d. \"operatorPubKey\" (string, required) The operator BLS public key. The BLS private key does not have to be known.\n" + " It has to match the BLS private key which is later used when operating the masternode.\n" }, - {operatorAddress_update, - "%d. \"operatorAddress\" (string, required) The operator key address. The private key does not have to be known by your wallet.\n" - " It has to match the private key which can be used later to update the service.\n" - " If set to an empty string, the currently active operator key is reused.\n" + {operatorPubKey_update, + "%d. \"operatorPubKey\" (string, required) The operator BLS public key. The BLS private key does not have to be known.\n" + " It has to match the BLS private key which is later used when operating the masternode.\n" + " If set to an empty string, the currently active operator BLS public key is reused.\n" }, {operatorKey, - "%d. \"operatorKey\" (string, optional) The operator key associated with the operator address of the masternode.\n" - " If not specified, or set to an empty string, then the mn key must be known by your wallet, in order to sign the tx.\n" + "%d. \"operatorKey\" (string, optional) The operator BLS private key associated with the\n" + " registered operator public key. If not specified, or set to an empty string, then this command must\n" + " be performed on the active masternode with the corresponding operator key.\n" }, {operatorPayoutAddress_register, "%d. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" " Only allowed when the ProRegTx had a non-zero operatorReward value.\n" - " If set to an empty string, the operatorAddress is used.\n" + " If set to an empty string, the operatorPubKey is used.\n" }, {operatorPayoutAddress_update, "%d. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n" @@ -208,6 +211,37 @@ static CKeyID ParsePubKeyIDFromAddress(const std::string& strAddress) return *keyID; } +static CBLSPublicKey ParseBLSPubKey(const std::string& hexKey) +{ + CBLSPublicKey pubKey; + if (!pubKey.SetHexStr(hexKey)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS public key: %s", hexKey)); + } + return pubKey; +} + +static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey) +{ + CBLSSecretKey secKey; + if (!secKey.SetHexStr(hexKey)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS secret key: %s", hexKey)); + } + return secKey; +} + +static CBLSSecretKey GetBLSSecretKey(const std::string& hexKey) +{ + if (!hexKey.empty()) { + return ParseBLSSecretKey(hexKey); + } + // If empty, get the active masternode key + CBLSSecretKey sk; CTxIn vin; + if (!GetActiveDMNKeys(sk, vin)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Active masternode key not found. Insert DMN operator private key."); + } + return sk; +} + #ifdef ENABLE_WALLET template @@ -244,6 +278,8 @@ static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, SpecialTxPa UpdateSpecialTxInputsHash(tx, payload); } +#endif + template static void UpdateSpecialTxInputsHash(const CMutableTransaction& tx, SpecialTxPayload& payload) { @@ -261,6 +297,15 @@ static void SignSpecialTxPayloadByHash(const CMutableTransaction& tx, SpecialTxP } } +template +static void SignSpecialTxPayloadByHash(const CMutableTransaction& tx, SpecialTxPayload& payload, const CBLSSecretKey& key) +{ + payload.sig = key.Sign(::SerializeHash(payload)); + if (!payload.sig.IsValid()) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to sign special tx payload"); + } +} + template static void SignSpecialTxPayloadByString(SpecialTxPayload& payload, const CKey& key) { @@ -277,6 +322,8 @@ static std::string TxInErrorToString(int i, const CTxIn& txin, const std::string return strprintf("Input %d (%s): %s", i, txin.prevout.ToStringShort(), strError); } +#ifdef ENABLE_WALLET + static OperationResult SignTransaction(CWallet* const pwallet, CMutableTransaction& tx) { LOCK2(cs_main, pwallet->cs_wallet); @@ -337,13 +384,10 @@ static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramId // addresses/keys const std::string& strAddOwner = params[paramIdx + 1].get_str(); - const std::string& strAddOperator = params[paramIdx + 2].get_str(); + const std::string& strPubKeyOperator = params[paramIdx + 2].get_str(); const std::string& strAddVoting = params[paramIdx + 3].get_str(); pl.keyIDOwner = ParsePubKeyIDFromAddress(strAddOwner); - pl.keyIDOperator = pl.keyIDOwner; - if (!strAddOperator.empty()) { - pl.keyIDOperator = ParsePubKeyIDFromAddress(strAddOperator); - } + pl.pubKeyOperator = ParseBLSPubKey(strPubKeyOperator); pl.keyIDVoting = pl.keyIDOwner; if (!strAddVoting.empty()) { pl.keyIDVoting = ParsePubKeyIDFromAddress(strAddVoting); @@ -388,11 +432,11 @@ static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) if (request.fHelp || request.params.size() < 7 || request.params.size() > 9) { throw std::runtime_error( (fSignAndSend ? - "protx_register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorAddress\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" + "protx_register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" : - "protx_register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorAddress\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" + "protx_register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" "\nCreates an unsigned ProTx and returns it. The ProTx must be signed externally with the collateral\n" "key and then passed to \"protx_register_submit\".\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent transaction output.\n" @@ -403,7 +447,7 @@ static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) + GetHelpString(2, collateralIndex) + GetHelpString(3, ipAndPort_register) + GetHelpString(4, ownerAddress) - + GetHelpString(5, operatorAddress_register) + + GetHelpString(5, operatorPubKey_register) + GetHelpString(6, votingAddress_register) + GetHelpString(7, payoutAddress_register) + GetHelpString(8, operatorReward) @@ -557,7 +601,7 @@ UniValue protx_register_fund(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 6 || request.params.size() > 8) { throw std::runtime_error( - "protx_register_fund \"collateralAddress\" \"ipAndPort\" \"ownerAddress\" \"operatorAddress\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" + "protx_register_fund \"collateralAddress\" \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 10000 PIV\n" "to the address specified by collateralAddress and will then function as masternode collateral.\n" + HelpRequiringPassphrase(pwallet) + "\n" @@ -565,7 +609,7 @@ UniValue protx_register_fund(const JSONRPCRequest& request) + GetHelpString(1, collateralAddress) + GetHelpString(2, ipAndPort_register) + GetHelpString(3, ownerAddress) - + GetHelpString(4, operatorAddress_register) + + GetHelpString(4, operatorPubKey_register) + GetHelpString(5, votingAddress_register) + GetHelpString(6, payoutAddress_register) + GetHelpString(7, operatorReward) @@ -651,7 +695,6 @@ static void AddDMNEntryToList(UniValue& ret, CWallet* pwallet, const CDeterminis assert(ret.isArray()); bool hasOwnerKey{false}; - bool hasOperatorKey{false}; bool hasVotingKey{false}; bool ownsCollateral{false}; bool ownsPayeeScript{false}; @@ -662,7 +705,6 @@ static void AddDMNEntryToList(UniValue& ret, CWallet* pwallet, const CDeterminis if (pwallet && !skipWalletCheck) { LOCK(pwallet->cs_wallet); hasOwnerKey = pwallet->HaveKey(dmn->pdmnState->keyIDOwner); - hasOperatorKey = pwallet->HaveKey(dmn->pdmnState->keyIDOperator); hasVotingKey = pwallet->HaveKey(dmn->pdmnState->keyIDVoting); ownsPayeeScript = CheckWalletOwnsScript(pwallet, dmn->pdmnState->scriptPayout); CTransactionRef collTx; @@ -672,7 +714,7 @@ static void AddDMNEntryToList(UniValue& ret, CWallet* pwallet, const CDeterminis } } - if (fFromWallet && !hasOwnerKey && !hasOperatorKey && !hasVotingKey && !ownsCollateral && !ownsPayeeScript) { + if (fFromWallet && !hasOwnerKey && !hasVotingKey && !ownsCollateral && !ownsPayeeScript) { // not one of ours return; } @@ -683,7 +725,6 @@ static void AddDMNEntryToList(UniValue& ret, CWallet* pwallet, const CDeterminis Optional confirmations = GetUTXOConfirmations(dmn->collateralOutpoint); o.pushKV("confirmations", confirmations ? *confirmations : -1); o.pushKV("hasOwnerKey", hasOwnerKey); - o.pushKV("hasOperatorKey", hasOperatorKey); o.pushKV("hasVotingKey", hasVotingKey); o.pushKV("ownsCollateral", ownsCollateral); o.pushKV("ownsPayeeScript", ownsPayeeScript); @@ -819,15 +860,12 @@ UniValue protx_update_service(const JSONRPCRequest& request) } const std::string& strOpKey = request.params.size() > 3 ? request.params[3].get_str() : ""; - const CKey& operatorKey = strOpKey.empty() ? GetKeyFromWallet(pwallet, dmn->pdmnState->keyIDOperator) - : ParsePrivKey(pwallet, strOpKey, false); + const CBLSSecretKey& operatorKey = GetBLSSecretKey(strOpKey); CMutableTransaction tx; tx.nVersion = CTransaction::TxVersion::SAPLING; tx.nType = CTransaction::TxType::PROUPSERV; - // make sure fee calculation works - pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE); FundSpecialTx(pwallet, tx, pl); SignSpecialTxPayloadByHash(tx, pl, operatorKey); @@ -843,14 +881,14 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 4 || request.params.size() > 5) { throw std::runtime_error( - "protx update_registrar \"proTxHash\" \"operatorAddress\" \"votingAddress\" \"payoutAddress\" (\"ownerKey\")\n" + "protx update_registrar \"proTxHash\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (\"ownerKey\")\n" "\nCreates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout\n" "address of the masternode specified by \"proTxHash\".\n" "The owner key of this masternode must be known to your wallet.\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" + GetHelpString(1, proTxHash) - + GetHelpString(2, operatorAddress_update) + + GetHelpString(2, operatorPubKey_update) + GetHelpString(3, votingAddress_update) + GetHelpString(4, payoutAddress_update) + GetHelpString(5, ownerKey) + @@ -879,9 +917,9 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode with hash %s not found", pl.proTxHash.ToString())); } - const std::string& strOperatorAddress = request.params[1].get_str(); - pl.keyIDOperator = strOperatorAddress.empty() ? dmn->pdmnState->keyIDOperator - : ParsePubKeyIDFromAddress(strOperatorAddress); + const std::string& strPubKeyOperator = request.params[1].get_str(); + pl.pubKeyOperator = strPubKeyOperator.empty() ? dmn->pdmnState->pubKeyOperator.Get() + : ParseBLSPubKey(strPubKeyOperator); const std::string& strVotingAddress = request.params[2].get_str(); pl.keyIDVoting = strVotingAddress.empty() ? dmn->pdmnState->keyIDVoting @@ -950,8 +988,7 @@ UniValue protx_revoke(const JSONRPCRequest& request) } const std::string& strOpKey = request.params.size() > 1 ? request.params[1].get_str() : ""; - const CKey& operatorKey = strOpKey.empty() ? GetKeyFromWallet(pwallet, dmn->pdmnState->keyIDOperator) - : ParsePrivKey(pwallet, strOpKey, false); + const CBLSSecretKey& operatorKey = GetBLSSecretKey(strOpKey); pl.nReason = ProUpRevPL::RevocationReason::REASON_NOT_SPECIFIED; if (request.params.size() > 2) { @@ -967,8 +1004,6 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nVersion = CTransaction::TxVersion::SAPLING; tx.nType = CTransaction::TxType::PROUPREV; - // make sure fee calculation works - pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE); FundSpecialTx(pwallet, tx, pl); SignSpecialTxPayloadByHash(tx, pl, operatorKey); @@ -976,17 +1011,44 @@ UniValue protx_revoke(const JSONRPCRequest& request) } #endif +UniValue generateblskeypair(const JSONRPCRequest& request) +{ + if (request.fHelp || !request.params.empty()) { + throw std::runtime_error( + "generateblskeypair\n" + "\nReturns a BLS secret/public key pair.\n" + "\nResult:\n" + "{\n" + " \"secret\": \"xxxx\", (string) BLS secret key\n" + " \"public\": \"xxxx\", (string) BLS public key\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("generateblskeypair", "") + + HelpExampleRpc("generateblskeypair", "") + ); + } + + CBLSSecretKey sk; + sk.MakeNewKey(); + UniValue ret(UniValue::VOBJ); + ret.pushKV("secret", sk.ToString()); + ret.pushKV("public", sk.GetPublicKey().ToString()); + return ret; +} + + static const CRPCCommand commands[] = { // category name actor (function) okSafe argNames // -------------- --------------------------------- ------------------------ ------ -------- + { "evo", "generateblskeypair", &generateblskeypair, true, {} }, { "evo", "protx_list", &protx_list, true, {"detailed","wallet_only","valid_only","height"} }, #ifdef ENABLE_WALLET - { "evo", "protx_register", &protx_register, true, {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorAddress","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, - { "evo", "protx_register_fund", &protx_register_fund, true, {"collateralAddress","ipAndPort","ownerAddress","operatorAddress","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, - { "evo", "protx_register_prepare", &protx_register_prepare, true, {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorAddress","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, + { "evo", "protx_register", &protx_register, true, {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, + { "evo", "protx_register_fund", &protx_register_fund, true, {"collateralAddress","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, + { "evo", "protx_register_prepare", &protx_register_prepare, true, {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} }, { "evo", "protx_register_submit", &protx_register_submit, true, {"tx","sig"} }, { "evo", "protx_revoke", &protx_revoke, true, {"proTxHash","operatorKey","reason"} }, - { "evo", "protx_update_registrar", &protx_update_registrar, true, {"proTxHash","operatorAddress","votingAddress","payoutAddress","ownerKey"} }, + { "evo", "protx_update_registrar", &protx_update_registrar, true, {"proTxHash","operatorPubKey","votingAddress","payoutAddress","ownerKey"} }, { "evo", "protx_update_service", &protx_update_service, true, {"proTxHash","ipAndPort","operatorPayoutAddress","operatorKey"} }, #endif //ENABLE_WALLET }; diff --git a/src/test/budget_tests.cpp b/src/test/budget_tests.cpp index be57f4d85bd0b..b4f574225cfec 100644 --- a/src/test/budget_tests.cpp +++ b/src/test/budget_tests.cpp @@ -4,6 +4,7 @@ #include "test_pivx.h" +#include "bls/bls_wrapper.h" #include "budget/budgetmanager.h" #include "masternode-payments.h" #include "masternode-sync.h" @@ -446,7 +447,62 @@ BOOST_FIXTURE_TEST_CASE(IsCoinbaseValueValid_test, TestingSetup) cbase.vout[1].nValue = budgAmt/2 + 1; BOOST_CHECK(!IsCoinbaseValueValid(MakeTransactionRef(cbase), budgAmt, state)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-superblock-cb-amt"); +} +BOOST_AUTO_TEST_CASE(fbv_signverify_bls) +{ + CBLSSecretKey sk1, sk2; + sk1.MakeNewKey(); + sk2.MakeNewKey(); + BOOST_ASSERT(sk1 != sk2); + + CTxIn vin(COutPoint(uint256S("0000000000000000000000000000000000000000000000000000000000000002"), 0)); + CTxIn vin2(COutPoint(uint256S("000000000000000000000000000000000000000000000000000000000000003"), 0)); + CTxIn vin3(COutPoint(uint256S("0000000000000000000000000000000000000000000000000000000000000002"), 1)); + + uint256 budgetHash1 = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); + uint256 budgetHash2 = uint256S("0000000000000000000000000000000000010000000000000000000000000001"); + + // Create serialized finalbudgetvote for budgetHash1, signed with sk1 + CFinalizedBudgetVote vote(vin, budgetHash1); + BOOST_CHECK(vote.Sign(sk1)); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << vote; + + // Verify received message on pk1 + CFinalizedBudgetVote _vote; + ss >> _vote; + BOOST_CHECK(_vote.CheckSignature(sk1.GetPublicKey())); + + // Failing verification on pk2 + BOOST_CHECK(!_vote.CheckSignature(sk2.GetPublicKey())); + + std::vector sig = _vote.GetVchSig(); + + // Failing with different time + CFinalizedBudgetVote vote1(_vote); + vote1.SetTime(vote1.GetTime()+1); + BOOST_CHECK(!vote1.CheckSignature(sk1.GetPublicKey())); + + // Failing with different budget hash + CFinalizedBudgetVote vote2(vin, budgetHash2); + vote2.SetTime(_vote.GetTime()); + vote2.SetVchSig(sig); + BOOST_CHECK(!vote2.CheckSignature(sk1.GetPublicKey())); + + // Failing with different vins: different txid (vin2) or voutn (vin3) + CFinalizedBudgetVote vote3_1(vin, budgetHash1); + CFinalizedBudgetVote vote3_2(vin2, budgetHash1); + CFinalizedBudgetVote vote3_3(vin3, budgetHash1); + vote3_1.SetTime(_vote.GetTime()); + vote3_2.SetTime(_vote.GetTime()); + vote3_3.SetTime(_vote.GetTime()); + vote3_1.SetVchSig(sig); + vote3_2.SetVchSig(sig); + vote3_3.SetVchSig(sig); + BOOST_CHECK(vote3_1.CheckSignature(sk1.GetPublicKey())); // vote3_1 == _vote + BOOST_CHECK(!vote3_2.CheckSignature(sk1.GetPublicKey())); + BOOST_CHECK(!vote3_3.CheckSignature(sk1.GetPublicKey())); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 3628664f7ae7f..63f6c328580d1 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -105,16 +105,23 @@ static CKey GetRandomKey() return keyRet; } +static CBLSSecretKey GetRandomBLSKey() +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + return sk; +} + // Creates a ProRegTx. // - if optCollateralOut is nullopt, generate a new collateral in the first output of the tx // - otherwise reference *optCollateralOut as external collateral -static CMutableTransaction CreateProRegTx(Optional optCollateralOut, SimpleUTXOMap& utxos, int port, const CScript& scriptPayout, const CKey& coinbaseKey, const CKey& ownerKey, const CKey& operatorKey, uint16_t operatorReward = 0) +static CMutableTransaction CreateProRegTx(Optional optCollateralOut, SimpleUTXOMap& utxos, int port, const CScript& scriptPayout, const CKey& coinbaseKey, const CKey& ownerKey, const CBLSPublicKey& operatorPubKey, uint16_t operatorReward = 0) { ProRegPL pl; pl.collateralOutpoint = (optCollateralOut ? *optCollateralOut : COutPoint(UINT256_ZERO, 0)); pl.addr = LookupNumeric("1.1.1.1", port); pl.keyIDOwner = ownerKey.GetPubKey().GetID(); - pl.keyIDOperator = operatorKey.GetPubKey().GetID(); + pl.pubKeyOperator = operatorPubKey; pl.keyIDVoting = ownerKey.GetPubKey().GetID(); pl.scriptPayout = scriptPayout; pl.nOperatorReward = operatorReward; @@ -133,7 +140,7 @@ static CMutableTransaction CreateProRegTx(Optional optCollateralOut, return tx; } -static CMutableTransaction CreateProUpServTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& operatorKey, int port, const CScript& scriptOperatorPayout, const CKey& coinbaseKey) +static CMutableTransaction CreateProUpServTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CBLSSecretKey& operatorKey, int port, const CScript& scriptOperatorPayout, const CKey& coinbaseKey) { CAmount change; auto inputs = SelectUTXOs(utxos, 1 * COIN, change); @@ -149,21 +156,22 @@ static CMutableTransaction CreateProUpServTx(SimpleUTXOMap& utxos, const uint256 const CScript& s = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); FundTransaction(tx, utxos, s, s, 1 * COIN); pl.inputsHash = CalcTxInputsHash(tx); - BOOST_ASSERT(CHashSigner::SignHash(::SerializeHash(pl), operatorKey, pl.vchSig)); + pl.sig = operatorKey.Sign(::SerializeHash(pl)); + BOOST_ASSERT(pl.sig.IsValid()); SetTxPayload(tx, pl); SignTransaction(tx, coinbaseKey); return tx; } -static CMutableTransaction CreateProUpRegTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& ownerKey, const CKey& operatorKey, const CKey& votingKey, const CScript& scriptPayout, const CKey& coinbaseKey) +static CMutableTransaction CreateProUpRegTx(SimpleUTXOMap& utxos, const uint256& proTxHash, const CKey& ownerKey, const CBLSPublicKey& operatorPubKey, const CKey& votingKey, const CScript& scriptPayout, const CKey& coinbaseKey) { CAmount change; auto inputs = SelectUTXOs(utxos, 1 * COIN, change); ProUpRegPL pl; pl.proTxHash = proTxHash; - pl.keyIDOperator = operatorKey.GetPubKey().GetID(); + pl.pubKeyOperator = operatorPubKey; pl.keyIDVoting = votingKey.GetPubKey().GetID(); pl.scriptPayout = scriptPayout; @@ -180,7 +188,7 @@ static CMutableTransaction CreateProUpRegTx(SimpleUTXOMap& utxos, const uint256& return tx; } -static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256& proTxHash, ProUpRevPL::RevocationReason reason, const CKey& operatorKey, const CKey& coinbaseKey) +static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256& proTxHash, ProUpRevPL::RevocationReason reason, const CBLSSecretKey& operatorKey, const CKey& coinbaseKey) { CAmount change; auto inputs = SelectUTXOs(utxos, 1 * COIN, change); @@ -195,7 +203,8 @@ static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256& const CScript& s = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); FundTransaction(tx, utxos, s, s, 1 * COIN); pl.inputsHash = CalcTxInputsHash(tx); - BOOST_ASSERT(CHashSigner::SignHash(::SerializeHash(pl), operatorKey, pl.vchSig)); + pl.sig = operatorKey.Sign(::SerializeHash(pl)); + BOOST_ASSERT(pl.sig.IsValid()); SetTxPayload(tx, pl); SignTransaction(tx, coinbaseKey); @@ -304,13 +313,13 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) std::vector dmnHashes; std::map ownerKeys; - std::map operatorKeys; + std::map operatorKeys; // register one MN per block for (size_t i = 0; i < 6; i++) { const CKey& ownerKey = GetRandomKey(); - const CKey& operatorKey = GetRandomKey(); - auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); + auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey()); const uint256& txid = tx.GetHash(); dmnHashes.emplace_back(txid); ownerKeys.emplace(txid, ownerKey); @@ -367,22 +376,22 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // Try to register used owner key { const CKey& ownerKey = ownerKeys.at(dmnHashes[InsecureRandRange(dmnHashes.size())]); - auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, GetRandomKey()); + auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, GetRandomBLSKey().GetPublicKey()); CValidationState state; BOOST_CHECK(!CheckSpecialTx(tx, chainTip, state)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-owner-key"); } // Try to register used operator key { - const CKey& operatorKey = operatorKeys.at(dmnHashes[InsecureRandRange(dmnHashes.size())]); - auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), operatorKey); + const CBLSSecretKey& operatorKey = operatorKeys.at(dmnHashes[InsecureRandRange(dmnHashes.size())]); + auto tx = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), operatorKey.GetPublicKey()); CValidationState state; BOOST_CHECK(!CheckSpecialTx(tx, chainTip, state)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-operator-key"); } // Try to register used IP address { - auto tx = CreateProRegTx(nullopt, utxos, 1 + InsecureRandRange(port-1), GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomKey()); + auto tx = CreateProRegTx(nullopt, utxos, 1 + InsecureRandRange(port-1), GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey()); CValidationState state; BOOST_CHECK(!CheckSpecialTx(tx, chainTip, state)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-dup-IP-address"); @@ -390,10 +399,10 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // Block with two ProReg txes using same owner key { const CKey& ownerKey = GetRandomKey(); - const CKey& operatorKey1 = GetRandomKey(); - const CKey& operatorKey2 = GetRandomKey(); - auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey1); - auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey2); + const CBLSSecretKey& operatorKey1 = GetRandomBLSKey(); + const CBLSSecretKey& operatorKey2 = GetRandomBLSKey(); + auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey1.GetPublicKey()); + auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey2.GetPublicKey()); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); CBlockIndex indexFake(block); indexFake.nHeight = nHeight; @@ -408,9 +417,9 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) { const CKey& ownerKey1 = GetRandomKey(); const CKey& ownerKey2 = GetRandomKey(); - const CKey& operatorKey = GetRandomKey(); - auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey1, operatorKey); - auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey2, operatorKey); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); + auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, ownerKey1, operatorKey.GetPublicKey()); + auto tx2 = CreateProRegTx(nullopt, utxos, (port+1), GenerateRandomAddress(), coinbaseKey, ownerKey2, operatorKey.GetPublicKey()); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); CBlockIndex indexFake(block); indexFake.nHeight = nHeight; @@ -423,8 +432,8 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) } // Block with two ProReg txes using ip address { - auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomKey()); - auto tx2 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomKey()); + auto tx1 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey()); + auto tx2 = CreateProRegTx(nullopt, utxos, port, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey()); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); CBlockIndex indexFake(block); indexFake.nHeight = nHeight; @@ -441,8 +450,8 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) std::vector txns; for (size_t j = 0; j < 3; j++) { const CKey& ownerKey = GetRandomKey(); - const CKey& operatorKey = GetRandomKey(); - auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); + auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey()); const uint256& txid = tx.GetHash(); dmnHashes.emplace_back(txid); ownerKeys.emplace(txid, ownerKey); @@ -549,7 +558,7 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // ProUpServ: Try to change the IP of a masternode that doesn't exist { - const CKey& operatorKey = GetRandomKey(); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); auto tx = CreateProUpServTx(utxos, GetRandHash(), operatorKey, port, CScript(), coinbaseKey); CValidationState state; @@ -561,8 +570,8 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) { // first create a ProRegTx with 5% reward for the operator, and mine it const CKey& ownerKey = GetRandomKey(); - const CKey& operatorKey = GetRandomKey(); - auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey, 500); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); + auto tx = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, ownerKey, operatorKey.GetPublicKey(), 500); const uint256& txid = tx.GetHash(); CreateAndProcessBlock({tx}, coinbaseKey); chainTip = chainActive.Tip(); @@ -599,7 +608,7 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // - (1) ProRegTx registering a masternode // - (2) ProUpServTx changing the IP of another masternode, to the one used by (1) { - auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomKey()); + auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), GetRandomBLSKey().GetPublicKey()); const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random auto tx2 = CreateProUpServTx(utxos, proTx, operatorKeys.at(proTx), (port-1), CScript(), coinbaseKey); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); @@ -616,17 +625,17 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // ProUpReg: change voting key, operator key and payout address { const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random - const CKey& new_operatorKey = GetRandomKey(); + const CBLSSecretKey& new_operatorKey = GetRandomBLSKey(); const CKey& new_votingKey = GetRandomKey(); const CScript& new_payee = GenerateRandomAddress(); // try first with wrong owner key CValidationState state; - auto tx = CreateProUpRegTx(utxos, proTx, GetRandomKey(), new_operatorKey, new_votingKey, new_payee, coinbaseKey); + auto tx = CreateProUpRegTx(utxos, proTx, GetRandomKey(), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey); BOOST_CHECK_MESSAGE(!CheckSpecialTx(tx, chainTip, state), "ProUpReg verifies with wrong owner key"); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig"); // then use the proper key state = CValidationState(); - tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey, new_votingKey, new_payee, coinbaseKey); + tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey); BOOST_CHECK_MESSAGE(CheckSpecialTx(tx, chainTip, state), state.GetRejectReason()); BOOST_CHECK_MESSAGE(CheckTransactionSignature(tx), "ProUpReg signature verification failed"); // also verify that payloads are not malleable after they have been signed @@ -641,7 +650,7 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) SyncWithValidationInterfaceQueue(); auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx); BOOST_ASSERT(dmn != nullptr); - BOOST_CHECK_MESSAGE(dmn->pdmnState->keyIDOperator == new_operatorKey.GetPubKey().GetID(), "mn operator key not changed"); + BOOST_CHECK_MESSAGE(dmn->pdmnState->pubKeyOperator.Get() == new_operatorKey.GetPublicKey(), "mn operator key not changed"); BOOST_CHECK_MESSAGE(dmn->pdmnState->keyIDVoting == new_votingKey.GetPubKey().GetID(), "mn voting key not changed"); BOOST_CHECK_MESSAGE(dmn->pdmnState->scriptPayout == new_payee, "mn script payout not changed"); @@ -685,7 +694,9 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // ProUpReg: Try to change the voting key of a masternode that doesn't exist { - auto tx = CreateProUpRegTx(utxos, GetRandHash(), GetRandomKey(), GetRandomKey(), GetRandomKey(), GenerateRandomAddress(), coinbaseKey); + const CKey& votingKey = GetRandomKey(); + const CBLSSecretKey& operatorKey = GetRandomBLSKey(); + auto tx = CreateProUpRegTx(utxos, GetRandHash(), GetRandomKey(), operatorKey.GetPublicKey(), votingKey, GenerateRandomAddress(), coinbaseKey); CValidationState state; BOOST_CHECK_MESSAGE(!CheckSpecialTx(tx, chainTip, state), "Accepted ProUpReg with invalid protx hash"); @@ -698,9 +709,9 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) int randomIdx2 = 0; do { randomIdx2 = InsecureRandRange(dmnHashes.size()); } while (randomIdx2 == randomIdx); const uint256& proTx = dmnHashes[randomIdx]; // mn to update - const CKey& new_operatorKey = operatorKeys.at(dmnHashes[randomIdx2]); + const CBLSSecretKey& new_operatorKey = operatorKeys.at(dmnHashes[randomIdx2]); - auto tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey, GetRandomKey(), GenerateRandomAddress(), coinbaseKey); + auto tx = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), GetRandomKey(), GenerateRandomAddress(), coinbaseKey); CValidationState state; BOOST_CHECK_MESSAGE(!CheckSpecialTx(tx, chainTip, state), "Accepted ProUpReg with duplicate operator key"); @@ -715,11 +726,11 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) const uint256& proTx1 = dmnHashes[randomIdx1]; const uint256& proTx2 = dmnHashes[randomIdx2]; BOOST_ASSERT(proTx1 != proTx2); - const CKey& new_operatorKey = GetRandomKey(); + const CBLSSecretKey& new_operatorKey = GetRandomBLSKey(); const CKey& new_votingKey = GetRandomKey(); const CScript& new_payee = GenerateRandomAddress(); - auto tx1 = CreateProUpRegTx(utxos, proTx1, ownerKeys.at(proTx1), new_operatorKey, new_votingKey, new_payee, coinbaseKey); - auto tx2 = CreateProUpRegTx(utxos, proTx2, ownerKeys.at(proTx2), new_operatorKey, new_votingKey, new_payee, coinbaseKey); + auto tx1 = CreateProUpRegTx(utxos, proTx1, ownerKeys.at(proTx1), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey); + auto tx2 = CreateProUpRegTx(utxos, proTx2, ownerKeys.at(proTx2), new_operatorKey.GetPublicKey(), new_votingKey, new_payee, coinbaseKey); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); CBlockIndex indexFake(block); indexFake.nHeight = nHeight; @@ -736,10 +747,10 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) // - (1) ProRegTx registering a masternode // - (2) ProUpRegTx changing the operator key of another masternode, to the one used by (1) { - const CKey& new_operatorKey = GetRandomKey(); - auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), new_operatorKey); + const CBLSSecretKey& new_operatorKey = GetRandomBLSKey(); + auto tx1 = CreateProRegTx(nullopt, utxos, port++, GenerateRandomAddress(), coinbaseKey, GetRandomKey(), new_operatorKey.GetPublicKey()); const uint256& proTx = dmnHashes[InsecureRandRange(dmnHashes.size())]; // pick one at random - auto tx2 = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey, GetRandomKey(), GenerateRandomAddress(), coinbaseKey); + auto tx2 = CreateProUpRegTx(utxos, proTx, ownerKeys.at(proTx), new_operatorKey.GetPublicKey(), GetRandomKey(), GenerateRandomAddress(), coinbaseKey); CBlock block = CreateBlock({tx1, tx2}, coinbaseKey); CBlockIndex indexFake(block); indexFake.nHeight = nHeight; @@ -758,7 +769,7 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) ProUpRevPL::RevocationReason reason = ProUpRevPL::RevocationReason::REASON_TERMINATION_OF_SERVICE; // try first with wrong operator key CValidationState state; - auto tx = CreateProUpRevTx(utxos, proTx, reason, GetRandomKey(), coinbaseKey); + auto tx = CreateProUpRevTx(utxos, proTx, reason, GetRandomBLSKey(), coinbaseKey); BOOST_CHECK_MESSAGE(!CheckSpecialTx(tx, chainTip, state), "ProUpReg verifies with wrong owner key"); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-sig"); // then use the proper key @@ -778,7 +789,7 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChain400Setup) SyncWithValidationInterfaceQueue(); auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx); BOOST_ASSERT(dmn != nullptr); - BOOST_CHECK_MESSAGE(dmn->pdmnState->keyIDOperator == CKeyID(), "mn operator key not removed"); + BOOST_CHECK_MESSAGE(!dmn->pdmnState->pubKeyOperator.Get().IsValid(), "mn operator key not removed"); BOOST_CHECK_MESSAGE(dmn->pdmnState->addr == CService(), "mn IP address not removed"); BOOST_CHECK_MESSAGE(dmn->pdmnState->scriptOperatorPayout.empty(), "mn operator payout not removed"); BOOST_CHECK_EQUAL(dmn->pdmnState->nRevocationReason, reason); diff --git a/src/test/evo_specialtx_tests.cpp b/src/test/evo_specialtx_tests.cpp index 2a71c9e3bc06d..799e4fc8e6dae 100644 --- a/src/test/evo_specialtx_tests.cpp +++ b/src/test/evo_specialtx_tests.cpp @@ -25,6 +25,13 @@ static CKeyID GetRandomKeyID() return GetRandomKey().GetPubKey().GetID(); } +static CBLSPublicKey GetRandomBLSKey() +{ + CBLSSecretKey sk; + sk.MakeNewKey(); + return sk.GetPublicKey(); +} + static CScript GetRandomScript() { return GetScriptForDestination(GetRandomKeyID()); @@ -37,7 +44,7 @@ static ProRegPL GetRandomProRegPayload() pl.collateralOutpoint.n = InsecureRandBits(2); BOOST_CHECK(Lookup("57.12.210.11:51472", pl.addr, Params().GetDefaultPort(), false)); pl.keyIDOwner = GetRandomKeyID(); - pl.keyIDOperator = GetRandomKeyID(); + pl.pubKeyOperator = GetRandomBLSKey(); pl.keyIDVoting = GetRandomKeyID(); pl.scriptPayout = GetRandomScript(); pl.nOperatorReward = InsecureRandRange(10000); @@ -54,7 +61,7 @@ static ProUpServPL GetRandomProUpServPayload() BOOST_CHECK(Lookup("127.0.0.1:51472", pl.addr, Params().GetDefaultPort(), false)); pl.scriptOperatorPayout = GetRandomScript(); pl.inputsHash = GetRandHash(); - pl.vchSig = InsecureRandBytes(63); + pl.sig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE)); return pl; } @@ -62,7 +69,7 @@ static ProUpRegPL GetRandomProUpRegPayload() { ProUpRegPL pl; pl.proTxHash = GetRandHash(); - pl.keyIDOperator = GetRandomKeyID(); + pl.pubKeyOperator = GetRandomBLSKey(); pl.keyIDVoting = GetRandomKeyID(); pl.scriptPayout = GetRandomScript(); pl.inputsHash = GetRandHash(); @@ -76,7 +83,7 @@ static ProUpRevPL GetRandomProUpRevPayload() pl.proTxHash = GetRandHash(); pl.nReason = InsecureRand16(); pl.inputsHash = GetRandHash(); - pl.vchSig = InsecureRandBytes(63); + pl.sig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE)); return pl; } @@ -144,7 +151,7 @@ BOOST_AUTO_TEST_CASE(proreg_setpayload_test) BOOST_CHECK(pl.collateralOutpoint == pl2.collateralOutpoint); BOOST_CHECK(pl.addr == pl2.addr); BOOST_CHECK(pl.keyIDOwner == pl2.keyIDOwner); - BOOST_CHECK(pl.keyIDOperator == pl2.keyIDOperator); + BOOST_CHECK(pl.pubKeyOperator == pl2.pubKeyOperator); BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting); BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout); BOOST_CHECK(pl.nOperatorReward == pl2.nOperatorReward); @@ -165,7 +172,7 @@ BOOST_AUTO_TEST_CASE(proupserv_setpayload_test) BOOST_CHECK(pl.addr == pl2.addr); BOOST_CHECK(pl.scriptOperatorPayout == pl2.scriptOperatorPayout); BOOST_CHECK(pl.inputsHash == pl2.inputsHash); - BOOST_CHECK(pl.vchSig == pl2.vchSig); + BOOST_CHECK(pl.sig == pl2.sig); } BOOST_AUTO_TEST_CASE(proupreg_setpayload_test) @@ -177,7 +184,7 @@ BOOST_AUTO_TEST_CASE(proupreg_setpayload_test) ProUpRegPL pl2; BOOST_CHECK(GetTxPayload(mtx, pl2)); BOOST_CHECK(pl.proTxHash == pl2.proTxHash); - BOOST_CHECK(pl.keyIDOperator == pl2.keyIDOperator); + BOOST_CHECK(pl.pubKeyOperator == pl2.pubKeyOperator); BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting); BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout); BOOST_CHECK(pl.inputsHash == pl2.inputsHash); @@ -195,7 +202,7 @@ BOOST_AUTO_TEST_CASE(prouprev_setpayload_test) BOOST_CHECK(pl.proTxHash == pl2.proTxHash); BOOST_CHECK(pl.nReason == pl2.nReason); BOOST_CHECK(pl.inputsHash == pl2.inputsHash); - BOOST_CHECK(pl.vchSig == pl2.vchSig); + BOOST_CHECK(pl.sig == pl2.sig); } BOOST_AUTO_TEST_CASE(proreg_checkstringsig_test) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 0ceb050a98ba9..4d55b9da16279 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -7,6 +7,7 @@ #include "txmempool.h" #include "clientversion.h" +#include "bls/bls_wrapper.h" #include "evo/deterministicmns.h" #include "evo/specialtx.h" #include "evo/providertx.h" @@ -378,7 +379,7 @@ void CTxMemPool::addUncheckedSpecialTx(const CTransaction& tx) } mapProTxAddresses.emplace(pl.addr, txid); mapProTxPubKeyIDs.emplace(pl.keyIDOwner, txid); - mapProTxPubKeyIDs.emplace(pl.keyIDOperator, txid); + mapProTxBlsPubKeyHashes.emplace(pl.pubKeyOperator.GetHash(), txid); break; } @@ -396,7 +397,7 @@ void CTxMemPool::addUncheckedSpecialTx(const CTransaction& tx) bool ok = GetTxPayload(tx, pl); assert(ok); mapProTxRefs.emplace(pl.proTxHash, txid); - mapProTxPubKeyIDs.emplace(pl.keyIDOperator, tx.GetHash()); + mapProTxBlsPubKeyHashes.emplace(pl.pubKeyOperator.GetHash(), txid); break; } @@ -503,7 +504,7 @@ void CTxMemPool::removeUncheckedSpecialTx(const CTransaction& tx) mapProTxCollaterals.erase(pl.collateralOutpoint); mapProTxAddresses.erase(pl.addr); mapProTxPubKeyIDs.erase(pl.keyIDOwner); - mapProTxPubKeyIDs.erase(pl.keyIDOperator); + mapProTxBlsPubKeyHashes.erase(pl.pubKeyOperator.GetHash()); break; } @@ -521,7 +522,7 @@ void CTxMemPool::removeUncheckedSpecialTx(const CTransaction& tx) bool ok = GetTxPayload(tx, pl); assert(ok); eraseProTxRef(pl.proTxHash, txid); - mapProTxPubKeyIDs.erase(pl.keyIDOperator); + mapProTxBlsPubKeyHashes.erase(pl.pubKeyOperator.GetHash()); break; } @@ -724,6 +725,16 @@ void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction& tx, const CKeyID } } +void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction& tx, const CBLSPublicKey& pubKey) +{ + if (mapProTxBlsPubKeyHashes.count(pubKey.GetHash())) { + const uint256& conflictHash = mapProTxBlsPubKeyHashes.at(pubKey.GetHash()); + if (conflictHash != tx.GetHash() && mapTx.count(conflictHash)) { + removeRecursive(mapTx.find(conflictHash)->GetTx(), MemPoolRemovalReason::CONFLICT); + } + } +} + void CTxMemPool::removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint) { if (mapProTxCollaterals.count(collateralOutpoint)) { @@ -792,7 +803,7 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx) } } removeProTxPubKeyConflicts(tx, pl.keyIDOwner); - removeProTxPubKeyConflicts(tx, pl.keyIDOperator); + removeProTxPubKeyConflicts(tx, pl.pubKeyOperator); if (!pl.collateralOutpoint.hash.IsNull()) { removeProTxCollateralConflicts(tx, pl.collateralOutpoint); } @@ -820,7 +831,7 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx) LogPrint(BCLog::MEMPOOL, "%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return; } - removeProTxPubKeyConflicts(tx, pl.keyIDOperator); + removeProTxPubKeyConflicts(tx, pl.pubKeyOperator); break; } @@ -1153,7 +1164,8 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const LogPrint(BCLog::MEMPOOL, "%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return true; // i.e. can't decode payload == conflict } - if (mapProTxAddresses.count(pl.addr) || mapProTxPubKeyIDs.count(pl.keyIDOwner) || mapProTxPubKeyIDs.count(pl.keyIDOperator)) { + if (mapProTxAddresses.count(pl.addr) || mapProTxPubKeyIDs.count(pl.keyIDOwner) || + mapProTxBlsPubKeyHashes.count(pl.pubKeyOperator.GetHash())) { return true; } if (!pl.collateralOutpoint.hash.IsNull()) { @@ -1185,8 +1197,8 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const LogPrint(BCLog::MEMPOOL, "%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return true; // i.e. can't decode payload == conflict } - auto it = mapProTxPubKeyIDs.find(pl.keyIDOperator); - return it != mapProTxPubKeyIDs.end() && it->second != pl.proTxHash; + auto it = mapProTxBlsPubKeyHashes.find(pl.pubKeyOperator.GetHash()); + return it != mapProTxBlsPubKeyHashes.end() && it->second != pl.proTxHash; } } diff --git a/src/txmempool.h b/src/txmempool.h index 7d9df6f59edb8..aca625f0bbeee 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -27,6 +27,7 @@ #include class CAutoFile; +class CBLSPublicKey; /** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */ @@ -496,6 +497,7 @@ class CTxMemPool std::multimap mapProTxRefs; // proTxHash -> transaction (all TXs that refer to an existing proTx) std::map mapProTxAddresses; std::map mapProTxPubKeyIDs; + std::map mapProTxBlsPubKeyHashes; std::map mapProTxCollaterals; void UpdateParent(txiter entry, txiter parent, bool add); @@ -709,6 +711,7 @@ class CTxMemPool void addUncheckedSpecialTx(const CTransaction& tx); void removeUncheckedSpecialTx(const CTransaction& tx); void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId); + void removeProTxPubKeyConflicts(const CTransaction& tx, const CBLSPublicKey& pubKey); void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint); void removeProTxSpentCollateralConflicts(const CTransaction &tx); void removeProTxConflicts(const CTransaction &tx); diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 1d8cec255fe74..db7cf569337e0 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1438,26 +1438,26 @@ def serialize(self): # PIVX Classes class Masternode(object): - def __init__(self, idx, owner_addr, operator_addr, voting_addr, ipport, payout_addr, operator_key): + def __init__(self, idx, owner_addr, operator_pk, voting_addr, ipport, payout_addr, operator_sk): self.idx = idx self.owner = owner_addr - self.operator = operator_addr + self.operator_pk = operator_pk self.voting = voting_addr self.ipport = ipport self.payee = payout_addr - self.operator_key = operator_key + self.operator_sk = operator_sk self.proTx = None self.collateral = None def revoked(self): self.ipport = "[::]:0" - self.operator = "" - self.operator_key = None + self.operator_pk = "0" * 96 + self.operator_sk = None def __repr__(self): return "Masternode(idx=%d, owner=%s, operator=%s, voting=%s, ip=%s, payee=%s, opkey=%s, protx=%s, collateral=%s)" % ( - self.idx, str(self.owner), str(self.operator), str(self.voting), str(self.ipport), - str(self.payee), str(self.operator_key), str(self.proTx), str(self.collateral) + self.idx, str(self.owner), str(self.operator_pk), str(self.voting), str(self.ipport), + str(self.payee), str(self.operator_sk), str(self.proTx), str(self.collateral) ) def __str__(self): diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 94a9a548d295f..1e89a40dbff98 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1072,8 +1072,7 @@ def setupDMN(self, collateralAdd = mnOwner.getnewaddress("dmn") ipport = "127.0.0.1:" + str(p2p_port(mnRemotePos)) ownerAdd = mnOwner.getnewaddress("dmn_owner") - operatorAdd = mnOwner.getnewaddress("dmn_operator") - operatorKey = mnOwner.dumpprivkey(operatorAdd) + bls_keypair = mnOwner.generateblskeypair() votingAdd = mnOwner.getnewaddress("dmn_voting") if strType == "fund": # send to the owner the collateral tx cost + some dust for the ProReg and fee @@ -1083,7 +1082,7 @@ def setupDMN(self, assert_greater_than(mnOwner.getrawtransaction(fundingTxId, 1)["confirmations"], 0) # create and send the ProRegTx funding the collateral proTxId = mnOwner.protx_register_fund(collateralAdd, ipport, ownerAdd, - operatorAdd, votingAdd, collateralAdd) + bls_keypair["public"], votingAdd, collateralAdd) elif strType == "internal": mnOwner.getnewaddress("dust") # send to the owner the collateral tx cost + some dust for the ProReg and fee @@ -1100,13 +1099,13 @@ def setupDMN(self, assert_greater_than(collateralTxId_n, -1) assert_greater_than(json_tx["confirmations"], 0) proTxId = mnOwner.protx_register(collateralTxId, collateralTxId_n, ipport, ownerAdd, - operatorAdd, votingAdd, collateralAdd) + bls_keypair["public"], votingAdd, collateralAdd) elif strType == "external": self.log.info("Setting up ProRegTx with collateral externally-signed...") # send the tx from the miner payoutAdd = mnOwner.getnewaddress("payout") register_res = miner.protx_register_prepare(outpoint.hash, outpoint.n, ipport, ownerAdd, - operatorAdd, votingAdd, payoutAdd) + bls_keypair["public"], votingAdd, payoutAdd) self.log.info("ProTx prepared") message_to_sign = register_res["signMessage"] collateralAdd = register_res["collateralAddress"] @@ -1121,7 +1120,7 @@ def setupDMN(self, self.stake_and_sync(self.nodes.index(miner), 1) assert_greater_than(self.nodes[mnRemotePos].getrawtransaction(proTxId, 1)["confirmations"], 0) assert proTxId in self.nodes[mnRemotePos].protx_list(False) - return proTxId, operatorKey + return proTxId, bls_keypair["secret"] def setupMasternode(self, mnOwner, @@ -1188,10 +1187,10 @@ def protx_register_fund(self, miner, controller, dmn, collateral_addr, op_rew=No # create and send the ProRegTx funding the collateral if op_rew is None: dmn.proTx = controller.protx_register_fund(collateral_addr, dmn.ipport, dmn.owner, - dmn.operator, dmn.voting, dmn.payee) + dmn.operator_pk, dmn.voting, dmn.payee) else: dmn.proTx = controller.protx_register_fund(collateral_addr, dmn.ipport, dmn.owner, - dmn.operator, dmn.voting, dmn.payee, + dmn.operator_pk, dmn.voting, dmn.payee, op_rew["reward"], op_rew["address"]) dmn.collateral = COutPoint(int(dmn.proTx, 16), get_collateral_vout(controller.getrawtransaction(dmn.proTx, True))) @@ -1213,7 +1212,7 @@ def protx_register(self, miner, controller, dmn, collateral_addr): # create and send the ProRegTx dmn.collateral = COutPoint(int(funding_txid, 16), get_collateral_vout(json_tx)) dmn.proTx = controller.protx_register(funding_txid, dmn.collateral.n, dmn.ipport, dmn.owner, - dmn.operator, dmn.voting, dmn.payee) + dmn.operator_pk, dmn.voting, dmn.payee) """ Create a ProReg tx, referencing a collateral signed externally (eg. HW wallets). @@ -1232,7 +1231,7 @@ def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit): dmn.collateral = outpoint # Prepare the message to be signed externally by the owner of the collateral (the controller) reg_tx = miner.protx_register_prepare("%064x" % outpoint.hash, outpoint.n, dmn.ipport, dmn.owner, - dmn.operator, dmn.voting, dmn.payee) + dmn.operator_pk, dmn.voting, dmn.payee) sig = controller.signmessage(reg_tx["collateralAddress"], reg_tx["signMessage"]) if fSubmit: dmn.proTx = miner.protx_register_submit(reg_tx["tx"], sig) @@ -1248,12 +1247,12 @@ def protx_register_ext(self, miner, controller, dmn, outpoint, fSubmit): outpoint: (COutPoint) collateral outpoint to be used with "external". It must be owned by the controller (proTx is sent from the miner). If not provided, a new utxo is created, sending it from the miner. - op_addr_and_key: (list of strings) List with two entries, operator address (0) and private key (1). + op_blskeys: (list of strings) List with two entries, operator public (0) and private (1) key. If not provided, a new address-key pair is generated. :return: dmn: (Masternode) the deterministic masternode object """ def register_new_dmn(self, idx, miner_idx, controller_idx, strType, - payout_addr=None, outpoint=None, op_addr_and_key=None): + payout_addr=None, outpoint=None, op_blskeys=None): # Prepare remote node assert idx != miner_idx assert idx != controller_idx @@ -1265,7 +1264,7 @@ def register_new_dmn(self, idx, miner_idx, controller_idx, strType, collateral_addr = controller_node.getnewaddress("mncollateral-%d" % idx) if payout_addr is None: payout_addr = collateral_addr - dmn = create_new_dmn(idx, controller_node, payout_addr, op_addr_and_key) + dmn = create_new_dmn(idx, controller_node, payout_addr, op_blskeys) # Create ProRegTx self.log.info("Creating%s proRegTx for deterministic masternode idx=%d..." % ( @@ -1315,7 +1314,7 @@ def check_mn_list_on_node(self, idx, mns): mn2 = protxs[mn.proTx] collateral = mn.collateral.to_json() assert_equal(mn.owner, mn2["dmnstate"]["ownerAddress"]) - assert_equal(mn.operator, mn2["dmnstate"]["operatorAddress"]) + assert_equal(mn.operator_pk, mn2["dmnstate"]["operatorPubKey"]) assert_equal(mn.voting, mn2["dmnstate"]["votingAddress"]) assert_equal(mn.ipport, mn2["dmnstate"]["service"]) assert_equal(mn.payee, mn2["dmnstate"]["payoutAddress"]) @@ -1334,7 +1333,7 @@ def check_proreg_payload(self, dmn, json_tx): assert_equal(pl["service"], dmn.ipport) assert_equal(pl["ownerAddress"], dmn.owner) assert_equal(pl["votingAddress"], dmn.voting) - assert_equal(pl["operatorAddress"], dmn.operator) + assert_equal(pl["operatorPubKey"], dmn.operator_pk) assert_equal(pl["payoutAddress"], dmn.payee) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index eae270527d78e..8e79bdd48b824 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -602,19 +602,20 @@ def get_collateral_vout(json_tx): return funding_txidn # owner and voting keys are created from controller node. -# operator key and address are created, if operator_addr_and_key is None. -def create_new_dmn(idx, controller, payout_addr, operator_addr_and_key): +# operator keys are created, if operator_keys is None. +def create_new_dmn(idx, controller, payout_addr, operator_keys): port = p2p_port(idx) if idx <= MAX_NODES else p2p_port(MAX_NODES) + (idx - MAX_NODES) ipport = "127.0.0.1:" + str(port) owner_addr = controller.getnewaddress("mnowner-%d" % idx) voting_addr = controller.getnewaddress("mnvoting-%d" % idx) - if operator_addr_and_key is None: - operator_addr = controller.getnewaddress("mnoperator-%d" % idx) - operator_key = controller.dumpprivkey(operator_addr) + if operator_keys is None: + bls_keypair = controller.generateblskeypair() + operator_pk = bls_keypair["public"] + operator_sk = bls_keypair["secret"] else: - operator_addr = operator_addr_and_key[0] - operator_key = operator_addr_and_key[1] - return messages.Masternode(idx, owner_addr, operator_addr, voting_addr, ipport, payout_addr, operator_key) + operator_pk = operator_keys[0] + operator_sk = operator_keys[1] + return messages.Masternode(idx, owner_addr, operator_pk, voting_addr, ipport, payout_addr, operator_sk) def spend_mn_collateral(spender, dmn): inputs = [dmn.collateral.to_json()] diff --git a/test/functional/tiertwo_deterministicmns.py b/test/functional/tiertwo_deterministicmns.py index 02f7bf3967001..8853df67dfb87 100755 --- a/test/functional/tiertwo_deterministicmns.py +++ b/test/functional/tiertwo_deterministicmns.py @@ -41,7 +41,7 @@ def add_new_dmn(self, mns, strType, op_keys=None, from_out=None): self.controllerPos, strType, outpoint=from_out, - op_addr_and_key=op_keys)) + op_blskeys=op_keys)) def check_mn_list(self, mns): for i in range(self.num_nodes): @@ -146,7 +146,7 @@ def run_test(self): self.add_new_dmn(mns, "external") self.add_new_dmn(mns, "fund") for mn in mns: - self.nodes[mn.idx].initmasternode(mn.operator_key, "", True) + self.nodes[mn.idx].initmasternode(mn.operator_sk, "", True) time.sleep(1) miner.generate(1) self.sync_blocks() @@ -159,11 +159,9 @@ def run_test(self): op_keys = [] for i in range(3): idx = 2 + len(mns) + i - add_and_key = [] - add_and_key.append(controller.getnewaddress("oper-%d-key" % idx)) - add_and_key.append(controller.dumpprivkey(add_and_key[0])) - self.nodes[idx].initmasternode(add_and_key[1], "", True) - op_keys.append(add_and_key) + bls_keypair = controller.generateblskeypair() + self.nodes[idx].initmasternode(bls_keypair["secret"], "", True) + op_keys.append([bls_keypair["public"], bls_keypair["secret"]]) time.sleep(1) # Now send the ProReg txes and check list @@ -211,11 +209,11 @@ def run_test(self): # Register dmn again, with the collateral of dmn2 # dmn must be added again to the list, and dmn2 must be removed dmn2 = mns.pop(randrange(len(mns))) # pop one at random - dmn_keys = [dmn.operator, dmn.operator_key] - dmn2_keys = [dmn2.operator, dmn2.operator_key] + dmn_keys = [dmn.operator_pk, dmn.operator_sk] + dmn2_keys = [dmn2.operator_pk, dmn2.operator_sk] self.log.info("Reactivating node %d reusing the collateral of node %d..." % (dmn.idx, dmn2.idx)) mns.append(self.register_new_dmn(dmn.idx, self.minerPos, self.controllerPos, "external", - outpoint=dmn2.collateral, op_addr_and_key=dmn_keys)) + outpoint=dmn2.collateral, op_blskeys=dmn_keys)) miner.generate(1) self.sync_blocks() @@ -228,7 +226,7 @@ def run_test(self): rand_idx = mns[randrange(len(mns))].idx assert_raises_rpc_error(-1, "bad-protx-dup-IP-address", self.register_new_dmn, rand_idx, self.minerPos, self.controllerPos, "fund", - op_addr_and_key=dmn2_keys) + op_blskeys=dmn2_keys) # Now try with duplicate operator key self.log.info("Trying duplicate operator key...") @@ -298,25 +296,32 @@ def run_test(self): "%064x" % getrandbits(256), "127.0.0.1:1000") self.log.info("Trying to update an IP address to an already used one...") assert_raises_rpc_error(-1, "bad-protx-dup-addr", miner.protx_update_service, - mns[0].proTx, mns[1].ipport, "", mns[0].operator_key) + mns[0].proTx, mns[1].ipport, "", mns[0].operator_sk) self.log.info("Trying to update the payout address when the reward is 0...") assert_raises_rpc_error(-8, "Operator reward is 0. Cannot set operator payout address", miner.protx_update_service, mns[0].proTx, "", - miner.getnewaddress(), mns[0].operator_key) + miner.getnewaddress(), mns[0].operator_sk) self.log.info("Trying to update the operator payee to an invalid address...") assert_raises_rpc_error(-5, "invalid PIVX address InvalidPayee", miner.protx_update_service, dmn2c.proTx, "", "InvalidPayee", "") self.log.info("Update IP address...") mns[0].ipport = "127.0.0.1:1000" - # Controller should already have the key (as it was generated there), no need to pass it - controller.protx_update_service(mns[0].proTx, mns[0].ipport) - self.sync_mempools([miner, controller]) + # Do it from the remote node (so no need to pass the operator BLS secret key) + remote_node = self.nodes[mns[0].idx] + # Send first some funds + miner.sendtoaddress(remote_node.getnewaddress(), 1.0) + miner.generate(1) + self.sync_blocks() + # Then send the ProUpServ tx from the masternode + remote_node.protx_update_service(mns[0].proTx, mns[0].ipport) + self.sync_mempools([miner, remote_node]) miner.generate(1) self.sync_blocks() self.check_mn_list(mns) self.log.info("Update operator payout address...") + # This time send the ProUpServ tx directly from the miner, giving the operator BLS secret key new_address = self.nodes[dmn2c.idx].getnewaddress() - miner.protx_update_service(dmn2c.proTx, dmn2c.ipport, new_address, dmn2c.operator_key) + miner.protx_update_service(dmn2c.proTx, dmn2c.ipport, new_address, dmn2c.operator_sk) miner.generate(len(mns) + 1) self.sync_blocks() # Check payment to new address @@ -329,15 +334,16 @@ def run_test(self): "%064x" % getrandbits(256), "", "", "") self.log.info("Trying to update an operator address to an already used one...") assert_raises_rpc_error(-1, "bad-protx-dup-key", controller.protx_update_registrar, - mns[0].proTx, mns[1].operator, "", "") + mns[0].proTx, mns[1].operator_pk, "", "") self.log.info("Trying to update the payee to an invalid address...") assert_raises_rpc_error(-5, "invalid PIVX address InvalidPayee", controller.protx_update_registrar, mns[0].proTx, "", "", "InvalidPayee") - self.log.info("Update operator address...") - mns[0].operator = self.nodes[mns[0].idx].getnewaddress() - mns[0].operator_key = self.nodes[mns[0].idx].dumpprivkey(mns[0].operator) + self.log.info("Update operator keys...") + bls_keypair = self.nodes[mns[0].idx].generateblskeypair() + mns[0].operator_pk = bls_keypair["public"] + mns[0].operator_sk = bls_keypair["secret"] # Controller should already have the key (as it was generated there), no need to pass it - controller.protx_update_registrar(mns[0].proTx, mns[0].operator, "", "") + controller.protx_update_registrar(mns[0].proTx, mns[0].operator_pk, "", "") self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() @@ -350,10 +356,11 @@ def run_test(self): old_mn0_balance = self.get_addr_balance(controller, mns[0].payee) self.log.info("Update operator address (with external key)...") - mns[0].operator = self.nodes[mns[0].idx].getnewaddress() - mns[0].operator_key = self.nodes[mns[0].idx].dumpprivkey(mns[0].operator) + bls_keypair = self.nodes[mns[0].idx].generateblskeypair() + mns[0].operator_pk = bls_keypair["public"] + mns[0].operator_sk = bls_keypair["secret"] ownerKey = controller.dumpprivkey(mns[0].owner) - miner.protx_update_registrar(mns[0].proTx, mns[0].operator, "", "", ownerKey) + miner.protx_update_registrar(mns[0].proTx, mns[0].operator_pk, "", "", ownerKey) miner.generate(1) self.sync_blocks() self.check_mn_enabled_count(5, 6) # stil not valid until new operator sends proUpServ @@ -390,19 +397,26 @@ def run_test(self): assert_raises_rpc_error(-8, "not found", miner.protx_revoke, "%064x" % getrandbits(256)) self.log.info("Trying to revoke with invalid reason...") - assert_raises_rpc_error(-8, "invalid reason", controller.protx_revoke, mns[3].proTx, "", 100) + assert_raises_rpc_error(-8, "invalid reason", controller.protx_revoke, mns[3].proTx, mns[3].operator_sk, 100) self.log.info("Revoke masternode...") - # Controller should already have the key (as it was generated there), no need to pass it - controller.protx_revoke(mns[3].proTx, "", 1) + # Do it from the remote node (so no need to pass the operator BLS secret key) + remote_node = self.nodes[mns[3].idx] + # Send first some funds + miner.sendtoaddress(remote_node.getnewaddress(), 1.0) + miner.generate(1) + self.sync_blocks() + # Then send the ProUpRev tx from the masternode + remote_node.protx_revoke(mns[3].proTx, "", 1) mns[3].revoked() - self.sync_mempools([miner, controller]) + self.sync_mempools([miner, remote_node]) miner.generate(1) self.sync_blocks() self.check_mn_enabled_count(4, 6) # mn3 has been revoked self.check_mn_list(mns) old_mn3_bal = self.get_addr_balance(controller, mns[3].payee) + # This time send the ProUpRev tx directly from the miner, giving the operator BLS secret key self.log.info("Revoke masternode (with external key)...") - miner.protx_revoke(mns[4].proTx, mns[4].operator_key, 2) + miner.protx_revoke(mns[4].proTx, mns[4].operator_sk, 2) mns[4].revoked() miner.generate(1) self.sync_blocks() @@ -422,12 +436,13 @@ def run_test(self): # Test reviving a masternode self.log.info("Reviving a masternode...") - mns[3].operator = controller.getnewaddress() - mns[3].operator_key = controller.dumpprivkey(mns[3].operator) - miner.protx_update_registrar(mns[3].proTx, mns[3].operator, "", "", controller.dumpprivkey(mns[3].owner)) + bls_keypair = controller.generateblskeypair() + mns[3].operator_pk = bls_keypair["public"] + mns[3].operator_sk = bls_keypair["secret"] + miner.protx_update_registrar(mns[3].proTx, mns[3].operator_pk, "", "", controller.dumpprivkey(mns[3].owner)) miner.generate(1) mns[3].ipport = "127.0.0.1:3000" - miner.protx_update_service(mns[3].proTx, mns[3].ipport, "", mns[3].operator_key) + miner.protx_update_service(mns[3].proTx, mns[3].ipport, "", mns[3].operator_sk) miner.generate(len(mns)) self.sync_blocks() diff --git a/test/functional/tiertwo_reorg_mempool.py b/test/functional/tiertwo_reorg_mempool.py index afc1cc6b0d034..9c5461056ef1a 100755 --- a/test/functional/tiertwo_reorg_mempool.py +++ b/test/functional/tiertwo_reorg_mempool.py @@ -51,7 +51,7 @@ def disconnect_all(self): def register_masternode(self, from_node, dmn, collateral_addr): dmn.proTx = from_node.protx_register_fund(collateral_addr, dmn.ipport, dmn.owner, - dmn.operator, dmn.voting, dmn.payee) + dmn.operator_pk, dmn.voting, dmn.payee) dmn.collateral = COutPoint(int(dmn.proTx, 16), get_collateral_vout(from_node.getrawtransaction(dmn.proTx, True))) @@ -142,7 +142,7 @@ def run_test(self): self.log.info("Testing in-mempool duplicate-operator rejection...") dmn_A2 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 - dmn_A2.operator = mempool_dmn1.operator + dmn_A2.operator_pk = mempool_dmn1.operator_pk assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A2, collateral_addr) assert dmn_A2.proTx not in nodeA.getrawmempool() @@ -171,24 +171,28 @@ def run_test(self): self.protx_register_ext(nodeA, nodeA, mempool_dmn4, mempool_dmn4.collateral, True) # Now send a valid proUpServ tx to the mempool, without mining it - proupserv1_txid = nodeA.protx_update_service(pre_split_mn1.proTx, "127.0.0.1:1000") + proupserv1_txid = nodeA.protx_update_service(pre_split_mn1.proTx, + "127.0.0.1:1000", "", pre_split_mn1.operator_sk) # Try sending another update, reusing the same ip of the previous mempool tx self.log.info("Testing proUpServ in-mempool duplicate-IP rejection...") - assert_raises_rpc_error(-26, "protx-dup", nodeA.protx_update_service, mnsA[0].proTx, "127.0.0.1:1000") + assert_raises_rpc_error(-26, "protx-dup", nodeA.protx_update_service, + mnsA[0].proTx, "127.0.0.1:1000", "", mnsA[0].operator_sk) # Now send other two valid proUpServ txes to the mempool, without mining them - proupserv2_txid = nodeA.protx_update_service(mnsA[3].proTx, "127.0.0.1:2000") - proupserv3_txid = nodeA.protx_update_service(pre_split_mn1.proTx, "127.0.0.1:1001") + proupserv2_txid = nodeA.protx_update_service(mnsA[3].proTx, + "127.0.0.1:2000", "", mnsA[3].operator_sk) + proupserv3_txid = nodeA.protx_update_service(pre_split_mn1.proTx, + "127.0.0.1:1001", "", pre_split_mn1.operator_sk) # Send valid proUpReg tx to the mempool - operator_to_reuse = nodeA.getnewaddress() + operator_to_reuse = nodeA.generateblskeypair()["public"] proupreg1_txid = nodeA.protx_update_registrar(mnsA[4].proTx, operator_to_reuse, "", "") # Try sending another one, reusing the operator key used by another mempool proTx self.log.info("Testing proUpReg in-mempool duplicate-operator-key rejection...") assert_raises_rpc_error(-26, "protx-dup", nodeA.protx_update_registrar, - mnsA[5].proTx, mempool_dmn1.operator, "", "") + mnsA[5].proTx, mempool_dmn1.operator_pk, "", "") # Now send other two valid proUpServ txes to the mempool, without mining them new_voting_address = nodeA.getnewaddress() @@ -197,8 +201,8 @@ def run_test(self): # Send two valid proUpRev txes to the mempool, without mining them self.log.info("Revoking two masternodes...") - prouprev1_txid = nodeA.protx_revoke(mnsA[6].proTx) - prouprev2_txid = nodeA.protx_revoke(pre_split_mn2.proTx) + prouprev1_txid = nodeA.protx_revoke(mnsA[6].proTx, mnsA[6].operator_sk) + prouprev2_txid = nodeA.protx_revoke(pre_split_mn2.proTx, pre_split_mn2.operator_sk) # Now nodeA has 4 proReg txes in its mempool, 3 proUpServ txes, 3 proUpReg txes, and 2 proUpRev mempoolA = nodeA.getrawmempool() @@ -269,7 +273,7 @@ def run_test(self): # Register one masternode reusing the operator-key of the proUpReg mempool tx on chainA dmnop = create_new_dmn(free_idx, nodeB, collateral_addr, None) free_idx += 1 - dmnop.operator = operator_to_reuse + dmnop.operator_pk = operator_to_reuse mnsB.append(dmnop) self.register_masternode(nodeB, dmnop, collateral_addr) @@ -329,7 +333,7 @@ def run_test(self): assert rereg_mn.proTx not in mempoolA assert replay_mn.proTx not in mempoolA assert pre_split_mn1.proTx not in mempoolA - assert pre_split_mn1.proTx not in mempoolA + assert pre_split_mn2.proTx not in mempoolA # Mine a block from nodeA so the mempool txes get included self.log.info("Mining mempool txes...") @@ -345,9 +349,10 @@ def run_test(self): pre_split_mn1.voting = new_voting_address mnsB.append(pre_split_mn1) # prouprev2 has revoked pre_split masternode 2 - pre_split_mn2.ipport = "[::]:0" - pre_split_mn2.operator = "" - pre_split_mn2.operator_key = None + mnsB.remove(pre_split_mn2) + pre_split_mn2.revoked() + mnsB.append(pre_split_mn2) + # the ProReg txes, that were added back to the mempool from the # disconnected blocks, have been mined again mns_all = mnsA + mnsB