mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-03 09:56:38 -05:00
Merge bitcoin/bitcoin#22275: A few follow-ups for taproot signing
08f57a0057
Assert that IsComplete() in GetSpendData() (Pieter Wuille)d8f4b976d5
Remove default nHashTypeIn arguments to MutableTransactionSignatureCreator (Pieter Wuille)c7048aae95
Simplify SignTransaction precomputation loop (Pieter Wuille)addb9b5a71
Improve comments in taproot signing logic (Pieter Wuille) Pull request description: This addresses a few review comments from #21365 that were left at the time of merge (as well as some from #22166 applying to the commit it shared with #21365). I do not think any are blockers for a 22.0 release. ACKs for top commit: theStack: re-ACK08f57a0057
🌴 Zero-1729: crACK08f57a0
jonatack: Code review ACK08f57a0057
per `git range-diffe9d6eb1
9336670 08f57a0` followed by re-code review per commit to swap context back into memory and debug build/run unit tests + feature_taproot.py as a sanity check Tree-SHA512: 968750109ba8d6faf3016035a38f81c6aefb724c632a3cab0bbf43cf31b9d187b6b0fddd8772768f57338df11eb07ab9c4c6dacdf3cf35b71f29699c67a301ea
This commit is contained in:
commit
e826b22da2
8 changed files with 34 additions and 21 deletions
13
src/key.h
13
src/key.h
|
@ -133,10 +133,15 @@ public:
|
||||||
* optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
|
* optionally tweaked by *merkle_root. Additional nonce entropy can be provided through
|
||||||
* aux.
|
* aux.
|
||||||
*
|
*
|
||||||
* When merkle_root is not nullptr, this results in a signature with a modified key as
|
* merkle_root is used to optionally perform tweaking of the private key, as specified
|
||||||
* specified in BIP341:
|
* in BIP341:
|
||||||
* - If merkle_root->IsNull(): key + H_TapTweak(pubkey)*G
|
* - If merkle_root == nullptr: no tweaking is done, sign with key directly (this is
|
||||||
* - Otherwise: key + H_TapTweak(pubkey || *merkle_root)
|
* used for signatures in BIP342 script).
|
||||||
|
* - If merkle_root->IsNull(): sign with key + H_TapTweak(pubkey) (this is used for
|
||||||
|
* key path spending when no scripts are present).
|
||||||
|
* - Otherwise: sign with key + H_TapTweak(pubkey || *merkle_root)
|
||||||
|
* (this is used for key path spending, with specific
|
||||||
|
* Merkle root of the script tree).
|
||||||
*/
|
*/
|
||||||
bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const;
|
bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const;
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,13 @@ struct PrecomputedTransactionData
|
||||||
|
|
||||||
PrecomputedTransactionData() = default;
|
PrecomputedTransactionData() = default;
|
||||||
|
|
||||||
|
/** Initialize this PrecomputedTransactionData with transaction data.
|
||||||
|
*
|
||||||
|
* @param[in] tx The transaction for which data is being precomputed.
|
||||||
|
* @param[in] spent_outputs The CTxOuts being spent, one for each tx.vin, in order.
|
||||||
|
* @param[in] force Whether to precompute data for all optional features,
|
||||||
|
* regardless of what is in the inputs (used at signing
|
||||||
|
* time, when the inputs aren't filled in yet). */
|
||||||
template <class T>
|
template <class T>
|
||||||
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
|
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs, bool force = false);
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
|
||||||
|
|
||||||
CKey key;
|
CKey key;
|
||||||
{
|
{
|
||||||
// For now, use the old full pubkey-based key derivation logic. As it indexed by
|
// For now, use the old full pubkey-based key derivation logic. As it is indexed by
|
||||||
// Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
|
// Hash160(full pubkey), we need to try both a version prefixed with 0x02, and one
|
||||||
// with 0x03.
|
// with 0x03.
|
||||||
unsigned char b[33] = {0x02};
|
unsigned char b[33] = {0x02};
|
||||||
|
@ -640,25 +640,22 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
|
||||||
|
|
||||||
PrecomputedTransactionData txdata;
|
PrecomputedTransactionData txdata;
|
||||||
std::vector<CTxOut> spent_outputs;
|
std::vector<CTxOut> spent_outputs;
|
||||||
spent_outputs.resize(mtx.vin.size());
|
for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
|
||||||
bool have_all_spent_outputs = true;
|
|
||||||
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
|
|
||||||
CTxIn& txin = mtx.vin[i];
|
CTxIn& txin = mtx.vin[i];
|
||||||
auto coin = coins.find(txin.prevout);
|
auto coin = coins.find(txin.prevout);
|
||||||
if (coin == coins.end() || coin->second.IsSpent()) {
|
if (coin == coins.end() || coin->second.IsSpent()) {
|
||||||
have_all_spent_outputs = false;
|
txdata.Init(txConst, /* spent_outputs */ {}, /* force */ true);
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
spent_outputs[i] = CTxOut(coin->second.out.nValue, coin->second.out.scriptPubKey);
|
spent_outputs.emplace_back(coin->second.out.nValue, coin->second.out.scriptPubKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (have_all_spent_outputs) {
|
if (spent_outputs.size() == mtx.vin.size()) {
|
||||||
txdata.Init(txConst, std::move(spent_outputs), true);
|
txdata.Init(txConst, std::move(spent_outputs), true);
|
||||||
} else {
|
|
||||||
txdata.Init(txConst, {}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign what we can:
|
// Sign what we can:
|
||||||
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
|
for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
|
||||||
CTxIn& txin = mtx.vin[i];
|
CTxIn& txin = mtx.vin[i];
|
||||||
auto coin = coins.find(txin.prevout);
|
auto coin = coins.find(txin.prevout);
|
||||||
if (coin == coins.end() || coin->second.IsSpent()) {
|
if (coin == coins.end() || coin->second.IsSpent()) {
|
||||||
|
|
|
@ -45,8 +45,8 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
|
||||||
const PrecomputedTransactionData* m_txdata;
|
const PrecomputedTransactionData* m_txdata;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn);
|
||||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn = SIGHASH_ALL);
|
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData* txdata, int nHashTypeIn);
|
||||||
const BaseSignatureChecker& Checker() const override { return checker; }
|
const BaseSignatureChecker& Checker() const override { return checker; }
|
||||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||||
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;
|
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;
|
||||||
|
|
|
@ -504,6 +504,7 @@ WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_
|
||||||
|
|
||||||
TaprootSpendData TaprootBuilder::GetSpendData() const
|
TaprootSpendData TaprootBuilder::GetSpendData() const
|
||||||
{
|
{
|
||||||
|
assert(IsComplete());
|
||||||
TaprootSpendData spd;
|
TaprootSpendData spd;
|
||||||
spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
|
spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
|
||||||
spd.internal_key = m_internal_key;
|
spd.internal_key = m_internal_key;
|
||||||
|
|
|
@ -227,8 +227,11 @@ struct TaprootSpendData
|
||||||
/** The Merkle root of the script tree (0 if no scripts). */
|
/** The Merkle root of the script tree (0 if no scripts). */
|
||||||
uint256 merkle_root;
|
uint256 merkle_root;
|
||||||
/** Map from (script, leaf_version) to (sets of) control blocks.
|
/** Map from (script, leaf_version) to (sets of) control blocks.
|
||||||
* The control blocks are sorted by size, so that the signing logic can
|
* More than one control block for a given script is only possible if it
|
||||||
* easily prefer the cheapest one. */
|
* appears in multiple branches of the tree. We keep them all so that
|
||||||
|
* inference can reconstruct the full tree. Within each set, the control
|
||||||
|
* blocks are sorted by size, so that the signing logic can easily
|
||||||
|
* prefer the cheapest one. */
|
||||||
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
|
std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> scripts;
|
||||||
/** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
|
/** Merge other TaprootSpendData (for the same scriptPubKey) into this. */
|
||||||
void Merge(TaprootSpendData other);
|
void Merge(TaprootSpendData other);
|
||||||
|
@ -252,7 +255,7 @@ private:
|
||||||
/** Merkle hash of this node. */
|
/** Merkle hash of this node. */
|
||||||
uint256 hash;
|
uint256 hash;
|
||||||
/** Tracked leaves underneath this node (either from the node itself, or its children).
|
/** Tracked leaves underneath this node (either from the node itself, or its children).
|
||||||
* The merkle_branch field for each is the partners to get to *this* node. */
|
* The merkle_branch field of each is the partners to get to *this* node. */
|
||||||
std::vector<LeafInfo> leaves;
|
std::vector<LeafInfo> leaves;
|
||||||
};
|
};
|
||||||
/** Whether the builder is in a valid state so far. */
|
/** Whether the builder is in a valid state so far. */
|
||||||
|
|
|
@ -1160,7 +1160,7 @@ SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction&
|
||||||
SignatureData data;
|
SignatureData data;
|
||||||
data.MergeSignatureData(scriptSig1);
|
data.MergeSignatureData(scriptSig1);
|
||||||
data.MergeSignatureData(scriptSig2);
|
data.MergeSignatureData(scriptSig2);
|
||||||
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&tx, 0, txout.nValue), txout.scriptPubKey, data);
|
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&tx, 0, txout.nValue, SIGHASH_DEFAULT), txout.scriptPubKey, data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -561,7 +561,7 @@ SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutabl
|
||||||
SignatureData sigdata;
|
SignatureData sigdata;
|
||||||
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
|
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
|
||||||
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
|
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
|
||||||
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue), tx->vout[0].scriptPubKey, sigdata);
|
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
|
||||||
return sigdata;
|
return sigdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue