mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
Merge bitcoin/bitcoin#30048: crypto: add NUMS_H
const
9408a04e42
tests, fuzz: use new NUMS_H const (josibake)b946f8a4c5
crypto: add NUMS_H const (josibake) Pull request description: Broken out from #28122 --- [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs) defines a NUMS point `H` as *H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)* which is [constructed](11af7015de/src/modules/rangeproof/main_impl.h (L16)
) by taking the hash of the standard uncompressed encoding of the [secp256k1](https://www.secg.org/sec2-v2.pdf) base point G as X coordinate." Add this as a constant so it can be used in our codebase. My primary motivation is BIP352 specifies a special case for when taproot spends use `H` as the internal key, but outside of BIP352 it seems generally useful to have `H` in the codebase, for testing or other use cases. ACKs for top commit: paplorinc: re-ACK9408a04e42
achow101: ACK9408a04e42
theStack: Code-review ACK9408a04e42
Tree-SHA512: ad84492f5d635c0cb05bd82546079ded7e5138e95361f20d8285a9ad6e69c10ee2cc3fe46e16b46ef03c4253c8bee1051911c6b91264c90c3b1ad33a824bff4b
This commit is contained in:
commit
4877fcdb42
8 changed files with 39 additions and 10 deletions
|
@ -13,6 +13,7 @@
|
||||||
#include <secp256k1_schnorrsig.h>
|
#include <secp256k1_schnorrsig.h>
|
||||||
#include <span.h>
|
#include <span.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
#include <util/strencodings.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -181,6 +182,17 @@ int ecdsa_signature_parse_der_lax(secp256k1_ecdsa_signature* sig, const unsigned
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Nothing Up My Sleeve (NUMS) point
|
||||||
|
*
|
||||||
|
* NUMS_H is a point with an unknown discrete logarithm, constructed by taking the sha256 of 'g'
|
||||||
|
* (uncompressed encoding), which happens to be a point on the curve.
|
||||||
|
*
|
||||||
|
* For an example script for calculating H, refer to the unit tests in
|
||||||
|
* ./test/functional/test_framework/crypto/secp256k1.py
|
||||||
|
*/
|
||||||
|
static const std::vector<unsigned char> NUMS_H_DATA{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
|
||||||
|
const XOnlyPubKey XOnlyPubKey::NUMS_H{NUMS_H_DATA};
|
||||||
|
|
||||||
XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
|
XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
|
||||||
{
|
{
|
||||||
assert(bytes.size() == 32);
|
assert(bytes.size() == 32);
|
||||||
|
|
|
@ -233,6 +233,11 @@ private:
|
||||||
uint256 m_keydata;
|
uint256 m_keydata;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/** Nothing Up My Sleeve point H
|
||||||
|
* Used as an internal key for provably disabling the key path spend
|
||||||
|
* see BIP341 for more details */
|
||||||
|
static const XOnlyPubKey NUMS_H;
|
||||||
|
|
||||||
/** Construct an empty x-only pubkey. */
|
/** Construct an empty x-only pubkey. */
|
||||||
XOnlyPubKey() = default;
|
XOnlyPubKey() = default;
|
||||||
|
|
||||||
|
|
|
@ -309,9 +309,6 @@ const struct KeyComparator {
|
||||||
// A dummy scriptsig to pass to VerifyScript (we always use Segwit v0).
|
// A dummy scriptsig to pass to VerifyScript (we always use Segwit v0).
|
||||||
const CScript DUMMY_SCRIPTSIG;
|
const CScript DUMMY_SCRIPTSIG;
|
||||||
|
|
||||||
//! Public key to be used as internal key for dummy Taproot spends.
|
|
||||||
const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
|
|
||||||
|
|
||||||
//! Construct a miniscript node as a shared_ptr.
|
//! Construct a miniscript node as a shared_ptr.
|
||||||
template<typename... Args> NodeRef MakeNodeRef(Args&&... args) {
|
template<typename... Args> NodeRef MakeNodeRef(Args&&... args) {
|
||||||
return miniscript::MakeNodeRef<CPubKey>(miniscript::internal::NoDupCheck{}, std::forward<Args>(args)...);
|
return miniscript::MakeNodeRef<CPubKey>(miniscript::internal::NoDupCheck{}, std::forward<Args>(args)...);
|
||||||
|
@ -1018,7 +1015,7 @@ CScript ScriptPubKey(MsCtx ctx, const CScript& script, TaprootBuilder& builder)
|
||||||
|
|
||||||
// For Taproot outputs we always use a tree with a single script and a dummy internal key.
|
// For Taproot outputs we always use a tree with a single script and a dummy internal key.
|
||||||
builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT);
|
builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT);
|
||||||
builder.Finalize(XOnlyPubKey{NUMS_PK});
|
builder.Finalize(XOnlyPubKey::NUMS_H);
|
||||||
return GetScriptForDestination(builder.GetOutput());
|
return GetScriptForDestination(builder.GetOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <common/system.h>
|
#include <common/system.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
|
#include <span.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <test/util/random.h>
|
#include <test/util/random.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
|
@ -364,4 +365,13 @@ BOOST_AUTO_TEST_CASE(key_ellswift)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(bip341_test_h)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> G_uncompressed = ParseHex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8");
|
||||||
|
HashWriter hw;
|
||||||
|
hw.write(MakeByteSpan(G_uncompressed));
|
||||||
|
XOnlyPubKey H{hw.GetSHA256()};
|
||||||
|
BOOST_CHECK(XOnlyPubKey::NUMS_H == H);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -288,9 +288,6 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Public key to be used as internal key for dummy Taproot spends.
|
|
||||||
const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
|
|
||||||
|
|
||||||
using Fragment = miniscript::Fragment;
|
using Fragment = miniscript::Fragment;
|
||||||
using NodeRef = miniscript::NodeRef<CPubKey>;
|
using NodeRef = miniscript::NodeRef<CPubKey>;
|
||||||
using miniscript::operator"" _mst;
|
using miniscript::operator"" _mst;
|
||||||
|
@ -330,7 +327,7 @@ CScript ScriptPubKey(miniscript::MiniscriptContext ctx, const CScript& script, T
|
||||||
|
|
||||||
// For Taproot outputs we always use a tree with a single script and a dummy internal key.
|
// For Taproot outputs we always use a tree with a single script and a dummy internal key.
|
||||||
builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT);
|
builder.Add(0, script, TAPROOT_LEAF_TAPSCRIPT);
|
||||||
builder.Finalize(XOnlyPubKey{NUMS_PK});
|
builder.Finalize(XOnlyPubKey::NUMS_H);
|
||||||
return GetScriptForDestination(builder.GetOutput());
|
return GetScriptForDestination(builder.GetOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1268,8 +1268,7 @@ BOOST_AUTO_TEST_CASE(sign_invalid_miniscript)
|
||||||
const auto invalid_pubkey{ParseHex("173d36c8c9c9c9ffffffffffff0200000000021e1e37373721361818181818181e1e1e1e19000000000000000000b19292929292926b006c9b9b9292")};
|
const auto invalid_pubkey{ParseHex("173d36c8c9c9c9ffffffffffff0200000000021e1e37373721361818181818181e1e1e1e19000000000000000000b19292929292926b006c9b9b9292")};
|
||||||
TaprootBuilder builder;
|
TaprootBuilder builder;
|
||||||
builder.Add(0, {invalid_pubkey}, 0xc0);
|
builder.Add(0, {invalid_pubkey}, 0xc0);
|
||||||
XOnlyPubKey nums{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
|
builder.Finalize(XOnlyPubKey::NUMS_H);
|
||||||
builder.Finalize(nums);
|
|
||||||
prev.vout.emplace_back(0, GetScriptForDestination(builder.GetOutput()));
|
prev.vout.emplace_back(0, GetScriptForDestination(builder.GetOutput()));
|
||||||
curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
|
curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
|
||||||
sig_data.tr_spenddata = builder.GetSpendData();
|
sig_data.tr_spenddata = builder.GetSpendData();
|
||||||
|
|
|
@ -25,6 +25,7 @@ TEST_FRAMEWORK_MODULES = [
|
||||||
"crypto.muhash",
|
"crypto.muhash",
|
||||||
"crypto.poly1305",
|
"crypto.poly1305",
|
||||||
"crypto.ripemd160",
|
"crypto.ripemd160",
|
||||||
|
"crypto.secp256k1",
|
||||||
"script",
|
"script",
|
||||||
"segwit_addr",
|
"segwit_addr",
|
||||||
"wallet_util",
|
"wallet_util",
|
||||||
|
|
|
@ -15,6 +15,8 @@ Exports:
|
||||||
* G: the secp256k1 generator point
|
* G: the secp256k1 generator point
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
class FE:
|
class FE:
|
||||||
"""Objects of this class represent elements of the field GF(2**256 - 2**32 - 977).
|
"""Objects of this class represent elements of the field GF(2**256 - 2**32 - 977).
|
||||||
|
@ -344,3 +346,9 @@ class FastGEMul:
|
||||||
|
|
||||||
# Precomputed table with multiples of G for fast multiplication
|
# Precomputed table with multiples of G for fast multiplication
|
||||||
FAST_G = FastGEMul(G)
|
FAST_G = FastGEMul(G)
|
||||||
|
|
||||||
|
class TestFrameworkSecp256k1(unittest.TestCase):
|
||||||
|
def test_H(self):
|
||||||
|
H = sha256(G.to_bytes_uncompressed()).digest()
|
||||||
|
assert GE.lift_x(FE.from_bytes(H)) is not None
|
||||||
|
self.assertEqual(H.hex(), "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")
|
||||||
|
|
Loading…
Add table
Reference in a new issue