Bitcoin backport #16227 refactoring CWallet (#4903)

* Add HaveKey and HaveCScript to SigningProvider

* Remove CKeyStore and squash into CBasicKeyStore

* Move HaveKey static function from keystore to rpcwallet where it is used

* scripted-diff: rename CBasicKeyStore to FillableSigningProvider

-BEGIN VERIFY SCRIPT-
git grep -l "CBasicKeyStore" | xargs sed -i -e 's/CBasicKeyStore/FillableSigningProvider/g'
-END VERIFY SCRIPT-

* Move KeyOriginInfo to its own header file

* Move various SigningProviders to signingprovider.{cpp,h}

Moves all of the various SigningProviders out of sign.{cpp,h} and
keystore.{cpp,h}. As such, keystore.{cpp,h} is also removed.

Includes and the Makefile are updated to reflect this. Includes were largely
changed using:
git grep -l "keystore.h" | xargs sed -i -e 's;keystore.h;script/signingprovider.h;g'

* Remove CCryptoKeyStore and move all of it's functionality into CWallet

Instead of having a separate CCryptoKeyStore that handles the encryption
stuff, just roll it all into CWallet.

* Fixed cases of mess CWallet functions with CCryptoKeyStore and conflicts

* Move WatchOnly stuff from SigningProvider to CWallet

* Fixes for lint cirtular dependencies to calm linter

Co-authored-by: Andrew Chow <achow101-github@achow101.com>
This commit is contained in:
Konstantin Akimov 2022-07-13 10:46:31 +07:00 committed by GitHub
parent eefdae1a53
commit 0a951622f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 861 additions and 850 deletions

View File

@ -445,7 +445,7 @@ reported in the `debug.log` file.
Re-architecting the core code so there are better-defined interfaces
between the various components is a goal, with any necessary locking
done by the components (e.g. see the self-contained `CBasicKeyStore` class
done by the components (e.g. see the self-contained `FillableSigningProvider` class
and its `cs_KeyStore` lock for example).
Threads

View File

@ -195,7 +195,6 @@ BITCOIN_CORE_H = \
interfaces/wallet.h \
key.h \
key_io.h \
keystore.h \
dbwrapper.h \
limitedmap.h \
llmq/quorums.h \
@ -263,8 +262,10 @@ BITCOIN_CORE_H = \
saltedhasher.h \
scheduler.h \
script/descriptor.h \
script/keyorigin.h \
script/sigcache.h \
script/sign.h \
script/signingprovider.h \
script/standard.h \
shutdown.h \
spork.h \
@ -652,7 +653,6 @@ libdash_common_a_SOURCES = \
hdchain.cpp \
key.cpp \
key_io.cpp \
keystore.cpp \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
@ -667,6 +667,7 @@ libdash_common_a_SOURCES = \
scheduler.cpp \
script/descriptor.cpp \
script/sign.cpp \
script/signingprovider.cpp \
script/standard.cpp \
versionbitsinfo.cpp \
warnings.cpp \

View File

@ -5,7 +5,7 @@
#include <bench/bench.h>
#include <coins.h>
#include <policy/policy.h>
#include <wallet/crypter.h>
#include <script/signingprovider.h>
#include <vector>
@ -17,7 +17,7 @@
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
@ -58,7 +58,7 @@ static void CCoinsCaching(benchmark::Bench& bench)
const ECCVerifyHandle verify_handle;
ECC_Start();
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);

View File

@ -12,10 +12,10 @@
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
#include <keystore.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <univalue.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
@ -515,7 +515,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!registers.count("privatekeys"))
throw std::runtime_error("privatekeys register variable must be set.");
CBasicKeyStore tempKeystore;
FillableSigningProvider tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
@ -589,7 +589,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
}
}
const CKeyStore& keystore = tempKeystore;
const FillableSigningProvider& keystore = tempKeystore;
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);

View File

@ -518,7 +518,7 @@ public:
}
std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
{
return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CWallet*) { fn(); }));
}
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{

View File

@ -1,142 +0,0 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <keystore.h>
#include <util/system.h>
bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
{
CKey key;
if (!GetKey(address, key)) {
LOCK(cs_KeyStore);
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
if (it != mapWatchKeys.end()) {
vchPubKeyOut = it->second;
return true;
}
return false;
}
vchPubKeyOut = key.GetPubKey();
return true;
}
bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
mapKeys[pubkey.GetID()] = key;
return true;
}
bool CBasicKeyStore::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
return mapKeys.count(address) > 0;
}
std::set<CKeyID> CBasicKeyStore::GetKeys() const
{
LOCK(cs_KeyStore);
std::set<CKeyID> set_address;
for (const auto& mi : mapKeys) {
set_address.insert(mi.first);
}
return set_address;
}
bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const
{
LOCK(cs_KeyStore);
KeyMap::const_iterator mi = mapKeys.find(address);
if (mi != mapKeys.end()) {
keyOut = mi->second;
return true;
}
return false;
}
bool CBasicKeyStore::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
return error("CBasicKeyStore::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
LOCK(cs_KeyStore);
mapScripts[CScriptID(redeemScript)] = redeemScript;
return true;
}
bool CBasicKeyStore::HaveCScript(const CScriptID& hash) const
{
LOCK(cs_KeyStore);
return mapScripts.count(hash) > 0;
}
std::set<CScriptID> CBasicKeyStore::GetCScripts() const
{
LOCK(cs_KeyStore);
std::set<CScriptID> set_script;
for (const auto& mi : mapScripts) {
set_script.insert(mi.first);
}
return set_script;
}
bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
{
LOCK(cs_KeyStore);
ScriptMap::const_iterator mi = mapScripts.find(hash);
if (mi != mapScripts.end())
{
redeemScriptOut = (*mi).second;
return true;
}
return false;
}
static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
{
std::vector<std::vector<unsigned char>> solutions;
return Solver(dest, solutions) == TX_PUBKEY &&
(pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
}
bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
{
LOCK(cs_KeyStore);
setWatchOnly.insert(dest);
CPubKey pubKey;
if (ExtractPubKey(dest, pubKey))
mapWatchKeys[pubKey.GetID()] = pubKey;
return true;
}
bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest)
{
LOCK(cs_KeyStore);
setWatchOnly.erase(dest);
CPubKey pubKey;
if (ExtractPubKey(dest, pubKey))
mapWatchKeys.erase(pubKey.GetID());
return true;
}
bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const
{
LOCK(cs_KeyStore);
return setWatchOnly.count(dest) > 0;
}
bool CBasicKeyStore::HaveWatchOnly() const
{
LOCK(cs_KeyStore);
return (!setWatchOnly.empty());
}
bool CBasicKeyStore::GetHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
hdChainRet = hdChain;
return !hdChain.IsNull();
}

