Coverage Report

Created: 2022-11-10 19:56

/home/runner/work/creditcoin/creditcoin/node/src/service.rs
Line
Count
Source (jump to first uncovered line)
1
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
2
3
mod nonce_monitor;
4
5
use crate::cli::Cli;
6
use codec::Encode;
7
use creditcoin_node_runtime::{self, opaque::Block, RuntimeApi};
8
use sc_client_api::{Backend, ExecutorProvider};
9
pub use sc_executor::NativeElseWasmExecutor;
10
use sc_keystore::LocalKeystore;
11
use sc_service::{
12
  error::Error as ServiceError, Configuration, TaskManager, TransactionPoolOptions,
13
};
14
use sc_telemetry::{Telemetry, TelemetryWorker};
15
use sc_transaction_pool::PoolLimit;
16
use sha3pow::Sha3Algorithm;
17
use sp_inherents::CreateInherentDataProviders;
18
use sp_runtime::{app_crypto::Ss58Codec, offchain::DbExternalities, traits::IdentifyAccount};
19
use std::{sync::Arc, thread, time::Duration};
20
21
// Our native executor instance.
22
pub struct ExecutorDispatch;
23
24
impl sc_executor::NativeExecutionDispatch for ExecutorDispatch {
25
  type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions;
26
27
0
  fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
28
0
    creditcoin_node_runtime::api::dispatch(method, data)
29
0
  }
30
31
0
  fn native_version() -> sc_executor::NativeVersion {
32
0
    creditcoin_node_runtime::native_version()
33
0
  }
34
}
35
36
type FullClient =
37
  sc_service::TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<ExecutorDispatch>>;
38
type FullBackend = sc_service::TFullBackend<Block>;
39
type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
40
type PartialComponentsType<T> = sc_service::PartialComponents<
41
  FullClient,
42
  FullBackend,
43
  FullSelectChain,
44
  sc_consensus::DefaultImportQueue<Block, FullClient>,
45
  sc_transaction_pool::FullPool<Block, FullClient>,
46
  (
47
    sc_consensus_pow::PowBlockImport<
48
      Block,
49
      Arc<FullClient>,
50
      FullClient,
51
      FullSelectChain,
52
      Sha3Algorithm<FullClient>,
53
      sp_consensus::CanAuthorWithNativeVersion<
54
        <FullClient as ExecutorProvider<Block>>::Executor,
55
      >,
56
      T,
57
    >,
58
    Option<Telemetry>,
59
  ),
60
>;
61
62
/// Creates a transaction pool config where the limits are 5x the default, unless a limit has been set higher manually
63
0
fn create_transaction_pool_config(mut config: TransactionPoolOptions) -> TransactionPoolOptions {
64
0
  let set_limit = |limit: &mut PoolLimit, default: &PoolLimit| {
65
0
    // set the value to `max(5 * default_value, current_value)`
66
0
    let new_setting = |curr: usize, def: usize| curr.max(def.saturating_mul(5));
67
0
68
0
    limit.count = new_setting(limit.count, default.count);
69
0
    limit.total_bytes = new_setting(limit.total_bytes, default.total_bytes);
70
0
  };
71
0
  let default = TransactionPoolOptions::default();
72
0
  set_limit(&mut config.future, &default.future);
73
0
  set_limit(&mut config.ready, &default.ready);
74
0
  config
75
0
}
76
77
0
pub fn new_partial(
78
0
  config: &Configuration,
79
0
) -> Result<PartialComponentsType<impl CreateInherentDataProviders<Block, ()>>, ServiceError> {
80
0
  if config.keystore_remote.is_some() {
81
0
    return Err(ServiceError::Other("Remote Keystores are not supported.".to_string()));
82
0
  }
83
84
0
  let telemetry = config
85
0
    .telemetry_endpoints
86
0
    .clone()
87
0
    .filter(|x| !x.is_empty())
88
0
    .map(|endpoints| -> Result<_, sc_telemetry::Error> {
89
0
      let worker = TelemetryWorker::new(16)?;
90
0
      let telemetry = worker.handle().new_telemetry(endpoints);
91
0
      Ok((worker, telemetry))
92
0
    })
93
0
    .transpose()?;
94
95
0
  let executor = NativeElseWasmExecutor::<ExecutorDispatch>::new(
96
0
    config.wasm_method,
97
0
    config.default_heap_pages,
98
0
    config.max_runtime_instances,
99
0
    config.runtime_cache_size,
100
0
  );
101
102
0
  let (client, backend, keystore_container, task_manager) =
103
0
    sc_service::new_full_parts::<Block, RuntimeApi, _>(
104
0
      config,
105
0
      telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
106
0
      executor,
107
0
    )?;
108
0
  let client = Arc::new(client);
109
0
110
0
  let telemetry = telemetry.map(|(worker, telemetry)| {
111
0
    task_manager.spawn_handle().spawn("telemetry", "telemetry_tasks", worker.run());
112
0
    telemetry
113
0
  });
114
0
115
0
  let select_chain = sc_consensus::LongestChain::new(backend.clone());
116
0
117
0
  let tx_pool_config = create_transaction_pool_config(config.transaction_pool.clone());
118
0
  let transaction_pool = sc_transaction_pool::BasicPool::new_full(
119
0
    tx_pool_config,
120
0
    config.role.is_authority().into(),
121
0
    config.prometheus_registry(),
122
0
    task_manager.spawn_essential_handle(),
123
0
    client.clone(),
124
0
  );
125
0
126
0
  let can_author_with = sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());
