//! Test if the JSON-RPC requests can be serialized and responses can be //! deserialized. //! //! We want to ensure that users can use this crate to build RPC clients, so //! this is an integration test to ensure only the public API is accessed. mod vectors; use vectors::{ GET_BLOCKCHAIN_INFO_RESPONSE, GET_BLOCK_RESPONSE_1, GET_BLOCK_RESPONSE_2, GET_BLOCK_TEMPLATE_RESPONSE_TEMPLATE, GET_RAW_TRANSACTION_RESPONSE_TRUE, }; use zebra_chain::subtree::NoteCommitmentSubtreeIndex; use zebra_rpc::methods::{ trees::{GetSubtrees, GetTreestate, SubtreeRpcData}, types::{ get_block_template::{self, GetBlockTemplate}, get_mining_info, get_raw_mempool::GetRawMempool, peer_info::PeerInfo, submit_block, subsidy::BlockSubsidy, transaction::TransactionObject, unified_address, validate_address, z_validate_address, }, AddressBalance, AddressStrings, GetAddressTxIdsRequest, GetAddressUtxos, GetBlock, GetBlockChainInfo, GetBlockHash, GetBlockHeader, GetBlockHeaderObject, GetBlockHeightAndHash, GetInfo, GetRawTransaction, SentTransactionHash, }; #[test] fn test_get_info() -> Result<(), Box> { let json = r#" { "version": 2030010, "build": "v2.3.0+10.gc66d6ca.modified", "subversion": "/Zebra:2.3.0/", "protocolversion": 170120, "blocks": 2930822, "connections": 75, "difficulty": 68556523.91969073, "testnet": false, "paytxfee": 0.0, "relayfee": 1e-6, "errors": "no errors", "errorstimestamp": "2025-05-20 19:33:53.395307694 UTC" }"#; let obj: GetInfo = serde_json::from_str(json)?; let ( version, build, subversion, protocol_version, blocks, connections, proxy, difficulty, testnet, pay_tx_fee, relay_fee, errors, errors_timestamp, ) = obj.clone().into_parts(); let new_obj = GetInfo::from_parts( version, build, subversion, protocol_version, blocks, connections, proxy, difficulty, testnet, pay_tx_fee, relay_fee, errors, errors_timestamp, ); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_blockchain_info() -> Result<(), Box> { let json = GET_BLOCKCHAIN_INFO_RESPONSE; let _obj: GetBlockChainInfo = serde_json::from_str(json)?; // TODO: add new() and getters for all fields Ok(()) } #[test] fn test_get_address_balance() -> Result<(), Box> { // Test request let json = r#"{"addresses":["t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak"]}"#; let obj = AddressStrings::new_valid(vec![String::from("t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak")])?; let new_json = serde_json::to_string(&obj)?; assert_eq!(json, new_json); // Test response let json = r#" { "balance": 11290259389 } "#; let obj: AddressBalance = serde_json::from_str(json)?; let new_obj = AddressBalance { balance: obj.balance, }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_send_raw_transaction() -> Result<(), Box> { let json = r#""0000000001695b61dd5c82ae33a326126d6153d1641a3a1759d3f687ea377148""#; let obj: SentTransactionHash = serde_json::from_str(json)?; let hash = obj.inner(); let new_obj = SentTransactionHash::new(hash); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_block_0() -> Result<(), Box> { let json = r#""00000000007bacdb373ca240dc6f044f0a816a407bc1924f82a2d84ebfa6103f""#; let _r: GetBlock = serde_json::from_str(json)?; // TODO: change GetBlock::Block to have a struct, add getters to it Ok(()) } #[test] fn test_get_block_1() -> Result<(), Box> { let json = GET_BLOCK_RESPONSE_1; let _r: GetBlock = serde_json::from_str(json)?; // TODO: change GetBlock::Block to have a struct, add getters to it Ok(()) } #[test] fn test_get_block_2() -> Result<(), Box> { let json = GET_BLOCK_RESPONSE_2; let _r: GetBlock = serde_json::from_str(json)?; // TODO: change GetBlock::Block to have a struct, add getters to it Ok(()) } #[test] fn test_get_block_header() -> Result<(), Box> { let json = r#" { "hash": "0000000001695b61dd5c82ae33a326126d6153d1641a3a1759d3f687ea377148", "confirmations": 47, "height": 2930583, "version": 4, "merkleroot": "4097b67ba0aa552538ed3fce670c756f22452f0273095f10cd693912551ebe3a", "blockcommitments": "cdf618b251ca2353360d06dc3efd9f16fb45d95d2692e69b2adffa26bf2db884", "finalsaplingroot": "35a0acf56d25f4e282d345e5a546331487b13a663f0b1f745088d57f878e9d6d", "time": 1747751624, "nonce": "7ddc00a80000000000000000000a00000000000000000000000000003e1e6cd7", "solution": "0038e90b8de2fd3fc1b62218e6caeb60f20d38c0ad38d6dd05176996455c5a54fef2f99eee4fe5b887e808da827951cc9e5adb73542891d451e147f4746eb70bd34a4a2ec5ecfa8fce87ae10e8c55b8b3ffe76e40b56057d714637ac33e6434e849f3bf21aeb14bf3e1b4336eb39493110c5f0ac63d272733fa94f9e7da529fe0c8c436f9c0feb49031a20c8310a419ab670d732cce9fceda95911f8e646ef64fe6462bb449fe2fc053ca4358d8495ee254644a530b1e59dd025d9a2ce131ec187805c1cbbef9362bda8dcaed1ec8697ab570806e1e0ff0b3f1cf891a086664d0efca6127244db1b564dfa960a8527e08029cef05aa71ac10e9923620d6719702685d27938c2910f385d18368f54b588f3129c55e9f9d27e46d563a190deb39dbc877d771ad213559232280a55d4a0f9513e38ba4f6973096bd3811cd70ee63613bdb4dec033a1aeb9b5b6c1f3b96d080082c9c6e683e7f72be7c834fef1dec64c4b75b30730ff374b00968c51d7e093d3867c503e2dce7faf220249d037e49202b5a7de013474e956c61b5e7526ff35637cbfd86abef37406f3a50ec1168ddb8b5ad96c08503de5d75cae433ae4b504f6e995858640151454460e9b2ee669a44969779592682ca56e4e10d60aae11818b708b19db8593e59389d1ff50359d13f67a311d2565749d20724f239407beabf6790e54479cd5d2015e0903f94f0043ac7484c61936832d7fdf7b13de0579969a795149f77eb1a6961461b6c33b9bbcdfd203c706bf634dc1f7bb6841aebaae01e492ef69fca14996eacc9ef54947dfc268b25a74f52e46f2f504d9105d51e6619d224b0e7b47ca0dbeeece2e04552b123056be9d383cb9a1f5cc75ab8c5aa76dc2709cec58108e4df4e74a5ee2dc299192ddc4ecb4e19a7df843138157422d610c690c34a33ae6ccf16d493711827900d82c1366cdb1e147b5d4fc2b4d5fd32ef95eaa4406bd7d52dec5ee30e258311336c27b4e7069faedd608f86cc239cd62006c03923df66d362ca5203026e4780d277f13e73b2163a04858c3c413de5e9c5470c90e59e6d7b391cd85a59cc47a68f5e95ada981eba3d35878435e39c23599efb53a411b6397d062b4e4f9b0f423d2b8ad7a0e2fdbe8489374f23193882bd473a53ac542d81e81dc9eb2b661ca9d6816e242bffb83a00dc6f70a511b469a75271458ef43a66b1ab7b43163fd3ddc0c1d24239d176db980fe5e316fc127adbd005253897ea0867306dc0811a3ea87cd049236e3b5f4ee58bb310ecf7039f33eabaf6e091ff682c9bb6740e0c3171bf7025cba3587827cc5008fb2d6a5cb83c1ba48d58718c4f42f506b4794ffe0721411738bd671d12d20c3a08c9e06c27258f0bd7d295b46fbfc53f48bdcdd7be62cb87a437b9865be5ca6fb6155e7e6801a73a8b335432d303fc22c5a7a27484f46936fe7124a1a363f90fd924a08e540968ecdc71c6f11ddc8a2aa9161c8b532984c911f4e780474785d296b02e4d2d12f9c4c46b735f79c3c9351ef5bebea2a65b48eb0747384a31d7e6c9d3a0c2507cef7df8971fd541570a3174b74ec91401acb5b45f105e8b25dd407c745d08da0cc4d5c88dd33bd3c2876c2af6a4f110c8867638e6dc6e72b3b0ddb37ef6aa4dedbb7dca039a0e08049502e526c8f72121a68ae5385bad3b5bd59efadc0b8882cccad2634937da612098e760c4f9510fcf311517d4ae2c4e0e8f081354194329b42d3a2c0c93924aa985a9b99598377a98489881e83b5eb3f155ca120a28d4bfd2d43d01a6dd368d52626905f26cb3ff9c0d5b98a9796172e54fd1f2b7dc7851fd3c9e191abd14e96c8781c6453f33a198797ee50f02682a7c2a7829420e0b40fe787dfc7f32ce05df3a3a86fc59700e", "bits": "1c023081", "difficulty": 61301397.633212306, "previousblockhash": "0000000000d12367f80be78e624d263faa6e6fda718453cbb6f7dc71205af574", "nextblockhash": "0000000001d8a2a9c19bc98ecb856c8406ba0b2d7d42654369014e2a14dd9c1d" } "#; let r: GetBlockHeader = serde_json::from_str(json)?; let GetBlockHeader::Object(obj) = &r else { panic!("Expected Object variant"); }; let new_obj = GetBlockHeader::Object(Box::new(GetBlockHeaderObject { hash: obj.hash, confirmations: obj.confirmations, height: obj.height, version: obj.version, merkle_root: obj.merkle_root, block_commitments: obj.block_commitments, final_sapling_root: obj.final_sapling_root, sapling_tree_size: obj.sapling_tree_size, time: obj.time, nonce: obj.nonce, solution: obj.solution, bits: obj.bits, difficulty: obj.difficulty, previous_block_hash: obj.previous_block_hash, next_block_hash: obj.next_block_hash, })); assert_eq!(r, new_obj); Ok(()) } #[test] fn test_get_block_height_hash() -> Result<(), Box> { let json = r#" { "height": 2931705, "hash": [35, 5, 244, 118, 21, 236, 8, 168, 3, 119, 95, 171, 238, 9, 233, 152, 250, 106, 153, 253, 6, 176, 155, 7, 155, 161, 146, 1, 0, 0, 0, 0] } "#; let obj: GetBlockHeightAndHash = serde_json::from_str(json)?; let new_obj = GetBlockHeightAndHash { height: obj.height, hash: obj.hash, }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_raw_mempool_false() -> Result<(), Box> { let json = r#" [ "77ec13dde45185e99dba408d592c5b30438e8c71af5b6e2d9f4d29cb4da8ccbf" ] "#; let obj: GetRawMempool = serde_json::from_str(json)?; let GetRawMempool::TxIds(txids) = &obj else { panic!("Expected TxIds variant"); }; let new_obj = GetRawMempool::TxIds(txids.clone()); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_raw_mempool_true() -> Result<(), Box> { let json = r#" { "05cef70f5ed2467bb657664fe9837cdb0490b9cd16780f05ced384fd2c7dc2b2": { "size": 9165, "fee": 0.0001, "modifiedfee": 0.0001, "time": 1747836987, "height": 2931716, "descendantcount": 1, "descendantsize": 9165, "descendantfees": 10000, "depends": [ ] }, "d1e0c4f9c5f19c86aec3df7744aed7a88bc47edd5c95dd4e502b889ea198c701": { "size": 1374, "fee": 0.0002, "modifiedfee": 0.0002, "time": 1747836995, "height": 2931716, "descendantcount": 1, "descendantsize": 1374, "descendantfees": 20000, "depends": [ ] } } "#; let obj: GetRawMempool = serde_json::from_str(json)?; let GetRawMempool::Verbose(mempool_map) = &obj else { panic!("Expected Verbose variant"); }; // TODO: add new()/getters to MempoolObject and test them let new_obj = GetRawMempool::Verbose(mempool_map.clone()); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_z_get_treestate() -> Result<(), Box> { let json = r#" { "hash": "000000000154f210e2451c45a192c69d12c0db18a427be13be3913e0feecd6f6", "height": 2931720, "time": 1747837185, "sapling": { "commitments": { "finalState": "01f84e35f84dfd9e53effcd74f98e9271b4df9c15e1681b7dc4f9a971e5c98531e001f0105354e35c5daa8831b957f6f702affaa835bc3758e9bd323aafeead50ddfa561000001157a4438a622a0677ec9d1099bf963614a0a65b1e24ea451c9f55eef64c62b650001a5fc8bf61968a934693b7b9a4abd894c4e4a1bd265525538f4877687504fe50a000193d7f432e23c862bf2f831392861199ab4c70d358d82695b6bf8fa9eb36b6b63000184585eb0d4f116b07b9bd359c461a499716a985a001201c66d1016e489a5672f01aad38587c7f2d5ebd1c2eea08a0660e9a9fd1a104b540767c2884354a48f0a6d01ff10064c6bf9aba73d638878a63c31de662f25aea58dc0033a3ada3d0a695b54000001060af6a6c1415a6eaf780073ffa3d0ab35af7bb391bccc4e6ea65a1230dad83001ab58f1ebb2860e257c50350a3e1b54778b7729bdd11eacaa9213c4b5f4dbb44c00017d1ce2f0839bdbf1bad7ae37f845e7fe2116e0c1197536bfbad549f3876c3c590000013e2598f743726006b8de42476ed56a55a75629a7b82e430c4e7c101a69e9b02a011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e0000000000" } }, "orchard": { "commitments": { "finalState": "01a110b4b3e1932f4e32e972d34ba5b9128a21b5dec5540dbb50d6f6eabd462237001f01206c514069d4cb68fb0a4d5dfe6eb7a31bcf399bf38a3bd6751ebd4b68cec3130001a73e87cab56a4461a676c7ff01ccbf8d15bbb7d9881b8f991322d721d02ded0a0001bc5a28c4a9014698c66a496bd35aa19c1b5ffe7b511ce8ff26bdcbe6cf0caa0c01ad5ba4f75b9685f7b4e1f47878e83d5bcd888b24359e4a3f2309b738c0211c1e01f12bdfe8eebc656f4f4fefc61ebd8a0b581a10b5cb3c4d8681f26384f907d910000158c6fbe19bb748e830a55b80fc62b414a3763efd461bb1885c10bebf9cee86130101683a742a4b5b3d7e0e802239d70cd480cc56eeaefac844359aa2c32dc41d3700000001756e99d87177e232e3c96f03e412d8bf3547a0fea00434ba153c7dac9990322d016211c99d795da43b33a1397859ae9745bc3e74966fa68b725ce3c90dca2d11300000012d113bc8f6a4f41b3963cfa0717176c2d31ce7bfae4d250a1fff5e061dd9d3250160040850b766b126a2b4843fcdfdffa5d5cab3f53bc860a3bef68958b5f066170001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000" } } } "#; let obj: GetTreestate = serde_json::from_str(json)?; let (hash, height, time, sapling_final_state, orchard_final_state) = obj.clone().into_parts(); let new_obj = GetTreestate::from_parts(hash, height, time, sapling_final_state, orchard_final_state); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_z_get_subtrees_by_index() -> Result<(), Box> { let json = r#" { "pool": "orchard", "start_index": 0, "subtrees": [ { "root": "d4e323b3ae0cabfb6be4087fec8c66d9a9bbfc354bf1d9588b6620448182063b", "end_height": 1707429 } ] } "#; let obj: GetSubtrees = serde_json::from_str(json)?; let pool = obj.pool.clone(); let start_index = obj.start_index.0; let subtree_root = obj.subtrees[0].root.clone(); let subtree_end_height = obj.subtrees[0].end_height; let new_obj = GetSubtrees { pool, start_index: NoteCommitmentSubtreeIndex(start_index), subtrees: vec![SubtreeRpcData { root: subtree_root, end_height: subtree_end_height, }], }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_raw_transaction_true() -> Result<(), Box> { let json = GET_RAW_TRANSACTION_RESPONSE_TRUE; let obj: GetRawTransaction = serde_json::from_str(json)?; let GetRawTransaction::Object(tx) = &obj else { panic!("Expected GetRawTransaction::Object"); }; let hex = tx.hex.clone(); let height = tx.height; let confirmations = tx.confirmations; let inputs = tx.inputs.clone(); let outputs = tx.outputs.clone(); let shielded_spends = tx.shielded_spends.clone(); let shielded_outputs = tx.shielded_outputs.clone(); let orchard = tx.orchard.clone(); let value_balance = tx.value_balance; let value_balance_zat = tx.value_balance_zat; let size = tx.size; let time = tx.time; // TODO: add test for ShieldedSpend, ShieldedOutput, Orchard let new_obj = GetRawTransaction::Object(Box::new(TransactionObject { hex, height, confirmations, inputs, outputs, shielded_spends, shielded_outputs, orchard, value_balance, value_balance_zat, size, time, })); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_address_tx_ids() -> Result<(), Box> { // Test request only (response is trivial) let json = r#"{"addresses":["t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak"],"start":2931856,"end":2932856}"#; // TODO: allow not passing start or end let obj = GetAddressTxIdsRequest::from_parts( vec!["t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak".to_string()], 2931856, 2932856, ); let new_json = serde_json::to_string(&obj)?; assert_eq!(json, new_json); Ok(()) } #[test] fn test_get_address_utxos() -> Result<(), Box> { let json = r#" [ { "address": "t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak", "txid": "6ee3e8a86dfeca629aeaf794aacb714db1cf1868bc9fe487de443e6197d8764a", "outputIndex": 0, "script": "76a914ba92ff06081d5ff6542af8d3b2d209d29ba6337c88ac", "satoshis": 125000000, "height": 2931856 } ] "#; let obj: Vec = serde_json::from_str(json)?; let (address, txid, output_index, script, satoshis, height) = obj[0].clone().into_parts(); let new_obj = vec![GetAddressUtxos::from_parts( address, txid, output_index, script, satoshis, height, )]; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_block_hash() -> Result<(), Box> { let json = r#""0000000001695b61dd5c82ae33a326126d6153d1641a3a1759d3f687ea377148""#; let obj: GetBlockHash = serde_json::from_str(json)?; let hash = obj.0; let new_obj = GetBlockHash(hash); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_block_template_request() -> Result<(), Box> { let json = r#"{"mode":"template"}"#; // TODO: add new() method let new_obj = get_block_template::parameters::JsonParameters { mode: get_block_template::parameters::GetBlockTemplateRequestMode::Template, data: None, capabilities: vec![], long_poll_id: None, _work_id: None, }; let new_json = serde_json::to_string(&new_obj)?; assert_eq!(json, new_json); Ok(()) } #[test] fn test_get_block_template_response() -> Result<(), Box> { let json = GET_BLOCK_TEMPLATE_RESPONSE_TEMPLATE; let obj: get_block_template::Response = serde_json::from_str(json)?; let get_block_template::Response::TemplateMode(template) = &obj else { panic!("Expected get_block_template::Response::TemplateMode"); }; let capabilities = template.capabilities.clone(); let version = template.version; let previous_block_hash = template.previous_block_hash.0 .0; let block_commitments_hash: [u8; 32] = template.block_commitments_hash.into(); let light_client_root_hash: [u8; 32] = template.light_client_root_hash.into(); let final_sapling_root_hash: [u8; 32] = template.final_sapling_root_hash.into(); let default_roots = template.default_roots.clone(); // TODO: test all these types to ensure they can be read fully let transactions = template.transactions.clone(); let coinbase_txn = template.coinbase_txn.clone(); let long_poll_id = template.long_poll_id; let target = template.target; let min_time = template.min_time; let mutable = template.mutable.clone(); let nonce_range = template.nonce_range.clone(); let sigop_limit = template.sigop_limit; let size_limit = template.size_limit; let cur_time = template.cur_time; let bits = template.bits; let height = template.height; let max_time = template.max_time; let submit_old = template.submit_old; let new_obj = get_block_template::Response::TemplateMode(Box::new(GetBlockTemplate { capabilities, version, previous_block_hash: GetBlockHash(zebra_chain::block::Hash(previous_block_hash)), block_commitments_hash: block_commitments_hash.into(), light_client_root_hash: light_client_root_hash.into(), final_sapling_root_hash: final_sapling_root_hash.into(), default_roots, transactions, coinbase_txn, long_poll_id, target, min_time, mutable, nonce_range, sigop_limit, size_limit, cur_time, bits, height, max_time, submit_old, })); assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_submit_block() -> Result<(), Box> { let json = r#""duplicate""#; let obj: submit_block::Response = serde_json::from_str(json)?; assert_eq!( obj, submit_block::Response::ErrorResponse(submit_block::ErrorResponse::Duplicate) ); Ok(()) } #[test] fn test_get_mining_info() -> Result<(), Box> { let json = r#" { "blocks": 2934350, "currentblocksize": 1629, "currentblocktx": 0, "networksolps": 6644588130, "networkhashps": 6644588130, "chain": "main", "testnet": false } "#; let _obj: get_mining_info::Response = serde_json::from_str(json)?; // TODO: add getters Ok(()) } #[test] fn test_get_peer_info() -> Result<(), Box> { let json = r#" [ { "addr": "192.168.0.1:8233", "inbound": false }, { "addr": "[2000:2000:2000:0000::]:8233", "inbound": false } ] "#; let obj: Vec = serde_json::from_str(json)?; let addr0 = obj[0].addr; let inbound0 = obj[0].inbound; let addr1 = obj[1].addr; let inbound1 = obj[1].inbound; // TODO: allow getting IP from PeerInfo (change to SocketAddr?) // TODO: add getters, new() let new_obj = vec![ PeerInfo { addr: addr0, inbound: inbound0, }, PeerInfo { addr: addr1, inbound: inbound1, }, ]; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_validate_address() -> Result<(), Box> { let json = r#" { "isvalid": true, "address": "t1at7nVNsv6taLRrNRvnQdtfLNRDfsGc3Ak", "isscript": false } "#; let obj: validate_address::Response = serde_json::from_str(json)?; let is_valid = obj.is_valid; let address = obj.address.clone(); let is_script = obj.is_script; let new_obj = validate_address::Response { is_valid, address, is_script, }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_z_validate_address() -> Result<(), Box> { let json = r#" { "isvalid": true, "address": "u1l8xunezsvhq8fgzfl7404m450nwnd76zshscn6nfys7vyz2ywyh4cc5daaq0c7q2su5lqfh23sp7fkf3kt27ve5948mzpfdvckzaect2jtte308mkwlycj2u0eac077wu70vqcetkxf", "address_type": "unified", "ismine": false } "#; let obj: z_validate_address::Response = serde_json::from_str(json)?; let is_valid = obj.is_valid; let address = obj.address.clone(); let address_type = obj.address_type.clone(); let is_mine = obj.is_mine; let new_obj = z_validate_address::Response { is_valid, address, address_type, is_mine, }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_get_block_subsidy() -> Result<(), Box> { let json = r#" { "fundingstreams": [ { "recipient": "Zcash Community Grants NU6", "specification": "https://zips.z.cash/zip-1015", "value": 0.125, "valueZat": 12500000, "address": "t3cFfPt1Bcvgez9ZbMBFWeZsskxTkPzGCow" } ], "lockboxstreams": [ { "recipient": "Lockbox NU6", "specification": "https://zips.z.cash/zip-1015", "value": 0.1875, "valueZat": 18750000 } ], "miner": 1.25, "founders": 0.0, "fundingstreamstotal": 0.125, "lockboxtotal": 0.1875, "totalblocksubsidy": 1.5625 } "#; let obj: BlockSubsidy = serde_json::from_str(json)?; let funding_streams = obj.funding_streams.clone(); let lockbox_streams = obj.lockbox_streams.clone(); // TODO: check if FundingStream can be read and recreated let miner = obj.miner; let founders = obj.founders; let funding_streams_total = obj.funding_streams_total; let lockbox_total = obj.lockbox_total; let total_block_subsidy = obj.total_block_subsidy; // TODO: add getters, new() let new_obj = BlockSubsidy { funding_streams, lockbox_streams, miner, founders, funding_streams_total, lockbox_total, total_block_subsidy, }; assert_eq!(obj, new_obj); Ok(()) } #[test] fn test_z_list_unified_receivers() -> Result<(), Box> { let json = r#" { "sapling": "zs1mrhc9y7jdh5r9ece8u5khgvj9kg0zgkxzdduyv0whkg7lkcrkx5xqem3e48avjq9wn2rukydkwn", "p2pkh": "t1V9mnyk5Z5cTNMCkLbaDwSskgJZucTLdgW" } "#; // TODO: fix, allow deserializing with missing fields let _obj: unified_address::Response = serde_json::from_str(json)?; // TODO: add getters Ok(()) } #[test] fn test_generate() -> Result<(), Box> { let json = r#" [ "0000000001695b61dd5c82ae33a326126d6153d1641a3a1759d3f687ea377148", "0000000001695b61dd5c82ae33a326126d6153d1641a3a1759d3f687ea377149" ] "#; // TODO: fix, allow deserializing with missing fields let obj: Vec = serde_json::from_str(json)?; let hash0 = obj[0].0; let hash1 = obj[1].0; let new_obj = vec![GetBlockHash(hash0), GetBlockHash(hash1)]; assert_eq!(obj, new_obj); Ok(()) }