View File

@ -1,80 +0,0 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KEYSTORE_H
#define BITCOIN_KEYSTORE_H
#include <hdchain.h>
#include <key.h>
#include <pubkey.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/standard.h>
#include <sync.h>
#include <boost/signals2/signal.hpp>
/** A virtual base class for key stores */
class CKeyStore : public SigningProvider
{
public:
//! Add a key to the store.
virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) =0;
//! Check whether a key corresponding to a given address is present in the store.
virtual bool HaveKey(const CKeyID &address) const =0;
virtual std::set<CKeyID> GetKeys() const =0;
//! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki
virtual bool AddCScript(const CScript& redeemScript) =0;
virtual bool HaveCScript(const CScriptID &hash) const =0;
virtual std::set<CScriptID> GetCScripts() const =0;
//! Support for Watch-only addresses
virtual bool AddWatchOnly(const CScript &dest) =0;
virtual bool RemoveWatchOnly(const CScript &dest) =0;
virtual bool HaveWatchOnly(const CScript &dest) const =0;
virtual bool HaveWatchOnly() const =0;
};
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
{
protected:
mutable CCriticalSection cs_KeyStore;
using KeyMap = std::map<CKeyID, CKey>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
using ScriptMap = std::map<CScriptID, CScript>;
using WatchOnlySet = std::set<CScript>;
KeyMap mapKeys GUARDED_BY(cs_KeyStore);
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
/* the HD chain data model*/
CHDChain hdChain GUARDED_BY(cs_KeyStore);
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
bool HaveKey(const CKeyID &address) const override;
std::set<CKeyID> GetKeys() const override;
bool GetKey(const CKeyID &address, CKey &keyOut) const override;
bool AddCScript(const CScript& redeemScript) override;
bool HaveCScript(const CScriptID &hash) const override;
std::set<CScriptID> GetCScripts() const override;
bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
bool AddWatchOnly(const CScript &dest) override;
bool RemoveWatchOnly(const CScript &dest) override;
bool HaveWatchOnly(const CScript &dest) const override;
bool HaveWatchOnly() const override;
virtual bool GetHDChain(CHDChain& hdChainRet) const;
};
#endif // BITCOIN_KEYSTORE_H

View File

@ -11,6 +11,7 @@
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/sign.h>
#include <script/signingprovider.h>
// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};

View File

@ -13,7 +13,6 @@
#include <index/txindex.h>
#include <init.h>
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
#include <node/coin.h>
#include <node/context.h>
@ -28,6 +27,7 @@
#include <rpc/util.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
@ -760,7 +760,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
CBasicKeyStore keystore;
FillableSigningProvider keystore;
const UniValue& keys = request.params[1].get_array();
for (unsigned int idx = 0; idx < keys.size(); ++idx) {
UniValue k = keys[idx];

View File

@ -9,11 +9,11 @@
#include <core_io.h>
#include <interfaces/chain.h>
#include <key_io.h>
#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <rpc/request.h>
#include <rpc/util.h>
#include <script/signingprovider.h>
#include <univalue.h>
#include <util/strencodings.h>
#include <validation.h>
@ -129,7 +129,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
{
// Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {

View File

@ -7,7 +7,7 @@
#include <map>
class CBasicKeyStore;
class FillableSigningProvider;
class UniValue;
struct CMutableTransaction;
class Coin;
@ -24,7 +24,7 @@ class COutPoint;
* @param hashType The signature hash type
* @returns JSON object with details of signed transaction
*/
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime);

View File

@ -4,10 +4,10 @@
#include <chainparamsbase.h>
#include <key_io.h>
#include <keystore.h>
#include <pubkey.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/signingprovider.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
@ -190,8 +190,8 @@ CPubKey HexToPubKey(const std::string& hex_in)
return vchPubKey;
}
// Retrieves a public key for an address from the given CKeyStore
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in)
// Retrieves a public key for an address from the given FillableSigningProvider
CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in)
{
CTxDestination dest = DecodeDestination(addr_in);
if (!IsValidDestination(dest)) {

View File

@ -13,6 +13,7 @@
#include <rpc/request.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <univalue.h>
#include <util/check.h>
@ -29,7 +30,7 @@
*/
extern const std::string UNIX_EPOCH_TIME;
class CKeyStore;
class FillableSigningProvider;
class FillableSigningProvider;
class CPubKey;
class CScript;
@ -83,7 +84,7 @@ extern std::string HelpExampleCli(const std::string& methodname, const std::stri
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in);
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
UniValue DescribeAddress(const CTxDestination& dest);

View File

@ -7,6 +7,7 @@
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <vector>

34
src/script/keyorigin.h Normal file
View File

@ -0,0 +1,34 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SCRIPT_KEYORIGIN_H
#define BITCOIN_SCRIPT_KEYORIGIN_H
#include <serialize.h>
#include <streams.h>
#include <vector>
struct KeyOriginInfo
{
unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
std::vector<uint32_t> path;
friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
{
return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
}
SERIALIZE_METHODS(KeyOriginInfo, obj)
{
READWRITE(obj.fingerprint, obj.path);
}
void clear()
{
memset(fingerprint, 0, 4);
path.clear();
}
};
#endif // BITCOIN_SCRIPT_KEYORIGIN_H

View File

@ -8,6 +8,7 @@
#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
@ -362,22 +363,10 @@ public:
}
};
template<typename M, typename K, typename V>
bool LookupHelper(const M& map, const K& key, V& value)
{
auto it = map.find(key);
if (it != map.end()) {
value = it->second;
return true;
}
return false;
}
}
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32);
const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32);
const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
@ -393,51 +382,3 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
return false;
}
bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
{
return m_provider->GetCScript(scriptid, script);
}
bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
{
return m_provider->GetPubKey(keyid, pubkey);
}
bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
{
if (m_hide_secret) return false;
return m_provider->GetKey(keyid, key);
}
bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
{
if (m_hide_origin) return false;
return m_provider->GetKeyOrigin(keyid, info);
}
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
{
std::pair<CPubKey, KeyOriginInfo> out;
bool ret = LookupHelper(origins, keyid, out);
if (ret) info = std::move(out.second);
return ret;
}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
{
FlatSigningProvider ret;
ret.scripts = a.scripts;
ret.scripts.insert(b.scripts.begin(), b.scripts.end());
ret.pubkeys = a.pubkeys;
ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
ret.keys = a.keys;
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
return ret;
}

View File

