0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-02 09:46:52 -05:00

wallet: bugfix, always use apostrophe for spkm descriptor ID

As we update the descriptor's db record every time that
the wallet is loaded (at `TopUp` time), if the spkm ID differs
from the one in db, the wallet will enter in an unrecoverable
corruption state, and no soft version will be able to open
it anymore.

Because we cannot change the past, to stay compatible between
releases, we need to always use the apostrophe version for the
spkm IDs.
This commit is contained in:
furszy 2023-06-20 12:34:26 -03:00
parent 97a965d98f
commit 6a9510d2da
No known key found for this signature in database
GPG key ID: 5DD23CCC686AA623
3 changed files with 31 additions and 19 deletions

View file

@ -191,8 +191,13 @@ public:
/** Get the size of the generated public key(s) in bytes (33 or 65). */
virtual size_t GetSize() const = 0;
enum class StringType {
PUBLIC,
COMPAT // string calculation that mustn't change over time to stay compatible with previous software versions
};
/** Get the descriptor string form. */
virtual std::string ToString() const = 0;
virtual std::string ToString(StringType type=StringType::PUBLIC) const = 0;
/** Get the descriptor string form including private data (if available in arg). */
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
@ -212,9 +217,11 @@ class OriginPubkeyProvider final : public PubkeyProvider
std::unique_ptr<PubkeyProvider> m_provider;
bool m_apostrophe;
std::string OriginString(bool normalized=false) const
std::string OriginString(StringType type, bool normalized=false) const
{
return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, /*apostrophe=*/!normalized && m_apostrophe);
// If StringType==COMPAT, always use the apostrophe to stay compatible with previous versions
bool use_apostrophe = (!normalized && m_apostrophe) || type == StringType::COMPAT;
return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, use_apostrophe);
}
public:
@ -228,12 +235,12 @@ public:
}
bool IsRange() const override { return m_provider->IsRange(); }
size_t GetSize() const override { return m_provider->GetSize(); }
std::string ToString() const override { return "[" + OriginString() + "]" + m_provider->ToString(); }
std::string ToString(StringType type) const override { return "[" + OriginString(type) + "]" + m_provider->ToString(type); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
std::string sub;
if (!m_provider->ToPrivateString(arg, sub)) return false;
ret = "[" + OriginString() + "]" + std::move(sub);
ret = "[" + OriginString(StringType::PUBLIC) + "]" + std::move(sub);
return true;
}
bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override
@ -245,9 +252,9 @@ public:
// and append that to our own origin string.
if (sub[0] == '[') {
sub = sub.substr(9);
ret = "[" + OriginString(/*normalized=*/true) + std::move(sub);
ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + std::move(sub);
} else {
ret = "[" + OriginString(/*normalized=*/true) + "]" + std::move(sub);
ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + "]" + std::move(sub);
}
return true;
}
@ -275,7 +282,7 @@ public:
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
std::string ToString() const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); }
std::string ToString(StringType type) const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
@ -293,7 +300,7 @@ public:
}
bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override
{
ret = ToString();
ret = ToString(StringType::PUBLIC);
return true;
}
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
@ -421,9 +428,10 @@ public:
return true;
}
std::string ToString(bool normalized) const
std::string ToString(StringType type, bool normalized) const
{
const bool use_apostrophe = !normalized && m_apostrophe;
// If StringType==COMPAT, always use the apostrophe to stay compatible with previous versions
const bool use_apostrophe = (!normalized && m_apostrophe) || type == StringType::COMPAT;
std::string ret = EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path, /*apostrophe=*/use_apostrophe);
if (IsRange()) {
ret += "/*";
@ -431,9 +439,9 @@ public:
}
return ret;
}
std::string ToString() const override
std::string ToString(StringType type=StringType::PUBLIC) const override
{
return ToString(/*normalized=*/false);
return ToString(type, /*normalized=*/false);
}
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
{
@ -449,7 +457,7 @@ public:
bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override
{
if (m_derive == DeriveType::HARDENED) {
out = ToString(/*normalized=*/true);
out = ToString(StringType::PUBLIC, /*normalized=*/true);
return true;
}
@ -556,6 +564,7 @@ public:
PUBLIC,
PRIVATE,
NORMALIZED,
COMPAT, // string calculation that mustn't change over time to stay compatible with previous software versions
};
bool IsSolvable() const override
@ -607,6 +616,9 @@ public:
case StringType::PUBLIC:
tmp = pubkey->ToString();
break;
case StringType::COMPAT:
tmp = pubkey->ToString(PubkeyProvider::StringType::COMPAT);
break;
}
ret += tmp;
}
@ -617,10 +629,10 @@ public:
return true;
}
std::string ToString() const final
std::string ToString(bool compat_format) const final
{
std::string ret;
ToStringHelper(nullptr, ret, StringType::PUBLIC);
ToStringHelper(nullptr, ret, compat_format ? StringType::COMPAT : StringType::PUBLIC);
return AddChecksum(ret);
}
@ -1780,7 +1792,7 @@ std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const Signing
uint256 DescriptorID(const Descriptor& desc)
{
std::string desc_str = desc.ToString();
std::string desc_str = desc.ToString(/*compat_format=*/true);
uint256 id;
CSHA256().Write((unsigned char*)desc_str.data(), desc_str.size()).Finalize(id.begin());
return id;

View file

@ -106,7 +106,7 @@ struct Descriptor {
virtual bool IsSolvable() const = 0;
/** Convert the descriptor back to a string, undoing parsing. */
virtual std::string ToString() const = 0;
virtual std::string ToString(bool compat_format=false) const = 0;
/** Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo) */
virtual bool IsSingleType() const = 0;

View file

@ -20,7 +20,7 @@ public:
explicit DummyDescriptor(const std::string& descriptor) : desc(descriptor) {};
~DummyDescriptor() = default;
std::string ToString() const override { return desc; }
std::string ToString(bool compat_format) const override { return desc; }
std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; }
bool IsRange() const override { return false; }