Skip to content

Commit 7c7e782

Browse files
authored
Merge pull request #1796 from evoskuil/master
Expand address parse for taproot, patterns for witness/taproot.
2 parents 04bcbba + 4b9439e commit 7c7e782

14 files changed

Lines changed: 1203 additions & 100 deletions

File tree

builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj

Lines changed: 265 additions & 1 deletion
Large diffs are not rendered by default.

builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters

Lines changed: 715 additions & 1 deletion
Large diffs are not rendered by default.

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ enum class script_pattern
5353
/// Signature script: <sig>[sig][sig...] <redeemScript>
5454
pay_script_hash,
5555

56+
/// Pay to Witness Public Key Hash [BIP141]
57+
/// Pubkey script: OP_0 <20-byte key hash>
58+
pay_witness_key_hash,
59+
60+
/// Pay to Witness Script Hash [BIP141]
61+
/// Pubkey script: OP_0 <32-byte script hash>
62+
pay_witness_script_hash,
63+
64+
/// Pay to Taproot [BIP341]
65+
/// Pubkey script: OP_1 <32-byte output key>
66+
pay_witness_v1_taproot,
67+
5668
/// Sign Multisig script [BIP11]
5769
sign_multisig,
5870

@@ -71,6 +83,7 @@ enum class script_pattern
7183
/// The script may be valid but does not conform to the common templates.
7284
/// Such scripts are always accepted if they are mined into blocks, but
7385
/// transactions with uncommon scripts may not be forwarded by peers.
86+
/// This is not a "standardness" concept, just an unrecognized pattern.
7487
non_standard
7588
};
7689

include/bitcoin/system/chain/script.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,19 @@ class BC_API script
5151
static constexpr bool is_anchor_program_pattern(const operations& ops) NOEXCEPT;
5252
static constexpr bool is_pay_null_data_pattern(const operations& ops) NOEXCEPT;
5353
static constexpr bool is_pay_op_return_pattern(const operations& ops) NOEXCEPT;
54-
static constexpr bool is_pay_multisig_pattern(const operations& ops) NOEXCEPT;
5554
static constexpr bool is_pay_public_key_pattern(const operations& ops) NOEXCEPT;
5655
static constexpr bool is_pay_key_hash_pattern(const operations& ops) NOEXCEPT;
5756
static constexpr bool is_pay_script_hash_pattern(const operations& ops) NOEXCEPT;
5857
static constexpr bool is_pay_witness_pattern(const operations& ops) NOEXCEPT;
5958
static constexpr bool is_pay_witness_key_hash_pattern(const operations& ops) NOEXCEPT;
6059
static constexpr bool is_pay_witness_script_hash_pattern(const operations& ops) NOEXCEPT;
60+
static constexpr bool is_pay_witness_taproot_pattern(const operations& ops) NOEXCEPT;
6161
static constexpr bool is_sign_multisig_pattern(const operations& ops) NOEXCEPT;
6262
static constexpr bool is_sign_public_key_pattern(const operations& ops) NOEXCEPT;
6363
static constexpr bool is_sign_key_hash_pattern(const operations& ops) NOEXCEPT;
6464
static constexpr bool is_sign_script_hash_pattern(const operations& ops) NOEXCEPT;
6565
static bool is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT;
66+
static bool is_pay_multisig_pattern(const operations& ops) NOEXCEPT;
6667

6768
static inline operations to_pay_null_data_pattern(
6869
const data_slice& data) NOEXCEPT;
@@ -84,6 +85,8 @@ class BC_API script
8485
const short_hash& hash) NOEXCEPT;
8586
static inline operations to_pay_witness_script_hash_pattern(
8687
const hash_digest& hash) NOEXCEPT;
88+
static inline operations to_pay_witness_taproot_pattern(
89+
const hash_digest& hash) NOEXCEPT;
8790

8891
/// Pattern optimizations.
8992
inline bool is_pay_to_witness(uint32_t active_flags) const NOEXCEPT;
@@ -108,7 +111,7 @@ class BC_API script
108111
script(reader& source, bool prefix) NOEXCEPT;
109112

110113
// TODO: move to config serialization wrapper.
111-
script(const std::string_view& mnemonic) NOEXCEPT;
114+
script(const std::string_view& mnemonic, bool bitcoind=false) NOEXCEPT;
112115

