当前位置:   article > 正文

基于rust的区块链实现

基于rust的区块链实现

在开源框架Substrate中构建核心区块链基础设施的初学者友好教程。

substrate是一个强大的区块链框架,它允许开发者构建自定义的区块链。Substrate提供了大量的模块化组件,使得开发者可以选择和定制这些组件以满足他们的特定需求,从而极大地加快了区块链开发的速度和灵活性。

Substrate的架构主要可以分为几个关键组件:

  1. 底层P2P网络: 用于节点之间的通信。
  2. 共识机制: Substrate支持多种共识算法,比如Aura、GRANDPA等,使得开发者可以根据自己的需求选择最合适的共识机制。
  3. 运行时环境: 这是Substrate的核心之一,允许在不需重启或硬分叉的情况下升级区块链逻辑。
  4. 存储: 提供了一个高效的数据库存储解决方案,用于存储区块链状态。
  5. 交易队列: 管理和优化进入区块链的交易处理。
  6. 模块化的框架: Substrate提供了许多现成的模块(称为“pallets”),如资产管理、身份验证、智能合约等,这些都可以用来构建区块链的特定功能。

Substrate的设计哲学是提供灵活性和可扩展性,使其不仅适用于创建新的区块链项目,还可以用来构建整个区块链网络。通过使用Substrate,Parity Technologies希望能够加速Web3技术的发展和采用。

原项目地址

GitHub - substrate-developer-hub/utxo-workshop at 2d7697ccfa4e7a5922db957d8fb1ef5529b600fc

cli.rs

一个简化的服务器端应用程序的一部分,看起来像是为了处理网络连接和执行一些基于TCP的请求/响应模式设计的。整个流程涉及到网络编程的基本概念,如套接字操作、非阻塞I/O、以及一些特定数据结构的使用。下面是对关键部分的详细讲解:

基本功能函数

  • msg: 打印一条消息到stderr
  • die: 打印一条包含errno的错误消息到stderr,然后中止程序。
  • get_monotonic_usec: 获取自系统启动以来的单调时间,单位为微秒。这个函数使用clock_gettime函数和CLOCK_MONOTONIC时钟。
  • fd_set_nb: 将文件描述符(fd)设置为非阻塞模式。这个对网络编程特别有用,因为它允许程序在没有数据可读或者不能立即写入时不被阻塞。

数据结构

  • Conn: 表示一个网络连接,包含了文件描述符、状态、读写缓冲区、和一些控制信息。
  • g_data: 包含了服务器的全局状态,比如所有客户端连接的映射、空闲连接列表、定时器、和一个线程池。

关键逻辑函数

整体而言,这段代码是为了展示如何使用Substrate和Tokio等Rust库来构建和管理一个区块链节点的生命周期,包括服务的启动、运行和停止。这需要对Rust的异步编程、Futures和Tokio运行时有较深的理解,同时也需要熟悉Substrate框架的基本概念和组件。

  • conn_put: 将新的Conn实例添加到全局的fd2conn映射中。
  • accept_new_conn: 接受一个新的连接请求,创建一个Conn实例,并将其设置为非阻塞。
  • state_reqstate_res: 这些函数的实现没有提供,但它们的名字暗示了它们用于处理请求和发送响应。这些函数可能会根据Conn结构中的状态字段来操作。
    1. static void msg(const char *msg) {
    2. fprintf(stderr, "%s\n", msg);
    3. }
    4. static void die(const char *msg) {
    5. int err = errno;
    6. fprintf(stderr, "[%d] %s\n", err, msg);
    7. abort();
    8. }
    9. static uint64_t get_monotonic_usec() {
    10. timespec tv = {0, 0};
    11. clock_gettime(CLOCK_MONOTONIC, &tv);
    12. return uint64_t(tv.tv_sec) * 1000000 + tv.tv_nsec / 1000;
    13. }
    14. static void fd_set_nb(int fd) {
    15. errno = 0;
    16. int flags = fcntl(fd, F_GETFL, 0);
    17. if (errno) {
    18. die("fcntl error");
    19. return;
    20. }
    21. flags |= O_NONBLOCK;
    22. errno = 0;
    23. (void)fcntl(fd, F_SETFL, flags);
    24. if (errno) {
    25. die("fcntl error");
    26. }
    27. }
    28. struct Conn;
    29. // global variables
    30. static struct {
    31. HMap db;
    32. // a map of all client connections, keyed by fd
    33. std::vector<Conn *> fd2conn;
    34. // timers for idle connections
    35. DList idle_list;
    36. // timers for TTLs
    37. std::vector<HeapItem> heap;
    38. // the thread pool
    39. TheadPool tp;
    40. } g_data;
    41. const size_t k_max_msg = 4096;
    42. enum {
    43. STATE_REQ = 0,
    44. STATE_RES = 1,
    45. STATE_END = 2, // mark the connection for deletion
    46. };
    47. struct Conn {
    48. int fd = -1;
    49. uint32_t state = 0; // either STATE_REQ or STATE_RES
    50. // buffer for reading
    51. size_t rbuf_size = 0;
    52. uint8_t rbuf[4 + k_max_msg];
    53. // buffer for writing
    54. size_t wbuf_size = 0;
    55. size_t wbuf_sent = 0;
    56. uint8_t wbuf[4 + k_max_msg];
    57. uint64_t idle_start = 0;
    58. // timer
    59. DList idle_list;
    60. };
    61. static void conn_put(std::vector<Conn *> &fd2conn, struct Conn *conn) {
    62. if (fd2conn.size() <= (size_t)conn->fd) {
    63. fd2conn.resize(conn->fd + 1);
    64. }
    65. fd2conn[conn->fd] = conn;
    66. }
    67. static int32_t accept_new_conn(int fd) {
    68. // accept
    69. struct sockaddr_in client_addr = {};
    70. socklen_t socklen = sizeof(client_addr);
    71. int connfd = accept(fd, (struct sockaddr *)&client_addr, &socklen);
    72. if (connfd < 0) {
    73. msg("accept() error");
    74. return -1; // error
    75. }
    76. // set the new connection fd to nonblocking mode
    77. fd_set_nb(connfd);
    78. // creating the struct Conn
    79. struct Conn *conn = (struct Conn *)malloc(sizeof(struct Conn));
    80. if (!conn) {
    81. close(connfd);
    82. return -1;
    83. }
    84. conn->fd = connfd;
    85. conn->state = STATE_REQ;
    86. conn->rbuf_size = 0;
    87. conn->wbuf_size = 0;
    88. conn->wbuf_sent = 0;
    89. conn->idle_start = get_monotonic_usec();
    90. dlist_insert_before(&g_data.idle_list, &conn->idle_list);
    91. conn_put(g_data.fd2conn, conn);
    92. return 0;
    93. }
    94. static void state_req(Conn *conn);
    95. static void state_res(Conn *conn);
    96. const size_t k_max_args = 1024;
    97. static int32_t parse_req(
    98. const uint8_t *data, size_t len, std::vector<std::string> &out)
    99. {
    100. if (len < 4) {
    101. return -1;
    102. }
    103. uint32_t n = 0;
    104. memcpy(&n, &data[0], 4);
    105. if (n > k_max_args) {
    106. return -1;
    107. }
    108. size_t pos = 4;
    109. while (n--) {
    110. if (pos + 4 > len) {
    111. return -1;
    112. }
    113. uint32_t sz = 0;
    114. memcpy(&sz, &data[pos], 4);
    115. if (pos + 4 + sz > len) {
    116. return -1;
    117. }
    118. out.push_back(std::string((char *)&data[pos + 4], sz));
    119. pos += 4 + sz;
    120. }
    121. if (pos != len) {
    122. return -1; // trailing garbage
    123. }
    124. return 0;
    125. }
    126. enum {
    127. T_STR = 0,
    128. T_ZSET = 1,
    129. };
    130. // the structure for the key
    131. struct Entry {
    132. struct HNode node;
    133. std::string key;
    134. std::string val;
    135. uint32_t type = 0;
    136. ZSet *zset = NULL;
    137. // for TTLs
    138. size_t heap_idx = -1;
    139. };
    140. static bool entry_eq(HNode *lhs, HNode *rhs) {
    141. struct Entry *le = container_of(lhs, struct Entry, node);
    142. struct Entry *re = container_of(rhs, struct Entry, node);
    143. return le->key == re->key;
    144. }
    145. enum {
    146. ERR_UNKNOWN = 1,
    147. ERR_2BIG = 2,
    148. ERR_TYPE = 3,
    149. ERR_ARG = 4,
    150. };
    151. static void out_nil(std::string &out) {
    152. out.push_back(SER_NIL);
    153. }
    154. static void out_str(std::string &out, const char *s, size_t size) {
    155. out.push_back(SER_STR);
    156. uint32_t len = (uint32_t)size;
    157. out.append((char *)&len, 4);
    158. out.append(s, len);
    159. }
    160. static void out_str(std::string &out, const std::string &val) {
    161. return out_str(out, val.data(), val.size());
    162. }
    163. static void out_int(std::string &out, int64_t val) {
    164. out.push_back(SER_INT);
    165. out.append((char *)&val, 8);
    166. }
    167. static void out_dbl(std::string &out, double val) {
    168. out.push_back(SER_DBL);
    169. out.append((char *)&val, 8);
    170. }
    171. static void out_err(std::string &out, int32_t code, const std::string &msg) {
    172. out.push_back(SER_ERR);
    173. out.append((char *)&code, 4);
    174. uint32_t len = (uint32_t)msg.size();
    175. out.append((char *)&len, 4);
    176. out.append(msg);
    177. }

  • 这段Rust代码主要是用于构建和运行一个Substrate区块链节点的命令行接口(CLI)。Substrate是一个区块链框架,允许开发者构建定制的区块链。这段代码展示了如何解析命令行参数,启动一个Substrate服务,并处理不同的命令行指令,例如运行节点、构建规格文件、导出或导入区块、检查区块、清除链数据或回退链上的数据。

    代码的主要部分包括:

  • run 函数:这是入口函数,负责解析命令行参数并根据不同的命令执行相应的操作。它使用parse_and_prepare函数解析命令行参数,并根据解析结果选择执行相应的动作(如运行节点、导出区块等)。

  • load_spec 函数:根据给定的标识符加载链规格(ChainSpec)。链规格定义了链的初始状态和配置。

  • run_until_exit 函数:启动服务并运行直到接收到退出信号。它在一个Tokio运行时上执行并处理退出逻辑,确保服务优雅地关闭。这个函数处理了服务的启动、运行以及退出信号(如Ctrl-C)的处理。

  • Exit 结构体和IntoExit trait实现:提供一个处理退出信号的机制。它使用ctrlc crate来监听Ctrl-C命令,并在收到信号时发送一个退出消息。

  1. use crate::service;
  2. use futures::{future::{select, Map}, FutureExt, TryFutureExt, channel::oneshot, compat::Future01CompatExt};
  3. use std::cell::RefCell;
  4. use tokio::runtime::Runtime;
  5. pub use sc_cli::{VersionInfo, IntoExit, error};
  6. use sc_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom};
  7. use sc_service::{AbstractService, Roles as ServiceRoles, Configuration};
  8. use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};
  9. use crate::chain_spec;
  10. use log::info;
  11. /// Parse command line arguments into service configuration.
  12. pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()> where
  13. I: IntoIterator<Item = T>,
  14. T: Into<std::ffi::OsString> + Clone,
  15. E: IntoExit,
  16. {
  17. type Config<T> = Configuration<(), T>;
  18. match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "substrate-node", args) {
  19. ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
  20. |exit, _cli_args, _custom_args, config: Config<_>| {
  21. info!("{}", version.name);
  22. info!(" version {}", config.full_version());
  23. info!(" by {}, 2017, 2018", version.author);
  24. info!("Chain specification: {}", config.chain_spec.name());
  25. info!("Node name: {}", config.name);
  26. info!("Roles: {}", display_role(&config));
  27. let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
  28. match config.roles {
  29. ServiceRoles::LIGHT => run_until_exit(
  30. runtime,
  31. service::new_light(config)?,
  32. exit
  33. ),
  34. _ => run_until_exit(
  35. runtime,
  36. service::new_full(config)?,
  37. exit
  38. ),
  39. }
  40. }),
  41. ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
  42. ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
  43. Ok(new_full_start!(config).0), load_spec, exit),
  44. ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
  45. Ok(new_full_start!(config).0), load_spec, exit),
  46. ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_>|
  47. Ok(new_full_start!(config).0), load_spec, exit),
  48. ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
  49. ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>|
  50. Ok(new_full_start!(config).0), load_spec),
  51. ParseAndPrepare::CustomCommand(_) => Ok(())
  52. }?;
  53. Ok(())
  54. }
  55. fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
  56. Ok(match chain_spec::Alternative::from(id) {
  57. Some(spec) => Some(spec.load()?),
  58. None => None,
  59. })
  60. }
  61. fn run_until_exit<T, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
  62. where
  63. T: AbstractService,
  64. E: IntoExit,
  65. {
  66. let (exit_send, exit) = oneshot::channel();
  67. let informant = informant::build(&service);
  68. let future = select(exit, informant)
  69. .map(|_| Ok(()))
  70. .compat();
  71. runtime.executor().spawn(future);
  72. // we eagerly drop the service so that the internal exit future is fired,
  73. // but we need to keep holding a reference to the global telemetry guard
  74. let _telemetry = service.telemetry();
  75. let service_res = {
  76. let exit = e.into_exit();
  77. let service = service
  78. .map_err(|err| error::Error::Service(err))
  79. .compat();
  80. let select = select(service, exit)
  81. .map(|_| Ok(()))
  82. .compat();
  83. runtime.block_on(select)
  84. };
  85. let _ = exit_send.send(());
  86. // TODO [andre]: timeout this future #1318
  87. use futures01::Future;
  88. let _ = runtime.shutdown_on_idle().wait();
  89. service_res
  90. }
  91. // handles ctrl-c
  92. pub struct Exit;
  93. impl IntoExit for Exit {
  94. type Exit = Map<oneshot::Receiver<()>, fn(Result<(), oneshot::Canceled>) -> ()>;
  95. fn into_exit(self) -> Self::Exit {
  96. // can't use signal directly here because CtrlC takes only `Fn`.
  97. let (exit_send, exit) = oneshot::channel();
  98. let exit_send_cell = RefCell::new(Some(exit_send));
  99. ctrlc::set_handler(move || {
  100. let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();
  101. if let Some(exit_send) = exit_send {
  102. exit_send.send(()).expect("Error sending exit notification");
  103. }
  104. }).expect("Error setting Ctrl-C handler");
  105. exit.map(drop)
  106. }
  107. }详细见即使