@ -9,6 +9,7 @@
#include <hash.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
#include <streams.h>
class CKey;
@ -16,74 +17,10 @@ class CKeyID;
class CScript;
class CScriptID;
class CTransaction;
class SigningProvider;
struct CMutableTransaction;
struct KeyOriginInfo
{
unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
std::vector<uint32_t> path;
friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
{
return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
}
SERIALIZE_METHODS(KeyOriginInfo, obj)
{
READWRITE(obj.fingerprint, obj.path);
}
void clear()
{
memset(fingerprint, 0, 4);
path.clear();
}
};
/** An interface to be implemented by keystores that support signing. */
class SigningProvider
{
public:
virtual ~SigningProvider() {}
virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
class HidingSigningProvider : public SigningProvider
{
private:
const bool m_hide_secret;
const bool m_hide_origin;
const SigningProvider* m_provider;
public:
HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
};
struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
/** Interface for signature creators. */
class BaseSignatureCreator {
public:

View File

@ -0,0 +1,159 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <script/keyorigin.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <util/system.h>
const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
template<typename M, typename K, typename V>
bool LookupHelper(const M& map, const K& key, V& value)
{
auto it = map.find(key);
if (it != map.end()) {
value = it->second;
return true;
}
return false;
}
bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
{
return m_provider->GetCScript(scriptid, script);
}
bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
{
return m_provider->GetPubKey(keyid, pubkey);
}
bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
{
if (m_hide_secret) return false;
return m_provider->GetKey(keyid, key);
}
bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
{
if (m_hide_origin) return false;
return m_provider->GetKeyOrigin(keyid, info);
}
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
{
std::pair<CPubKey, KeyOriginInfo> out;
bool ret = LookupHelper(origins, keyid, out);
if (ret) info = std::move(out.second);
return ret;
}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
{
FlatSigningProvider ret;
ret.scripts = a.scripts;
ret.scripts.insert(b.scripts.begin(), b.scripts.end());
ret.pubkeys = a.pubkeys;
ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
ret.keys = a.keys;
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
return ret;
}
bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
{
CKey key;
if (!GetKey(address, key)) {
return false;
}
vchPubKeyOut = key.GetPubKey();
return true;
}
bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
mapKeys[pubkey.GetID()] = key;
return true;
}
bool FillableSigningProvider::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
return mapKeys.count(address) > 0;
}
std::set<CKeyID> FillableSigningProvider::GetKeys() const
{
LOCK(cs_KeyStore);
std::set<CKeyID> set_address;
for (const auto& mi : mapKeys) {
set_address.insert(mi.first);
}
return set_address;
}
bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
{
LOCK(cs_KeyStore);
KeyMap::const_iterator mi = mapKeys.find(address);
if (mi != mapKeys.end()) {
keyOut = mi->second;
return true;
}
return false;
}
bool FillableSigningProvider::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
return error("FillableSigningProvider::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
LOCK(cs_KeyStore);
mapScripts[CScriptID(redeemScript)] = redeemScript;
return true;
}
bool FillableSigningProvider::HaveCScript(const CScriptID& hash) const
{
LOCK(cs_KeyStore);
return mapScripts.count(hash) > 0;
}
std::set<CScriptID> FillableSigningProvider::GetCScripts() const
{
LOCK(cs_KeyStore);
std::set<CScriptID> set_script;
for (const auto& mi : mapScripts) {
set_script.insert(mi.first);
}
return set_script;
}
bool FillableSigningProvider::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
{
LOCK(cs_KeyStore);
ScriptMap::const_iterator mi = mapScripts.find(hash);
if (mi != mapScripts.end())
{
redeemScriptOut = (*mi).second;
return true;
}
return false;
}
bool FillableSigningProvider::GetHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
hdChainRet = hdChain;
return !hdChain.IsNull();
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SCRIPT_SIGNINGPROVIDER_H
#define BITCOIN_SCRIPT_SIGNINGPROVIDER_H
#include <hdchain.h>
#include <key.h>
#include <pubkey.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
struct KeyOriginInfo;
/** An interface to be implemented by keystores that support signing. */
class SigningProvider
{
public:
virtual ~SigningProvider() {}
virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
virtual bool HaveCScript(const CScriptID &scriptid) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
class HidingSigningProvider : public SigningProvider
{
private:
const bool m_hide_secret;
const bool m_hide_origin;
const SigningProvider* m_provider;
public:
HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
};
struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
/** Fillable signing provider that keeps keys in an address->secret map */
class FillableSigningProvider : public SigningProvider
{
protected:
mutable CCriticalSection cs_KeyStore;
using KeyMap = std::map<CKeyID, CKey>;
using ScriptMap = std::map<CScriptID, CScript>;
KeyMap mapKeys GUARDED_BY(cs_KeyStore);
ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
/* the HD chain data model*/
CHDChain hdChain GUARDED_BY(cs_KeyStore);
public:
virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
virtual bool HaveKey(const CKeyID &address) const override;
virtual std::set<CKeyID> GetKeys() const;
virtual bool GetKey(const CKeyID &address, CKey &keyOut) const override;
virtual bool AddCScript(const CScript& redeemScript);
virtual bool HaveCScript(const CScriptID &hash) const override;
virtual std::set<CScriptID> GetCScripts() const;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
virtual bool GetHDChain(CHDChain& hdChainRet) const;
};
#endif // BITCOIN_SCRIPT_SIGNINGPROVIDER_H

View File

@ -8,7 +8,6 @@
#include <pubkey.h>
#include <script/script.h>
typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;

View File

@ -6,12 +6,12 @@
#include <chainparams.h>
#include <consensus/validation.h>
#include <keystore.h>
#include <messagesigner.h>
#include <miner.h>
#include <netbase.h>
#include <script/interpreter.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <validation.h>
@ -96,7 +96,7 @@ static void FundTransaction(CMutableTransaction& tx, SimpleUTXOMap& utoxs, const
static void SignTransaction(const CTxMemPool& mempool, CMutableTransaction& tx, const CKey& coinbaseKey)
{
CBasicKeyStore tempKeystore;
FillableSigningProvider tempKeystore;
tempKeystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
for (size_t i = 0; i < tx.vin.size(); i++) {

View File

@ -6,10 +6,11 @@
#include <banman.h>
#include <chainparams.h>
#include <keystore.h>
#include <net.h>
#include <net_processing.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
@ -364,7 +365,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
{
CKey key;
key.MakeNewKey(true);
CBasicKeyStore keystore;
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(key));
// 50 orphan transactions:

View File

@ -7,12 +7,12 @@
#include <base58.h>
#include <chainparams.h>
#include <consensus/validation.h>
#include <keystore.h>
#include <messagesigner.h>
#include <netbase.h>
#include <policy/policy.h>
#include <script/interpreter.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <spork.h>
#include <txmempool.h>
@ -82,7 +82,7 @@ static void FundTransaction(CMutableTransaction& tx, SimpleUTXOMap& utoxs, const
static void SignTransaction(const CTxMemPool& mempool, CMutableTransaction& tx, const CKey& coinbaseKey)
{
CBasicKeyStore tempKeystore;
FillableSigningProvider tempKeystore;
tempKeystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
for (size_t i = 0; i < tx.vin.size(); i++) {

View File

@ -3,12 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
#include <keystore.h>
#include <policy/policy.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/interpreter.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <tinyformat.h>
#include <uint256.h>
#include <test/util/setup_common.h>
@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
BOOST_AUTO_TEST_CASE(multisig_Sign)
{
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{

View File

@ -4,13 +4,13 @@
#include <consensus/tx_verify.h>
#include <key.h>
#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <test/util/setup_common.h>
#include <vector>
@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(sign)
// scriptPubKey: HASH160 <hash> EQUAL
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(set)
{
LOCK(cs_main);
// Test the CScript::Set* methods
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CKey key[4];
std::vector<CPubKey> keys;
for (int i = 0; i < 4; i++)
@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
LOCK(cs_main);
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CKey key[6];
std::vector<CPubKey> keys;
for (int i = 0; i < 6; i++)

View File

@ -3,8 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
#include <keystore.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <test/util/setup_common.h>

View File

@ -6,10 +6,10 @@
#include <core_io.h>
#include <key.h>
#include <keystore.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <util/strencodings.h>
#include <rpc/util.h>
#include <test/util/setup_common.h>
@ -1113,7 +1113,7 @@ SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction&
BOOST_AUTO_TEST_CASE(script_combineSigs)
{
// Test the ProduceSignature's ability to combine signatures function
CBasicKeyStore keystore;
FillableSigningProvider keystore;
std::vector<CKey> keys;
std::vector<CPubKey> pubkeys;
for (int i = 0; i < 3; i++)

View File

@ -12,11 +12,11 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <script/script_error.h>
#include <streams.h>
#include <util/strencodings.h>
@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
@ -295,7 +295,7 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
BOOST_AUTO_TEST_CASE(test_Get)
{
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
@ -322,7 +322,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_AUTO_TEST_CASE(test_IsStandard)
{
LOCK(cs_main);
CBasicKeyStore keystore;
FillableSigningProvider keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);

View File

@ -7,8 +7,8 @@
#include <txmempool.h>
#include <script/standard.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <test/util/setup_common.h>
#include <keystore.h>
#include <boost/test/unit_test.hpp>
@ -153,7 +153,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
CBasicKeyStore keystore;
FillableSigningProvider keystore;
BOOST_CHECK(keystore.AddKey(coinbaseKey));
BOOST_CHECK(keystore.AddCScript(p2pk_scriptPubKey));

View File

@ -104,8 +104,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
return true;
}
static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
@ -115,7 +114,6 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
}
// General secure AES 256 CBC encryption routine
bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, const std::string& sIV, std::string& sCiphertext)
{
@ -138,7 +136,7 @@ bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, con
}
static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
@ -171,7 +169,7 @@ bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, con
}
static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
{
CKeyingMaterial vchSecret;
if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
@ -183,373 +181,3 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsi
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
return key.VerifyPubKey(vchPubKey);
}
bool CCryptoKeyStore::SetCrypted()
{
LOCK(cs_KeyStore);
if (fUseCrypto)
return true;
if (!mapKeys.empty())
return false;
fUseCrypto = true;
return true;
}
// This function should be used in a different combinations to determine
// if CCryptoKeyStore is fully locked so that no operations requiring access
// to private keys are possible:
// IsLocked(true)
// or if CCryptoKeyStore's private keys are available for mixing only:
// !IsLocked(true) && IsLocked()
// or if they are available for everything:
// !IsLocked()
bool CCryptoKeyStore::IsLocked(bool fForMixing) const
{
if (!IsCrypted())
return false;
bool result;
{
LOCK(cs_KeyStore);
result = vMasterKey.empty();
}
// fForMixing fOnlyMixingAllowed return
// ---------------------------------------
// true true result
// true false result
// false true true
// false false result
if(!fForMixing && fOnlyMixingAllowed) return true;
return result;
}
bool CCryptoKeyStore::Lock(bool fAllowMixing)
{
if (!SetCrypted())
return false;
if(!fAllowMixing) {
LOCK(cs_KeyStore);
vMasterKey.clear();
}
fOnlyMixingAllowed = fAllowMixing;
NotifyStatusChanged(this);
return true;
}
bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly, bool accept_no_keys)
{
{
LOCK(cs_KeyStore);
if (!SetCrypted())
return false;
bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
bool keyFail = false;
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
for (; mi != mapCryptedKeys.end(); ++mi)
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
CKey key;
if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
{
keyFail = true;
break;
}
keyPass = true;
if (fDecryptionThoroughlyChecked)
break;
}
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
}
if (keyFail || (!keyPass && cryptedHDChain.IsNull() && !accept_no_keys))
return false;
vMasterKey = vMasterKeyIn;
if(!cryptedHDChain.IsNull()) {
bool chainPass = false;
// try to decrypt seed and make sure it matches
CHDChain hdChainTmp;
if (DecryptHDChain(hdChainTmp)) {
// make sure seed matches this chain
chainPass = cryptedHDChain.GetID() == hdChainTmp.GetSeedHash();
}
if (!chainPass) {
vMasterKey.clear();
return false;
}
}
fDecryptionThoroughlyChecked = true;
}
fOnlyMixingAllowed = fForMixingOnly;
NotifyStatusChanged(this);
return true;
}
bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return CBasicKeyStore::AddKeyPubKey(key, pubkey);
}
if (IsLocked(true)) {
return false;
}
std::vector<unsigned char> vchCryptedSecret;
CKeyingMaterial vchSecret(key.begin(), key.end());
if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
return false;
}
if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
return false;
}
return true;
}
bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
LOCK(cs_KeyStore);
if (!SetCrypted()) {
return false;
}
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
return true;
}
bool CCryptoKeyStore::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return CBasicKeyStore::HaveKey(address);
}
return mapCryptedKeys.count(address) > 0;
}
bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return CBasicKeyStore::GetKey(address, keyOut);
}
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
}
return false;
}
bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
{
vchPubKeyOut = (*mi).second.first;
return true;
}
// Check for watch-only pubkeys
return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
}
std::set<CKeyID> CCryptoKeyStore::GetKeys() const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return CBasicKeyStore::GetKeys();
}
std::set<CKeyID> set_address;
for (const auto& mi : mapCryptedKeys) {
set_address.insert(mi.first);
}
return set_address;
}
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
LOCK(cs_KeyStore);
if (!mapCryptedKeys.empty() || IsCrypted())
return false;
fUseCrypto = true;
for (const KeyMap::value_type& mKey : mapKeys)
{
const CKey &key = mKey.second;
CPubKey vchPubKey = key.GetPubKey();
CKeyingMaterial vchSecret(key.begin(), key.end());
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
}
mapKeys.clear();
return true;
}
bool CCryptoKeyStore::EncryptHDChain(const CKeyingMaterial& vMasterKeyIn, const CHDChain& chain)
{
LOCK(cs_KeyStore);
// should call EncryptKeys first
if (!IsCrypted())
return false;
if (!cryptedHDChain.IsNull())
return true;
if (cryptedHDChain.IsCrypted())
return true;
if (hdChain.IsNull() && !chain.IsNull()) {
// Encrypting a new HDChain for an already encrypted non-HD wallet
hdChain = chain;
}
// make sure seed matches this chain
if (hdChain.GetID() != hdChain.GetSeedHash())
return false;
std::vector<unsigned char> vchCryptedSeed;
if (!EncryptSecret(vMasterKeyIn, hdChain.GetSeed(), hdChain.GetID(), vchCryptedSeed))
return false;
hdChain.Debug(__func__);
cryptedHDChain = hdChain;
cryptedHDChain.SetCrypted(true);
SecureVector vchSecureCryptedSeed(vchCryptedSeed.begin(), vchCryptedSeed.end());
if (!cryptedHDChain.SetSeed(vchSecureCryptedSeed, false))
return false;
SecureVector vchMnemonic;
SecureVector vchMnemonicPassphrase;
// it's ok to have no mnemonic if wallet was initialized via hdseed
if (hdChain.GetMnemonic(vchMnemonic, vchMnemonicPassphrase)) {
std::vector<unsigned char> vchCryptedMnemonic;
std::vector<unsigned char> vchCryptedMnemonicPassphrase;
if (!vchMnemonic.empty() && !EncryptSecret(vMasterKeyIn, vchMnemonic, hdChain.GetID(), vchCryptedMnemonic))
return false;
if (!vchMnemonicPassphrase.empty() && !EncryptSecret(vMasterKeyIn, vchMnemonicPassphrase, hdChain.GetID(), vchCryptedMnemonicPassphrase))
return false;
SecureVector vchSecureCryptedMnemonic(vchCryptedMnemonic.begin(), vchCryptedMnemonic.end());
SecureVector vchSecureCryptedMnemonicPassphrase(vchCryptedMnemonicPassphrase.begin(), vchCryptedMnemonicPassphrase.end());
if (!cryptedHDChain.SetMnemonic(vchSecureCryptedMnemonic, vchSecureCryptedMnemonicPassphrase, false))
return false;
}
if (!hdChain.SetNull())
return false;
return true;
}
bool CCryptoKeyStore::DecryptHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return true;
if (cryptedHDChain.IsNull())
return false;
if (!cryptedHDChain.IsCrypted())
return false;
SecureVector vchSecureSeed;
SecureVector vchSecureCryptedSeed = cryptedHDChain.GetSeed();
std::vector<unsigned char> vchCryptedSeed(vchSecureCryptedSeed.begin(), vchSecureCryptedSeed.end());
if (!DecryptSecret(vMasterKey, vchCryptedSeed, cryptedHDChain.GetID(), vchSecureSeed))
return false;
hdChainRet = cryptedHDChain;
if (!hdChainRet.SetSeed(vchSecureSeed, false))
return false;
// hash of decrypted seed must match chain id
if (hdChainRet.GetSeedHash() != cryptedHDChain.GetID())
return false;
SecureVector vchSecureCryptedMnemonic;
SecureVector vchSecureCryptedMnemonicPassphrase;
// it's ok to have no mnemonic if wallet was initialized via hdseed
if (cryptedHDChain.GetMnemonic(vchSecureCryptedMnemonic, vchSecureCryptedMnemonicPassphrase)) {
SecureVector vchSecureMnemonic;
SecureVector vchSecureMnemonicPassphrase;
std::vector<unsigned char> vchCryptedMnemonic(vchSecureCryptedMnemonic.begin(), vchSecureCryptedMnemonic.end());
std::vector<unsigned char> vchCryptedMnemonicPassphrase(vchSecureCryptedMnemonicPassphrase.begin(), vchSecureCryptedMnemonicPassphrase.end());
if (!vchCryptedMnemonic.empty() && !DecryptSecret(vMasterKey, vchCryptedMnemonic, cryptedHDChain.GetID(), vchSecureMnemonic))
return false;
if (!vchCryptedMnemonicPassphrase.empty() && !DecryptSecret(vMasterKey, vchCryptedMnemonicPassphrase, cryptedHDChain.GetID(), vchSecureMnemonicPassphrase))
return false;
if (!hdChainRet.SetMnemonic(vchSecureMnemonic, vchSecureMnemonicPassphrase, false))
return false;
}
hdChainRet.SetCrypted(false);
hdChainRet.Debug(__func__);
return true;
}
bool CCryptoKeyStore::SetHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
if (IsCrypted())
return false;
if (chain.IsCrypted())
return false;
hdChain = chain;
return true;
}
bool CCryptoKeyStore::SetCryptedHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
if (!SetCrypted())
return false;
if (!chain.IsCrypted())
return false;
cryptedHDChain = chain;
return true;
}
bool CCryptoKeyStore::GetHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
if(IsCrypted()) {
hdChainRet = cryptedHDChain;
return !cryptedHDChain.IsNull();
}
hdChainRet = hdChain;
return !hdChain.IsNull();
}

