#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(enable_alloc_error_handler, feature(alloc_error_handler))]
extern crate alloc;
use alloc::vec::Vec;
#[cfg(not(bizinikiwi_runtime))]
use tracing;
#[cfg(not(bizinikiwi_runtime))]
use pezsp_core::{
crypto::Pair,
hexdisplay::HexDisplay,
offchain::{OffchainDbExt, OffchainWorkerExt, TransactionPoolExt},
storage::ChildInfo,
};
#[cfg(not(bizinikiwi_runtime))]
use pezsp_keystore::KeystoreExt;
#[cfg(feature = "bandersnatch-experimental")]
use pezsp_core::bandersnatch;
use pezsp_core::{
crypto::KeyTypeId,
ecdsa, ed25519,
offchain::{
HttpError, HttpRequestId, HttpRequestStatus, OpaqueNetworkState, StorageKind, Timestamp,
},
sr25519,
storage::StateVersion,
LogLevelFilter, OpaquePeerId, RuntimeInterfaceLogLevel, H256,
};
#[cfg(feature = "bls-experimental")]
use pezsp_core::{bls381, ecdsa_bls381};
#[cfg(not(bizinikiwi_runtime))]
use pezsp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
use pezsp_runtime_interface::{
pass_by::{
AllocateAndReturnByCodec, AllocateAndReturnFatPointer, AllocateAndReturnPointer, PassAs,
PassFatPointerAndDecode, PassFatPointerAndDecodeSlice, PassFatPointerAndRead,
PassFatPointerAndReadWrite, PassPointerAndRead, PassPointerAndReadCopy, ReturnAs,
},
runtime_interface, Pointer,
};
use codec::{Decode, Encode};
#[cfg(not(bizinikiwi_runtime))]
use secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message,
};
#[cfg(not(bizinikiwi_runtime))]
use pezsp_externalities::{Externalities, ExternalitiesExt};
pub use pezsp_externalities::MultiRemovalResults;
#[cfg(all(not(feature = "disable_allocator"), bizinikiwi_runtime, target_family = "wasm"))]
mod global_alloc_wasm;
#[cfg(all(
not(feature = "disable_allocator"),
bizinikiwi_runtime,
any(target_arch = "riscv32", target_arch = "riscv64")
))]
mod global_alloc_riscv;
#[cfg(not(bizinikiwi_runtime))]
const LOG_TARGET: &str = "runtime::io";
#[derive(Encode, Decode)]
pub enum EcdsaVerifyError {
BadRS,
BadV,
BadSignature,
}
#[derive(Encode, Decode)]
pub enum KillStorageResult {
AllRemoved(u32),
SomeRemaining(u32),
}
impl From<MultiRemovalResults> for KillStorageResult {
fn from(r: MultiRemovalResults) -> Self {
match r.maybe_cursor {
None => Self::AllRemoved(r.loops),
Some(..) => Self::SomeRemaining(r.loops),
}
}
}
#[runtime_interface]
pub trait Storage {
fn get(
&mut self,
key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<bytes::Bytes>> {
self.storage(key).map(|s| bytes::Bytes::from(s.to_vec()))
}
fn read(
&mut self,
key: PassFatPointerAndRead<&[u8]>,
value_out: PassFatPointerAndReadWrite<&mut [u8]>,
value_offset: u32,
) -> AllocateAndReturnByCodec<Option<u32>> {
self.storage(key).map(|value| {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = core::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
data.len() as u32
})
}
fn set(&mut self, key: PassFatPointerAndRead<&[u8]>, value: PassFatPointerAndRead<&[u8]>) {
self.set_storage(key.to_vec(), value.to_vec());
}
fn clear(&mut self, key: PassFatPointerAndRead<&[u8]>) {
self.clear_storage(key)
}
fn exists(&mut self, key: PassFatPointerAndRead<&[u8]>) -> bool {
self.exists_storage(key)
}
fn clear_prefix(&mut self, prefix: PassFatPointerAndRead<&[u8]>) {
let _ = Externalities::clear_prefix(*self, prefix, None, None);
}
#[version(2)]
fn clear_prefix(
&mut self,
prefix: PassFatPointerAndRead<&[u8]>,
limit: PassFatPointerAndDecode<Option<u32>>,
) -> AllocateAndReturnByCodec<KillStorageResult> {
Externalities::clear_prefix(*self, prefix, limit, None).into()
}
#[version(3, register_only)]
fn clear_prefix(
&mut self,
maybe_prefix: PassFatPointerAndRead<&[u8]>,
maybe_limit: PassFatPointerAndDecode<Option<u32>>,
maybe_cursor: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnByCodec<MultiRemovalResults> {
Externalities::clear_prefix(
*self,
maybe_prefix,
maybe_limit,
maybe_cursor.as_ref().map(|x| &x[..]),
)
.into()
}
fn append(&mut self, key: PassFatPointerAndRead<&[u8]>, value: PassFatPointerAndRead<Vec<u8>>) {
self.storage_append(key.to_vec(), value);
}
fn root(&mut self) -> AllocateAndReturnFatPointer<Vec<u8>> {
self.storage_root(StateVersion::V0)
}
#[version(2)]
fn root(&mut self, version: PassAs<StateVersion, u8>) -> AllocateAndReturnFatPointer<Vec<u8>> {
self.storage_root(version)
}
fn changes_root(
&mut self,
_parent_hash: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
None
}
fn next_key(
&mut self,
key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
self.next_storage_key(key)
}
fn start_transaction(&mut self) {
self.storage_start_transaction();
}
fn rollback_transaction(&mut self) {
self.storage_rollback_transaction()
.expect("No open transaction that can be rolled back.");
}
fn commit_transaction(&mut self) {
self.storage_commit_transaction()
.expect("No open transaction that can be committed.");
}
}
#[runtime_interface]
pub trait DefaultChildStorage {
fn get(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage(&child_info, key).map(|s| s.to_vec())
}
fn read(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
value_out: PassFatPointerAndReadWrite<&mut [u8]>,
value_offset: u32,
) -> AllocateAndReturnByCodec<Option<u32>> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage(&child_info, key).map(|value| {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = core::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
data.len() as u32
})
}
fn set(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
) {
let child_info = ChildInfo::new_default(storage_key);
self.set_child_storage(&child_info, key.to_vec(), value.to_vec());
}
fn clear(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
) {
let child_info = ChildInfo::new_default(storage_key);
self.clear_child_storage(&child_info, key);
}
fn storage_kill(&mut self, storage_key: PassFatPointerAndRead<&[u8]>) {
let child_info = ChildInfo::new_default(storage_key);
let _ = self.kill_child_storage(&child_info, None, None);
}
#[version(2)]
fn storage_kill(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
limit: PassFatPointerAndDecode<Option<u32>>,
) -> bool {
let child_info = ChildInfo::new_default(storage_key);
let r = self.kill_child_storage(&child_info, limit, None);
r.maybe_cursor.is_none()
}
#[version(3)]
fn storage_kill(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
limit: PassFatPointerAndDecode<Option<u32>>,
) -> AllocateAndReturnByCodec<KillStorageResult> {
let child_info = ChildInfo::new_default(storage_key);
self.kill_child_storage(&child_info, limit, None).into()
}
#[version(4, register_only)]
fn storage_kill(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
maybe_limit: PassFatPointerAndDecode<Option<u32>>,
maybe_cursor: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnByCodec<MultiRemovalResults> {
let child_info = ChildInfo::new_default(storage_key);
self.kill_child_storage(&child_info, maybe_limit, maybe_cursor.as_ref().map(|x| &x[..]))
.into()
}
fn exists(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
) -> bool {
let child_info = ChildInfo::new_default(storage_key);
self.exists_child_storage(&child_info, key)
}
fn clear_prefix(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
prefix: PassFatPointerAndRead<&[u8]>,
) {
let child_info = ChildInfo::new_default(storage_key);
let _ = self.clear_child_prefix(&child_info, prefix, None, None);
}
#[version(2)]
fn clear_prefix(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
prefix: PassFatPointerAndRead<&[u8]>,
limit: PassFatPointerAndDecode<Option<u32>>,
) -> AllocateAndReturnByCodec<KillStorageResult> {
let child_info = ChildInfo::new_default(storage_key);
self.clear_child_prefix(&child_info, prefix, limit, None).into()
}
#[version(3, register_only)]
fn clear_prefix(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
prefix: PassFatPointerAndRead<&[u8]>,
maybe_limit: PassFatPointerAndDecode<Option<u32>>,
maybe_cursor: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnByCodec<MultiRemovalResults> {
let child_info = ChildInfo::new_default(storage_key);
self.clear_child_prefix(
&child_info,
prefix,
maybe_limit,
maybe_cursor.as_ref().map(|x| &x[..]),
)
.into()
}
fn root(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnFatPointer<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage_root(&child_info, StateVersion::V0)
}
#[version(2)]
fn root(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
version: PassAs<StateVersion, u8>,
) -> AllocateAndReturnFatPointer<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage_root(&child_info, version)
}
fn next_key(
&mut self,
storage_key: PassFatPointerAndRead<&[u8]>,
key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
let child_info = ChildInfo::new_default(storage_key);
self.next_child_storage_key(&child_info, key)
}
}
#[runtime_interface]
pub trait Trie {
fn blake2_256_root(
input: PassFatPointerAndDecode<Vec<(Vec<u8>, Vec<u8>)>>,
) -> AllocateAndReturnPointer<H256, 32> {
LayoutV0::<pezsp_core::Blake2Hasher>::trie_root(input)
}
#[version(2)]
fn blake2_256_root(
input: PassFatPointerAndDecode<Vec<(Vec<u8>, Vec<u8>)>>,
version: PassAs<StateVersion, u8>,
) -> AllocateAndReturnPointer<H256, 32> {
match version {
StateVersion::V0 => LayoutV0::<pezsp_core::Blake2Hasher>::trie_root(input),
StateVersion::V1 => LayoutV1::<pezsp_core::Blake2Hasher>::trie_root(input),
}
}
fn blake2_256_ordered_root(
input: PassFatPointerAndDecode<Vec<Vec<u8>>>,
) -> AllocateAndReturnPointer<H256, 32> {
LayoutV0::<pezsp_core::Blake2Hasher>::ordered_trie_root(input)
}
#[version(2)]
fn blake2_256_ordered_root(
input: PassFatPointerAndDecode<Vec<Vec<u8>>>,
version: PassAs<StateVersion, u8>,
) -> AllocateAndReturnPointer<H256, 32> {
match version {
StateVersion::V0 => LayoutV0::<pezsp_core::Blake2Hasher>::ordered_trie_root(input),
StateVersion::V1 => LayoutV1::<pezsp_core::Blake2Hasher>::ordered_trie_root(input),
}
}
fn keccak_256_root(
input: PassFatPointerAndDecode<Vec<(Vec<u8>, Vec<u8>)>>,
) -> AllocateAndReturnPointer<H256, 32> {
LayoutV0::<pezsp_core::KeccakHasher>::trie_root(input)
}
#[version(2)]
fn keccak_256_root(
input: PassFatPointerAndDecode<Vec<(Vec<u8>, Vec<u8>)>>,
version: PassAs<StateVersion, u8>,
) -> AllocateAndReturnPointer<H256, 32> {
match version {
StateVersion::V0 => LayoutV0::<pezsp_core::KeccakHasher>::trie_root(input),
StateVersion::V1 => LayoutV1::<pezsp_core::KeccakHasher>::trie_root(input),
}
}
fn keccak_256_ordered_root(
input: PassFatPointerAndDecode<Vec<Vec<u8>>>,
) -> AllocateAndReturnPointer<H256, 32> {
LayoutV0::<pezsp_core::KeccakHasher>::ordered_trie_root(input)
}
#[version(2)]
fn keccak_256_ordered_root(
input: PassFatPointerAndDecode<Vec<Vec<u8>>>,
version: PassAs<StateVersion, u8>,
) -> AllocateAndReturnPointer<H256, 32> {
match version {
StateVersion::V0 => LayoutV0::<pezsp_core::KeccakHasher>::ordered_trie_root(input),
StateVersion::V1 => LayoutV1::<pezsp_core::KeccakHasher>::ordered_trie_root(input),
}
}
fn blake2_256_verify_proof(
root: PassPointerAndReadCopy<H256, 32>,
proof: PassFatPointerAndDecodeSlice<&[Vec<u8>]>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
) -> bool {
pezsp_trie::verify_trie_proof::<LayoutV0<pezsp_core::Blake2Hasher>, _, _, _>(
&root,
proof,
&[(key, Some(value))],
)
.is_ok()
}
#[version(2)]
fn blake2_256_verify_proof(
root: PassPointerAndReadCopy<H256, 32>,
proof: PassFatPointerAndDecodeSlice<&[Vec<u8>]>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
version: PassAs<StateVersion, u8>,
) -> bool {
match version {
StateVersion::V0 => pezsp_trie::verify_trie_proof::<
LayoutV0<pezsp_core::Blake2Hasher>,
_,
_,
_,
>(&root, proof, &[(key, Some(value))])
.is_ok(),
StateVersion::V1 => pezsp_trie::verify_trie_proof::<
LayoutV1<pezsp_core::Blake2Hasher>,
_,
_,
_,
>(&root, proof, &[(key, Some(value))])
.is_ok(),
}
}
fn keccak_256_verify_proof(
root: PassPointerAndReadCopy<H256, 32>,
proof: PassFatPointerAndDecodeSlice<&[Vec<u8>]>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
) -> bool {
pezsp_trie::verify_trie_proof::<LayoutV0<pezsp_core::KeccakHasher>, _, _, _>(
&root,
proof,
&[(key, Some(value))],
)
.is_ok()
}
#[version(2)]
fn keccak_256_verify_proof(
root: PassPointerAndReadCopy<H256, 32>,
proof: PassFatPointerAndDecodeSlice<&[Vec<u8>]>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
version: PassAs<StateVersion, u8>,
) -> bool {
match version {
StateVersion::V0 => pezsp_trie::verify_trie_proof::<
LayoutV0<pezsp_core::KeccakHasher>,
_,
_,
_,
>(&root, proof, &[(key, Some(value))])
.is_ok(),
StateVersion::V1 => pezsp_trie::verify_trie_proof::<
LayoutV1<pezsp_core::KeccakHasher>,
_,
_,
_,
>(&root, proof, &[(key, Some(value))])
.is_ok(),
}
}
}
#[runtime_interface]
pub trait Misc {
fn print_num(val: u64) {
log::debug!(target: "runtime", "{}", val);
}
fn print_utf8(utf8: PassFatPointerAndRead<&[u8]>) {
if let Ok(data) = core::str::from_utf8(utf8) {
log::debug!(target: "runtime", "{}", data)
}
}
fn print_hex(data: PassFatPointerAndRead<&[u8]>) {
log::debug!(target: "runtime", "{}", HexDisplay::from(&data));
}
fn runtime_version(
&mut self,
wasm: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
use pezsp_core::traits::ReadRuntimeVersionExt;
let mut ext = pezsp_state_machine::BasicExternalities::default();
match self
.extension::<ReadRuntimeVersionExt>()
.expect("No `ReadRuntimeVersionExt` associated for the current context!")
.read_runtime_version(wasm, &mut ext)
{
Ok(v) => Some(v),
Err(err) => {
log::debug!(
target: LOG_TARGET,
"cannot read version from the given runtime: {}",
err,
);
None
},
}
}
}
#[cfg(not(bizinikiwi_runtime))]
pezsp_externalities::decl_extension! {
pub struct UseDalekExt;
}
#[cfg(not(bizinikiwi_runtime))]
impl Default for UseDalekExt {
fn default() -> Self {
Self
}
}
#[runtime_interface]
pub trait Crypto {
fn ed25519_public_keys(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
) -> AllocateAndReturnByCodec<Vec<ed25519::Public>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ed25519_public_keys(id)
}
fn ed25519_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<ed25519::Public, 32> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ed25519_generate_new(id, seed)
.expect("`ed25519_generate` failed")
}
fn ed25519_sign(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&ed25519::Public, 32>,
msg: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<ed25519::Signature>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ed25519_sign(id, pub_key, msg)
.ok()
.flatten()
}
fn ed25519_verify(
sig: PassPointerAndRead<&ed25519::Signature, 64>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&ed25519::Public, 32>,
) -> bool {
if pezsp_externalities::with_externalities(|mut e| e.extension::<UseDalekExt>().is_some())
.unwrap_or_default()
{
use ed25519_dalek::Verifier;
let Ok(public_key) = ed25519_dalek::VerifyingKey::from_bytes(&pub_key.0) else {
return false;
};
let sig = ed25519_dalek::Signature::from_bytes(&sig.0);
public_key.verify(msg, &sig).is_ok()
} else {
ed25519::Pair::verify(sig, msg, pub_key)
}
}
#[version(1, register_only)]
fn ed25519_batch_verify(
&mut self,
sig: PassPointerAndRead<&ed25519::Signature, 64>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&ed25519::Public, 32>,
) -> bool {
let res = ed25519_verify(sig, msg, pub_key);
if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
ext.0 &= res;
}
res
}
#[version(2)]
fn sr25519_verify(
sig: PassPointerAndRead<&sr25519::Signature, 64>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&sr25519::Public, 32>,
) -> bool {
sr25519::Pair::verify(sig, msg, pub_key)
}
#[version(1, register_only)]
fn sr25519_batch_verify(
&mut self,
sig: PassPointerAndRead<&sr25519::Signature, 64>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&sr25519::Public, 32>,
) -> bool {
let res = sr25519_verify(sig, msg, pub_key);
if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
ext.0 &= res;
}
res
}
#[version(1, register_only)]
fn start_batch_verify(&mut self) {
self.register_extension(VerificationExtDeprecated(true))
.expect("Failed to register required extension: `VerificationExt`");
}
#[version(1, register_only)]
fn finish_batch_verify(&mut self) -> bool {
let result = self
.extension::<VerificationExtDeprecated>()
.expect("`finish_batch_verify` should only be called after `start_batch_verify`")
.0;
self.deregister_extension::<VerificationExtDeprecated>()
.expect("No verification extension in current context!");
result
}
fn sr25519_public_keys(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
) -> AllocateAndReturnByCodec<Vec<sr25519::Public>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.sr25519_public_keys(id)
}
fn sr25519_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<sr25519::Public, 32> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.sr25519_generate_new(id, seed)
.expect("`sr25519_generate` failed")
}
fn sr25519_sign(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&sr25519::Public, 32>,
msg: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<sr25519::Signature>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.sr25519_sign(id, pub_key, msg)
.ok()
.flatten()
}
fn sr25519_verify(
sig: PassPointerAndRead<&sr25519::Signature, 64>,
msg: PassFatPointerAndRead<&[u8]>,
pubkey: PassPointerAndRead<&sr25519::Public, 32>,
) -> bool {
sr25519::Pair::verify_deprecated(sig, msg, pubkey)
}
fn ecdsa_public_keys(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
) -> AllocateAndReturnByCodec<Vec<ecdsa::Public>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_public_keys(id)
}
fn ecdsa_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<ecdsa::Public, 33> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_generate_new(id, seed)
.expect("`ecdsa_generate` failed")
}
fn ecdsa_sign(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
msg: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<ecdsa::Signature>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_sign(id, pub_key, msg)
.ok()
.flatten()
}
fn ecdsa_sign_prehashed(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
msg: PassPointerAndRead<&[u8; 32], 32>,
) -> AllocateAndReturnByCodec<Option<ecdsa::Signature>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_sign_prehashed(id, pub_key, msg)
.ok()
.flatten()
}
fn ecdsa_verify(
sig: PassPointerAndRead<&ecdsa::Signature, 65>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
) -> bool {
#[allow(deprecated)]
ecdsa::Pair::verify_deprecated(sig, msg, pub_key)
}
#[version(2)]
fn ecdsa_verify(
sig: PassPointerAndRead<&ecdsa::Signature, 65>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
) -> bool {
ecdsa::Pair::verify(sig, msg, pub_key)
}
fn ecdsa_verify_prehashed(
sig: PassPointerAndRead<&ecdsa::Signature, 65>,
msg: PassPointerAndRead<&[u8; 32], 32>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
) -> bool {
ecdsa::Pair::verify_prehashed(sig, msg, pub_key)
}
#[version(1, register_only)]
fn ecdsa_batch_verify(
&mut self,
sig: PassPointerAndRead<&ecdsa::Signature, 65>,
msg: PassFatPointerAndRead<&[u8]>,
pub_key: PassPointerAndRead<&ecdsa::Public, 33>,
) -> bool {
let res = ecdsa_verify(sig, msg, pub_key);
if let Some(ext) = self.extension::<VerificationExtDeprecated>() {
ext.0 &= res;
}
res
}
fn secp256k1_ecdsa_recover(
sig: PassPointerAndRead<&[u8; 65], 65>,
msg: PassPointerAndRead<&[u8; 32], 32>,
) -> AllocateAndReturnByCodec<Result<[u8; 64], EcdsaVerifyError>> {
let rid = libsecp256k1::RecoveryId::parse(
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[..64])
.map_err(|_| EcdsaVerifyError::BadRS)?;
let msg = libsecp256k1::Message::parse(msg);
let pubkey =
libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
let mut res = [0u8; 64];
res.copy_from_slice(&pubkey.serialize()[1..65]);
Ok(res)
}
#[version(2)]
fn secp256k1_ecdsa_recover(
sig: PassPointerAndRead<&[u8; 65], 65>,
msg: PassPointerAndRead<&[u8; 32], 32>,
) -> AllocateAndReturnByCodec<Result<[u8; 64], EcdsaVerifyError>> {
let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
.map_err(|_| EcdsaVerifyError::BadV)?;
let sig = RecoverableSignature::from_compact(&sig[..64], rid)
.map_err(|_| EcdsaVerifyError::BadRS)?;
let msg = Message::from_digest_slice(msg).expect("Message is 32 bytes; qed");
#[cfg(feature = "std")]
let ctx = secp256k1::SECP256K1;
#[cfg(not(feature = "std"))]
let ctx = secp256k1::Secp256k1::<secp256k1::VerifyOnly>::gen_new();
let pubkey = ctx.recover_ecdsa(&msg, &sig).map_err(|_| EcdsaVerifyError::BadSignature)?;
let mut res = [0u8; 64];
res.copy_from_slice(&pubkey.serialize_uncompressed()[1..]);
Ok(res)
}
fn secp256k1_ecdsa_recover_compressed(
sig: PassPointerAndRead<&[u8; 65], 65>,
msg: PassPointerAndRead<&[u8; 32], 32>,
) -> AllocateAndReturnByCodec<Result<[u8; 33], EcdsaVerifyError>> {
let rid = libsecp256k1::RecoveryId::parse(
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
.map_err(|_| EcdsaVerifyError::BadRS)?;
let msg = libsecp256k1::Message::parse(msg);
let pubkey =
libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize_compressed())
}
#[version(2)]
fn secp256k1_ecdsa_recover_compressed(
sig: PassPointerAndRead<&[u8; 65], 65>,
msg: PassPointerAndRead<&[u8; 32], 32>,
) -> AllocateAndReturnByCodec<Result<[u8; 33], EcdsaVerifyError>> {
let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
.map_err(|_| EcdsaVerifyError::BadV)?;
let sig = RecoverableSignature::from_compact(&sig[..64], rid)
.map_err(|_| EcdsaVerifyError::BadRS)?;
let msg = Message::from_digest_slice(msg).expect("Message is 32 bytes; qed");
#[cfg(feature = "std")]
let ctx = secp256k1::SECP256K1;
#[cfg(not(feature = "std"))]
let ctx = secp256k1::Secp256k1::<secp256k1::VerifyOnly>::gen_new();
let pubkey = ctx.recover_ecdsa(&msg, &sig).map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize())
}
#[cfg(feature = "bls-experimental")]
fn bls381_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<bls381::Public, 144> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.bls381_generate_new(id, seed)
.expect("`bls381_generate` failed")
}
#[cfg(feature = "bls-experimental")]
fn bls381_generate_proof_of_possession(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&bls381::Public, 144>,
owner: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<bls381::ProofOfPossession>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.bls381_generate_proof_of_possession(id, pub_key, owner)
.ok()
.flatten()
}
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls381_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<ecdsa_bls381::Public, { 144 + 33 }> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_bls381_generate_new(id, seed)
.expect("`ecdsa_bls381_generate` failed")
}
#[cfg(feature = "bandersnatch-experimental")]
fn bandersnatch_generate(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
seed: PassFatPointerAndDecode<Option<Vec<u8>>>,
) -> AllocateAndReturnPointer<bandersnatch::Public, 32> {
let seed = seed.as_ref().map(|s| core::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.bandersnatch_generate_new(id, seed)
.expect("`bandernatch_generate` failed")
}
#[cfg(feature = "bandersnatch-experimental")]
fn bandersnatch_sign(
&mut self,
id: PassPointerAndReadCopy<KeyTypeId, 4>,
pub_key: PassPointerAndRead<&bandersnatch::Public, 32>,
msg: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<bandersnatch::Signature>> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.bandersnatch_sign(id, pub_key, msg)
.ok()
.flatten()
}
}
#[runtime_interface]
pub trait Hashing {
fn keccak_256(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 32], 32> {
pezsp_crypto_hashing::keccak_256(data)
}
fn keccak_512(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 64], 64> {
pezsp_crypto_hashing::keccak_512(data)
}
fn sha2_256(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 32], 32> {
pezsp_crypto_hashing::sha2_256(data)
}
fn blake2_128(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 16], 16> {
pezsp_crypto_hashing::blake2_128(data)
}
fn blake2_256(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 32], 32> {
pezsp_crypto_hashing::blake2_256(data)
}
fn twox_256(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 32], 32> {
pezsp_crypto_hashing::twox_256(data)
}
fn twox_128(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 16], 16> {
pezsp_crypto_hashing::twox_128(data)
}
fn twox_64(data: PassFatPointerAndRead<&[u8]>) -> AllocateAndReturnPointer<[u8; 8], 8> {
pezsp_crypto_hashing::twox_64(data)
}
}
#[runtime_interface]
pub trait TransactionIndex {
fn index(
&mut self,
extrinsic: u32,
size: u32,
context_hash: PassPointerAndReadCopy<[u8; 32], 32>,
) {
self.storage_index_transaction(extrinsic, &context_hash, size);
}
fn renew(&mut self, extrinsic: u32, context_hash: PassPointerAndReadCopy<[u8; 32], 32>) {
self.storage_renew_transaction_index(extrinsic, &context_hash);
}
}
#[runtime_interface]
pub trait OffchainIndex {
fn set(&mut self, key: PassFatPointerAndRead<&[u8]>, value: PassFatPointerAndRead<&[u8]>) {
self.set_offchain_storage(key, Some(value));
}
fn clear(&mut self, key: PassFatPointerAndRead<&[u8]>) {
self.set_offchain_storage(key, None);
}
}
#[cfg(not(bizinikiwi_runtime))]
pezsp_externalities::decl_extension! {
struct VerificationExtDeprecated(bool);
}
#[runtime_interface]
pub trait Offchain {
fn is_validator(&mut self) -> bool {
self.extension::<OffchainWorkerExt>()
.expect("is_validator can be called only in the offchain worker context")
.is_validator()
}
fn submit_transaction(
&mut self,
data: PassFatPointerAndRead<Vec<u8>>,
) -> AllocateAndReturnByCodec<Result<(), ()>> {
self.extension::<TransactionPoolExt>()
.expect(
"submit_transaction can be called only in the offchain call context with
TransactionPool capabilities enabled",
)
.submit_transaction(data)
}
fn network_state(&mut self) -> AllocateAndReturnByCodec<Result<OpaqueNetworkState, ()>> {
self.extension::<OffchainWorkerExt>()
.expect("network_state can be called only in the offchain worker context")
.network_state()
}
fn timestamp(&mut self) -> ReturnAs<Timestamp, u64> {
self.extension::<OffchainWorkerExt>()
.expect("timestamp can be called only in the offchain worker context")
.timestamp()
}
fn sleep_until(&mut self, deadline: PassAs<Timestamp, u64>) {
self.extension::<OffchainWorkerExt>()
.expect("sleep_until can be called only in the offchain worker context")
.sleep_until(deadline)
}
fn random_seed(&mut self) -> AllocateAndReturnPointer<[u8; 32], 32> {
self.extension::<OffchainWorkerExt>()
.expect("random_seed can be called only in the offchain worker context")
.random_seed()
}
fn local_storage_set(
&mut self,
kind: PassAs<StorageKind, u32>,
key: PassFatPointerAndRead<&[u8]>,
value: PassFatPointerAndRead<&[u8]>,
) {
self.extension::<OffchainDbExt>()
.expect(
"local_storage_set can be called only in the offchain call context with
OffchainDb extension",
)
.local_storage_set(kind, key, value)
}
fn local_storage_clear(
&mut self,
kind: PassAs<StorageKind, u32>,
key: PassFatPointerAndRead<&[u8]>,
) {
self.extension::<OffchainDbExt>()
.expect(
"local_storage_clear can be called only in the offchain call context with
OffchainDb extension",
)
.local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: PassAs<StorageKind, u32>,
key: PassFatPointerAndRead<&[u8]>,
old_value: PassFatPointerAndDecode<Option<Vec<u8>>>,
new_value: PassFatPointerAndRead<&[u8]>,
) -> bool {
self.extension::<OffchainDbExt>()
.expect(
"local_storage_compare_and_set can be called only in the offchain call context
with OffchainDb extension",
)
.local_storage_compare_and_set(kind, key, old_value.as_deref(), new_value)
}
fn local_storage_get(
&mut self,
kind: PassAs<StorageKind, u32>,
key: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
self.extension::<OffchainDbExt>()
.expect(
"local_storage_get can be called only in the offchain call context with
OffchainDb extension",
)
.local_storage_get(kind, key)
}
fn http_request_start(
&mut self,
method: PassFatPointerAndRead<&str>,
uri: PassFatPointerAndRead<&str>,
meta: PassFatPointerAndRead<&[u8]>,
) -> AllocateAndReturnByCodec<Result<HttpRequestId, ()>> {
self.extension::<OffchainWorkerExt>()
.expect("http_request_start can be called only in the offchain worker context")
.http_request_start(method, uri, meta)
}
fn http_request_add_header(
&mut self,
request_id: PassAs<HttpRequestId, u16>,
name: PassFatPointerAndRead<&str>,
value: PassFatPointerAndRead<&str>,
) -> AllocateAndReturnByCodec<Result<(), ()>> {
self.extension::<OffchainWorkerExt>()
.expect("http_request_add_header can be called only in the offchain worker context")
.http_request_add_header(request_id, name, value)
}
fn http_request_write_body(
&mut self,
request_id: PassAs<HttpRequestId, u16>,
chunk: PassFatPointerAndRead<&[u8]>,
deadline: PassFatPointerAndDecode<Option<Timestamp>>,
) -> AllocateAndReturnByCodec<Result<(), HttpError>> {
self.extension::<OffchainWorkerExt>()
.expect("http_request_write_body can be called only in the offchain worker context")
.http_request_write_body(request_id, chunk, deadline)
}
fn http_response_wait(
&mut self,
ids: PassFatPointerAndDecodeSlice<&[HttpRequestId]>,
deadline: PassFatPointerAndDecode<Option<Timestamp>>,
) -> AllocateAndReturnByCodec<Vec<HttpRequestStatus>> {
self.extension::<OffchainWorkerExt>()
.expect("http_response_wait can be called only in the offchain worker context")
.http_response_wait(ids, deadline)
}
fn http_response_headers(
&mut self,
request_id: PassAs<HttpRequestId, u16>,
) -> AllocateAndReturnByCodec<Vec<(Vec<u8>, Vec<u8>)>> {
self.extension::<OffchainWorkerExt>()
.expect("http_response_headers can be called only in the offchain worker context")
.http_response_headers(request_id)
}
fn http_response_read_body(
&mut self,
request_id: PassAs<HttpRequestId, u16>,
buffer: PassFatPointerAndReadWrite<&mut [u8]>,
deadline: PassFatPointerAndDecode<Option<Timestamp>>,
) -> AllocateAndReturnByCodec<Result<u32, HttpError>> {
self.extension::<OffchainWorkerExt>()
.expect("http_response_read_body can be called only in the offchain worker context")
.http_response_read_body(request_id, buffer, deadline)
.map(|r| r as u32)
}
fn set_authorized_nodes(
&mut self,
nodes: PassFatPointerAndDecode<Vec<OpaquePeerId>>,
authorized_only: bool,
) {
self.extension::<OffchainWorkerExt>()
.expect("set_authorized_nodes can be called only in the offchain worker context")
.set_authorized_nodes(nodes, authorized_only)
}
}
#[runtime_interface(wasm_only)]
pub trait Allocator {
fn malloc(&mut self, size: u32) -> Pointer<u8> {
self.allocate_memory(size).expect("Failed to allocate memory")
}
fn free(&mut self, ptr: Pointer<u8>) {
self.deallocate_memory(ptr).expect("Failed to deallocate memory")
}
}
#[runtime_interface(wasm_only)]
pub trait PanicHandler {
#[trap_on_return]
fn abort_on_panic(&mut self, message: PassFatPointerAndRead<&str>) {
self.register_panic_error_message(message);
}
}
#[runtime_interface]
pub trait Logging {
fn log(
level: PassAs<RuntimeInterfaceLogLevel, u8>,
target: PassFatPointerAndRead<&str>,
message: PassFatPointerAndRead<&[u8]>,
) {
if let Ok(message) = core::str::from_utf8(message) {
log::log!(target: target, log::Level::from(level), "{}", message)
}
}
fn max_level() -> ReturnAs<LogLevelFilter, u8> {
log::max_level().into()
}
}
#[runtime_interface(wasm_only, no_tracing)]
pub trait WasmTracing {
fn enabled(&mut self, metadata: PassFatPointerAndDecode<pezsp_tracing::WasmMetadata>) -> bool {
let metadata: &tracing_core::metadata::Metadata<'static> = (&metadata).into();
tracing::dispatcher::get_default(|d| d.enabled(metadata))
}
fn enter_span(
&mut self,
span: PassFatPointerAndDecode<pezsp_tracing::WasmEntryAttributes>,
) -> u64 {
let span: tracing::Span = span.into();
match span.id() {
Some(id) => tracing::dispatcher::get_default(|d| {
let final_id = d.clone_span(&id);
d.enter(&final_id);
final_id.into_u64()
}),
_ => 0,
}
}
fn event(&mut self, event: PassFatPointerAndDecode<pezsp_tracing::WasmEntryAttributes>) {
event.emit();
}
fn exit(&mut self, span: u64) {
tracing::dispatcher::get_default(|d| {
let id = tracing_core::span::Id::from_u64(span);
d.exit(&id);
});
}
}
#[cfg(all(bizinikiwi_runtime, feature = "with-tracing"))]
mod tracing_setup {
use super::wasm_tracing;
use core::sync::atomic::{AtomicBool, Ordering};
use tracing_core::{
dispatcher::{set_global_default, Dispatch},
span::{Attributes, Id, Record},
Event, Metadata,
};
static TRACING_SET: AtomicBool = AtomicBool::new(false);
struct PassingTracingSubscriber;
impl tracing_core::Subscriber for PassingTracingSubscriber {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
wasm_tracing::enabled(metadata.into())
}
fn new_span(&self, attrs: &Attributes<'_>) -> Id {
Id::from_u64(wasm_tracing::enter_span(attrs.into()))
}
fn enter(&self, _: &Id) {
}
fn record(&self, _: &Id, _: &Record<'_>) {
unimplemented! {} }
fn record_follows_from(&self, _: &Id, _: &Id) {
unimplemented! {} }
fn event(&self, event: &Event<'_>) {
wasm_tracing::event(event.into())
}
fn exit(&self, span: &Id) {
wasm_tracing::exit(span.into_u64())
}
}
pub fn init_tracing() {
if TRACING_SET.load(Ordering::Relaxed) == false {
set_global_default(Dispatch::new(PassingTracingSubscriber {}))
.expect("We only ever call this once");
TRACING_SET.store(true, Ordering::Relaxed);
}
}
}
#[cfg(not(all(bizinikiwi_runtime, feature = "with-tracing")))]
mod tracing_setup {
pub fn init_tracing() {}
}
pub use tracing_setup::init_tracing;
pub fn unreachable() -> ! {
#[cfg(target_family = "wasm")]
{
core::arch::wasm32::unreachable();
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
unsafe {
core::arch::asm!("unimp", options(noreturn));
}
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64", target_family = "wasm")))]
unreachable!();
}
#[cfg(all(not(feature = "disable_panic_handler"), bizinikiwi_runtime))]
#[panic_handler]
pub fn panic(info: &core::panic::PanicInfo) -> ! {
let message = alloc::format!("{}", info);
#[cfg(feature = "improved_panic_error_reporting")]
{
panic_handler::abort_on_panic(&message);
}
#[cfg(not(feature = "improved_panic_error_reporting"))]
{
logging::log(RuntimeInterfaceLogLevel::Error, "runtime", message.as_bytes());
unreachable();
}
}
#[cfg(all(not(feature = "disable_oom"), enable_alloc_error_handler))]
#[alloc_error_handler]
pub fn oom(_: core::alloc::Layout) -> ! {
#[cfg(feature = "improved_panic_error_reporting")]
{
panic_handler::abort_on_panic("Runtime memory exhausted.");
}
#[cfg(not(feature = "improved_panic_error_reporting"))]
{
logging::log(
RuntimeInterfaceLogLevel::Error,
"runtime",
b"Runtime memory exhausted. Aborting",
);
unreachable();
}
}
#[cfg(feature = "std")] pub type TestExternalities = pezsp_state_machine::TestExternalities<pezsp_core::Blake2Hasher>;
#[docify::export]
#[cfg(not(bizinikiwi_runtime))]
pub type BizinikiwiHostFunctions = (
storage::HostFunctions,
default_child_storage::HostFunctions,
misc::HostFunctions,
wasm_tracing::HostFunctions,
offchain::HostFunctions,
crypto::HostFunctions,
hashing::HostFunctions,
allocator::HostFunctions,
panic_handler::HostFunctions,
logging::HostFunctions,
crate::trie::HostFunctions,
offchain_index::HostFunctions,
transaction_index::HostFunctions,
);
#[cfg(test)]
mod tests {
use super::*;
use pezsp_core::{crypto::UncheckedInto, map, storage::Storage};
use pezsp_state_machine::BasicExternalities;
#[test]
fn storage_works() {
let mut t = BasicExternalities::default();
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
storage::set(b"hello", b"world");
assert_eq!(storage::get(b"hello"), Some(b"world".to_vec().into()));
assert_eq!(storage::get(b"foo"), None);
storage::set(b"foo", &[1, 2, 3][..]);
});
t = BasicExternalities::new(Storage {
top: map![b"foo".to_vec() => b"bar".to_vec()],
children_default: map![],
});
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
assert_eq!(storage::get(b"foo"), Some(b"bar".to_vec().into()));
});
let value = vec![7u8; 35];
let storage =
Storage { top: map![b"foo00".to_vec() => value.clone()], children_default: map![] };
t = BasicExternalities::new(storage);
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
assert_eq!(storage::get(b"foo00"), Some(value.clone().into()));
});
}
#[test]
fn read_storage_works() {
let value = b"\x0b\0\0\0Hello world".to_vec();
let mut t = BasicExternalities::new(Storage {
top: map![b":test".to_vec() => value.clone()],
children_default: map![],
});
t.execute_with(|| {
let mut v = [0u8; 4];
assert_eq!(storage::read(b":test", &mut v[..], 0).unwrap(), value.len() as u32);
assert_eq!(v, [11u8, 0, 0, 0]);
let mut w = [0u8; 11];
assert_eq!(storage::read(b":test", &mut w[..], 4).unwrap(), value.len() as u32 - 4);
assert_eq!(&w, b"Hello world");
});
}
#[test]
fn clear_prefix_works() {
let mut t = BasicExternalities::new(Storage {
top: map![
b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec()
],
children_default: map![],
});
t.execute_with(|| {
assert!(matches!(
storage::clear_prefix(b":abc", None),
KillStorageResult::AllRemoved(2),
));
assert!(storage::get(b":a").is_some());
assert!(storage::get(b":abdd").is_some());
assert!(storage::get(b":abcd").is_none());
assert!(storage::get(b":abc").is_none());
assert!(matches!(
storage::clear_prefix(b":abc", None),
KillStorageResult::AllRemoved(0),
));
});
}
fn zero_ed_pub() -> ed25519::Public {
[0u8; 32].unchecked_into()
}
fn zero_ed_sig() -> ed25519::Signature {
ed25519::Signature::from_raw([0u8; 64])
}
#[test]
fn use_dalek_ext_works() {
let mut ext = BasicExternalities::default();
ext.register_extension(UseDalekExt::default());
ext.execute_with(|| {
assert!(!crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
});
BasicExternalities::default().execute_with(|| {
assert!(crypto::ed25519_verify(&zero_ed_sig(), &Vec::new(), &zero_ed_pub()));
})
}
#[test]
fn dalek_should_not_panic_on_invalid_signature() {
let mut ext = BasicExternalities::default();
ext.register_extension(UseDalekExt::default());
ext.execute_with(|| {
let mut bytes = [0u8; 64];
bytes[63] = 0b1110_0000;
assert!(!crypto::ed25519_verify(
&ed25519::Signature::from_raw(bytes),
&Vec::new(),
&zero_ed_pub()
));
});
}
}