// This file is part of Substrate.
// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! Chain api required for the transaction pool.
use std::{marker::PhantomData, pin::Pin, sync::Arc};
use codec::{Decode, Encode};
use futures::{
channel::oneshot, executor::{ThreadPool, ThreadPoolBuilder}, future::{Future, FutureExt, ready, Ready},
};
use sc_client_api::{
blockchain::HeaderBackend,
light::{Fetcher, RemoteCallRequest, RemoteBodyRequest},
BlockBackend,
};
use sp_runtime::{
generic::BlockId, traits::{self, Block as BlockT, BlockIdTo, Header as HeaderT, Hash as HashT},
transaction_validity::{TransactionValidity, TransactionSource},
};
use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
use sp_api::{ProvideRuntimeApi, ApiExt};
use crate::error::{self, Error};
/// The transaction pool logic for full client.
pub struct FullChainApi {
client: Arc,
pool: ThreadPool,
_marker: PhantomData,
}
impl FullChainApi where
Block: BlockT,
Client: ProvideRuntimeApi + BlockIdTo,
{
/// Create new transaction pool logic.
pub fn new(client: Arc) -> Self {
FullChainApi {
client,
pool: ThreadPoolBuilder::new()
.pool_size(2)
.name_prefix("txpool-verifier")
.create()
.expect("Failed to spawn verifier threads, that are critical for node operation."),
_marker: Default::default()
}
}
}
impl sc_transaction_graph::ChainApi for FullChainApi where
Block: BlockT,
Client: ProvideRuntimeApi + BlockBackend + BlockIdTo,
Client: Send + Sync + 'static,
Client::Api: TaggedTransactionQueue,
sp_api::ApiErrorFor: Send,
{
type Block = Block;
type Hash = Block::Hash;
type Error = error::Error;
type ValidationFuture = Pin> + Send>>;
type BodyFuture = Ready::Extrinsic>>>>;
fn block_body(&self, id: &BlockId) -> Self::BodyFuture {
ready(self.client.block_body(&id).map_err(|e| error::Error::from(e)))
}
fn validate_transaction(
&self,
at: &BlockId,
source: TransactionSource,
uxt: sc_transaction_graph::ExtrinsicFor,
) -> Self::ValidationFuture {
let (tx, rx) = oneshot::channel();
let client = self.client.clone();
let at = at.clone();
self.pool.spawn_ok(futures_diagnose::diagnose("validate-transaction", async move {
sp_tracing::enter_span!("validate_transaction");
let runtime_api = client.runtime_api();
let has_v2 = sp_tracing::tracing_span! { "check_version";
runtime_api
.has_api_with::, _>(
&at, |v| v >= 2,
)
.unwrap_or_default()
};
sp_tracing::enter_span!("runtime::validate_transaction");
let res = if has_v2 {
runtime_api.validate_transaction(&at, source, uxt)
} else {
#[allow(deprecated)] // old validate_transaction
runtime_api.validate_transaction_before_version_2(&at, uxt)
};
let res = res.map_err(|e| Error::RuntimeApi(format!("{:?}", e)));
if let Err(e) = tx.send(res) {
log::warn!("Unable to send a validate transaction result: {:?}", e);
}
}));
Box::pin(async move {
match rx.await {
Ok(r) => r,
Err(_) => Err(Error::RuntimeApi("Validation was canceled".into())),
}
})
}
fn block_id_to_number(
&self,
at: &BlockId,
) -> error::Result