From 6c6e1c87e9857e93018a3cd706f168225db60f3c Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 24 Jan 2022 16:56:16 -0300 Subject: [PATCH 1/7] Introduce bls key encoding and decoding methods. --- CMakeLists.txt | 1 + src/Makefile.am | 2 ++ src/bls/key_io.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++ src/bls/key_io.h | 24 +++++++++++++ src/chainparams.cpp | 9 +++++ src/chainparams.h | 3 ++ src/test/bls_tests.cpp | 55 ++++++++++++++++++++++++++++++ 7 files changed, 170 insertions(+) create mode 100644 src/bls/key_io.cpp create mode 100644 src/bls/key_io.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b1e87ab806fb7..21011c5eb751b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,6 +397,7 @@ set(COMMON_SOURCES ./src/bls/bls_ies.cpp ./src/bls/bls_worker.cpp ./src/bls/bls_wrapper.cpp + ./src/bls/key_io.cpp ./src/budget/budgetdb.cpp ./src/budget/budgetmanager.cpp ./src/budget/budgetproposal.cpp diff --git a/src/Makefile.am b/src/Makefile.am index c0d4f60be196f..31a861fb69c59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -153,6 +153,7 @@ BITCOIN_CORE_H = \ bls/bls_ies.h \ bls/bls_worker.h \ bls/bls_wrapper.h \ + bls/key_io.h \ chain.h \ chainparams.h \ chainparamsbase.h \ @@ -345,6 +346,7 @@ libbitcoin_server_a_SOURCES = \ bls/bls_ies.cpp \ bls/bls_worker.cpp \ bls/bls_wrapper.cpp \ + bls/key_io.cpp \ chain.cpp \ checkpoints.cpp \ consensus/params.cpp \ diff --git a/src/bls/key_io.cpp b/src/bls/key_io.cpp new file mode 100644 index 0000000000000..ef37315104721 --- /dev/null +++ b/src/bls/key_io.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2022 The PIVX developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#include "bls/key_io.h" + +#include "chainparams.h" +#include "bech32.h" +#include "bls/bls_wrapper.h" + +namespace bls { + +template +static std::string EncodeBLS(const CChainParams& params, + const BLSKey& key, + CChainParams::Bech32Type type) +{ + assert(key.IsValid()); + auto vec{key.ToByteVector()}; + // ConvertBits requires unsigned char, but CDataStream uses char + std::vector ss(vec.begin(), vec.end()); + std::vector data; + // See calculation comment below + data.reserve((ss.size() * 8 + 4) / 5); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, ss.begin(), ss.end()); + auto res = bech32::Encode(params.Bech32HRP(type), data); + memory_cleanse(ss.data(), ss.size()); + memory_cleanse(data.data(), data.size()); + return res; +} + +template +static Optional DecodeBLS(const CChainParams& params, + const std::string& keyStr, + CChainParams::Bech32Type type, + unsigned int keySize) +{ + if (keyStr.empty()) return nullopt; + auto bech = bech32::Decode(keyStr); + if (bech.first == params.Bech32HRP(type) && bech.second.size() == keySize) { + std::vector data; + data.reserve((bech.second.size() * 5) / 8); + if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) { + CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); + BLSKey key; + ss >> key; + if (key.IsValid()) return {key}; + } + } + return nullopt; +} + +std::string EncodeSecret(const CChainParams& params, const CBLSSecretKey& key) +{ + return EncodeBLS(params, key, CChainParams::BLS_SECRET_KEY); +} + +std::string EncodePublic(const CChainParams& params, const CBLSPublicKey& pk) +{ + return EncodeBLS(params, pk, CChainParams::BLS_PUBLIC_KEY); +} + +const size_t ConvertedBlsSkSize = (BLS_CURVE_SECKEY_SIZE * 8 + 4) / 5; +const size_t ConvertedBlsPkSize = (BLS_CURVE_PUBKEY_SIZE * 8 + 4) / 5; + +Optional DecodeSecret(const CChainParams& params, const std::string& keyStr) +{ + return DecodeBLS(params, keyStr, CChainParams::BLS_SECRET_KEY, ConvertedBlsSkSize); +} + +Optional DecodePublic(const CChainParams& params, const std::string& keyStr) +{ + return DecodeBLS(params, keyStr, CChainParams::BLS_PUBLIC_KEY, ConvertedBlsPkSize); +} + +} // end bls namespace diff --git a/src/bls/key_io.h b/src/bls/key_io.h new file mode 100644 index 0000000000000..04df90a8de3d0 --- /dev/null +++ b/src/bls/key_io.h @@ -0,0 +1,24 @@ +// Copyright (c) 2022 The PIVX developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php. + +#ifndef PIVX_BLS_KEY_IO_H +#define PIVX_BLS_KEY_IO_H + +#include "optional.h" + +class CChainParams; +class CBLSPublicKey; +class CBLSSecretKey; + +namespace bls { + + std::string EncodeSecret(const CChainParams& params, const CBLSSecretKey& key); + Optional DecodeSecret(const CChainParams& params, const std::string& keyStr); + + std::string EncodePublic(const CChainParams& params, const CBLSPublicKey& pk); + Optional DecodePublic(const CChainParams& params, const std::string& keyStr); + +} // end bls namespace + +#endif //PIVX_BLS_KEY_IO_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 33ed35cbe472a..7a7389d3c3675 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -334,6 +334,9 @@ class CMainParams : public CChainParams bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "p-secret-spending-key-main"; bech32HRPs[SAPLING_EXTENDED_FVK] = "pxviews"; + bech32HRPs[BLS_SECRET_KEY] = "bls-sk"; + bech32HRPs[BLS_PUBLIC_KEY] = "bls-pk"; + // long living quorum params consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; @@ -468,6 +471,9 @@ class CTestNetParams : public CChainParams bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "p-secret-spending-key-test"; bech32HRPs[SAPLING_EXTENDED_FVK] = "pxviewtestsapling"; + bech32HRPs[BLS_SECRET_KEY] = "bls-sk-test"; + bech32HRPs[BLS_PUBLIC_KEY] = "bls-pk-test"; + // long living quorum params consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; @@ -602,6 +608,9 @@ class CRegTestParams : public CChainParams bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "p-secret-spending-key-test"; bech32HRPs[SAPLING_EXTENDED_FVK] = "pxviewtestsapling"; + bech32HRPs[BLS_SECRET_KEY] = "bls-sk-test"; + bech32HRPs[BLS_PUBLIC_KEY] = "bls-pk-test"; + // long living quorum params consensus.llmqs[Consensus::LLMQ_TEST] = llmq_test; nLLMQConnectionRetryTimeout = 5; diff --git a/src/chainparams.h b/src/chainparams.h index 45825c4190ed9..8a37f13a893cd 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -62,6 +62,9 @@ class CChainParams SAPLING_EXTENDED_SPEND_KEY, SAPLING_EXTENDED_FVK, + BLS_SECRET_KEY, + BLS_PUBLIC_KEY, + MAX_BECH32_TYPES }; diff --git a/src/test/bls_tests.cpp b/src/test/bls_tests.cpp index da5475a06bf26..5359de0760eef 100644 --- a/src/test/bls_tests.cpp +++ b/src/test/bls_tests.cpp @@ -4,6 +4,7 @@ #include "test/test_pivx.h" #include "bls/bls_ies.h" +#include "bls/key_io.h" #include "bls/bls_worker.h" #include "bls/bls_wrapper.h" #include "random.h" @@ -250,4 +251,58 @@ BOOST_AUTO_TEST_CASE(bls_ies_tests) BOOST_CHECK(decrypted_message2 != message); } +BOOST_AUTO_TEST_CASE(bls_sk_io_tests) +{ + const auto& params = Params(); + + CBLSSecretKey sk; + sk.SetHexStr("2eb071f4c520b3102e8cb9f520783da252d33993dba0313b501d69d113af9d39"); + BOOST_ASSERT(sk.IsValid()); + + // Basic encoding-decoding roundtrip + std::string encodedSk = bls::EncodeSecret(params, sk); + auto opSk2 = bls::DecodeSecret(params, encodedSk); + BOOST_CHECK(opSk2 != nullopt); + CBLSSecretKey sk2 = *opSk2; + BOOST_CHECK(sk == sk2); + + // Invalid sk, one extra char + encodedSk.push_back('f'); + auto opSk3 = bls::DecodeSecret(params, encodedSk); + BOOST_CHECK(opSk3 == nullopt); + + // Invalid sk, one less char + encodedSk.pop_back(); + encodedSk.pop_back(); + auto opSk4 = bls::DecodeSecret(params, encodedSk); + BOOST_CHECK(opSk4 == nullopt); +} + +BOOST_AUTO_TEST_CASE(bls_pk_io_tests) +{ + const auto& params = Params(); + + CBLSPublicKey pk; + pk.SetHexStr("901138a12a352c7e30408c071b1ec097f32ab735a12c8dbb43c637612a3f805668a6bb73894982366d287cf0b02aaf5b"); + BOOST_ASSERT(pk.IsValid()); + + // Basic encoding-decoding roundtrip + std::string encodedPk = bls::EncodePublic(params, pk); + auto opPk2 = bls::DecodePublic(params, encodedPk); + BOOST_CHECK(opPk2 != nullopt); + CBLSPublicKey pk2 = *opPk2; + BOOST_CHECK(pk == pk2); + + // Invalid pk, one extra char + encodedPk.push_back('f'); + auto oppk3 = bls::DecodePublic(params, encodedPk); + BOOST_CHECK(oppk3 == nullopt); + + // Invalid pk, one less char + encodedPk.pop_back(); + encodedPk.pop_back(); + auto oppk4 = bls::DecodePublic(params, encodedPk); + BOOST_CHECK(oppk4 == nullopt); +} + BOOST_AUTO_TEST_SUITE_END() From 31a5335017099a6f880c1af812ddb9ab8369059d Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 24 Jan 2022 17:02:35 -0300 Subject: [PATCH 2/7] RPC: connect encode/decode bls keys --- src/activemasternode.cpp | 6 +++++- src/rpc/rpcevo.cpp | 46 +++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index d3cd51f2e27c8..0bfcd3b611d4c 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -6,6 +6,7 @@ #include "activemasternode.h" #include "addrman.h" +#include "bls/key_io.h" #include "bls/bls_wrapper.h" #include "masternode.h" #include "masternodeconfig.h" @@ -61,9 +62,12 @@ OperationResult CActiveDeterministicMasternodeManager::SetOperatorKey(const std: if (strMNOperatorPrivKey.empty()) { return errorOut("ERROR: Masternode operator priv key cannot be empty."); } - if (!info.keyOperator.SetHexStr(strMNOperatorPrivKey)) { + + auto opSk = bls::DecodeSecret(Params(), strMNOperatorPrivKey); + if (!opSk) { return errorOut(_("Invalid mnoperatorprivatekey. Please see the documentation.")); } + info.keyOperator = *opSk; info.pubKeyOperator = info.keyOperator.GetPublicKey(); return OperationResult(true); } diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 9f6154463f98b..1dd479385c27c 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "activemasternode.h" +#include "bls/key_io.h" #include "bls/bls_wrapper.h" #include "core_io.h" #include "destination_io.h" @@ -212,28 +213,28 @@ static CKeyID ParsePubKeyIDFromAddress(const std::string& strAddress) return *keyID; } -static CBLSPublicKey ParseBLSPubKey(const std::string& hexKey) +static CBLSPublicKey ParseBLSPubKey(const CChainParams& params, const std::string& strKey) { - CBLSPublicKey pubKey; - if (!pubKey.SetHexStr(hexKey)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS public key: %s", hexKey)); + auto opKey = bls::DecodePublic(params, strKey); + if (!opKey) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS public key: %s", strKey)); } - return pubKey; + return *opKey; } -static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey) +static CBLSSecretKey ParseBLSSecretKey(const CChainParams& params, const std::string& strKey) { - CBLSSecretKey secKey; - if (!secKey.SetHexStr(hexKey)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS secret key: %s", hexKey)); + auto opKey = bls::DecodeSecret(params, strKey); + if (!opKey) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS secret key: %s", strKey)); } - return secKey; + return *opKey; } -static CBLSSecretKey GetBLSSecretKey(const std::string& hexKey) +static CBLSSecretKey GetBLSSecretKey(const CChainParams& params, const std::string& hexKey) { if (!hexKey.empty()) { - return ParseBLSSecretKey(hexKey); + return ParseBLSSecretKey(params, hexKey); } // If empty, get the active masternode key CBLSSecretKey sk; CTxIn vin; @@ -390,12 +391,13 @@ static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramId { assert(params.size() > paramIdx + 4); assert(params.size() < paramIdx + 8); + const auto& chainparams = Params(); ProRegPL pl; // ip and port const std::string& strIpPort = params[paramIdx].get_str(); if (!strIpPort.empty()) { - if (!Lookup(strIpPort, pl.addr, Params().GetDefaultPort(), false)) { + if (!Lookup(strIpPort, pl.addr, chainparams.GetDefaultPort(), false)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid network address %s", strIpPort)); } } @@ -405,7 +407,7 @@ static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramId const std::string& strPubKeyOperator = params[paramIdx + 2].get_str(); const std::string& strAddVoting = params[paramIdx + 3].get_str(); pl.keyIDOwner = ParsePubKeyIDFromAddress(strAddOwner); - pl.pubKeyOperator = ParseBLSPubKey(strPubKeyOperator); + pl.pubKeyOperator = ParseBLSPubKey(chainparams, strPubKeyOperator); pl.keyIDVoting = pl.keyIDOwner; if (!strAddVoting.empty()) { pl.keyIDVoting = ParsePubKeyIDFromAddress(strAddVoting); @@ -857,9 +859,10 @@ UniValue protx_update_service(const JSONRPCRequest& request) if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode with hash %s not found", pl.proTxHash.ToString())); } + const auto& chainparams = Params(); const std::string& addrStr = request.params[1].get_str(); if (!addrStr.empty()) { - if (!Lookup(addrStr.c_str(), pl.addr, Params().GetDefaultPort(), false)) { + if (!Lookup(addrStr.c_str(), pl.addr, chainparams.GetDefaultPort(), false)) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid network address %s", addrStr)); } } else { @@ -878,7 +881,7 @@ UniValue protx_update_service(const JSONRPCRequest& request) } const std::string& strOpKey = request.params.size() > 3 ? request.params[3].get_str() : ""; - const CBLSSecretKey& operatorKey = GetBLSSecretKey(strOpKey); + const CBLSSecretKey& operatorKey = GetBLSSecretKey(chainparams, strOpKey); CMutableTransaction tx; tx.nVersion = CTransaction::TxVersion::SAPLING; @@ -926,6 +929,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); + const auto& chainparams = Params(); ProUpRegPL pl; pl.nVersion = ProUpServPL::CURRENT_VERSION; @@ -937,7 +941,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) } const std::string& strPubKeyOperator = request.params[1].get_str(); pl.pubKeyOperator = strPubKeyOperator.empty() ? dmn->pdmnState->pubKeyOperator.Get() - : ParseBLSPubKey(strPubKeyOperator); + : ParseBLSPubKey(chainparams, strPubKeyOperator); const std::string& strVotingAddress = request.params[2].get_str(); pl.keyIDVoting = strVotingAddress.empty() ? dmn->pdmnState->keyIDVoting @@ -995,6 +999,7 @@ UniValue protx_revoke(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); + const auto& chainparams = Params(); ProUpRevPL pl; pl.nVersion = ProUpServPL::CURRENT_VERSION; @@ -1006,7 +1011,7 @@ UniValue protx_revoke(const JSONRPCRequest& request) } const std::string& strOpKey = request.params.size() > 1 ? request.params[1].get_str() : ""; - const CBLSSecretKey& operatorKey = GetBLSSecretKey(strOpKey); + const CBLSSecretKey& operatorKey = GetBLSSecretKey(chainparams, strOpKey); pl.nReason = ProUpRevPL::RevocationReason::REASON_NOT_SPECIFIED; if (request.params.size() > 2) { @@ -1046,11 +1051,12 @@ UniValue generateblskeypair(const JSONRPCRequest& request) ); } + const auto& params = Params(); CBLSSecretKey sk; sk.MakeNewKey(); UniValue ret(UniValue::VOBJ); - ret.pushKV("secret", sk.ToString()); - ret.pushKV("public", sk.GetPublicKey().ToString()); + ret.pushKV("secret", bls::EncodeSecret(params, sk)); + ret.pushKV("public", bls::EncodePublic(params, sk.GetPublicKey())); return ret; } From 618ca5404a5b71d3b2a61b224ccf332695c6fc49 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 25 Jan 2022 10:28:30 +0100 Subject: [PATCH 3/7] Refactor: Return bech32 encoded string for BLS public keys --- src/bls/bls_wrapper.h | 4 +++- src/bls/key_io.cpp | 2 +- src/evo/deterministicmns.cpp | 7 ++++--- src/evo/providertx.cpp | 9 +++++---- src/llmq/quorums_blockprocessor.cpp | 3 ++- src/llmq/quorums_commitment.cpp | 3 ++- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/bls/bls_wrapper.h b/src/bls/bls_wrapper.h index 0e3184d99cd53..70424c5ff47a0 100644 --- a/src/bls/bls_wrapper.h +++ b/src/bls/bls_wrapper.h @@ -173,7 +173,9 @@ class CBLSWrapper } return true; } - + + // hex-encoding. Used only for signatures. + // For secret/public keys use bls::EncodeSecret/EncodePublic inline std::string ToString() const { std::vector buf = ToByteVector(); diff --git a/src/bls/key_io.cpp b/src/bls/key_io.cpp index ef37315104721..69f5313983d3f 100644 --- a/src/bls/key_io.cpp +++ b/src/bls/key_io.cpp @@ -57,7 +57,7 @@ std::string EncodeSecret(const CChainParams& params, const CBLSSecretKey& key) std::string EncodePublic(const CChainParams& params, const CBLSPublicKey& pk) { - return EncodeBLS(params, pk, CChainParams::BLS_PUBLIC_KEY); + return EncodeBLS(params, pk, CChainParams::BLS_PUBLIC_KEY); } const size_t ConvertedBlsSkSize = (BLS_CURVE_SECKEY_SIZE * 8 + 4) / 5; diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 94daab94ba30f..82984183afad0 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -5,6 +5,7 @@ #include "evo/deterministicmns.h" +#include "bls/key_io.h" #include "chain.h" #include "coins.h" #include "chainparams.h" @@ -41,7 +42,7 @@ std::string CDeterministicMNState::ToString() const 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), pubKeyOperator.Get().ToString(), EncodeDestination(keyIDVoting), addr.ToStringIPPort(), payoutAddress, operatorPayoutAddress); + EncodeDestination(keyIDOwner), bls::EncodePublic(Params(), pubKeyOperator.Get()), EncodeDestination(keyIDVoting), addr.ToStringIPPort(), payoutAddress, operatorPayoutAddress); } void CDeterministicMNState::ToJson(UniValue& obj) const @@ -56,7 +57,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const obj.pushKV("PoSeBanHeight", nPoSeBanHeight); obj.pushKV("revocationReason", nRevocationReason); obj.pushKV("ownerAddress", EncodeDestination(keyIDOwner)); - obj.pushKV("operatorPubKey", pubKeyOperator.Get().ToString()); + obj.pushKV("operatorPubKey", bls::EncodePublic(Params(), pubKeyOperator.Get())); obj.pushKV("votingAddress", EncodeDestination(keyIDVoting)); CTxDestination dest1; @@ -389,7 +390,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota 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->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()))); + throw(std::runtime_error(strprintf("%s: can't add a masternode with a duplicate key (%s or %s)", __func__, EncodeDestination(dmn->pdmnState->keyIDOwner), bls::EncodePublic(Params(), dmn->pdmnState->pubKeyOperator.Get())))); } mnMap = mnMap.set(dmn->proTxHash, dmn); diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 24de86715676b..ac203bc0a432e 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -5,6 +5,7 @@ #include "evo/providertx.h" +#include "bls/key_io.h" #include "key_io.h" std::string ProRegPL::MakeSignString() const @@ -28,7 +29,7 @@ std::string ProRegPL::ToString() const std::string payee = ExtractDestination(scriptPayout, dest) ? EncodeDestination(dest) : "unknown"; 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); + nVersion, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(keyIDOwner), bls::EncodePublic(Params(), pubKeyOperator), EncodeDestination(keyIDVoting), payee); } void ProRegPL::ToJson(UniValue& obj) const @@ -40,7 +41,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("operatorPubKey", pubKeyOperator.ToString()); + obj.pushKV("operatorPubKey", bls::EncodePublic(Params(), pubKeyOperator)); obj.pushKV("votingAddress", EncodeDestination(keyIDVoting)); CTxDestination dest1; @@ -84,7 +85,7 @@ std::string ProUpRegPL::ToString() const std::string payee = ExtractDestination(scriptPayout, dest) ? EncodeDestination(dest) : "unknown"; return strprintf("ProUpRegPL(nVersion=%d, proTxHash=%s, operatorPubKey=%s, votingAddress=%s, payoutAddress=%s)", - nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(keyIDVoting), payee); + nVersion, proTxHash.ToString(), bls::EncodePublic(Params(), pubKeyOperator), EncodeDestination(keyIDVoting), payee); } void ProUpRegPL::ToJson(UniValue& obj) const @@ -98,7 +99,7 @@ void ProUpRegPL::ToJson(UniValue& obj) const if (ExtractDestination(scriptPayout, dest)) { obj.pushKV("payoutAddress", EncodeDestination(dest)); } - obj.pushKV("operatorPubKey", pubKeyOperator.ToString()); + obj.pushKV("operatorPubKey", bls::EncodePublic(Params(), pubKeyOperator)); obj.pushKV("inputsHash", inputsHash.ToString()); } diff --git a/src/llmq/quorums_blockprocessor.cpp b/src/llmq/quorums_blockprocessor.cpp index 101496a110c6a..5470dc34f5b67 100644 --- a/src/llmq/quorums_blockprocessor.cpp +++ b/src/llmq/quorums_blockprocessor.cpp @@ -5,6 +5,7 @@ #include "llmq/quorums_blockprocessor.h" +#include "bls/key_io.h" #include "chain.h" #include "chainparams.h" #include "consensus/validation.h" @@ -181,7 +182,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH } LogPrintf("%s: processed commitment from block. type=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s\n", __func__, - qc.llmqType, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), ""/*qc.quorumPublicKey.ToString()*/); + qc.llmqType, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), bls::EncodePublic(Params(), qc.quorumPublicKey)); return true; } diff --git a/src/llmq/quorums_commitment.cpp b/src/llmq/quorums_commitment.cpp index d35e881728bdd..a1c03c6374f77 100644 --- a/src/llmq/quorums_commitment.cpp +++ b/src/llmq/quorums_commitment.cpp @@ -5,6 +5,7 @@ #include "llmq/quorums_commitment.h" +#include "bls/key_io.h" #include "chainparams.h" #include "llmq/quorums_utils.h" #include "logging.h" @@ -47,7 +48,7 @@ void CFinalCommitment::ToJson(UniValue& obj) const obj.pushKV("signers", utils::ToHexStr(signers)); obj.pushKV("validMembersCount", CountValidMembers()); obj.pushKV("validMembers", utils::ToHexStr(validMembers)); - obj.pushKV("quorumPublicKey", quorumPublicKey.ToString()); + obj.pushKV("quorumPublicKey", bls::EncodePublic(Params(), quorumPublicKey)); obj.pushKV("quorumVvecHash", quorumVvecHash.ToString()); obj.pushKV("quorumSig", quorumSig.ToString()); obj.pushKV("membersSig", membersSig.ToString()); From ddff90abdc9daf10a3c1bee12c63b6fce91dba56 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 25 Jan 2022 10:42:02 +0100 Subject: [PATCH 4/7] Cleanup:: Remove unused CBLSWrapper::SetHexStr --- src/bls/bls_wrapper.h | 17 +---------------- src/test/bls_tests.cpp | 32 ++++++++++---------------------- 2 files changed, 11 insertions(+), 38 deletions(-) diff --git a/src/bls/bls_wrapper.h b/src/bls/bls_wrapper.h index 70424c5ff47a0..9cbb1912844d3 100644 --- a/src/bls/bls_wrapper.h +++ b/src/bls/bls_wrapper.h @@ -129,21 +129,6 @@ class CBLSWrapper return cachedHash; } - bool SetHexStr(const std::string& str) - { - if (!IsHex(str)) { - Reset(); - return false; - } - auto b = ParseHex(str); - if (b.size() != SerSize) { - Reset(); - return false; - } - SetByteVector(b); - return IsValid(); - } - public: template inline void Serialize(Stream& s) const @@ -173,7 +158,7 @@ class CBLSWrapper } return true; } - + // hex-encoding. Used only for signatures. // For secret/public keys use bls::EncodeSecret/EncodePublic inline std::string ToString() const diff --git a/src/test/bls_tests.cpp b/src/test/bls_tests.cpp index 5359de0760eef..ba983693bfc77 100644 --- a/src/test/bls_tests.cpp +++ b/src/test/bls_tests.cpp @@ -13,24 +13,6 @@ BOOST_FIXTURE_TEST_SUITE(bls_tests, BasicTestingSetup) -BOOST_AUTO_TEST_CASE(bls_sethexstr_tests) -{ - CBLSSecretKey sk; - std::string strValidSecret = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; - // Note: invalid string passed to SetHexStr() should cause it to fail and reset key internal data - BOOST_CHECK(sk.SetHexStr(strValidSecret)); - BOOST_CHECK(!sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1g")); // non-hex - BOOST_CHECK(!sk.IsValid()); - BOOST_CHECK(sk == CBLSSecretKey()); - // Try few more invalid strings - BOOST_CHECK(sk.SetHexStr(strValidSecret)); - BOOST_CHECK(!sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e")); // hex but too short - BOOST_CHECK(!sk.IsValid()); - BOOST_CHECK(sk.SetHexStr(strValidSecret)); - BOOST_CHECK(!sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20")); // hex but too long - BOOST_CHECK(!sk.IsValid()); -} - BOOST_AUTO_TEST_CASE(bls_sig_tests) { CBLSSecretKey sk1, sk2; @@ -251,12 +233,19 @@ BOOST_AUTO_TEST_CASE(bls_ies_tests) BOOST_CHECK(decrypted_message2 != message); } +template +BLSKey FromHex(const std::string& str) +{ + BLSKey k; + k.SetByteVector(ParseHex(str)); + return k; +} + BOOST_AUTO_TEST_CASE(bls_sk_io_tests) { const auto& params = Params(); - CBLSSecretKey sk; - sk.SetHexStr("2eb071f4c520b3102e8cb9f520783da252d33993dba0313b501d69d113af9d39"); + CBLSSecretKey sk = FromHex("2eb071f4c520b3102e8cb9f520783da252d33993dba0313b501d69d113af9d39"); BOOST_ASSERT(sk.IsValid()); // Basic encoding-decoding roundtrip @@ -282,8 +271,7 @@ BOOST_AUTO_TEST_CASE(bls_pk_io_tests) { const auto& params = Params(); - CBLSPublicKey pk; - pk.SetHexStr("901138a12a352c7e30408c071b1ec097f32ab735a12c8dbb43c637612a3f805668a6bb73894982366d287cf0b02aaf5b"); + CBLSPublicKey pk = FromHex("901138a12a352c7e30408c071b1ec097f32ab735a12c8dbb43c637612a3f805668a6bb73894982366d287cf0b02aaf5b"); BOOST_ASSERT(pk.IsValid()); // Basic encoding-decoding roundtrip From 6fce6975d55fac00eff016beb798d8a9601efca0 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 25 Jan 2022 11:25:27 +0100 Subject: [PATCH 5/7] BUG: fix empty bls key encoding for revoked masternodes --- src/bls/key_io.cpp | 2 +- test/functional/test_framework/messages.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bls/key_io.cpp b/src/bls/key_io.cpp index 69f5313983d3f..dfeb916521ef4 100644 --- a/src/bls/key_io.cpp +++ b/src/bls/key_io.cpp @@ -15,7 +15,7 @@ static std::string EncodeBLS(const CChainParams& params, const BLSKey& key, CChainParams::Bech32Type type) { - assert(key.IsValid()); + if (!key.IsValid()) return ""; auto vec{key.ToByteVector()}; // ConvertBits requires unsigned char, but CDataStream uses char std::vector ss(vec.begin(), vec.end()); diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 01e0067673c70..be740415b2028 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1492,7 +1492,7 @@ def __init__(self, idx, owner_addr, operator_pk, voting_addr, ipport, payout_add def revoked(self): self.ipport = "[::]:0" - self.operator_pk = "0" * 96 + self.operator_pk = "" self.operator_sk = None def __repr__(self): From cd2ebeb136b29397d5d916c87652ff5e378e3785 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 25 Jan 2022 13:02:16 +0100 Subject: [PATCH 6/7] QA: Add bech32 decoding to python framework and fix p2p_quorum_connect --- test/functional/p2p_quorum_connect.py | 4 +-- test/functional/test_framework/util.py | 37 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/test/functional/p2p_quorum_connect.py b/test/functional/p2p_quorum_connect.py index f6160d5ccb740..10b9fb8e2495b 100755 --- a/test/functional/p2p_quorum_connect.py +++ b/test/functional/p2p_quorum_connect.py @@ -16,7 +16,7 @@ bytes_to_hex_str, connect_nodes_clique, hash256, - hex_str_to_bytes, + bech32_str_to_bytes, wait_until, ) @@ -108,7 +108,7 @@ def check_peers_count(self, expected_count): def check_peer_info(self, peer_info, mn, is_iqr_conn, inbound=False): assert_equal(peer_info["masternode"], True) assert_equal(peer_info["verif_mn_proreg_tx_hash"], mn.proTx) - assert_equal(peer_info["verif_mn_operator_pubkey_hash"], bytes_to_hex_str(hash256(hex_str_to_bytes(mn.operator_pk)))) + assert_equal(peer_info["verif_mn_operator_pubkey_hash"], bytes_to_hex_str(hash256(bech32_str_to_bytes(mn.operator_pk)))) assert_equal(peer_info["masternode_iqr_conn"], is_iqr_conn) # An inbound connection has obviously a different internal port. if not inbound: diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 8e79bdd48b824..5a1e8202659b4 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -201,6 +201,43 @@ def hash256(byte_str): def hex_str_to_bytes(hex_str): return unhexlify(hex_str.encode('ascii')) +def convertbits(data, frombits, tobits, pad=True): + """General power-of-2 base conversion.""" + acc = 0 + bits = 0 + ret = [] + maxv = (1 << tobits) - 1 + max_acc = (1 << (frombits + tobits - 1)) - 1 + for value in data: + if value < 0 or (value >> frombits): + return None + acc = ((acc << frombits) | value) & max_acc + bits += frombits + while bits >= tobits: + bits -= tobits + ret.append((acc >> bits) & maxv) + if pad: + if bits: + ret.append((acc << (tobits - bits)) & maxv) + elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + return None + return ret + +def bech32_str_to_bytes(bech_str): + BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + bech_str = bech_str.lower() + pos = bech_str.rfind('1') + if pos < 1 or pos + 7 > len(bech_str): + return None + if not all(x in BECH32_CHARSET for x in bech_str[pos+1:]): + return None + # !TODO: verify checksum + data = [BECH32_CHARSET.find(x) for x in bech_str[pos+1:]] + decoded = convertbits(data[:-6], 5, 8, False) + if decoded is None: + return None + return bytes(decoded) + def str_to_b64str(string): return b64encode(string.encode('utf-8')).decode('ascii') From 3d2a71a0c53b4297e382bbea9d1f86a50546d43b Mon Sep 17 00:00:00 2001 From: random-zebra Date: Tue, 25 Jan 2022 13:23:43 +0100 Subject: [PATCH 7/7] Cleanup: remove unneeded copies CBLSWrapper::ToByteVector does not return a CDataStream --- src/bls/key_io.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/bls/key_io.cpp b/src/bls/key_io.cpp index dfeb916521ef4..1187311acb73b 100644 --- a/src/bls/key_io.cpp +++ b/src/bls/key_io.cpp @@ -16,15 +16,12 @@ static std::string EncodeBLS(const CChainParams& params, CChainParams::Bech32Type type) { if (!key.IsValid()) return ""; - auto vec{key.ToByteVector()}; - // ConvertBits requires unsigned char, but CDataStream uses char - std::vector ss(vec.begin(), vec.end()); + std::vector vec{key.ToByteVector()}; std::vector data; - // See calculation comment below - data.reserve((ss.size() * 8 + 4) / 5); - ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, ss.begin(), ss.end()); + data.reserve((vec.size() * 8 + 4) / 5); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, vec.begin(), vec.end()); auto res = bech32::Encode(params.Bech32HRP(type), data); - memory_cleanse(ss.data(), ss.size()); + memory_cleanse(vec.data(), vec.size()); memory_cleanse(data.data(), data.size()); return res; }