@@ -24,13 +24,13 @@ extern crate blockstack_lib;
2424use blockstack_lib:: address:: AddressHashMode ;
2525use blockstack_lib:: burnchains:: Address ;
2626use blockstack_lib:: chainstate:: stacks:: {
27- StacksAddress , StacksPrivateKey , StacksPublicKey , StacksTransaction , StacksTransactionSigner ,
27+ StacksAddress , StacksBlock , StacksPrivateKey , StacksPublicKey , StacksTransaction , StacksTransactionSigner ,
2828 TokenTransferMemo , TransactionAuth , TransactionContractCall , TransactionPayload ,
2929 TransactionSmartContract , TransactionSpendingCondition , TransactionVersion ,
3030 C32_ADDRESS_VERSION_MAINNET_SINGLESIG , C32_ADDRESS_VERSION_TESTNET_SINGLESIG ,
3131} ;
3232use blockstack_lib:: net:: { Error as NetError , StacksMessageCodec } ;
33- use blockstack_lib:: util:: { hash:: hex_bytes, hash:: to_hex, log, strings:: StacksString } ;
33+ use blockstack_lib:: util:: { hash:: hex_bytes, hash:: to_hex, log, strings:: StacksString , retry :: LogReader } ;
3434use blockstack_lib:: vm;
3535use blockstack_lib:: vm:: {
3636 errors:: { Error as ClarityError , RuntimeErrorType } ,
@@ -116,6 +116,18 @@ The addresses command calculates both the Bitcoin and Stacks addresses from a se
116116If successful, this command outputs both the Bitcoin and Stacks addresses to stdout, formatted
117117as JSON, and exits with code 0" ;
118118
119+ const DECODE_TRANSACTION_USAGE : & str = "blockstack-cli (options) decode-tx [transaction-hex-or-stdin]
120+
121+ The decode-tx command decodes a serialized Stacks transaction and prints it to stdout as JSON.
122+ The transaction, if given, must be a hex string. Alternatively, you may pass - instead, and the
123+ raw binary transaction will be read from stdin" ;
124+
125+ const DECODE_BLOCK_USAGE : & str = "blockstack-cli (options) decode-block [block-path-or-stdin]
126+
127+ The decode-tx command decodes a serialized Stacks block and prints it to stdout as JSON.
128+ The block, if given, must be a hex string. Alternatively, you may pass - instead, and the
129+ raw binary block will be read from stdin" ;
130+
119131#[ derive( Debug ) ]
120132enum CliError {
121133 ClarityRuntimeError ( RuntimeErrorType ) ,
@@ -518,6 +530,81 @@ fn get_addresses(args: &[String], version: TransactionVersion) -> Result<String,
518530 ) )
519531}
520532
533+ fn decode_transaction ( args : & [ String ] , _version : TransactionVersion ) -> Result < String , CliError > {
534+ if ( args. len ( ) >= 1 && args[ 0 ] == "-h" ) || args. len ( ) != 1 {
535+ return Err ( CliError :: Message ( format ! ( "Usage: {}\n " , DECODE_TRANSACTION_USAGE ) ) ) ;
536+ }
537+
538+ let tx_str =
539+ if args[ 0 ] == "-" {
540+ // read from stdin
541+ let mut tx_str = Vec :: new ( ) ;
542+ io:: stdin ( ) . read_to_end ( & mut tx_str) . expect ( "Failed to read transaction from stdin" ) ;
543+ tx_str
544+ }
545+ else {
546+ // given as a command-line arg
547+ hex_bytes ( & args[ 0 ] . clone ( ) )
548+ . expect ( "Failed to decode transaction: must be a hex string" )
549+ } ;
550+
551+ let mut cursor = io:: Cursor :: new ( & tx_str) ;
552+ let mut debug_cursor = LogReader :: from_reader ( & mut cursor) ;
553+
554+ match StacksTransaction :: consensus_deserialize ( & mut debug_cursor) {
555+ Ok ( tx) => {
556+ Ok ( serde_json:: to_string ( & tx) . expect ( "Failed to serialize transaction to JSON" ) )
557+ } ,
558+ Err ( e) => {
559+ let mut ret = String :: new ( ) ;
560+ ret. push_str ( & format ! ( "Failed to decode transaction: {:?}\n " , & e) ) ;
561+ ret. push_str ( "Bytes consumed:\n " ) ;
562+ for buf in debug_cursor. log ( ) . iter ( ) {
563+ ret. push_str ( & format ! ( " {}" , to_hex( buf) ) ) ;
564+ }
565+ ret. push_str ( "\n " ) ;
566+ Ok ( ret)
567+ }
568+ }
569+ }
570+
571+ fn decode_block ( args : & [ String ] , _version : TransactionVersion ) -> Result < String , CliError > {
572+ if ( args. len ( ) >= 1 && args[ 0 ] == "-h" ) || args. len ( ) != 1 {
573+ return Err ( CliError :: Message ( format ! ( "Usage: {}\n " , DECODE_BLOCK_USAGE ) ) ) ;
574+ }
575+ let block_data =
576+ if args[ 0 ] == "-" {
577+ // read from stdin
578+ let mut block_str = Vec :: new ( ) ;
579+ io:: stdin ( ) . read_to_end ( & mut block_str) . expect ( "Failed to read block from stdin" ) ;
580+ block_str
581+ }
582+ else {
583+ // given as a command-line arg
584+ hex_bytes ( & args[ 0 ] . clone ( ) )
585+ . expect ( "Failed to decode block: must be a hex string" )
586+ } ;
587+
588+ let mut cursor = io:: Cursor :: new ( & block_data) ;
589+ let mut debug_cursor = LogReader :: from_reader ( & mut cursor) ;
590+
591+ match StacksBlock :: consensus_deserialize ( & mut debug_cursor) {
592+ Ok ( block) => {
593+ Ok ( serde_json:: to_string ( & block) . expect ( "Failed to serialize block to JSON" ) )
594+ } ,
595+ Err ( e) => {
596+ let mut ret = String :: new ( ) ;
597+ ret. push_str ( & format ! ( "Failed to decode block: {:?}\n " , & e) ) ;
598+ ret. push_str ( "Bytes consumed:\n " ) ;
599+ for buf in debug_cursor. log ( ) . iter ( ) {
600+ ret. push_str ( & format ! ( " {}" , to_hex( buf) ) ) ;
601+ }
602+ ret. push_str ( "\n " ) ;
603+ Ok ( ret)
604+ }
605+ }
606+ }
607+
521608fn main ( ) {
522609 log:: set_loglevel ( log:: LOG_DEBUG ) . unwrap ( ) ;
523610 let mut argv: Vec < String > = env:: args ( ) . collect ( ) ;
@@ -556,6 +643,8 @@ fn main_handler(mut argv: Vec<String>) -> Result<String, CliError> {
556643 "token-transfer" => handle_token_transfer ( args, tx_version, chain_id) ,
557644 "generate-sk" => generate_secret_key ( args, tx_version) ,
558645 "addresses" => get_addresses ( args, tx_version) ,
646+ "decode-tx" => decode_transaction ( args, tx_version) ,
647+ "decode-block" => decode_block ( args, tx_version) ,
559648 _ => Err ( CliError :: Usage ) ,
560649 }
561650 } else {
@@ -830,4 +919,26 @@ mod test {
830919 assert ! ( result. contains( "mzGHS7KN25DEtXipGxjo1tFebb7Fw5aAkp" ) ) ;
831920 assert ! ( result. contains( "ST36T883PDD2EK4PHVTA5GFHC8NQW6558XJQX6Q3K" ) ) ;
832921 }
922+
923+ #[ test]
924+ fn simple_decode_tx ( ) {
925+ let tx_args = [
926+ "decode-tx" ,
927+ "8080000000040021a3c334fc0ee50359353799e8b2605ac6be1fe4000000000000000100000000000000000100c90ae0235365f3a73c595f8c6ab3c529807feb3cb269247329c9a24218d50d3f34c7eef5d28ba26831affa652a73ec32f098fec4bf1decd1ceb3fde4b8ce216b030200000000021a21a3c334fc0ee50359353799e8b2605ac6be1fe40573746f7265096765742d76616c7565000000010d00000003666f6f"
928+ ] ;
929+
930+ let result = main_handler ( to_string_vec ( & tx_args) ) . unwrap ( ) ;
931+ eprintln ! ( "result:\n {}" , result) ;
932+ }
933+
934+ #[ test]
935+ fn simple_decode_block ( ) {
936+ let block_args = [
937+ "decode-block" ,
938+ "000000000000395f800000000000000179cb51f6bbd6d90cb257616e77a495919667c3772dd08ea7c4f5c372739490bc91da6609c5c95c96f612dbc8cab2f7a0d8bfb83abdb630167579ccc36b66c03c1d0d250cd3b3615c03afcdaef313dbd30d3d5b0fd10ed5acbc35d042abfba66cdfc32881c5a665ad9685a2eb6e0c131fb400000000000000000000000000000000000000000000000000000000000000000000e87f28593f66d77ae3c57abd4e5ae0e632b837b2596be14c2b2572cd4d0015229976eb5c4a5b08816b31f485513d2e6501f6cd29ee240a2c4056b1f7cc32c2e118ef6499e0fcc575da75fca8cc409e5c884eb3450000000180800000000400403e2ff80a8a8ecacfb827dcf6adddd21fdd4c3c000000000000017800000000000000000000f3f497268f8a12e318f96ba4f1ad3ed2485e87cefe75b88bf735bb1bbb7db754746e6a244ba869183a2ab73002c6465936b7d9b059ffc5a94488bee7b5afb33c010200000000040000000000000000000000000000000000000000000000000000000000000000" ,
939+ ] ;
940+
941+ let result = main_handler ( to_string_vec ( & block_args) ) . unwrap ( ) ;
942+ eprintln ! ( "result:\n {}" , result) ;
943+ }
833944}
0 commit comments