diff options
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/descriptor.cpp | 86 | ||||
-rw-r--r-- | src/script/descriptor.h | 21 | ||||
-rw-r--r-- | src/script/sign.h | 5 |
3 files changed, 111 insertions, 1 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index d343972c40..ca80d3451f 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -211,6 +211,7 @@ public: AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {} bool IsRange() const override { return false; } + bool IsSolvable() const override { return false; } std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; } bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override @@ -229,6 +230,7 @@ public: RawDescriptor(CScript script) : m_script(std::move(script)) {} bool IsRange() const override { return false; } + bool IsSolvable() const override { return false; } std::string ToString() const override { return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; } bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override @@ -249,6 +251,7 @@ public: SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {} bool IsRange() const override { return m_provider->IsRange(); } + bool IsSolvable() const override { return true; } std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { @@ -290,6 +293,8 @@ public: return false; } + bool IsSolvable() const override { return true; } + std::string ToString() const override { std::string ret = strprintf("multi(%i", m_threshold); @@ -343,6 +348,7 @@ public: ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {} bool IsRange() const override { return m_descriptor->IsRange(); } + bool IsSolvable() const override { return m_descriptor->IsSolvable(); } std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { @@ -377,6 +383,7 @@ public: ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {} bool IsRange() const override { return m_provider->IsRange(); } + bool IsSolvable() const override { return true; } std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { @@ -625,6 +632,80 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext return nullptr; } +std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider) +{ + std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey); + KeyOriginInfo info; + if (provider.GetKeyOrigin(pubkey.GetID(), info)) { + return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(key_provider)); + } + return key_provider; +} + +std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider) +{ + std::vector<std::vector<unsigned char>> data; + txnouttype txntype = Solver(script, data); + + if (txntype == TX_PUBKEY) { + CPubKey pubkey(data[0].begin(), data[0].end()); + if (pubkey.IsValid()) { + return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk"); + } + } + if (txntype == TX_PUBKEYHASH) { + uint160 hash(data[0]); + CKeyID keyid(hash); + CPubKey pubkey; + if (provider.GetPubKey(keyid, pubkey)) { + return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh"); + } + } + if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) { + uint160 hash(data[0]); + CKeyID keyid(hash); + CPubKey pubkey; + if (provider.GetPubKey(keyid, pubkey)) { + return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2WPKHGetScript, "wpkh"); + } + } + if (txntype == TX_MULTISIG) { + std::vector<std::unique_ptr<PubkeyProvider>> providers; + for (size_t i = 1; i + 1 < data.size(); ++i) { + CPubKey pubkey(data[i].begin(), data[i].end()); + providers.push_back(InferPubkey(pubkey, ctx, provider)); + } + return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers)); + } + if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) { + uint160 hash(data[0]); + CScriptID scriptid(hash); + CScript subscript; + if (provider.GetCScript(scriptid, subscript)) { + auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider); + if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2SH, "sh"); + } + } + if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) { + CScriptID scriptid; + CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin()); + CScript subscript; + if (provider.GetCScript(scriptid, subscript)) { + auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider); + if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2WSH, "wsh"); + } + } + + CTxDestination dest; + if (ExtractDestination(script, dest)) { + if (GetScriptForDestination(dest) == script) { + return MakeUnique<AddressDescriptor>(std::move(dest)); + } + } + + return MakeUnique<RawDescriptor>(script); +} + } // namespace std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out) @@ -634,3 +715,8 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv if (sp.size() == 0 && ret) return ret; return nullptr; } + +std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider) +{ + return InferScript(script, ParseScriptContext::TOP, provider); +} diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 87e07369c7..0111972f85 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -32,6 +32,10 @@ struct Descriptor { /** Whether the expansion of this descriptor depends on the position. */ virtual bool IsRange() const = 0; + /** Whether this descriptor has all information about signing ignoring lack of private keys. + * This is true for all descriptors except ones that use `raw` or `addr` constructions. */ + virtual bool IsSolvable() const = 0; + /** Convert the descriptor back to a string, undoing parsing. */ virtual std::string ToString() const = 0; @@ -51,5 +55,20 @@ struct Descriptor { /** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out); -#endif // BITCOIN_SCRIPT_DESCRIPTOR_H +/** Find a descriptor for the specified script, using information from provider where possible. + * + * A non-ranged descriptor which only generates the specified script will be returned in all + * circumstances. + * + * For public keys with key origin information, this information will be preserved in the returned + * descriptor. + * + * - If all information for solving `script` is present in `provider`, a descriptor will be returned + * which is `IsSolvable()` and encapsulates said information. + * - Failing that, if `script` corresponds to a known address type, an "addr()" descriptor will be + * returned (which is not `IsSolvable()`). + * - Failing that, a "raw()" descriptor is returned. + */ +std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider); +#endif // BITCOIN_SCRIPT_DESCRIPTOR_H diff --git a/src/script/sign.h b/src/script/sign.h index a478f49789..20c7203b26 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -24,6 +24,11 @@ struct KeyOriginInfo { unsigned char fingerprint[4]; 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; + } }; /** An interface to be implemented by keystores that support signing. */ |