Skip to content

Commit a6c2ae2

Browse files
authored
Merge pull request #1677 from evoskuil/master
Integrate secp256k1::verify_commitment into tapscript extract.
2 parents a6eeb7e + 39006b1 commit a6c2ae2

File tree

4 files changed

+157
-140
lines changed

4 files changed

+157
-140
lines changed

include/bitcoin/system/chain/enums/magic_numbers.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ constexpr uint8_t witness_enabled = 0x01;
8888
/// ---------------------------------------------------------------------------
8989

9090
constexpr size_t signature_cost = 50;
91-
constexpr size_t taproot_max_nodes = 128;
91+
constexpr size_t taproot_max_keys = 128;
9292
constexpr uint8_t taproot_annex_prefix = 0x50;
9393
constexpr uint8_t tapleaf_tapscript = 0b1100'0000; // 0xc0
9494
constexpr uint8_t tapleaf_root_mask = 0b1111'1110; // 0xfe

include/bitcoin/system/crypto/secp256k1.hpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,20 @@ typedef data_array<ec_secret_size> ec_secret;
3636

3737
typedef std_vector<ec_secret> secret_list;
3838

39-
/// Compressed public key:
39+
/// Compressed ECDSA public key:
4040
static constexpr size_t ec_compressed_size = 33;
4141
typedef data_array<ec_compressed_size> ec_compressed;
4242
typedef std_vector<ec_compressed> compressed_list;
4343

44-
/// Uncompressed public key:
44+
/// Uncompressed ECDSA public key:
4545
static constexpr size_t ec_uncompressed_size = 65;
4646
typedef data_array<ec_uncompressed_size> ec_uncompressed;
4747
typedef std_vector<ec_uncompressed> uncompressed_list;
4848

49+
/// X-only Schnorr public key:
50+
static constexpr size_t ec_xonly_size = 32;
51+
typedef data_array<ec_xonly_size> ec_xonly;
52+
4953
// Parsed ECDSA or Schnorr signature:
5054
static constexpr size_t ec_signature_size = 64;
5155
typedef data_array<ec_signature_size> ec_signature;
@@ -228,14 +232,23 @@ static constexpr size_t public_key_size = 32;
228232
/// ---------------------------------------------------------------------------
229233
/// It is recommended to verify a signature after signing.
230234

231-
/// Create a Schnorr signature using a private key (simple version, no tweaks).
235+
/// Create Schnorr signature using a private key (simple version, no tweaks).
232236
BC_API bool sign(ec_signature& out, const ec_secret& secret,
233237
const hash_digest& hash, const hash_digest& auxiliary) NOEXCEPT;
234238

235-
/// Verify an Schnorr signature using a potential x-only point.
236-
BC_API bool verify_signature(const data_slice& x_point,
239+
/// Verify Schnorr signature of hash by associated secret of the x-only point.
240+
BC_API bool verify_signature(const data_chunk& x_point,
241+
const hash_digest& hash, const ec_signature& signature) NOEXCEPT;
242+
243+
/// Verify Schnorr signature of hash by associated secret of the x-only point.
244+
BC_API bool verify_signature(const ec_xonly& x_point,
237245
const hash_digest& hash, const ec_signature& signature) NOEXCEPT;
238246

247+
/// Verify Schnorr commitment of key/parity to hash, results in x-only point.
248+
BC_API bool verify_commitment(const ec_xonly& internal_key,
249+
const hash_digest& tweak, const ec_xonly& tweaked_key,
250+
bool tweaked_key_parity) NOEXCEPT;
251+
239252
} // namespace schnorr
240253
} // namespace system
241254
} // namespace libbitcoin

src/chain/witness_extract.cpp

