在开源框架Substrate中构建核心区块链基础设施的初学者友好教程。
substrate是一个强大的区块链框架,它允许开发者构建自定义的区块链。Substrate提供了大量的模块化组件,使得开发者可以选择和定制这些组件以满足他们的特定需求,从而极大地加快了区块链开发的速度和灵活性。
Substrate的架构主要可以分为几个关键组件:
- 底层P2P网络: 用于节点之间的通信。
- 共识机制: Substrate支持多种共识算法,比如Aura、GRANDPA等,使得开发者可以根据自己的需求选择最合适的共识机制。
- 运行时环境: 这是Substrate的核心之一,允许在不需重启或硬分叉的情况下升级区块链逻辑。
- 存储: 提供了一个高效的数据库存储解决方案,用于存储区块链状态。
- 交易队列: 管理和优化进入区块链的交易处理。
- 模块化的框架: 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_req, state_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 宏
- 目的:这个宏的主要目的是简化完整节点服务的初始化过程。通过将重复的初始化代码封装在宏中,可以更简洁地重用这些代码。
-
关键操作:
-
服务构建器初始化:使用
sc_service::ServiceBuilder
创建一个新的服务构建器实例,这是启动Substrate服务的第一步。 -
选择链:配置选择链的逻辑,以便节点知道如何选择最长的链作为主链。这里使用的是
sc_client::LongestChain
策略。 - 交易池:设置交易池,这是管理和维护待处理交易的组件。
- 导入队列:配置区块导入队列,这包括设置Aura共识和GRANDPA的区块导入逻辑。Aura用于生产新区块,而GRANDPA负责最终性确定。
-
服务构建器初始化:使用
new_full 函数
-
目的:构建一个完整节点服务。这个函数使用
new_full_start
宏来初始化服务的核心部分,并进一步配置节点以参与共识等。 -
关键配置:
- 网络协议:设置网络协议,以便节点可以与其他节点通信。
- 最终性证明提供者:配置节点以提供关于区块最终性的证明。
- 共识参与:根据节点的角色(如是否作为权威节点),配置其参与共识的相关逻辑。对于权威节点,需要设置AURA和GRANDPA共识机制。
- GRANDPA配置:配置GRANDPA共识,包括启用观察者模式或全节点投票者模式。
new_light 函数
- 目的:构建一个轻节点服务。轻节点不保留完整的区块链数据,而是仅维护必要的信息以验证交易和区块。
-
关键配置:
- 选择链、交易池与完整节点相同,但为轻客户端特定的实现。
- 导入队列:配置轻节点的区块导入队列,以及最终性证明请求构建器。
- 网络协议和最终性证明提供者与完整节点相似,但适应轻节点的特性。
展示了如何使用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)。以下是代码的关键部分的详细解释:
-
导入语句: 代码开始部分的
use
语句导入了在文件中使用的模块和类型。这些导入包括Substrate的核心模块、加密组件、账户识别和验证机制等。 -
类型别名和枚举:
-
ChainSpec
是一个特化的类型,用于定义链的规格。 -
Alternative
枚举定义了两种区块链环境:Development
和LocalTestnet
,分别代表开发环境和本地测试网络。
-
-
帮助函数: 提供了几个函数用于从种子生成公私钥对、账户ID、和权威节点ID(用于Aura和Grandpa共识算法)。
-
链规格加载:
Alternative
枚举的load
方法根据枚举值生成相应的ChainSpec
。这个ChainSpec
定义了链的名称、节点、创世账户和UTXO等配置。 -
testnet_genesis
函数: 这个函数生成了链的创始区块配置,包括系统配置、Indices配置、初始余额、Sudo权限、Aura和Grandpa共识机制的配置,以及UTXO的初始设置。这个函数也输出了一些用于UI演示的辅助信息。 -
具体配置:
-
SystemConfig
包含了链代码和变化跟踪配置。 -
IndicesConfig
定义了具有特定索引的账户。 -
BalancesConfig
配置了初始账户及其余额。 -
SudoConfig
为特定账户提供了超级用户权限。 -
AuraConfig
和GrandpaConfig
分别配置了用于Aura和Grandpa共识算法的验证者。
-
-
Genesis UTXO配置: 在UTXO模型中,初始的交易输出(UTXOs)被分配给特定的公钥。这为链上的初始状态配置了一系列可以被花费的UTXOs。文章来源:https://www.toymoban.com/news/detail-842539.html
总的来说,这段代码是一个基于Substrate的区块链项目的配置片段,它设置了链的初始状态、共识机制、账户余额等。通过调整这些配置,开发者可以定制自己的区块链网络来满足特定的需求。文章来源地址https://www.toymoban.com/news/detail-842539.html
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()
}详细解释
到了这里,关于基于rust的区块链实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!