#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
pub use codec;
#[doc(hidden)]
pub use scale_info;
#[cfg(feature = "std")]
#[doc(hidden)]
pub use serde;
#[doc(hidden)]
pub use sp_std;
#[doc(hidden)]
pub use paste;
#[doc(hidden)]
pub use sp_arithmetic::traits::Saturating;
#[doc(hidden)]
pub use sp_application_crypto as app_crypto;
pub use sp_core::storage::StateVersion;
#[cfg(feature = "std")]
pub use sp_core::storage::{Storage, StorageChild};
use sp_core::{
crypto::{self, ByteArray},
ecdsa, ed25519,
hash::{H256, H512},
sr25519,
};
use sp_std::prelude::*;
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
pub mod curve;
pub mod generic;
pub mod legacy;
mod multiaddress;
pub mod offchain;
pub mod runtime_logger;
mod runtime_string;
#[cfg(feature = "std")]
pub mod testing;
pub mod traits;
pub mod transaction_validity;
pub use crate::runtime_string::*;
pub use multiaddress::MultiAddress;
pub use generic::{Digest, DigestItem};
pub use sp_application_crypto::{BoundToRuntimeAppPublic, RuntimeAppPublic};
pub use sp_core::{
bounded::{BoundedBTreeMap, BoundedBTreeSet, BoundedSlice, BoundedVec, WeakBoundedVec},
crypto::{key_types, AccountId32, CryptoType, CryptoTypeId, KeyTypeId},
TypeId,
};
#[cfg(feature = "std")]
pub use sp_core::{bounded_btree_map, bounded_vec};
pub use sp_core::RuntimeDebug;
pub use sp_arithmetic::biguint;
pub use sp_arithmetic::helpers_128bit;
pub use sp_arithmetic::{
traits::SaturatedConversion, ArithmeticError, FixedI128, FixedI64, FixedPointNumber,
FixedPointOperand, FixedU128, InnerOf, PerThing, PerU16, Perbill, Percent, Permill,
Perquintill, Rational128, Rounding, UpperOf,
};
pub use either::Either;
pub const MAX_MODULE_ERROR_ENCODED_SIZE: usize = 4;
pub type Justification = (ConsensusEngineId, EncodedJustification);
pub type EncodedJustification = Vec<u8>;
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct Justifications(Vec<Justification>);
impl Justifications {
pub fn iter(&self) -> impl Iterator<Item = &Justification> {
self.0.iter()
}
pub fn append(&mut self, justification: Justification) -> bool {
if self.get(justification.0).is_some() {
return false
}
self.0.push(justification);
true
}
pub fn get(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification> {
self.iter().find(|j| j.0 == engine_id).map(|j| &j.1)
}
pub fn remove(&mut self, engine_id: ConsensusEngineId) {
self.0.retain(|j| j.0 != engine_id)
}
pub fn into_justification(self, engine_id: ConsensusEngineId) -> Option<EncodedJustification> {
self.into_iter().find(|j| j.0 == engine_id).map(|j| j.1)
}
}
impl IntoIterator for Justifications {
type Item = Justification;
type IntoIter = sp_std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<Justification> for Justifications {
fn from(justification: Justification) -> Self {
Self(vec![justification])
}
}
use traits::{Lazy, Verify};
use crate::traits::IdentifyAccount;
#[cfg(feature = "std")]
pub use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "std")]
pub trait BuildStorage {
fn build_storage(&self) -> Result<sp_core::storage::Storage, String> {
let mut storage = Default::default();
self.assimilate_storage(&mut storage)?;
Ok(storage)
}
fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String>;
}
#[cfg(feature = "std")]
pub trait BuildModuleGenesisStorage<T, I>: Sized {
fn build_module_genesis_storage(
&self,
storage: &mut sp_core::storage::Storage,
) -> Result<(), String>;
}
#[cfg(feature = "std")]
impl BuildStorage for sp_core::storage::Storage {
fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String> {
storage.top.extend(self.top.iter().map(|(k, v)| (k.clone(), v.clone())));
for (k, other_map) in self.children_default.iter() {
let k = k.clone();
if let Some(map) = storage.children_default.get_mut(&k) {
map.data.extend(other_map.data.iter().map(|(k, v)| (k.clone(), v.clone())));
if !map.child_info.try_update(&other_map.child_info) {
return Err("Incompatible child info update".to_string())
}
} else {
storage.children_default.insert(k, other_map.clone());
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl BuildStorage for () {
fn assimilate_storage(&self, _: &mut sp_core::storage::Storage) -> Result<(), String> {
Err("`assimilate_storage` not implemented for `()`".into())
}
}
pub type ConsensusEngineId = [u8; 4];
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Eq, PartialEq, Clone, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub enum MultiSignature {
Ed25519(ed25519::Signature),
Sr25519(sr25519::Signature),
Ecdsa(ecdsa::Signature),
}
impl From<ed25519::Signature> for MultiSignature {
fn from(x: ed25519::Signature) -> Self {
Self::Ed25519(x)
}
}
impl TryFrom<MultiSignature> for ed25519::Signature {
type Error = ();
fn try_from(m: MultiSignature) -> Result<Self, Self::Error> {
if let MultiSignature::Ed25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}
impl From<sr25519::Signature> for MultiSignature {
fn from(x: sr25519::Signature) -> Self {
Self::Sr25519(x)
}
}
impl TryFrom<MultiSignature> for sr25519::Signature {
type Error = ();
fn try_from(m: MultiSignature) -> Result<Self, Self::Error> {
if let MultiSignature::Sr25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}
impl From<ecdsa::Signature> for MultiSignature {
fn from(x: ecdsa::Signature) -> Self {
Self::Ecdsa(x)
}
}
impl TryFrom<MultiSignature> for ecdsa::Signature {
type Error = ();
fn try_from(m: MultiSignature) -> Result<Self, Self::Error> {
if let MultiSignature::Ecdsa(x) = m {
Ok(x)
} else {
Err(())
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum MultiSigner {
Ed25519(ed25519::Public),
Sr25519(sr25519::Public),
Ecdsa(ecdsa::Public),
}
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 {
Self::Ed25519(ref who) => who.as_ref(),
Self::Sr25519(ref who) => who.as_ref(),
Self::Ecdsa(ref who) => who.as_ref(),
}
}
}
impl traits::IdentifyAccount for MultiSigner {
type AccountId = AccountId32;
fn into_account(self) -> AccountId32 {
match self {
Self::Ed25519(who) => <[u8; 32]>::from(who).into(),
Self::Sr25519(who) => <[u8; 32]>::from(who).into(),
Self::Ecdsa(who) => sp_io::hashing::blake2_256(who.as_ref()).into(),
}
}
}
impl From<ed25519::Public> for MultiSigner {
fn from(x: ed25519::Public) -> Self {
Self::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 {
Self::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 {
Self::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 {
Self::Ed25519(ref who) => write!(fmt, "ed25519: {}", who),
Self::Sr25519(ref who) => write!(fmt, "sr25519: {}", who),
Self::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 {
match (self, signer) {
(Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) {
Ok(signer) => sig.verify(msg, &signer),
Err(()) => false,
},
(Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) {
Ok(signer) => sig.verify(msg, &signer),
Err(()) => false,
},
(Self::Ecdsa(ref sig), who) => {
let m = sp_io::hashing::blake2_256(msg.get());
match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
Ok(pubkey) =>
&sp_io::hashing::blake2_256(pubkey.as_ref()) ==
<dyn AsRef<[u8; 32]>>::as_ref(who),
_ => false,
}
},
}
}
}
#[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 {
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| match ed25519::Public::from_slice(signer.as_ref()) {
Err(()) => false,
Ok(signer) => s.verify(msg, &signer),
})
.unwrap_or(false)
}
}
impl From<sr25519::Signature> for AnySignature {
fn from(s: sr25519::Signature) -> Self {
Self(s.into())
}
}
impl From<ed25519::Signature> for AnySignature {
fn from(s: ed25519::Signature) -> Self {
Self(s.into())
}
}
impl From<DispatchError> for DispatchOutcome {
fn from(err: DispatchError) -> Self {
Err(err)
}
}
pub type DispatchResult = sp_std::result::Result<(), DispatchError>;
pub type DispatchResultWithInfo<T> = sp_std::result::Result<T, DispatchErrorWithPostInfo<T>>;
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ModuleError {
pub index: u8,
pub error: [u8; MAX_MODULE_ERROR_ENCODED_SIZE],
#[codec(skip)]
#[cfg_attr(feature = "std", serde(skip_deserializing))]
pub message: Option<&'static str>,
}
impl PartialEq for ModuleError {
fn eq(&self, other: &Self) -> bool {
(self.index == other.index) && (self.error == other.error)
}
}
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum TransactionalError {
LimitReached,
NoLayer,
}
impl From<TransactionalError> for &'static str {
fn from(e: TransactionalError) -> &'static str {
match e {
TransactionalError::LimitReached => "Too many transactional layers have been spawned",
TransactionalError::NoLayer => "A transactional layer was expected, but does not exist",
}
}
}
impl From<TransactionalError> for DispatchError {
fn from(e: TransactionalError) -> DispatchError {
Self::Transactional(e)
}
}
#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum DispatchError {
Other(
#[codec(skip)]
#[cfg_attr(feature = "std", serde(skip_deserializing))]
&'static str,
),
CannotLookup,
BadOrigin,
Module(ModuleError),
ConsumerRemaining,
NoProviders,
TooManyConsumers,
Token(TokenError),
Arithmetic(ArithmeticError),
Transactional(TransactionalError),
Exhausted,
Corruption,
Unavailable,
}
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct DispatchErrorWithPostInfo<Info>
where
Info: Eq + PartialEq + Clone + Copy + Encode + Decode + traits::Printable,
{
pub post_info: Info,
pub error: DispatchError,
}
impl DispatchError {
pub fn stripped(self) -> Self {
match self {
DispatchError::Module(ModuleError { index, error, message: Some(_) }) =>
DispatchError::Module(ModuleError { index, error, message: None }),
m => m,
}
}
}
impl<T, E> From<E> for DispatchErrorWithPostInfo<T>
where
T: Eq + PartialEq + Clone + Copy + Encode + Decode + traits::Printable + Default,
E: Into<DispatchError>,
{
fn from(error: E) -> Self {
Self { post_info: Default::default(), error: error.into() }
}
}
impl From<crate::traits::LookupError> for DispatchError {
fn from(_: crate::traits::LookupError) -> Self {
Self::CannotLookup
}
}
impl From<crate::traits::BadOrigin> for DispatchError {
fn from(_: crate::traits::BadOrigin) -> Self {
Self::BadOrigin
}
}
#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum TokenError {
NoFunds,
WouldDie,
BelowMinimum,
CannotCreate,
UnknownAsset,
Frozen,
Unsupported,
}
impl From<TokenError> for &'static str {
fn from(e: TokenError) -> &'static str {
match e {
TokenError::NoFunds => "Funds are unavailable",
TokenError::WouldDie => "Account that must exist would die",
TokenError::BelowMinimum => "Account cannot exist with the funds that would be given",
TokenError::CannotCreate => "Account cannot be created",
TokenError::UnknownAsset => "The asset in question is unknown",
TokenError::Frozen => "Funds exist but are frozen",
TokenError::Unsupported => "Operation is not supported by the asset",
}
}
}
impl From<TokenError> for DispatchError {
fn from(e: TokenError) -> DispatchError {
Self::Token(e)
}
}
impl From<ArithmeticError> for DispatchError {
fn from(e: ArithmeticError) -> DispatchError {
Self::Arithmetic(e)
}
}
impl From<&'static str> for DispatchError {
fn from(err: &'static str) -> DispatchError {
Self::Other(err)
}
}
impl From<DispatchError> for &'static str {
fn from(err: DispatchError) -> &'static str {
use DispatchError::*;
match err {
Other(msg) => msg,
CannotLookup => "Cannot lookup",
BadOrigin => "Bad origin",
Module(ModuleError { message, .. }) => message.unwrap_or("Unknown module error"),
ConsumerRemaining => "Consumer remaining",
NoProviders => "No providers",
TooManyConsumers => "Too many consumers",
Token(e) => e.into(),
Arithmetic(e) => e.into(),
Transactional(e) => e.into(),
Exhausted => "Resources exhausted",
Corruption => "State corrupt",
Unavailable => "Resource unavailable",
}
}
}
impl<T> From<DispatchErrorWithPostInfo<T>> for &'static str
where
T: Eq + PartialEq + Clone + Copy + Encode + Decode + traits::Printable,
{
fn from(err: DispatchErrorWithPostInfo<T>) -> &'static str {
err.error.into()
}
}
impl traits::Printable for DispatchError {
fn print(&self) {
use DispatchError::*;
"DispatchError".print();
match self {
Other(err) => err.print(),
CannotLookup => "Cannot lookup".print(),
BadOrigin => "Bad origin".print(),
Module(ModuleError { index, error, message }) => {
index.print();
error.print();
if let Some(msg) = message {
msg.print();
}
},
ConsumerRemaining => "Consumer remaining".print(),
NoProviders => "No providers".print(),
TooManyConsumers => "Too many consumers".print(),
Token(e) => {
"Token error: ".print();
<&'static str>::from(*e).print();
},
Arithmetic(e) => {
"Arithmetic error: ".print();
<&'static str>::from(*e).print();
},
Transactional(e) => {
"Transactional error: ".print();
<&'static str>::from(*e).print();
},
Exhausted => "Resources exhausted".print(),
Corruption => "State corrupt".print(),
Unavailable => "Resource unavailable".print(),
}
}
}
impl<T> traits::Printable for DispatchErrorWithPostInfo<T>
where
T: Eq + PartialEq + Clone + Copy + Encode + Decode + traits::Printable,
{
fn print(&self) {
self.error.print();
"PostInfo: ".print();
self.post_info.print();
}
}
pub type DispatchOutcome = Result<(), DispatchError>;
pub type ApplyExtrinsicResult =
Result<DispatchOutcome, transaction_validity::TransactionValidityError>;
pub type ApplyExtrinsicResultWithInfo<T> =
Result<DispatchResultWithInfo<T>, transaction_validity::TransactionValidityError>;
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(
sig: &V,
item: &T,
signer: &<V::Signer as IdentifyAccount>::AccountId,
) -> bool {
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)
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_eq_error_rate {
($x:expr, $y:expr, $error:expr $(,)?) => {
assert!(
($x >= $crate::Saturating::saturating_sub($y, $error)) &&
($x <= $crate::Saturating::saturating_add($y, $error)),
"{:?} != {:?} (with error rate {:?})",
$x,
$y,
$error,
);
};
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_eq_error_rate_float {
($x:expr, $y:expr, $error:expr $(,)?) => {
assert!(
($x >= $y - $error) && ($x <= $y + $error),
"{:?} != {:?} (with error rate {:?})",
$x,
$y,
$error,
);
};
}
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, TypeInfo)]
pub struct OpaqueExtrinsic(Vec<u8>);
impl OpaqueExtrinsic {
pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, codec::Error> {
Self::decode(&mut bytes)
}
}
impl sp_std::fmt::Debug for OpaqueExtrinsic {
#[cfg(feature = "std")]
fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
write!(fmt, "{}", sp_core::hexdisplay::HexDisplay::from(&self.0))
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _fmt: &mut sp_std::fmt::Formatter) -> sp_std::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| ::sp_core::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 = ::sp_core::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 = ();
}
pub fn print(print: impl traits::Printable) {
print.print();
}
#[must_use = "`verify()` needs to be called to finish batch signature verification!"]
pub struct SignatureBatching(bool);
impl SignatureBatching {
pub fn start() -> Self {
sp_io::crypto::start_batch_verify();
SignatureBatching(false)
}
#[must_use]
pub fn verify(mut self) -> bool {
self.0 = true;
sp_io::crypto::finish_batch_verify()
}
}
impl Drop for SignatureBatching {
fn drop(&mut self) {
if !self.0 && !sp_std::thread::panicking() {
panic!("Signature verification has not been called before `SignatureBatching::drop`")
}
}
}
pub enum TransactionOutcome<R> {
Commit(R),
Rollback(R),
}
impl<R> TransactionOutcome<R> {
pub fn into_inner(self) -> R {
match self {
Self::Commit(r) => r,
Self::Rollback(r) => r,
}
}
}
#[cfg(test)]
mod tests {
use crate::traits::BlakeTwo256;
use super::*;
use codec::{Decode, Encode};
use sp_core::crypto::{Pair, UncheckedFrom};
use sp_io::TestExternalities;
use sp_state_machine::create_proof_check_backend;
#[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(ModuleError {
index: 1,
error: [2, 0, 0, 0],
message: Some("error message"),
});
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![3, 1, 2, 0, 0, 0]);
assert_eq!(
decoded,
DispatchError::Module(ModuleError { index: 1, error: [2, 0, 0, 0], message: None })
);
}
#[test]
fn dispatch_error_equality() {
use DispatchError::*;
let variants = vec![
Other("foo"),
Other("bar"),
CannotLookup,
BadOrigin,
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: None }),
Module(ModuleError { index: 1, error: [2, 0, 0, 0], message: None }),
Module(ModuleError { index: 2, error: [1, 0, 0, 0], message: None }),
ConsumerRemaining,
NoProviders,
Token(TokenError::NoFunds),
Token(TokenError::WouldDie),
Token(TokenError::BelowMinimum),
Token(TokenError::CannotCreate),
Token(TokenError::UnknownAsset),
Token(TokenError::Frozen),
Arithmetic(ArithmeticError::Overflow),
Arithmetic(ArithmeticError::Underflow),
Arithmetic(ArithmeticError::DivisionByZero),
];
for (i, variant) in variants.iter().enumerate() {
for (j, other_variant) in variants.iter().enumerate() {
if i == j {
assert_eq!(variant, other_variant);
} else {
assert_ne!(variant, other_variant);
}
}
}
assert_eq!(
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: Some("foo") }),
Module(ModuleError { index: 1, error: [1, 0, 0, 0], message: None }),
);
}
#[test]
fn multi_signature_ecdsa_verify_works() {
let msg = &b"test-message"[..];
let (pair, _) = ecdsa::Pair::generate();
let signature = pair.sign(&msg);
assert!(ecdsa::Pair::verify(&signature, msg, &pair.public()));
let multi_sig = MultiSignature::from(signature);
let multi_signer = MultiSigner::from(pair.public());
assert!(multi_sig.verify(msg, &multi_signer.into_account()));
let multi_signer = MultiSigner::from(pair.public());
assert!(multi_sig.verify(msg, &multi_signer.into_account()));
}
#[test]
#[should_panic(expected = "Signature verification has not been called")]
fn batching_still_finishes_when_not_called_directly() {
let mut ext = sp_state_machine::BasicExternalities::default();
ext.register_extension(sp_core::traits::TaskExecutorExt::new(
sp_core::testing::TaskExecutor::new(),
));
ext.execute_with(|| {
let _batching = SignatureBatching::start();
let dummy = UncheckedFrom::unchecked_from([1; 32]);
let dummy_sig = UncheckedFrom::unchecked_from([1; 64]);
sp_io::crypto::sr25519_verify(&dummy_sig, &Vec::new(), &dummy);
});
}
#[test]
#[should_panic(expected = "Hey, I'm an error")]
fn batching_does_not_panic_while_thread_is_already_panicking() {
let mut ext = sp_state_machine::BasicExternalities::default();
ext.register_extension(sp_core::traits::TaskExecutorExt::new(
sp_core::testing::TaskExecutor::new(),
));
ext.execute_with(|| {
let _batching = SignatureBatching::start();
panic!("Hey, I'm an error");
});
}
#[test]
fn execute_and_generate_proof_works() {
use codec::Encode;
use sp_state_machine::Backend;
let mut ext = TestExternalities::default();
ext.insert(b"a".to_vec(), vec![1u8; 33]);
ext.insert(b"b".to_vec(), vec![2u8; 33]);
ext.insert(b"c".to_vec(), vec![3u8; 33]);
ext.insert(b"d".to_vec(), vec![4u8; 33]);
let pre_root = *ext.backend.root();
let (_, proof) = ext.execute_and_prove(|| {
sp_io::storage::get(b"a");
sp_io::storage::get(b"b");
sp_io::storage::get(b"v");
sp_io::storage::get(b"d");
});
let compact_proof = proof.clone().into_compact_proof::<BlakeTwo256>(pre_root).unwrap();
let compressed_proof = zstd::stream::encode_all(&compact_proof.encode()[..], 0).unwrap();
println!("proof size: {:?}", proof.encoded_size());
println!("compact proof size: {:?}", compact_proof.encoded_size());
println!("zstd-compressed compact proof size: {:?}", &compressed_proof.len());
let proof_check = create_proof_check_backend::<BlakeTwo256>(pre_root, proof).unwrap();
assert_eq!(proof_check.storage(b"a",).unwrap().unwrap(), vec![1u8; 33]);
let _ = ext.execute_and_prove(|| {
sp_io::storage::set(b"a", &vec![1u8; 44]);
});
ext.execute_with(|| {
assert_eq!(sp_io::storage::get(b"a").unwrap(), vec![1u8; 44]);
assert_eq!(sp_io::storage::get(b"b").unwrap(), vec![2u8; 33]);
});
}
}