View File

@ -5,9 +5,9 @@
#ifndef BITCOIN_WALLET_CRYPTER_H
#define BITCOIN_WALLET_CRYPTER_H
#include <keystore.h>
#include <serialize.h>
#include <support/allocators/secure.h>
#include <script/signingprovider.h>
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
@ -106,65 +106,8 @@ bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, con
bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, const std::string& sIV, SecureString& sPlaintext);
/** Keystore which keeps the private keys encrypted.
* It derives from the basic key store, which is used if no encryption is active.
*/
class CCryptoKeyStore : public CBasicKeyStore
{
private:
CHDChain cryptedHDChain GUARDED_BY(cs_KeyStore);
CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
std::atomic<bool> fUseCrypto;
//! keeps track of whether Unlock has run a thorough check before
bool fDecryptionThoroughlyChecked;
//! if fOnlyMixingAllowed is true, only mixing should be allowed in unlocked wallet
bool fOnlyMixingAllowed;
protected:
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
bool SetCrypted();
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool EncryptHDChain(const CKeyingMaterial& vMasterKeyIn, const CHDChain& chain = CHDChain());
bool DecryptHDChain(CHDChain& hdChainRet) const;
bool SetHDChain(const CHDChain& chain);
bool SetCryptedHDChain(const CHDChain& chain);
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly = false, bool accept_no_keys = false);
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
public:
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false), fOnlyMixingAllowed(false)
{
}
bool IsCrypted() const { return fUseCrypto; }
bool IsLocked(bool fForMixing = false) const;
bool Lock(bool fForMixing = false);
virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
bool HaveKey(const CKeyID &address) const override;
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
std::set<CKeyID> GetKeys() const override;
virtual bool GetHDChain(CHDChain& hdChainRet) const override;
/**
* Wallet status (encrypted, locked) changed.
* Note: Called without locks held.
*/
boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged;
};
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext);
bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext);
bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key);
#endif // BITCOIN_WALLET_CRYPTER_H

