Skip to content

Commit a238012

Browse files
committed
Basic Taproot signing logic in script/sign.cpp
1 parent 49487bc commit a238012

File tree

4 files changed

+166
-4
lines changed

4 files changed

+166
-4
lines changed

src/script/interpreter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ enum class MissingDataBehavior
260260
FAIL, //!< Just act as if the signature was invalid
261261
};
262262

263+
template<typename T>
264+
bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb);
265+
263266
template <class T>
264267
class GenericTransactionSignatureChecker : public BaseSignatureChecker
265268
{

src/script/sign.cpp

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <script/signingprovider.h>
1212
#include <script/standard.h>
1313
#include <uint256.h>
14+
#include <util/vector.h>
1415

1516
typedef std::vector<unsigned char> valtype;
1617

@@ -30,6 +31,8 @@ MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMu
3031

3132
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
3233
{
34+
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
35+
3336
CKey key;
3437
if (!provider.GetKey(address, key))
3538
return false;
@@ -48,6 +51,51 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
4851
return true;
4952
}
5053

54+
bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const
55+
{
56+
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
57+
58+
CKey key;
59+
{
60+
// For now, use the old full pubkey-based key derivation logic. As it indexed by
61+
// Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
62+
// with 0x03.
63+
unsigned char b[33] = {0x02};
64+
std::copy(pubkey.begin(), pubkey.end(), b + 1);
65+
CPubKey fullpubkey;
66+
fullpubkey.Set(b, b + 33);
67+
CKeyID keyid = fullpubkey.GetID();
68+
if (!provider.GetKey(keyid, key)) {
69+
b[0] = 0x03;
70+
fullpubkey.Set(b, b + 33);
71+
CKeyID keyid = fullpubkey.GetID();
72+
if (!provider.GetKey(keyid, key)) return false;
73+
}
74+
}
75+
76+
// BIP341/BIP342 signing needs lots of precomputed transaction data. While some
77+
// (non-SIGHASH_DEFAULT) sighash modes exist that can work with just some subset
78+
// of data present, for now, only support signing when everything is provided.
79+
if (!m_txdata || !m_txdata->m_bip341_taproot_ready || !m_txdata->m_spent_outputs_ready) return false;
80+
81+
ScriptExecutionData execdata;
82+
execdata.m_annex_init = true;
83+
execdata.m_annex_present = false; // Only support annex-less signing for now.
84+
if (sigversion == SigVersion::TAPSCRIPT) {
85+
execdata.m_codeseparator_pos_init = true;
86+
execdata.m_codeseparator_pos = 0xFFFFFFFF; // Only support non-OP_CODESEPARATOR BIP342 signing for now.
87+
if (!leaf_hash) return false; // BIP342 signing needs leaf hash.
88+
execdata.m_tapleaf_hash_init = true;
89+
execdata.m_tapleaf_hash = *leaf_hash;
90+
}
91+
uint256 hash;
92+
if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
93+
sig.resize(64);
94+
if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false;
95+
if (nHashType) sig.push_back(nHashType);
96+
return true;
97+
}
98+
5199
static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
52100
{
53101
if (provider.GetCScript(scriptid, script)) {
@@ -104,6 +152,86 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
104152
return false;
105153
}
106154

155+
static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
156+
{
157+
auto lookup_key = std::make_pair(pubkey, leaf_hash);
158+
auto it = sigdata.taproot_script_sigs.find(lookup_key);
159+
if (it != sigdata.taproot_script_sigs.end()) {
160+
sig_out = it->second;
161+
}
162+
if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
163+
sigdata.taproot_script_sigs[lookup_key] = sig_out;
164+
return true;
165+
}
166+
return false;
167+
}
168+
169+
static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatureCreator& creator, SignatureData& sigdata, int leaf_version, const CScript& script, std::vector<valtype>& result)
170+
{
171+
// Only BIP342 tapscript signing is supported for now.
172+
if (leaf_version != TAPROOT_LEAF_TAPSCRIPT) return false;
173+
SigVersion sigversion = SigVersion::TAPSCRIPT;
174+
175+
uint256 leaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(leaf_version) << script).GetSHA256();
176+
177+
// <xonly pubkey> OP_CHECKSIG
178+
if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
179+
XOnlyPubKey pubkey(MakeSpan(script).subspan(1, 32));
180+
std::vector<unsigned char> sig;
181+
if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
182+
result = Vector(std::move(sig));
183+
return true;
184+
}
185+
}
186+
187+
return false;
188+
}
189+
190+
static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
191+
{
192+
TaprootSpendData spenddata;
193+
194+
// Gather information about this output.
195+
if (provider.GetTaprootSpendData(output, spenddata)) {
196+
sigdata.tr_spenddata.Merge(spenddata);
197+
}
198+
199+
// Try key path spending.
200+
{
201+
std::vector<unsigned char> sig;
202+
if (sigdata.taproot_key_path_sig.size() == 0) {
203+
if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
204+
sigdata.taproot_key_path_sig = sig;
205+
}
206+
}
207+
if (sigdata.taproot_key_path_sig.size()) {
208+
result = Vector(sigdata.taproot_key_path_sig);
209+
return true;
210+
}
211+
}
212+
213+
// Try script path spending.
214+
std::vector<std::vector<unsigned char>> smallest_result_stack;
215+
for (const auto& [key, control_blocks] : sigdata.tr_spenddata.scripts) {
216+
const auto& [script, leaf_ver] = key;
217+
std::vector<std::vector<unsigned char>> result_stack;
218+
if (SignTaprootScript(provider, creator, sigdata, leaf_ver, script, result_stack)) {
219+
result_stack.emplace_back(std::begin(script), std::end(script)); // Push the script
220+
result_stack.push_back(*control_blocks.begin()); // Push the smallest control block
221+
if (smallest_result_stack.size() == 0 ||
222+
GetSerializeSize(result_stack, PROTOCOL_VERSION) < GetSerializeSize(smallest_result_stack, PROTOCOL_VERSION)) {
223+
smallest_result_stack = std::move(result_stack);
224+
}
225+
}
226+
}
227+
if (smallest_result_stack.size() != 0) {
228+
result = std::move(smallest_result_stack);
229+
return true;
230+
}
231+
232+
return false;
233+
}
234+
107235
/**
108236
* Sign scriptPubKey using signature made with creator.
109237
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
@@ -125,7 +253,6 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
125253
case TxoutType::NONSTANDARD:
126254
case TxoutType::NULL_DATA:
127255
case TxoutType::WITNESS_UNKNOWN:
128-
case TxoutType::WITNESS_V1_TAPROOT:
129256
return false;
130257
case TxoutType::PUBKEY:
131258
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
@@ -187,6 +314,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
187314
// Could not find witnessScript, add to missing
188315
sigdata.missing_witness_script = uint256(vSolutions[0]);
189316
return false;
317+
318+
case TxoutType::WITNESS_V1_TAPROOT:
319+
return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
190320
} // no default case, so the compiler can warn about missing cases
191321
assert(false);
192322
}
@@ -249,6 +379,12 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
249379
sigdata.scriptWitness.stack = result;
250380
sigdata.witness = true;
251381
result.clear();
382+
} else if (whichType == TxoutType::WITNESS_V1_TAPROOT && !P2SH) {
383+
sigdata.witness = true;
384+
if (solved) {
385+
sigdata.scriptWitness.stack = std::move(result);
386+
}
387+
result.clear();
252388
} else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
253389
sigdata.witness = true;
254390
}
@@ -414,6 +550,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
414550
public:
415551
DummySignatureChecker() {}
416552
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
553+
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const override { return true; }
417554
};
418555
const DummySignatureChecker DUMMY_CHECKER;
419556

@@ -439,6 +576,11 @@ class DummySignatureCreator final : public BaseSignatureCreator {
439576
vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL;
440577
return true;
441578
}
579+
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* tweak, SigVersion sigversion) const override
580+
{
581+
sig.assign(64, '\000');
582+
return true;
583+
}
442584
};
443585

444586
}

src/script/sign.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
#include <pubkey.h>
1212
#include <script/interpreter.h>
1313
#include <script/keyorigin.h>
14+
#include <script/standard.h>
1415
#include <span.h>
1516
#include <streams.h>
1617

1718
class CKey;
1819
class CKeyID;
1920
class CScript;
20-
class CScriptID;
2121
class CTransaction;
2222
class SigningProvider;
2323

@@ -31,6 +31,7 @@ class BaseSignatureCreator {
3131

3232
/** Create a singular (non-script) signature. */
3333
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
34+
virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
3435
};
3536

