基于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结构中的状态字段来操作。
    static void msg(const char *msg) {
        fprintf(stderr, "%s\n", msg);
    }
    
    static void die(const char *msg) {
        int err = errno;
        fprintf(stderr, "[%d] %s\n", err, msg);
        abort();
    }
    
    static uint64_t get_monotonic_usec() {
        timespec tv = {0, 0};
        clock_gettime(CLOCK_MONOTONIC, &tv);
        return uint64_t(tv.tv_sec) * 1000000 + tv.tv_nsec / 1000;
    }
    
    static void fd_set_nb(int fd) {
        errno = 0;
        int flags = fcntl(fd, F_GETFL, 0);
        if (errno) {
            die("fcntl error");
            return;
        }
    
        flags |= O_NONBLOCK;
    
        errno = 0;
        (void)fcntl(fd, F_SETFL, flags);
        if (errno) {
            die("fcntl error");
        }
    }
    
    struct Conn;
    
    // global variables
    static struct {
        HMap db;
        // a map of all client connections, keyed by fd
        std::vector<Conn *> fd2conn;
        // timers for idle connections
        DList idle_list;
        // timers for TTLs
        std::vector<HeapItem> heap;
        // the thread pool
        TheadPool tp;
    } g_data;
    
    const size_t k_max_msg = 4096;
    
    enum {
        STATE_REQ = 0,
        STATE_RES = 1,
        STATE_END = 2,  // mark the connection for deletion
    };
    
    struct Conn {
        int fd = -1;
        uint32_t state = 0;     // either STATE_REQ or STATE_RES
        // buffer for reading
        size_t rbuf_size = 0;
        uint8_t rbuf[4 + k_max_msg];
        // buffer for writing
        size_t wbuf_size = 0;
        size_t wbuf_sent = 0;
        uint8_t wbuf[4 + k_max_msg];
        uint64_t idle_start = 0;
        // timer
        DList idle_list;
    };
    
    static void conn_put(std::vector<Conn *> &fd2conn, struct Conn *conn) {
        if (fd2conn.size() <= (size_t)conn->fd) {
            fd2conn.resize(conn->fd + 1);
        }
        fd2conn[conn->fd] = conn;
    }
    
    static int32_t accept_new_conn(int fd) {
        // accept
        struct sockaddr_in client_addr = {};
        socklen_t socklen = sizeof(client_addr);
        int connfd = accept(fd, (struct sockaddr *)&client_addr, &socklen);
        if (connfd < 0) {
            msg("accept() error");
            return -1;  // error
        }
    
        // set the new connection fd to nonblocking mode
        fd_set_nb(connfd);
        // creating the struct Conn
        struct Conn *conn = (struct Conn *)malloc(sizeof(struct Conn));
        if (!conn) {
            close(connfd);
            return -1;
        }
        conn->fd = connfd;
        conn->state = STATE_REQ;
        conn->rbuf_size = 0;
        conn->wbuf_size = 0;
        conn->wbuf_sent = 0;
        conn->idle_start = get_monotonic_usec();
        dlist_insert_before(&g_data.idle_list, &conn->idle_list);
        conn_put(g_data.fd2conn, conn);
        return 0;
    }
    
    static void state_req(Conn *conn);
    static void state_res(Conn *conn);
    
    const size_t k_max_args = 1024;
    
    static int32_t parse_req(
        const uint8_t *data, size_t len, std::vector<std::string> &out)
    {
        if (len < 4) {
            return -1;
        }
        uint32_t n = 0;
        memcpy(&n, &data[0], 4);
        if (n > k_max_args) {
            return -1;
        }
    
        size_t pos = 4;
        while (n--) {
            if (pos + 4 > len) {
                return -1;
            }
            uint32_t sz = 0;
            memcpy(&sz, &data[pos], 4);
            if (pos + 4 + sz > len) {
                return -1;
            }
            out.push_back(std::string((char *)&data[pos + 4], sz));
            pos += 4 + sz;
        }
    
        if (pos != len) {
            return -1;  // trailing garbage
        }
        return 0;
    }
    
    enum {
        T_STR = 0,
        T_ZSET = 1,
    };
    
    // the structure for the key
    struct Entry {
        struct HNode node;
        std::string key;
        std::string val;
        uint32_t type = 0;
        ZSet *zset = NULL;
        // for TTLs
        size_t heap_idx = -1;
    };
    
    static bool entry_eq(HNode *lhs, HNode *rhs) {
        struct Entry *le = container_of(lhs, struct Entry, node);
        struct Entry *re = container_of(rhs, struct Entry, node);
        return le->key == re->key;
    }
    
    enum {
        ERR_UNKNOWN = 1,
        ERR_2BIG = 2,
        ERR_TYPE = 3,
        ERR_ARG = 4,
    };
    
    static void out_nil(std::string &out) {
        out.push_back(SER_NIL);
    }
    
    static void out_str(std::string &out, const char *s, size_t size) {
        out.push_back(SER_STR);
        uint32_t len = (uint32_t)size;
        out.append((char *)&len, 4);
        out.append(s, len);
    }
    
    static void out_str(std::string &out, const std::string &val) {
        return out_str(out, val.data(), val.size());
    }
    
    static void out_int(std::string &out, int64_t val) {
        out.push_back(SER_INT);
        out.append((char *)&val, 8);
    }
    
    static void out_dbl(std::string &out, double val) {
        out.push_back(SER_DBL);
        out.append((char *)&val, 8);
    }
    
    static void out_err(std::string &out, int32_t code, const std::string &msg) {
        out.push_back(SER_ERR);
        out.append((char *)&code, 4);
        uint32_t len = (uint32_t)msg.size();
        out.append((char *)&len, 4);
        out.append(msg);
    }

  • 这段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命令,并在收到信号时发送一个退出消息。