View File

@ -7,6 +7,7 @@
#include <key.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <wallet/wallet.h>
typedef std::vector<unsigned char> valtype;

View File

@ -61,6 +61,16 @@ static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& pa
return avoid_reuse;
}
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
/*
bool HaveKey(const CWallet& wallet, const CKey& key)
{
CKey key2;
key2.Set(key.begin(), key.end(), !key.IsCompressed());
return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
}
*/
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
{
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {

View File

@ -16,7 +16,6 @@
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
@ -25,6 +24,7 @@
#include <script/descriptor.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <txmempool.h>
#include <util/bip32.h>
#include <util/error.h>
@ -475,7 +475,7 @@ bool CWallet::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
return true;
}
else
return CCryptoKeyStore::GetPubKey(address, vchPubKeyOut);
return GetPubKeyInner(address, vchPubKeyOut);
}
bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const
@ -503,16 +503,25 @@ bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const
return true;
}
else {
return CCryptoKeyStore::GetKey(address, keyOut);
return GetKeyInner(address, keyOut);
}
}
bool CWallet::HaveKeyInner(const CKeyID &address) const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return FillableSigningProvider::HaveKey(address);
}
return mapCryptedKeys.count(address) > 0;
}
bool CWallet::HaveKey(const CKeyID &address) const
{
LOCK(cs_wallet);
if (mapHdPubKeys.count(address) > 0)
return true;
return CCryptoKeyStore::HaveKey(address);
return HaveKeyInner(address);
}
bool CWallet::LoadHDPubKey(const CHDPubKey &hdPubKey)
@ -559,14 +568,14 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
// Make sure we aren't adding private keys to private key disabled wallets
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
// CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey
// FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
// which is overridden below. To avoid flushes, the database handle is
// tunneled through to it.
bool needsDB = !encrypted_batch;
if (needsDB) {
encrypted_batch = &batch;
}
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
if (!AddKeyPubKeyInner(secret, pubkey)) {
if (needsDB) encrypted_batch = nullptr;
return false;
}
@ -602,7 +611,7 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<unsigned char> &vchCryptedSecret)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret))
return false;
{
LOCK(cs_wallet);
@ -690,7 +699,7 @@ void CWallet::UpgradeKeyMetadata()
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
}
/**
@ -723,7 +732,7 @@ bool CWallet::AddCScript(const CScript& redeemScript)
bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
if (!FillableSigningProvider::AddCScript(redeemScript))
return false;
if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET);
@ -744,12 +753,30 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return true;
}
return CCryptoKeyStore::AddCScript(redeemScript);
return FillableSigningProvider::AddCScript(redeemScript);
}
static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
{
std::vector<std::vector<unsigned char>> solutions;
return Solver(dest, solutions) == TX_PUBKEY &&
(pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
}
bool CWallet::AddWatchOnlyInMem(const CScript &dest)
{
LOCK(cs_KeyStore);
setWatchOnly.insert(dest);
CPubKey pubKey;
if (ExtractPubKey(dest, pubKey)) {
mapWatchKeys[pubKey.GetID()] = pubKey;
}
return true;
}
bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
if (!AddWatchOnlyInMem(dest))
return false;
const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
@ -782,8 +809,15 @@ bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
bool CWallet::RemoveWatchOnly(const CScript &dest)
{
AssertLockHeld(cs_wallet);
if (!CCryptoKeyStore::RemoveWatchOnly(dest))
return false;
{
LOCK(cs_KeyStore);
setWatchOnly.erase(dest);
CPubKey pubKey;
if (ExtractPubKey(dest, pubKey)) {
mapWatchKeys.erase(pubKey.GetID());
}
}
if (!HaveWatchOnly())
NotifyWatchonlyChanged(false);
if (!WalletBatch(*database).EraseWatchOnly(dest))
@ -794,7 +828,19 @@ bool CWallet::RemoveWatchOnly(const CScript &dest)
bool CWallet::LoadWatchOnly(const CScript &dest)
{
return CCryptoKeyStore::AddWatchOnly(dest);
return AddWatchOnlyInMem(dest);
}
bool CWallet::HaveWatchOnly(const CScript &dest) const
{
LOCK(cs_KeyStore);
return setWatchOnly.count(dest) > 0;
}
bool CWallet::HaveWatchOnly() const
{
LOCK(cs_KeyStore);
return (!setWatchOnly.empty());
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly, bool accept_no_keys)
@ -813,7 +859,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnl
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key
if (CCryptoKeyStore::Unlock(_vMasterKey, fForMixingOnly, accept_no_keys)) {
if (Unlock(_vMasterKey, fForMixingOnly, accept_no_keys)) {
// Now that we've unlocked, upgrade the key metadata
UpgradeKeyMetadata();
if(nWalletBackups == -2) {
@ -844,7 +890,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
return false;
if (CCryptoKeyStore::Unlock(_vMasterKey))
if (Unlock(_vMasterKey))
{
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
@ -2028,7 +2074,7 @@ bool CWallet::SetHDChain(WalletBatch &batch, const CHDChain& chain, bool memonly
{
LOCK(cs_wallet);
if (!CCryptoKeyStore::SetHDChain(chain))
if (!SetHDChain(chain))
return false;
if (!memonly) {
@ -2046,7 +2092,7 @@ bool CWallet::SetCryptedHDChain(WalletBatch &batch, const CHDChain& chain, bool
{
LOCK(cs_wallet);
if (!CCryptoKeyStore::SetCryptedHDChain(chain))
if (!SetCryptedHDChain(chain))
return false;
if (!memonly) {
@ -5771,3 +5817,378 @@ bool CWallet::AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info)
mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
return WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
}
bool CWallet::SetCrypted()
{
LOCK(cs_KeyStore);
if (fUseCrypto)
return true;
if (!mapKeys.empty())
return false;
fUseCrypto = true;
return true;
}
// This function should be used in a different combinations to determine
// if FillableSigningProvider is fully locked so that no operations requiring access
// to private keys are possible:
// IsLocked(true)
// or if FillableSigningProvider's private keys are available for mixing only:
// !IsLocked(true) && IsLocked()
// or if they are available for everything:
// !IsLocked()
bool CWallet::IsLocked(bool fForMixing) const
{
if (!IsCrypted())
return false;
bool result;
{
LOCK(cs_KeyStore);
result = vMasterKey.empty();
}
// fForMixing fOnlyMixingAllowed return
// ---------------------------------------
// true true result
// true false result
// false true true
// false false result
if(!fForMixing && fOnlyMixingAllowed) return true;
return result;
}
bool CWallet::Lock(bool fAllowMixing)
{
if (!SetCrypted())
return false;
if(!fAllowMixing) {
LOCK(cs_KeyStore);
vMasterKey.clear();
}
fOnlyMixingAllowed = fAllowMixing;
NotifyStatusChanged(this);
return true;
}
bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly, bool accept_no_keys)
{
{
LOCK(cs_KeyStore);
if (!SetCrypted())
return false;
bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
bool keyFail = false;
CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
for (; mi != mapCryptedKeys.end(); ++mi)
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
CKey key;
if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
{
keyFail = true;
break;
}
keyPass = true;
if (fDecryptionThoroughlyChecked)
break;
}
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
}
if (keyFail || (!keyPass && cryptedHDChain.IsNull() && !accept_no_keys))
return false;
vMasterKey = vMasterKeyIn;
if(!cryptedHDChain.IsNull()) {
bool chainPass = false;
// try to decrypt seed and make sure it matches
CHDChain hdChainTmp;
if (DecryptHDChain(hdChainTmp)) {
// make sure seed matches this chain
chainPass = cryptedHDChain.GetID() == hdChainTmp.GetSeedHash();
}
if (!chainPass) {
vMasterKey.clear();
return false;
}
}
fDecryptionThoroughlyChecked = true;
}
fOnlyMixingAllowed = fForMixingOnly;
NotifyStatusChanged(this);
return true;
}
bool CWallet::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return FillableSigningProvider::AddKeyPubKey(key, pubkey);
}
if (IsLocked(true)) {
return false;
}
std::vector<unsigned char> vchCryptedSecret;
CKeyingMaterial vchSecret(key.begin(), key.end());
if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
return false;
}
if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
return false;
}
return true;
}
bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
LOCK(cs_KeyStore);
if (!SetCrypted()) {
return false;
}
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
return true;
}
bool CWallet::GetKeyInner(const CKeyID &address, CKey& keyOut) const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return FillableSigningProvider::GetKey(address, keyOut);
}
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
}
return false;
}
bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
{
LOCK(cs_KeyStore);
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
if (it != mapWatchKeys.end()) {
pubkey_out = it->second;
return true;
}
return false;
}
bool CWallet::GetPubKeyInner(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) {
return GetWatchPubKey(address, vchPubKeyOut);
}
return true;
}
CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
if (mi != mapCryptedKeys.end())
{
vchPubKeyOut = (*mi).second.first;
return true;
}
// Check for watch-only pubkeys
return GetWatchPubKey(address, vchPubKeyOut);
}
std::set<CKeyID> CWallet::GetKeys() const
{
LOCK(cs_KeyStore);
if (!IsCrypted()) {
return FillableSigningProvider::GetKeys();
}
std::set<CKeyID> set_address;
for (const auto& mi : mapCryptedKeys) {
set_address.insert(mi.first);
}
return set_address;
}
bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
LOCK(cs_KeyStore);
if (!mapCryptedKeys.empty() || IsCrypted())
return false;
fUseCrypto = true;
for (const KeyMap::value_type& mKey : mapKeys)
{
const CKey &key = mKey.second;
CPubKey vchPubKey = key.GetPubKey();
CKeyingMaterial vchSecret(key.begin(), key.end());
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
}
mapKeys.clear();
return true;
}
bool CWallet::EncryptHDChain(const CKeyingMaterial& vMasterKeyIn, const CHDChain& chain)
{
LOCK(cs_KeyStore);
// should call EncryptKeys first
if (!IsCrypted())
return false;
if (!cryptedHDChain.IsNull())
return true;
if (cryptedHDChain.IsCrypted())
return true;
if (hdChain.IsNull() && !chain.IsNull()) {
// Encrypting a new HDChain for an already encrypted non-HD wallet
hdChain = chain;
}
// make sure seed matches this chain
if (hdChain.GetID() != hdChain.GetSeedHash())
return false;
std::vector<unsigned char> vchCryptedSeed;
if (!EncryptSecret(vMasterKeyIn, hdChain.GetSeed(), hdChain.GetID(), vchCryptedSeed))
return false;
hdChain.Debug(__func__);
cryptedHDChain = hdChain;
cryptedHDChain.SetCrypted(true);
SecureVector vchSecureCryptedSeed(vchCryptedSeed.begin(), vchCryptedSeed.end());
if (!cryptedHDChain.SetSeed(vchSecureCryptedSeed, false))
return false;
SecureVector vchMnemonic;
SecureVector vchMnemonicPassphrase;
// it's ok to have no mnemonic if wallet was initialized via hdseed
if (hdChain.GetMnemonic(vchMnemonic, vchMnemonicPassphrase)) {
std::vector<unsigned char> vchCryptedMnemonic;
std::vector<unsigned char> vchCryptedMnemonicPassphrase;
if (!vchMnemonic.empty() && !EncryptSecret(vMasterKeyIn, vchMnemonic, hdChain.GetID(), vchCryptedMnemonic))
return false;
if (!vchMnemonicPassphrase.empty() && !EncryptSecret(vMasterKeyIn, vchMnemonicPassphrase, hdChain.GetID(), vchCryptedMnemonicPassphrase))
return false;
SecureVector vchSecureCryptedMnemonic(vchCryptedMnemonic.begin(), vchCryptedMnemonic.end());
SecureVector vchSecureCryptedMnemonicPassphrase(vchCryptedMnemonicPassphrase.begin(), vchCryptedMnemonicPassphrase.end());
if (!cryptedHDChain.SetMnemonic(vchSecureCryptedMnemonic, vchSecureCryptedMnemonicPassphrase, false))
return false;
}
if (!hdChain.SetNull())
return false;
return true;
}
bool CWallet::DecryptHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return true;
if (cryptedHDChain.IsNull())
return false;
if (!cryptedHDChain.IsCrypted())
return false;
SecureVector vchSecureSeed;
SecureVector vchSecureCryptedSeed = cryptedHDChain.GetSeed();
std::vector<unsigned char> vchCryptedSeed(vchSecureCryptedSeed.begin(), vchSecureCryptedSeed.end());
if (!DecryptSecret(vMasterKey, vchCryptedSeed, cryptedHDChain.GetID(), vchSecureSeed))
return false;
hdChainRet = cryptedHDChain;
if (!hdChainRet.SetSeed(vchSecureSeed, false))
return false;
// hash of decrypted seed must match chain id
if (hdChainRet.GetSeedHash() != cryptedHDChain.GetID())
return false;
SecureVector vchSecureCryptedMnemonic;
SecureVector vchSecureCryptedMnemonicPassphrase;
// it's ok to have no mnemonic if wallet was initialized via hdseed
if (cryptedHDChain.GetMnemonic(vchSecureCryptedMnemonic, vchSecureCryptedMnemonicPassphrase)) {
SecureVector vchSecureMnemonic;
SecureVector vchSecureMnemonicPassphrase;
std::vector<unsigned char> vchCryptedMnemonic(vchSecureCryptedMnemonic.begin(), vchSecureCryptedMnemonic.end());
std::vector<unsigned char> vchCryptedMnemonicPassphrase(vchSecureCryptedMnemonicPassphrase.begin(), vchSecureCryptedMnemonicPassphrase.end());
if (!vchCryptedMnemonic.empty() && !DecryptSecret(vMasterKey, vchCryptedMnemonic, cryptedHDChain.GetID(), vchSecureMnemonic))
return false;
if (!vchCryptedMnemonicPassphrase.empty() && !DecryptSecret(vMasterKey, vchCryptedMnemonicPassphrase, cryptedHDChain.GetID(), vchSecureMnemonicPassphrase))
return false;
if (!hdChainRet.SetMnemonic(vchSecureMnemonic, vchSecureMnemonicPassphrase, false))
return false;
}
hdChainRet.SetCrypted(false);
hdChainRet.Debug(__func__);
return true;
}
bool CWallet::SetHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
if (IsCrypted())
return false;
if (chain.IsCrypted())
return false;
hdChain = chain;
return true;
}
bool CWallet::SetCryptedHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
if (!SetCrypted())
return false;
if (!chain.IsCrypted())
return false;
cryptedHDChain = chain;
return true;
}
bool CWallet::GetHDChain(CHDChain& hdChainRet) const
{
LOCK(cs_KeyStore);
if(IsCrypted()) {
hdChainRet = cryptedHDChain;
return !cryptedHDChain.IsNull();
}
hdChainRet = hdChain;
return !hdChain.IsNull();
}

View File

@ -38,6 +38,8 @@
#include <utility>
#include <vector>
#include <boost/signals2/signal.hpp>
struct bilingual_str;
using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
@ -764,9 +766,48 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
class CWallet final : public CCryptoKeyStore, public interfaces::Chain::Notifications
class CWallet final : public FillableSigningProvider, public interfaces::Chain::Notifications
{
private:
CHDChain cryptedHDChain GUARDED_BY(cs_KeyStore);
CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
std::atomic<bool> fUseCrypto;
//! keeps track of whether Unlock has run a thorough check before
bool fDecryptionThoroughlyChecked;
//! if fOnlyMixingAllowed is true, only mixing should be allowed in unlocked wallet
bool fOnlyMixingAllowed;
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
bool SetCrypted();
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool EncryptHDChain(const CKeyingMaterial& vMasterKeyIn, const CHDChain& chain = CHDChain());
bool DecryptHDChain(CHDChain& hdChainRet) const;
bool SetHDChain(const CHDChain& chain);
bool SetCryptedHDChain(const CHDChain& chain);
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly = false, bool accept_no_keys = false);
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
bool HaveKeyInner(const CKeyID &address) const;
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
bool GetKeyInner(const CKeyID &address, CKey& keyOut) const;
bool GetPubKeyInner(const CKeyID &address, CPubKey& vchPubKeyOut) const;
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<int64_t> m_scanning_start{0};
@ -852,7 +893,8 @@ private:
* of the other AddWatchOnly which accepts a timestamp and sets
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyInMem(const CScript &dest);
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
@ -941,7 +983,10 @@ public:
/** Construct wallet with specified name and database implementation. */
CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
: m_chain(chain),
: fUseCrypto(false),
fDecryptionThoroughlyChecked(false),
fOnlyMixingAllowed(false),
m_chain(chain),
m_location(location),
database(std::move(database))
{
@ -957,6 +1002,9 @@ public:
/** Interface to assert chain access */
bool HaveChain() const { return m_chain ? true : false; }
bool IsCrypted() const { return fUseCrypto; }
bool IsLocked(bool fForMixing = false) const;
bool Lock(bool fForMixing = false);
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
@ -1068,7 +1116,7 @@ public:
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); }
//! Load metadata (used by LoadWallet)
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -1080,9 +1128,11 @@ public:
int64_t GetTimeFirstKey() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override;
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
std::set<CKeyID> GetKeys() const override;
virtual bool GetHDChain(CHDChain& hdChainRet) const override;
bool AddCScript(const CScript& redeemScript) override;
bool LoadCScript(const CScript& redeemScript);
@ -1100,9 +1150,15 @@ public:
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool RemoveWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
//! Returns whether the watch-only script is in the wallet
bool HaveWatchOnly(const CScript &dest) const;
//! Returns whether there are any watch-only things in the wallet
bool HaveWatchOnly() const;
//! Fetches a pubkey from mapWatchKeys if it exists there
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@ -1357,6 +1413,12 @@ public:
/** ChainLock received */
boost::signals2::signal<void (int height)> NotifyChainLockReceived;
/**
* Wallet status (encrypted, locked) changed.
* Note: Called without locks held.
*/
boost::signals2::signal<void (CWallet* wallet)> NotifyStatusChanged;
/** Inquire whether this wallet broadcasts transactions. */
bool GetBroadcastTransactions() const { return fBroadcastTransactions; }
/** Set whether this wallet broadcasts transactions. */

View File

@ -41,6 +41,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"governance/governance -> net_processing -> governance/governance"
"governance/object -> governance/validators -> governance/object"
"hdchain -> wallet/walletdb -> hdchain"
"hdchain -> wallet/walletdb -> script/sign -> script/signingprovider -> hdchain"
"llmq/quorums -> llmq/utils -> llmq/quorums"
"llmq/blockprocessor -> net_processing -> llmq/blockprocessor"
"llmq/chainlocks -> llmq/instantsend -> llmq/chainlocks"