mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-30 11:55:42 +00:00
Extrinsic pool (#182)
* Use latest version of txpool. * Initial version of the pool. * Fix abstraction. * Implement watchers and notifications. * Return hash from RPC. * Remove commented code. * Remove client dep. * Fix tests.
This commit is contained in:
@@ -231,7 +231,7 @@ impl<B, E> Client<B, E> where
|
||||
/// No changes are made.
|
||||
pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec<u8>, Vec<Vec<u8>>)> {
|
||||
use call_executor::state_to_execution_proof;
|
||||
|
||||
|
||||
let result = self.executor.call(id, method, call_data);
|
||||
let result = result?.return_data;
|
||||
let proof = self.backend.state_at(*id).map(|state| state_to_execution_proof(&state))?;
|
||||
@@ -344,7 +344,8 @@ impl<B, E> Client<B, E> where
|
||||
header: header,
|
||||
is_new_best: is_new_best,
|
||||
};
|
||||
self.import_notification_sinks.lock().retain(|sink| !sink.unbounded_send(notification.clone()).is_err());
|
||||
self.import_notification_sinks.lock()
|
||||
.retain(|sink| sink.unbounded_send(notification.clone()).is_ok());
|
||||
}
|
||||
Ok(ImportResult::Queued)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "substrate-extrinsic-pool"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
error-chain = "0.11"
|
||||
futures = "0.1"
|
||||
log = "0.3"
|
||||
parking_lot = "0.4"
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
transaction-pool = "1.12"
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! External API for extrinsic pool.
|
||||
|
||||
use std::ops::Deref;
|
||||
use txpool::{self, VerifiedTransaction};
|
||||
use primitives::{
|
||||
Hash,
|
||||
block::{Extrinsic, ExtrinsicHash},
|
||||
};
|
||||
|
||||
/// Extrinsic pool error.
|
||||
pub trait Error: ::std::error::Error + Send + Sized {
|
||||
/// Try to extract original `txpool::Error`
|
||||
///
|
||||
/// This implementation is optional and used only to
|
||||
/// provide more descriptive error messages for end users
|
||||
/// of RPC API.
|
||||
fn into_pool_error(self) -> Result<txpool::Error, Self> { Err(self) }
|
||||
}
|
||||
|
||||
impl Error for txpool::Error {
|
||||
fn into_pool_error(self) -> Result<txpool::Error, Self> { Ok(self) }
|
||||
}
|
||||
|
||||
/// Extrinsic pool.
|
||||
pub trait ExtrinsicPool: Send + Sync + 'static {
|
||||
/// Error type
|
||||
type Error: Error;
|
||||
|
||||
/// Submit a collection of extrinsics to the pool.
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error>;
|
||||
}
|
||||
|
||||
// Blanket implementation for anything that `Derefs` to the pool.
|
||||
impl<V, S, E, T> ExtrinsicPool for T where
|
||||
T: Deref<Target=super::Pool<V, S, E>> + Send + Sync + 'static,
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||
E: From<V::Error>,
|
||||
E: From<txpool::Error>,
|
||||
E: Error,
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||
self.deref().submit(xt).map(|result| result.into_iter().map(|xt| *xt.hash()).collect())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Generic extrinsic pool.
|
||||
|
||||
extern crate futures;
|
||||
extern crate parking_lot;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub extern crate transaction_pool as txpool;
|
||||
|
||||
pub mod api;
|
||||
|
||||
mod listener;
|
||||
mod pool;
|
||||
mod watcher;
|
||||
|
||||
pub use self::pool::Pool;
|
||||
pub use self::watcher::Watcher;
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
sync::Arc,
|
||||
collections::HashMap,
|
||||
};
|
||||
use primitives::Hash;
|
||||
use txpool;
|
||||
|
||||
use watcher;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Listener {
|
||||
watchers: HashMap<Hash, watcher::Sender>
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
pub fn create_watcher<T: txpool::VerifiedTransaction<Hash=Hash>>(&mut self, xt: Arc<T>) -> watcher::Watcher {
|
||||
let sender = self.watchers.entry(*xt.hash()).or_insert_with(watcher::Sender::default);
|
||||
sender.new_watcher()
|
||||
}
|
||||
|
||||
pub fn broadcasted(&mut self, hash: &Hash, peers: Vec<String>) {
|
||||
self.fire(hash, |watcher| watcher.broadcast(peers));
|
||||
}
|
||||
|
||||
fn fire<F: FnOnce(&mut watcher::Sender)>(&mut self, hash: &Hash, fun: F) {
|
||||
let clean = if let Some(h) = self.watchers.get_mut(hash) {
|
||||
fun(h);
|
||||
h.is_done()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if clean {
|
||||
self.watchers.remove(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: txpool::VerifiedTransaction<Hash=Hash>> txpool::Listener<T> for Listener {
|
||||
fn added(&mut self, tx: &Arc<T>, old: Option<&Arc<T>>) {
|
||||
if let Some(old) = old {
|
||||
let hash = tx.hash();
|
||||
self.fire(old.hash(), |watcher| watcher.usurped(*hash));
|
||||
}
|
||||
}
|
||||
|
||||
fn dropped(&mut self, tx: &Arc<T>, by: Option<&T>) {
|
||||
self.fire(tx.hash(), |watcher| match by {
|
||||
Some(t) => watcher.usurped(*t.hash()),
|
||||
None => watcher.dropped(),
|
||||
})
|
||||
}
|
||||
|
||||
fn rejected(&mut self, tx: &Arc<T>, reason: &txpool::ErrorKind) {
|
||||
warn!("Extrinsic rejected ({}): {:?}", reason, tx);
|
||||
}
|
||||
|
||||
fn invalid(&mut self, tx: &Arc<T>) {
|
||||
warn!("Extrinsic invalid: {:?}", tx);
|
||||
}
|
||||
|
||||
fn canceled(&mut self, tx: &Arc<T>) {
|
||||
warn!("Extrinsic canceled: {:?}", tx);
|
||||
}
|
||||
|
||||
fn mined(&mut self, tx: &Arc<T>) {
|
||||
// TODO [ToDr] latest block number?
|
||||
let header_hash = 1.into();
|
||||
self.fire(tx.hash(), |watcher| watcher.finalised(header_hash))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use txpool;
|
||||
use primitives::{Hash, block::Extrinsic};
|
||||
|
||||
use listener::Listener;
|
||||
use watcher::Watcher;
|
||||
|
||||
/// Extrinsics pool.
|
||||
pub struct Pool<V, S, E> where
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
{
|
||||
_error: Mutex<PhantomData<E>>,
|
||||
pool: RwLock<txpool::Pool<
|
||||
V::VerifiedTransaction,
|
||||
S,
|
||||
Listener,
|
||||
>>,
|
||||
verifier: V,
|
||||
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<Weak<V::VerifiedTransaction>>>>,
|
||||
}
|
||||
|
||||
impl<V, S, E> Pool<V, S, E> where
|
||||
V: txpool::Verifier<Extrinsic>,
|
||||
S: txpool::Scoring<V::VerifiedTransaction>,
|
||||
V::VerifiedTransaction: txpool::VerifiedTransaction<Hash=Hash>,
|
||||
E: From<V::Error>,
|
||||
E: From<txpool::Error>,
|
||||
{
|
||||
/// Create a new transaction pool.
|
||||
pub fn new(options: txpool::Options, verifier: V, scoring: S) -> Self {
|
||||
Pool {
|
||||
_error: Default::default(),
|
||||
pool: RwLock::new(txpool::Pool::new(Listener::default(), scoring, options)),
|
||||
verifier,
|
||||
import_notification_sinks: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports a pre-verified extrinsic to the pool.
|
||||
pub fn import(&self, xt: V::VerifiedTransaction) -> Result<Arc<V::VerifiedTransaction>, E> {
|
||||
let result = self.pool.write().import(xt)?;
|
||||
|
||||
let weak = Arc::downgrade(&result);
|
||||
self.import_notification_sinks.lock()
|
||||
.retain(|sink| sink.unbounded_send(weak.clone()).is_ok());
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Return an event stream of transactions imported to the pool.
|
||||
pub fn import_notification_stream(&self) -> mpsc::UnboundedReceiver<Weak<V::VerifiedTransaction>> {
|
||||
let (sink, stream) = mpsc::unbounded();
|
||||
self.import_notification_sinks.lock().push(sink);
|
||||
stream
|
||||
}
|
||||
|
||||
/// Invoked when extrinsics are broadcasted.
|
||||
pub fn on_broadcasted(&self, propagated: HashMap<Hash, Vec<String>>) {
|
||||
for (hash, peers) in propagated.into_iter() {
|
||||
self.pool.write().listener_mut().broadcasted(&hash, peers);
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports a bunch of extrinsics to the pool
|
||||
pub fn submit(&self, xts: Vec<Extrinsic>) -> Result<Vec<Arc<V::VerifiedTransaction>>, E> {
|
||||
xts
|
||||
.into_iter()
|
||||
.map(|xt| self.verifier.verify_transaction(xt))
|
||||
.map(|xt| {
|
||||
Ok(self.pool.write().import(xt?)?)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Import a single extrinsic and starts to watch their progress in the pool.
|
||||
pub fn submit_and_watch(&self, xt: Extrinsic) -> Result<Watcher, E> {
|
||||
let xt = self.submit(vec![xt])?.pop().expect("One extrinsic passed; one result returned; qed");
|
||||
Ok(self.pool.write().listener_mut().create_watcher(xt))
|
||||
}
|
||||
|
||||
/// Remove from the pool.
|
||||
pub fn remove(&self, hashes: &[Hash], is_valid: bool) -> Vec<Option<Arc<V::VerifiedTransaction>>> {
|
||||
let mut pool = self.pool.write();
|
||||
let mut results = Vec::with_capacity(hashes.len());
|
||||
for hash in hashes {
|
||||
results.push(pool.remove(hash, is_valid));
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// Cull transactions from the queue.
|
||||
pub fn cull<R>(&self, senders: Option<&[<V::VerifiedTransaction as txpool::VerifiedTransaction>::Sender]>, ready: R) -> usize where
|
||||
R: txpool::Ready<V::VerifiedTransaction>,
|
||||
{
|
||||
self.pool.write().cull(senders, ready)
|
||||
}
|
||||
|
||||
/// Cull transactions from the queue and then compute the pending set.
|
||||
pub fn cull_and_get_pending<R, F, T>(&self, ready: R, f: F) -> T where
|
||||
R: txpool::Ready<V::VerifiedTransaction> + Clone,
|
||||
F: FnOnce(txpool::PendingIterator<V::VerifiedTransaction, R, S, Listener>) -> T,
|
||||
{
|
||||
let mut pool = self.pool.write();
|
||||
pool.cull(None, ready.clone());
|
||||
f(pool.pending(ready))
|
||||
}
|
||||
|
||||
/// Get the full status of the queue (including readiness)
|
||||
pub fn status<R: txpool::Ready<V::VerifiedTransaction>>(&self, ready: R) -> txpool::Status {
|
||||
self.pool.read().status(ready)
|
||||
}
|
||||
|
||||
/// Returns light status of the pool.
|
||||
pub fn light_status(&self) -> txpool::LightStatus {
|
||||
self.pool.read().light_status()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use primitives::{
|
||||
block::{HeaderHash, ExtrinsicHash}
|
||||
};
|
||||
|
||||
/// Possible extrinsic status events
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Status {
|
||||
/// Extrinsic has been finalised in block with given hash.
|
||||
Finalised(HeaderHash),
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
Usurped(ExtrinsicHash),
|
||||
/// The extrinsic has been broadcast to the given peers.
|
||||
Broadcast(Vec<String>),
|
||||
/// Extrinsic has been dropped from the pool because of the limit.
|
||||
Dropped,
|
||||
}
|
||||
|
||||
/// Extrinsic watcher.
|
||||
///
|
||||
/// Represents a stream of status updates for particular extrinsic.
|
||||
#[derive(Debug)]
|
||||
pub struct Watcher {
|
||||
receiver: mpsc::UnboundedReceiver<Status>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Sender {
|
||||
receivers: Vec<mpsc::UnboundedSender<Status>>,
|
||||
finalised: bool,
|
||||
}
|
||||
|
||||
impl Sender {
|
||||
/// Add a new watcher to this sender object.
|
||||
pub fn new_watcher(&mut self) -> Watcher {
|
||||
let (tx, receiver) = mpsc::unbounded();
|
||||
self.receivers.push(tx);
|
||||
Watcher {
|
||||
receiver,
|
||||
}
|
||||
}
|
||||
|
||||
/// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid.
|
||||
pub fn usurped(&mut self, hash: ExtrinsicHash) {
|
||||
self.send(Status::Usurped(hash))
|
||||
}
|
||||
|
||||
/// Extrinsic has been finalised in block with given hash.
|
||||
pub fn finalised(&mut self, hash: HeaderHash) {
|
||||
self.send(Status::Finalised(hash));
|
||||
self.finalised = true;
|
||||
}
|
||||
|
||||
/// Transaction has been dropped from the pool because of the limit.
|
||||
pub fn dropped(&mut self) {
|
||||
self.send(Status::Dropped);
|
||||
}
|
||||
|
||||
/// The extrinsic has been broadcast to the given peers.
|
||||
pub fn broadcast(&mut self, peers: Vec<String>) {
|
||||
self.send(Status::Broadcast(peers))
|
||||
}
|
||||
|
||||
/// Returns true if the are no more listeners for this extrinsic or it was finalised.
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.finalised || self.receivers.is_empty()
|
||||
}
|
||||
|
||||
fn send(&mut self, status: Status) {
|
||||
self.receivers.retain(|sender| sender.unbounded_send(status.clone()).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
||||
|
||||
use std::collections::{HashMap, HashSet, BTreeMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::{mem, cmp};
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
@@ -24,7 +24,7 @@ use serde_json;
|
||||
use primitives::block::{HeaderHash, ExtrinsicHash, Number as BlockNumber, Header, Id as BlockId};
|
||||
use primitives::{Hash, blake2_256};
|
||||
use runtime_support::Hashable;
|
||||
use network::{PeerId, NodeId};
|
||||
use network::PeerId;
|
||||
|
||||
use message::{self, Message};
|
||||
use sync::{ChainSync, Status as SyncStatus, SyncState};
|
||||
@@ -103,15 +103,6 @@ pub struct PeerInfo {
|
||||
pub best_number: BlockNumber,
|
||||
}
|
||||
|
||||
/// Transaction stats
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionStats {
|
||||
/// Block number where this TX was first seen.
|
||||
pub first_seen: u64,
|
||||
/// Peers it was propagated to.
|
||||
pub propagated_to: BTreeMap<NodeId, usize>,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
/// Create a new instance.
|
||||
pub fn new(config: ProtocolConfig, chain: Arc<Client>, on_demand: Option<Arc<OnDemandService>>, transaction_pool: Arc<TransactionPool>) -> error::Result<Protocol> {
|
||||
@@ -463,15 +454,31 @@ impl Protocol {
|
||||
|
||||
let transactions = self.transaction_pool.transactions();
|
||||
|
||||
let mut propagated_to = HashMap::new();
|
||||
let mut peers = self.peers.write();
|
||||
for (peer_id, ref mut peer) in peers.iter_mut() {
|
||||
let to_send: Vec<_> = transactions.iter().filter_map(|&(hash, ref t)|
|
||||
if peer.known_transactions.insert(hash.clone()) { Some(t.clone()) } else { None }).collect();
|
||||
let (hashes, to_send): (Vec<_>, Vec<_>) = transactions
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|&(hash, _)| peer.known_transactions.insert(hash))
|
||||
.unzip();
|
||||
|
||||
if !to_send.is_empty() {
|
||||
let node_id = io.peer_session_info(*peer_id).map(|info| match info.id {
|
||||
Some(id) => format!("{}@{:x}", info.remote_address, id),
|
||||
None => info.remote_address.clone(),
|
||||
});
|
||||
|
||||
if let Some(id) = node_id {
|
||||
for hash in hashes {
|
||||
propagated_to.entry(hash).or_insert_with(Vec::new).push(id.clone());
|
||||
}
|
||||
}
|
||||
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), peer_id);
|
||||
self.send_message(io, *peer_id, Message::Transactions(to_send));
|
||||
}
|
||||
}
|
||||
self.transaction_pool.on_broadcasted(propagated_to);
|
||||
}
|
||||
|
||||
/// Send Status message
|
||||
@@ -551,10 +558,6 @@ impl Protocol {
|
||||
self.on_demand.as_ref().map(|s| s.on_remote_response(io, peer_id, response));
|
||||
}
|
||||
|
||||
pub fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats> {
|
||||
BTreeMap::new()
|
||||
}
|
||||
|
||||
pub fn chain(&self) -> &Client {
|
||||
&*self.chain
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.?
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::collections::{BTreeMap};
|
||||
use std::io;
|
||||
use std::time::Duration;
|
||||
use futures::sync::{oneshot, mpsc};
|
||||
@@ -26,7 +26,7 @@ use primitives::block::{ExtrinsicHash, Header, HeaderHash};
|
||||
use primitives::Hash;
|
||||
use core_io::{TimerToken};
|
||||
use io::NetSyncIo;
|
||||
use protocol::{Protocol, ProtocolStatus, PeerInfo as ProtocolPeerInfo, TransactionStats};
|
||||
use protocol::{Protocol, ProtocolStatus, PeerInfo as ProtocolPeerInfo};
|
||||
use config::{ProtocolConfig};
|
||||
use error::Error;
|
||||
use chain::Client;
|
||||
@@ -75,8 +75,6 @@ pub trait SyncProvider: Send + Sync {
|
||||
fn peers(&self) -> Vec<PeerInfo>;
|
||||
/// Get this node id if available.
|
||||
fn node_id(&self) -> Option<String>;
|
||||
/// Returns propagation count for pending transactions.
|
||||
fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats>;
|
||||
}
|
||||
|
||||
/// Transaction pool interface
|
||||
@@ -85,6 +83,8 @@ pub trait TransactionPool: Send + Sync {
|
||||
fn transactions(&self) -> Vec<(ExtrinsicHash, Vec<u8>)>;
|
||||
/// Import a transction into the pool.
|
||||
fn import(&self, transaction: &[u8]) -> Option<ExtrinsicHash>;
|
||||
/// Notify the pool about transactions broadcast.
|
||||
fn on_broadcasted(&self, propagations: HashMap<ExtrinsicHash, Vec<String>>);
|
||||
}
|
||||
|
||||
/// ConsensusService
|
||||
@@ -249,10 +249,6 @@ impl SyncProvider for Service {
|
||||
fn node_id(&self) -> Option<String> {
|
||||
self.network.external_url()
|
||||
}
|
||||
|
||||
fn transactions_stats(&self) -> BTreeMap<ExtrinsicHash, TransactionStats> {
|
||||
self.handler.protocol.transactions_stats()
|
||||
}
|
||||
}
|
||||
|
||||
/// ConsensusService
|
||||
|
||||
@@ -204,6 +204,8 @@ impl TransactionPool for EmptyTransactionPool {
|
||||
fn import(&self, _transaction: &[u8]) -> Option<ExtrinsicHash> {
|
||||
None
|
||||
}
|
||||
|
||||
fn on_broadcasted(&self, _: HashMap<ExtrinsicHash, Vec<String>>) {}
|
||||
}
|
||||
|
||||
pub struct TestNet {
|
||||
|
||||
@@ -12,6 +12,7 @@ log = "0.3"
|
||||
parking_lot = "0.4"
|
||||
substrate-client = { path = "../client" }
|
||||
substrate-executor = { path = "../executor" }
|
||||
substrate-extrinsic-pool = { path = "../extrinsic-pool" }
|
||||
substrate-primitives = { path = "../primitives" }
|
||||
substrate-state-machine = { path = "../state-machine" }
|
||||
tokio-core = "0.1.12"
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
//! Authoring RPC module errors.
|
||||
|
||||
use client;
|
||||
use extrinsic_pool::txpool;
|
||||
use rpc;
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Client(client::error::Error, client::error::ErrorKind) #[doc = "Client error"];
|
||||
Pool(txpool::Error, txpool::ErrorKind) #[doc = "Pool error"];
|
||||
}
|
||||
errors {
|
||||
/// Not implemented yet
|
||||
@@ -29,15 +29,10 @@ error_chain! {
|
||||
description("not yet implemented"),
|
||||
display("Method Not Implemented"),
|
||||
}
|
||||
/// Invalid format
|
||||
InvalidFormat {
|
||||
description("invalid format"),
|
||||
display("Invalid format for the extrinsic data"),
|
||||
}
|
||||
/// Some error with the pool since the import failed.
|
||||
PoolError {
|
||||
description("pool import failed"),
|
||||
display("Pool import failed"),
|
||||
/// Verification error
|
||||
Verification(e: Box<::std::error::Error + Send>) {
|
||||
description("extrinsic verification error"),
|
||||
display("Extrinsic verification error: {}", e.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +45,7 @@ impl From<Error> for rpc::Error {
|
||||
message: "Not implemented yet".into(),
|
||||
data: None,
|
||||
},
|
||||
// TODO [ToDr] Unwrap Pool errors.
|
||||
_ => rpc::Error::internal_error(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
//! Substrate block-author/full-node API.
|
||||
|
||||
use primitives::block::Extrinsic;
|
||||
use std::sync::Arc;
|
||||
use primitives::block::{Extrinsic, ExtrinsicHash};
|
||||
use extrinsic_pool::api::{Error, ExtrinsicPool};
|
||||
|
||||
pub mod error;
|
||||
|
||||
@@ -30,6 +32,20 @@ build_rpc_trait! {
|
||||
pub trait AuthorApi {
|
||||
/// Submit extrinsic for inclusion in block.
|
||||
#[rpc(name = "author_submitExtrinsic")]
|
||||
fn submit_extrinsic(&self, Extrinsic) -> Result<()>;
|
||||
fn submit_extrinsic(&self, Extrinsic) -> Result<ExtrinsicHash>;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AuthorApi for Arc<T> where
|
||||
T: ExtrinsicPool,
|
||||
{
|
||||
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<ExtrinsicHash> {
|
||||
self
|
||||
.submit(vec![xt])
|
||||
.map(|mut res| res.pop().expect("One extrinsic passed; one result back; qed"))
|
||||
.map_err(|e| e.into_pool_error()
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|e| error::ErrorKind::Verification(Box::new(e)).into())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,37 +15,53 @@
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use super::error::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, sync::Arc};
|
||||
use extrinsic_pool::api;
|
||||
use parking_lot::Mutex;
|
||||
use primitives::block;
|
||||
|
||||
#[derive(Default)]
|
||||
struct DummyTxPool {
|
||||
submitted: Vec<block::Extrinsic>,
|
||||
submitted: Mutex<Vec<block::Extrinsic>>,
|
||||
}
|
||||
|
||||
impl AuthorApi for Arc<Mutex<DummyTxPool>> {
|
||||
#[derive(Debug)]
|
||||
struct Error;
|
||||
impl api::Error for Error {}
|
||||
impl ::std::error::Error for Error {
|
||||
fn description(&self) -> &str { "Error" }
|
||||
}
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ExtrinsicPool for DummyTxPool {
|
||||
type Error = Error;
|
||||
|
||||
/// Submit extrinsic for inclusion in block.
|
||||
fn submit_extrinsic(&self, xt: Extrinsic) -> Result<()> {
|
||||
let mut s = self.lock();
|
||||
if s.submitted.len() < 1 {
|
||||
s.submitted.push(xt);
|
||||
Ok(())
|
||||
fn submit(&self, xt: Vec<Extrinsic>) -> ::std::result::Result<Vec<ExtrinsicHash>, Self::Error> {
|
||||
let mut submitted = self.submitted.lock();
|
||||
if submitted.len() < 1 {
|
||||
let hashes = xt.iter().map(|_xt| 1.into()).collect();
|
||||
submitted.extend(xt);
|
||||
Ok(hashes)
|
||||
} else {
|
||||
Err(ErrorKind::PoolError.into())
|
||||
Err(Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submit_transaction_should_not_cause_error() {
|
||||
let p = Arc::new(Mutex::new(DummyTxPool::default()));
|
||||
let p = Arc::new(DummyTxPool::default());
|
||||
let hash: ExtrinsicHash = 1.into();
|
||||
|
||||
assert_matches!(
|
||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])),
|
||||
Ok(())
|
||||
Ok(hash)
|
||||
);
|
||||
assert!(
|
||||
AuthorApi::submit_extrinsic(&p, block::Extrinsic(vec![])).is_err()
|
||||
|
||||
@@ -22,6 +22,7 @@ extern crate jsonrpc_core as rpc;
|
||||
extern crate jsonrpc_pubsub;
|
||||
extern crate parking_lot;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
||||
extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_state_machine as state_machine;
|
||||
extern crate tokio_core;
|
||||
|
||||
Reference in New Issue
Block a user