In this commit, we strip all of the local `replace ... => ../...`
directives that were introduced as part of #1825 (the v2 module
restructuring), now that proper tags exist for every freshly carved-out
submodule. Every in-tree go.mod is pinned to the newly published tags:
chainhash/v2.0.0, wire/v2.0.0, chaincfg/v2.0.0, address/v2.0.0,
txscript/v2.0.0, btcutil/v2.0.0, psbt/v2.0.0, and btcec is bumped to
v2.5.0 since it now depends on chainhash/v2 (previously
chaincfg/chainhash).
While here, we also unify the Go toolchain to 1.25 across every
submodule so the workspace resolves a consistent set of language
features.
Finally, we bump the main btcd version to v0.26.0-beta.rc1 in
preparation for the upcoming release candidate.
In this commit, we modify the execution logic, such that the P2A witness
program doesn't appear as a unknown witness program version. For
execution, we just make sure the script passes.
Extract the nested P2SH analysis from CalcScriptInfo into a dedicated
calcP2SHScriptInfo helper, and switch it to use finalOpcodeData for
redeem-script extraction (matching the consolidation in NewEngine and
GetWitnessSigOpCount). The helper also threads the inner witness-script
expected-input contribution for nested P2WSH spends, which the previous
inline code did not account for.
Both NewEngine and GetWitnessSigOpCount used sigScript[1:] (a raw byte
suffix of the scriptSig) to identify the candidate redeem script for
nested P2SH witness detection. GetPreciseSigOpCount and the existing
P2SH execution path already get the redeem script as the final pushed
element of the push-only scriptSig (via finalOpcodeData and
savedFirstStack[len-1] respectively). This commit consolidates the
nested-witness detection sites to use finalOpcodeData(0, scriptSig) so
all three places agree on what the redeem script is.
As a side effect of using the actual redeem script for detection, the
len(witness) != 0 precondition in NewEngine becomes redundant — the
redeem script's shape alone determines whether the spend is nested
witness, and verifyWitnessProgram already enforces the required
witness-stack shape downstream.
The refreshed Bitcoin Core script reference tests cover cases where an
empty CHECKSIG input must still fail pubkey encoding checks before the
interpreter falls back to a generic false result.
Add support for newly named Bitcoin Core script-test result tokens and
skip taproot placeholder rows that are already covered by
TestTaprootReferenceTests.
In this commit, we fix a policy-level divergence with Bitcoin Core when
handling OP_CODESEPARATOR inside unexecuted OP_IF branches in non-segwit
scripts with the ScriptVerifyConstScriptCode flag.
Bitcoin Core's EvalScript (interpreter.cpp:474-476) places the
SCRIPT_VERIFY_CONST_SCRIPTCODE check for OP_CODESEPARATOR before the
fExec branch-execution gate, causing it to fire unconditionally on every
OP_CODESEPARATOR encountered during script iteration -- even inside
OP_FALSE OP_IF ... OP_ENDIF envelopes.
Previously, btcd's equivalent check lived inside the opcodeCodeSeparator
handler, which was never reached for opcodes in unexecuted branches due
to the early return in executeOpcode that skips non-conditional opcodes
when isBranchExecuting() is false. This meant a script like:
OP_FALSE OP_IF OP_CODESEPARATOR OP_ENDIF <validation>
would be rejected by Bitcoin Core's mempool but accepted by btcd's.
The fix moves the check before the branch-execution gate in
executeOpcode, matching Bitcoin Core's structure. This follows the
existing pattern in btcd where isOpcodeDisabled and isOpcodeAlwaysIllegal
checks already fire regardless of branch execution state.
Note: SCRIPT_VERIFY_CONST_SCRIPTCODE is purely a policy flag (included
in STANDARD_SCRIPT_VERIFY_FLAGS but not MANDATORY_SCRIPT_VERIFY_FLAGS),
so this was not a consensus divergence. Both implementations would accept
such transactions if mined in a block.
Found via differential fuzzing by Bruno from bitcoinfuzz.
html/template automatically HTML-escapes special characters to prevent
XSS, which can silently corrupt arbitrary script data. Since the output
is binary script bytes and not HTML, this escaping provides no benefit
and might breaks correctness.
In this commit, we add a new function, `ScriptTemplate` to make the
process of making custom Bitcoin scripts a bit less verbose.
ScriptTemplate processes a script template with parameters and returns the
corresponding script bytes. This functions allows Bitcoin scripts to be
created using a DSL-like syntax, based on Go's templating system.
An example of a simple p2pkh template would be:
`OP_DUP OP_HASH160 0x14e8948c7afa71b6e6fad621256474b5959e0305 OP_EQUALVERIFY OP_CHECKSIG`
Strings that have the `0x` prefix are assumed to byte strings to be pushed
ontop of the stack. Integers can be passed as normal. If a value can't be
parsed as an integer, then it's assume that it's a byte slice without the 0x
prefix.
Normal go template operations can be used as well. The params argument
houses paramters to pass into the script, for example a local variable
storing a computed public key.
In this commit, we add more detail to the invalid tapscript merkle proof
error. Before this commit, the error was blank, making such a case hard
to debug. We'll now log the expected witness program, what we derived,
and also the passed in tapscript root.
We used to use a lot of small buffers for serialization, but now we'll
use one buffer large enough, and slice into it when needed.
``
name old time/op new time/op delta
CalcWitnessSigHash-8 31.5µs ± 0% 29.2µs ± 0% -7.05% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
CalcWitnessSigHash-8 19.9kB ± 0% 18.5kB ± 0% -7.14% (p=0.000 n=10+10)
name old allocs/op new allocs/op delta
CalcWitnessSigHash-8 801 ± 0% 445 ± 0% -44.44% (p=0.000 n=10+10)
```
In this commit, we optimize the sighash calc further by writing directly
into the buffer used for serialization by the sha256.New() instance
rather than to an intermediate buffer, which is then write to the hash
buffer.
In this commit, we update the top-level btcd package to use the latest
version of btcutil and also the chainhash package. With this version
bump, we can now use the new optimized dsha256 routine where applicable.
With this commit, I've covered most of the areas we'll hash an entire
transaction/block/header, but we may want to optimize some other areas
further, in particular, the witness sighash calc.
We add a new stepCallback as optional function closure on the Engine
that will be called every time a step has been performed during script
execution.
It is accessed by calling the NewDebugEngine constructor.
This is only meant to be used in debugging.
The doc formatting changes introduced in the recent go version is
increasing the diff for all of the new commits. Formatting it all in
this commit will help the readability of future PRs by reducing the
diff.
The default script builder allocates 500 bytes of memory for each
script. That is quite large for most applications and therefore wastes a
lot of memory when only small scripts are created.
To avoid breaking backward compatibility, we add a new functional option
to the NewScriptBuilder function that allows the user to specify the
initial allocation size.
In this commit, we add tests for the public functions used to generate
keyspend and tapscript signatures. Without the prior commit, these tests
will fail as the keyspend function won't properly add the sighash bytes
for things that aren't sighash default.
In this commit, we fix a bug in RawTxInTaprootSignature that would cause
the function to not properly apply the sighash flag for non-default
sighash signatures. The logic would end up applying `0x00` as a mask,
which will always be `0x00` on the other end.
The RawTxInTapscriptSignature function was correct, though it had the
ordering switched as it applies the sighash if the type doesn't equal
default.
In this commit, we fix an inadvertent mutation bug that would at times
cause the private key passed into the tweak function to actually be
*modified* in place.
We fix this by accepting the value instead of a pointer. The actual
private key struct itself contains no pointer fields, so this is
effectively a deep copy via dereference.
We also add a new unit test that fails w/o this change, to show that the
private key was indeed being modified.
In this commit, we add a total of 2760 taproot reference tests generated
by the bitcoind functional tests located at:
https://github.com/bitcoin/bitcoin/blob/master/test/functional/feature_taproot.py.
The tests aren't deterministic (fresh private keys are generated), so we
time we go to update the set of tests, we'll end up with fresh hashes
(the file name is the sha1 of the raw json test) and tests.