113116
/// Operators.
114117
/// -----------------------------------------------------------------------
@@ -128,7 +131,8 @@ class BC_API script
128131
void to_data(writer& sink, bool prefix) const NOEXCEPT;
129132

130133
// TODO: move to config serialization wrapper.
131-
std::string to_string(uint32_t active_flags) const NOEXCEPT;
134+
std::string to_string(uint32_t active_flags,
135+
bool bitcoind=false) const NOEXCEPT;
132136

133137
/// Reset mutable signature hashing op_codeseparator offset.
134138
void clear_offset() const NOEXCEPT;
@@ -174,7 +178,8 @@ class BC_API script
174178
static inline size_t op_size(size_t total, const operation& op) NOEXCEPT;
175179
static script from_operations(operations&& ops) NOEXCEPT;
176180
static script from_operations(const operations& ops) NOEXCEPT;
177-
static script from_string(const std::string_view& mnemonic) NOEXCEPT;
181+
static script from_string(const std::string_view& mnemonic,
182+
bool bitcoind=false) NOEXCEPT;
178183
static size_t op_count(reader& source) NOEXCEPT;
179184
static size_t serialized_size(const operations& ops) NOEXCEPT;
180185
void assign_data(reader& source, bool prefix) NOEXCEPT;

include/bitcoin/system/impl/chain/script_patterns.ipp

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -123,41 +123,6 @@ constexpr bool script::is_pay_op_return_pattern(const operations& ops) NOEXCEPT
123123
&& ops[0].code() == opcode::op_return;
124124
}
125125

126-
// The current 16 (or 20) limit does not affect server indexing because bare
127-
// multisig is not indexable and p2sh multisig is byte-limited to 15 sigs.
128-
// The satoshi client policy limit is 3 signatures for bare multisig.
129-
constexpr bool script::is_pay_multisig_pattern(const operations& ops) NOEXCEPT
130-
{
131-
constexpr auto op_1 = static_cast<uint8_t>(opcode::push_positive_1);
132-
constexpr auto op_16 = static_cast<uint8_t>(opcode::push_positive_16);
133-
134-
const auto op_count = ops.size();
135-
136-
if (op_count < 4 || ops[op_count - 1].code() != opcode::checkmultisig)
137-
return false;
138-
139-
const auto op_m = static_cast<uint8_t>(ops[0].code());
140-
const auto op_n = static_cast<uint8_t>(ops[op_count - 2].code());
141-
142-
if (op_m < op_1 || op_m > op_n || op_n < op_1 || op_n > op_16)
143-
return false;
144-
145-
const auto number = op_n - op_1 + 1u;
146-
const auto points = op_count - 3u;
147-
148-
if (number != points)
149-
return false;
150-
151-
for (auto op = std::next(ops.begin());
152-
op != std::prev(ops.end(), 2); ++op)
153-
{
154-
if (!is_public_key(op->data()))
155-
return false;
156-
}
157-
158-
return true;
159-
}
160-
161126
// The satoshi client considers this non-standard for policy.
162127
constexpr bool script::is_pay_public_key_pattern(
163128
const operations& ops) NOEXCEPT
@@ -204,9 +169,6 @@ constexpr bool script::is_pay_witness_key_hash_pattern(
204169
&& ops[1].code() == opcode::push_size_20;
205170
}
206171

207-
// ****************************************************************************
208-
// CONSENSUS: this pattern is used to activate bip141 validation rules.
209-
// ****************************************************************************
210172
constexpr bool script::is_pay_witness_script_hash_pattern(
211173
const operations& ops) NOEXCEPT
212174
{
@@ -215,6 +177,14 @@ constexpr bool script::is_pay_witness_script_hash_pattern(
215177
&& ops[1].code() == opcode::push_size_32;
216178
}
217179

180+
constexpr bool script::is_pay_witness_taproot_pattern(
181+
const operations& ops) NOEXCEPT
182+
{
183+
return ops.size() == 2
184+
&& ops[0].code() == opcode::push_size_1
185+
&& ops[1].code() == opcode::push_size_32;
186+
}
187+
218188
// The first push is based on wacky satoshi op_check_multisig behavior that
219189
// we must perpetuate, though this is not used in consensus validation.
220190
constexpr bool script::is_sign_multisig_pattern(const operations& ops) NOEXCEPT
@@ -389,6 +359,16 @@ inline operations script::to_pay_witness_script_hash_pattern(
389359
};
390360
}
391361

