// Smoldot // Copyright (C) 2023 Pierre Krieger // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . use smoldot::json_rpc; use std::sync::Arc; async fn start_client() -> smoldot_full_node::Client { smoldot_full_node::start(smoldot_full_node::Config { chain: smoldot_full_node::ChainConfig { chain_spec: (&include_bytes!("./substrate-node-template.json")[..]).into(), additional_bootnodes: Vec::new(), keystore_memory: vec![], sqlite_database_path: None, sqlite_cache_size: 256 * 1024 * 1024, keystore_path: None, json_rpc_listen: None, }, relay_chain: None, libp2p_key: Box::new([0; 32]), listen_addresses: Vec::new(), tasks_executor: Arc::new(|task| smol::spawn(task).detach()), log_callback: Arc::new(move |_, _| {}), jaeger_agent: None, }) .await .unwrap() } #[test] fn chain_spec_v1_chain_name() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chainSpec_v1_chainName","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "Local Testnet" ); }); } #[test] fn chain_spec_v1_genesis_hash() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chainSpec_v1_genesisHash","params":[]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "0x6bf30d04495c16ef053de4ac74eac35dfd6473e4907810f450bea1b976ac518f" ); }); } #[test] fn chain_spec_v1_properties() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chainSpec_v1_properties","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!(result_json, r#"{"tokenDecimals": 15}"#); }); } #[test] fn chain_get_block_hash() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chain_getBlockHash","params":[0]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "0x6bf30d04495c16ef053de4ac74eac35dfd6473e4907810f450bea1b976ac518f" ); client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chain_getBlockHash","params":[10000]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!(result_json, "null"); // TODO: test for a non-zero block? }); } #[test] fn chain_get_header() { smol::block_on(async move { let client = start_client().await; // Test the current best block. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded_header = serde_json::from_str::(result_json).unwrap(); assert_eq!( decoded_header.parent_hash.0, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ); assert_eq!(decoded_header.number, 0); assert_eq!( decoded_header.state_root.0, [ 40, 162, 219, 5, 170, 164, 232, 78, 136, 198, 190, 40, 202, 73, 212, 91, 4, 51, 248, 171, 238, 66, 27, 9, 45, 250, 15, 77, 216, 87, 135, 166 ] ); assert!(decoded_header.digest.logs.is_empty()); // Test passing an explicit block hash. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":["0x6bf30d04495c16ef053de4ac74eac35dfd6473e4907810f450bea1b976ac518f"]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded_header = serde_json::from_str::(result_json).unwrap(); assert_eq!( decoded_header.parent_hash.0, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ); assert_eq!(decoded_header.number, 0); assert_eq!( decoded_header.state_root.0, [ 40, 162, 219, 5, 170, 164, 232, 78, 136, 198, 190, 40, 202, 73, 212, 91, 4, 51, 248, 171, 238, 66, 27, 9, 45, 250, 15, 77, 216, 87, 135, 166 ] ); assert!(decoded_header.digest.logs.is_empty()); // Test for unknown block. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":["0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead"]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!(result_json, "null"); }); } #[test] fn state_get_metadata() { smol::block_on(async move { let client = start_client().await; // Query the metadata of the genesis. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getMetadata","params":["0x6bf30d04495c16ef053de4ac74eac35dfd6473e4907810f450bea1b976ac518f"]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), include_str!("./substrate-node-template-metadata.hex").trim() ); // TOOD: there are no tests for the corner cases of state_getMetadata because it's unclear how the function is supposed to behave at all }); } #[test] fn state_get_runtime_version() { smol::block_on(async move { let client = start_client().await; // Query the runtime of the genesis. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getRuntimeVersion","params":["0x6bf30d04495c16ef053de4ac74eac35dfd6473e4907810f450bea1b976ac518f"]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded = serde_json::from_str::(result_json).unwrap(); assert_eq!(decoded.impl_name, "node-template"); assert_eq!(decoded.spec_version, 100); assert_eq!(decoded.apis.len(), 10); // TOOD: there are no tests for the corner cases of state_getRuntimeVersion because it's unclear how the function is supposed to behave at all }); } #[test] fn state_get_keys_paged_basic() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getKeysPaged","params":["0x", 10]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded = serde_json::from_str::>(result_json) .unwrap() .into_iter() .map(|v| v.0) .collect::>(); assert_eq!( decoded, &[ &[ 23, 126, 104, 87, 251, 29, 14, 64, 147, 118, 18, 47, 238, 58, 212, 248, 78, 123, 144, 18, 9, 107, 65, 196, 235, 58, 175, 148, 127, 110, 164, 41 ][..], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 78, 123, 144, 18, 9, 107, 65, 196, 235, 58, 175, 148, 127, 110, 164, 41 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 86, 132, 160, 34, 163, 77, 216, 191, 162, 186, 175, 68, 241, 114, 183, 16 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 138, 66, 243, 51, 35, 203, 92, 237, 59, 68, 221, 130, 95, 218, 159, 204 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 164, 71, 4, 181, 104, 210, 22, 103, 53, 106, 90, 5, 12, 17, 135, 70, 180, 222, 242, 92, 253, 166, 239, 58, 0, 0, 0, 0 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 167, 253, 108, 40, 131, 107, 154, 40, 82, 45, 201, 36, 17, 12, 244, 57 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169, 0, 124, 188, 18, 112, 181, 176, 145, 117, 143, 156, 66, 245, 145, 91, 62, 138, 197, 158, 17, 150, 58, 241, 145, 116, 208, 185, 77, 93, 120, 4, 28, 35, 63, 85, 210, 225, 147, 36, 102, 91, 175, 223, 182, 41, 37, 175, 45 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169, 35, 160, 92, 171, 246, 211, 189, 231, 202, 62, 240, 209, 21, 150, 181, 97, 28, 189, 45, 67, 83, 10, 68, 112, 90, 208, 136, 175, 49, 62, 24, 248, 11, 83, 239, 22, 179, 97, 119, 205, 75, 119, 184, 70, 242, 165, 240, 124 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169, 50, 165, 147, 95, 110, 220, 97, 122, 225, 120, 254, 249, 235, 30, 33, 31, 190, 93, 219, 21, 121, 183, 46, 132, 82, 79, 194, 158, 120, 96, 158, 60, 175, 66, 232, 90, 161, 24, 235, 254, 11, 10, 212, 4, 181, 189, 210, 95 ], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 185, 157, 136, 14, 198, 129, 121, 156, 12, 243, 14, 136, 134, 55, 29, 169, 79, 154, 234, 26, 250, 121, 18, 101, 250, 227, 89, 39, 43, 173, 193, 207, 142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254, 161, 126, 37, 252, 82, 135, 97, 54, 147, 201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72 ] ] ); }); } #[test] fn state_get_keys_paged_prefix_works() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getKeysPaged","params":["0x26aa394e", 2]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded = serde_json::from_str::>(result_json) .unwrap() .into_iter() .map(|v| v.0) .collect::>(); assert_eq!( decoded, &[ &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 78, 123, 144, 18, 9, 107, 65, 196, 235, 58, 175, 148, 127, 110, 164, 41 ][..], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 86, 132, 160, 34, 163, 77, 216, 191, 162, 186, 175, 68, 241, 114, 183, 16 ] ] ); }); } #[test] fn state_get_keys_paged_start_key_exact_match() { smol::block_on(async move { let client = start_client().await; // This test checks whether the start key is included in the results on an exact match. // `0x26aa394eea5630e07c48ae0c9558cef7` is equal to `[38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247]`. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getKeysPaged","params":["0x", 2, "0x26aa394eea5630e07c48ae0c9558cef7"]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded = serde_json::from_str::>(result_json) .unwrap() .into_iter() .map(|v| v.0) .collect::>(); assert_eq!( decoded, &[ &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 78, 123, 144, 18, 9, 107, 65, 196, 235, 58, 175, 148, 127, 110, 164, 41 ][..], &[ 38, 170, 57, 78, 234, 86, 48, 224, 124, 72, 174, 12, 149, 88, 206, 247, 86, 132, 160, 34, 163, 77, 216, 191, 162, 186, 175, 68, 241, 114, 183, 16 ] ] ); }); } #[test] fn state_get_keys_paged_count_overflow() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getKeysPaged","params":["0x", 1000]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; assert!(matches!( json_rpc::parse::parse_response(&response_raw).unwrap(), json_rpc::parse::Response::Success { .. } )); client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":2,"method":"state_getKeysPaged","params":["0x", 1001]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; assert!(matches!( json_rpc::parse::parse_response(&response_raw).unwrap(), json_rpc::parse::Response::Error { error_code: -32602, // Invalid parameter error code. .. } )); }); } #[test] fn state_get_keys_paged_unknown_block() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"state_getKeysPaged","params":["0x", 10, "0x", "0x0000000000000000000000000000000000000000000000000000000000000000"]}"# .to_owned(), ); let response_raw = client.next_json_rpc_response().await; assert!(matches!( json_rpc::parse::parse_response(&response_raw).unwrap(), json_rpc::parse::Response::Error { error_code: -32602, // Invalid parameter error code. .. } )); }); } #[test] fn system_chain() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_chain","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "Local Testnet" ); }); } #[test] fn system_chain_type() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_chainType","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "Local" ); }); } #[test] fn system_health() { smol::block_on(async move { let client = start_client().await; // Query the runtime of the genesis. client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_health","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); let decoded = serde_json::from_str::(result_json).unwrap(); assert_eq!(decoded.peers, 0); assert_eq!(decoded.should_have_peers, true); }); } #[test] fn system_local_peer_id() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_localPeerId","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN" ); }); } #[test] fn system_name() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_name","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!( serde_json::from_str::(result_json).unwrap(), "smoldot-full-node" ); }); } #[test] fn system_properties() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_properties","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; let (_, result_json) = json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); assert_eq!(result_json, r#"{"tokenDecimals": 15}"#); }); } #[test] fn system_version() { smol::block_on(async move { let client = start_client().await; client.send_json_rpc_request( r#"{"jsonrpc":"2.0","id":1,"method":"system_version","params":[]}"#.to_owned(), ); let response_raw = client.next_json_rpc_response().await; // Note: we don't check the actual result, as the version changes pretty often. json_rpc::parse::parse_response(&response_raw) .unwrap() .into_success() .unwrap(); }); } // TODO: add tests for `chain_subscribeAllHeads` // TODO: add tests for `chain_subscribeFinalizedHeads` // TODO: add tests for `chain_subscribeNewHeads` // TODO: add tests for `state_queryStorageAt`