diff --git a/src/blockstack_cli.rs b/src/blockstack_cli.rs index da1fb5e5f..20cb772ce 100644 --- a/src/blockstack_cli.rs +++ b/src/blockstack_cli.rs @@ -72,6 +72,7 @@ This CLI has these methods: decode-header used to decode a hex-encoded Stacks header into a human-readable representation decode-block used to decode a hex-encoded Stacks block into a human-readable representation decode-microblock used to decode a hex-encoded Stacks microblock into a human-readable representation + decode-microblocks used to decode a hex-encoded stream of Stacks microblocks into a human-readable representation For usage information on those methods, call `blockstack-cli [method] -h` @@ -174,6 +175,15 @@ raw binary microblock will be read from stdin. N.B. Stacks microblocks are not stored as files in the Stacks chainstate -- they are stored in block's sqlite database."; +const DECODE_MICROBLOCKS_USAGE: &str = "blockstack-cli (options) decode-microblocks [microblocks-path-or-stdin] + +The decode-microblocks command decodes a serialized list of Stacks microblocks and prints it to stdout as JSON. +The microblocks, if given, must be a hex string. Alternatively, you may pass `-` instead, and the +raw binary microblocks will be read from stdin. + +N.B. Stacks microblocks are not stored as files in the Stacks chainstate -- they are stored in +block's sqlite database."; + #[derive(Debug)] enum CliError { ClarityRuntimeError(RuntimeErrorType), @@ -787,6 +797,45 @@ fn decode_microblock(args: &[String], _version: TransactionVersion) -> Result Result { + if (args.len() >= 1 && args[0] == "-h") || args.len() != 1 { + return Err(CliError::Message(format!( + "Usage: {}\n", + DECODE_MICROBLOCKS_USAGE + ))); + } + let mblock_data = if args[0] == "-" { + // read from stdin + let mut block_str = Vec::new(); + io::stdin() + .read_to_end(&mut block_str) + .expect("Failed to read block from stdin"); + block_str + } else { + // given as a command-line arg + hex_bytes(&args[0].clone()).expect("Failed to decode microblock: must be a hex string") + }; + + let mut cursor = io::Cursor::new(&mblock_data); + let mut debug_cursor = LogReader::from_reader(&mut cursor); + + match Vec::::consensus_deserialize(&mut debug_cursor) { + Ok(blocks) => { + Ok(serde_json::to_string(&blocks).expect("Failed to serialize microblock to JSON")) + } + Err(e) => { + let mut ret = String::new(); + ret.push_str(&format!("Failed to decode microblocks: {:?}\n", &e)); + ret.push_str("Bytes consumed:\n"); + for buf in debug_cursor.log().iter() { + ret.push_str(&format!(" {}\n", to_hex(buf))); + } + ret.push_str("\n"); + Ok(ret) + } + } +} + fn main() { let mut argv: Vec = env::args().collect(); @@ -830,6 +879,7 @@ fn main_handler(mut argv: Vec) -> Result { "decode-header" => decode_header(args, tx_version), "decode-block" => decode_block(args, tx_version), "decode-microblock" => decode_microblock(args, tx_version), + "decode-microblocks" => decode_microblocks(args, tx_version), _ => Err(CliError::Usage), } } else {