362+
inline operations script::to_pay_witness_taproot_pattern(
363+
const hash_digest& hash) NOEXCEPT
364+
{
365+
return
366+
{
367+
{ opcode::push_size_1 },
368+
{ to_chunk(hash), false }
369+
};
370+
}
371+
392372
// private
393373
inline size_t script::op_size(size_t total, const operation& op) NOEXCEPT
394374
{

include/bitcoin/system/impl/machine/program.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ pop_ternary32(int32_t& upper, int32_t& lower,
313313
// This value is only used for stack indexing (key/sig counts & pick/roll).
314314
// The upper bound of int32_t always exceeds the possible stack size, which
315315
// is checked downstream. Similarly, a negative causes a downstream script
316-
// failure. As such it is sufficient to fail on non-idexability here,
316+
// failure. As such it is sufficient to fail on non-indexability here,
317317
// allowing the value to be returned as a valid and unsigned stack index.
318318
// ****************************************************************************
319319
TEMPLATE

include/bitcoin/system/wallet/addresses/witness_address.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class BC_API witness_address
7070
{
7171
version0_p2kh,
7272
version0_p2sh,
73+
version1_taproot,
7374
unknown,
7475
invalid
7576
};
@@ -95,6 +96,7 @@ class BC_API witness_address
9596
static const size_t program_maximum_size;
9697
static const size_t version0_p2kh_program_size;
9798
static const size_t version0_p2sh_program_size;
99+
static const size_t version1_taproot_program_size;
98100

99101
// TODO: script address extraction, see payment_address.
100102

@@ -110,8 +112,8 @@ class BC_API witness_address
110112
/// Constructors.
111113
witness_address() NOEXCEPT;
112114
witness_address(const std::string& address, bool strict=false) NOEXCEPT;
113-
witness_address(const data_slice& program, const std::string& prefix,
114-
uint8_t version) NOEXCEPT;
115+
witness_address(const data_slice& program, uint8_t version,
116+
const std::string& prefix=mainnet) NOEXCEPT;
115117

116118
// version0_p2kh
117119
witness_address(const short_hash& public_key_hash,
@@ -158,7 +160,7 @@ class BC_API witness_address
158160
static witness_address from_address(const std::string& address,
159161
bool strict) NOEXCEPT;
160162
static witness_address from_parameters(const data_slice& program,
161-
const std::string& prefix, uint8_t version) NOEXCEPT;
163+
uint8_t version, const std::string& prefix) NOEXCEPT;
162164

163165
// version0_p2kh
164166
static witness_address from_short(const short_hash& hash,

src/chain/json/script.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ DEFINE_JSON_TO_TAG(script)
3333

3434
DEFINE_JSON_FROM_TAG(script)
3535
{
36-
// TODO: inject rules.
36+
// TODO: how to inject rules.
3737
value = instance.to_string(flags::all_rules);
3838
}
3939

@@ -55,8 +55,8 @@ DEFINE_JSON_FROM_TAGGED(bitcoind_tag, script)
5555
const auto& script = instance.value;
5656
value =
5757
{
58-
// TODO: this is native encoding (and how does bitcoind handle forks).
59-
{ "asm", script.to_string(flags::all_rules) },
58+
// TODO: how to inject rules.
59+
{ "asm", script.to_string(flags::all_rules, true) },
6060
{ "hex", encode_base16(script.to_data(false)) }
6161
};
6262
}

src/chain/script.cpp

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3939
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
4040

4141
// static
42-
// TODO: would be inlined but machine is a circular include.
42+
// This would be inlined but machine is a circular include.
4343
//*****************************************************************************
4444
// CONSENSUS: BIP34 requires coinbase input script to begin with one byte
4545
// that indicates height size. This is inconsistent with an extreme future
@@ -54,6 +54,40 @@ bool script::is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT
5454
&& ops[0].data() == chunk::from_integer(to_signed(height));
5555
}
5656

57+
// static
58+
// This would be constexpr but machine is a circular include.
59+
// op_check_multisig is limited to 20 signatures (max_script_public_keys).
60+
bool script::is_pay_multisig_pattern(const operations& ops) NOEXCEPT
61+
{
62+
if (ops.size() < 4u || ops.back().code() != opcode::checkmultisig)
63+
return false;
64+
65+
const auto signatures = [](const operation& op) NOEXCEPT
66+
{
67+
using namespace machine::number;
68+
if (op.is_positive())
69+
return operation::opcode_to_positive(op.code());
70+
71+
int32_t count{};
72+
if (op.is_payload() && integer<4>::from_chunk(count, op.data()) &&
73+
!is_limited(count, one, max_script_public_keys))
74+
return narrow_sign_cast<uint8_t>(count);
75+
76+
return 0_u8;
77+
};
78+
79+
const auto m = signatures(ops.front());
80+
const auto n = signatures(ops[ops.size() - 2u]);
81+
if (is_zero(m) || is_zero(n) || (m > n) || (n != ops.size() - 3u))
82+
return false;
83+
84+
for (auto op = std::next(ops.begin()); op != std::prev(ops.end(), 2); ++op)
85+
if (!is_public_key(op->data()))
86+
return false;
87+
88+
return true;
89+
}
90+
5791
// Constructors.
5892
// ----------------------------------------------------------------------------
5993

@@ -131,8 +165,8 @@ script::script(reader& source, bool prefix) NOEXCEPT
131165
assign_data(source, prefix);
132166
}
133167

