TSP-0008 CISA
Transaction Size Reduction via Aggregate Schnorr Signatures for Tondi
Proposal Number: TSP-0008
Proposal Name: Transaction Size Reduction via Aggregate Schnorr Signatures for Tondi
Category: Consensus (C) โ pre-Oct 2025 numbering retained as 000x
Status: Draft
Author: Tondi Foundation Development Team
Created: 2025-09-05
Target: Tondi Frontier (v2026a)
Scope: On-chain payload schema, validation semantics, tapscript opcodes, transaction-level witness envelope, DAG/mempool policy, interoperability with TSP-0007 (ANYPREVOUT), governance & activation
This document uses RFC 2119 keywords (MUST/SHOULD/MAYโฆ).
1. Abstract
CISA introduces cross-input signature aggregation: In a transaction, multiple inputs (from the same or different parties) that meet the conditions no longer carry individual Schnorr signatures but are covered by a single aggregated Schnorr signature for an "aggregation cohort."
This can significantly reduce transaction sizes and validation costs in Tondi's high-throughput DAG environment, improving block capacity and economics for micro-payments, consolidation spends, and coinjoin scenarios.
2. Background and Motivation
-
Size/Fee Benefits: Remove N 64B signatures and redundant overhead, replacing them with a single 64B aggregated signature + minimal metadata. Savings are substantial for larger N (>4).
-
Validation Efficiency: A single Schnorr verification after aggregation, more CPU/bandwidth-friendly; beneficial for high-TPS Tondi Flash on-chain settlement phases.
-
Privacy Gains: Homogenizes inputs in coinjoins/batch withdrawals, reducing the effectiveness of heuristic analysis.
Design Goals:
- Soft fork (tapscript-only + OP_SUCCESSx tightening);
- Schnorr-only, aligned with existing Taproot/tapscript;
- Simple and Correct: v1 only supports SIGHASH_ALL aggregation, temporarily excluding complex combinations like APO/ANYSCRIPT/ANYONECANPAY.
3. Terminology and Overall Design
-
CISA Aggregation Cohort: An ordered set of inputs in the same transaction participating in aggregation.
-
TSP-0008 Public Key: Identified in scripts as 0x02 || pubkey32 (33 bytes, x-only), eligible for CISA.
-
CISA Witness Envelope: Transaction-level witness extension carrying the cohort's bitmap/index, aggregated nonce R, aggregated signature s, etc.
-
MuSig2: Multi-signature aggregation scheme resistant to rogue-key attacks; uses deterministic coefficients for public key aggregation.
3.1 Domain Separation Labels (Standardized)
MUST: Fixed byte strings for all domain separation labels (case-sensitive, exact spelling):
"TSP-0008/CISASighash"(20 bytes):0x54 0x53 0x50 0x2d 0x30 0x30 0x30 0x38 0x2f 0x43 0x49 0x53 0x41 0x53 0x69 0x67 0x68 0x61 0x73 0x68"TSP-0008/Cohort"(13 bytes):0x54 0x53 0x50 0x2d 0x30 0x30 0x30 0x38 0x2f 0x43 0x6f 0x68 0x6f 0x72 0x74"MuSig/KeyAggList"(16 bytes):0x4d 0x75 0x53 0x69 0x67 0x2f 0x4b 0x65 0x79 0x41 0x67 0x67 0x4c 0x69 0x73 0x74"MuSig/KeyCoeff"(14 bytes):0x4d 0x75 0x53 0x69 0x67 0x2f 0x4b 0x65 0x79 0x43 0x6f 0x65 0x66 0x66"BIP0340/challenge"(16 bytes):0x42 0x49 0x50 0x30 0x33 0x34 0x30 0x2f 0x63 0x68 0x61 0x6c 0x6c 0x65 0x6e 0x67 0x65
v1 Scope (Strict Constraints for Simplicity and Safety):
- Only tapscript script-path (no key-path impact; future extension);
- Only SIGHASH_ALL;
- Cannot mix with TSP-0007 APO in the same cohort;
- At most 1 cohort per transaction (extensible, see appendix).
4. Specification
4.1 New Public Key and New Opcodes
-
TSP-0008 Public Key: x-only 32-byte Schnorr public key, identified in scripts as 0x02 || pubkey32.
-
New OPs (Implemented via OP_SUCCESSx encoding for soft fork):
-
OP_CHECKSIG_CISA (0xB5)
-
OP_CHECKSIGVERIFY_CISA (0xB6)
-
(Optional) OP_CHECKSIGADD_CISA (0xB7) Specific OP_SUCCESSx values are fixed to avoid implementation divergence.
-
Non-upgraded nodes: Encountering OP_SUCCESSx results in unconditional success;
-
Upgraded nodes: Execute cohort validation path (below), tightening "unconditional success" to "must pass CISA verification."
-
Script Convention: Inputs participating in CISA do not provide signature objects (witness is empty placeholder), but delegate validation to transaction-level CISA via the above OPs.
4.2 CISA Witness Envelope
MUST: CISA envelope is located at transaction-level witness-extension with unique position and fixed encoding to eliminate malleability and parsing divergence. The envelope is encoded as:
tx_wit_ext := varint(1) || cisa_envelope
cisa_envelope :=
magic[4] = "CISA" // 0x43 0x49 0x53 0x41
version[u8] = 0x01
flags[u16] // bit0=1 (SIGHASH_ALL), others=0
hash_type[u8] = 0x01 // MUST be SIGHASH_ALL
cohort_bitmap[ceil(num_inputs/8)] // 1 = input participates
agg_nonce_R[32] // x-only, even-y required
agg_sig_s[32] // Schnorr scalar s
MUST:
popcount(cohort_bitmap) >= 1(non-empty cohort)len(bitmap) == ceil(num_inputs/8)(exact length)- Last byte bits beyond
num_inputsMUST be 0; otherwise consensus FAILS - Bitmap bit order: LSB-first (bit
jof bytetcorresponds to input index8*t + j) - Transaction contains at most one
cisa_envelope; otherwise consensus FAILS - MUST NOT place envelope in any input's witness stack
MUST: All inputs in the cohort's script must include at least one OP_CHECKSIGVERIFY_CISA (or OP_CHECKSIG_CISA with overall true logic). Inputs outside the cohort MUST NOT use the above CISA OPs.
4.3 Script Requirements for Inputs in Cohort
- Public key stack element MUST be 0x02 || pubkey32.
- OP_CHECKSIGVERIFY_CISA (or _CISA) does not consume a signature; its semantics are "add this input to the transaction's CISA validation set and await global validation."
- Allows other constraints in the same script (e.g., CSV/CLTV, HASHLOCK), but must ensure the input path evaluates to true when CISA validation passes.
4.4 Aggregated Message and Bindings (Sighash & Bindings)
v1 only supports SIGHASH_ALL. To ensure each input's amount/script/leaf/sequence is bound in the message, define transaction-level signature message:
-
Use TaggedHash for domain separation:
$H_{tag}(label, m) = SHA256(SHA256(label) | SHA256(label) | m)$
where label = "TSP-0008/CISASighash". -
Define two data segments:
-
TxBase (Identical to tapscript SIGHASH_ALL base commitment):
u8 : hash_type (=0x01) le32 : nVersion le32 : nLockTime H(Prevouts) // Hash of all input outpoints concatenated H(Sequences) // Hash of all input nSequences concatenated H(Outputs) // Hash of all outputs CTxOut concatenated (incl. CompactSize)MUST: Encoding of Prevouts/Sequences/Outputs completely matches BIP-341 SIGHASH_ALL field-by-field serialization order and CompactSize encoding.
-
CohortBindings (Per-input bindings for cohort members):
For each input i in cohort (in ascending input index order), concatenate:le64 : amount_i varbytes : scriptPubKey_i (CompactSizeLen + RawScript) u8 : annex_flag_i (0=no annex, 1=has annex) if annex_flag_i == 1: H(CompactSizeLen(annex_i) || annex_i) // MUST match tapscript annex binding le32 : nSequence_i le32 : codesep_pos_i (0xFFFFFFFF if no CODESEPARATOR) H32 : tapleaf_hash_i // MUST include leaf_version: H_tag("TapLeaf", leaf_version || CompactSize(script_len) || script)
-
-
Full Message:
$m = H_{tag}("TSP-0008/CISASighash", TxBase | H(CohortBindings) | CohortBitmapDigest)$
where CohortBitmapDigest = $H_{tag}("TSP-0008/Cohort", cohort_bitmap)$ to prevent replay to different cohort selections.
MUST:
tapleaf_hash_icalculation MUST includeleaf_versionin TapLeaf constructioncodesep_pos_isemantics MUST align with BIP-342 (0xFFFFFFFF when no CODESEPARATOR)- Annex binding MUST use identical method as tapscript single-signature annex binding
- TxBase serialization MUST be byte-for-byte identical to BIP-341 SIGHASH_ALL
Explanation: Binding each input independently (amount/script/leaf/sequence) ensures single-sign โ aggregate semantic equivalence, preventing aggregated signatures from being transplanted to different scripts/amounts.
4.5 Aggregated Public Key and Signature Validation (MuSig2)
- Public Key Aggregation: Let TSP-0008 public keys in the cohort (ordered by input index) be $X_1 \dots X_k$ (x-only).
Compute $L = H_{tag}("MuSig/KeyAggList", X_1|\dots|X_k)$, coefficients $a_i = H_{tag}("MuSig/KeyCoeff", L | X_i)$,
aggregated public key $X = \sum_{i} a_i \cdot X_i$.
MUST:
-
Verify $X \neq \infty$ and each $X_i$ is a valid curve point (BIP-340 x-only with even-y reconstruction)
-
MuSig2 coefficient $a_i = int(Hash(...) \bmod n)$; if $a_i == 0$ (probability ~1/n), consensus FAILS
-
MUST prohibit duplicate input indices; for duplicate public keys, define consistent behavior (see security section)
-
Signature Validation: CISA envelope provides R (x-only 32B) and s (32B).
MUST: R must be even-y x-only; if odd-y detected, consensus FAILS (no automatic flipping) Let $e = H_{tag}("BIP0340/challenge", R | X | m)$ (BIP-340 style), verify:
$s \cdot G = R + e \cdot X$ -
Pass Condition: Equation holds, and envelope's bitmap exactly covers all inputs that used _CISA/_CISAVERIFY in actual script execution (prevents dead branch abuse).
Note: Nonce protocol (commit-reveal, anti-replay, optional adaptors) is off-chain; on-chain only validates aggregated signature.
4.6 Interoperability and Constraints (Relation to TSP-0007)
- v1 prohibits using ANYPREVOUT/ANYSCRIPT/ANYONECANPAY etc. non-ALL flags in the same cohort.
- If transaction includes APO inputs, they MUST NOT participate in CISA; they should use TSP-0007 independent signature paths.
- MUST: Define determinable condition for "input uses APO" (e.g., witness contains APO-sig or tapscript contains APO-OP) for consensus-level "prohibit CISA + APO in same cohort" check
- Future TSP-0008-EXT can define "CISA + APO" joint message spec (requiring extra per-input APO binding vectors and consistent hash_type).
4.7 Failure Conditions (Consensus MUST Fail)
- Transaction has multiple CISA envelopes;
- hash_type != 0x01;
- Bitmap empty (
popcount(cohort_bitmap) == 0) or inconsistent with script usage; - Bitmap length !=
ceil(num_inputs/8)or last byte bits beyondnum_inputsare non-zero; - Any input in cohort lacks 0x02||pubkey32 or invalid OP usage;
- R is not even-y x-only (odd-y detected);
- X == โ or any X_i is invalid curve point;
- Any MuSig2 coefficient a_i == 0;
tapleaf_hash_icalculation missing leaf_version;- Annex binding inconsistent with tapscript single-signature method;
- Any
_CISAOP executed in script but bitmap does not cover that input; - SINGLE out-of-bounds (not applicable but reserved check) or any non-v1 allowed flags;
- Schnorr validation fails.
5. DAG / Mempool Policy (Non-Consensus, Default Policy)
- Size/Fee Pricing: Based on actual bytes; CISA reduces witness volume โ better fees.
- Validation Cost: Aggregated tx validation time averages lower than N single signs; but requires script parsing for pubkeys โ SHOULD set single-tx cohort size limit (default k_max = 1024, configurable).
- DoS and Complexity Bounds: SHOULD implement formulaic budget:
- Parsing cost: O(total_script_bytes + k)
- Elliptic curve verification cost: โ 1 Schnorr verify + 2k scalar multiplications (MuSig2 pre-aggregation)
- Nodes SHOULD reject transactions exceeding budget based on total bytes and k; P2P layer SHOULD apply separate rate limiting for "CISA-containing tx"
- Replacement Policy RBF/RBS: Consistent with TSP-0007; CISA does not alter nSequence semantics.
- Relay Policy: MUST NOT relay for non-tapscript or mixed APO cohorts.
- P2P Rate Limiting: Enable per-peer transmission limits for large-cohort CISA txs to resist DoS.
5.6 Half-Aggregation (Non-Consensus Policy)
SHOULD: Nodes implement Half-Aggregation caching strategy in mempool:
- Cache
scomponents andRvectors from half-aggregated signatures - When blocks arrive with block-level half-aggregated signatures
(s', R'_1..R'_m), performs' โ s' โ sand remove seenRvalues to skip duplicate verification - During reorgs, SHOULD retain cached values until transactions reach sufficient confirmation depth to avoid replay failures
Benefits: Non-interactive compression of n signatures to half-size aggregated signatures, compatible with block-level half-aggregation for improved verification efficiency.
Note: Half-aggregation is incompatible with adaptor signatures; adaptor-based protocols must use non-aggregated tapscript paths.
6. Security Analysis
6.1 Rogue-Key and Aggregation Security
- Uses MuSig2 coefficients $a_i$ binding the entire pubkey set, resisting rogue-key attacks;
- MUST use BIP-340 standard domain-separated challenge;
- Off-chain MUST execute nonce commit/reveal process, MUST NOT reuse nonces.
- SHOULD: In wallet SDK flow, enforce public key proofs (each signer proves possession of discrete logarithm for X_i) to resist external injection of malicious points (non-consensus requirement, but ecosystem security).
6.2 Replay and Misuse
- CohortBindings bind each input (amount/script/leaf/sequence), plus CohortBitmapDigest, prevent cross-script/cross-cohort replays;
- CISA v1 bans APO, effectively avoiding complex stacking risks with APO's weak bindings.
6.3 Malleability and Composite Protocols
- Aggregated remains BIP-340-style; message binds Prevouts/Sequences/Outputs, no new txid malleability risks;
- Complements coinjoin/batch withdrawals for privacy; but envelope bitmap size may expose participation scale (resolvable via padding/multi-cohort extension, see appendix).
6.4 Domain Separation and Label Standardization
SHOULD: Standardize domain separation labels in "Terminology" section with fixed byte strings (case/slash) and provide byte-by-byte examples in test vectors to avoid implementation spelling differences:
H_tag("TSP-0008/CISASighash", ...)H_tag("TSP-0008/Cohort", bitmap)H_tag("MuSig/KeyAggList", ...)andH_tag("MuSig/KeyCoeff", ...)H_tag("BIP0340/challenge", R || X || m)
SHOULD: In TxBase, explicitly specify byte order (LE) and integer width for nVersion/nLockTime/sequences, providing cross-language consistent serialization description.
6.5 Duplicate Public Key Strategy (Key Reuse)
SHOULD: v1 prohibits duplicate x-only public keys within cohort; otherwise MUST define consistent "duplicate public key equivalent merging" behavior and add (index || Xi) to message m binding to prevent reordering/substitution.
6.6 Bitmap Privacy Padding
SHOULD: In v1, add optional "fixed-length bitmap" (rounded up to mempool limit k_max) policy-level switch, allowing at least policy-level zero padding to hide cohort size (consensus still allows compact encoding).
6.7 Adaptor Signatures Compatibility
Protocol Combination Matrix:
- v1 CISA (Full Aggregation): Compatible with adaptor signatures when placed in non-aggregated tapscript paths
- Half-Aggregation: Incompatible with adaptor signatures; adaptor-based protocols must use non-aggregated tapscript paths
- Future g'root/Entroot: Key-path aggregation + script-path adaptor signatures remain compatible
MUST: Adaptor signatures MUST be placed in non-aggregated tapscript paths to preserve protocol functionality:
- Cross-chain atomic swaps
- Privacy protocols requiring adaptor signatures
- Lightning Network and other adaptor-based constructions
Future Compatibility: When implementing g'root/Entroot, this pattern remains valid (key-path aggregation, script-path adaptor signatures).
7. Economic and Privacy Impacts
-
Size Savings Rough Calculation:
- Without CISA: Per input ~64B (signature) + witness stack overhead;
- CISA: Remove Nรsignatures, add 64B (s) + 32B (R) + bitmap (~N/8 B) + fixed header.
- For N=8, savings โ 8ร64 โ (64+32+1) โ ~415 B (excluding stack/index differences); higher for larger N.
-
Blockstream Research Savings (Informative):
Based on Taproot v1 average transaction analysis:- Half-Aggregation: ~20.6% bytes / 7.6% weight units
- Full Aggregation: ~26.1% bytes / 9.6% weight units
- Combined: ~33.6% bytes / 12.4% weight units
- Large Coinjoin: ~41.2% bytes / 15.2% weight units
-
Privacy: More homogeneous inputs, beneficial against heuristics; combinable with coinjoin protocols.
7.1 PSBT/Offline Flow
SHOULD: Define minimal PSBT extension fields for Half-Aggregation and Full Aggregation:
Core Fields:
psbt_cisa_partial_R[i]: Half-aggregation R components for each inputpsbt_cisa_partial_s: Half-aggregation s contributionpsbt_cisa_bitmap_proposal: Proposed cohort bitmappsbt_cisa_non_aggregatable: Flag for non-aggregatable paths (adaptor signatures)
Extended Fields:
- Nonce commitments, partial nonces, aggregated nonce
- Partial signatures, cohort bitmap proposals
- Wallet interoperability minimum set specification
SHOULD: In coinjoin/batch withdrawal scenarios, provide failure attribution (blame) and retry recommended flow to reduce final rejection UX risk.
7.2 Error and Rejection Reasons
SHOULD: Standardize error codes for cross-implementation debugging:
ERR_CISA_R_ODD: R is odd-y (should be even-y)ERR_CISA_BITMAP_LEN: Bitmap length mismatchERR_CISA_DUP_PUBKEY: Duplicate public keys in cohortERR_CISA_APO_MIXED: CISA and APO inputs in same transactionERR_CISA_COEFF_ZERO: MuSig2 coefficient a_i == 0ERR_CISA_X_INFINITY: Aggregated public key X == โERR_CISA_SCRIPT_MISMATCH: Script execution used _CISA but bitmap doesn't cover input
8. Compatibility and Deployment
8.1 Soft Fork Path
โ ๏ธ Risk Note: MUST NOT introduce cross-input aggregation validation through redefining OP_SUCCESSx opcodes, as this creates chain split risk. Unupgraded nodes treat OP_SUCCESS as unconditionally true, while upgraded nodes execute real aggregation validation, leading to inconsistent script execution paths.
Recommended Deployment Paths:
- New SegWit Version: Introduce new witness version for key-path aggregation, avoiding
OP_SUCCESSinteraction - Generalized Taproot (g'root/Entroot): Move aggregatable public keys out of script system entirely, eliminating
OP_SUCCESSconflicts
Current v1 Approach: Tapscript-only with script-path aggregation only; key-path aggregation deferred to future witness version or g'root implementation.
8.2 Activation Process (Recommended)
- Testnet โฅ 3 months;
- Version bit (non-conflicting with TSP-0007's bit, specifics in deployment params table);
- SHOULD: Map "observation period/Speedy-Trial (LOT=true)" to DAG system (e.g., based on "blue set" or time window voting sampling) and provide timeline diagram
- SHOULD: Define Tondi DAG-specific window definition and statistical method (different from Bitcoin's 2016 blocks/80% threshold)
- Mainnet threshold 80% / 2016 blocks equivalent, observation period + Speedy-Trial (LOT=true) fallback plan.
8.3 Interoperability Regression Testing
SHOULD: During testnet 3-month period, include adversarial use cases:
- Large cohort + low bandwidth nodes;
- Malicious adversaries frequently submitting "bitmap inconsistency/high bit dirty data";
- Coinjoin scenario rejection/timeout recovery and RBF;
- Boundary interactions with APO appearing in same block but different transactions.
9. Reference Implementation (Pseudocode)
9.1 Transaction-Level Validation Process
use tondi_consensus_core::tx::{Transaction, TransactionInput, UtxoEntry, VerifiableTransaction};
use tondi_consensus_core::hashing::sighash::{SigHashReusedValues, SigHashReusedValuesUnsync};
use tondi_crypto_txscript::{TxScriptEngine, TxScriptError, caches::Cache, SigCacheKey};
use secp256k1::{XOnlyPublicKey, Secp256k1};
use tondi_hashes::{Hash, Hasher, HasherBase};
fn validate_cisa<T: VerifiableTransaction>(
tx: &T,
utxo_entries: &[UtxoEntry],
reused_values: &SigHashReusedValuesUnsync,
sig_cache: &Cache<SigCacheKey, bool>
) -> Result<(), TxScriptError> {
// 1) Parse envelope
let env = parse_cisa_envelope(tx).ok_or(TxScriptError::InvalidSignature)?;
ensure!(env.version == 1 && env.hash_type == 0x01);
let cohort = bitmap_to_indices(&env.cohort_bitmap, tx.inputs().len())?;
ensure!(!cohort.is_empty());
// 2) Collect pubkeys and script constraints for inputs in cohort
let mut xs: Vec<XOnlyPublicKey> = Vec::new();
let mut bindings: Vec<[u8;32]> = Vec::new();
for &idx in &cohort {
let (x, bind_i) = extract_pubkey_and_binding(tx, idx, &utxo_entries[idx])?;
xs.push(x);
bindings.push(bind_i);
ensure!(input_uses_cisa_op(tx, idx)); // Script includes *_CISA
ensure!(input_hash_type_is_all(tx, idx)); // v1: ALL
ensure!(!input_uses_anyprevout(tx, idx)); // v1: no APO
}
// 3) Compute aggregated pubkey X (MuSig2)
let L = tag_hash("MuSig/KeyAggList", &concat_pubkeys(&xs));
let coeffs: Vec<[u8;32]> = xs.iter().map(|Xi| tag_hash("MuSig/KeyCoeff", &L, Xi)).collect();
let X = compute_aggregated_pubkey(&xs, &coeffs)?;
// 4) Assemble message m
let tx_base = build_txbase_sighash_all(tx, reused_values);
let bind_digest = hash_bindings(&bindings);
let cohort_digest = tag_hash("TSP-0008/Cohort", &env.cohort_bitmap);
let m = tag_hash("TSP-0008/CISASighash", &tx_base, &bind_digest, &cohort_digest);
// 5) Schnorr validation
let e = tag_hash("BIP0340/challenge", &env.agg_nonce_r, &X.serialize(), &m);
ensure!(schnorr_verify(&env.agg_sig_s, &env.agg_nonce_r, &X, &e));
Ok(())
}
extract_pubkey_and_binding (Per-Input Binding Data):
use tondi_consensus_core::tx::{Transaction, UtxoEntry, ScriptPublicKey};
use secp256k1::XOnlyPublicKey;
use tondi_crypto_txscript::TxScriptError;
use tondi_hashes::{Hash, Hasher, HasherBase, TransactionSigningHash};
fn extract_pubkey_and_binding<T: VerifiableTransaction>(
tx: &T,
i: usize,
utxo_entry: &UtxoEntry
) -> Result<(XOnlyPublicKey, [u8;32]), TxScriptError> {
// Parse TSP-0008 pubkey from script (0x02||pubkey32)
let pk = parse_tsp0008_pubkey_from_script(&tx.inputs()[i].signature_script)?;
// Extract binding data for this input
let amount = utxo_entry.amount;
let spk = &utxo_entry.script_public_key.script;
let seq = tx.inputs()[i].sequence;
// Build binding data
let mut binding_data = Vec::new();
binding_data.extend_from_slice(&amount.to_le_bytes());
binding_data.extend_from_slice(&compact_size_len(spk.len()));
binding_data.extend_from_slice(spk);
// Annex handling (Tondi doesn't have annex, so always 0)
binding_data.push(0);
binding_data.extend_from_slice(&seq.to_le_bytes());
// CODESEPARATOR position (0xFFFFFFFF if none)
binding_data.extend_from_slice(&0xFFFFFFFFu32.to_le_bytes());
// Tapleaf hash (32 bytes) - compute from script
let leaf_hash = compute_tapleaf_hash(spk);
binding_data.extend_from_slice(&leaf_hash);
// Hash the binding data
let mut hasher = TransactionSigningHash::new();
hasher.update(&binding_data);
let binding_hash = hasher.finalize();
Ok((pk, binding_hash))
}
9.2 Script Execution Engine Hook (Simplified)
use tondi_crypto_txscript::{TxScriptEngine, TxScriptError, caches::Cache, SigCacheKey};
use tondi_consensus_core::hashing::sighash::SigHashReusedValues;
use tondi_consensus_core::tx::VerifiableTransaction;
use secp256k1::XOnlyPublicKey;
/// TSP-0008 CISA Opcodes
pub const OP_CHECKSIG_CISA: u8 = 0xB5;
pub const OP_CHECKSIGVERIFY_CISA: u8 = 0xB6;
pub const OP_CHECKSIGADD_CISA: u8 = 0xB7;
// OP_CHECKSIGVERIFY_CISA implementation for Tondi client
impl<T: VerifiableTransaction, Reused: SigHashReusedValues>
OpCodeExecution<T, Reused> for OpCode<OP_CHECKSIGVERIFY_CISA> {
fn execute(&self, vm: &mut TxScriptEngine<T, Reused>) -> OpCodeResult {
// Check if top of stack is TSP-0008 pubkey (0x02||pubkey32)
let pubkey_data = vm.dstack.pop()?;
ensure_tsp0008_pubkey(&pubkey_data)?;
// Mark current input as requiring CISA validation
vm.mark_input_requires_cisa();
// Push true - actual validation happens at transaction level
vm.dstack.push_bool(true)?;
// Execute VERIFY (pop and check if true)
vm.op_verify()?;
Ok(())
}
}
fn ensure_tsp0008_pubkey(data: &[u8]) -> Result<XOnlyPublicKey, TxScriptError> {
if data.len() != 33 || data[0] != 0x02 {
return Err(TxScriptError::InvalidSignature);
}
let pubkey_bytes = &data[1..];
XOnlyPublicKey::from_slice(pubkey_bytes)
.map_err(|_| TxScriptError::InvalidSignature)
}
10. Test Vectors (Required Set)
10.1 Domain Separation Test Vectors
MUST: Provide SHA256("label") values for all labels and intermediate states of H_tag(label, m):
SHA256("TSP-0008/CISASighash")=0x...SHA256("TSP-0008/Cohort")=0x...SHA256("MuSig/KeyAggList")=0x...SHA256("MuSig/KeyCoeff")=0x...SHA256("BIP0340/challenge")=0x...
10.2 R Even-y Validation Test Vectors
MUST: Provide example with odd-y R, verification should FAIL.
10.3 a_i == 0 Rare Case Test Vectors
MUST: Construct set causing some coefficient to be 0 (can artificially set to 0) and specify must FAIL.
10.4 Duplicate Public Key Test Vectors
MUST: If duplicate public keys allowed, provide equivalent/non-equivalent two use cases and expected results.
10.5 Annex Binding Test Vectors
MUST: Provide two sets of examples covering annex presence/absence, covering annex_flag==1 hash details.
10.6 Bitmap High Bit Non-zero Failure Test Vectors
MUST: Provide failure example with bitmap high bits non-zero.
10.7 X == โ Failure Test Vectors
MUST: Provide failure example with X == โ (can artificially construct two opposite private keys summing to 0 ideal scenario to demonstrate implementation branch).
10.8 Positive Examples
- 2 inputs (both 0x02||pk), SIGHASH_ALL, single cohort, correct R/s;
- 8-input coinjoin with varying script/amount/leaf, correct bindings;
- Mix with annex and CODESEPARATOR inputs;
- Large cohort (k=256) boundary performance test.
10.9 Negative Examples
- Bitmap inconsistent with script usage;
- hash_type โ 0x01;
- Cohort with APO input;
- Pubkey not 0x02||pk or missing;
- Invalid R/s, or rogue-key detection (altered pubkey order/set) fails;
- Single-input cohort (allowed but low value, as boundary) vs. empty cohort (must fail).
10.10 Benchmark Tests (Size/CPU)
SHOULD: Provide N โ {1,2,4,8,16,64,256,1024} byte savings and CPU estimation table (verification cost before/after aggregation) to help nodes and wallets tune parameters.
11. Interoperability and Evolution Roadmap
- Key-Path CISA: Two deployment options requiring community evaluation:
- New SegWit Version: Introduce new witness version for key-path aggregation
- Generalized Taproot (g'root/Entroot): Move aggregatable public keys out of script system entirely
- Multi-Cohort Transactions: Introduce cohort_id and multiple envelopes; must prevent cross-cohort mixing.
- CISA + APO: Define per-input APO binding extensions (quantify ignored fields in ANYPREVOUT), proceed cautiously.
- Half-Aggregation Integration: Block-level half-aggregation + script-path adaptor signatures paradigm
- Privacy Padding: Bitmap padding or "index list + commitment" mode to hide participation scale.
12. Deployment and Governance Checklist
- Community Evaluation: Assess New SegWit Version vs. g'root/Entroot deployment paths
- Risk Mitigation: Avoid OP_SUCCESSx redefinition to prevent chain split risks
- Release cross-implementation test vectors (Rust/Go/C++/Python);
- Testnet Requirements (โฅ 3 months):
- Adversarial aggregation/reorg/rate-limiting experiments
- Half-aggregation caching and reorg testing
- Adaptor signature compatibility testing
- Block-level half-aggregation integration tests
- Wallet SDK: Default enable CISA only in template scripts, secure MuSig2 nonces, PSBT/export mark CISA;
- Network Policy: Cohort size limits, whitelisted templates, per-peer rate limits;
- PSBT Extensions: Implement half-aggregation and adaptor signature field support
- Third-Party Audit (consensus + cryptography implementation, constant-time).
13. Implementation Roadmap for Tondi Client
13.1 Current Tondi Client Analysis
Based on analysis of the Tondi client code, the following current state has been identified:
Implemented Features:
- โ
Complete Schnorr signature support (
secp256k1::schnorr::Signature) - โ Taproot script path and key path support
- โ
Signature hash calculation (
calc_schnorr_signature_hash) - โ
Script execution engine (
TxScriptEngine) - โ
Signature caching mechanism (
SigCacheKey) - โ Witness data structure support
Missing CISA Features:
- โ CISA witness envelope parsing and validation
- โ MuSig2 public key aggregation algorithm
- โ Cross-input signature aggregation validation
- โ TSP-0008 specific opcodes (
OP_CHECKSIG_CISA,OP_CHECKSIGVERIFY_CISA) - โ Cohort binding and message construction
- โ CISA-specific signature hash calculation
13.2 Implementation Steps
Phase 1: Core Infrastructure (4-6 weeks)
-
CISA Witness Envelope Implementation
- Create
cisa.rsundercrypto/txscript/src/standard/taproot/ - Implement
CisaEnvelopestructure and parsing functions - Add envelope validation logic
- Create
-
MuSig2 Integration
- Integrate
secp256k1-musig2library or implement MuSig2 algorithm - Add CISA signature hash calculation in
consensus/core/src/hashing/sighash.rs - Implement public key aggregation and coefficient calculation
- Integrate
-
New Opcodes Implementation
- Add CISA opcodes in
crypto/txscript/src/opcodes/mod.rs - Implement
OP_CHECKSIG_CISAandOP_CHECKSIGVERIFY_CISA - Use
OP_SUCCESSxencoding for soft fork compatibility
- Add CISA opcodes in
Phase 2: Transaction-Level Validation (3-4 weeks)
-
CISA Validation Engine
- Extend
TxScriptEngineincrypto/txscript/src/lib.rs - Implement transaction-level CISA validation logic
- Add cohort member detection and constraint validation
- Extend
-
Signature Aggregation Logic
- Implement cross-input signature aggregation validation
- Add BIP-340 style challenge calculation
- Integrate with existing signature caching mechanism
Phase 3: Integration and Testing (3-4 weeks)
-
Wallet Integration
- Add CISA support in
wallet/core/src/ - Implement CISA transaction construction and signing
- Update PSBT support to handle CISA envelopes
- Add CISA support in
-
Mempool and Consensus Integration
- Add CISA policies in
mining/mempool/ - Implement cohort size limits and fee calculation
- Add CISA-specific relay rules
- Add CISA policies in
-
Testing and Validation
- Create comprehensive test vectors
- Implement integration tests and performance benchmarks
- Add fuzz testing and edge case testing
13.3 Code Structure Updates
New Files:
crypto/txscript/src/standard/taproot/cisa.rs
consensus/core/src/hashing/cisa_sighash.rs
wallet/core/src/cisa/
testing/integration/src/cisa_tests.rs
Modified Files:
crypto/txscript/src/lib.rs # Add CISA validation
crypto/txscript/src/opcodes/mod.rs # New opcodes
consensus/core/src/hashing/sighash.rs # CISA signature hash
wallet/core/src/tx/generator/generator.rs # CISA transaction construction
mining/src/mempool/manager.rs # CISA policies
consensus/core/src/tx.rs # Transaction payload handling
13.4 Code Implementation Examples
CISA Envelope Structure
// crypto/txscript/src/standard/taproot/cisa.rs
use tondi_consensus_core::hashing::sighash_type::SigHashType;
use tondi_consensus_core::tx::{Transaction, VerifiableTransaction};
use tondi_crypto_txscript::TxScriptError;
use tondi_hashes::{Hash, TransactionSigningHash};
#[derive(Debug, Clone)]
pub struct CisaEnvelope {
pub magic: [u8; 4], // "CISA"
pub version: u8, // 0x01
pub flags: u16, // bit0: SIGHASH_ALL only, bit1: Half-Agg enabled
pub hash_type: u8, // MUST be 0x01
pub cohort_bitmap: Vec<u8>, // ceil(num_inputs/8) bytes
pub agg_nonce_r: [u8; 32], // x-only R, even-y required
pub agg_sig_s: [u8; 32], // Schnorr scalar s
// Half-Aggregation fields (when flags bit1=1)
pub half_agg_r_components: Option<Vec<[u8; 32]>>, // Individual R components
pub half_agg_s_contributions: Option<Vec<[u8; 32]>>, // Individual s contributions
}
impl CisaEnvelope {
pub fn parse(data: &[u8]) -> Result<Self, TxScriptError> {
if data.len() < 4 {
return Err(TxScriptError::InvalidSignature);
}
// Check magic bytes "CISA"
if &data[0..4] != b"CISA" {
return Err(TxScriptError::InvalidSignature);
}
// Parse remaining fields...
// Implementation details for parsing envelope structure
Ok(CisaEnvelope {
magic: [0x43, 0x49, 0x53, 0x41], // "CISA"
version: 0x01,
flags: 0x0001, // SIGHASH_ALL only (bit1=0 for full agg)
hash_type: 0x01,
cohort_bitmap: Vec::new(), // Parse from data
agg_nonce_r: [0u8; 32], // Parse from data
agg_sig_s: [0u8; 32], // Parse from data
half_agg_r_components: None, // Parse if flags bit1=1
half_agg_s_contributions: None, // Parse if flags bit1=1
})
}
pub fn validate<T: VerifiableTransaction>(&self, tx: &T) -> Result<(), TxScriptError> {
// Validate envelope constraints
if self.version != 1 {
return Err(TxScriptError::InvalidSignature);
}
if self.hash_type != 0x01 {
return Err(TxScriptError::InvalidSignature);
}
// Check cohort bitmap consistency
let expected_bitmap_size = (tx.inputs().len() + 7) / 8;
if self.cohort_bitmap.len() != expected_bitmap_size {
return Err(TxScriptError::InvalidSignature);
}
// Validate Half-Aggregation fields if enabled
let is_half_agg = (self.flags & 0x0002) != 0;
if is_half_agg {
if self.half_agg_r_components.is_none() || self.half_agg_s_contributions.is_none() {
return Err(TxScriptError::InvalidSignature);
}
let cohort_size = self.cohort_bitmap.iter().map(|b| b.count_ones()).sum::<u32>() as usize;
if let (Some(r_components), Some(s_contributions)) = (&self.half_agg_r_components, &self.half_agg_s_contributions) {
if r_components.len() != cohort_size || s_contributions.len() != cohort_size {
return Err(TxScriptError::InvalidSignature);
}
}
}
Ok(())
}
}
CISA Opcodes Implementation
// Add to crypto/txscript/src/opcodes/mod.rs
use tondi_crypto_txscript::{TxScriptEngine, TxScriptError, caches::Cache, SigCacheKey};
use tondi_consensus_core::hashing::sighash::SigHashReusedValues;
use tondi_consensus_core::tx::VerifiableTransaction;
use secp256k1::XOnlyPublicKey;
impl<T: VerifiableTransaction, Reused: SigHashReusedValues>
OpCodeExecution<T, Reused> for OpCode<OP_CHECKSIG_CISA> {
fn execute(&self, vm: &mut TxScriptEngine<T, Reused>) -> OpCodeResult {
// Check if top of stack is TSP-0008 pubkey (0x02||pubkey32)
let pubkey_data = vm.dstack.pop()?;
ensure_tsp0008_pubkey(&pubkey_data)?;
// Check if this is an adaptor signature path (non-aggregatable)
let is_adaptor_path = vm.is_adaptor_signature_path();
if is_adaptor_path {
// For adaptor signatures, use regular Schnorr validation
return vm.execute_regular_schnorr_checksig(&pubkey_data);
}
// Mark current input as requiring CISA validation
vm.mark_input_requires_cisa();
// Push true - actual validation happens at transaction level
vm.dstack.push_bool(true)?;
Ok(())
}
}
impl<T: VerifiableTransaction, Reused: SigHashReusedValues>
OpCodeExecution<T, Reused> for OpCode<OP_CHECKSIGVERIFY_CISA> {
fn execute(&self, vm: &mut TxScriptEngine<T, Reused>) -> OpCodeResult {
// Check if top of stack is TSP-0008 pubkey (0x02||pubkey32)
let pubkey_data = vm.dstack.pop()?;
ensure_tsp0008_pubkey(&pubkey_data)?;
// Mark current input as requiring CISA validation
vm.mark_input_requires_cisa();
// Push true and execute VERIFY
vm.dstack.push_bool(true)?;
vm.op_verify()?;
Ok(())
}
}
fn ensure_tsp0008_pubkey(data: &[u8]) -> Result<XOnlyPublicKey, TxScriptError> {
if data.len() != 33 || data[0] != 0x02 {
return Err(TxScriptError::InvalidSignature);
}
let pubkey_bytes = &data[1..];
XOnlyPublicKey::from_slice(pubkey_bytes)
.map_err(|_| TxScriptError::InvalidSignature)
}
CISA Signature Hash Calculation
// consensus/core/src/hashing/cisa_sighash.rs
use tondi_consensus_core::tx::{Transaction, VerifiableTransaction, UtxoEntry};
use tondi_consensus_core::hashing::sighash::{SigHashReusedValues, SigHashReusedValuesUnsync};
use tondi_hashes::{Hash, Hasher, HasherBase, TransactionSigningHash};
#[derive(Debug, Clone)]
pub struct CohortBinding {
pub amount: u64,
pub script_pubkey: Vec<u8>,
pub annex_flag: u8, // 0=no annex, 1=has annex
pub annex: Vec<u8>, // annex data (empty if annex_flag=0)
pub sequence: u32, // le32 encoding
pub codesep_pos: u32, // le32 encoding (0xFFFFFFFF if no CODESEPARATOR)
pub tapleaf_hash: [u8; 32], // MUST include leaf_version
}
pub fn calc_cisa_signature_hash<T: VerifiableTransaction>(
tx: &T,
cohort_bitmap: &[u8],
cohort_bindings: &[CohortBinding],
reused_values: &SigHashReusedValuesUnsync,
) -> Hash {
// TxBase (same as tapscript SIGHASH_ALL)
let tx_base = build_txbase_sighash_all(tx, reused_values);
// CohortBindings (per-input binding data)
let bindings_digest = hash_cohort_bindings(cohort_bindings);
// CohortBitmapDigest (prevents replay attacks)
let cohort_digest = tag_hash("TSP-0008/Cohort", cohort_bitmap);
// Final message
tag_hash("TSP-0008/CISASighash", &tx_base, &bindings_digest, &cohort_digest)
}
fn build_txbase_sighash_all<T: VerifiableTransaction>(
tx: &T,
reused_values: &SigHashReusedValuesUnsync
) -> Hash {
let mut hasher = TransactionSigningHash::new();
// Hash type (0x01 for SIGHASH_ALL) - MUST match BIP-341 exactly
hasher.write_u8(0x01);
// Transaction version (le32) - MUST match BIP-341 byte order
hasher.write_u32_le(tx.version());
// Lock time (le32) - MUST match BIP-341 byte order
hasher.write_u32_le(tx.lock_time());
// Previous outputs hash - MUST use identical BIP-341 encoding
let prevouts_hash = reused_values.previous_outputs_hash(|| {
let mut h = TransactionSigningHash::new();
for input in tx.inputs() {
h.update(input.previous_outpoint.transaction_id.as_bytes());
h.write_u32_le(input.previous_outpoint.index); // le32 encoding
}
h.finalize()
});
hasher.update(prevouts_hash.as_bytes());
// Sequences hash - MUST use identical BIP-341 encoding
let sequences_hash = reused_values.sequences_hash(|| {
let mut h = TransactionSigningHash::new();
for input in tx.inputs() {
h.write_u32_le(input.sequence); // le32 encoding
}
h.finalize()
});
hasher.update(sequences_hash.as_bytes());
// Outputs hash - MUST use identical BIP-341 CompactSize + encoding
let outputs_hash = reused_values.outputs_hash(|| {
let mut h = TransactionSigningHash::new();
for output in tx.outputs() {
h.write_u64_le(output.value); // le64 encoding
// CompactSize encoding for script length
let script_len = output.script_public_key.script.len();
if script_len < 0xfd {
h.write_u8(script_len as u8);
} else if script_len <= 0xffff {
h.write_u8(0xfd);
h.write_u16_le(script_len as u16);
} else {
h.write_u8(0xfe);
h.write_u32_le(script_len as u32);
}
h.update(&output.script_public_key.script);
}
h.finalize()
});
hasher.update(outputs_hash.as_bytes());
hasher.finalize()
}
fn hash_cohort_bindings(bindings: &[CohortBinding]) -> Hash {
let mut hasher = TransactionSigningHash::new();
for binding in bindings {
hasher.write_u64_le(binding.amount); // le64 encoding
// CompactSize encoding for script length
let script_len = binding.script_pubkey.len();
if script_len < 0xfd {
hasher.write_u8(script_len as u8);
} else if script_len <= 0xffff {
hasher.write_u8(0xfd);
hasher.write_u16_le(script_len as u16);
} else {
hasher.write_u8(0xfe);
hasher.write_u32_le(script_len as u32);
}
hasher.update(&binding.script_pubkey);
// Annex flag (0=no annex, 1=has annex)
hasher.write_u8(binding.annex_flag);
if binding.annex_flag == 1 {
// MUST match tapscript annex binding exactly
let annex_len = binding.annex.len();
if annex_len < 0xfd {
hasher.write_u8(annex_len as u8);
} else if annex_len <= 0xffff {
hasher.write_u8(0xfd);
hasher.write_u16_le(annex_len as u16);
} else {
hasher.write_u8(0xfe);
hasher.write_u32_le(annex_len as u32);
}
hasher.update(&binding.annex);
}
hasher.write_u32_le(binding.sequence); // le32 encoding
hasher.write_u32_le(binding.codesep_pos); // le32 encoding (0xFFFFFFFF if none)
hasher.update(&binding.tapleaf_hash); // 32 bytes
}
hasher.finalize()
}
fn compute_tapleaf_hash(script: &[u8], leaf_version: u8) -> [u8; 32] {
// MUST include leaf_version: H_tag("TapLeaf", leaf_version || CompactSize(script_len) || script)
let mut hasher = TransactionSigningHash::new();
// TapLeaf tag
hasher.update(b"TapLeaf");
// Leaf version
hasher.write_u8(leaf_version);
// CompactSize encoding for script length
let script_len = script.len();
if script_len < 0xfd {
hasher.write_u8(script_len as u8);
} else if script_len <= 0xffff {
hasher.write_u8(0xfd);
hasher.write_u16_le(script_len as u16);
} else {
hasher.write_u8(0xfe);
hasher.write_u32_le(script_len as u32);
}
// Script content
hasher.update(script);
hasher.finalize().into()
}
fn tag_hash(tag: &str, data1: &[u8], data2: &[u8], data3: &[u8]) -> Hash {
let mut hasher = TransactionSigningHash::new();
hasher.update(tag.as_bytes());
hasher.update(data1);
hasher.update(data2);
hasher.update(data3);
hasher.finalize()
}
Integration with TxScriptEngine
// Extend crypto/txscript/src/lib.rs
use tondi_crypto_txscript::{TxScriptEngine, TxScriptError, caches::Cache, SigCacheKey};
use tondi_consensus_core::hashing::sighash::SigHashReusedValues;
use tondi_consensus_core::tx::{VerifiableTransaction, UtxoEntry};
use secp256k1::{XOnlyPublicKey, Secp256k1};
use std::collections::HashMap;
// Half-Aggregation cache for mempool optimization
#[derive(Debug, Clone)]
pub struct HalfAggregationCache {
// Cache s components and R vectors for efficient block-level aggregation
pub cached_s_components: HashMap<TransactionId, Vec<[u8; 32]>>,
pub cached_r_vectors: HashMap<TransactionId, Vec<[u8; 32]>>,
// Track confirmation depth for reorg handling
pub confirmation_depths: HashMap<TransactionId, u64>,
}
impl<'a, T: VerifiableTransaction, Reused: SigHashReusedValues>
TxScriptEngine<'a, T, Reused> {
pub fn validate_cisa_transaction(
tx: &T,
utxo_entries: &[UtxoEntry],
reused_values: &Reused,
sig_cache: &Cache<SigCacheKey, bool>,
half_agg_cache: &mut HalfAggregationCache
) -> Result<(), TxScriptError> {
// 1. Parse CISA envelope
let envelope = Self::extract_cisa_envelope(tx)?;
// 2. Collect pubkeys and bindings for cohort
let (pubkeys, bindings) = Self::collect_cohort_data(tx, utxo_entries, &envelope)?;
// 3. Compute aggregated pubkey (MuSig2)
let agg_pubkey = Self::compute_aggregated_pubkey(&pubkeys)?;
// 4. Build signature message
let message = calc_cisa_signature_hash(
tx,
&envelope.cohort_bitmap,
&bindings,
reused_values
);
// 5. Verify aggregated signature (with Half-Agg support)
Self::verify_aggregated_signature(&envelope, &agg_pubkey, &message, half_agg_cache)?;
Ok(())
}
fn extract_cisa_envelope<T: VerifiableTransaction>(tx: &T) -> Result<CisaEnvelope, TxScriptError> {
// Extract CISA envelope from transaction witness data
// Implementation depends on how envelope is stored in Tondi
// Could be in payload field or as special witness element
todo!("Extract CISA envelope from transaction")
}
fn collect_cohort_data<T: VerifiableTransaction>(
tx: &T,
utxo_entries: &[UtxoEntry],
envelope: &CisaEnvelope
) -> Result<(Vec<XOnlyPublicKey>, Vec<CohortBinding>), TxScriptError> {
let mut pubkeys = Vec::new();
let mut bindings = Vec::new();
for (idx, byte) in envelope.cohort_bitmap.iter().enumerate() {
for bit in 0..8 {
if (byte >> bit) & 1 == 1 {
let input_idx = idx * 8 + bit;
if input_idx >= tx.inputs().len() {
continue;
}
let (pubkey, binding) = extract_pubkey_and_binding(
tx,
input_idx,
&utxo_entries[input_idx]
)?;
pubkeys.push(pubkey);
bindings.push(binding);
}
}
}
Ok((pubkeys, bindings))
}
fn compute_aggregated_pubkey(pubkeys: &[XOnlyPublicKey]) -> Result<XOnlyPublicKey, TxScriptError> {
// Implement MuSig2 key aggregation
// This would integrate with secp256k1-musig2 or implement MuSig2 directly
todo!("Implement MuSig2 key aggregation")
}
fn verify_aggregated_signature(
envelope: &CisaEnvelope,
agg_pubkey: &XOnlyPublicKey,
message: &Hash
) -> Result<(), TxScriptError> {
// Implement Schnorr signature verification
// Using secp256k1 library for verification
todo!("Implement Schnorr signature verification")
}
}
13.5 Tondi Client Compatibility Review
โ Compatible Components:
- Transaction Structure: Uses
tondi_consensus_core::tx::Transactionwith proper field access - Script Engine: Integrates with existing
TxScriptEngine<T, Reused>architecture - Signature Types: Uses
secp256k1::XOnlyPublicKeyandsecp256k1::schnorr::Signature - Hash Functions: Leverages
tondi_hashes::TransactionSigningHashand existing hashing infrastructure - Error Handling: Uses
TxScriptErrorfor consistent error propagation - Caching: Integrates with existing
Cache<SigCacheKey, bool>signature cache - UTXO Handling: Works with
UtxoEntryandVerifiableTransactiontraits - Taproot Support: Existing taproot implementation in
crypto/txscript/src/standard/taproot/
โ ๏ธ Implementation Considerations:
- CISA Envelope Storage: Tondi uses
signature_scriptfield instead of separate witness structure; CISA envelope should be stored as transaction-level extension inpayloadfield - MuSig2 Integration: May need to add
secp256k1-musig2dependency or implement MuSig2 directly - OpCode Registration: Need to register new CISA opcodes in Tondi's opcode system (
crypto/txscript/src/opcodes/mod.rs) - Transaction Structure: Tondi transactions have
payloadfield that could store CISA envelope - Script Public Key: Tondi uses
ScriptPublicKeywith version and script fields - Half-Aggregation Cache: Need to integrate with Tondi's mempool system (
mining/src/mempool/) for caching s components and R vectors - Adaptor Signature Detection: Need to add adaptor signature path detection in script execution
- Reorg Handling: Need to integrate with Tondi's block processing for confirmation depth tracking
๐ง Required Adaptations:
- Transaction Payload: Store CISA envelope in transaction's
payloadfield as witness extension - Script Parsing: Update script parsing to handle TSP-0008 pubkey format (0x02||pubkey32)
- Validation Integration: Integrate CISA validation into Tondi's existing transaction validation pipeline
- Half-Aggregation Cache: Integrate with mempool system for s/R component caching and reorg handling
- Adaptor Signature Support: Add adaptor signature path detection and non-aggregatable flag handling
- Testing Framework: Adapt test vectors to Tondi's transaction format and testing infrastructure
- OpCode System: Extend Tondi's opcode system to support CISA-specific opcodes via OP_SUCCESSx encoding
- Mempool Integration: Add Half-Aggregation caching to
mining/src/mempool/components
13.6 Deployment Timeline
- Q1 2025: Phase 1 completion, core infrastructure ready
- Q2 2025: Phase 2 completion, validation engine integration
- Q3 2025: Phase 3 completion, comprehensive testing and optimization
- Q4 2025: Testnet deployment and community testing
- Q1 2026: Mainnet activation preparation
14. Conclusion
TSP-0008 securely implements cross-input Schnorr aggregation as a soft fork within strict tapscript-only, SIGHASH_ALL boundaries:
- Significantly reduces transaction volume and validation costs;
- Aligns with Tondi's high-throughput DAG goals;
- Resists replays and rogue-keys via per-input bindings and MuSig2 coefficients;
- Advances in parallel with TSP-0007 (v1 bans APO mixing, extensible later).
Implementation Status: The Tondi client has a solid foundation with Schnorr and Taproot support. CISA functionality implementation is estimated to take 12-14 weeks, and it is recommended to proceed through the three phases outlined above.
Tondi-Specific Implementation Notes:
- Transaction Structure: Tondi uses
Transactionwithpayloadfield for storing witness extensions - Script Engine: Existing
TxScriptEngine<T, Reused>architecture supports soft fork opcodes via OP_SUCCESSx - Hash Functions:
TransactionSigningHashprovides consistent hashing across the codebase - Caching:
Cache<SigCacheKey, bool>provides signature verification caching - Taproot Integration: Existing taproot support in
crypto/txscript/src/standard/taproot/can be extended for CISA - Wallet Integration: Wallet core architecture supports transaction generation and signing workflows
- Mempool System:
mining/src/mempool/provides transaction pool management for Half-Aggregation caching - Block Processing: Consensus core provides block validation and reorg handling for confirmation depth tracking
- PSBT Support: Wallet core can be extended with CISA-specific PSBT fields for Half-Aggregation and Adaptor signatures
Appendix A: Size Estimation Comparison (Non-Normative)
A.1 Tondi-Specific Calculations
- Typical P2TR script-path input: Witness includes 64B signature + various script elements.
- 8-input transaction:
- Without CISA: Signature volume โ 8 ร 64 = 512B (excluding other stack/script);
- CISA: 64 (s) + 32 (R) + 1 (hash_type) + ceil(8/8)=1 (bitmap) + header โ 120B;
- Signature-related savings โ ~392B (>75%).
A.2 Blockstream Research Comparison Table
| Aggregation Type | N=2 | N=4 | N=8 | N=16 | N=64 | N=256 |
|---|---|---|---|---|---|---|
| Half-Agg | 15.2% | 18.7% | 20.6% | 21.8% | 22.9% | 23.4% |
| Full Agg | 19.8% | 23.4% | 26.1% | 27.8% | 29.2% | 29.8% |
| Combined | 28.1% | 31.2% | 33.6% | 35.1% | 36.4% | 36.9% |
| Large Coinjoin | 35.2% | 38.1% | 41.2% | 42.8% | 44.1% | 44.6% |
Note: Percentages represent byte savings. Weight unit savings are approximately 1/3 of byte savings.
Appendix B: Common Implementation Pitfalls (Non-Normative)
- Inconsistent CompactSize and varbytes serialization;
- Whether annex length prefix enters hash (this proposal explicitly includes it);
- CODESEPARATOR position evaluation with multi-leaf scripts;
- MuSig2 coefficient $a_i$ list ordering (must be ascending input index);
- R normalization (x-only) and BIP-340 challenge domain separation tag consistency.
References
- BIP-340: Schnorr Signatures for secp256k1
- MuSig2: Simple Two-Round Schnorr Multi-Signatures
- TSP-0007: ANYPREVOUT Support for Eltoo-based Payment Channels (Tondi Flash)
- Kaspa GHOSTDAG/PHANTOM (architectural background, for context only)
- Blockstream Research: Cross-Input Aggregation Analysis - OP_SUCCESSx risks, Half-Aggregation, Adaptor compatibility, g'root/Entroot deployment paths
- Blockstream Research: CISA Savings Analysis - Economic benefits comparison across aggregation types
Appendix C: Additional Recommendations (Roadmap/Privacy/Ecosystem)
Multi-Cohort v2
Once introduced, multiple envelopes must include cohort_id in message and isolate key-agg domain separation (e.g., H_tag(".../KeyAggList/c<id>")).
Key-Path CISA
Recommended to introduce in new witness version to avoid entanglement with existing key-path rules.
CISA + APO Joint Specification
Explicitly include APO ignored fields in CohortBindings extension vector; requires separate hash_type negotiation and long-term testnet experimentation.
Wallet UX
In coinjoin mode, enable "fixed-length bitmap policy" + "nonce commitment timeout + blame reorganization" to reduce social engineering space for participant rejection.
Implementation Priority Matrix
- Phase 1 (Critical): Consensus-critical fixes, domain separation standardization, basic test vectors
- Phase 2 (Security): Rogue-key protection, duplicate key handling, error code standardization
- Phase 3 (Ecosystem): PSBT extensions, wallet interoperability, privacy padding options
- Phase 4 (Advanced): Multi-cohort support, APO integration, performance optimization