diff --git a/doc/release-notes-7594.md b/doc/release-notes-7594.md new file mode 100644 index 000000000000..c830898ea4b0 --- /dev/null +++ b/doc/release-notes-7594.md @@ -0,0 +1,8 @@ +Wallet +------ + +- Mnemonic-backed descriptor wallets can now derive DashSync-compatible + masternode operator BLS keys from the wallet seed, so the recovery phrase + also backs up operator keys. Restored wallets avoid keys that are currently + registered, but may reuse a key that was retired in the past. Other wallet + types remain unchanged and can continue using `bls generate`. (#7594) diff --git a/src/Makefile.am b/src/Makefile.am index 0cf8ce802fdb..997f65a5350b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -281,6 +281,7 @@ BITCOIN_CORE_H = \ interfaces/handler.h \ interfaces/init.h \ interfaces/ipc.h \ + interfaces/masternode_operator.h \ interfaces/node.h \ interfaces/providertx.h \ interfaces/wallet.h \ @@ -478,6 +479,7 @@ BITCOIN_CORE_H = \ wallet/hdchain.h \ wallet/ismine.h \ wallet/load.h \ + wallet/masternode_operator_types.h \ wallet/receive.h \ wallet/rpc/util.h \ wallet/rpc/wallet.h \ @@ -709,6 +711,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/hdchain.cpp \ wallet/interfaces.cpp \ wallet/load.cpp \ + wallet/masternode_operator.cpp \ wallet/receive.cpp \ wallet/rpc/addresses.cpp \ wallet/rpc/backup.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 805083ffeb5f..2b4be209884c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -224,6 +224,7 @@ if ENABLE_WALLET BITCOIN_TESTS += \ wallet/test/bip39_tests.cpp \ wallet/test/coinjoin_tests.cpp \ + wallet/test/masternode_operator_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/spend_tests.cpp \ wallet/test/wallet_tests.cpp \ diff --git a/src/bls/bls.h b/src/bls/bls.h index ae2124cd38e1..f08f448a914e 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -283,6 +283,13 @@ class CBLSSecretKey : public CBLSWrapper bytes) const + { + if (!IsValid() || bytes.size() != SerSize) return false; + impl.Serialize(bytes.data()); + return true; + } + void AggregateInsecure(const CBLSSecretKey& o); static CBLSSecretKey AggregateInsecure(Span sks); diff --git a/src/interfaces/masternode_operator.h b/src/interfaces/masternode_operator.h new file mode 100644 index 000000000000..6bbfeadb285a --- /dev/null +++ b/src/interfaces/masternode_operator.h @@ -0,0 +1,43 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_INTERFACES_MASTERNODE_OPERATOR_H +#define BITCOIN_INTERFACES_MASTERNODE_OPERATOR_H + +#include + +#include +#include +#include + +namespace interfaces { + +//! Result of a deterministic masternode operator-key operation. +enum class MasternodeOperatorKeyStatus : uint8_t { + SUCCESS, + NOT_SUPPORTED, + WALLET_LOCKED, + EXHAUSTED, + INVALID_KEY, + NOT_FOUND, + DATABASE_ERROR, + DERIVATION_ERROR, +}; + +//! A deterministic masternode operator key returned by the wallet. The public +//! key uses the canonical basic-scheme serialization. +struct MasternodeOperatorKey { + SecureVector secret_key; + std::vector public_key; + std::string path; +}; + +struct MasternodeOperatorKeyResult { + MasternodeOperatorKeyStatus status{MasternodeOperatorKeyStatus::DERIVATION_ERROR}; + MasternodeOperatorKey key; +}; + +} // namespace interfaces + +#endif // BITCOIN_INTERFACES_MASTERNODE_OPERATOR_H diff --git a/src/interfaces/node.h b/src/interfaces/node.h index f3362cefba6b..59a3be12e44a 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -30,6 +30,7 @@ class BanMan; class CBlockIndex; +class CBLSPublicKey; class CDeterministicMNList; class CFeeRate; class CGovernanceObject; @@ -151,6 +152,15 @@ class EVO Wallet& wallet, const ProviderUpdateRegistrarRequest& request) = 0; virtual ProviderTxResult revokeMasternode(Wallet& wallet, const ProviderRevokeRequest& request) = 0; + /** + * Whether an operator public key is assigned to any masternode in the + * deterministic list at the current chain tip, under either BLS scheme + * encoding. This is a UX guard for skipping keys that would be rejected + * by DIP3 duplicate-key checks, not a safety mechanism: when the node is + * not ready to answer (no tip or no masternode manager yet), it returns + * false. Keys used only historically also return false. + */ + virtual bool isMasternodeOperatorKeyInUse(const CBLSPublicKey& public_key) = 0; virtual void setContext(node::NodeContext* context) {} }; diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 9fc779906135..a78a20e4ff99 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -8,6 +8,7 @@ #include // For CAmount #include #include // For ChainClient +#include #include // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include