Lines changed: 111 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <bitcoin/system/chain/enums/opcode.hpp>
2626
#include <bitcoin/system/chain/operation.hpp>
2727
#include <bitcoin/system/chain/script.hpp>
28+
#include <bitcoin/system/crypto/crypto.hpp>
2829
#include <bitcoin/system/data/data.hpp>
2930
#include <bitcoin/system/define.hpp>
3031
#include <bitcoin/system/error/error.hpp>
@@ -82,11 +83,11 @@ static inline const hash_digest& to_array32(const data_chunk& program) NOEXCEPT
8283
static bool is_valid_control_block(const data_chunk& control) NOEXCEPT
8384
{
8485
const auto size = control.size();
85-
constexpr auto max = add1(hash_size) + hash_size * taproot_max_nodes;
86+
constexpr auto max = add1(ec_xonly_size) + ec_xonly_size * taproot_max_keys;
8687

8788
// Control block must be add1(32) + 32m, for integer m [0..128] [bip341].
88-
return !is_limited(size, add1(hash_size), max) && is_zero(
89-
floored_modulo(size - add1(hash_size), hash_size));
89+
return !is_limited(size, add1(ec_xonly_size), max) && is_zero(
90+
floored_modulo(size - add1(ec_xonly_size), ec_xonly_size));
9091
}
9192

9293
// out_script is only useful only for sigop counting.
@@ -146,6 +147,86 @@ inline bool witness::drop_annex(chunk_cptrs& stack) NOEXCEPT
146147
return false;
147148
}
148149