127
0
128
0
  let algorithm = Sha3Algorithm::new(client.clone());
129
0
130
0
  let pow_block_import = sc_consensus_pow::PowBlockImport::new(
131
0
    client.clone(),
132
0
    client.clone(),
133
0
    algorithm.clone(),
134
0
    0,
135
0
    select_chain.clone(),
136
0
    move |_, ()| async move {
137
0
      let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
138
0
      Ok(timestamp)
139
0
    },
140
0
    can_author_with,
141
0
  );
142
143
0
  let import_queue = sc_consensus_pow::import_queue(
144
0
    Box::new(pow_block_import.clone()),
145
0
    None,
146
0
    algorithm,
147
0
    &task_manager.spawn_essential_handle(),
148
0
    config.prometheus_registry(),
149
0
  )?;
150
151
0
  Ok(sc_service::PartialComponents {
152
0
    client,
153
0
    backend,
154
0
    task_manager,
155
0
    import_queue,
156
0
    keystore_container,
157
0
    select_chain,
158
0
    transaction_pool,
159
0
    other: (pow_block_import, telemetry),
160
0
  })
161
0
}
162
163
0
fn remote_keystore(_url: &str) -> Result<Arc<LocalKeystore>, &'static str> {
164
0
  // FIXME: here would the concrete keystore be built,
165
0
  //        must return a concrete type (NOT `LocalKeystore`) that
166
0
  //        implements `CryptoStore` and `SyncCryptoStore`
167
0
  Err("Remote Keystore not supported.")