这段代码主要涉及构建Substrate区块链服务的过程,其中包括完整节点服务(new_full)和轻节点服务(new_light)的建立。Substrate是一个区块链框架,允许你构建定制的区块链。这段代码使用Rust语言编写,展示了如何利用Substrate的服务构建器来配置和启动一个区块链节点。我们将逐步解析代码的关键部分。


service.rs

new_full_start 宏

  • 目的:这个宏的主要目的是简化完整节点服务的初始化过程。通过将重复的初始化代码封装在宏中,可以更简洁地重用这些代码。
  • 关键操作
    1. 服务构建器初始化:使用sc_service::ServiceBuilder创建一个新的服务构建器实例,这是启动Substrate服务的第一步。
    2. 选择链:配置选择链的逻辑,以便节点知道如何选择最长的链作为主链。这里使用的是sc_client::LongestChain策略。
    3. 交易池:设置交易池,这是管理和维护待处理交易的组件。
    4. 导入队列:配置区块导入队列,这包括设置Aura共识和GRANDPA的区块导入逻辑。Aura用于生产新区块,而GRANDPA负责最终性确定。

new_full 函数

  • 目的:构建一个完整节点服务。这个函数使用new_full_start宏来初始化服务的核心部分,并进一步配置节点以参与共识等。
  • 关键配置
    1. 网络协议:设置网络协议,以便节点可以与其他节点通信。
    2. 最终性证明提供者:配置节点以提供关于区块最终性的证明。
    3. 共识参与:根据节点的角色(如是否作为权威节点),配置其参与共识的相关逻辑。对于权威节点,需要设置AURA和GRANDPA共识机制。
    4. GRANDPA配置:配置GRANDPA共识,包括启用观察者模式或全节点投票者模式。