150+
static hash_digest get_tapleaf_hash(uint8_t version,
151+
const script& script) NOEXCEPT
152+
{
153+
hash_digest out{};
154+
stream::out::fast stream{ out };
155+
hash::sha256t::fast<"TapLeaf"> sink{ stream };
156+
sink.write_byte(version);
157+
script.to_data(sink, true);
158+
sink.flush();
159+
return out;
160+
}
161+
162+
static hash_digest get_taptweak_hash(const ec_xonly& key,
163+
const hash_digest& merkle) NOEXCEPT
164+
{
165+
hash_digest out{};
166+
stream::out::fast stream{ out };
167+
hash::sha256t::fast<"TapTweak"> sink{ stream };
168+
sink.write_bytes(key);
169+
sink.write_bytes(merkle);
170+
sink.flush();
171+
return out;
172+
}
173+
174+
static hash_digest get_tapbranch_hash(const hash_digest& left,
175+
const hash_digest& right) NOEXCEPT
176+
{
177+
hash_digest out{};
178+
stream::out::fast stream{ out };
179+
hash::sha256t::fast<"TapBranch"> sink{ stream };
180+
181+
if (std::lexicographical_compare(left.begin(), left.end(),
182+
right.begin(), right.end()))
183+
{
184+
sink.write_bytes(left);
185+
sink.write_bytes(right);
186+
}
187+
else
188+
{
189+
sink.write_bytes(right);
190+
sink.write_bytes(left);
191+
}
192+
193+
sink.flush();
194+
return out;
195+
}
196+
197+
static hash_digest get_merkle_root(const data_chunk& control,
198+
const hash_digest& tapleaf_hash) NOEXCEPT
199+
{
200+
BC_ASSERT(is_valid_control_block(control));
201+
202+
constexpr auto start = add1(ec_xonly_size);
203+
const auto bytes = floored_subtract(control.size(), start);
204+
const auto count = floored_divide(bytes, ec_xonly_size);
205+
const auto begin = std::next(control.data(), start);
206+
const auto nodes = unsafe_array_cast<ec_xonly, taproot_max_keys>(begin);
207+
208+
hash_digest hash{ tapleaf_hash };
209+
for (size_t node{}; node < count; ++node)
210+
hash = get_tapbranch_hash(hash, nodes.at(node));
211+
212+
return hash;
213+
}
214+
215+
static bool verify_commitment(const data_chunk& control,
216+
const data_chunk& program, const hash_digest& hash,
217+
bool parity) NOEXCEPT
218+
{
219+
BC_ASSERT(is_valid_control_block(control));
220+
221+
const auto out = program.data();
222+
const auto& out_key = unsafe_array_cast<uint8_t, ec_xonly_size>(out);
223+
const auto in = std::next(control.data());
224+
const auto& in_key = unsafe_array_cast<uint8_t, ec_xonly_size>(in);
225+
const auto merkle = get_merkle_root(control, hash);
226+
const auto tweak = get_taptweak_hash(out_key, merkle);
227+
return schnorr::verify_commitment(in_key, tweak, out_key, parity);
228+
}
229+
149230
// Extract script and initial execution stack.
150231
code witness::extract_script(script::cptr& out_script,
151232
chunk_cptrs_ptr& out_stack, const script& program_script) const NOEXCEPT
@@ -205,7 +286,7 @@ code witness::extract_script(script::cptr& out_script,
205286
case script_version::taproot:
206287
{
207288
// witness stack : [annex]...
208-
if (program->size() == hash_size)
289+
if (program->size() == ec_xonly_size)
209290
{
210291
auto stack_size = out_stack->size();
211292

@@ -227,139 +308,30 @@ code witness::extract_script(script::cptr& out_script,
227308
// The second-to-last stack element is the script.
228309
out_script = to_shared<script>(*pop(*out_stack), false);
229310

311+
// Extract version and parity from control byte.
230312
const auto bits = control->front();
231313
const auto version = bit_and(bits, tapleaf_root_mask);
232314
const auto parity = bit_and(bits, bit_not(tapleaf_root_mask));
233315

234-
// TODO: pass into signature hashing.
235-
const auto tapscript = (version == tapleaf_tapscript);
236-
237-
if (tapscript)
316+
if (version == tapleaf_tapscript)
238317
{
239-
const auto get_tapleaf_hash = [](uint8_t version,
240-
const script& script) NOEXCEPT
241-
{
242-
hash_digest out{};
243-
stream::out::fast stream{ out };
244-
hash::sha256t::fast<"TapLeaf"> sink{ stream };
245-
sink.write_byte(version);
246-
script.to_data(sink, true);
247-
sink.flush();
248-
return out;
249-
};
250-
251318
// TODO: pass into tapscript signature hashing.
252-
const auto tapleaf_hash = get_tapleaf_hash(version,
253-
*out_script);
254-
255-
const auto get_taptweak_hash = [](
256-
const hash_digest& out_key,
257-
const hash_digest& merkle) NOEXCEPT
258-
{
259-
hash_digest out{};
260-
stream::out::fast stream{ out };
261-
hash::sha256t::fast<"TapTweak"> sink{ stream };
262-
sink.write_bytes(out_key);
263-
sink.write_bytes(merkle);
264-
sink.flush();
265-
return out;
266-
};
267-
268-
// TODO: add secp256k1_xonly_pubkey_parse to secp256k1.
269-
// TODO: add secp256k1_xonly_pubkey_tweak_add_check to secp256k1.
270-
const auto check_taptweak = [&](
271-
const hash_digest& /* in_key */,
272-
const hash_digest& out_key,
273-
const hash_digest& merkle,
274-
bool /* parity */) NOEXCEPT
275-
{
276-
////secp256k1_xonly_pubkey xonly_pubkey{};
277-
////if (!secp256k1_xonly_pubkey_parse(
278-
//// secp256k1_context_static,
279-
//// &xonly_pubkey,
280-
//// out_key.data()))
281-
//// return false;
282-
283-
/* const auto hash = */ get_taptweak_hash(out_key, merkle);
284-
////return secp256k1_xonly_pubkey_tweak_add_check(
285-
//// secp256k1_context_static,
286-
//// in_key.begin(),
287-
//// parity,
288-
//// &xonly_pubkey,
289-
//// hash.data());
290-
return false;
291-
};
292-
293-
const auto get_tapbranch_hash = [](const hash_digest& left,
294-
const hash_digest& right) NOEXCEPT
295-
{
296-
hash_digest out{};
297-
stream::out::fast stream{ out };
298-
hash::sha256t::fast<"TapBranch"> sink{ stream };
299-
300-
// lesser is true, shorter is lesser, equal is not.
301-
if (std::lexicographical_compare(left.begin(), left.end(),
302-
right.begin(), right.end()))
303-
{
304-
sink.write_bytes(left);
305-
sink.write_bytes(right);
306-
}
307-
else
308-
{
309-
sink.write_bytes(right);
310-
sink.write_bytes(left);
311-
}
312-
313-
sink.flush();
314-
return out;
315-
};
316-
317-
// There is no null in validation path.
318-
const auto get_merkle_root = [&](const data_chunk& control,
319-
const hash_digest& tapleaf_hash) NOEXCEPT
320-
{
321-
BC_ASSERT(is_valid_control_block(control));
322-
using node_t = std_array<uint8_t, hash_size>;
323-
constexpr auto key = add1(hash_size);
324-
constexpr auto max = taproot_max_nodes;
325-
hash_digest hash{ tapleaf_hash };
326-
327-
// Cast control into std::array<node_t, 128> (unsafe).
328-
const auto bytes = floored_subtract(control.size(), key);
329-
const auto count = floored_divide(bytes, hash_size);
330-
const auto first = std::next(control.data(), key);
331-
const auto nodes = unsafe_array_cast<node_t, max>(first);
332-
333-
// Read only to actual count of nodes (safe).
334-
for (size_t node{}; node < count; ++node)
335-
hash = get_tapbranch_hash(hash, nodes.at(node));
336-
337-
return hash;
338-
};
339-
340-
const auto verify_commitment = [&](const data_chunk& control,
341-
const data_chunk& program, const hash_digest& hash) NOEXCEPT
342-
{
343-
const auto out = program.data();
344-
const auto in = std::next(control.data());
345-
const auto& in_key = unsafe_array_cast<uint8_t, hash_size>(in);
346-
const auto& out_key = unsafe_array_cast<uint8_t, hash_size>(out);
347-
const auto merkle = get_merkle_root(control, hash);
348-
return check_taptweak(in_key, out_key, merkle, parity);
349-
};
350-
351-
if (!verify_commitment(*control, *program, tapleaf_hash))
319+
const auto tapleaf_hash = get_tapleaf_hash(version, *out_script);
320+
if (!verify_commitment(*control, *program, tapleaf_hash, parity))
352321
return error::invalid_commitment;
353322

354-
// Execute script with remaining stack.
323+
// TODO: return tapleaf_hash in pointer.
324+
// Execute tapleaf script.
325+
// out stack : [stack-elements]
326+
// out script : (popped-from-stack)
355327
return error::script_success;
356328
}
357329

358-
// This op_success script succeeds immediately.
330+
// Others remain unencumbered (success).
331+
// out stack : (empty)
332+
// out script : <op_success>
359333
out_script = success_script_ptr();
360334
out_stack->clear();
361-
362-
// Others remain unencumbered (success).
363335
return error::script_success;
364336
}
365337

@@ -371,34 +343,42 @@ code witness::extract_script(script::cptr& out_script,
371343
{
372344
// Stack element is a signature that must be valid for q.
373345
// Program is q, a 32 byte bip340 public key, so push it.
346+
// out stack : (empty)
347+
// out script : <op_checksig>
374348
out_stack->push_back(program);
375349
out_script = checksig_script_ptr();
376-
377-
// out_stack : <public-key> <signature>
378-
// out_script : <op_checksig>
379350
return error::script_success;
380351
}
381352

382353
// Fail if witness stack was empty.
354+
// witness stack : (empty)
355+
// input script : (empty)
356+
// output script : <1> <32-byte-value>
383357
return error::invalid_witness;
384358
}
385359

386-
// pay-to-anchor (p2a) programs land here (standardness).
360+
// pay-to-anchor (p2a) programs pass by here (standardness).
387361
////script::is_anchor_program_pattern(program_script.ops())
388362

389-
// This op_success script succeeds immediately.
363+
// Version 1 other than 32 bytes...
364+
// witness stack : [stack-elements]
365+
// input script : (empty)
366+
// output script : <1> <2..40 byte program>
367+
// ...remain unencumbered (success).
368+
// out stack : (empty)
369+
// out script : <op_success>
390370
out_script = success_script_ptr();
391371
out_stack->clear();
392-
393-
// Version 1 other than 32 bytes remain unencumbered (success).
394372
return error::script_success;
395373
}
396374

397375
// These versions are reserved for future extensions [bip141].
376+
// output script : <2..16> <2..40 byte program>
398377
case script_version::reserved:
399378
return error::script_success;
400379

401380
// The witness version is undefined.
381+
// output script : <!0..16> <2..40 byte program>
402382
case script_version::unversioned:
403383
default:
404384
return error::invalid_witness;

0 commit comments

Comments
 (0)