1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! SegWit witness commitment validation tests
//!
//! Tests for SegWit witness commitment requirements.
//! In SegWit blocks, the coinbase transaction must include a witness commitment
//! that matches the witness merkle root.
//!
//! Consensus-critical: Incorrect witness commitment causes consensus violation.
use blvm_consensus::opcodes::OP_RETURN;
use blvm_consensus::segwit::{compute_witness_merkle_root, validate_witness_commitment, Witness};
use blvm_consensus::types::{
Block, BlockHeader, Hash, OutPoint, Transaction, TransactionInput, TransactionOutput,
};
/// Test witness commitment validation in SegWit blocks
#[test]
fn test_witness_commitment_segwit_block() {
// Create a SegWit block with witness data
let block = Block {
header: BlockHeader {
version: 0x20000000, // SegWit version bit set
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1501593374, // Approximate SegWit activation time
bits: 0x18013ce9,
nonce: 0,
},
transactions: vec![
// Coinbase transaction with witness commitment
Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00], // Height encoding
sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![
TransactionOutput {
value: 12_5000_0000, // 12.5 BTC
script_pubkey: vec![0x51], // Regular output
},
// Witness commitment would be in second output
],
lock_time: 0,
},
]
.into_boxed_slice(),
};
// Create witness data
let witnesses = vec![Witness::new()]; // Empty witness for coinbase
// Compute witness merkle root
let witness_root = compute_witness_merkle_root(&block, &witnesses);
// Should compute witness root successfully
assert!(witness_root.is_ok());
}
/// Test witness commitment validation at SegWit activation height
#[test]
fn test_witness_commitment_activation_height() {
// Scenario name: at mainnet SegWit deployment (`SEGWIT_ACTIVATION_MAINNET`).
let block = Block {
header: BlockHeader {
version: 0x20000000, // SegWit version bit
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1501593374,
bits: 0x18013ce9,
nonce: 0,
},
transactions: vec![Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00],
sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![TransactionOutput {
value: 12_5000_0000,
script_pubkey: vec![],
}],
lock_time: 0,
}]
.into_boxed_slice(),
};
let witnesses = vec![Witness::new()];
let witness_root = compute_witness_merkle_root(&block, &witnesses);
// At activation height, witness commitment should be validated
assert!(witness_root.is_ok());
}
/// Test witness commitment in blocks without witness transactions
#[test]
fn test_witness_commitment_no_witness_txs() {
// SegWit block can have no witness transactions
// but still must have witness commitment
let block = Block {
header: BlockHeader {
version: 0x20000000, // SegWit version bit
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1501593374,
bits: 0x18013ce9,
nonce: 0,
},
transactions: vec![Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![],
sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![TransactionOutput {
value: 12_5000_0000,
script_pubkey: vec![],
}],
lock_time: 0,
}]
.into_boxed_slice(),
};
// All witnesses are empty (no witness transactions)
let witnesses = vec![Witness::new()];
let witness_root = compute_witness_merkle_root(&block, &witnesses);
// Should still compute witness root (all zeros)
assert!(witness_root.is_ok());
}
/// Test invalid witness commitment rejection
#[test]
fn test_invalid_witness_commitment_rejection() {
let block = Block {
header: BlockHeader {
version: 0x20000000,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1501593374,
bits: 0x18013ce9,
nonce: 0,
},
transactions: vec![Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![],
sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![TransactionOutput {
value: 12_5000_0000,
script_pubkey: vec![],
}],
lock_time: 0,
}]
.into_boxed_slice(),
};
let witnesses = vec![Witness::new()];
let witness_root = compute_witness_merkle_root(&block, &witnesses).unwrap();
// BIP141 OP_RETURN witness commitment with an incorrect 32-byte payload (not sha256d(root||nonce))
let mut bad_commitment_script = vec![OP_RETURN, 0x24, 0xaa, 0x21, 0xa9, 0xed];
bad_commitment_script.extend_from_slice(&[0xff; 32]);
let coinbase = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32].into(),
index: 0xffffffff,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 12_5000_0000,
script_pubkey: bad_commitment_script.into(),
}]
.into(),
lock_time: 0,
};
assert!(
!validate_witness_commitment(&coinbase, &witness_root, &[]).unwrap(),
"wrong commitment payload must fail BIP141 check"
);
}
/// Test witness commitment format
///
/// Witness commitment format: OP_RETURN (0x6a) + 0x24 + 0xaa21a9ed + 32-byte hash
#[test]
fn test_witness_commitment_format() {
// Witness commitment should be: 6a 24 aa21a9ed <32-byte-hash>
let commitment_hash: Hash = [0x42; 32];
// Construct witness commitment script
let mut commitment_script = Vec::new();
commitment_script.push(0x6a); // OP_RETURN
commitment_script.push(0x24); // Push 36 bytes
commitment_script.push(0xaa); // Magic bytes
commitment_script.push(0x21);
commitment_script.push(0xa9);
commitment_script.push(0xed);
commitment_script.extend_from_slice(&commitment_hash);
// Should be 36 bytes total (1 + 1 + 4 + 32)
assert_eq!(commitment_script.len(), 38);
}