Update documentation to reflect actual BIP9 deployment parameters: - Activation threshold: 540/720 blocks (75%) vs incorrectly documented 90% - Confirmation window: 720 blocks (Palladium custom) vs Bitcoin's 2016 - Add explanatory notes about Palladium's lower threshold for faster coordination
18 KiB
Taproot Implementation in Palladium Core
Introduction
Taproot is a major protocol upgrade to Palladium Core that enhances privacy, improves efficiency, and enables more flexible smart contract development. Introduced in version 2.0.0, this implementation brings the benefits of Bitcoin's Taproot upgrade to the Palladium blockchain through a complete backport from Bitcoin Core v24.2.
Taproot combines three key innovations:
- Schnorr signatures (BIP340) for more efficient and private transactions
- Taproot outputs (BIP341) enabling key-path and script-path spending
- Tapscript (BIP342) with updated validation rules and new opcodes
- Bech32m addresses (BIP350) for encoding Taproot outputs
BIP Implementation Reference
BIP340: Schnorr Signatures for secp256k1
Status: Fully implemented in v2.0.0 Specification: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
BIP340 defines the Schnorr signature scheme for secp256k1, replacing ECDSA for witness version 1 outputs. Key features:
- 64-byte signatures: More compact than DER-encoded ECDSA (71-72 bytes)
- X-only public keys: 32-byte representation (only x-coordinate)
- Linearity: Enables key and signature aggregation (foundation for future multisig improvements)
- Provable security: Based on discrete logarithm assumption
- Batch verification: Multiple signatures can be verified more efficiently together
Implementation components:
XOnlyPubKeyclass: 32-byte x-only public key representationCKey::SignSchnorr(): BIP340-compliant signature generationXOnlyPubKey::VerifySchnorr(): Signature verificationCPubKey::GetXOnlyPubKey(): Extraction of x-only key from compressed pubkey- Integration with libsecp256k1's
extrakeysandschnorrsigmodules
BIP341: Taproot: SegWit version 1 spending rules
Status: Fully implemented in v2.0.0 Specification: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
BIP341 introduces Taproot outputs (witness version 1), enabling two spending paths:
- Key-path spending: Direct signature verification (default, most efficient)
- Script-path spending: Reveal and execute a script from a Merkle tree
Key innovations:
- Unified spending: Key-path and script-path spends are indistinguishable until spent
- MAST (Merkelized Alternative Script Trees): Only reveal executed script branch
- TapTweak: Output key is internal key tweaked with script tree commitment
- Parity bit: Even/odd y-coordinate handled implicitly
Implementation components:
SigVersion::TAPROOT: New signature version for key-path spendingCKey::SignSchnorrTaproot(): Key-path signature with BIP341 tweakingXOnlyPubKey::ComputeTapTweak(): Taproot tweak calculationXOnlyPubKey::CreatePayToTaprootPubKey(): Output key derivation with parity handling- Control block validation: 33-4129 bytes, 32-byte node increments
- Taproot signature hash with SHA256 tagged hashing
BIP342: Validation of Taproot Scripts
Status: Fully implemented in v2.0.0 Specification: https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki
BIP342 defines validation rules for scripts revealed via script-path spending:
New features:
- OP_CHECKSIGADD: Replaces OP_CHECKMULTISIG with better semantics
- Validation weight limits: Prevent DoS attacks in script validation
- OP_SUCCESS opcodes: Reserved for future soft-fork upgrades (80, 98, 126-129, 131-134, 137-138, 141-142, 149-153)
- Signature opcodes: Use Schnorr instead of ECDSA
- Taproot leaf version: 0xc0 (extensible to 0xc1-0xfe)
Implementation components:
SigVersion::TAPSCRIPT: New signature version for script-pathScriptExecutionData: Tracks tapleaf hash, codeseparator position, annex, validation weight- Validation weight constraints:
VALIDATION_WEIGHT_PER_SIGOP_PASSED= 50 unitsVALIDATION_WEIGHT_OFFSET= 50 units
- Annex support with tag 0x50
- Minimal-if requirement for tapscript conditionals
- CHECKMULTISIG disabled in tapscript (use OP_CHECKSIGADD instead)
BIP350: Bech32m format for v1+ witness addresses
Status: Fully implemented in v2.0.0 Specification: https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
BIP350 introduces Bech32m, a modified version of Bech32 for witness version 1+ addresses:
Key differences from Bech32:
- Different constant: Polymod check uses 0x2bc830a3 (vs 1 for Bech32)
- Witness version: v0 uses Bech32, v1+ uses Bech32m
- Length mutation protection: Better resistance to certain error patterns
Address format:
- Taproot addresses:
plm1p...(witness v1) - SegWit v0 addresses:
plm1q...(witness v0, uses Bech32)
Implementation components:
bech32::Encodingenum:BECH32andBECH32M- Dual-constant checksum creation/verification
- Automatic encoding selection based on witness version
- Palladium-specific HRP (Human Readable Part): "plm"
Source Attribution
This implementation is a backport of Taproot functionality from Bitcoin Core v24.2 with appropriate adaptations for Palladium's chain parameters, consensus rules, and testing infrastructure.
Bitcoin Core components:
- secp256k1 library upgraded to Bitcoin Core v24.2 tag
- Schnorr signature implementation from Bitcoin Core
- Taproot consensus rules from Bitcoin Core
- Bech32m encoding logic from Bitcoin Core
Palladium-specific adaptations:
- Chain parameters (deployment timing, genesis blocks, maturity rules)
- Address prefixes (HRP: "plm" for mainnet)
- Test vector alignment to Palladium's consensus
- Wallet infrastructure integration
- Deployment schedule for Palladium mainnet/testnet
Architecture Overview
Crypto Layer
Files: src/key.cpp, src/key.h, src/pubkey.cpp, src/pubkey.h
Classes and methods:
-
XOnlyPubKey: 32-byte x-only public keyVerifySchnorr(msg, sig): BIP340 signature verificationComputeTapTweak(merkle_root): Calculate BIP341 taproot tweakCreatePayToTaprootPubKey(merkle_root, &output_key): Derive output keyCheckTapTweak(internal_key, merkle_root, parity): Verify taproot tweak
-
CKey: Private key operationsSignSchnorr(msg, &sig): Basic Schnorr signature (BIP340)SignSchnorrTaproot(msg, merkle_root, &sig): Taproot key-path signature (BIP341)
-
CPubKey: Compressed public key (33 bytes)GetXOnlyPubKey(): Extract x-only representation
Statistics:
key.cpp: +26 lineskey.h: +11 linespubkey.cpp: +49 linespubkey.h: +66 linestest/key_tests.cpp: +60 lines (Schnorr test coverage)
Encoding Layer
Files: src/bech32.cpp, src/bech32.h, src/key_io.cpp
Key changes:
bech32::Encodingenum for dual-mode operationEncode(encoding, hrp, values): Supports both BECH32 and BECH32MDecode(str): Auto-detects encoding based on witness version- Address encoding/decoding logic updated for automatic selection
Statistics:
bech32.cpp: +67/-48 linesbech32.h: +15/-1 lineskey_io.cpp: +22/-9 lines
Script Layer
Files: src/script/interpreter.cpp, src/script/interpreter.h, src/script/script.h, src/script/script_error.cpp, src/script/script_error.h, src/script/standard.cpp, src/script/standard.h
Major additions:
SigVersion::TAPROOT: Key-path spendingSigVersion::TAPSCRIPT: Script-path spendingEvalScript(): Taproot/tapscript execution pathsBaseSignatureChecker::CheckSchnorrSignature(): Schnorr verification hook- Taproot signature hash computation with precomputed data
- Control block parsing and validation
- OP_CHECKSIGADD implementation
- Validation weight tracking
New error codes:
SCRIPT_ERR_SCHNORR_SIG_SIZESCRIPT_ERR_SCHNORR_SIG_HASHTYPESCRIPT_ERR_SCHNORR_SIGSCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZESCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHTSCRIPT_ERR_TAPSCRIPT_CHECKMULTISIGSCRIPT_ERR_TAPSCRIPT_MINIMALIF
Statistics:
interpreter.cpp: +469/-36 linesinterpreter.h: +69/-1 linesscript.h: +22/-1 linesscript_error.cpp: +20 linesscript_error.h: +12 linesstandard.cpp: +12 linesstandard.h: +1 line
Consensus Layer
Files: src/chainparams.cpp, src/consensus/params.h, src/versionbitsinfo.cpp
Deployment parameters:
Mainnet:
- Deployment: BIP9 soft fork
- Bit: 2
- Start time: 1771027200 (February 14, 2026 00:00:00 UTC)
- Timeout: 1803859200 (March 1, 2027 00:00:00 UTC)
- Min activation height: Not specified (activation determined by signaling threshold)
Testnet:
- Same parameters as mainnet
Regtest:
- Status: ALWAYS_ACTIVE (immediate activation for testing)
Statistics:
chainparams.cpp: +9 linesconsensus/params.h: +3/-1 linesversionbitsinfo.cpp: +4 lines
Wallet Layer
Files: src/outputtype.cpp, src/outputtype.h, src/script/descriptor.cpp, src/script/sign.cpp, src/script/signingprovider.cpp, src/wallet/scriptpubkeyman.cpp, src/wallet/rpcwallet.cpp
Key additions:
OutputType::BECH32M: New output type for TaprootGetDestinationForTaprootKey(): Create witness v1 destinationCreateSig(): Updated for Schnorr signature generation- Key-path: Uses
SignSchnorrTaproot()with empty scriptCode - Script-path: Uses
SignSchnorr()for tapscript validation
- Key-path: Uses
- Taproot internal key storage:
m_taproot_internal_keysmap GetTaprootInternalKey(): Lookup internal key from output key
RPC changes:
getnewaddresssupports "bech32m" address typecreatemultisigextended for bech32m (with taproot limitations)getaddressinforeports address type correctly for taproot
Statistics:
outputtype.cpp: +35/-1 linesoutputtype.h: +3/-1 linesdescriptor.cpp: +72/-2 linessign.cpp: +90/-27 linessigningprovider.cpp: +17 linesscriptpubkeyman.cpp: +36 linesrpcwallet.cpp: +12/-3 lines
Policy Layer
Files: src/policy/policy.cpp, src/policy/policy.h, src/validation.cpp
Script verification flags:
SCRIPT_VERIFY_TAPROOT(bit 17): Enable taproot/tapscript consensusSCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION(bit 18): Reject unknown taproot versionsSCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS(bit 19): Reject OP_SUCCESS opcodesSCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE(bit 20): Reject unknown pubkey types
Standardness limits:
MAX_STANDARD_TAPSCRIPT_STACK_ITEMS= 100MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE= 80 bytesMAX_STANDARD_TAPSCRIPT_SCRIPT_SIZE= 3600 bytesTAPROOT_CONTROL_BASE_SIZE= 33 bytes (control block minimum)TAPROOT_CONTROL_NODE_SIZE= 32 bytes (each merkle node)TAPROOT_CONTROL_MAX_SIZE= 4129 bytes (max control block)
Statistics:
policy/policy.cpp: +46/-1 linespolicy/policy.h: +12/-1 linesvalidation.cpp: +10 lines
Consensus Rules
Deployment Activation
Taproot activates via BIP9 signaling:
- Miners signal readiness by setting bit 2 in block version
- When 540 of 720 blocks in a confirmation window signal (75% threshold), deployment is LOCKED_IN
- After LOCKED_IN period ends, deployment becomes ACTIVE for all subsequent blocks
- Timeout on March 1, 2027 if insufficient signaling
Note: Palladium uses a 75% activation threshold (540/720 blocks) compared to Bitcoin's 90% (1815/2016 blocks), with a shorter confirmation window to facilitate faster network coordination while maintaining security.
Script Verification Flags
Standard transaction validation (mempool/relay):
SCRIPT_VERIFY_TAPROOT |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE
Consensus validation (blocks):
SCRIPT_VERIFY_TAPROOT
Taproot Spending Rules
Key-path spending (witness v1 with single witness element):
- Witness must contain exactly 1 element (signature, or 65 bytes after annex removal)
- Signature must be 64 or 65 bytes (65 = 64-byte sig + SIGHASH byte)
- Extract internal key from output key using BIP341 taproot tweak
- Verify Schnorr signature against tweaked pubkey
- Empty scriptCode per BIP341
Script-path spending (witness v1 with multiple elements):
- Last witness element is control block (33-4129 bytes, 33 + 32n)
- Second-to-last element is tapscript (max 3600 bytes for standard)
- Control block format:
[leaf_version || internal_key_parity || internal_key[1:] || merkle_proof] - Verify control block reconstructs output key
- Execute tapscript with TAPSCRIPT signature version
- Track validation weight (max 1650 units)
Annex handling:
- If first witness stack element starts with 0x50, it's the annex
- Annex is removed before script validation
- Annex hash is committed in signature hash
Validation Weight
Tapscript execution tracks validation weight to prevent DoS:
- Each signature operation: 50 weight units (if signature validates)
- Initial offset: 50 weight units
- Maximum: 1650 units per script-path spend
- Formula:
weight = 50 + 50 * num_successful_checksigs - Limit:
num_successful_checksigs <= 32(1650 / 50 - 1)
Standardness Policy
Tapscript stack limits:
- Max stack items: 100
- Max stack item size: 80 bytes
- Max script size: 3600 bytes
Control block validation:
- Minimum size: 33 bytes (version + internal key)
- Maximum size: 4129 bytes (33 + 32 * 128 merkle nodes)
- Size must be 33 + 32n for integer n
Leaf version:
- Standard: 0xc0 (tapscript)
- Non-standard: 0xc1-0xfe (reserved for future soft forks)
- Invalid: 0x00-0xbf, 0xff
Key Files Modified
Summary by Layer
| Layer | Files Modified | Lines Added | Lines Removed |
|---|---|---|---|
| Crypto | 5 | 212 | 6 |
| Encoding | 3 | 104 | 58 |
| Script | 7 | 604 | 38 |
| Consensus | 3 | 16 | 1 |
| Wallet | 9 | 265 | 37 |
| Policy | 3 | 68 | 2 |
| Testing | Multiple | Extensive | - |
| Total | 30+ | ~1269 | ~142 |
Critical Implementation Files
Cryptographic primitives:
src/key.cpp,src/key.h: Schnorr signing with BIP340/BIP341src/pubkey.cpp,src/pubkey.h: XOnlyPubKey, signature verification, taproot tweakingsrc/secp256k1/: Upgraded to Bitcoin Core v24.2 with extrakeys and schnorrsig modules
Consensus rules:
src/script/interpreter.cpp: Taproot/tapscript validation logic (469 lines added)src/script/script_error.h: New error codes for taprootsrc/chainparams.cpp: BIP9 deployment parameters
Address encoding:
src/bech32.cpp: Bech32m implementationsrc/key_io.cpp: Address encoding/decoding with automatic format selection
Wallet integration:
src/outputtype.cpp: BECH32M output typesrc/script/sign.cpp: Schnorr signature generation for transactionssrc/wallet/scriptpubkeyman.cpp: Internal key tracking for taproot
Testing:
test/functional/feature_taproot.py: End-to-end taproot functional testsrc/test/key_tests.cpp: Schnorr signature unit testssrc/test/data/: Bech32m test vectors aligned to Palladium
Testing
Functional Tests
Feature test: test/functional/feature_taproot.py
Test coverage:
- Wallet creation with default bech32m support
- Taproot address generation:
getnewaddress("", "bech32m") - Funding test: Send to P2TR address
- Balance verification
- Spending test: Send from P2TR address
- Transaction confirmation
- End-to-end P2TR lifecycle validation
Test framework:
- Python segwit_addr module updated for Bech32m
- Address validation for Palladium HRP ("plm")
- Consensus rule alignment to Palladium parameters
Unit Tests
Schnorr signatures: src/test/key_tests.cpp
Test coverage:
- XOnlyPubKey construction and validation
- Schnorr signature round-trip (sign + verify)
- Signature size validation (64 bytes)
- XOnlyPubKey invariants (negation property)
- ECDSA interoperability (same key, both signatures valid)
- BIP340 test vectors
Bech32m encoding: src/test/bech32_tests.cpp
Test coverage:
- Bech32m encoding/decoding
- Checksum validation
- Witness version detection
- Error detection (insertions, deletions, substitutions)
- Palladium-specific test vectors
Integration Tests
RPC interface:
getnewaddresswith "bech32m" typegetaddressinfofor taproot addressessendtoaddressto/from taproot outputscreatemultisigtaproot limitationsgetblockchaininfotaproot deployment status
Wallet operations:
- Create taproot receive address
- Send to taproot address
- Spend from taproot address (key-path)
- Fee estimation for taproot transactions
- UTXO selection with taproot inputs
References
BIP Specifications
-
BIP340: Schnorr Signatures for secp256k1 https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
-
BIP341: Taproot: SegWit version 1 spending rules https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
-
BIP342: Validation of Taproot Scripts https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki
-
BIP350: Bech32m format for v1+ witness addresses https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
Additional Resources
-
Bitcoin Core v24.2 release: Source for Taproot backport https://github.com/bitcoin/bitcoin/releases/tag/v24.2
-
secp256k1 library: Cryptographic primitives https://github.com/bitcoin-core/secp256k1
-
Taproot Workshop: Technical deep-dive (Bitcoin-focused but applicable) https://github.com/bitcoinops/taproot-workshop
Palladium-Specific Documentation
- User guide:
doc/taproot-guide.md - Release notes:
doc/release-notes.md(v2.0.0) - BIP implementation list:
doc/bips.md
Version: Palladium Core 2.0.0 Last Updated: February 2026 Status: Production-ready, mainnet deployment March 2026