3637
/** A signature creator for transactions. */
@@ -47,6 +48,7 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
4748
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
4849
const BaseSignatureChecker& Checker() const override { return checker; }
4950
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
51+
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
5052
};
5153

5254
/** A signature creator that just produces 71-byte empty signatures. */
@@ -66,8 +68,11 @@ struct SignatureData {
6668
CScript redeem_script; ///< The redeemScript (if any) for the input
6769
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
6870
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
71+
TaprootSpendData tr_spenddata; ///< Taproot spending data.
6972
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
7073
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
74+
std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
75+
std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
7176
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
7277
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
7378
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)

src/script/standard.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,26 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
210210
/** Generate a multisig script. */
211211
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
212212

213+
struct ShortestVectorFirstComparator
214+
{
215+
bool operator()(const std::vector<unsigned char>& a, const std::vector<unsigned char>& b) const
216+
{
217+
if (a.size() < b.size()) return true;
218+
if (a.size() > b.size()) return false;
219+
return a < b;
220+
}
221+
};
222+
213223
struct TaprootSpendData
214224
{
215225
/** The BIP341 internal key. */
216226
XOnlyPubKey internal_key;
217227
/** The Merkle root of the script tree (0 if no scripts). */
218228
uint256 merkle_root;
219-
/** Map from (script, leaf_version) to (sets of) control blocks. */
220-
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>>> scripts;
229+
/** Map from (script, leaf_version) to (sets of) control blocks.
230+
* The control blocks are sorted by size, so that the signing logic can
231+
* easily prefer the cheapest one. */
232+
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
221233
/** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
222234
void Merge(TaprootSpendData other);
223235
};

0 commit comments

Comments
 (0)