use crate::service;
use futures::{future::{select, Map}, FutureExt, TryFutureExt, channel::oneshot, compat::Future01CompatExt};
use std::cell::RefCell;
use tokio::runtime::Runtime;
pub use sc_cli::{VersionInfo, IntoExit, error};
use sc_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom};
use sc_service::{AbstractService, Roles as ServiceRoles, Configuration};
use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};
use crate::chain_spec;
use log::info;

/// Parse command line arguments into service configuration.
pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()> where
	I: IntoIterator<Item = T>,
	T: Into<std::ffi::OsString> + Clone,
	E: IntoExit,
{
	type Config<T> = Configuration<(), T>;
	match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "substrate-node", args) {
		ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
		|exit, _cli_args, _custom_args, config: Config<_>| {
			info!("{}", version.name);
			info!("  version {}", config.full_version());
			info!("  by {}, 2017, 2018", version.author);
			info!("Chain specification: {}", config.chain_spec.name());
			info!("Node name: {}", config.name);
			info!("Roles: {}", display_role(&config));
			let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
			match config.roles {
				ServiceRoles::LIGHT => run_until_exit(
					runtime,
					service::new_light(config)?,
					exit
				),
				_ => run_until_exit(
					runtime,
					service::new_full(config)?,
					exit
				),
			}
		}),
		ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
		ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
			Ok(new_full_start!(config).0), load_spec, exit),
		ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
			Ok(new_full_start!(config).0), load_spec, exit),
		ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_>|
			Ok(new_full_start!(config).0), load_spec, exit),
		ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
		ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>|
			Ok(new_full_start!(config).0), load_spec),
		ParseAndPrepare::CustomCommand(_) => Ok(())
	}?;

	Ok(())
}

fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
    Ok(match chain_spec::Alternative::from(id) {
        Some(spec) => Some(spec.load()?),
        None => None,
    })
}

fn run_until_exit<T, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
where
    T: AbstractService,
    E: IntoExit,
{
    let (exit_send, exit) = oneshot::channel();

    let informant = informant::build(&service);

    let future = select(exit, informant)
        .map(|_| Ok(()))
        .compat();

    runtime.executor().spawn(future);

    // we eagerly drop the service so that the internal exit future is fired,
    // but we need to keep holding a reference to the global telemetry guard
    let _telemetry = service.telemetry();

    let service_res = {
        let exit = e.into_exit();
        let service = service
            .map_err(|err| error::Error::Service(err))
            .compat();
        let select = select(service, exit)
            .map(|_| Ok(()))
            .compat();
        runtime.block_on(select)
    };

    let _ = exit_send.send(());

    // TODO [andre]: timeout this future #1318

    use futures01::Future;

    let _ = runtime.shutdown_on_idle().wait();

    service_res
}