new_light 函数

  • 目的:构建一个轻节点服务。轻节点不保留完整的区块链数据,而是仅维护必要的信息以验证交易和区块。
  • 关键配置
    1. 选择链交易池与完整节点相同,但为轻客户端特定的实现。
    2. 导入队列:配置轻节点的区块导入队列,以及最终性证明请求构建器。
    3. 网络协议最终性证明提供者与完整节点相似,但适应轻节点的特性。

展示了如何使用Substrate框架来构建和配置完整节点和轻节点服务,包括设置交易池、选择链策略、配置共识机制等关键组件。这是构建区块链节点的核心步骤,确保节点能够正常参与区块链网络的运作。

  1. macro_rules! new_full_start {
  2. ($config:expr) => {{
  3. let mut import_setup = None;
  4. let inherent_data_providers = sp_inherents::InherentDataProviders::new();
  5. let builder = sc_service::ServiceBuilder::new_full::<
  6. utxo_runtime::opaque::Block, utxo_runtime::RuntimeApi, crate::service::Executor
  7. >($config)?
  8. .with_select_chain(|_config, backend| {
  9. Ok(sc_client::LongestChain::new(backend.clone()))
  10. })?
  11. .with_transaction_pool(|config, client, _fetcher| {
  12. let pool_api = sc_transaction_pool::FullChainApi::new(client.clone());
  13. let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
  14. let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
  15. let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
  16. Ok(maintainable_pool)
  17. })?
  18. .with_import_queue(|_config, client, mut select_chain, transaction_pool| {
  19. let select_chain = select_chain.take()
  20. .ok_or_else(|| sc_service::Error::SelectChainRequired)?;
  21. let (grandpa_block_import, grandpa_link) =
  22. grandpa::block_import::<_, _, _, utxo_runtime::RuntimeApi, _>(
  23. client.clone(), &*client, select_chain
  24. )?;
  25. let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(
  26. grandpa_block_import.clone(), client.clone(),
  27. );
  28. let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _>(
  29. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  30. aura_block_import,
  31. Some(Box::new(grandpa_block_import.clone())),
  32. None,
  33. client,
  34. inherent_data_providers.clone(),
  35. Some(transaction_pool),
  36. )?;
  37. import_setup = Some((grandpa_block_import, grandpa_link));
  38. Ok(import_queue)
  39. })?;
  40. (builder, import_setup, inherent_data_providers)
  41. }}
  42. }
  43. /// Builds a new service for a full client.
  44. pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
  45. -> Result<impl AbstractService, ServiceError>
  46. {
  47. let is_authority = config.roles.is_authority();
  48. let force_authoring = config.force_authoring;
  49. let name = config.name.clone();
  50. let disable_grandpa = config.disable_grandpa;
  51. // sentry nodes announce themselves as authorities to the network
  52. // and should run the same protocols authorities do, but it should
  53. // never actively participate in any consensus process.
  54. let participates_in_consensus = is_authority && !config.sentry_mode;
  55. let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);
  56. let (block_import, grandpa_link) =
  57. import_setup.take()
  58. .expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
  59. let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?
  60. .with_finality_proof_provider(|client, backend|
  61. Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
  62. )?
  63. .build()?;
  64. if participates_in_consensus {
  65. let proposer = sc_basic_authority::ProposerFactory {
  66. client: service.client(),
  67. transaction_pool: service.transaction_pool(),
  68. };
  69. let client = service.client();
  70. let select_chain = service.select_chain()
  71. .ok_or(ServiceError::SelectChainRequired)?;
  72. let can_author_with =
  73. sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());
  74. let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _, _>(
  75. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  76. client,
  77. select_chain,
  78. block_import,
  79. proposer,
  80. service.network(),
  81. inherent_data_providers.clone(),
  82. force_authoring,
  83. service.keystore(),
  84. can_author_with,
  85. )?;
  86. // the AURA authoring task is considered essential, i.e. if it
  87. // fails we take down the service with it.
  88. service.spawn_essential_task(aura);
  89. }
  90. // if the node isn't actively participating in consensus then it doesn't
  91. // need a keystore, regardless of which protocol we use below.
  92. let keystore = if participates_in_consensus {
  93. Some(service.keystore())
  94. } else {
  95. None
  96. };
  97. let grandpa_config = grandpa::Config {
  98. // FIXME #1578 make this available through chainspec
  99. gossip_duration: Duration::from_millis(333),
  100. justification_period: 512,
  101. name: Some(name),
  102. observer_enabled: true,
  103. keystore,
  104. is_authority,
  105. };
  106. match (is_authority, disable_grandpa) {
  107. (false, false) => {
  108. // start the lightweight GRANDPA observer
  109. service.spawn_task(grandpa::run_grandpa_observer(
  110. grandpa_config,
  111. grandpa_link,
  112. service.network(),
  113. service.on_exit(),
  114. service.spawn_task_handle(),
  115. )?);
  116. },
  117. (true, false) => {
  118. // start the full GRANDPA voter
  119. let voter_config = grandpa::GrandpaParams {
  120. config: grandpa_config,
  121. link: grandpa_link,
  122. network: service.network(),
  123. inherent_data_providers: inherent_data_providers.clone(),
  124. on_exit: service.on_exit(),
  125. telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
  126. voting_rule: grandpa::VotingRulesBuilder::default().build(),
  127. executor: service.spawn_task_handle(),
  128. };
  129. // the GRANDPA voter task is considered infallible, i.e.
  130. // if it fails we take down the service with it.
  131. service.spawn_essential_task(grandpa::run_grandpa_voter(voter_config)?);
  132. },
  133. (_, true) => {
  134. grandpa::setup_disabled_grandpa(
  135. service.client(),
  136. &inherent_data_providers,
  137. service.network(),
  138. )?;
  139. },
  140. }
  141. Ok(service)
  142. }
  143. /// Builds a new service for a light client.
  144. pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
  145. -> Result<impl AbstractService, ServiceError>
  146. {
  147. let inherent_data_providers = InherentDataProviders::new();
  148. ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
  149. .with_select_chain(|_config, backend| {
  150. Ok(LongestChain::new(backend.clone()))
  151. })?
  152. .with_transaction_pool(|config, client, fetcher| {
  153. let fetcher = fetcher
  154. .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
  155. let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone());
  156. let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
  157. let maintainer = sc_transaction_pool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
  158. let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
  159. Ok(maintainable_pool)
  160. })?
  161. .with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
  162. let fetch_checker = fetcher
  163. .map(|fetcher| fetcher.checker().clone())
  164. .ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
  165. let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
  166. client.clone(), backend, &*client.clone(), Arc::new(fetch_checker),
  167. )?;
  168. let finality_proof_import = grandpa_block_import.clone();
  169. let finality_proof_request_builder =
  170. finality_proof_import.create_finality_proof_request_builder();
  171. let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, ()>(
  172. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  173. grandpa_block_import,
  174. None,
  175. Some(Box::new(finality_proof_import)),
  176. client,
  177. inherent_data_providers.clone(),
  178. None,
  179. )?;
  180. Ok((import_queue, finality_proof_request_builder))
  181. })?
  182. .with_network_protocol(|_| Ok(NodeProtocol::new()))?
  183. .with_finality_proof_provider(|client, backend|
  184. Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
  185. )?
  186. .build()
  187. }

