Transaction eras (#758)

* Initial groundwork

* A mess.

* Integrate

* Fix tests

* Unit tests

* Tests for unchecked_extrisnic

* fix tab

* Improve binary format.

* fix tests

* Rename extrinsic-pool -> transaction-pool

Closes #770

* Implement unimplemented.

* typo
This commit is contained in:
Gav Wood
2018-09-20 14:01:01 +02:00
committed by GitHub
parent 43068f8fc3
commit 67bf1a6eaa
42 changed files with 782 additions and 232 deletions
+4 -5
View File
@@ -31,10 +31,9 @@ use client::backend::Backend;
use client::block_builder::BlockBuilder as ClientBlockBuilder;
use client::{Client, CallExecutor};
use primitives::{
AccountId, Block, BlockId, Hash, Index, InherentData,
SessionKey, Timestamp, UncheckedExtrinsic
AccountId, Block, BlockId, BlockNumber, Hash, Index, InherentData, SessionKey, Timestamp, UncheckedExtrinsic
};
use sr_primitives::transaction_validity::TransactionValidity;
use sr_primitives::{transaction_validity::TransactionValidity, traits::{GetHeight, BlockNumberToHash}};
use substrate_primitives::{Blake2Hasher, RlpCodec};
/// Build new blocks.
@@ -49,7 +48,7 @@ pub trait BlockBuilder {
/// Trait encapsulating the node API.
///
/// All calls should fail when the exact runtime is unknown.
pub trait Api {
pub trait Api: GetHeight<BlockNumber=BlockNumber> + BlockNumberToHash<BlockNumber=BlockNumber,Hash=Hash> {
/// The block builder for this API type.
type BlockBuilder: BlockBuilder;
@@ -91,7 +90,7 @@ pub trait Api {
impl<B, E> BlockBuilder for ClientBlockBuilder<B, E, Block, Blake2Hasher, RlpCodec>
where
B: Backend<Block, Blake2Hasher, RlpCodec>,
E: CallExecutor<Block, Blake2Hasher, RlpCodec>+ Clone,
E: CallExecutor<Block, Blake2Hasher, RlpCodec> + Clone,
{
fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> {
self.push(extrinsic).map_err(Into::into)
+3 -3
View File
@@ -48,6 +48,7 @@ use std::time::{self, Duration, Instant};
use codec::{Decode, Encode};
use node_api::Api;
use node_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey};
use runtime_primitives::generic::Era;
use primitives::{AuthorityId, ed25519};
use transaction_pool::TransactionPool;
use tokio::runtime::TaskExecutor;
@@ -400,14 +401,13 @@ impl<C> bft::Proposer<Block> for Proposer<C>
=> MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)),
}
};
let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report)));
let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report)), self.client.genesis_hash());
let signature = self.local_key.sign(&payload.encode()).into();
next_index += 1;
let local_id = self.local_key.public().0.into();
let extrinsic = UncheckedExtrinsic {
signature: Some((node_runtime::RawAddress::Id(local_id), signature)),
index: payload.0,
signature: Some((node_runtime::RawAddress::Id(local_id), signature, payload.0, Era::immortal())),
function: payload.1,
};
let uxt: GenericExtrinsic = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid");
+13 -22
View File
@@ -54,7 +54,7 @@ mod tests {
ed25519::{Public, Pair}};
use node_primitives::{Hash, BlockNumber, AccountId};
use runtime_primitives::traits::{Header as HeaderT, Digest as DigestT};
use runtime_primitives::{generic, ApplyOutcome, ApplyError, ApplyResult};
use runtime_primitives::{generic, generic::Era, ApplyOutcome, ApplyError, ApplyResult};
use {balances, staking, session, system, consensus, timestamp, treasury};
use system::{EventRecord, Phase};
use node_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances,
@@ -63,6 +63,7 @@ mod tests {
const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.wasm");
const COMPACT_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm");
const GENESIS_HASH: [u8; 32] = [69u8; 32];
// TODO: move into own crate.
macro_rules! map {
@@ -81,19 +82,17 @@ mod tests {
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
match xt.signed {
Some(signed) => {
let payload = (xt.index, xt.function);
Some((signed, index)) => {
let payload = (index, xt.function, GENESIS_HASH);
let pair = Pair::from(Keyring::from_public(Public::from_raw(signed.clone().into())).unwrap());
let signature = pair.sign(&payload.encode()).into();
UncheckedExtrinsic {
signature: Some((balances::address::Address::Id(signed), signature)),
index: payload.0,
signature: Some((balances::address::Address::Id(signed), signature, payload.0, Era::mortal(256, 0))),
function: payload.1,
}
}
None => UncheckedExtrinsic {
signature: None,
index: xt.index,
function: xt.function,
},
}
@@ -101,8 +100,7 @@ mod tests {
fn xt() -> UncheckedExtrinsic {
sign(CheckedExtrinsic {
signed: Some(alice()),
index: 0,
signed: Some((alice(), 0)),
function: Call::Balances(balances::Call::transfer::<Runtime>(bob().into(), 69)),
})
}
@@ -283,7 +281,7 @@ mod tests {
fn block1(support_changes_trie: bool) -> (Vec<u8>, Hash) {
construct_block(
1,
[69u8; 32].into(),
GENESIS_HASH.into(),
if support_changes_trie {
hex!("1755be7303767b4d3855694b4f0ebd9d64b7011124d0ec1ad3e17c2a0d65e245").into()
} else {
@@ -297,12 +295,10 @@ mod tests {
vec![
CheckedExtrinsic {
signed: None,
index: 0,
function: Call::Timestamp(timestamp::Call::set(42)),
},
CheckedExtrinsic {
signed: Some(alice()),
index: 0,
signed: Some((alice(), 0)),
function: Call::Balances(balances::Call::transfer(bob().into(), 69)),
},
]
@@ -313,22 +309,19 @@ mod tests {
construct_block(
2,
block1(false).1,
hex!("29fa1d0aa83662c571315af54b106c73823a31f759793803bf8929960b67b138").into(),
hex!("60efe1a65e7c79041b02e56ec122d6eaedfa476e0a9f6f1f68eb0c8f402c4514").into(),
None,
vec![
CheckedExtrinsic {
signed: None,
index: 0,
function: Call::Timestamp(timestamp::Call::set(52)),
},
CheckedExtrinsic {
signed: Some(bob()),
index: 0,
signed: Some((bob(), 0)),
function: Call::Balances(balances::Call::transfer(alice().into(), 5)),
},
CheckedExtrinsic {
signed: Some(alice()),
index: 1,
signed: Some((alice(), 1)),
function: Call::Balances(balances::Call::transfer(bob().into(), 15)),
}
]
@@ -338,18 +331,16 @@ mod tests {
fn block1big() -> (Vec<u8>, Hash) {
construct_block(
1,
[69u8; 32].into(),
GENESIS_HASH.into(),
hex!("fe0e07c7b054fe186387461d455d536860e9c71d6979fd9dbf755e96ce070d04").into(),
None,
vec![
CheckedExtrinsic {
signed: None,
index: 0,
function: Call::Timestamp(timestamp::Call::set(42)),
},
CheckedExtrinsic {
signed: Some(alice()),
index: 0,
signed: Some((alice(), 0)),
function: Call::Consensus(consensus::Call::remark(vec![0; 120000])),
}
]
+7 -13
View File
@@ -223,11 +223,11 @@ pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// BlockId type as expected by this runtime.
pub type BlockId = generic::BlockId<Block>;
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
pub type UncheckedExtrinsic = generic::UncheckedMortalExtrinsic<Address, Index, Call, Signature>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, Balances, Balances, AllModules>;
pub type Executive = executive::Executive<Runtime, Block, balances::ChainContext<Runtime>, Balances, AllModules>;
pub mod api {
impl_stubs!(
@@ -251,19 +251,13 @@ pub mod api {
/// Produces the list of inherent extrinsics.
fn inherent_extrinsics(data: InherentData) -> Vec<UncheckedExtrinsic> {
let make_inherent = |function| UncheckedExtrinsic {
signature: Default::default(),
function,
index: 0,
};
let mut inherent = vec![
make_inherent(Call::Timestamp(TimestampCall::set(data.timestamp))),
];
let mut inherent = vec![generic::UncheckedMortalExtrinsic::new_unsigned(
Call::Timestamp(TimestampCall::set(data.timestamp))
)];
if !data.offline_indices.is_empty() {
inherent.push(make_inherent(
Call::Consensus(ConsensusCall::note_offline(data.offline_indices))
inherent.push(generic::UncheckedMortalExtrinsic::new_unsigned(
Call::Consensus(ConsensusCall::note_offline(data.offline_indices))
));
}
+6 -6
View File
@@ -50,7 +50,7 @@ use tokio::runtime::TaskExecutor;
use service::FactoryFullConfiguration;
use primitives::{Blake2Hasher, RlpCodec};
pub use service::{Roles, PruningMode, ExtrinsicPoolOptions,
pub use service::{Roles, PruningMode, TransactionPoolOptions,
ErrorKind, Error, ComponentBlock, LightComponents, FullComponents};
pub use client::ExecutionStrategy;
@@ -97,20 +97,20 @@ impl service::ServiceFactory for Factory {
type ExtrinsicHash = Hash;
type NetworkProtocol = DemoProtocol;
type RuntimeDispatch = node_executor::Executor;
type FullExtrinsicPoolApi = transaction_pool::ChainApi<service::FullClient<Self>>;
type LightExtrinsicPoolApi = transaction_pool::ChainApi<service::LightClient<Self>>;
type FullTransactionPoolApi = transaction_pool::ChainApi<service::FullClient<Self>>;
type LightTransactionPoolApi = transaction_pool::ChainApi<service::LightClient<Self>>;
type Genesis = GenesisConfig;
type Configuration = CustomConfiguration;
const NETWORK_PROTOCOL_ID: network::ProtocolId = ::node_network::PROTOCOL_ID;
fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc<service::FullClient<Self>>)
fn build_full_transaction_pool(config: TransactionPoolOptions, client: Arc<service::FullClient<Self>>)
-> Result<TransactionPool<service::FullClient<Self>>, Error>
{
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
}
fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc<service::LightClient<Self>>)
fn build_light_transaction_pool(config: TransactionPoolOptions, client: Arc<service::LightClient<Self>>)
-> Result<TransactionPool<service::LightClient<Self>>, Error>
{
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client)))
@@ -180,7 +180,7 @@ pub fn new_full(config: Configuration, executor: TaskExecutor)
client.clone(),
client.clone(),
consensus_net,
service.extrinsic_pool(),
service.transaction_pool(),
executor,
key,
))
+1 -1
View File
@@ -13,6 +13,6 @@ node-runtime = { path = "../runtime" }
substrate-client = { path = "../../core/client" }
parity-codec = { version = "1.1" }
substrate-keyring = { path = "../../core/keyring" }
substrate-extrinsic-pool = { path = "../../core/extrinsic-pool" }
substrate-transaction-pool = { path = "../../core/transaction-pool" }
substrate-primitives = { path = "../../core/primitives" }
sr-primitives = { path = "../../core/sr-primitives" }
+5 -5
View File
@@ -14,14 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use extrinsic_pool;
use transaction_pool;
use node_api;
use primitives::Hash;
use runtime::{Address, UncheckedExtrinsic};
error_chain! {
links {
Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind);
Pool(transaction_pool::Error, transaction_pool::ErrorKind);
Api(node_api::Error, node_api::ErrorKind);
}
errors {
@@ -63,10 +63,10 @@ error_chain! {
}
}
impl extrinsic_pool::IntoPoolError for Error {
fn into_pool_error(self) -> ::std::result::Result<extrinsic_pool::Error, Self> {
impl transaction_pool::IntoPoolError for Error {
fn into_pool_error(self) -> ::std::result::Result<transaction_pool::Error, Self> {
match self {
Error(ErrorKind::Pool(e), c) => Ok(extrinsic_pool::Error(e, c)),
Error(ErrorKind::Pool(e), c) => Ok(transaction_pool::Error(e, c)),
e => Err(e),
}
}
+37 -18
View File
@@ -16,7 +16,7 @@
extern crate substrate_client as client;
extern crate parity_codec as codec;
extern crate substrate_extrinsic_pool as extrinsic_pool;
extern crate substrate_transaction_pool as transaction_pool;
extern crate substrate_primitives;
extern crate sr_primitives;
extern crate node_runtime as runtime;
@@ -42,20 +42,20 @@ use std::{
};
use codec::{Decode, Encode};
use extrinsic_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor};
use transaction_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor};
use node_api::Api;
use primitives::{AccountId, BlockId, Block, Hash, Index};
use runtime::{UncheckedExtrinsic, RawAddress};
use sr_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256};
use primitives::{AccountId, BlockId, Block, Hash, Index, BlockNumber};
use runtime::{Address, UncheckedExtrinsic};
use sr_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256, Lookup, GetHeight, BlockNumberToHash};
pub use extrinsic_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
pub use transaction_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
pub use error::{Error, ErrorKind, Result};
/// Maximal size of a single encoded extrinsic.
const MAX_TRANSACTION_SIZE: usize = 4 * 1024 * 1024;
/// Type alias for the transaction pool.
pub type TransactionPool<A> = extrinsic_pool::Pool<ChainApi<A>>;
pub type TransactionPool<A> = transaction_pool::Pool<ChainApi<A>>;
/// A verified transaction which should be includable and non-inherent.
#[derive(Clone, Debug)]
@@ -86,7 +86,7 @@ impl VerifiedTransaction {
}
}
impl extrinsic_pool::VerifiedTransaction for VerifiedTransaction {
impl transaction_pool::VerifiedTransaction for VerifiedTransaction {
type Hash = Hash;
type Sender = AccountId;
@@ -119,8 +119,32 @@ impl<A> ChainApi<A> where
}
}
/// "Chain" context (used for checking transactions) which uses data local to our node/transaction pool.
///
/// This is due for removal when #721 lands
pub struct LocalContext<'a, A: 'a>(&'a Arc<A>);
impl<'a, A: 'a + Api> GetHeight for LocalContext<'a, A> {
type BlockNumber = BlockNumber;
fn get_height(&self) -> BlockNumber {
self.0.get_height()
}
}
impl<'a, A: 'a + Api> BlockNumberToHash for LocalContext<'a, A> {
type BlockNumber = BlockNumber;
type Hash = Hash;
fn block_number_to_hash(&self, n: BlockNumber) -> Option<Hash> {
self.0.block_number_to_hash(n)
}
}
impl<'a, A: 'a + Api> Lookup for LocalContext<'a, A> {
type Source = Address;
type Target = AccountId;
fn lookup(&self, a: Address) -> ::std::result::Result<AccountId, &'static str> {
self.0.lookup(&BlockId::number(self.get_height()), a).unwrap_or(None).ok_or("error with lookup")
}
}
impl<A> extrinsic_pool::ChainApi for ChainApi<A> where
impl<A> transaction_pool::ChainApi for ChainApi<A> where
A: Api + Send + Sync,
{
type Block = Block;
@@ -145,13 +169,8 @@ impl<A> extrinsic_pool::ChainApi for ChainApi<A> where
}
debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded));
let checked = uxt.clone().check_with(|a| {
match a {
RawAddress::Id(id) => Ok(id),
RawAddress::Index(_) => Err("Index based addresses are not supported".into()),// TODO: Make index addressing optional in substrate
}
})?;
let sender = checked.signed.expect("Only signed extrinsics are allowed at this point");
let checked = uxt.clone().check(&LocalContext(&self.api))?;
let (sender, index) = checked.signed.expect("function previously bailed unless uxt.is_signed(); qed");
if encoded_size < 1024 {
@@ -161,7 +180,7 @@ impl<A> extrinsic_pool::ChainApi for ChainApi<A> where
}
Ok(VerifiedTransaction {
index: checked.index,
index,
sender,
hash,
encoded_size,
@@ -214,7 +233,7 @@ impl<A> extrinsic_pool::ChainApi for ChainApi<A> where
}
fn update_scores(
xts: &[extrinsic_pool::Transaction<VerifiedFor<Self>>],
xts: &[transaction_pool::Transaction<VerifiedFor<Self>>],
scores: &mut [Self::Score],
_change: Change<()>
) {