// handles ctrl-c
pub struct Exit;
impl IntoExit for Exit {
    type Exit = Map<oneshot::Receiver<()>, fn(Result<(), oneshot::Canceled>) -> ()>;
    fn into_exit(self) -> Self::Exit {
        // can't use signal directly here because CtrlC takes only `Fn`.
        let (exit_send, exit) = oneshot::channel();

        let exit_send_cell = RefCell::new(Some(exit_send));
        ctrlc::set_handler(move || {
            let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();
            if let Some(exit_send) = exit_send {
                exit_send.send(()).expect("Error sending exit notification");
            }
        }).expect("Error setting Ctrl-C handler");

        exit.map(drop)
    }
}详细见即使

这段代码主要涉及构建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框架来构建和配置完整节点和轻节点服务,包括设置交易池、选择链策略、配置共识机制等关键组件。这是构建区块链节点的核心步骤,确保节点能够正常参与区块链网络的运作。

macro_rules! new_full_start {
	($config:expr) => {{
		let mut import_setup = None;
		let inherent_data_providers = sp_inherents::InherentDataProviders::new();

		let builder = sc_service::ServiceBuilder::new_full::<
			utxo_runtime::opaque::Block, utxo_runtime::RuntimeApi, crate::service::Executor
		>($config)?
			.with_select_chain(|_config, backend| {
				Ok(sc_client::LongestChain::new(backend.clone()))
			})?
			.with_transaction_pool(|config, client, _fetcher| {
				let pool_api = sc_transaction_pool::FullChainApi::new(client.clone());
				let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
				let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
				let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
				Ok(maintainable_pool)
			})?
			.with_import_queue(|_config, client, mut select_chain, transaction_pool| {
				let select_chain = select_chain.take()
					.ok_or_else(|| sc_service::Error::SelectChainRequired)?;

				let (grandpa_block_import, grandpa_link) =
					grandpa::block_import::<_, _, _, utxo_runtime::RuntimeApi, _>(
						client.clone(), &*client, select_chain
					)?;

				let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(
					grandpa_block_import.clone(), client.clone(),
				);

				let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _>(
					sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
					aura_block_import,
					Some(Box::new(grandpa_block_import.clone())),
					None,
					client,
					inherent_data_providers.clone(),
					Some(transaction_pool),
				)?;

				import_setup = Some((grandpa_block_import, grandpa_link));

				Ok(import_queue)
			})?;

		(builder, import_setup, inherent_data_providers)
	}}
}

/// Builds a new service for a full client.
pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
	-> Result<impl AbstractService, ServiceError>
{
	let is_authority = config.roles.is_authority();
	let force_authoring = config.force_authoring;
	let name = config.name.clone();
	let disable_grandpa = config.disable_grandpa;

	// sentry nodes announce themselves as authorities to the network
	// and should run the same protocols authorities do, but it should
	// never actively participate in any consensus process.
	let participates_in_consensus = is_authority && !config.sentry_mode;

	let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);

	let (block_import, grandpa_link) =
		import_setup.take()
			.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");

	let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?
		.with_finality_proof_provider(|client, backend|
			Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
		)?
		.build()?;

	if participates_in_consensus {
		let proposer = sc_basic_authority::ProposerFactory {
			client: service.client(),
			transaction_pool: service.transaction_pool(),
		};

		let client = service.client();
		let select_chain = service.select_chain()
			.ok_or(ServiceError::SelectChainRequired)?;

		let can_author_with =
			sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());

		let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _, _>(
			sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
			client,
			select_chain,
			block_import,
			proposer,
			service.network(),
			inherent_data_providers.clone(),
			force_authoring,
			service.keystore(),
			can_author_with,
		)?;

		// the AURA authoring task is considered essential, i.e. if it
		// fails we take down the service with it.
		service.spawn_essential_task(aura);
	}

	// if the node isn't actively participating in consensus then it doesn't
	// need a keystore, regardless of which protocol we use below.
	let keystore = if participates_in_consensus {
		Some(service.keystore())
	} else {
		None
	};

	let grandpa_config = grandpa::Config {
		// FIXME #1578 make this available through chainspec
		gossip_duration: Duration::from_millis(333),
		justification_period: 512,
		name: Some(name),
		observer_enabled: true,
		keystore,
		is_authority,
	};

	match (is_authority, disable_grandpa) {
		(false, false) => {
			// start the lightweight GRANDPA observer
			service.spawn_task(grandpa::run_grandpa_observer(
				grandpa_config,
				grandpa_link,
				service.network(),
				service.on_exit(),
				service.spawn_task_handle(),
			)?);
		},
		(true, false) => {
			// start the full GRANDPA voter
			let voter_config = grandpa::GrandpaParams {
				config: grandpa_config,
				link: grandpa_link,
				network: service.network(),
				inherent_data_providers: inherent_data_providers.clone(),
				on_exit: service.on_exit(),
				telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
				voting_rule: grandpa::VotingRulesBuilder::default().build(),
				executor: service.spawn_task_handle(),
			};

			// the GRANDPA voter task is considered infallible, i.e.
			// if it fails we take down the service with it.
			service.spawn_essential_task(grandpa::run_grandpa_voter(voter_config)?);
		},
		(_, true) => {
			grandpa::setup_disabled_grandpa(
				service.client(),
				&inherent_data_providers,
				service.network(),
			)?;
		},
	}

	Ok(service)
}