chain_spec.rs

这段代码是基于Rust编程语言和Substrate框架,用于构建区块链的一个例子。Substrate是一个用于创建定制区块链的框架,由Parity Technologies开发。这段代码特别是为了创建和配置一个新的Substrate-based 区块链的链规格(ChainSpec)和创始区块(genesis block)。以下是代码的关键部分的详细解释:

  1. 导入语句: 代码开始部分的use语句导入了在文件中使用的模块和类型。这些导入包括Substrate的核心模块、加密组件、账户识别和验证机制等。

  2. 类型别名和枚举:

    • ChainSpec是一个特化的类型,用于定义链的规格。
    • Alternative枚举定义了两种区块链环境:DevelopmentLocalTestnet,分别代表开发环境和本地测试网络。
  3. 帮助函数: 提供了几个函数用于从种子生成公私钥对、账户ID、和权威节点ID(用于Aura和Grandpa共识算法)。

  4. 链规格加载: Alternative枚举的load方法根据枚举值生成相应的ChainSpec。这个ChainSpec定义了链的名称、节点、创世账户和UTXO等配置。

  5. testnet_genesis函数: 这个函数生成了链的创始区块配置,包括系统配置、Indices配置、初始余额、Sudo权限、Aura和Grandpa共识机制的配置,以及UTXO的初始设置。这个函数也输出了一些用于UI演示的辅助信息。

  6. 具体配置:

    • SystemConfig包含了链代码和变化跟踪配置。
    • IndicesConfig定义了具有特定索引的账户。
    • BalancesConfig配置了初始账户及其余额。
    • SudoConfig为特定账户提供了超级用户权限。
    • AuraConfigGrandpaConfig分别配置了用于Aura和Grandpa共识算法的验证者。
  7. Genesis UTXO配置: 在UTXO模型中,初始的交易输出(UTXOs)被分配给特定的公钥。这为链上的初始状态配置了一系列可以被花费的UTXOs。