168
0
}
169
170
pub fn decode_mining_key(
171
  mining_key: Option<&str>,
172
) -> Result<creditcoin_node_runtime::AccountId, String> {
173
0
  if let Some(key) = mining_key {
174
    // raw public key
175
0
    if let Some(key_without_prefix) = key.strip_prefix("0x") {
176
0
      let key_bytes = hex::decode(&key_without_prefix)
177
0
        .map_err(|e| format!("Invalid mining key, expected hex: {}", e))?;
178
      Ok(creditcoin_node_runtime::Signer::from(
179
0
        sp_core::ecdsa::Public::from_full(&key_bytes)
180
0
          .map_err(|_| String::from("Invalid mining key, expected 33 bytes"))?,
181
      )
182
0
      .into_account())
183
    } else {
184
      // ss58 encoded key
185
0
      match sp_core::ecdsa::Public::from_ss58check(key) {
186
0
        Ok(key) => Ok(creditcoin_node_runtime::Signer::from(key).into_account()),
187
0
        Err(err) => match creditcoin_node_runtime::AccountId::from_ss58check(key) {
188
0
          Ok(account_id) => Ok(account_id),
189
0
          Err(e) => {
190
0
            let msg = format!("Invalid mining key, failed to interpret it as an ECDSA public key (error: {}) and as an account ID (error: {})", err, e);
191
0
            log::error!("{}", msg);
192
0
            Err(msg)
193
          },
194
        },
195
      }
196
    }
197
  } else {
198
0
    Err("The node is configured for mining but is missing a mining key".into())
199
  }
200
0
}
201
202
/// Builds a new service for a full client.
203
0
pub fn new_full(config: Configuration, cli: Cli) -> Result<TaskManager, ServiceError> {
204
0
  let Cli {
205
0
    rpc_mapping, mining_key, mining_threads, monitor_nonce: monitor_nonce_account, ..
206
  } = cli;
207
208
  let sc_service::PartialComponents {
209
0
    client,
210
0
    backend,
211
0
    mut task_manager,
212
0
    import_queue,
213
0
    mut keystore_container,
214
0
    select_chain,
215
0
    transaction_pool,
216
0
    other: (pow_block_import, mut telemetry),
217
0
  } = new_partial(&config)?;
218
219
0
  if let Some(url) = &config.keystore_remote {
220
0
    match remote_keystore(url) {
221
0
      Ok(k) => keystore_container.set_remote_keystore(k),
222
0
      Err(e) => {
223
0
        return Err(ServiceError::Other(format!(
224
0
          "Error hooking up remote keystore for {}: {}",
225
0
          url, e
226
0
        )))
227
      },
228
    };
229
0
  }
230
231
0
  let (network, system_rpc_tx, network_starter) =
232
0
    sc_service::build_network(sc_service::BuildNetworkParams {
233
0
      config: &config,
234
0
      client: client.clone(),
235
0
      transaction_pool: transaction_pool.clone(),
236
0
      spawn_handle: task_manager.spawn_handle(),
237
0
      import_queue,
238
0
      block_announce_validator_builder: None,
239
0
      warp_sync: None,
240
0
    })?;
241
242
0
  if config.offchain_worker.enabled {
243
0
    sc_service::build_offchain_workers(
244
0
      &config,
245
0
      task_manager.spawn_handle(),
246
0
      client.clone(),
247
0
      network.clone(),
248
0
    );
249
0
    if let Some(mapping) = rpc_mapping {
250
0
      let storage = backend.offchain_storage().unwrap();
251
0
      let mut offchain_db = sc_offchain::OffchainDb::new(storage);
252
0
      for (chain, uri) in mapping {
253
0
        let mut key = Vec::from(chain.as_bytes());
254
0
        key.extend("-rpc-uri".bytes());
255
0
        offchain_db.local_storage_set(
256
0
          sp_core::offchain::StorageKind::PERSISTENT,
257
0
          &key,
258
0
          &uri.encode(),
259
0
        );
260
0
      }
261
0
    }
262
0
  }
263
264
0
  let role = config.role.clone();
265
0
  let _force_authoring = config.force_authoring;
266
0
  let _backoff_authoring_blocks: Option<()> = None;
267
0
  let _name = config.network.node_name.clone();
268
0
  let _enable_grandpa = !config.disable_grandpa;
269
0
  let prometheus_registry = config.prometheus_registry().cloned();
270
0
  let mining_metrics = primitives::metrics::MiningMetrics::new(prometheus_registry.as_ref())?;
271
272
0
  let rpc_extensions_builder = {
273
0
    let client = client.clone();
274
0
    let pool = transaction_pool.clone();
275
0
276
0
    let mining_metrics = mining_metrics.clone();
277
0
    Box::new(move |deny_unsafe, _| {
278
0
      let deps = crate::rpc::FullDeps {
279
0
        client: client.clone(),
280
0
        pool: pool.clone(),
281
0
        deny_unsafe,
282
0
        mining_metrics: mining_metrics.clone(),
283
0
      };
284
0
285
0
      Ok(crate::rpc::create_full(deps))
286
0
    })
287
  };
288
289
0
  let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams {
290
0
    network: network.clone(),
291
0
    client: client.clone(),
292
0
    keystore: keystore_container.sync_keystore(),
293
0
    task_manager: &mut task_manager,
294
0
    transaction_pool: transaction_pool.clone(),
295
0
    rpc_extensions_builder,
296
0
    backend: backend.clone(),
297
0
    system_rpc_tx,
298
0
    config,
299
0
    telemetry: telemetry.as_mut(),
300
0
  })?;
