Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(no merge) refactor: migrate testnet chain extension #167

Closed
7 changes: 3 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ substrate-build-script-utils = "11.0.0"

# Local
pallet-api = { path = "pallets/api", default-features = false }
pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-common = { path = "runtime/common", default-features = false }
pop-runtime-extensions = { path = "runtime/extensions", default-features = false }
pop-runtime-extension = { path = "runtime/extension", default-features = false }
pop-primitives = { path = "./primitives", default-features = false }

# Substrate
Expand Down
4 changes: 2 additions & 2 deletions runtime/devnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ smallvec.workspace = true
# Local
pop-primitives.workspace = true
pop-runtime-common.workspace = true
pop-runtime-extensions.workspace = true
pop-runtime-extension.workspace = true
pallet-api.workspace = true

# Substrate
Expand Down Expand Up @@ -141,7 +141,7 @@ std = [
"polkadot-parachain-primitives/std",
"polkadot-runtime-common/std",
"pop-primitives/std",
"pop-runtime-extensions/std",
"pop-runtime-extension/std",
"scale-info/std",
"sp-api/std",
"sp-io/std",
Expand Down
65 changes: 60 additions & 5 deletions runtime/devnet/src/config/api.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,63 @@
use crate::{
config::assets::TrustBackedAssetsInstance,
fungibles::{self},
Runtime,
};
use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::traits::Contains;
use pop_runtime_extension::ReadStateHandler;

/// A query of runtime state.
#[derive(Encode, Decode, Debug, MaxEncodedLen)]
#[repr(u8)]
pub enum RuntimeRead {
/// Fungible token queries.
#[codec(index = 150)]
Fungibles(fungibles::Read<Runtime>),
}

impl ReadStateHandler<Runtime> for RuntimeRead {
fn handle_read(read: RuntimeRead) -> sp_std::vec::Vec<u8> {
match read {
RuntimeRead::Fungibles(key) => fungibles::Pallet::read_state(key),
}
}
}

/// A type to identify allowed calls to the Runtime from the API.
pub struct AllowedApiCalls;

impl Contains<RuntimeCall> for AllowedApiCalls {
/// Allowed runtime calls from the API.
fn contains(c: &RuntimeCall) -> bool {
use fungibles::Call::*;
matches!(
c,
RuntimeCall::Fungibles(
transfer { .. }
| transfer_from { .. }
| approve { .. } | increase_allowance { .. }
| decrease_allowance { .. }
| create { .. } | set_metadata { .. }
| start_destroy { .. }
| clear_metadata { .. }
| mint { .. } | burn { .. }
)
)
}
}

impl Contains<RuntimeRead> for AllowedApiCalls {
/// Allowed state queries from the API.
fn contains(c: &RuntimeRead) -> bool {
use fungibles::Read::*;
matches!(
c,
RuntimeRead::Fungibles(
TotalSupply(..)
| BalanceOf { .. } | Allowance { .. }
| TokenName(..) | TokenSymbol(..)
| TokenDecimals(..) | AssetExists(..)
)
)
}
}

impl fungibles::Config for Runtime {
type AssetsInstance = TrustBackedAssetsInstance;
Expand Down
9 changes: 8 additions & 1 deletion runtime/devnet/src/config/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use frame_support::{
};
use frame_system::pallet_prelude::BlockNumberFor;

use super::api::{AllowedApiCalls, RuntimeRead};

pub enum AllowBalancesCall {}

impl frame_support::traits::Contains<RuntimeCall> for AllowBalancesCall {
Expand Down Expand Up @@ -44,6 +46,11 @@ parameter_types! {
pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
}

impl pop_runtime_extension::Config for Runtime {
type RuntimeRead = RuntimeRead;
type AllowedApiCalls = AllowedApiCalls;
}

impl pallet_contracts::Config for Runtime {
type Time = Timestamp;
type Randomness = DummyRandomness<Self>;
Expand All @@ -63,7 +70,7 @@ impl pallet_contracts::Config for Runtime {
type CallStack = [pallet_contracts::Frame<Self>; 23];
type WeightPrice = pallet_transaction_payment::Pallet<Self>;
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
type ChainExtension = pop_runtime_extensions::PopApiExtension;
type ChainExtension = pop_runtime_extension::PopApiExtension;
type Schedule = Schedule;
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
// This node is geared towards development and testing of contracts.
Expand Down
176 changes: 72 additions & 104 deletions runtime/devnet/src/config/extension.rs
Original file line number Diff line number Diff line change
@@ -1,112 +1,80 @@
use crate::{
fungibles::{self},
Runtime, RuntimeCall,
};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{ensure, traits::Contains};
use pallet_contracts::chain_extension::{BufInBufOutState, Environment, Ext};
use pop_runtime_extensions::{
constants::{DECODING_FAILED_ERROR, LOG_TARGET, UNKNOWN_CALL_ERROR},
PopApiExtensionConfig, StateReadHandler,
};
use sp_core::Get;
use sp_runtime::DispatchError;
#[cfg(test)]
mod tests {
use crate::{config::assets::TrustBackedAssetsInstance, Assets, Runtime, System};
use codec::{Decode, Encode};
use sp_runtime::{
ArithmeticError, BuildStorage, DispatchError, ModuleError, TokenError,
MAX_MODULE_ERROR_ENCODED_SIZE,
};

/// A query of runtime state.
#[derive(Encode, Decode, Debug, MaxEncodedLen)]
#[repr(u8)]
pub enum RuntimeRead {
/// Fungible token queries.
#[codec(index = 150)]
Fungibles(fungibles::Read<Runtime>),
}

/// A type to identify allowed calls to the Runtime from the API.
pub struct AllowedApiCalls;

impl Contains<RuntimeCall> for AllowedApiCalls {
/// Allowed runtime calls from the API.
fn contains(c: &RuntimeCall) -> bool {
use fungibles::Call::*;
matches!(
c,
RuntimeCall::Fungibles(
transfer { .. }
| transfer_from { .. }
| approve { .. } | increase_allowance { .. }
| decrease_allowance { .. }
)
)
fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.expect("Frame system builds valid default genesis config");
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
}

impl Contains<RuntimeRead> for AllowedApiCalls {
/// Allowed state queries from the API.
fn contains(c: &RuntimeRead) -> bool {
use fungibles::Read::*;
matches!(
c,
RuntimeRead::Fungibles(
TotalSupply(..)
| BalanceOf { .. } | Allowance { .. }
| TokenName(..) | TokenSymbol(..)
| TokenDecimals(..)
)
)
}
}
#[test]
fn encoding_decoding_dispatch_error() {
new_test_ext().execute_with(|| {
let error = DispatchError::Module(ModuleError {
index: 255,
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, 255, 2, 0, 0, 0]);
assert_eq!(
decoded,
// `message` is skipped for encoding.
DispatchError::Module(ModuleError {
index: 255,
error: [2, 0, 0, 0],
message: None
})
);

/// Wrapper to enable versioning of runtime state reads.
#[derive(Decode, Debug)]
enum VersionedStateRead {
/// Version zero of state reads.
#[codec(index = 0)]
V0(RuntimeRead),
}
// Example pallet assets Error into ModuleError.
let index = <<Runtime as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::index::<
Assets,
>()
.expect("Every active module has an index in the runtime; qed") as u8;
let mut error =
pallet_assets::Error::NotFrozen::<Runtime, TrustBackedAssetsInstance>.encode();
error.resize(MAX_MODULE_ERROR_ENCODED_SIZE, 0);
let error = DispatchError::Module(ModuleError {
index,
error: TryInto::try_into(error).expect("should work"),
message: None,
});
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![3, 52, 18, 0, 0, 0]);
assert_eq!(
decoded,
DispatchError::Module(ModuleError {
index: 52,
error: [18, 0, 0, 0],
message: None
})
);

/// Wrapper to enable versioning of runtime calls.
#[derive(Decode, Debug)]
enum VersionedDispatch<T: PopApiExtensionConfig> {
/// Version zero of dispatch calls.
#[codec(index = 0)]
V0(T::RuntimeCall),
}

pub struct ContractExecutionContext;

impl StateReadHandler for ContractExecutionContext {
fn handle_params<T, E>(
env: &mut Environment<E, BufInBufOutState>,
params: Vec<u8>,
) -> Result<(), DispatchError>
where
E: Ext<T = T>,
T: PopApiExtensionConfig,
{
const LOG_PREFIX: &str = " read_state |";
// Example DispatchError::Token
let error = DispatchError::Token(TokenError::UnknownAsset);
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![7, 4]);
assert_eq!(decoded, error);

let read =
<VersionedStateRead>::decode(&mut &params[..]).map_err(|_| DECODING_FAILED_ERROR)?;

// Charge weight for doing one storage read.
env.charge_weight(T::DbWeight::get().reads(1_u64))?;
let result = match read {
VersionedStateRead::V0(read) => {
ensure!(AllowedApiCalls::contains(&read), UNKNOWN_CALL_ERROR);
match read {
RuntimeRead::Fungibles(key) => fungibles::Pallet::read_state(key),
}
},
};
log::trace!(
target:LOG_TARGET,
"{} result: {:?}.", LOG_PREFIX, result
);
env.write(&result, false, None)
// Example DispatchError::Arithmetic
let error = DispatchError::Arithmetic(ArithmeticError::Overflow);
let encoded = error.encode();
let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
assert_eq!(encoded, vec![8, 1]);
assert_eq!(decoded, error);
});
}
}

impl PopApiExtensionConfig for Runtime {
type StateReadHandler = ContractExecutionContext;
type AllowedDispatchCalls = AllowedApiCalls;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "pop-runtime-extensions"
name = "pop-runtime-extension"
version = "0.1.0"
authors.workspace = true
description.workspace = true
Expand All @@ -13,7 +13,6 @@ publish = false
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
cfg-if = "1.0"
codec.workspace = true
log.workspace = true

Expand Down Expand Up @@ -43,6 +42,7 @@ std = [
"frame-system/std",
"pallet-contracts/std",
"pop-primitives/std",
"sp-runtime/std",
"sp-core/std",
"sp-std/std",
]
Expand All @@ -51,4 +51,5 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-contracts/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
File renamed without changes.
Loading
Loading