/// Builds a new service for a light client.
pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
	-> Result<impl AbstractService, ServiceError>
{
	let inherent_data_providers = InherentDataProviders::new();

	ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
		.with_select_chain(|_config, backend| {
			Ok(LongestChain::new(backend.clone()))
		})?
		.with_transaction_pool(|config, client, fetcher| {
			let fetcher = fetcher
				.ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
			let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone());
			let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
			let maintainer = sc_transaction_pool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
			let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
			Ok(maintainable_pool)
		})?
		.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
			let fetch_checker = fetcher
				.map(|fetcher| fetcher.checker().clone())
				.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
			let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
				client.clone(), backend, &*client.clone(), Arc::new(fetch_checker),
			)?;
			let finality_proof_import = grandpa_block_import.clone();
			let finality_proof_request_builder =
				finality_proof_import.create_finality_proof_request_builder();

			let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, ()>(
				sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
				grandpa_block_import,
				None,
				Some(Box::new(finality_proof_import)),
				client,
				inherent_data_providers.clone(),
				None,
			)?;

			Ok((import_queue, finality_proof_request_builder))
		})?
		.with_network_protocol(|_| Ok(NodeProtocol::new()))?
		.with_finality_proof_provider(|client, backend|
			Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
		)?
		.build()
}

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的区块链项目的配置片段,它设置了链的初始状态、共识机制、账户余额等。通过调整这些配置,开发者可以定制自己的区块链网络来满足特定的需求。

macro_rules! new_full_start {
	($config:expr) => {{
		let mut import_setup = None;
		let inherent_data_providers = sp_inherents::InherentDataProviders::new();

		let builder = sc_service::ServiceBuilder::new_full::<
			utxo_runtime::opaque::Block, utxo_runtime::RuntimeApi, crate::service::Executor
		>($config)?
			.with_select_chain(|_config, backend| {
				Ok(sc_client::LongestChain::new(backend.clone()))
			})?
			.with_transaction_pool(|config, client, _fetcher| {
				let pool_api = sc_transaction_pool::FullChainApi::new(client.clone());
				let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
				let maintainer = sc_transaction_pool::FullBasicPoolMaintainer::new(pool.pool().clone(), client);
				let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
				Ok(maintainable_pool)
			})?
			.with_import_queue(|_config, client, mut select_chain, transaction_pool| {
				let select_chain = select_chain.take()
					.ok_or_else(|| sc_service::Error::SelectChainRequired)?;

				let (grandpa_block_import, grandpa_link) =
					grandpa::block_import::<_, _, _, utxo_runtime::RuntimeApi, _>(
						client.clone(), &*client, select_chain
					)?;

				let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(
					grandpa_block_import.clone(), client.clone(),
				);

				let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, _>(
					sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
					aura_block_import,
					Some(Box::new(grandpa_block_import.clone())),
					None,
					client,
					inherent_data_providers.clone(),
					Some(transaction_pool),
				)?;

				import_setup = Some((grandpa_block_import, grandpa_link));

				Ok(import_queue)
			})?;

		(builder, import_setup, inherent_data_providers)
	}}
}

