push_decoder/
lib.rs

1//! Push decoder for bitcoin blocks and transactions.
2
3#![forbid(unsafe_code)]
4#![allow(bare_trait_objects)]
5#![allow(ellipsis_inclusive_range_patterns)]
6#![warn(rustdoc::broken_intra_doc_links)]
7#![warn(missing_docs)]
8#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
9
10#[cfg(not(any(feature = "std", feature = "no-std")))]
11compile_error!("at least one of the `std` or `no-std` features must be enabled");
12
13use bitcoin::absolute::LockTime;
14use bitcoin::hash_types::TxMerkleNode;
15use merkle::IncrementalHasher;
16use bitcoin::{io, Amount};
17
18extern crate alloc;
19extern crate core;
20
21/// Incremental merkle tree hasher
22pub mod merkle;
23
24use alloc::collections::VecDeque;
25use alloc::vec::Vec;
26use core::mem;
27use core::ops::{Deref, DerefMut};
28
29use bitcoin::consensus::{encode, Decodable, Encodable};
30use bitcoin::hashes::{sha256::HashEngine, Hash, HashEngine as _};
31use bitcoin::{
32    OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Txid,
33    VarInt,
34};
35use bitcoin::blockdata::block::Header as BlockHeader;
36
37use log::*;
38
39/// Block decoder error
40#[derive(Debug)]
41pub enum Error {
42    /// the input did not contain enough data to parse a block
43    IncompleteData,
44    /// the input contained invalid data
45    ParseError,
46    /// the input contained more data than the block
47    TrailingData,
48}
49
50impl From<encode::Error> for Error {
51    fn from(e: encode::Error) -> Self {
52        debug!("parse error: {}", e);
53        Error::ParseError
54    }
55}
56
57impl From<io::Error> for Error {
58    fn from(e: io::Error) -> Self {
59        debug!("IO error: {}", e);
60        Error::ParseError
61    }
62}
63
64/// Block decoder listener.
65pub trait Listener {
66    /// called after a block header is parsed
67    fn on_block_start(&mut self, header: &BlockHeader);
68    /// called after a transaction header is parsed
69    fn on_transaction_start(&mut self, version: i32);
70    /// called after a transaction input is parsed
71    fn on_transaction_input(&mut self, txin: &TxIn);
72    /// called after a transaction output is parsed
73    fn on_transaction_output(&mut self, txout: &TxOut);
74    /// called after a transaction locktime is parsed at the end of the transaction
75    fn on_transaction_end(&mut self, locktime: LockTime, txid: Txid);
76    /// called after the complete block has been parsed
77    fn on_block_end(&mut self);
78}
79
80#[derive(Debug, PartialEq)]
81enum ParserState {
82    BeforeHeader,
83    ReadingTransactionHeader,
84    // remaining inputs
85    ReadingInputs(usize),
86    // remaining inputs, prev outpoint, remaining script size
87    ReadingInputScript(usize, OutPoint, usize),
88    // remaining inputs, elements in input, bytes in element
89    ReadingWitnesses(usize, usize, usize),
90    BeforeOutputs,
91    ReadingOutputs(usize),
92    // remaining inputs, value, remaining script size
93    ReadingOutputScript(usize, u64, usize),
94    ReadingLockTime,
95    FinishedBlock,
96}
97
98// Wrapper around a VecDeque<u8> that implements bitcoin::io::Read.
99struct Buffer(VecDeque<u8>);
100
101impl Buffer {
102    fn with_capacity(capacity: usize) -> Self {
103        Self(VecDeque::with_capacity(capacity))
104    }
105}
106
107impl Deref for Buffer {
108    type Target = VecDeque<u8>;
109
110    fn deref(&self) -> &Self::Target {
111        &self.0
112    }
113}
114
115impl DerefMut for Buffer {
116    fn deref_mut(&mut self) -> &mut Self::Target {
117        &mut self.0
118    }
119}
120
121impl io::Read for Buffer {
122    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
123        let (ref mut front, _) = self.as_slices();
124        let n = io::Read::read(front, buf)?;
125        self.drain(..n);
126        Ok(n)
127    }
128}
129
130/// Push decoder for bitcoin blocks.
131///
132/// IMPORTANT: you must call `finish()` after the last byte of the block has been supplied,
133/// in order to complete the block validation and ensure that there is no trailing data.
134///
135/// The decoder uses a limited amount of memory by streaming parse results
136/// to a listener.
137///
138/// Scripts beyond `max_script_size` are replaced with the empty script (saves
139/// 10 KB of peak memory usage).
140///
141/// The computed transactionm IDs cover the original script.
142pub struct BlockDecoder {
143    buffer: Buffer,
144    buffer_capacity: usize,
145    // scripts beyond this size are replaced with the empty script
146    max_script_size: usize,
147    parser_state: ParserState,
148    // current script - reset after each input and output
149    script: Option<Vec<u8>>,
150    // number of transactions remaining in the block
151    remaining_txs: usize,
152    // number of inputs in the current transaction if the transaction has witnesses
153    segwit_inputs: Option<usize>,
154    // hasher for computing the current transaction's ID
155    hasher: HashEngine,
156    // merkle tree hasher
157    merkle: IncrementalHasher,
158    // merkle root from the header
159    merkle_root: Option<TxMerkleNode>,
160}
161
162impl BlockDecoder {
163    /// Create a new decoder with a max script size of 100 bytes
164    pub fn new() -> Self {
165        Self::new_with_capacity(100, 100)
166    }
167
168    /// Create a new decoder with a specific buffer capacity.
169    /// must be at least 100 bytes.
170    pub fn new_with_capacity(buffer_capacity: usize, max_script_size: usize) -> Self {
171        assert!(buffer_capacity >= 100);
172        let hasher = Txid::engine();
173        Self {
174            buffer: Buffer::with_capacity(buffer_capacity),
175            buffer_capacity,
176            max_script_size,
177            parser_state: ParserState::BeforeHeader,
178            script: None,
179            remaining_txs: 0,
180            segwit_inputs: None,
181            hasher,
182            merkle: IncrementalHasher::new(),
183            merkle_root: None,
184        }
185    }
186
187    /// Supply data to the decoder
188    pub fn decode_next<L: Listener>(
189        &mut self,
190        mut data: &[u8],
191        listener: &mut L,
192    ) -> Result<(), Error> {
193        while !data.is_empty() {
194            let bytes_to_copy = usize::min(data.len(), self.buffer_capacity - self.buffer.len());
195            trace!("copying {} bytes", bytes_to_copy);
196            self.buffer.extend(&data[..bytes_to_copy]);
197            data = &data[bytes_to_copy..];
198
199            if !self.parse_step(listener)? {
200                break;
201            }
202        }
203
204        trace!("data is empty");
205        // Attempt to completely parse any remaining data in the buffer
206        self.parse_step(listener)?;
207        trace!(
208            "no progress possible at state {:?} len {}",
209            self.parser_state,
210            self.buffer.len()
211        );
212        Ok(())
213    }
214
215    /// Signal that no more data will be supplied, validate that the block is complete.
216    pub fn finish(self) -> Result<(), Error> {
217        assert_eq!(
218            self.merkle_root,
219            Some(self.merkle.finish()),
220            "merkle root mismatch"
221        );
222        if self.parser_state != ParserState::FinishedBlock {
223            Err(Error::IncompleteData)
224        } else {
225            Ok(())
226        }
227    }
228
229    // parse as much as we can of the buffer and return true if we made progress
230    fn parse_step<L: Listener>(&mut self, listener: &mut L) -> Result<bool, Error> {
231
232        let initial_buffer_len = self.buffer.len();
233
234        loop {
235            trace!("state is {:?} len {}", self.parser_state, self.buffer.len());
236            trace!("buffer {}", hex::encode(self.buffer.make_contiguous()));
237
238            match self.parser_state {
239                ParserState::BeforeHeader =>
240                // less than 2^32 transactions - max 5 byte varint
241                {
242                    if self.buffer.len() >= 80 + 5 {
243                        let header = BlockHeader::consensus_decode(&mut self.buffer)?;
244                        listener.on_block_start(&header);
245                        self.merkle_root = Some(header.merkle_root);
246                        let tx_count = VarInt::consensus_decode(&mut self.buffer)?;
247                        self.remaining_txs = tx_count.0 as usize;
248                        // merkle tree must have at least one leaf
249                        if self.remaining_txs == 0 {
250                            return Err(Error::IncompleteData);
251                        }
252                        self.parser_state = ParserState::ReadingTransactionHeader;
253                    } else {
254                        break;
255                    }
256                }
257                ParserState::ReadingTransactionHeader => {
258                    // less than 2^32 outputs, max 5 byte varint
259                    if self.buffer.len() >= 4 + 5 {
260                        let version = i32::consensus_decode(&mut self.buffer)?;
261                        version.consensus_encode(&mut self.hasher)?;
262
263                        let mut input_count = VarInt::consensus_decode(&mut self.buffer)?;
264                        if input_count.0 == 0 {
265                            // segwit variant
266                            let expected_one = VarInt::consensus_decode(&mut self.buffer)?;
267                            if expected_one.0 != 1 {
268                                return Err(Error::ParseError);
269                            }
270
271                            // now we have the real count
272                            input_count = VarInt::consensus_decode(&mut self.buffer)?;
273                            self.segwit_inputs = Some(input_count.0 as usize);
274                        } else {
275                            self.segwit_inputs = None;
276                        }
277
278                        input_count.consensus_encode(&mut self.hasher)?;
279
280                        listener.on_transaction_start(version);
281                        if input_count.0 > 0 {
282                            self.parser_state = ParserState::ReadingInputs(input_count.0 as usize);
283                        } else {
284                            // not really allowed by bitcoind, but be lenient
285                            self.parser_state = ParserState::BeforeOutputs;
286                        }
287                    } else {
288                        break;
289                    }
290                }
291                ParserState::ReadingInputs(remaining_inputs) => {
292                    // the script length must not be longer than 10000 bytes, max 3 byte varint
293                    if self.buffer.len() >= 36 + 3 {
294                        let outpoint = OutPoint::consensus_decode(&mut self.buffer)?;
295                        outpoint.consensus_encode(&mut self.hasher)?;
296
297                        let script_len = VarInt::consensus_decode(&mut self.buffer)?;
298                        script_len.consensus_encode(&mut self.hasher)?;
299                        self.script = if script_len.0 > self.max_script_size as u64 {
300                            None
301                        } else {
302                            Some(Vec::<u8>::with_capacity(script_len.0 as usize))
303                        };
304                        self.parser_state = ParserState::ReadingInputScript(
305                            remaining_inputs,
306                            outpoint,
307                            script_len.0 as usize,
308                        );
309                    } else {
310                        break;
311                    }
312                }
313                ParserState::ReadingInputScript(
314                    mut remaining_inputs,
315                    outpoint,
316                    mut remaining_script_len,
317                ) => {
318                    if self.buffer.is_empty() {
319                        break;
320                    }
321                    let to_read = usize::min(remaining_script_len, self.buffer.len());
322
323                    if let Some(ref mut script) = self.script {
324                        script.extend(self.buffer.range(..to_read));
325                    }
326
327                    for byte in self.buffer.drain(..to_read) {
328                        self.hasher.input(&[byte]);
329                    }
330
331                    remaining_script_len -= to_read;
332                    self.parser_state = ParserState::ReadingInputScript(
333                        remaining_inputs,
334                        outpoint,
335                        remaining_script_len,
336                    );
337
338                    if remaining_script_len == 0 {
339                        if self.buffer.len() >= 4 {
340                            let sequence = Sequence(u32::consensus_decode(&mut self.buffer)?);
341                            sequence.consensus_encode(&mut self.hasher)?;
342                            let script = self.script.take().unwrap_or(Vec::new());
343                            let txin = TxIn {
344                                previous_output: outpoint,
345                                script_sig: ScriptBuf::from(script),
346                                sequence,
347                                witness: Default::default(),
348                            };
349                            listener.on_transaction_input(&txin);
350                            remaining_inputs -= 1;
351
352                            if remaining_inputs == 0 {
353                                self.parser_state = ParserState::BeforeOutputs;
354                            } else {
355                                self.parser_state = ParserState::ReadingInputs(remaining_inputs);
356                            }
357                        } else {
358                            // not enough data in buffer, wait for more
359                            break;
360                        }
361                    }
362                }
363                ParserState::ReadingWitnesses(
364                    mut remaining_inputs,
365                    mut remaining_witnesses,
366                    mut remaining_witness_len,
367                ) => {
368                    if remaining_witness_len > 0 {
369                        if self.buffer.is_empty() {
370                            break;
371                        }
372                        let to_read = usize::min(remaining_witness_len, self.buffer.len());
373                        self.buffer.drain(..to_read);
374                        remaining_witness_len -= to_read;
375                        self.parser_state = ParserState::ReadingWitnesses(
376                            remaining_inputs,
377                            remaining_witnesses,
378                            remaining_witness_len,
379                        );
380                    } else if remaining_witnesses > 0 {
381                        if self.buffer.len() < 5 {
382                            break;
383                        }
384                        let witness_length = VarInt::consensus_decode(&mut self.buffer)?.0 as usize;
385                        remaining_witnesses -= 1;
386                        self.parser_state = ParserState::ReadingWitnesses(
387                            remaining_inputs,
388                            remaining_witnesses,
389                            witness_length,
390                        );
391                    } else if remaining_inputs > 0 {
392                        if self.buffer.len() < 5 {
393                            break;
394                        }
395                        let witnesses = VarInt::consensus_decode(&mut self.buffer)?.0 as usize;
396                        remaining_inputs -= 1;
397                        self.parser_state =
398                            ParserState::ReadingWitnesses(remaining_inputs, witnesses, 0);
399                    } else {
400                        self.parser_state = ParserState::ReadingLockTime;
401                    }
402                }
403                ParserState::BeforeOutputs => {
404                    // less than 2^32 outputs
405                    if self.buffer.len() >= 5 {
406                        let output_count = VarInt::consensus_decode(&mut self.buffer)?;
407                        output_count.consensus_encode(&mut self.hasher)?;
408                        self.parser_state = ParserState::ReadingOutputs(output_count.0 as usize);
409                    } else {
410                        break;
411                    }
412                }
413                ParserState::ReadingOutputs(remaining_outputs) => {
414                    // the script length must not be longer than 10000 bytes, max 3 byte varint
415                    if self.buffer.len() >= 8 + 3 {
416                        let value = u64::consensus_decode(&mut self.buffer)?;
417                        value.consensus_encode(&mut self.hasher)?;
418
419                        let script_len = VarInt::consensus_decode(&mut self.buffer)?;
420                        script_len.consensus_encode(&mut self.hasher)?;
421                        self.script = if script_len.0 > self.max_script_size as u64 {
422                            None
423                        } else {
424                            Some(Vec::with_capacity(script_len.0 as usize))
425                        };
426                        self.parser_state = ParserState::ReadingOutputScript(
427                            remaining_outputs,
428                            value,
429                            script_len.0 as usize,
430                        );
431                    } else {
432                        break;
433                    }
434                }
435                ParserState::ReadingOutputScript(
436                    mut remaining_outputs,
437                    value,
438                    mut remaining_script_len,
439                ) => {
440                    if self.buffer.is_empty() {
441                        break;
442                    }
443                    let to_read = usize::min(remaining_script_len, self.buffer.len());
444                    if let Some(ref mut script) = self.script {
445                        script.extend(self.buffer.range(..to_read));
446                    }
447                    for byte in self.buffer.drain(..to_read) {
448                        self.hasher.input(&[byte]);
449                    }
450                    remaining_script_len -= to_read;
451                    if remaining_script_len > 0 {
452                        self.parser_state = ParserState::ReadingOutputScript(
453                            remaining_outputs,
454                            value,
455                            remaining_script_len,
456                        );
457                    } else {
458                        let script = self.script.take().unwrap_or(Vec::new());
459                        let txout = TxOut {
460                            value: Amount::from_sat(value),
461                            script_pubkey: ScriptBuf::from(script),
462                        };
463                        listener.on_transaction_output(&txout);
464                        remaining_outputs -= 1;
465
466                        if remaining_outputs == 0 {
467                            if let Some(segwit_inputs) = self.segwit_inputs {
468                                self.parser_state =
469                                    ParserState::ReadingWitnesses(segwit_inputs, 0, 0);
470                            } else {
471                                self.parser_state = ParserState::ReadingLockTime;
472                            }
473                        } else {
474                            self.parser_state = ParserState::ReadingOutputs(remaining_outputs);
475                        }
476                    }
477                }
478                ParserState::ReadingLockTime => {
479                    if self.buffer.len() >= 4 {
480                        let locktime = LockTime::consensus_decode(&mut self.buffer)?;
481                        locktime.consensus_encode(&mut self.hasher)?;
482                        // this also resets the hasher
483                        let engine = mem::take(&mut self.hasher);
484                        let txid = Txid::from_engine(engine);
485                        let txid_hash = txid.as_raw_hash();
486                        self.merkle.add(TxMerkleNode::from_raw_hash(txid_hash.clone()));
487                        listener.on_transaction_end(locktime, txid);
488                        self.remaining_txs -= 1;
489                        if self.remaining_txs == 0 {
490                            self.parser_state = ParserState::FinishedBlock;
491                            listener.on_block_end();
492                        } else {
493                            self.parser_state = ParserState::ReadingTransactionHeader;
494                        }
495                    } else {
496                        break;
497                    }
498                }
499                ParserState::FinishedBlock => {
500                    // ensure that we have consumed all bytes from the buffer
501                    if self.buffer.is_empty() {
502                        break;
503                    } else {
504                        return Err(Error::TrailingData);
505                    }
506                }
507            }
508        }
509
510        Ok(self.buffer.len() != initial_buffer_len)
511    }
512}
513
514/// Test utilities
515#[cfg(feature = "std")]
516#[allow(missing_docs)]
517pub mod test_util {
518    use super::*;
519
520    pub struct MockListener {
521        pub block_headers: Vec<BlockHeader>,
522        pub transaction_versions: Vec<i32>,
523        pub transaction_inputs: Vec<TxIn>,
524        pub transaction_outputs: Vec<TxOut>,
525        pub transaction_locktimes: Vec<LockTime>,
526        pub transaction_ids: Vec<Txid>,
527        pub output_index: usize,
528    }
529
530    impl MockListener {
531        pub fn new() -> Self {
532            Self {
533                block_headers: Vec::new(),
534                transaction_versions: Vec::new(),
535                transaction_inputs: Vec::new(),
536                transaction_outputs: Vec::new(),
537                transaction_locktimes: Vec::new(),
538                transaction_ids: Vec::new(),
539                output_index: 0,
540            }
541        }
542    }
543
544    impl Listener for MockListener {
545        fn on_block_start(&mut self, header: &BlockHeader) {
546            self.block_headers.push(header.clone());
547        }
548
549        fn on_transaction_start(&mut self, version: i32) {
550            trace!("on_transaction_start({})", version);
551            self.output_index = 0;
552            self.transaction_versions.push(version);
553        }
554
555        fn on_transaction_input(&mut self, txin: &TxIn) {
556            trace!("on_transaction_input: {:?}", txin);
557            // fail fast
558            assert_eq!(txin.sequence, Sequence::default());
559            self.transaction_inputs.push(txin.clone());
560        }
561
562        fn on_transaction_output(&mut self, txout: &TxOut) {
563            trace!("on_transaction_output: {:?}", txout);
564            // fail fast
565            assert_eq!(txout.value.to_sat(), self.output_index as u64);
566
567            self.output_index += 1;
568            self.transaction_outputs.push(txout.clone());
569        }
570
571        fn on_transaction_end(&mut self, locktime: LockTime, txid: Txid) {
572            self.transaction_locktimes.push(locktime);
573            self.transaction_ids.push(txid);
574        }
575
576        fn on_block_end(&mut self) {}
577    }
578}
579
580#[cfg(test)]
581mod tests {
582    use alloc::vec::Vec;
583    use bitcoin::block::Version as BlockVersion;
584    use bitcoin::transaction::Version;
585    use bitcoin::consensus::{serialize, Encodable};
586    use bitcoin::hashes::Hash;
587    use bitcoin::{Block, BlockHash, CompactTarget, Transaction, Witness};
588    use test_log::test;
589
590    use super::test_util::MockListener;
591    use super::*;
592
593    #[test]
594    fn test_decode_block_with_no_inputs() {
595        let mut listener = MockListener::new();
596        let mut decoder = BlockDecoder::new();
597        // create a block with one transaction
598        let mut block = Block {
599            header: BlockHeader {
600                version: BlockVersion::ONE,
601                prev_blockhash: BlockHash::all_zeros(),
602                merkle_root: TxMerkleNode::all_zeros(),
603                time: 0,
604                bits: CompactTarget::from_consensus(0),
605                nonce: 0,
606            },
607            txdata: vec![Transaction {
608                version: Version::ONE,
609                lock_time: LockTime::from_consensus(0),
610                input: vec![],
611                output: vec![TxOut {
612                    value: Amount::ZERO,
613                    script_pubkey: ScriptBuf::from(vec![0x33, 0x44]),
614                }],
615            }],
616        };
617        block.header.merkle_root = block.compute_merkle_root().unwrap();
618        let mut block_bytes = Vec::new();
619        block.consensus_encode(&mut block_bytes).unwrap();
620        trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
621        decoder.decode_next(&block_bytes, &mut listener).unwrap();
622        decoder.finish().unwrap();
623        assert_eq!(listener.transaction_ids.len(), 1);
624        assert_eq!(listener.transaction_inputs.len(), 0);
625    }
626
627    #[test]
628    fn test_decode_block() {
629        let mut listener = MockListener::new();
630        let mut decoder = BlockDecoder::new();
631        // create a block with one transaction
632        let mut block = Block {
633            header: BlockHeader {
634                version: BlockVersion::ONE,
635                prev_blockhash: BlockHash::all_zeros(),
636                merkle_root: TxMerkleNode::all_zeros(),
637                time: 0,
638                bits: CompactTarget::from_consensus(0),
639                nonce: 0,
640            },
641            txdata: vec![Transaction {
642                version: Version::ONE,
643                lock_time: LockTime::from_consensus(0),
644                input: vec![TxIn {
645                    previous_output: OutPoint {
646                        txid: Txid::from_slice(&[0x33u8; 32]).unwrap(),
647                        vout: 0x44,
648                    },
649                    script_sig: ScriptBuf::from(vec![0x11, 0x22]),
650                    sequence: Default::default(),
651                    witness: Default::default(),
652                }],
653                output: vec![TxOut {
654                    value: Amount::ZERO,
655                    script_pubkey: ScriptBuf::from(vec![0x33, 0x44]),
656                }],
657            }],
658        };
659        block.header.merkle_root = block.compute_merkle_root().unwrap();
660
661        let mut block_bytes = Vec::new();
662        block.consensus_encode(&mut block_bytes).unwrap();
663        trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
664        decoder.decode_next(&block_bytes, &mut listener).unwrap();
665        decoder.finish().unwrap();
666        assert_eq!(listener.block_headers.len(), 1);
667        assert_eq!(listener.block_headers[0], block.header);
668
669        assert_eq!(listener.transaction_versions.len(), 1);
670        assert_eq!(listener.transaction_inputs.len(), 1);
671        assert_eq!(
672            listener.transaction_inputs[0].script_sig.as_bytes(),
673            &[0x11, 0x22]
674        );
675        assert_eq!(listener.transaction_outputs.len(), 1);
676        assert_eq!(
677            listener.transaction_outputs[0].script_pubkey.as_bytes(),
678            &[0x33, 0x44]
679        );
680        assert_eq!(listener.transaction_locktimes.len(), 1);
681
682        assert_eq!(listener.transaction_ids.len(), 1);
683        assert_eq!(listener.transaction_ids[0], block.txdata[0].compute_txid());
684
685        // parse a segwit block
686        let mut listener = MockListener::new();
687        let mut decoder = BlockDecoder::new();
688        block.txdata[0].input[0].witness =
689            Witness::from_slice(&vec![vec![0x11, 0x22], vec![], vec![0x33, 0x44]]);
690        let mut block_bytes = Vec::new();
691        block.consensus_encode(&mut block_bytes).unwrap();
692        trace!("block: {}\n{:#?}", hex::encode(block_bytes.clone()), block);
693        let ser = serialize(&block.txdata[0]);
694        Transaction::consensus_decode(&mut &ser[..]).unwrap();
695        trace!("tx: {}\n{:#?}", hex::encode(ser), block.txdata[0]);
696
697        decoder.decode_next(&block_bytes, &mut listener).unwrap();
698        decoder.finish().unwrap();
699        assert_eq!(listener.transaction_ids.len(), 1);
700        assert_eq!(listener.transaction_ids[0], block.txdata[0].compute_txid());
701    }
702}