subcoin_test_service/
lib.rs

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