总的来说,这段代码是一个基于Substrate的区块链项目的配置片段,它设置了链的初始状态、共识机制、账户余额等。通过调整这些配置,开发者可以定制自己的区块链网络来满足特定的需求。

  1. macro_rules! new_full_start {
  2. ($config:expr) => {{
  3. let mut import_setup = None;
  4. let inherent_data_providers = sp_inherents::InherentDataProviders::new();
  5. let builder = sc_service::ServiceBuilder::new_full::<
  6. utxo_runtime::opaque::Block, utxo_runtime::RuntimeApi, crate::service::Executor
  7. >($config)?
  8. .with_select_chain(|_config, backend| {
  9. Ok(sc_client::LongestChain::new(backend.clone()))
  10. })?
  11. .with_transaction_pool(|config, client, _fetcher| {
  12. let pool_api = sc_transaction_pool::FullChainApi::new(client.clone());
  13. let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
  14. let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
  15. let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
  16. Ok(maintainable_pool)
  17. })?
  18. .with_import_queue(|_config, client, mut select_chain, transaction_pool| {
  19. let select_chain = select_chain.take()
  20. .ok_or_else(|| sc_service::Error::SelectChainRequired)?;
  21. let (grandpa_block_import, grandpa_link) =
  22. grandpa::block_import::<_, _, _, utxo_runtime::RuntimeApi, _>(
  23. client.clone(), &*client, select_chain
  24. )?;
  25. let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(
  26. grandpa_block_import.clone(), client.clone(),
  27. );
  28. let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _>(
  29. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  30. aura_block_import,
  31. Some(Box::new(grandpa_block_import.clone())),
  32. None,
  33. client,
  34. inherent_data_providers.clone(),
  35. Some(transaction_pool),
  36. )?;
  37. import_setup = Some((grandpa_block_import, grandpa_link));
  38. Ok(import_queue)
  39. })?;
  40. (builder, import_setup, inherent_data_providers)
  41. }}
  42. }
  43. /// Builds a new service for a full client.
  44. pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
  45. -> Result<impl AbstractService, ServiceError>
  46. {
  47. let is_authority = config.roles.is_authority();
  48. let force_authoring = config.force_authoring;
  49. let name = config.name.clone();
  50. let disable_grandpa = config.disable_grandpa;
  51. // sentry nodes announce themselves as authorities to the network
  52. // and should run the same protocols authorities do, but it should
  53. // never actively participate in any consensus process.
  54. let participates_in_consensus = is_authority && !config.sentry_mode;
  55. let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);
  56. let (block_import, grandpa_link) =
  57. import_setup.take()
  58. .expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
  59. let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?
  60. .with_finality_proof_provider(|client, backend|
  61. Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
  62. )?
  63. .build()?;
  64. if participates_in_consensus {
  65. let proposer = sc_basic_authority::ProposerFactory {
  66. client: service.client(),
  67. transaction_pool: service.transaction_pool(),
  68. };
  69. let client = service.client();
  70. let select_chain = service.select_chain()
  71. .ok_or(ServiceError::SelectChainRequired)?;
  72. let can_author_with =
  73. sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());
  74. let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _, _>(
  75. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  76. client,
  77. select_chain,
  78. block_import,
  79. proposer,
  80. service.network(),
  81. inherent_data_providers.clone(),
  82. force_authoring,
  83. service.keystore(),
  84. can_author_with,
  85. )?;
  86. // the AURA authoring task is considered essential, i.e. if it
  87. // fails we take down the service with it.
  88. service.spawn_essential_task(aura);
  89. }
  90. // if the node isn't actively participating in consensus then it doesn't
  91. // need a keystore, regardless of which protocol we use below.
  92. let keystore = if participates_in_consensus {
  93. Some(service.keystore())
  94. } else {
  95. None
  96. };
  97. let grandpa_config = grandpa::Config {
  98. // FIXME #1578 make this available through chainspec
  99. gossip_duration: Duration::from_millis(333),
  100. justification_period: 512,
  101. name: Some(name),
  102. observer_enabled: true,
  103. keystore,
  104. is_authority,
  105. };
  106. match (is_authority, disable_grandpa) {
  107. (false, false) => {
  108. // start the lightweight GRANDPA observer
  109. service.spawn_task(grandpa::run_grandpa_observer(
  110. grandpa_config,
  111. grandpa_link,
  112. service.network(),
  113. service.on_exit(),
  114. service.spawn_task_handle(),
  115. )?);
  116. },
  117. (true, false) => {
  118. // start the full GRANDPA voter
  119. let voter_config = grandpa::GrandpaParams {
  120. config: grandpa_config,
  121. link: grandpa_link,
  122. network: service.network(),
  123. inherent_data_providers: inherent_data_providers.clone(),
  124. on_exit: service.on_exit(),
  125. telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
  126. voting_rule: grandpa::VotingRulesBuilder::default().build(),
  127. executor: service.spawn_task_handle(),
  128. };
  129. // the GRANDPA voter task is considered infallible, i.e.
  130. // if it fails we take down the service with it.
  131. service.spawn_essential_task(grandpa::run_grandpa_voter(voter_config)?);
  132. },
  133. (_, true) => {
  134. grandpa::setup_disabled_grandpa(
  135. service.client(),
  136. &inherent_data_providers,
  137. service.network(),
  138. )?;
  139. },
  140. }
  141. Ok(service)
  142. }
  143. /// Builds a new service for a light client.
  144. pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
  145. -> Result<impl AbstractService, ServiceError>
  146. {
  147. let inherent_data_providers = InherentDataProviders::new();
  148. ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
  149. .with_select_chain(|_config, backend| {
  150. Ok(LongestChain::new(backend.clone()))
  151. })?
  152. .with_transaction_pool(|config, client, fetcher| {
  153. let fetcher = fetcher
  154. .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
  155. let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone());
  156. let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
  157. let maintainer = sc_transaction_pool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
  158. let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
  159. Ok(maintainable_pool)
  160. })?
  161. .with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
  162. let fetch_checker = fetcher
  163. .map(|fetcher| fetcher.checker().clone())
  164. .ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
  165. let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
  166. client.clone(), backend, &*client.clone(), Arc::new(fetch_checker),
  167. )?;
  168. let finality_proof_import = grandpa_block_import.clone();
  169. let finality_proof_request_builder =
  170. finality_proof_import.create_finality_proof_request_builder();
  171. let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, ()>(
  172. sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
  173. grandpa_block_import,
  174. None,
  175. Some(Box::new(finality_proof_import)),
  176. client,
  177. inherent_data_providers.clone(),
  178. None,
  179. )?;
  180. Ok((import_queue, finality_proof_request_builder))
  181. })?
  182. .with_network_protocol(|_| Ok(NodeProtocol::new()))?
  183. .with_finality_proof_provider(|client, backend|
  184. Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
  185. )?
  186. .build()
  187. }详细解释

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/315058
推荐阅读
相关标签
  

闽ICP备14008679号