diff --git a/configure.ac b/configure.ac index d36f8046cfa2..daf733fb2224 100644 --- a/configure.ac +++ b/configure.ac @@ -2077,7 +2077,9 @@ CPPFLAGS="$CPPFLAGS_TEMP" if test -n "$use_sanitizers"; then export SECP_CFLAGS="$SECP_CFLAGS $SANITIZER_CFLAGS" fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --disable-module-ecdh --disable-openssl-tests" +dnl The ECDH module is required by the wallet's Platform key provider +dnl (DashPay contact request encryption, wallet/platformkeys.cpp). +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-ecdh --disable-openssl-tests" AC_CONFIG_SUBDIRS([src/dashbls src/secp256k1]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 0cf8ce802fdb..e99ab46d3f86 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -478,6 +478,8 @@ BITCOIN_CORE_H = \ wallet/hdchain.h \ wallet/ismine.h \ wallet/load.h \ + wallet/platformkeys.h \ + wallet/platformtypes.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/platformkeys.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..a3c8cb3aead3 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/platformkeys_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/spend_tests.cpp \ wallet/test/wallet_tests.cpp \ diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 9fc779906135..6bcde7e75af5 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -142,6 +143,26 @@ class Wallet //! Sign special transaction payload virtual bool signSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector& vchSig) = 0; + //! Derive and return a public Platform key. The mnemonic-owning key + //! manager performs derivation without exposing the wallet's root seed. + virtual wallet::PlatformKeyResult getPlatformPubKey(const wallet::PlatformKeyRequest& request) = 0; + + //! Sign a 32-byte digest with a platform key (compact/recoverable ECDSA, + //! as used by Platform state transitions). + virtual wallet::PlatformKeyResult> signPlatformDigest(const wallet::PlatformKeyRequest& request, + const uint256& digest) = 0; + + //! ECDH shared secret between the identity authentication key + //! and a counterparty public key, using the libsecp256k1 ECDH KDF. + virtual wallet::PlatformKeyResult platformECDHSecret(const wallet::IdentityAuthKey& key, + const CPubKey& counterparty) = 0; + + //! Ensure our private DIP-15 receiving chain is imported as a ranged + //! descriptor and return the corresponding public chain. Derivation, + //! descriptor update and result publication are one wallet-locked action. + virtual wallet::PlatformKeyResult ensureFriendshipReceivingKeychain( + const wallet::FriendshipKeychainRequest& request) = 0; + //! Return whether wallet has private key. virtual bool isSpendable(const CScript& script) = 0; virtual bool isSpendable(const CTxDestination& dest) = 0; @@ -170,6 +191,14 @@ class Wallet //! Save or remove receive request. virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0; + //! Write (or, with an empty value, erase) a generic Platform data + //! record. Records are persisted in the wallet database and travel with + //! backups; they are opaque to the wallet itself. + virtual bool writePlatformData(const std::string& key, const std::vector& value) = 0; + + //! All Platform data records whose key starts with prefix. + virtual std::map> getPlatformData(const std::string& prefix) = 0; + //! Display address on external signer virtual bool displayAddress(const CTxDestination& dest) = 0; diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index bdf6252d868e..4454f256a0e3 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -187,6 +187,12 @@ struct PubkeyProvider /** Derive a private key, if private data is available in arg. */ virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0; + + /** Return this key expression's root extended public key, if any. */ + virtual bool GetRootExtPubKey(CExtPubKey& key) const { return false; } + + /** Return this key expression's root extended private key, if available. */ + virtual bool GetRootExtKey(const SigningProvider& arg, CExtKey& key) const { return false; } }; class OriginPubkeyProvider final : public PubkeyProvider @@ -238,6 +244,14 @@ class OriginPubkeyProvider final : public PubkeyProvider { return m_provider->GetPrivKey(pos, arg, key); } + bool GetRootExtPubKey(CExtPubKey& key) const override + { + return m_provider->GetRootExtPubKey(key); + } + bool GetRootExtKey(const SigningProvider& arg, CExtKey& key) const override + { + return m_provider->GetRootExtKey(arg, key); + } }; /** An object representing a parsed constant public key in a descriptor. */ @@ -488,6 +502,15 @@ class BIP32PubkeyProvider final : public PubkeyProvider key = extkey.key; return true; } + bool GetRootExtPubKey(CExtPubKey& key) const override + { + key = m_root_extkey; + return true; + } + bool GetRootExtKey(const SigningProvider& arg, CExtKey& key) const override + { + return GetExtKey(arg, key); + } }; /** Base class for all Descriptor implementations. */ @@ -597,6 +620,28 @@ class DescriptorImpl : public Descriptor return AddChecksum(ret); } + bool GetRootExtPubKey(CExtPubKey& out) const final + { + if (m_pubkey_args.size() == 1 && m_subdescriptor_args.empty()) { + return m_pubkey_args.front()->GetRootExtPubKey(out); + } + if (m_pubkey_args.empty() && m_subdescriptor_args.size() == 1) { + return m_subdescriptor_args.front()->GetRootExtPubKey(out); + } + return false; + } + + bool GetRootExtKey(const SigningProvider& provider, CExtKey& out) const final + { + if (m_pubkey_args.size() == 1 && m_subdescriptor_args.empty()) { + return m_pubkey_args.front()->GetRootExtKey(provider, out); + } + if (m_pubkey_args.empty() && m_subdescriptor_args.size() == 1) { + return m_subdescriptor_args.front()->GetRootExtKey(provider, out); + } + return false; + } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { bool ret = ToStringHelper(&arg, out, StringType::PRIVATE); diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 505c44537b06..7b85eae51986 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -108,6 +108,14 @@ struct Descriptor { /** Convert the descriptor back to a string, undoing parsing. */ virtual std::string ToString() const = 0; + /** Return the single extended public key at the root of this descriptor's + * key expression, if it has one. */ + virtual bool GetRootExtPubKey(CExtPubKey& out) const { return false; } + + /** Return the corresponding single extended private key when it is + * available from `provider`. */ + virtual bool GetRootExtKey(const SigningProvider& provider, CExtKey& out) const { return false; } + /** Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo) */ virtual bool IsSingleType() const = 0; diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 69c03ccddf71..83b1a6b23f12 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include