134-
script::script(const std::string_view& mnemonic) NOEXCEPT
135-
: script(from_string(mnemonic))
168+
script::script(const std::string_view& mnemonic, bool bitcoind) NOEXCEPT
169+
: script(from_string(mnemonic, bitcoind))
136170
{
137171
}
138172

@@ -286,8 +320,11 @@ void script::assign_data(reader& source, bool prefix) NOEXCEPT
286320
}
287321

288322
// static/private
289-
script script::from_string(const std::string_view& mnemonic) NOEXCEPT
323+
script script::from_string(const std::string_view& mnemonic,
324+
bool /* bitcoind */) NOEXCEPT
290325
{
326+
// TODO: incorporate bitcoind option.
327+
291328
constexpr auto valid = true;
292329
auto easier = false;
293330
auto failer = false;
@@ -303,8 +340,6 @@ script script::from_string(const std::string_view& mnemonic) NOEXCEPT
303340

304341
operations ops{};
305342
ops.reserve(tokens.size());
306-
307-
// Create an op list from the split tokens.
308343
for (const auto& token: tokens)
309344
{
310345
ops.emplace_back(std::string_view{ token });
@@ -351,14 +386,16 @@ void script::to_data(writer& sink, bool prefix) const NOEXCEPT
351386
op->to_data(sink);
352387
}
353388

354-
std::string script::to_string(uint32_t active_flags) const NOEXCEPT
389+
std::string script::to_string(uint32_t active_flags,
390+
bool /* bitcoind */) const NOEXCEPT
355391
{
392+
// TODO: incorporate bitcoind option.
393+
356394
auto first = true;
357395
std::ostringstream text{};
358-
359-
// Throwing stream aborts.
360396
for (const auto& op: ops())
361397
{
398+
// Throwing stream aborts.
362399
text << (first ? "" : " ") << op.to_string(active_flags);
363400
first = false;
364401
}

src/chain/script_extract.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,18 @@ script_pattern script::output_pattern() const NOEXCEPT
7979
if (is_pay_public_key_pattern(ops()))
8080
return script_pattern::pay_public_key;
8181

82-
// Limited to 16 signatures though op_check_multisig allows 20.
8382
if (is_pay_multisig_pattern(ops()))
8483
return script_pattern::pay_multisig;
8584

85+
if (is_pay_witness_key_hash_pattern(ops()))
86+
return script_pattern::pay_witness_key_hash;
87+
88+
if (is_pay_witness_script_hash_pattern(ops()))
89+
return script_pattern::pay_witness_script_hash;
90+
91+
if (is_pay_witness_taproot_pattern(ops()))
92+
return script_pattern::pay_witness_v1_taproot;
93+
8694
return script_pattern::non_standard;
8795
}
8896

0 commit comments

Comments
 (0)