subcoin_test_service/
lib.rs

1use bitcoin::Block;
2use bitcoin::consensus::Decodable;
3use bitcoin::hex::FromHex;
4use sc_consensus_nakamoto::{
5    BitcoinBlockImport, BitcoinBlockImporter, BlockVerification, ImportConfig, ImportStatus,
6    ScriptEngine,
7};
8use sc_service::config::{
9    BlocksPruning, DatabaseSource, ExecutorConfiguration, KeystoreConfig, NetworkConfiguration,
10    OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, RpcConfiguration,
11    WasmExecutionMethod, WasmtimeInstantiationStrategy,
12};
13use sc_service::error::Error as ServiceError;
14use sc_service::{BasePath, ChainSpec, Configuration, Role};
15use sp_consensus::BlockOrigin;
16use sp_keyring::sr25519::Keyring as Sr25519Keyring;
17use std::sync::Arc;
18use subcoin_service::{FullClient, NodeComponents, SubcoinConfiguration};
19
20fn decode_raw_block(hex_str: &str) -> Block {
21    let data = Vec::<u8>::from_hex(hex_str).expect("Failed to convert hex str");
22    Block::consensus_decode(&mut data.as_slice()).expect("Failed to convert hex data to Block")
23}
24
25pub fn block_data() -> Vec<Block> {
26    // genesis block
27    let block0 = decode_raw_block(
28        "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",
29    );
30    // https://webbtc.com/block/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048
31    // height 1
32    let block1 = decode_raw_block(
33        "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000",
34    );
35    // https://webbtc.com/block/000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd.hex
36    // height 2
37    let block2 = decode_raw_block(
38        "010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd610101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010bffffffff0100f2052a010000004341047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac00000000",
39    );
40    // https://webbtc.com/block/0000000082b5015589a3fdf2d4baff403e6f0be035a5d9742c1cae6295464449.hex
41    // height 3
42    let block3 = decode_raw_block(
43        "01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d010effffffff0100f2052a0100000043410494b9d3e76c5b1629ecf97fff95d7a4bbdac87cc26099ada28066c6ff1eb9191223cd897194a08d0c2726c5747f1db49e8cf90e75dc3e3550ae9b30086f3cd5aaac00000000",
44    );
45    vec![block0, block1, block2, block3]
46}
47
48pub fn full_test_configuration(
49    tokio_handle: tokio::runtime::Handle,
50    chain_spec: Box<dyn ChainSpec>,
51) -> Configuration {
52    let tmp = tempfile::tempdir().unwrap();
53    let base_path = BasePath::new(tmp.path());
54    let root = base_path.path().to_path_buf();
55
56    let network_config = NetworkConfiguration::new(
57        Sr25519Keyring::Alice.to_seed(),
58        "network/test/0.1",
59        Default::default(),
60        None,
61    );
62
63    Configuration {
64        impl_name: "subcoin-test-node".to_string(),
65        impl_version: "1.0".to_string(),
66        role: Role::Full,
67        tokio_handle,
68        transaction_pool: Default::default(),
69        network: network_config,
70        keystore: KeystoreConfig::InMemory,
71        database: DatabaseSource::ParityDb {
72            path: root.join("db"),
73        },
74        trie_cache_maximum_size: Some(64 * 1024 * 1024),
75        state_pruning: Some(PruningMode::ArchiveAll),
76        blocks_pruning: BlocksPruning::KeepAll,
77        chain_spec,
78        executor: ExecutorConfiguration {
79            wasm_method: WasmExecutionMethod::Compiled {
80                instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
81            },
82            max_runtime_instances: 8,
83            runtime_cache_size: 2,
84            default_heap_pages: None,
85        },
86        rpc: RpcConfiguration {
87            addr: None,
88            max_connections: Default::default(),
89            cors: None,
90            methods: Default::default(),
91            max_request_size: Default::default(),
92            max_response_size: Default::default(),
93            id_provider: Default::default(),
94            max_subs_per_conn: Default::default(),
95            port: 9944,
96            message_buffer_capacity: Default::default(),
97            batch_config: RpcBatchRequestConfig::Unlimited,
98            rate_limit: None,
99            rate_limit_whitelisted_ips: Default::default(),
100            rate_limit_trust_proxy_headers: Default::default(),
101        },
102        prometheus_config: None,
103        telemetry_endpoints: None,
104        offchain_worker: OffchainWorkerConfig {
105            enabled: true,
106            indexing_enabled: false,
107        },
108        force_authoring: false,
109        disable_grandpa: false,
110        dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()),
111        tracing_targets: None,
112        tracing_receiver: Default::default(),
113        announce_block: true,
114        data_path: base_path.path().into(),
115        base_path,
116        wasm_runtime_overrides: None,
117        warm_up_trie_cache: None,
118    }
119}
120
121pub fn test_configuration(tokio_handle: tokio::runtime::Handle) -> Configuration {
122    let spec = subcoin_service::chain_spec::config(bitcoin::Network::Bitcoin)
123        .expect("Failed to create chain spec");
124
125    full_test_configuration(tokio_handle, Box::new(spec))
126}
127
128pub fn new_test_node(tokio_handle: tokio::runtime::Handle) -> Result<NodeComponents, ServiceError> {
129    let config = test_configuration(tokio_handle);
130    subcoin_service::new_node(SubcoinConfiguration {
131        network: bitcoin::Network::Bitcoin,
132        config: &config,
133        no_hardware_benchmarks: true,
134        storage_monitor: Default::default(),
135    })
136}
137
138pub async fn new_test_node_and_produce_blocks(
139    config: &Configuration,
140    up_to: u32,
141) -> Arc<FullClient> {
142    let NodeComponents { client, .. } =
143        subcoin_service::new_node(SubcoinConfiguration::test_config(config))
144            .expect("Failed to create node");
145
146    let mut bitcoin_block_import =
147        BitcoinBlockImporter::<_, _, _, _, subcoin_service::TransactionAdapter>::new(
148            client.clone(),
149            client.clone(),
150            ImportConfig {
151                network: bitcoin::Network::Bitcoin,
152                block_verification: BlockVerification::None,
153                execute_block: true,
154                script_engine: ScriptEngine::Core,
155            },
156            Arc::new(subcoin_service::CoinStorageKey),
157            None,
158        );
159
160    let test_blocks = block_data();
161
162    if up_to as usize >= test_blocks.len() {
163        panic!(
164            "Can not produce too many blocks (maximum: {}) in test env",
165            test_blocks.len()
166        );
167    }
168
169    for block_number in 1..=up_to {
170        let block = test_blocks[block_number as usize].clone();
171        let import_status = bitcoin_block_import
172            .import_block(block, BlockOrigin::Own)
173            .await
174            .unwrap();
175        assert!(matches!(import_status, ImportStatus::Imported { .. }));
176    }
177
178    client
179}