/// Builds a new service for a full client.
pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
	-> Result<impl AbstractService, ServiceError>
{
	let is_authority = config.roles.is_authority();
	let force_authoring = config.force_authoring;
	let name = config.name.clone();
	let disable_grandpa = config.disable_grandpa;

	// sentry nodes announce themselves as authorities to the network
	// and should run the same protocols authorities do, but it should
	// never actively participate in any consensus process.
	let participates_in_consensus = is_authority && !config.sentry_mode;

	let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);

	let (block_import, grandpa_link) =
		import_setup.take()
			.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");

	let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?
		.with_finality_proof_provider(|client, backend|
			Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
		)?
		.build()?;

	if participates_in_consensus {
		let proposer = sc_basic_authority::ProposerFactory {
			client: service.client(),
			transaction_pool: service.transaction_pool(),
		};

		let client = service.client();
		let select_chain = service.select_chain()
			.ok_or(ServiceError::SelectChainRequired)?;

		let can_author_with =
			sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());

		let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _, _>(
			sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
			client,
			select_chain,
			block_import,
			proposer,
			service.network(),
			inherent_data_providers.clone(),
			force_authoring,
			service.keystore(),
			can_author_with,
		)?;

		// the AURA authoring task is considered essential, i.e. if it
		// fails we take down the service with it.
		service.spawn_essential_task(aura);
	}

	// if the node isn't actively participating in consensus then it doesn't
	// need a keystore, regardless of which protocol we use below.
	let keystore = if participates_in_consensus {
		Some(service.keystore())
	} else {
		None
	};

	let grandpa_config = grandpa::Config {
		// FIXME #1578 make this available through chainspec
		gossip_duration: Duration::from_millis(333),
		justification_period: 512,
		name: Some(name),
		observer_enabled: true,
		keystore,
		is_authority,
	};

	match (is_authority, disable_grandpa) {
		(false, false) => {
			// start the lightweight GRANDPA observer
			service.spawn_task(grandpa::run_grandpa_observer(
				grandpa_config,
				grandpa_link,
				service.network(),
				service.on_exit(),
				service.spawn_task_handle(),
			)?);
		},
		(true, false) => {
			// start the full GRANDPA voter
			let voter_config = grandpa::GrandpaParams {
				config: grandpa_config,
				link: grandpa_link,
				network: service.network(),
				inherent_data_providers: inherent_data_providers.clone(),
				on_exit: service.on_exit(),
				telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
				voting_rule: grandpa::VotingRulesBuilder::default().build(),
				executor: service.spawn_task_handle(),
			};

			// the GRANDPA voter task is considered infallible, i.e.
			// if it fails we take down the service with it.
			service.spawn_essential_task(grandpa::run_grandpa_voter(voter_config)?);
		},
		(_, true) => {
			grandpa::setup_disabled_grandpa(
				service.client(),
				&inherent_data_providers,
				service.network(),
			)?;
		},
	}

	Ok(service)
}

/// Builds a new service for a light client.
pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
	-> Result<impl AbstractService, ServiceError>
{
	let inherent_data_providers = InherentDataProviders::new();

	ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
		.with_select_chain(|_config, backend| {
			Ok(LongestChain::new(backend.clone()))
		})?
		.with_transaction_pool(|config, client, fetcher| {
			let fetcher = fetcher
				.ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;
			let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone());
			let pool = sc_transaction_pool::BasicPool::new(config, pool_api);
			let maintainer = sc_transaction_pool::LightBasicPoolMaintainer::with_defaults(pool.pool().clone(), client, fetcher);
			let maintainable_pool = sp_transaction_pool::MaintainableTransactionPool::new(pool, maintainer);
			Ok(maintainable_pool)
		})?
		.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
			let fetch_checker = fetcher
				.map(|fetcher| fetcher.checker().clone())
				.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
			let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
				client.clone(), backend, &*client.clone(), Arc::new(fetch_checker),
			)?;
			let finality_proof_import = grandpa_block_import.clone();
			let finality_proof_request_builder =
				finality_proof_import.create_finality_proof_request_builder();

			let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair, ()>(
				sc_consensus_aura::SlotDuration::get_or_compute(&*client)?,
				grandpa_block_import,
				None,
				Some(Box::new(finality_proof_import)),
				client,
				inherent_data_providers.clone(),
				None,
			)?;

			Ok((import_queue, finality_proof_request_builder))
		})?
		.with_network_protocol(|_| Ok(NodeProtocol::new()))?
		.with_finality_proof_provider(|client, backend|
			Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
		)?
		.build()
}详细解释

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值