301
302
0
  if let Some(monitor_target) = monitor_nonce_account {
303
0
    if let Some(registry) = prometheus_registry.clone() {
304
0
      task_manager.spawn_handle().spawn("nonce_metrics", None, {
305
0
        nonce_monitor::task(nonce_monitor::TaskArgs {
306
0
          registry,
307
0
          monitor_target,
308
0
          handlers: rpc_handlers,
309
0
          backend,
310
0
          keystore: keystore_container.keystore(),
311
0
        })
312
0
      });
313
0
    }
314
0
  }
315
316
0
  if role.is_authority() {
317
0
    let mining_key = decode_mining_key(mining_key.as_deref())?;
318
0
    let proposer_factory = sc_basic_authorship::ProposerFactory::new(
319
0
      task_manager.spawn_handle(),
320
0
      client.clone(),
321
0
      transaction_pool,
322
0
      prometheus_registry.as_ref(),
323
0
      telemetry.as_ref().map(|x| x.handle()),
324
0
    );
325
0
326
0
    let can_author_with =
327
0
      sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());
328
0
329
0
    let algorithm = Sha3Algorithm::new(client.clone());
330
0
331
0
    let (worker, worker_task) = sc_consensus_pow::start_mining_worker(
332
0
      Box::new(pow_block_import),
333
0
      client.clone(),
334
0
      select_chain,
335
0
      algorithm,
336
0
      proposer_factory,
337
0
      network.clone(),
338
0
      network,
339
0
      Some(mining_key.encode()),
340
0
      move |_, ()| async move {
341
0
        let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
342
0
        Ok(timestamp)
343
0
      },
344
0
      Duration::from_secs(10),
345
0
      Duration::from_secs(10),
346
0
      can_author_with,
347
0
    );
348
0
349
0
    task_manager
350
0
      .spawn_essential_handle()
351
0
      .spawn_blocking("pow", "pow_group", worker_task);
352
0
353
0
    let threads = mining_threads.unwrap_or_else(num_cpus::get);
354
0
    for _ in 0..threads {
355
0
      if let Some(keystore) = keystore_container.local_keystore() {
356
0
        let worker = worker.clone();
357
0
        let client = client.clone();
358
0
        let mining_metrics = mining_metrics.clone();
359
0
        thread::spawn(move || {
360
0
          let mut count = 0;
361
0
          loop {
362
0
            let metadata = worker.metadata();
363
0
            let version = worker.version();
364
0
            if let Some(metadata) = metadata {
365
              loop {
366
                match sha3pow::mine(
367
0
                  client.as_ref(),
368
0
                  &keystore,
369
0
                  &metadata.pre_hash,
370
0
                  metadata.pre_runtime.as_ref().map(|v| &v[..]),
371
0
                  metadata.difficulty,
372
                ) {
373
0
                  Ok(Some(seal)) => {
374
0
                    if version == worker.version() {
375
0
                      let _ =
376
0
                        futures_lite::future::block_on(worker.submit(seal));
377
0
                    }
378
                  },
379
0
                  Ok(None) => {
380
0
                    count += 1;
381
0
                  },
382
0
                  Err(e) => eprintln!("Mining error: {}", e),
383
                }
384
0
                if count >= 1_000_000 {
385
0
                  mining_metrics.add(count);
386
0
                  count = 0;
387
0
                }
388
0
                if version != worker.version() {
389
0
                  break;
390
0
                }
391
              }
392
0
            }
393
          }
394
0
        });
395
0
      }
396
    }
397
0
  }
398
399
  // if the node isn't actively participating in consensus then it doesn't
400
  // need a keystore, regardless of which protocol we use below.
401
0
  let _keystore =
402
0
    if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None };
403
404
0
  network_starter.start_network();
405
0
  Ok(task_manager)
406
0
}