mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 22:47:56 +00:00
Rename folder: primitives/sr-primitives -> primitives/runtime (#4280)
* primitives/sr-primitives -> primitives/runtime * update
This commit is contained in:
committed by
Bastian Köcher
parent
94b6921d03
commit
04fcc71809
@@ -0,0 +1,166 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Provides some utilities to define a piecewise linear function.
|
||||
|
||||
use crate::{Perbill, traits::{SimpleArithmetic, SaturatedConversion}};
|
||||
use core::ops::Sub;
|
||||
|
||||
/// Piecewise Linear function in [0, 1] -> [0, 1].
|
||||
#[derive(PartialEq, Eq, primitives::RuntimeDebug)]
|
||||
pub struct PiecewiseLinear<'a> {
|
||||
/// Array of points. Must be in order from the lowest abscissas to the highest.
|
||||
pub points: &'a [(Perbill, Perbill)],
|
||||
/// The maximum value that can be returned.
|
||||
pub maximum: Perbill,
|
||||
}
|
||||
|
||||
fn abs_sub<N: Ord + Sub<Output=N> + Clone>(a: N, b: N) -> N where {
|
||||
a.clone().max(b.clone()) - a.min(b)
|
||||
}
|
||||
|
||||
impl<'a> PiecewiseLinear<'a> {
|
||||
/// Compute `f(n/d)*d` with `n <= d`. This is useful to avoid loss of precision.
|
||||
pub fn calculate_for_fraction_times_denominator<N>(&self, n: N, d: N) -> N where
|
||||
N: SimpleArithmetic + Clone
|
||||
{
|
||||
let n = n.min(d.clone());
|
||||
|
||||
if self.points.len() == 0 {
|
||||
return N::zero()
|
||||
}
|
||||
|
||||
let next_point_index = self.points.iter()
|
||||
.position(|p| n < p.0 * d.clone());
|
||||
|
||||
let (prev, next) = if let Some(next_point_index) = next_point_index {
|
||||
if let Some(previous_point_index) = next_point_index.checked_sub(1) {
|
||||
(self.points[previous_point_index], self.points[next_point_index])
|
||||
} else {
|
||||
// There is no previous points, take first point ordinate
|
||||
return self.points.first().map(|p| p.1).unwrap_or_else(Perbill::zero) * d
|
||||
}
|
||||
} else {
|
||||
// There is no next points, take last point ordinate
|
||||
return self.points.last().map(|p| p.1).unwrap_or_else(Perbill::zero) * d
|
||||
};
|
||||
|
||||
let delta_y = multiply_by_rational_saturating(
|
||||
abs_sub(n.clone(), prev.0 * d.clone()),
|
||||
abs_sub(next.1.deconstruct(), prev.1.deconstruct()),
|
||||
// Must not saturate as prev abscissa > next abscissa
|
||||
next.0.deconstruct().saturating_sub(prev.0.deconstruct()),
|
||||
);
|
||||
|
||||
// If both substration are same sign then result is positive
|
||||
if (n > prev.0 * d.clone()) == (next.1.deconstruct() > prev.1.deconstruct()) {
|
||||
(prev.1 * d).saturating_add(delta_y)
|
||||
// Otherwise result is negative
|
||||
} else {
|
||||
(prev.1 * d).saturating_sub(delta_y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute value * p / q.
|
||||
// This is guaranteed not to overflow on whatever values nor lose precision.
|
||||
// `q` must be superior to zero.
|
||||
fn multiply_by_rational_saturating<N>(value: N, p: u32, q: u32) -> N
|
||||
where N: SimpleArithmetic + Clone
|
||||
{
|
||||
let q = q.max(1);
|
||||
|
||||
// Mul can saturate if p > q
|
||||
let result_divisor_part = (value.clone() / q.into()).saturating_mul(p.into());
|
||||
|
||||
let result_remainder_part = {
|
||||
let rem = value % q.into();
|
||||
|
||||
// Fits into u32 because q is u32 and remainder < q
|
||||
let rem_u32 = rem.saturated_into::<u32>();
|
||||
|
||||
// Multiplication fits into u64 as both term are u32
|
||||
let rem_part = rem_u32 as u64 * p as u64 / q as u64;
|
||||
|
||||
// Can saturate if p > q
|
||||
rem_part.saturated_into::<N>()
|
||||
};
|
||||
|
||||
// Can saturate if p > q
|
||||
result_divisor_part.saturating_add(result_remainder_part)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiply_by_rational_saturating() {
|
||||
use std::convert::TryInto;
|
||||
|
||||
let div = 100u32;
|
||||
for value in 0..=div {
|
||||
for p in 0..=div {
|
||||
for q in 1..=div {
|
||||
let value: u64 = (value as u128 * u64::max_value() as u128 / div as u128)
|
||||
.try_into().unwrap();
|
||||
let p = (p as u64 * u32::max_value() as u64 / div as u64)
|
||||
.try_into().unwrap();
|
||||
let q = (q as u64 * u32::max_value() as u64 / div as u64)
|
||||
.try_into().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
multiply_by_rational_saturating(value, p, q),
|
||||
(value as u128 * p as u128 / q as u128)
|
||||
.try_into().unwrap_or(u64::max_value())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_for_fraction_times_denominator() {
|
||||
use std::convert::TryInto;
|
||||
|
||||
let curve = PiecewiseLinear {
|
||||
points: &[
|
||||
(Perbill::from_parts(0_000_000_000), Perbill::from_parts(0_500_000_000)),
|
||||
(Perbill::from_parts(0_500_000_000), Perbill::from_parts(1_000_000_000)),
|
||||
(Perbill::from_parts(1_000_000_000), Perbill::from_parts(0_000_000_000)),
|
||||
],
|
||||
maximum: Perbill::from_parts(1_000_000_000),
|
||||
};
|
||||
|
||||
pub fn formal_calculate_for_fraction_times_denominator(n: u64, d: u64) -> u64 {
|
||||
if n <= Perbill::from_parts(0_500_000_000) * d.clone() {
|
||||
n + d / 2
|
||||
} else {
|
||||
(d as u128 * 2 - n as u128 * 2).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
let div = 100u32;
|
||||
for d in 0..=div {
|
||||
for n in 0..=d {
|
||||
let d: u64 = (d as u128 * u64::max_value() as u128 / div as u128)
|
||||
.try_into().unwrap();
|
||||
let n: u64 = (n as u128 * u64::max_value() as u128 / div as u128)
|
||||
.try_into().unwrap();
|
||||
|
||||
let res = curve.calculate_for_fraction_times_denominator(n, d);
|
||||
let expected = formal_calculate_for_fraction_times_denominator(n, d);
|
||||
|
||||
assert!(abs_sub(res, expected) <= 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of a block and associated items.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use rstd::prelude::*;
|
||||
use primitives::RuntimeDebug;
|
||||
use crate::codec::{Codec, Encode, Decode};
|
||||
use crate::traits::{self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize};
|
||||
use crate::Justification;
|
||||
|
||||
/// Something to identify a block.
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub enum BlockId<Block: BlockT> {
|
||||
/// Identify by block header hash.
|
||||
Hash(<<Block as BlockT>::Header as HeaderT>::Hash),
|
||||
/// Identify by block number.
|
||||
Number(<<Block as BlockT>::Header as HeaderT>::Number),
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockId<Block> {
|
||||
/// Create a block ID from a hash.
|
||||
pub fn hash(hash: Block::Hash) -> Self {
|
||||
BlockId::Hash(hash)
|
||||
}
|
||||
|
||||
/// Create a block ID from a number.
|
||||
pub fn number(number: <Block::Header as HeaderT>::Number) -> Self {
|
||||
BlockId::Number(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> Copy for BlockId<Block> {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Block: BlockT> fmt::Display for BlockId<Block> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Block<Header, Extrinsic: MaybeSerialize> {
|
||||
/// The block header.
|
||||
pub header: Header,
|
||||
/// The accompanying extrinsics.
|
||||
pub extrinsics: Vec<Extrinsic>,
|
||||
}
|
||||
|
||||
impl<Header, Extrinsic: MaybeSerialize> traits::Block for Block<Header, Extrinsic>
|
||||
where
|
||||
Header: HeaderT,
|
||||
Extrinsic: Member + Codec + traits::Extrinsic,
|
||||
{
|
||||
type Extrinsic = Extrinsic;
|
||||
type Header = Header;
|
||||
type Hash = <Self::Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec<u8> {
|
||||
(header, extrinsics).encode()
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over a substrate block and justification.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct SignedBlock<Block> {
|
||||
/// Full block.
|
||||
pub block: Block,
|
||||
/// Block justification.
|
||||
pub justification: Option<Justification>,
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of an extrinsic that has passed the verification
|
||||
//! stage.
|
||||
|
||||
use crate::traits::{
|
||||
self, Member, MaybeDisplay, SignedExtension, Dispatchable,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use crate::traits::ValidateUnsigned;
|
||||
use crate::transaction_validity::TransactionValidity;
|
||||
|
||||
/// Definition of something that the external world might want to say; its
|
||||
/// existence implies that it has been checked and is good, particularly with
|
||||
/// regards to the signature.
|
||||
#[derive(PartialEq, Eq, Clone, primitives::RuntimeDebug)]
|
||||
pub struct CheckedExtrinsic<AccountId, Call, Extra> {
|
||||
/// Who this purports to be from and the number of extrinsics have come before
|
||||
/// from the same signer, if anyone (note this is not a signature).
|
||||
pub signed: Option<(AccountId, Extra)>,
|
||||
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<AccountId, Call, Extra, Origin, Info> traits::Applyable for
|
||||
CheckedExtrinsic<AccountId, Call, Extra>
|
||||
where
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Call: Member + Dispatchable<Origin=Origin>,
|
||||
Extra: SignedExtension<AccountId=AccountId, Call=Call, DispatchInfo=Info>,
|
||||
Origin: From<Option<AccountId>>,
|
||||
Info: Clone,
|
||||
{
|
||||
type AccountId = AccountId;
|
||||
type Call = Call;
|
||||
type DispatchInfo = Info;
|
||||
|
||||
fn sender(&self) -> Option<&Self::AccountId> {
|
||||
self.signed.as_ref().map(|x| &x.0)
|
||||
}
|
||||
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
fn validate<U: ValidateUnsigned<Call = Self::Call>>(
|
||||
&self,
|
||||
info: Self::DispatchInfo,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
if let Some((ref id, ref extra)) = self.signed {
|
||||
Extra::validate(extra, id, &self.function, info.clone(), len)
|
||||
} else {
|
||||
let valid = Extra::validate_unsigned(&self.function, info, len)?;
|
||||
let unsigned_validation = U::validate_unsigned(&self.function)?;
|
||||
Ok(valid.combine_with(unsigned_validation))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
fn apply<U: ValidateUnsigned<Call=Self::Call>>(
|
||||
self,
|
||||
info: Self::DispatchInfo,
|
||||
len: usize,
|
||||
) -> crate::ApplyExtrinsicResult {
|
||||
let (maybe_who, pre) = if let Some((id, extra)) = self.signed {
|
||||
let pre = Extra::pre_dispatch(extra, &id, &self.function, info.clone(), len)?;
|
||||
(Some(id), pre)
|
||||
} else {
|
||||
let pre = Extra::pre_dispatch_unsigned(&self.function, info.clone(), len)?;
|
||||
U::pre_dispatch(&self.function)?;
|
||||
(None, pre)
|
||||
};
|
||||
let res = self.function.dispatch(Origin::from(maybe_who));
|
||||
Extra::post_dispatch(pre, info.clone(), len);
|
||||
Ok(res.map_err(Into::into))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of a digest.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use rstd::prelude::*;
|
||||
|
||||
use crate::ConsensusEngineId;
|
||||
use crate::codec::{Decode, Encode, Input, Error};
|
||||
use primitives::RuntimeDebug;
|
||||
|
||||
/// Generic header digest.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct Digest<Hash: Encode + Decode> {
|
||||
/// A list of logs in the digest.
|
||||
pub logs: Vec<DigestItem<Hash>>,
|
||||
}
|
||||
|
||||
impl<Item: Encode + Decode> Default for Digest<Item> {
|
||||
fn default() -> Self {
|
||||
Digest { logs: Vec::new(), }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: Encode + Decode> Digest<Hash> {
|
||||
/// Get reference to all digest items.
|
||||
pub fn logs(&self) -> &[DigestItem<Hash>] {
|
||||
&self.logs
|
||||
}
|
||||
|
||||
/// Push new digest item.
|
||||
pub fn push(&mut self, item: DigestItem<Hash>) {
|
||||
self.logs.push(item);
|
||||
}
|
||||
|
||||
/// Pop a digest item.
|
||||
pub fn pop(&mut self) -> Option<DigestItem<Hash>> {
|
||||
self.logs.pop()
|
||||
}
|
||||
|
||||
/// Get reference to the first digest item that matches the passed predicate.
|
||||
pub fn log<T: ?Sized, F: Fn(&DigestItem<Hash>) -> Option<&T>>(&self, predicate: F) -> Option<&T> {
|
||||
self.logs().iter()
|
||||
.filter_map(predicate)
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Get a conversion of the first digest item that successfully converts using the function.
|
||||
pub fn convert_first<T, F: Fn(&DigestItem<Hash>) -> Option<T>>(&self, predicate: F) -> Option<T> {
|
||||
self.logs().iter()
|
||||
.filter_map(predicate)
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Digest item that is able to encode/decode 'system' digest items and
|
||||
/// provide opaque access to other items.
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
||||
pub enum DigestItem<Hash> {
|
||||
/// System digest item that contains the root of changes trie at given
|
||||
/// block. It is created for every block iff runtime supports changes
|
||||
/// trie creation.
|
||||
ChangesTrieRoot(Hash),
|
||||
|
||||
/// A pre-runtime digest.
|
||||
///
|
||||
/// These are messages from the consensus engine to the runtime, although
|
||||
/// the consensus engine can (and should) read them itself to avoid
|
||||
/// code and state duplication. It is erroneous for a runtime to produce
|
||||
/// these, but this is not (yet) checked.
|
||||
PreRuntime(ConsensusEngineId, Vec<u8>),
|
||||
|
||||
/// A message from the runtime to the consensus engine. This should *never*
|
||||
/// be generated by the native code of any consensus engine, but this is not
|
||||
/// checked (yet).
|
||||
Consensus(ConsensusEngineId, Vec<u8>),
|
||||
|
||||
/// Put a Seal on it. This is only used by native code, and is never seen
|
||||
/// by runtimes.
|
||||
Seal(ConsensusEngineId, Vec<u8>),
|
||||
|
||||
/// Some other thing. Unsupported and experimental.
|
||||
Other(Vec<u8>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Hash: Encode> serde::Serialize for DigestItem<Hash> {
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
self.using_encoded(|bytes| {
|
||||
primitives::bytes::serialize(bytes, seq)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, Hash: Decode> serde::Deserialize<'a> for DigestItem<Hash> {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error> where
|
||||
D: serde::Deserializer<'a>,
|
||||
{
|
||||
let r = primitives::bytes::deserialize(de)?;
|
||||
Decode::decode(&mut &r[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A 'referencing view' for digest item. Does not own its contents. Used by
|
||||
/// final runtime implementations for encoding/decoding its log items.
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
||||
pub enum DigestItemRef<'a, Hash: 'a> {
|
||||
/// Reference to `DigestItem::ChangesTrieRoot`.
|
||||
ChangesTrieRoot(&'a Hash),
|
||||
/// A pre-runtime digest.
|
||||
///
|
||||
/// These are messages from the consensus engine to the runtime, although
|
||||
/// the consensus engine can (and should) read them itself to avoid
|
||||
/// code and state duplication. It is erroneous for a runtime to produce
|
||||
/// these, but this is not (yet) checked.
|
||||
PreRuntime(&'a ConsensusEngineId, &'a Vec<u8>),
|
||||
/// A message from the runtime to the consensus engine. This should *never*
|
||||
/// be generated by the native code of any consensus engine, but this is not
|
||||
/// checked (yet).
|
||||
Consensus(&'a ConsensusEngineId, &'a Vec<u8>),
|
||||
/// Put a Seal on it. This is only used by native code, and is never seen
|
||||
/// by runtimes.
|
||||
Seal(&'a ConsensusEngineId, &'a Vec<u8>),
|
||||
/// Any 'non-system' digest item, opaque to the native code.
|
||||
Other(&'a Vec<u8>),
|
||||
}
|
||||
|
||||
/// Type of the digest item. Used to gain explicit control over `DigestItem` encoding
|
||||
/// process. We need an explicit control, because final runtimes are encoding their own
|
||||
/// digest items using `DigestItemRef` type and we can't auto-derive `Decode`
|
||||
/// trait for `DigestItemRef`.
|
||||
#[repr(u32)]
|
||||
#[derive(Encode, Decode)]
|
||||
pub enum DigestItemType {
|
||||
ChangesTrieRoot = 2,
|
||||
PreRuntime = 6,
|
||||
Consensus = 4,
|
||||
Seal = 5,
|
||||
Other = 0,
|
||||
}
|
||||
|
||||
/// Type of a digest item that contains raw data; this also names the consensus engine ID where
|
||||
/// applicable. Used to identify one or more digest items of interest.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum OpaqueDigestItemId<'a> {
|
||||
/// Type corresponding to DigestItem::PreRuntime.
|
||||
PreRuntime(&'a ConsensusEngineId),
|
||||
/// Type corresponding to DigestItem::Consensus.
|
||||
Consensus(&'a ConsensusEngineId),
|
||||
/// Type corresponding to DigestItem::Seal.
|
||||
Seal(&'a ConsensusEngineId),
|
||||
/// Some other (non-prescribed) type.
|
||||
Other,
|
||||
}
|
||||
|
||||
impl<Hash> DigestItem<Hash> {
|
||||
/// Returns a 'referencing view' for this digest item.
|
||||
pub fn dref<'a>(&'a self) -> DigestItemRef<'a, Hash> {
|
||||
match *self {
|
||||
DigestItem::ChangesTrieRoot(ref v) => DigestItemRef::ChangesTrieRoot(v),
|
||||
DigestItem::PreRuntime(ref v, ref s) => DigestItemRef::PreRuntime(v, s),
|
||||
DigestItem::Consensus(ref v, ref s) => DigestItemRef::Consensus(v, s),
|
||||
DigestItem::Seal(ref v, ref s) => DigestItemRef::Seal(v, s),
|
||||
DigestItem::Other(ref v) => DigestItemRef::Other(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some` if the entry is the `ChangesTrieRoot` entry.
|
||||
pub fn as_changes_trie_root(&self) -> Option<&Hash> {
|
||||
self.dref().as_changes_trie_root()
|
||||
}
|
||||
|
||||
/// Returns `Some` if this entry is the `PreRuntime` entry.
|
||||
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &[u8])> {
|
||||
self.dref().as_pre_runtime()
|
||||
}
|
||||
|
||||
/// Returns `Some` if this entry is the `Consensus` entry.
|
||||
pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &[u8])> {
|
||||
self.dref().as_consensus()
|
||||
}
|
||||
|
||||
/// Returns `Some` if this entry is the `Seal` entry.
|
||||
pub fn as_seal(&self) -> Option<(ConsensusEngineId, &[u8])> {
|
||||
self.dref().as_seal()
|
||||
}
|
||||
|
||||
/// Returns Some if `self` is a `DigestItem::Other`.
|
||||
pub fn as_other(&self) -> Option<&[u8]> {
|
||||
match *self {
|
||||
DigestItem::Other(ref v) => Some(&v[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the opaque data contained in the item if `Some` if this entry has the id given.
|
||||
pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&[u8]> {
|
||||
self.dref().try_as_raw(id)
|
||||
}
|
||||
|
||||
/// Returns the data contained in the item if `Some` if this entry has the id given, decoded
|
||||
/// to the type provided `T`.
|
||||
pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
|
||||
self.dref().try_to::<T>(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: Encode> Encode for DigestItem<Hash> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
self.dref().encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Hash: Encode> codec::EncodeLike for DigestItem<Hash> {}
|
||||
|
||||
impl<Hash: Decode> Decode for DigestItem<Hash> {
|
||||
#[allow(deprecated)]
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
|
||||
let item_type: DigestItemType = Decode::decode(input)?;
|
||||
match item_type {
|
||||
DigestItemType::ChangesTrieRoot => Ok(DigestItem::ChangesTrieRoot(
|
||||
Decode::decode(input)?,
|
||||
)),
|
||||
DigestItemType::PreRuntime => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
|
||||
Ok(DigestItem::PreRuntime(vals.0, vals.1))
|
||||
},
|
||||
DigestItemType::Consensus => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
|
||||
Ok(DigestItem::Consensus(vals.0, vals.1))
|
||||
}
|
||||
DigestItemType::Seal => {
|
||||
let vals: (ConsensusEngineId, Vec<u8>) = Decode::decode(input)?;
|
||||
Ok(DigestItem::Seal(vals.0, vals.1))
|
||||
},
|
||||
DigestItemType::Other => Ok(DigestItem::Other(
|
||||
Decode::decode(input)?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Hash> DigestItemRef<'a, Hash> {
|
||||
/// Cast this digest item into `ChangesTrieRoot`.
|
||||
pub fn as_changes_trie_root(&self) -> Option<&'a Hash> {
|
||||
match *self {
|
||||
DigestItemRef::ChangesTrieRoot(ref changes_trie_root) => Some(changes_trie_root),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast this digest item into `PreRuntime`
|
||||
pub fn as_pre_runtime(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
|
||||
match *self {
|
||||
DigestItemRef::PreRuntime(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast this digest item into `Consensus`
|
||||
pub fn as_consensus(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
|
||||
match *self {
|
||||
DigestItemRef::Consensus(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast this digest item into `Seal`
|
||||
pub fn as_seal(&self) -> Option<(ConsensusEngineId, &'a [u8])> {
|
||||
match *self {
|
||||
DigestItemRef::Seal(consensus_engine_id, ref data) => Some((*consensus_engine_id, data)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast this digest item into `PreRuntime`
|
||||
pub fn as_other(&self) -> Option<&'a [u8]> {
|
||||
match *self {
|
||||
DigestItemRef::Other(ref data) => Some(data),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to match this digest item to the given opaque item identifier; if it matches, then
|
||||
/// return the opaque data it contains.
|
||||
pub fn try_as_raw(&self, id: OpaqueDigestItemId) -> Option<&'a [u8]> {
|
||||
match (id, self) {
|
||||
(OpaqueDigestItemId::Consensus(w), &DigestItemRef::Consensus(v, s)) |
|
||||
(OpaqueDigestItemId::Seal(w), &DigestItemRef::Seal(v, s)) |
|
||||
(OpaqueDigestItemId::PreRuntime(w), &DigestItemRef::PreRuntime(v, s))
|
||||
if v == w => Some(&s[..]),
|
||||
(OpaqueDigestItemId::Other, &DigestItemRef::Other(s)) => Some(&s[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to match this digest item to the given opaque item identifier; if it matches, then
|
||||
/// try to cast to the given datatype; if that works, return it.
|
||||
pub fn try_to<T: Decode>(&self, id: OpaqueDigestItemId) -> Option<T> {
|
||||
self.try_as_raw(id).and_then(|mut x| Decode::decode(&mut x).ok())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Hash: Encode> Encode for DigestItemRef<'a, Hash> {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
let mut v = Vec::new();
|
||||
|
||||
match *self {
|
||||
DigestItemRef::ChangesTrieRoot(changes_trie_root) => {
|
||||
DigestItemType::ChangesTrieRoot.encode_to(&mut v);
|
||||
changes_trie_root.encode_to(&mut v);
|
||||
},
|
||||
DigestItemRef::Consensus(val, data) => {
|
||||
DigestItemType::Consensus.encode_to(&mut v);
|
||||
(val, data).encode_to(&mut v);
|
||||
},
|
||||
DigestItemRef::Seal(val, sig) => {
|
||||
DigestItemType::Seal.encode_to(&mut v);
|
||||
(val, sig).encode_to(&mut v);
|
||||
},
|
||||
DigestItemRef::PreRuntime(val, data) => {
|
||||
DigestItemType::PreRuntime.encode_to(&mut v);
|
||||
(val, data).encode_to(&mut v);
|
||||
},
|
||||
DigestItemRef::Other(val) => {
|
||||
DigestItemType::Other.encode_to(&mut v);
|
||||
val.encode_to(&mut v);
|
||||
},
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Hash: Encode> codec::EncodeLike for DigestItemRef<'a, Hash> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_serialize_digest() {
|
||||
let digest = Digest {
|
||||
logs: vec![
|
||||
DigestItem::ChangesTrieRoot(4),
|
||||
DigestItem::Other(vec![1, 2, 3]),
|
||||
DigestItem::Seal(*b"test", vec![1, 2, 3])
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
::serde_json::to_string(&digest).unwrap(),
|
||||
r#"{"logs":["0x0204000000","0x000c010203","0x05746573740c010203"]}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of an unchecked (pre-verification) extrinsic.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::codec::{Decode, Encode, Input, Output, Error};
|
||||
|
||||
/// Era period
|
||||
pub type Period = u64;
|
||||
|
||||
/// Era phase
|
||||
pub type Phase = u64;
|
||||
|
||||
/// An era to describe the longevity of a transaction.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, primitives::RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum Era {
|
||||
/// The transaction is valid forever. The genesis hash must be present in the signed content.
|
||||
Immortal,
|
||||
|
||||
/// Period and phase are encoded:
|
||||
/// - The period of validity from the block hash found in the signing material.
|
||||
/// - The phase in the period that this transaction's lifetime begins (and, importantly,
|
||||
/// implies which block hash is included in the signature material). If the `period` is
|
||||
/// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that
|
||||
/// `period` is.
|
||||
Mortal(Period, Phase),
|
||||
}
|
||||
|
||||
/*
|
||||
E.g. with period == 4:
|
||||
0 10 20 30 40
|
||||
0123456789012345678901234567890123456789012
|
||||
|...|
|
||||
authored -/ \- expiry
|
||||
phase = 1
|
||||
n = Q(current - phase, period) + phase
|
||||
*/
|
||||
impl Era {
|
||||
/// Create a new era based on a period (which should be a power of two between 4 and 65536 inclusive)
|
||||
/// and a block number on which it should start (or, for long periods, be shortly after the start).
|
||||
pub fn mortal(period: u64, current: u64) -> Self {
|
||||
let period = period.checked_next_power_of_two()
|
||||
.unwrap_or(1 << 16)
|
||||
.max(4)
|
||||
.min(1 << 16);
|
||||
let phase = current % period;
|
||||
let quantize_factor = (period >> 12).max(1);
|
||||
let quantized_phase = phase / quantize_factor * quantize_factor;
|
||||
|
||||
Era::Mortal(period, quantized_phase)
|
||||
}
|
||||
|
||||
/// Create an "immortal" transaction.
|
||||
pub fn immortal() -> Self {
|
||||
Era::Immortal
|
||||
}
|
||||
|
||||
/// `true` if this is an immortal transaction.
|
||||
pub fn is_immortal(&self) -> bool {
|
||||
match self {
|
||||
Era::Immortal => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the block number of the start of the era whose properties this object
|
||||
/// describes that `current` belongs to.
|
||||
pub fn birth(self, current: u64) -> u64 {
|
||||
match self {
|
||||
Era::Immortal => 0,
|
||||
Era::Mortal(period, phase) => (current.max(phase) - phase) / period * period + phase,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the block number of the first block at which the era has ended.
|
||||
pub fn death(self, current: u64) -> u64 {
|
||||
match self {
|
||||
Era::Immortal => u64::max_value(),
|
||||
Era::Mortal(period, _) => self.birth(current) + period,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Era {
|
||||
fn encode_to<T: Output>(&self, output: &mut T) {
|
||||
match self {
|
||||
Era::Immortal => output.push_byte(0),
|
||||
Era::Mortal(period, phase) => {
|
||||
let quantize_factor = (*period as u64 >> 12).max(1);
|
||||
let encoded = (period.trailing_zeros() - 1).max(1).min(15) as u16 | ((phase / quantize_factor) << 4) as u16;
|
||||
output.push(&encoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl codec::EncodeLike for Era {}
|
||||
|
||||
impl Decode for Era {
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
|
||||
let first = input.read_byte()?;
|
||||
if first == 0 {
|
||||
Ok(Era::Immortal)
|
||||
} else {
|
||||
let encoded = first as u64 + ((input.read_byte()? as u64) << 8);
|
||||
let period = 2 << (encoded % (1 << 4));
|
||||
let quantize_factor = (period >> 12).max(1);
|
||||
let phase = (encoded >> 4) * quantize_factor;
|
||||
if period >= 4 && phase < period {
|
||||
Ok(Era::Mortal(period, phase))
|
||||
} else {
|
||||
Err("Invalid period and phase".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn immortal_works() {
|
||||
let e = Era::immortal();
|
||||
assert_eq!(e.birth(0), 0);
|
||||
assert_eq!(e.death(0), u64::max_value());
|
||||
assert_eq!(e.birth(1), 0);
|
||||
assert_eq!(e.death(1), u64::max_value());
|
||||
assert_eq!(e.birth(u64::max_value()), 0);
|
||||
assert_eq!(e.death(u64::max_value()), u64::max_value());
|
||||
assert!(e.is_immortal());
|
||||
|
||||
assert_eq!(e.encode(), vec![0u8]);
|
||||
assert_eq!(e, Era::decode(&mut&[0u8][..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mortal_codec_works() {
|
||||
let e = Era::mortal(64, 42);
|
||||
assert!(!e.is_immortal());
|
||||
|
||||
let expected = vec![5 + 42 % 16 * 16, 42 / 16];
|
||||
assert_eq!(e.encode(), expected);
|
||||
assert_eq!(e, Era::decode(&mut&expected[..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_period_mortal_codec_works() {
|
||||
let e = Era::mortal(32768, 20000);
|
||||
|
||||
let expected = vec![(14 + 2500 % 16 * 16) as u8, (2500 / 16) as u8];
|
||||
assert_eq!(e.encode(), expected);
|
||||
assert_eq!(e, Era::decode(&mut&expected[..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn era_initialization_works() {
|
||||
assert_eq!(Era::mortal(64, 42), Era::Mortal(64, 42));
|
||||
assert_eq!(Era::mortal(32768, 20000), Era::Mortal(32768, 20000));
|
||||
assert_eq!(Era::mortal(200, 513), Era::Mortal(256, 1));
|
||||
assert_eq!(Era::mortal(2, 1), Era::Mortal(4, 1));
|
||||
assert_eq!(Era::mortal(4, 5), Era::Mortal(4, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quantized_clamped_era_initialization_works() {
|
||||
// clamp 1000000 to 65536, quantize 1000001 % 65536 to the nearest 4
|
||||
assert_eq!(Era::mortal(1000000, 1000001), Era::Mortal(65536, 1000001 % 65536 / 4 * 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mortal_birth_death_works() {
|
||||
let e = Era::mortal(4, 6);
|
||||
for i in 6..10 {
|
||||
assert_eq!(e.birth(i), 6);
|
||||
assert_eq!(e.death(i), 10);
|
||||
}
|
||||
|
||||
// wrong because it's outside of the (current...current + period) range
|
||||
assert_ne!(e.birth(10), 6);
|
||||
assert_ne!(e.birth(5), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_less_than_phase() {
|
||||
// should not panic
|
||||
Era::mortal(4, 3).birth(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of a block header.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::codec::{Decode, Encode, Codec, Input, Output, HasCompact, EncodeAsRef, Error};
|
||||
use crate::traits::{
|
||||
self, Member, SimpleArithmetic, SimpleBitOps, Hash as HashT,
|
||||
MaybeSerializeDeserialize, MaybeSerialize, MaybeDisplay,
|
||||
};
|
||||
use crate::generic::Digest;
|
||||
use primitives::U256;
|
||||
use rstd::{
|
||||
convert::TryFrom,
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
/// Abstraction over a block header for a substrate chain.
|
||||
#[derive(PartialEq, Eq, Clone, primitives::RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
|
||||
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
|
||||
pub struct Header<Number: Copy + Into<U256> + TryFrom<U256>, Hash: HashT> {
|
||||
/// The parent hash.
|
||||
pub parent_hash: Hash::Output,
|
||||
/// The block number.
|
||||
#[cfg_attr(feature = "std", serde(
|
||||
serialize_with = "serialize_number",
|
||||
deserialize_with = "deserialize_number"))]
|
||||
pub number: Number,
|
||||
/// The state trie merkle root
|
||||
pub state_root: Hash::Output,
|
||||
/// The merkle root of the extrinsics.
|
||||
pub extrinsics_root: Hash::Output,
|
||||
/// A chain-specific digest of data useful for light clients or referencing auxiliary data.
|
||||
pub digest: Digest<Hash::Output>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn serialize_number<S, T: Copy + Into<U256> + TryFrom<U256>>(
|
||||
val: &T, s: S,
|
||||
) -> Result<S::Ok, S::Error> where S: serde::Serializer {
|
||||
let u256: U256 = (*val).into();
|
||||
serde::Serialize::serialize(&u256, s)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn deserialize_number<'a, D, T: Copy + Into<U256> + TryFrom<U256>>(
|
||||
d: D,
|
||||
) -> Result<T, D::Error> where D: serde::Deserializer<'a> {
|
||||
let u256: U256 = serde::Deserialize::deserialize(d)?;
|
||||
TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed"))
|
||||
}
|
||||
|
||||
impl<Number, Hash> Decode for Header<Number, Hash> where
|
||||
Number: HasCompact + Copy + Into<U256> + TryFrom<U256>,
|
||||
Hash: HashT,
|
||||
Hash::Output: Decode,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
|
||||
Ok(Header {
|
||||
parent_hash: Decode::decode(input)?,
|
||||
number: <<Number as HasCompact>::Type>::decode(input)?.into(),
|
||||
state_root: Decode::decode(input)?,
|
||||
extrinsics_root: Decode::decode(input)?,
|
||||
digest: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash> Encode for Header<Number, Hash> where
|
||||
Number: HasCompact + Copy + Into<U256> + TryFrom<U256>,
|
||||
Hash: HashT,
|
||||
Hash::Output: Encode,
|
||||
{
|
||||
fn encode_to<T: Output>(&self, dest: &mut T) {
|
||||
dest.push(&self.parent_hash);
|
||||
dest.push(&<<<Number as HasCompact>::Type as EncodeAsRef<_>>::RefType>::from(&self.number));
|
||||
dest.push(&self.state_root);
|
||||
dest.push(&self.extrinsics_root);
|
||||
dest.push(&self.digest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash> codec::EncodeLike for Header<Number, Hash> where
|
||||
Number: HasCompact + Copy + Into<U256> + TryFrom<U256>,
|
||||
Hash: HashT,
|
||||
Hash::Output: Encode,
|
||||
{}
|
||||
|
||||
impl<Number, Hash> traits::Header for Header<Number, Hash> where
|
||||
Number: Member + MaybeSerializeDeserialize + Debug + rstd::hash::Hash + MaybeDisplay +
|
||||
SimpleArithmetic + Codec + Copy + Into<U256> + TryFrom<U256>,
|
||||
Hash: HashT,
|
||||
Hash::Output: Default + rstd::hash::Hash + Copy + Member +
|
||||
MaybeSerialize + Debug + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
type Number = Number;
|
||||
type Hash = <Hash as HashT>::Output;
|
||||
type Hashing = Hash;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Digest<Self::Hash> { &self.digest }
|
||||
|
||||
fn digest_mut(&mut self) -> &mut Digest<Self::Hash> {
|
||||
#[cfg(feature = "std")]
|
||||
log::debug!(target: "header", "Retrieving mutable reference to digest");
|
||||
&mut self.digest
|
||||
}
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Digest<Self::Hash>,
|
||||
) -> Self {
|
||||
Header {
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root,
|
||||
parent_hash,
|
||||
digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Number, Hash> Header<Number, Hash> where
|
||||
Number: Member + rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec + Into<U256> + TryFrom<U256>,
|
||||
Hash: HashT,
|
||||
Hash::Output: Default + rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
|
||||
{
|
||||
/// Convenience helper for computing the hash of the header without having
|
||||
/// to import the trait.
|
||||
pub fn hash(&self) -> Hash::Output {
|
||||
Hash::hash_of(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "std"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_serialize_numbers() {
|
||||
fn serialize(num: u128) -> String {
|
||||
let mut v = vec![];
|
||||
{
|
||||
let mut ser = serde_json::Serializer::new(std::io::Cursor::new(&mut v));
|
||||
serialize_number(&num, &mut ser).unwrap();
|
||||
}
|
||||
String::from_utf8(v).unwrap()
|
||||
}
|
||||
|
||||
assert_eq!(serialize(0), "\"0x0\"".to_owned());
|
||||
assert_eq!(serialize(1), "\"0x1\"".to_owned());
|
||||
assert_eq!(serialize(u64::max_value() as u128), "\"0xffffffffffffffff\"".to_owned());
|
||||
assert_eq!(serialize(u64::max_value() as u128 + 1), "\"0x10000000000000000\"".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_number() {
|
||||
fn deserialize(num: &str) -> u128 {
|
||||
let mut der = serde_json::Deserializer::new(serde_json::de::StrRead::new(num));
|
||||
deserialize_number(&mut der).unwrap()
|
||||
}
|
||||
|
||||
assert_eq!(deserialize("\"0x0\""), 0);
|
||||
assert_eq!(deserialize("\"0x1\""), 1);
|
||||
assert_eq!(deserialize("\"0xffffffffffffffff\""), u64::max_value() as u128);
|
||||
assert_eq!(deserialize("\"0x10000000000000000\""), u64::max_value() as u128 + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Generic implementations of Extrinsic/Header/Block.
|
||||
// end::description[]
|
||||
|
||||
mod unchecked_extrinsic;
|
||||
mod era;
|
||||
mod checked_extrinsic;
|
||||
mod header;
|
||||
mod block;
|
||||
mod digest;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::unchecked_extrinsic::{UncheckedExtrinsic, SignedPayload};
|
||||
pub use self::era::{Era, Phase};
|
||||
pub use self::checked_extrinsic::CheckedExtrinsic;
|
||||
pub use self::header::Header;
|
||||
pub use self::block::{Block, SignedBlock, BlockId};
|
||||
pub use self::digest::{
|
||||
Digest, DigestItem, DigestItemRef, OpaqueDigestItemId
|
||||
};
|
||||
|
||||
use crate::codec::Encode;
|
||||
use rstd::prelude::*;
|
||||
|
||||
fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
|
||||
let size = ::rstd::mem::size_of::<T>();
|
||||
let reserve = match size {
|
||||
0..=0b00111111 => 1,
|
||||
0..=0b00111111_11111111 => 2,
|
||||
_ => 4,
|
||||
};
|
||||
let mut v = Vec::with_capacity(reserve + size);
|
||||
v.resize(reserve, 0);
|
||||
encoder(&mut v);
|
||||
|
||||
// need to prefix with the total length to ensure it's binary compatible with
|
||||
// Vec<u8>.
|
||||
let mut length: Vec<()> = Vec::new();
|
||||
length.resize(v.len() - reserve, ());
|
||||
length.using_encoded(|s| {
|
||||
v.splice(0..reserve, s.iter().cloned());
|
||||
});
|
||||
|
||||
v
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tests for the generic implementations of Extrinsic/Header/Block.
|
||||
|
||||
use crate::codec::{Decode, Encode};
|
||||
use primitives::H256;
|
||||
use super::DigestItem;
|
||||
|
||||
#[test]
|
||||
fn system_digest_item_encoding() {
|
||||
let item = DigestItem::ChangesTrieRoot::<H256>(H256::default());
|
||||
let encoded = item.encode();
|
||||
assert_eq!(encoded, vec![
|
||||
// type = DigestItemType::ChangesTrieRoot
|
||||
2,
|
||||
// trie root
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
]);
|
||||
|
||||
let decoded: DigestItem<H256> = Decode::decode(&mut &encoded[..]).unwrap();
|
||||
assert_eq!(item, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_system_digest_item_encoding() {
|
||||
let item = DigestItem::Other::<H256>(vec![10, 20, 30]);
|
||||
let encoded = item.encode();
|
||||
assert_eq!(encoded, vec![
|
||||
// type = DigestItemType::Other
|
||||
0,
|
||||
// length of other data
|
||||
12,
|
||||
// authorities
|
||||
10, 20, 30,
|
||||
]);
|
||||
|
||||
let decoded: DigestItem<H256> = Decode::decode(&mut &encoded[..]).unwrap();
|
||||
assert_eq!(item, decoded);
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generic implementation of an unchecked (pre-verification) extrinsic.
|
||||
|
||||
use rstd::{fmt, prelude::*};
|
||||
use runtime_io::hashing::blake2_256;
|
||||
use codec::{Decode, Encode, EncodeLike, Input, Error};
|
||||
use crate::{
|
||||
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount},
|
||||
generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction},
|
||||
};
|
||||
|
||||
const TRANSACTION_VERSION: u8 = 4;
|
||||
|
||||
/// A extrinsic right from the external world. This is unchecked and so
|
||||
/// can contain a signature.
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Extra: SignedExtension
|
||||
{
|
||||
/// The signature, address, number of extrinsics have come before from
|
||||
/// the same signer and an era describing the longevity of this transaction,
|
||||
/// if this is a signed extrinsic.
|
||||
pub signature: Option<(Address, Signature, Extra)>,
|
||||
/// The function that should be called.
|
||||
pub function: Call,
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra: SignedExtension>
|
||||
UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
/// New instance of a signed extrinsic aka "transaction".
|
||||
pub fn new_signed(
|
||||
function: Call,
|
||||
signed: Address,
|
||||
signature: Signature,
|
||||
extra: Extra
|
||||
) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
signature: Some((signed, signature, extra)),
|
||||
function,
|
||||
}
|
||||
}
|
||||
|
||||
/// New instance of an unsigned extrinsic aka "inherent".
|
||||
pub fn new_unsigned(function: Call) -> Self {
|
||||
UncheckedExtrinsic {
|
||||
signature: None,
|
||||
function,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra: SignedExtension> Extrinsic
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
type Call = Call;
|
||||
|
||||
type SignaturePayload = (
|
||||
Address,
|
||||
Signature,
|
||||
Extra,
|
||||
);
|
||||
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
|
||||
fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(if let Some((address, signature, extra)) = signed_data {
|
||||
UncheckedExtrinsic::new_signed(function, address, signature, extra)
|
||||
} else {
|
||||
UncheckedExtrinsic::new_unsigned(function)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, AccountId, Call, Signature, Extra, Lookup>
|
||||
Checkable<Lookup>
|
||||
for
|
||||
UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Address: Member + MaybeDisplay,
|
||||
Call: Encode + Member,
|
||||
Signature: Member + traits::Verify,
|
||||
<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId=AccountId>,
|
||||
Extra: SignedExtension<AccountId=AccountId>,
|
||||
AccountId: Member + MaybeDisplay,
|
||||
Lookup: traits::Lookup<Source=Address, Target=AccountId>,
|
||||
{
|
||||
type Checked = CheckedExtrinsic<AccountId, Call, Extra>;
|
||||
|
||||
fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signed, signature, extra)) => {
|
||||
let signed = lookup.lookup(signed)?;
|
||||
let raw_payload = SignedPayload::new(self.function, extra)?;
|
||||
if !raw_payload.using_encoded(|payload| {
|
||||
signature.verify(payload, &signed)
|
||||
}) {
|
||||
return Err(InvalidTransaction::BadProof.into())
|
||||
}
|
||||
|
||||
let (function, extra, _) = raw_payload.deconstruct();
|
||||
CheckedExtrinsic {
|
||||
signed: Some((signed, extra)),
|
||||
function,
|
||||
}
|
||||
}
|
||||
None => CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: self.function,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A payload that has been signed for an unchecked extrinsics.
|
||||
///
|
||||
/// Note that the payload that we sign to produce unchecked extrinsic signature
|
||||
/// is going to be different than the `SignaturePayload` - so the thing the extrinsic
|
||||
/// actually contains.
|
||||
pub struct SignedPayload<Call, Extra: SignedExtension>((
|
||||
Call,
|
||||
Extra,
|
||||
Extra::AdditionalSigned,
|
||||
));
|
||||
|
||||
impl<Call, Extra> SignedPayload<Call, Extra> where
|
||||
Call: Encode,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
/// Create new `SignedPayload`.
|
||||
///
|
||||
/// This function may fail if `additional_signed` of `Extra` is not available.
|
||||
pub fn new(call: Call, extra: Extra) -> Result<Self, TransactionValidityError> {
|
||||
let additional_signed = extra.additional_signed()?;
|
||||
let raw_payload = (call, extra, additional_signed);
|
||||
Ok(Self(raw_payload))
|
||||
}
|
||||
|
||||
/// Create new `SignedPayload` from raw components.
|
||||
pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self {
|
||||
Self((call, extra, additional_signed))
|
||||
}
|
||||
|
||||
/// Deconstruct the payload into it's components.
|
||||
pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> Encode for SignedPayload<Call, Extra> where
|
||||
Call: Encode,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
/// Get an encoded version of this payload.
|
||||
///
|
||||
/// Payloads longer than 256 bytes are going to be `blake2_256`-hashed.
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
self.0.using_encoded(|payload| {
|
||||
if payload.len() > 256 {
|
||||
f(&blake2_256(payload)[..])
|
||||
} else {
|
||||
f(payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> EncodeLike for SignedPayload<Call, Extra>
|
||||
where
|
||||
Call: Encode,
|
||||
Extra: SignedExtension,
|
||||
{}
|
||||
|
||||
impl<Address, Call, Signature, Extra> Decode
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Address: Decode,
|
||||
Signature: Decode,
|
||||
Call: Decode,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
|
||||
// This is a little more complicated than usual since the binary format must be compatible
|
||||
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
|
||||
// will be a prefix of vector length (we don't need
|
||||
// to use this).
|
||||
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
|
||||
|
||||
let version = input.read_byte()?;
|
||||
|
||||
let is_signed = version & 0b1000_0000 != 0;
|
||||
let version = version & 0b0111_1111;
|
||||
if version != TRANSACTION_VERSION {
|
||||
return Err("Invalid transaction version".into());
|
||||
}
|
||||
|
||||
Ok(UncheckedExtrinsic {
|
||||
signature: if is_signed { Some(Decode::decode(input)?) } else { None },
|
||||
function: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> Encode
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Address: Encode,
|
||||
Signature: Encode,
|
||||
Call: Encode,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
super::encode_with_vec_prefix::<Self, _>(|v| {
|
||||
// 1 byte version id.
|
||||
match self.signature.as_ref() {
|
||||
Some(s) => {
|
||||
v.push(TRANSACTION_VERSION | 0b1000_0000);
|
||||
s.encode_to(v);
|
||||
}
|
||||
None => {
|
||||
v.push(TRANSACTION_VERSION & 0b0111_1111);
|
||||
}
|
||||
}
|
||||
self.function.encode_to(v);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> EncodeLike
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Address: Encode,
|
||||
Signature: Encode,
|
||||
Call: Encode,
|
||||
Extra: SignedExtension,
|
||||
{}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Address: Encode, Signature: Encode, Call: Encode, Extra: SignedExtension> serde::Serialize
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
{
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Address, Call, Signature, Extra> fmt::Debug
|
||||
for UncheckedExtrinsic<Address, Call, Signature, Extra>
|
||||
where
|
||||
Address: fmt::Debug,
|
||||
Call: fmt::Debug,
|
||||
Extra: SignedExtension,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"UncheckedExtrinsic({:?}, {:?})",
|
||||
self.signature.as_ref().map(|x| (&x.0, &x.2)),
|
||||
self.function,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::hashing::blake2_256;
|
||||
use crate::codec::{Encode, Decode};
|
||||
use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
type TestContext = IdentityLookup<u64>;
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode)]
|
||||
pub struct TestSigner(pub u64);
|
||||
impl From<u64> for TestSigner { fn from(x: u64) -> Self { Self(x) } }
|
||||
impl From<TestSigner> for u64 { fn from(x: TestSigner) -> Self { x.0 } }
|
||||
impl IdentifyAccount for TestSigner {
|
||||
type AccountId = u64;
|
||||
fn into_account(self) -> u64 { self.into() }
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)]
|
||||
struct TestSig(u64, Vec<u8>);
|
||||
impl traits::Verify for TestSig {
|
||||
type Signer = TestSigner;
|
||||
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &u64) -> bool {
|
||||
signer == &self.0 && msg.get() == &self.1[..]
|
||||
}
|
||||
}
|
||||
|
||||
type TestAccountId = u64;
|
||||
type TestCall = Vec<u8>;
|
||||
|
||||
const TEST_ACCOUNT: TestAccountId = 0;
|
||||
|
||||
// NOTE: this is demonstration. One can simply use `()` for testing.
|
||||
#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
struct TestExtra;
|
||||
impl SignedExtension for TestExtra {
|
||||
type AccountId = u64;
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type DispatchInfo = ();
|
||||
type Pre = ();
|
||||
|
||||
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) }
|
||||
}
|
||||
|
||||
type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, TestExtra>;
|
||||
type CEx = CheckedExtrinsic<TestAccountId, TestCall, TestExtra>;
|
||||
|
||||
#[test]
|
||||
fn unsigned_codec_should_work() {
|
||||
let ux = Ex::new_unsigned(vec![0u8; 0]);
|
||||
let encoded = ux.encode();
|
||||
assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_codec_should_work() {
|
||||
let ux = Ex::new_signed(
|
||||
vec![0u8; 0],
|
||||
TEST_ACCOUNT,
|
||||
TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
|
||||
TestExtra
|
||||
);
|
||||
let encoded = ux.encode();
|
||||
assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_signed_codec_should_work() {
|
||||
let ux = Ex::new_signed(
|
||||
vec![0u8; 0],
|
||||
TEST_ACCOUNT,
|
||||
TestSig(TEST_ACCOUNT, (vec![0u8; 257], TestExtra)
|
||||
.using_encoded(blake2_256)[..].to_owned()),
|
||||
TestExtra
|
||||
);
|
||||
let encoded = ux.encode();
|
||||
assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsigned_check_should_work() {
|
||||
let ux = Ex::new_unsigned(vec![0u8; 0]);
|
||||
assert!(!ux.is_signed().unwrap_or(false));
|
||||
assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn badly_signed_check_should_fail() {
|
||||
let ux = Ex::new_signed(
|
||||
vec![0u8; 0],
|
||||
TEST_ACCOUNT,
|
||||
TestSig(TEST_ACCOUNT, vec![0u8; 0]),
|
||||
TestExtra,
|
||||
);
|
||||
assert!(ux.is_signed().unwrap_or(false));
|
||||
assert_eq!(
|
||||
<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
|
||||
Err(InvalidTransaction::BadProof.into()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_check_should_work() {
|
||||
let ux = Ex::new_signed(
|
||||
vec![0u8; 0],
|
||||
TEST_ACCOUNT,
|
||||
TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
|
||||
TestExtra,
|
||||
);
|
||||
assert!(ux.is_signed().unwrap_or(false));
|
||||
assert_eq!(
|
||||
<Ex as Checkable<TestContext>>::check(ux, &Default::default()),
|
||||
Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_matches_vec() {
|
||||
let ex = Ex::new_unsigned(vec![0u8; 0]);
|
||||
let encoded = ex.encode();
|
||||
let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
|
||||
assert_eq!(decoded, ex);
|
||||
let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
|
||||
assert_eq!(as_vec.encode(), encoded);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Runtime Modules shared primitive types.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
// to allow benchmarking
|
||||
#![cfg_attr(feature = "bench", feature(test))]
|
||||
#[cfg(feature = "bench")] extern crate test;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
#[cfg(feature = "std")]
|
||||
#[doc(hidden)]
|
||||
pub use serde;
|
||||
#[doc(hidden)]
|
||||
pub use rstd;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use paste;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use app_crypto;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use primitives::storage::{StorageOverlay, ChildrenStorageOverlay};
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::convert::TryFrom;
|
||||
use primitives::{crypto, ed25519, sr25519, ecdsa, hash::{H256, H512}};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
pub mod curve;
|
||||
pub mod generic;
|
||||
pub mod offchain;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod testing;
|
||||
pub mod traits;
|
||||
pub mod transaction_validity;
|
||||
pub mod random_number_generator;
|
||||
|
||||
/// Re-export these since they're only "kind of" generic.
|
||||
pub use generic::{DigestItem, Digest};
|
||||
|
||||
/// Re-export this since it's part of the API of this crate.
|
||||
pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType, AccountId32}};
|
||||
pub use app_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic};
|
||||
|
||||
/// Re-export `RuntimeDebug`, to avoid dependency clutter.
|
||||
pub use primitives::RuntimeDebug;
|
||||
|
||||
/// Re-export top-level arithmetic stuff.
|
||||
pub use arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64};
|
||||
/// Re-export 128 bit helpers.
|
||||
pub use arithmetic::helpers_128bit;
|
||||
/// Re-export big_uint stuff.
|
||||
pub use arithmetic::biguint;
|
||||
|
||||
pub use random_number_generator::RandomNumberGenerator;
|
||||
|
||||
/// An abstraction over justification for a block's validity under a consensus algorithm.
|
||||
///
|
||||
/// Essentially a finality proof. The exact formulation will vary between consensus
|
||||
/// algorithms. In the case where there are multiple valid proofs, inclusion within
|
||||
/// the block itself would allow swapping justifications to change the block's hash
|
||||
/// (and thus fork the chain). Sending a `Justification` alongside a block instead
|
||||
/// bypasses this problem.
|
||||
pub type Justification = Vec<u8>;
|
||||
|
||||
use traits::{Verify, Lazy};
|
||||
|
||||
/// A module identifier. These are per module and should be stored in a registry somewhere.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)]
|
||||
pub struct ModuleId(pub [u8; 8]);
|
||||
|
||||
impl TypeId for ModuleId {
|
||||
const TYPE_ID: [u8; 4] = *b"modl";
|
||||
}
|
||||
|
||||
/// A String that is a `&'static str` on `no_std` and a `Cow<'static, str>` on `std`.
|
||||
#[cfg(feature = "std")]
|
||||
pub type RuntimeString = std::borrow::Cow<'static, str>;
|
||||
/// A String that is a `&'static str` on `no_std` and a `Cow<'static, str>` on `std`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub type RuntimeString = &'static str;
|
||||
|
||||
/// Create a const [`RuntimeString`].
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_export]
|
||||
macro_rules! create_runtime_str {
|
||||
( $y:expr ) => {{ std::borrow::Cow::Borrowed($y) }}
|
||||
}
|
||||
|
||||
/// Create a const [`RuntimeString`].
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_export]
|
||||
macro_rules! create_runtime_str {
|
||||
( $y:expr ) => {{ $y }}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use serde::{Serialize, Deserialize, de::DeserializeOwned};
|
||||
use crate::traits::IdentifyAccount;
|
||||
|
||||
/// Complex storage builder stuff.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait BuildStorage: Sized {
|
||||
/// Build the storage out of this builder.
|
||||
fn build_storage(&self) -> Result<(StorageOverlay, ChildrenStorageOverlay), String> {
|
||||
let mut storage = (Default::default(), Default::default());
|
||||
self.assimilate_storage(&mut storage)?;
|
||||
Ok(storage)
|
||||
}
|
||||
/// Assimilate the storage for this module into pre-existing overlays.
|
||||
fn assimilate_storage(
|
||||
&self,
|
||||
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
|
||||
) -> Result<(), String>;
|
||||
}
|
||||
|
||||
/// Something that can build the genesis storage of a module.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait BuildModuleGenesisStorage<T, I>: Sized {
|
||||
/// Create the module genesis storage into the given `storage` and `child_storage`.
|
||||
fn build_module_genesis_storage(
|
||||
&self,
|
||||
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
|
||||
) -> Result<(), String>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) {
|
||||
fn assimilate_storage(
|
||||
&self,
|
||||
storage: &mut (StorageOverlay, ChildrenStorageOverlay),
|
||||
)-> Result<(), String> {
|
||||
storage.0.extend(self.0.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
for (k, other_map) in self.1.iter() {
|
||||
let k = k.clone();
|
||||
if let Some(map) = storage.1.get_mut(&k) {
|
||||
map.extend(other_map.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
} else {
|
||||
storage.1.insert(k, other_map.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Consensus engine unique ID.
|
||||
pub type ConsensusEngineId = [u8; 4];
|
||||
|
||||
/// Signature verify that can work with any known signature types..
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[derive(Eq, PartialEq, Clone, Encode, Decode, RuntimeDebug)]
|
||||
pub enum MultiSignature {
|
||||
/// An Ed25519 signature.
|
||||
Ed25519(ed25519::Signature),
|
||||
/// An Sr25519 signature.
|
||||
Sr25519(sr25519::Signature),
|
||||
/// An ECDSA/SECP256k1 signature.
|
||||
Ecdsa(ecdsa::Signature),
|
||||
}
|
||||
|
||||
impl From<ed25519::Signature> for MultiSignature {
|
||||
fn from(x: ed25519::Signature) -> Self {
|
||||
MultiSignature::Ed25519(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sr25519::Signature> for MultiSignature {
|
||||
fn from(x: sr25519::Signature) -> Self {
|
||||
MultiSignature::Sr25519(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ecdsa::Signature> for MultiSignature {
|
||||
fn from(x: ecdsa::Signature) -> Self {
|
||||
MultiSignature::Ecdsa(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MultiSignature {
|
||||
fn default() -> Self {
|
||||
MultiSignature::Ed25519(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Public key for any known crypto algorithm.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub enum MultiSigner {
|
||||
/// An Ed25519 identity.
|
||||
Ed25519(ed25519::Public),
|
||||
/// An Sr25519 identity.
|
||||
Sr25519(sr25519::Public),
|
||||
/// An SECP256k1/ECDSA identity (actually, the Blake2 hash of the pub key).
|
||||
Ecdsa(ecdsa::Public),
|
||||
}
|
||||
|
||||
impl Default for MultiSigner {
|
||||
fn default() -> Self {
|
||||
MultiSigner::Ed25519(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: This implementations is required by `SimpleAddressDeterminator`,
|
||||
/// we convert the hash into some AccountId, it's fine to use any scheme.
|
||||
impl<T: Into<H256>> crypto::UncheckedFrom<T> for MultiSigner {
|
||||
fn unchecked_from(x: T) -> Self {
|
||||
ed25519::Public::unchecked_from(x.into()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for MultiSigner {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
match *self {
|
||||
MultiSigner::Ed25519(ref who) => who.as_ref(),
|
||||
MultiSigner::Sr25519(ref who) => who.as_ref(),
|
||||
MultiSigner::Ecdsa(ref who) => who.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl traits::IdentifyAccount for MultiSigner {
|
||||
type AccountId = AccountId32;
|
||||
fn into_account(self) -> AccountId32 {
|
||||
match self {
|
||||
MultiSigner::Ed25519(who) => <[u8; 32]>::from(who).into(),
|
||||
MultiSigner::Sr25519(who) => <[u8; 32]>::from(who).into(),
|
||||
MultiSigner::Ecdsa(who) => runtime_io::hashing::blake2_256(who.as_ref()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ed25519::Public> for MultiSigner {
|
||||
fn from(x: ed25519::Public) -> Self {
|
||||
MultiSigner::Ed25519(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MultiSigner> for ed25519::Public {
|
||||
type Error = ();
|
||||
fn try_from(m: MultiSigner) -> Result<Self, Self::Error> {
|
||||
if let MultiSigner::Ed25519(x) = m { Ok(x) } else { Err(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sr25519::Public> for MultiSigner {
|
||||
fn from(x: sr25519::Public) -> Self {
|
||||
MultiSigner::Sr25519(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MultiSigner> for sr25519::Public {
|
||||
type Error = ();
|
||||
fn try_from(m: MultiSigner) -> Result<Self, Self::Error> {
|
||||
if let MultiSigner::Sr25519(x) = m { Ok(x) } else { Err(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ecdsa::Public> for MultiSigner {
|
||||
fn from(x: ecdsa::Public) -> Self {
|
||||
MultiSigner::Ecdsa(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MultiSigner> for ecdsa::Public {
|
||||
type Error = ();
|
||||
fn try_from(m: MultiSigner) -> Result<Self, Self::Error> {
|
||||
if let MultiSigner::Ecdsa(x) = m { Ok(x) } else { Err(()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::fmt::Display for MultiSigner {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match *self {
|
||||
MultiSigner::Ed25519(ref who) => write!(fmt, "ed25519: {}", who),
|
||||
MultiSigner::Sr25519(ref who) => write!(fmt, "sr25519: {}", who),
|
||||
MultiSigner::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Verify for MultiSignature {
|
||||
type Signer = MultiSigner;
|
||||
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
|
||||
use primitives::crypto::Public;
|
||||
match (self, signer) {
|
||||
(MultiSignature::Ed25519(ref sig), who) => sig.verify(msg, &ed25519::Public::from_slice(who.as_ref())),
|
||||
(MultiSignature::Sr25519(ref sig), who) => sig.verify(msg, &sr25519::Public::from_slice(who.as_ref())),
|
||||
(MultiSignature::Ecdsa(ref sig), who) => {
|
||||
let m = runtime_io::hashing::blake2_256(msg.get());
|
||||
match runtime_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
|
||||
Ok(pubkey) =>
|
||||
&runtime_io::hashing::blake2_256(pubkey.as_ref())
|
||||
== <dyn AsRef<[u8; 32]>>::as_ref(who),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Signature verify that can work with any known signature types..
|
||||
#[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct AnySignature(H512);
|
||||
|
||||
impl Verify for AnySignature {
|
||||
type Signer = sr25519::Public;
|
||||
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &sr25519::Public) -> bool {
|
||||
use primitives::crypto::Public;
|
||||
let msg = msg.get();
|
||||
sr25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
|
||||
.map(|s| s.verify(msg, signer))
|
||||
.unwrap_or(false)
|
||||
|| ed25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
|
||||
.map(|s| s.verify(msg, &ed25519::Public::from_slice(signer.as_ref())))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sr25519::Signature> for AnySignature {
|
||||
fn from(s: sr25519::Signature) -> Self {
|
||||
AnySignature(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ed25519::Signature> for AnySignature {
|
||||
fn from(s: ed25519::Signature) -> Self {
|
||||
AnySignature(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DispatchError> for DispatchOutcome {
|
||||
fn from(err: DispatchError) -> Self {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize))]
|
||||
/// Reason why a dispatch call failed
|
||||
pub struct DispatchError {
|
||||
/// Module index, matching the metadata module index
|
||||
pub module: Option<u8>,
|
||||
/// Module specific error value
|
||||
pub error: u8,
|
||||
/// Optional error message.
|
||||
#[codec(skip)]
|
||||
pub message: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl DispatchError {
|
||||
/// Create a new instance of `DispatchError`.
|
||||
pub fn new(module: Option<u8>, error: u8, message: Option<&'static str>) -> Self {
|
||||
Self {
|
||||
module,
|
||||
error,
|
||||
message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl traits::Printable for DispatchError {
|
||||
fn print(&self) {
|
||||
"DispatchError".print();
|
||||
if let Some(module) = self.module {
|
||||
module.print();
|
||||
}
|
||||
self.error.print();
|
||||
if let Some(msg) = self.message {
|
||||
msg.print();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl traits::ModuleDispatchError for &'static str {
|
||||
fn as_u8(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &'static str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for DispatchError {
|
||||
fn from(err: &'static str) -> DispatchError {
|
||||
DispatchError::new(None, 0, Some(err))
|
||||
}
|
||||
}
|
||||
|
||||
/// This type specifies the outcome of dispatching a call to a module.
|
||||
///
|
||||
/// In case of failure an error specific to the module is returned.
|
||||
///
|
||||
/// Failure of the module call dispatching doesn't invalidate the extrinsic and it is still included
|
||||
/// in the block, therefore all state changes performed by the dispatched call are still persisted.
|
||||
///
|
||||
/// For example, if the dispatching of an extrinsic involves inclusion fee payment then these
|
||||
/// changes are going to be preserved even if the call dispatched failed.
|
||||
pub type DispatchOutcome = Result<(), DispatchError>;
|
||||
|
||||
/// The result of applying of an extrinsic.
|
||||
///
|
||||
/// This type is typically used in the context of `BlockBuilder` to signal that the extrinsic
|
||||
/// in question cannot be included.
|
||||
///
|
||||
/// A block containing extrinsics that have a negative inclusion outcome is invalid. A negative
|
||||
/// result can only occur during the block production, where such extrinsics are detected and
|
||||
/// removed from the block that is being created and the transaction pool.
|
||||
///
|
||||
/// To rehash: every extrinsic in a valid block must return a positive `ApplyExtrinsicResult`.
|
||||
///
|
||||
/// Examples of reasons preventing inclusion in a block:
|
||||
/// - More block weight is required to process the extrinsic than is left in the block being built.
|
||||
/// This doesn't neccessarily mean that the extrinsic is invalid, since it can still be
|
||||
/// included in the next block if it has enough spare weight available.
|
||||
/// - The sender doesn't have enough funds to pay the transaction inclusion fee. Including such
|
||||
/// a transaction in the block doesn't make sense.
|
||||
/// - The extrinsic supplied a bad signature. This transaction won't become valid ever.
|
||||
pub type ApplyExtrinsicResult = Result<DispatchOutcome, transaction_validity::TransactionValidityError>;
|
||||
|
||||
/// Verify a signature on an encoded value in a lazy manner. This can be
|
||||
/// an optimization if the signature scheme has an "unsigned" escape hash.
|
||||
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(
|
||||
sig: &V,
|
||||
item: &T,
|
||||
signer: &<V::Signer as IdentifyAccount>::AccountId
|
||||
) -> bool {
|
||||
// The `Lazy<T>` trait expresses something like `X: FnMut<Output = for<'a> &'a T>`.
|
||||
// unfortunately this is a lifetime relationship that can't
|
||||
// be expressed without generic associated types, better unification of HRTBs in type position,
|
||||
// and some kind of integration into the Fn* traits.
|
||||
struct LazyEncode<F> {
|
||||
inner: F,
|
||||
encoded: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<F: Fn() -> Vec<u8>> traits::Lazy<[u8]> for LazyEncode<F> {
|
||||
fn get(&mut self) -> &[u8] {
|
||||
self.encoded.get_or_insert_with(&self.inner).as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
sig.verify(
|
||||
LazyEncode { inner: || item.encode(), encoded: None },
|
||||
signer,
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper macro for `impl_outer_config`
|
||||
#[macro_export]
|
||||
macro_rules! __impl_outer_config_types {
|
||||
// Generic + Instance
|
||||
(
|
||||
$concrete:ident $config:ident $snake:ident { $instance:ident } < $ignore:ident >;
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub type $config = $snake::GenesisConfig<$concrete, $snake::$instance>;
|
||||
$crate::__impl_outer_config_types! { $concrete $( $rest )* }
|
||||
};
|
||||
// Generic
|
||||
(
|
||||
$concrete:ident $config:ident $snake:ident < $ignore:ident >;
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub type $config = $snake::GenesisConfig<$concrete>;
|
||||
$crate::__impl_outer_config_types! { $concrete $( $rest )* }
|
||||
};
|
||||
// No Generic and maybe Instance
|
||||
(
|
||||
$concrete:ident $config:ident $snake:ident $( { $instance:ident } )?;
|
||||
$( $rest:tt )*
|
||||
) => {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub type $config = $snake::GenesisConfig;
|
||||
$crate::__impl_outer_config_types! { $concrete $( $rest )* }
|
||||
};
|
||||
($concrete:ident) => ()
|
||||
}
|
||||
|
||||
/// Implement the output "meta" module configuration struct,
|
||||
/// which is basically:
|
||||
/// pub struct GenesisConfig {
|
||||
/// rust_module_one: Option<ModuleOneConfig>,
|
||||
/// ...
|
||||
/// }
|
||||
#[macro_export]
|
||||
macro_rules! impl_outer_config {
|
||||
(
|
||||
pub struct $main:ident for $concrete:ident {
|
||||
$( $config:ident =>
|
||||
$snake:ident $( $instance:ident )? $( <$generic:ident> )*, )*
|
||||
}
|
||||
) => {
|
||||
$crate::__impl_outer_config_types! {
|
||||
$concrete $( $config $snake $( { $instance } )? $( <$generic> )*; )*
|
||||
}
|
||||
|
||||
$crate::paste::item! {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct $main {
|
||||
$(
|
||||
pub [< $snake $(_ $instance )? >]: Option<$config>,
|
||||
)*
|
||||
}
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl $crate::BuildStorage for $main {
|
||||
fn assimilate_storage(
|
||||
&self,
|
||||
storage: &mut ($crate::StorageOverlay, $crate::ChildrenStorageOverlay),
|
||||
) -> std::result::Result<(), String> {
|
||||
$(
|
||||
if let Some(ref extra) = self.[< $snake $(_ $instance )? >] {
|
||||
$crate::impl_outer_config! {
|
||||
@CALL_FN
|
||||
$concrete;
|
||||
$snake;
|
||||
$( $instance )?;
|
||||
extra;
|
||||
storage;
|
||||
}
|
||||
}
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(@CALL_FN
|
||||
$runtime:ident;
|
||||
$module:ident;
|
||||
$instance:ident;
|
||||
$extra:ident;
|
||||
$storage:ident;
|
||||
) => {
|
||||
$crate::BuildModuleGenesisStorage::<$runtime, $module::$instance>::build_module_genesis_storage(
|
||||
$extra,
|
||||
$storage,
|
||||
)?;
|
||||
};
|
||||
(@CALL_FN
|
||||
$runtime:ident;
|
||||
$module:ident;
|
||||
;
|
||||
$extra:ident;
|
||||
$storage:ident;
|
||||
) => {
|
||||
$crate::BuildModuleGenesisStorage::<$runtime, $module::__InherentHiddenInstance>::build_module_genesis_storage(
|
||||
$extra,
|
||||
$storage,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `$x` is equal to `$y` with an error rate of `$error`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() {
|
||||
/// sp_runtime::assert_eq_error_rate!(10, 10, 0);
|
||||
/// sp_runtime::assert_eq_error_rate!(10, 11, 1);
|
||||
/// sp_runtime::assert_eq_error_rate!(12, 10, 2);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// # fn main() {
|
||||
/// sp_runtime::assert_eq_error_rate!(12, 10, 1);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(feature = "std")]
|
||||
macro_rules! assert_eq_error_rate {
|
||||
($x:expr, $y:expr, $error:expr $(,)?) => {
|
||||
assert!(
|
||||
($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)),
|
||||
"{:?} != {:?} (with error rate {:?})",
|
||||
$x,
|
||||
$y,
|
||||
$error,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Simple blob to hold an extrinsic without committing to its format and ensure it is serialized
|
||||
/// correctly.
|
||||
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode)]
|
||||
pub struct OpaqueExtrinsic(pub Vec<u8>);
|
||||
|
||||
impl rstd::fmt::Debug for OpaqueExtrinsic {
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, fmt: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
|
||||
write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _fmt: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl ::serde::Serialize for OpaqueExtrinsic {
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
codec::Encode::using_encoded(&self.0, |bytes| ::primitives::bytes::serialize(bytes, seq))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'a> {
|
||||
let r = ::primitives::bytes::deserialize(de)?;
|
||||
Decode::decode(&mut &r[..])
|
||||
.map_err(|e| ::serde::de::Error::custom(format!("Decode error: {}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
impl traits::Extrinsic for OpaqueExtrinsic {
|
||||
type Call = ();
|
||||
type SignaturePayload = ();
|
||||
}
|
||||
|
||||
/// Print something that implements `Printable` from the runtime.
|
||||
pub fn print(print: impl traits::Printable) {
|
||||
print.print();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::DispatchError;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
#[test]
|
||||
fn opaque_extrinsic_serialization() {
|
||||
let ex = super::OpaqueExtrinsic(vec![1, 2, 3, 4]);
|
||||
assert_eq!(serde_json::to_string(&ex).unwrap(), "\"0x1001020304\"".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch_error_encoding() {
|
||||
let error = DispatchError {
|
||||
module: Some(1),
|
||||
error: 2,
|
||||
message: Some("error message"),
|
||||
};
|
||||
let encoded = error.encode();
|
||||
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
|
||||
assert_eq!(encoded, vec![1, 1, 2]);
|
||||
assert_eq!(
|
||||
decoded,
|
||||
DispatchError {
|
||||
module: Some(1),
|
||||
error: 2,
|
||||
message: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A high-level helpers for making HTTP requests from Offchain Workers.
|
||||
//!
|
||||
//! `sp-io` crate exposes a low level methods to make and control HTTP requests
|
||||
//! available only for Offchain Workers. Those might be hard to use
|
||||
//! and usually that level of control is not really necessary.
|
||||
//! This module aims to provide high-level wrappers for those APIs
|
||||
//! to simplify making HTTP requests.
|
||||
//!
|
||||
//!
|
||||
//! Example:
|
||||
//! ```rust,no_run
|
||||
//! use sp_runtime::offchain::http::Request;
|
||||
//!
|
||||
//! // initiate a GET request to localhost:1234
|
||||
//! let request: Request = Request::get("http://localhost:1234");
|
||||
//! let pending = request
|
||||
//! .add_header("X-Auth", "hunter2")
|
||||
//! .send()
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! // wait for the response indefinitely
|
||||
//! let mut response = pending.wait().unwrap();
|
||||
//!
|
||||
//! // then check the headers
|
||||
//! let mut headers = response.headers().into_iter();
|
||||
//! assert_eq!(headers.current(), None);
|
||||
//!
|
||||
//! // and collect the body
|
||||
//! let body = response.body();
|
||||
//! assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
|
||||
//! assert_eq!(body.error(), &None);
|
||||
//! ```
|
||||
|
||||
use rstd::str;
|
||||
use rstd::prelude::Vec;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::prelude::vec;
|
||||
use primitives::RuntimeDebug;
|
||||
use primitives::offchain::{
|
||||
Timestamp,
|
||||
HttpRequestId as RequestId,
|
||||
HttpRequestStatus as RequestStatus,
|
||||
HttpError,
|
||||
};
|
||||
|
||||
/// Request method (HTTP verb)
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub enum Method {
|
||||
/// GET request
|
||||
Get,
|
||||
/// POST request
|
||||
Post,
|
||||
/// PUT request
|
||||
Put,
|
||||
/// PATCH request
|
||||
Patch,
|
||||
/// DELETE request
|
||||
Delete,
|
||||
/// Custom verb
|
||||
Other(&'static str),
|
||||
}
|
||||
|
||||
impl AsRef<str> for Method {
|
||||
fn as_ref(&self) -> &str {
|
||||
match *self {
|
||||
Method::Get => "GET",
|
||||
Method::Post => "POST",
|
||||
Method::Put => "PUT",
|
||||
Method::Patch => "PATCH",
|
||||
Method::Delete => "DELETE",
|
||||
Method::Other(m) => m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod header {
|
||||
use super::*;
|
||||
|
||||
/// A header type.
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct Header {
|
||||
name: Vec<u8>,
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Creates new header given it's name and value.
|
||||
pub fn new(name: &str, value: &str) -> Self {
|
||||
Header {
|
||||
name: name.as_bytes().to_vec(),
|
||||
value: value.as_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the name of this header.
|
||||
pub fn name(&self) -> &str {
|
||||
// Header keys are always produced from `&str` so this is safe.
|
||||
// we don't store them as `Strings` to avoid bringing `alloc::String` to rstd
|
||||
// or here.
|
||||
unsafe { str::from_utf8_unchecked(&self.name) }
|
||||
}
|
||||
|
||||
/// Returns the value of this header.
|
||||
pub fn value(&self) -> &str {
|
||||
// Header values are always produced from `&str` so this is safe.
|
||||
// we don't store them as `Strings` to avoid bringing `alloc::String` to rstd
|
||||
// or here.
|
||||
unsafe { str::from_utf8_unchecked(&self.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An HTTP request builder.
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct Request<'a, T = Vec<&'static [u8]>> {
|
||||
/// Request method
|
||||
pub method: Method,
|
||||
/// Request URL
|
||||
pub url: &'a str,
|
||||
/// Body of the request
|
||||
pub body: T,
|
||||
/// Deadline to finish sending the request
|
||||
pub deadline: Option<Timestamp>,
|
||||
/// Request list of headers.
|
||||
headers: Vec<header::Header>,
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Request<'static, T> {
|
||||
fn default() -> Self {
|
||||
Request {
|
||||
method: Method::Get,
|
||||
url: "http://localhost",
|
||||
headers: Vec::new(),
|
||||
body: Default::default(),
|
||||
deadline: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
/// Start a simple GET request
|
||||
pub fn get(url: &'a str) -> Self {
|
||||
Self::new(url)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Request<'a, T> {
|
||||
/// Create new POST request with given body.
|
||||
pub fn post(url: &'a str, body: T) -> Self {
|
||||
let req: Request = Request::default();
|
||||
|
||||
Request {
|
||||
url,
|
||||
body,
|
||||
method: Method::Post,
|
||||
headers: req.headers,
|
||||
deadline: req.deadline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Default> Request<'a, T> {
|
||||
/// Create new Request builder with given URL and body.
|
||||
pub fn new(url: &'a str) -> Self {
|
||||
Request::default().url(url)
|
||||
}
|
||||
|
||||
/// Change the method of the request
|
||||
pub fn method(mut self, method: Method) -> Self {
|
||||
self.method = method;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change the URL of the request.
|
||||
pub fn url(mut self, url: &'a str) -> Self {
|
||||
self.url = url;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the body of the request.
|
||||
pub fn body(mut self, body: T) -> Self {
|
||||
self.body = body;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a header.
|
||||
pub fn add_header(mut self, name: &str, value: &str) -> Self {
|
||||
self.headers.push(header::Header::new(name, value));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the deadline of the request.
|
||||
pub fn deadline(mut self, deadline: Timestamp) -> Self {
|
||||
self.deadline = Some(deadline);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: AsRef<[u8]>, T: IntoIterator<Item=I>> Request<'a, T> {
|
||||
/// Send the request and return a handle.
|
||||
///
|
||||
/// Err is returned in case the deadline is reached
|
||||
/// or the request timeouts.
|
||||
pub fn send(self) -> Result<PendingRequest, HttpError> {
|
||||
let meta = &[];
|
||||
|
||||
// start an http request.
|
||||
let id = runtime_io::offchain::http_request_start(
|
||||
self.method.as_ref(),
|
||||
self.url,
|
||||
meta,
|
||||
).map_err(|_| HttpError::IoError)?;
|
||||
|
||||
// add custom headers
|
||||
for header in &self.headers {
|
||||
runtime_io::offchain::http_request_add_header(
|
||||
id,
|
||||
header.name(),
|
||||
header.value(),
|
||||
).map_err(|_| HttpError::IoError)?
|
||||
}
|
||||
|
||||
// write body
|
||||
for chunk in self.body {
|
||||
runtime_io::offchain::http_request_write_body(id, chunk.as_ref(), self.deadline)?;
|
||||
}
|
||||
|
||||
// finalise the request
|
||||
runtime_io::offchain::http_request_write_body(id, &[], self.deadline)?;
|
||||
|
||||
Ok(PendingRequest {
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A request error
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub enum Error {
|
||||
/// Deadline has been reached.
|
||||
DeadlineReached,
|
||||
/// Request had timed out.
|
||||
IoError,
|
||||
/// Unknown error has been ecountered.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// A struct representing an uncompleted http request.
|
||||
#[derive(PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct PendingRequest {
|
||||
/// Request ID
|
||||
pub id: RequestId,
|
||||
}
|
||||
|
||||
/// A result of waiting for a pending request.
|
||||
pub type HttpResult = Result<Response, Error>;
|
||||
|
||||
impl PendingRequest {
|
||||
/// Wait for the request to complete.
|
||||
///
|
||||
/// NOTE this waits for the request indefinitely.
|
||||
pub fn wait(self) -> HttpResult {
|
||||
match self.try_wait(None) {
|
||||
Ok(res) => res,
|
||||
Err(_) => panic!("Since `None` is passed we will never get a deadline error; qed"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to wait for the request to finish,
|
||||
/// but will return `Err` in case the deadline is reached.
|
||||
pub fn try_wait(self, deadline: impl Into<Option<Timestamp>>) -> Result<HttpResult, PendingRequest> {
|
||||
Self::try_wait_all(vec![self], deadline).pop().expect("One request passed, one status received; qed")
|
||||
}
|
||||
|
||||
/// Wait for all provided requests.
|
||||
pub fn wait_all(requests: Vec<PendingRequest>) -> Vec<HttpResult> {
|
||||
Self::try_wait_all(requests, None)
|
||||
.into_iter()
|
||||
.map(|r| match r {
|
||||
Ok(r) => r,
|
||||
Err(_) => panic!("Since `None` is passed we will never get a deadline error; qed"),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Attempt to wait for all provided requests, but up to given deadline.
|
||||
///
|
||||
/// Requests that are complete will resolve to an `Ok` others will return a `DeadlineReached` error.
|
||||
pub fn try_wait_all(
|
||||
requests: Vec<PendingRequest>,
|
||||
deadline: impl Into<Option<Timestamp>>
|
||||
) -> Vec<Result<HttpResult, PendingRequest>> {
|
||||
let ids = requests.iter().map(|r| r.id).collect::<Vec<_>>();
|
||||
let statuses = runtime_io::offchain::http_response_wait(&ids, deadline.into());
|
||||
|
||||
statuses
|
||||
.into_iter()
|
||||
.zip(requests.into_iter())
|
||||
.map(|(status, req)| match status {
|
||||
RequestStatus::DeadlineReached => Err(req),
|
||||
RequestStatus::IoError => Ok(Err(Error::IoError)),
|
||||
RequestStatus::Invalid => Ok(Err(Error::Unknown)),
|
||||
RequestStatus::Finished(code) => Ok(Ok(Response::new(req.id, code))),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A HTTP response.
|
||||
#[derive(RuntimeDebug)]
|
||||
pub struct Response {
|
||||
/// Request id
|
||||
pub id: RequestId,
|
||||
/// Response status code
|
||||
pub code: u16,
|
||||
/// A collection of headers.
|
||||
headers: Option<Headers>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
fn new(id: RequestId, code: u16) -> Self {
|
||||
Self {
|
||||
id,
|
||||
code,
|
||||
headers: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the headers for this response.
|
||||
pub fn headers(&mut self) -> &Headers {
|
||||
if self.headers.is_none() {
|
||||
self.headers = Some(
|
||||
Headers { raw: runtime_io::offchain::http_response_headers(self.id) },
|
||||
);
|
||||
}
|
||||
self.headers.as_ref().expect("Headers were just set; qed")
|
||||
}
|
||||
|
||||
/// Retrieve the body of this response.
|
||||
pub fn body(&self) -> ResponseBody {
|
||||
ResponseBody::new(self.id)
|
||||
}
|
||||
}
|
||||
|
||||
/// A buffered byte iterator over response body.
|
||||
///
|
||||
/// Note that reading the body may return `None` in following cases:
|
||||
/// 1. Either the deadline you've set is reached (check via `#error`;
|
||||
/// In such case you can resume the reader by setting a new deadline)
|
||||
/// 2. Or because of IOError. In such case the reader is not resumable and will keep
|
||||
/// returning `None`.
|
||||
/// 3. The body has been returned. The reader will keep returning `None`.
|
||||
#[derive(Clone)]
|
||||
pub struct ResponseBody {
|
||||
id: RequestId,
|
||||
error: Option<HttpError>,
|
||||
buffer: [u8; 4096],
|
||||
filled_up_to: Option<usize>,
|
||||
position: usize,
|
||||
deadline: Option<Timestamp>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::fmt::Debug for ResponseBody {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmt.debug_struct("ResponseBody")
|
||||
.field("id", &self.id)
|
||||
.field("error", &self.error)
|
||||
.field("buffer", &self.buffer.len())
|
||||
.field("filled_up_to", &self.filled_up_to)
|
||||
.field("position", &self.position)
|
||||
.field("deadline", &self.deadline)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseBody {
|
||||
fn new(id: RequestId) -> Self {
|
||||
ResponseBody {
|
||||
id,
|
||||
error: None,
|
||||
buffer: [0_u8; 4096],
|
||||
filled_up_to: None,
|
||||
position: 0,
|
||||
deadline: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the deadline for reading the body.
|
||||
pub fn deadline(&mut self, deadline: impl Into<Option<Timestamp>>) {
|
||||
self.deadline = deadline.into();
|
||||
self.error = None;
|
||||
}
|
||||
|
||||
/// Return an error that caused the iterator to return `None`.
|
||||
///
|
||||
/// If the error is `DeadlineReached` you can resume the iterator by setting
|
||||
/// a new deadline.
|
||||
pub fn error(&self) -> &Option<HttpError> {
|
||||
&self.error
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ResponseBody {
|
||||
type Item = u8;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.error.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.filled_up_to.is_none() {
|
||||
let result = runtime_io::offchain::http_response_read_body(
|
||||
self.id,
|
||||
&mut self.buffer,
|
||||
self.deadline);
|
||||
match result {
|
||||
Err(e) => {
|
||||
self.error = Some(e);
|
||||
return None;
|
||||
}
|
||||
Ok(0) => {
|
||||
return None;
|
||||
}
|
||||
Ok(size) => {
|
||||
self.position = 0;
|
||||
self.filled_up_to = Some(size as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if Some(self.position) == self.filled_up_to {
|
||||
self.filled_up_to = None;
|
||||
return self.next();
|
||||
}
|
||||
|
||||
let result = self.buffer[self.position];
|
||||
self.position += 1;
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of Headers in the response.
|
||||
#[derive(Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
pub struct Headers {
|
||||
/// Raw headers
|
||||
pub raw: Vec<(Vec<u8>, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
/// Retrieve a single header from the list of headers.
|
||||
///
|
||||
/// Note this method is linearly looking from all the headers
|
||||
/// comparing them with the needle byte-by-byte.
|
||||
/// If you want to consume multiple headers it's better to iterate
|
||||
/// and collect them on your own.
|
||||
pub fn find(&self, name: &str) -> Option<&str> {
|
||||
let raw = name.as_bytes();
|
||||
for &(ref key, ref val) in &self.raw {
|
||||
if &**key == raw {
|
||||
return str::from_utf8(&val).ok()
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Convert this headers into an iterator.
|
||||
pub fn into_iter(&self) -> HeadersIterator {
|
||||
HeadersIterator { collection: &self.raw, index: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom iterator traversing all the headers.
|
||||
#[derive(Clone, RuntimeDebug)]
|
||||
pub struct HeadersIterator<'a> {
|
||||
collection: &'a [(Vec<u8>, Vec<u8>)],
|
||||
index: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a> HeadersIterator<'a> {
|
||||
/// Move the iterator to the next position.
|
||||
///
|
||||
/// Returns `true` is `current` has been set by this call.
|
||||
pub fn next(&mut self) -> bool {
|
||||
let index = self.index.map(|x| x + 1).unwrap_or(0);
|
||||
self.index = Some(index);
|
||||
index < self.collection.len()
|
||||
}
|
||||
|
||||
/// Returns current element (if any).
|
||||
///
|
||||
/// Note that you have to call `next` prior to calling this
|
||||
pub fn current(&self) -> Option<(&str, &str)> {
|
||||
self.collection.get(self.index?)
|
||||
.map(|val| (str::from_utf8(&val.0).unwrap_or(""), str::from_utf8(&val.1).unwrap_or("")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_io::TestExternalities;
|
||||
use primitives::offchain::{
|
||||
OffchainExt,
|
||||
testing,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn should_send_a_basic_request_and_get_response() {
|
||||
let (offchain, state) = testing::TestOffchainExt::new();
|
||||
let mut t = TestExternalities::default();
|
||||
t.register_extension(OffchainExt::new(offchain));
|
||||
|
||||
t.execute_with(|| {
|
||||
let request: Request = Request::get("http://localhost:1234");
|
||||
let pending = request
|
||||
.add_header("X-Auth", "hunter2")
|
||||
.send()
|
||||
.unwrap();
|
||||
// make sure it's sent correctly
|
||||
state.write().fulfill_pending_request(
|
||||
0,
|
||||
testing::PendingRequest {
|
||||
method: "GET".into(),
|
||||
uri: "http://localhost:1234".into(),
|
||||
headers: vec![("X-Auth".into(), "hunter2".into())],
|
||||
sent: true,
|
||||
..Default::default()
|
||||
},
|
||||
b"1234".to_vec(),
|
||||
None,
|
||||
);
|
||||
|
||||
// wait
|
||||
let mut response = pending.wait().unwrap();
|
||||
|
||||
// then check the response
|
||||
let mut headers = response.headers().into_iter();
|
||||
assert_eq!(headers.current(), None);
|
||||
assert_eq!(headers.next(), false);
|
||||
assert_eq!(headers.current(), None);
|
||||
|
||||
let body = response.body();
|
||||
assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
|
||||
assert_eq!(body.error(), &None);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_send_a_post_request() {
|
||||
let (offchain, state) = testing::TestOffchainExt::new();
|
||||
let mut t = TestExternalities::default();
|
||||
t.register_extension(OffchainExt::new(offchain));
|
||||
|
||||
t.execute_with(|| {
|
||||
let pending = Request::default()
|
||||
.method(Method::Post)
|
||||
.url("http://localhost:1234")
|
||||
.body(vec![b"1234"])
|
||||
.send()
|
||||
.unwrap();
|
||||
// make sure it's sent correctly
|
||||
state.write().fulfill_pending_request(
|
||||
0,
|
||||
testing::PendingRequest {
|
||||
method: "POST".into(),
|
||||
uri: "http://localhost:1234".into(),
|
||||
body: b"1234".to_vec(),
|
||||
sent: true,
|
||||
..Default::default()
|
||||
},
|
||||
b"1234".to_vec(),
|
||||
Some(("Test".to_owned(), "Header".to_owned())),
|
||||
);
|
||||
|
||||
// wait
|
||||
let mut response = pending.wait().unwrap();
|
||||
|
||||
// then check the response
|
||||
let mut headers = response.headers().into_iter();
|
||||
assert_eq!(headers.current(), None);
|
||||
assert_eq!(headers.next(), true);
|
||||
assert_eq!(headers.current(), Some(("Test", "Header")));
|
||||
|
||||
let body = response.body();
|
||||
assert_eq!(body.clone().collect::<Vec<_>>(), b"1234".to_vec());
|
||||
assert_eq!(body.error(), &None);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A collection of higher lever helpers for offchain calls.
|
||||
|
||||
pub mod http;
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A simple pseudo random number generator that allows a stream of random numbers to be efficiently
|
||||
//! created from a single initial seed hash.
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
use crate::traits::{Hash, TrailingZeroInput};
|
||||
|
||||
/// Pseudo-random number streamer. This retains the state of the random number stream. It's as
|
||||
/// secure as the combination of the seed with which it is constructed and the hash function it uses
|
||||
/// to cycle elements.
|
||||
///
|
||||
/// It can be saved and later reloaded using the Codec traits.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// use sp_runtime::traits::{Hash, BlakeTwo256};
|
||||
/// use sp_runtime::RandomNumberGenerator;
|
||||
/// let random_seed = BlakeTwo256::hash(b"Sixty-nine");
|
||||
/// let mut rng = <RandomNumberGenerator<BlakeTwo256>>::new(random_seed);
|
||||
/// assert_eq!(rng.pick_u32(100), 59);
|
||||
/// assert_eq!(rng.pick_item(&[1, 2, 3]), Some(&1));
|
||||
/// ```
|
||||
///
|
||||
/// This can use any cryptographic `Hash` function as the means of entropy-extension, and avoids
|
||||
/// needless extensions of entropy.
|
||||
///
|
||||
/// If you're persisting it over blocks, be aware that the sequence will start to repeat. This won't
|
||||
/// be a practical issue unless you're using tiny hash types (e.g. 64-bit) and pulling hundred of
|
||||
/// megabytes of data from it.
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct RandomNumberGenerator<Hashing: Hash> {
|
||||
current: Hashing::Output,
|
||||
offset: u32,
|
||||
}
|
||||
|
||||
impl<Hashing: Hash> RandomNumberGenerator<Hashing> {
|
||||
/// A new source of random data.
|
||||
pub fn new(seed: Hashing::Output) -> Self {
|
||||
Self {
|
||||
current: seed,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize { self.offset as usize }
|
||||
|
||||
/// Returns a number at least zero, at most `max`.
|
||||
pub fn pick_u32(&mut self, max: u32) -> u32 {
|
||||
let needed = (4 - max.leading_zeros() / 8) as usize;
|
||||
let top = ((1 << (needed as u64 * 8)) / ((max + 1) as u64) * ((max + 1) as u64) - 1) as u32;
|
||||
loop {
|
||||
if self.offset() + needed > self.current.as_ref().len() {
|
||||
// rehash
|
||||
self.current = Hashing::hash(self.current.as_ref());
|
||||
self.offset = 0;
|
||||
}
|
||||
let data = &self.current.as_ref()[self.offset()..self.offset() + needed];
|
||||
self.offset += needed as u32;
|
||||
let raw = u32::decode(&mut TrailingZeroInput::new(data)).unwrap_or(0);
|
||||
if raw <= top {
|
||||
break if max < u32::max_value() {
|
||||
raw % (max + 1)
|
||||
} else {
|
||||
raw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a number at least zero, at most `max`.
|
||||
///
|
||||
/// This returns a `usize`, but internally it only uses `u32` so avoid consensus problems.
|
||||
pub fn pick_usize(&mut self, max: usize) -> usize {
|
||||
self.pick_u32(max as u32) as usize
|
||||
}
|
||||
|
||||
/// Pick a random element from an array of `items`.
|
||||
///
|
||||
/// This is guaranteed to return `Some` except in the case that the given array `items` is
|
||||
/// empty.
|
||||
pub fn pick_item<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> {
|
||||
if items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&items[self.pick_usize(items.len() - 1)])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Testing utilities.
|
||||
|
||||
use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializer};
|
||||
use std::{fmt::Debug, ops::Deref, fmt, cell::RefCell};
|
||||
use crate::codec::{Codec, Encode, Decode};
|
||||
use crate::traits::{
|
||||
self, Checkable, Applyable, BlakeTwo256, OpaqueKeys,
|
||||
SignedExtension, Dispatchable,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use crate::traits::ValidateUnsigned;
|
||||
use crate::{generic, KeyTypeId, ApplyExtrinsicResult};
|
||||
pub use primitives::{H256, sr25519};
|
||||
use primitives::{crypto::{CryptoType, Dummy, key_types, Public}, U256};
|
||||
use crate::transaction_validity::{TransactionValidity, TransactionValidityError};
|
||||
|
||||
/// Authority Id
|
||||
#[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize, PartialOrd, Ord)]
|
||||
pub struct UintAuthorityId(pub u64);
|
||||
|
||||
impl From<u64> for UintAuthorityId {
|
||||
fn from(id: u64) -> Self {
|
||||
UintAuthorityId(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UintAuthorityId> for u64 {
|
||||
fn from(id: UintAuthorityId) -> u64 {
|
||||
id.0
|
||||
}
|
||||
}
|
||||
|
||||
impl UintAuthorityId {
|
||||
/// Convert this authority id into a public key.
|
||||
pub fn to_public_key<T: Public>(&self) -> T {
|
||||
let bytes: [u8; 32] = U256::from(self.0).into();
|
||||
T::from_slice(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoType for UintAuthorityId {
|
||||
type Pair = Dummy;
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for UintAuthorityId {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
// Unsafe, i know, but it's test code and it's just there because it's really convenient to
|
||||
// keep `UintAuthorityId` as a u64 under the hood.
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(&self.0 as *const u64 as *const _, std::mem::size_of::<u64>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// A list of all UintAuthorityId keys returned to the runtime.
|
||||
static ALL_KEYS: RefCell<Vec<UintAuthorityId>> = RefCell::new(vec![]);
|
||||
}
|
||||
|
||||
impl UintAuthorityId {
|
||||
/// Set the list of keys returned by the runtime call for all keys of that type.
|
||||
pub fn set_all_keys<T: Into<UintAuthorityId>>(keys: impl IntoIterator<Item=T>) {
|
||||
ALL_KEYS.with(|l| *l.borrow_mut() = keys.into_iter().map(Into::into).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl app_crypto::RuntimeAppPublic for UintAuthorityId {
|
||||
const ID: KeyTypeId = key_types::DUMMY;
|
||||
|
||||
type Signature = u64;
|
||||
|
||||
fn all() -> Vec<Self> {
|
||||
ALL_KEYS.with(|l| l.borrow().clone())
|
||||
}
|
||||
|
||||
fn generate_pair(_: Option<Vec<u8>>) -> Self {
|
||||
use rand::RngCore;
|
||||
UintAuthorityId(rand::thread_rng().next_u64())
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature> {
|
||||
let mut signature = [0u8; 8];
|
||||
msg.as_ref().iter()
|
||||
.chain(std::iter::repeat(&42u8))
|
||||
.take(8)
|
||||
.enumerate()
|
||||
.for_each(|(i, v)| { signature[i] = *v; });
|
||||
|
||||
Some(u64::from_le_bytes(signature))
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
let mut msg_signature = [0u8; 8];
|
||||
msg.as_ref().iter()
|
||||
.chain(std::iter::repeat(&42))
|
||||
.take(8)
|
||||
.enumerate()
|
||||
.for_each(|(i, v)| { msg_signature[i] = *v; });
|
||||
|
||||
u64::from_le_bytes(msg_signature) == *signature
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueKeys for UintAuthorityId {
|
||||
type KeyTypeIdProviders = ();
|
||||
|
||||
fn key_ids() -> &'static [KeyTypeId] {
|
||||
&[key_types::DUMMY]
|
||||
}
|
||||
|
||||
fn get_raw(&self, _: KeyTypeId) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
fn get<T: Decode>(&self, _: KeyTypeId) -> Option<T> {
|
||||
self.using_encoded(|mut x| T::decode(&mut x)).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::BoundToRuntimeAppPublic for UintAuthorityId {
|
||||
type Public = Self;
|
||||
}
|
||||
|
||||
/// Digest item
|
||||
pub type DigestItem = generic::DigestItem<H256>;
|
||||
|
||||
/// Header Digest
|
||||
pub type Digest = generic::Digest<H256>;
|
||||
|
||||
/// Block Header
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Header {
|
||||
/// Parent hash
|
||||
pub parent_hash: H256,
|
||||
/// Block Number
|
||||
pub number: u64,
|
||||
/// Post-execution state trie root
|
||||
pub state_root: H256,
|
||||
/// Merkle root of block's extrinsics
|
||||
pub extrinsics_root: H256,
|
||||
/// Digest items
|
||||
pub digest: Digest,
|
||||
}
|
||||
|
||||
impl traits::Header for Header {
|
||||
type Number = u64;
|
||||
type Hashing = BlakeTwo256;
|
||||
type Hash = H256;
|
||||
|
||||
fn number(&self) -> &Self::Number { &self.number }
|
||||
fn set_number(&mut self, num: Self::Number) { self.number = num }
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash { &self.extrinsics_root }
|
||||
fn set_extrinsics_root(&mut self, root: Self::Hash) { self.extrinsics_root = root }
|
||||
|
||||
fn state_root(&self) -> &Self::Hash { &self.state_root }
|
||||
fn set_state_root(&mut self, root: Self::Hash) { self.state_root = root }
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash { &self.parent_hash }
|
||||
fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash }
|
||||
|
||||
fn digest(&self) -> &Digest { &self.digest }
|
||||
fn digest_mut(&mut self) -> &mut Digest { &mut self.digest }
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Digest,
|
||||
) -> Self {
|
||||
Header {
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root,
|
||||
parent_hash,
|
||||
digest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// A new header with the given number and default hash for all other fields.
|
||||
pub fn new_from_number(number: <Self as traits::Header>::Number) -> Self {
|
||||
Self {
|
||||
number,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Header {
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let r = <Vec<u8>>::deserialize(de)?;
|
||||
Decode::decode(&mut &r[..])
|
||||
.map_err(|e| DeError::custom(format!("Invalid value passed into decode: {}", e.what())))
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque extrinsic wrapper type.
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)]
|
||||
pub struct ExtrinsicWrapper<Xt>(Xt);
|
||||
|
||||
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
|
||||
type Call = ();
|
||||
type SignaturePayload = ();
|
||||
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt> {
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt> From<Xt> for ExtrinsicWrapper<Xt> {
|
||||
fn from(xt: Xt) -> Self {
|
||||
ExtrinsicWrapper(xt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Xt> Deref for ExtrinsicWrapper<Xt> {
|
||||
type Target = Xt;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Testing block
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode)]
|
||||
pub struct Block<Xt> {
|
||||
/// Block header
|
||||
pub header: Header,
|
||||
/// List of extrinsics
|
||||
pub extrinsics: Vec<Xt>,
|
||||
}
|
||||
|
||||
impl<Xt: 'static + Codec + Sized + Send + Sync + Serialize + Clone + Eq + Debug + traits::Extrinsic> traits::Block
|
||||
for Block<Xt>
|
||||
{
|
||||
type Extrinsic = Xt;
|
||||
type Header = Header;
|
||||
type Hash = <Header as traits::Header>::Hash;
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.extrinsics[..]
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.extrinsics)
|
||||
}
|
||||
fn new(header: Self::Header, extrinsics: Vec<Self::Extrinsic>) -> Self {
|
||||
Block { header, extrinsics }
|
||||
}
|
||||
fn encode_from(header: &Self::Header, extrinsics: &[Self::Extrinsic]) -> Vec<u8> {
|
||||
(header, extrinsics).encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Xt> Deserialize<'a> for Block<Xt> where Block<Xt>: Decode {
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let r = <Vec<u8>>::deserialize(de)?;
|
||||
Decode::decode(&mut &r[..])
|
||||
.map_err(|e| DeError::custom(format!("Invalid value passed into decode: {}", e.what())))
|
||||
}
|
||||
}
|
||||
|
||||
/// Test transaction, tuple of (sender, call, signed_extra)
|
||||
/// with index only used if sender is some.
|
||||
///
|
||||
/// If sender is some then the transaction is signed otherwise it is unsigned.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
pub struct TestXt<Call, Extra>(pub Option<(u64, Extra)>, pub Call);
|
||||
|
||||
impl<Call, Extra> Serialize for TestXt<Call, Extra> where TestXt<Call, Extra>: Encode {
|
||||
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call, Extra> Debug for TestXt<Call, Extra> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TestXt({:?}, ...)", self.0.as_ref().map(|x| &x.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: Codec + Sync + Send, Context, Extra> Checkable<Context> for TestXt<Call, Extra> {
|
||||
type Checked = Self;
|
||||
fn check(self, _: &Context) -> Result<Self::Checked, TransactionValidityError> { Ok(self) }
|
||||
}
|
||||
impl<Call: Codec + Sync + Send, Extra> traits::Extrinsic for TestXt<Call, Extra> {
|
||||
type Call = Call;
|
||||
type SignaturePayload = (u64, Extra);
|
||||
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.0.is_some())
|
||||
}
|
||||
|
||||
fn new(c: Call, sig: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(TestXt(sig, c))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Origin, Call, Extra, Info> Applyable for TestXt<Call, Extra> where
|
||||
Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable<Origin=Origin>,
|
||||
Extra: SignedExtension<AccountId=u64, Call=Call, DispatchInfo=Info>,
|
||||
Origin: From<Option<u64>>,
|
||||
Info: Clone,
|
||||
{
|
||||
type AccountId = u64;
|
||||
type Call = Call;
|
||||
type DispatchInfo = Info;
|
||||
|
||||
fn sender(&self) -> Option<&Self::AccountId> { self.0.as_ref().map(|x| &x.0) }
|
||||
|
||||
/// Checks to see if this is a valid *transaction*. It returns information on it if so.
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
fn validate<U: ValidateUnsigned<Call=Self::Call>>(
|
||||
&self,
|
||||
_info: Self::DispatchInfo,
|
||||
_len: usize,
|
||||
) -> TransactionValidity {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
/// Executes all necessary logic needed prior to dispatch and deconstructs into function call,
|
||||
/// index and sender.
|
||||
#[allow(deprecated)] // Allow ValidateUnsigned
|
||||
fn apply<U: ValidateUnsigned<Call=Self::Call>>(
|
||||
self,
|
||||
info: Self::DispatchInfo,
|
||||
len: usize,
|
||||
) -> ApplyExtrinsicResult {
|
||||
let maybe_who = if let Some((who, extra)) = self.0 {
|
||||
Extra::pre_dispatch(extra, &who, &self.1, info, len)?;
|
||||
Some(who)
|
||||
} else {
|
||||
Extra::pre_dispatch_unsigned(&self.1, info, len)?;
|
||||
None
|
||||
};
|
||||
|
||||
Ok(self.1.dispatch(maybe_who.into()).map_err(Into::into))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,247 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction validity interface.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use crate::codec::{Encode, Decode};
|
||||
use crate::RuntimeDebug;
|
||||
|
||||
/// Priority for a transaction. Additive. Higher is better.
|
||||
pub type TransactionPriority = u64;
|
||||
|
||||
/// Minimum number of blocks a transaction will remain valid for.
|
||||
/// `TransactionLongevity::max_value()` means "forever".
|
||||
pub type TransactionLongevity = u64;
|
||||
|
||||
/// Tag for a transaction. No two transactions with the same tag should be placed on-chain.
|
||||
pub type TransactionTag = Vec<u8>;
|
||||
|
||||
/// An invalid transaction validity.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(serde::Serialize))]
|
||||
pub enum InvalidTransaction {
|
||||
/// The call of the transaction is not expected.
|
||||
Call,
|
||||
/// General error to do with the inability to pay some fees (e.g. account balance too low).
|
||||
Payment,
|
||||
/// General error to do with the transaction not yet being valid (e.g. nonce too high).
|
||||
Future,
|
||||
/// General error to do with the transaction being outdated (e.g. nonce too low).
|
||||
Stale,
|
||||
/// General error to do with the transaction's proofs (e.g. signature).
|
||||
BadProof,
|
||||
/// The transaction birth block is ancient.
|
||||
AncientBirthBlock,
|
||||
/// The transaction would exhaust the resources of current block.
|
||||
///
|
||||
/// The transaction might be valid, but there are not enough resources left in the current block.
|
||||
ExhaustsResources,
|
||||
/// Any other custom invalid validity that is not covered by this enum.
|
||||
Custom(u8),
|
||||
}
|
||||
|
||||
impl InvalidTransaction {
|
||||
/// Returns if the reason for the invalidity was block resource exhaustion.
|
||||
pub fn exhausted_resources(&self) -> bool {
|
||||
match self {
|
||||
Self::ExhaustsResources => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InvalidTransaction> for &'static str {
|
||||
fn from(invalid: InvalidTransaction) -> &'static str {
|
||||
match invalid {
|
||||
InvalidTransaction::Call => "Transaction call is not expected",
|
||||
InvalidTransaction::Future => "Transaction will be valid in the future",
|
||||
InvalidTransaction::Stale => "Transaction is outdated",
|
||||
InvalidTransaction::BadProof => "Transaction has a bad signature",
|
||||
InvalidTransaction::AncientBirthBlock => "Transaction has an ancient birth block",
|
||||
InvalidTransaction::ExhaustsResources =>
|
||||
"Transaction would exhausts the block limits",
|
||||
InvalidTransaction::Payment =>
|
||||
"Inability to pay some fees (e.g. account balance too low)",
|
||||
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An unknown transaction validity.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(serde::Serialize))]
|
||||
pub enum UnknownTransaction {
|
||||
/// Could not lookup some information that is required to validate the transaction.
|
||||
CannotLookup,
|
||||
/// No validator found for the given unsigned transaction.
|
||||
NoUnsignedValidator,
|
||||
/// Any other custom unknown validity that is not covered by this enum.
|
||||
Custom(u8),
|
||||
}
|
||||
|
||||
impl From<UnknownTransaction> for &'static str {
|
||||
fn from(unknown: UnknownTransaction) -> &'static str {
|
||||
match unknown {
|
||||
UnknownTransaction::CannotLookup =>
|
||||
"Could not lookup information required to validate the transaction",
|
||||
UnknownTransaction::NoUnsignedValidator =>
|
||||
"Could not find an unsigned validator for the unsigned transaction",
|
||||
UnknownTransaction::Custom(_) => "UnknownTransaction custom error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur while checking the validity of a transaction.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(serde::Serialize))]
|
||||
pub enum TransactionValidityError {
|
||||
/// The transaction is invalid.
|
||||
Invalid(InvalidTransaction),
|
||||
/// Transaction validity can't be determined.
|
||||
Unknown(UnknownTransaction),
|
||||
}
|
||||
|
||||
impl TransactionValidityError {
|
||||
/// Returns `true` if the reason for the error was block resource exhaustion.
|
||||
pub fn exhausted_resources(&self) -> bool {
|
||||
match self {
|
||||
Self::Invalid(e) => e.exhausted_resources(),
|
||||
Self::Unknown(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionValidityError> for &'static str {
|
||||
fn from(err: TransactionValidityError) -> &'static str {
|
||||
match err {
|
||||
TransactionValidityError::Invalid(invalid) => invalid.into(),
|
||||
TransactionValidityError::Unknown(unknown) => unknown.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InvalidTransaction> for TransactionValidityError {
|
||||
fn from(err: InvalidTransaction) -> Self {
|
||||
TransactionValidityError::Invalid(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnknownTransaction> for TransactionValidityError {
|
||||
fn from(err: UnknownTransaction) -> Self {
|
||||
TransactionValidityError::Unknown(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information on a transaction's validity and, if valid, on how it relates to other transactions.
|
||||
pub type TransactionValidity = Result<ValidTransaction, TransactionValidityError>;
|
||||
|
||||
impl Into<TransactionValidity> for InvalidTransaction {
|
||||
fn into(self) -> TransactionValidity {
|
||||
Err(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<TransactionValidity> for UnknownTransaction {
|
||||
fn into(self) -> TransactionValidity {
|
||||
Err(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Information concerning a valid transaction.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
|
||||
pub struct ValidTransaction {
|
||||
/// Priority of the transaction.
|
||||
///
|
||||
/// Priority determines the ordering of two transactions that have all
|
||||
/// their dependencies (required tags) satisfied.
|
||||
pub priority: TransactionPriority,
|
||||
/// Transaction dependencies
|
||||
///
|
||||
/// A non-empty list signifies that some other transactions which provide
|
||||
/// given tags are required to be included before that one.
|
||||
pub requires: Vec<TransactionTag>,
|
||||
/// Provided tags
|
||||
///
|
||||
/// A list of tags this transaction provides. Successfully importing the transaction
|
||||
/// will enable other transactions that depend on (require) those tags to be included as well.
|
||||
/// Provided and required tags allow Substrate to build a dependency graph of transactions
|
||||
/// and import them in the right (linear) order.
|
||||
pub provides: Vec<TransactionTag>,
|
||||
/// Transaction longevity
|
||||
///
|
||||
/// Longevity describes minimum number of blocks the validity is correct.
|
||||
/// After this period transaction should be removed from the pool or revalidated.
|
||||
pub longevity: TransactionLongevity,
|
||||
/// A flag indicating if the transaction should be propagated to other peers.
|
||||
///
|
||||
/// By setting `false` here the transaction will still be considered for
|
||||
/// including in blocks that are authored on the current node, but will
|
||||
/// never be sent to other peers.
|
||||
pub propagate: bool,
|
||||
}
|
||||
|
||||
impl Default for ValidTransaction {
|
||||
fn default() -> Self {
|
||||
ValidTransaction {
|
||||
priority: 0,
|
||||
requires: vec![],
|
||||
provides: vec![],
|
||||
longevity: TransactionLongevity::max_value(),
|
||||
propagate: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidTransaction {
|
||||
/// Combine two instances into one, as a best effort. This will take the superset of each of the
|
||||
/// `provides` and `requires` tags, it will sum the priorities, take the minimum longevity and
|
||||
/// the logic *And* of the propagate flags.
|
||||
pub fn combine_with(mut self, mut other: ValidTransaction) -> Self {
|
||||
ValidTransaction {
|
||||
priority: self.priority.saturating_add(other.priority),
|
||||
requires: { self.requires.append(&mut other.requires); self.requires },
|
||||
provides: { self.provides.append(&mut other.provides); self.provides },
|
||||
longevity: self.longevity.min(other.longevity),
|
||||
propagate: self.propagate && other.propagate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode() {
|
||||
let v: TransactionValidity = Ok(ValidTransaction {
|
||||
priority: 5,
|
||||
requires: vec![vec![1, 2, 3, 4]],
|
||||
provides: vec![vec![4, 5, 6]],
|
||||
longevity: 42,
|
||||
propagate: false,
|
||||
});
|
||||
|
||||
let encoded = v.encode();
|
||||
assert_eq!(
|
||||
encoded,
|
||||
vec![0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 16, 1, 2, 3, 4, 4, 12, 4, 5, 6, 42, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
|
||||
// decode back
|
||||
assert_eq!(TransactionValidity::decode(&mut &*encoded), Ok(v));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user