Skip to content
Snippets Groups Projects
Unverified Commit 71c768a9 authored by Cyrill Leutwiler's avatar Cyrill Leutwiler Committed by GitHub
Browse files

[pallet-revive] Add chain ID to config an runtime API (#5807)


This PR adds the EVM chain ID to Config as well as a corresponding
runtime API so contracts can query it.

Related issue: https://github.com/paritytech/revive/issues/44

---------

Signed-off-by: default avatarxermicus <cyrill@parity.io>
Co-authored-by: command-bot <>
parent 3962ec08
No related merge requests found
Pipeline #498365 waiting for manual action with stages
in 1 hour, 3 minutes, and 45 seconds
title: "[pallet-revive] last call return data API"
doc:
- audience: Runtime Dev
description: |
This PR adds the EVM chain ID to Config as well as a corresponding runtime API so contracts can query it.
Related issue: https://github.com/paritytech/revive/issues/44
crates:
- name: pallet-revive
bump: major
- name: pallet-revive-fixtures
bump: patch
- name: pallet-revive-uapi
bump: minor
......@@ -49,8 +49,8 @@ use frame_support::{
imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount,
GetSalary, PayFromAccount,
},
AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency,
EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth,
AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, Contains,
Currency, EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth,
InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing,
OnUnbalanced, VariantCountOf, WithdrawReasons,
},
......@@ -1419,6 +1419,7 @@ impl pallet_revive::Config for Runtime {
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Debug = ();
type Xcm = ();
type ChainId = ConstU64<420_420_420>;
}
impl pallet_sudo::Config for Runtime {
......
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_std]
#![no_main]
extern crate common;
use uapi::{HostFn, HostFnImpl as api, ReturnFlags};
#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn deploy() {
call()
}
#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn call() {
let mut buf = [0; 32];
api::chain_id(&mut buf);
api::return_value(ReturnFlags::empty(), &buf);
}
......@@ -60,7 +60,7 @@ use frame_support::{
ensure,
traits::{
fungible::{Inspect, Mutate, MutateHold},
ConstU32, Contains, EnsureOrigin, Get, Time,
ConstU32, ConstU64, Contains, EnsureOrigin, Get, Time,
},
weights::{Weight, WeightMeter},
BoundedVec, RuntimeDebugNoBound,
......@@ -293,6 +293,13 @@ pub mod pallet {
/// This value is usually higher than [`Self::RuntimeMemory`] to account for the fact
/// that validators have to hold all storage items in PvF memory.
type PVFMemory: Get<u32>;
/// The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID.
///
/// This is a unique identifier assigned to each blockchain network,
/// preventing replay attacks.
#[pallet::constant]
type ChainId: Get<u64>;
}
/// Container for different types that implement [`DefaultConfig`]` of this pallet.
......@@ -365,6 +372,7 @@ pub mod pallet {
type Xcm = ();
type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
type ChainId = ConstU64<{ 0 }>;
}
}
......@@ -919,7 +927,7 @@ pub mod pallet {
let contract = if let Some(contract) = contract {
contract
} else {
return Err(<Error<T>>::ContractNotFound.into())
return Err(<Error<T>>::ContractNotFound.into());
};
<ExecStack<T, WasmBlob<T>>>::increment_refcount(code_hash)?;
<ExecStack<T, WasmBlob<T>>>::decrement_refcount(contract.code_hash);
......
......@@ -412,6 +412,7 @@ parameter_types! {
pub static DepositPerByte: BalanceOf<Test> = 1;
pub const DepositPerItem: BalanceOf<Test> = 2;
pub static CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
pub static ChainId: u64 = 384;
}
impl Convert<Weight, BalanceOf<Self>> for Test {
......@@ -496,6 +497,7 @@ impl Config for Test {
type InstantiateOrigin = EnsureAccount<Self, InstantiateAccount>;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Debug = TestDebug;
type ChainId = ChainId;
}
pub struct ExtBuilder {
......@@ -4310,4 +4312,17 @@ mod run_tests {
assert_ok!(builder::call(addr).build());
});
}
#[test]
fn chain_id_works() {
let (code, _) = compile_module("chain_id").unwrap();
ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
let chain_id = U256::from(<Test as Config>::ChainId::get());
let received = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_result();
assert_eq!(received.result.data, chain_id.encode());
});
}
}
......@@ -468,22 +468,28 @@ impl<T: Config> Token<T> for RuntimeCosts {
Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies),
DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len),
DebugMessage(len) => T::WeightInfo::seal_debug_message(len),
SetStorage { new_bytes, old_bytes } =>
cost_storage!(write, seal_set_storage, new_bytes, old_bytes),
SetStorage { new_bytes, old_bytes } => {
cost_storage!(write, seal_set_storage, new_bytes, old_bytes)
},
ClearStorage(len) => cost_storage!(write, seal_clear_storage, len),
ContainsStorage(len) => cost_storage!(read, seal_contains_storage, len),
GetStorage(len) => cost_storage!(read, seal_get_storage, len),
TakeStorage(len) => cost_storage!(write, seal_take_storage, len),
SetTransientStorage { new_bytes, old_bytes } =>
cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes),
ClearTransientStorage(len) =>
cost_storage!(write_transient, seal_clear_transient_storage, len),
ContainsTransientStorage(len) =>
cost_storage!(read_transient, seal_contains_transient_storage, len),
GetTransientStorage(len) =>
cost_storage!(read_transient, seal_get_transient_storage, len),
TakeTransientStorage(len) =>
cost_storage!(write_transient, seal_take_transient_storage, len),
SetTransientStorage { new_bytes, old_bytes } => {
cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes)
},
ClearTransientStorage(len) => {
cost_storage!(write_transient, seal_clear_transient_storage, len)
},
ContainsTransientStorage(len) => {
cost_storage!(read_transient, seal_contains_transient_storage, len)
},
GetTransientStorage(len) => {
cost_storage!(read_transient, seal_get_transient_storage, len)
},
TakeTransientStorage(len) => {
cost_storage!(write_transient, seal_take_transient_storage, len)
},
Transfer => T::WeightInfo::seal_transfer(),
CallBase => T::WeightInfo::seal_call(0, 0),
DelegateCallBase => T::WeightInfo::seal_delegate_call(),
......@@ -571,7 +577,7 @@ impl<'a, E: Ext, M: PolkaVmInstance<E::T>> Runtime<'a, E, M> {
Ok(Step) => None,
Ok(Ecalli(idx)) => {
let Some(syscall_symbol) = module.imports().get(idx) else {
return Some(Err(<Error<E::T>>::InvalidSyscall.into()))
return Some(Err(<Error<E::T>>::InvalidSyscall.into()));
};
match self.handle_ecall(instance, syscall_symbol.as_bytes(), api_version) {
Ok(None) => None,
......@@ -679,7 +685,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
) -> Result<(), DispatchError> {
if allow_skip && out_ptr == SENTINEL {
return Ok(())
return Ok(());
}
let len = memory.read_u32(out_len_ptr)?;
......@@ -703,7 +709,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
) -> Result<(), DispatchError> {
if allow_skip && out_ptr == SENTINEL {
return Ok(())
return Ok(());
}
let buf_len = buf.len() as u32;
......@@ -820,7 +826,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
let max_size = self.ext.max_value_size();
let charged = self.charge_gas(costs(value_len, self.ext.max_value_size()))?;
if value_len > max_size {
return Err(Error::<E::T>::ValueTooLarge.into())
return Err(Error::<E::T>::ValueTooLarge.into());
}
let key = self.decode_key(memory, key_ptr, key_len)?;
let value = Some(memory.read(value_ptr, value_len)?);
......@@ -1022,7 +1028,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
},
CallType::DelegateCall { code_hash_ptr } => {
if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
return Err(Error::<E::T>::InvalidCallFlags.into())
return Err(Error::<E::T>::InvalidCallFlags.into());
}
let code_hash = memory.read_h256(code_hash_ptr)?;
......@@ -1037,7 +1043,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
return Err(TrapReason::Return(ReturnData {
flags: return_value.flags.bits(),
data: return_value.data,
}))
}));
}
}
......@@ -1536,6 +1542,19 @@ pub mod env {
)?)
}
/// Returns the chain ID.
/// See [`pallet_revive_uapi::HostFn::chain_id`].
#[api_version(0)]
fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&as_bytes(U256::from(<E::T as Config>::ChainId::get())),
false,
|_| Some(RuntimeCosts::CopyToContract(32)),
)?)
}
/// Stores the value transferred along with this call/instantiate into the supplied buffer.
/// See [`pallet_revive_uapi::HostFn::value_transferred`].
#[api_version(0)]
......
......@@ -71,6 +71,9 @@ pub trait HostFn: private::Sealed {
/// - `output`: A reference to the output data buffer to write the balance.
fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]);
/// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID.
fn chain_id(output: &mut [u8; 32]);
/// Stores the current block number of the current contract into the supplied buffer.
///
/// # Parameters
......
......@@ -83,6 +83,7 @@ mod sys {
pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32);
pub fn balance(out_ptr: *mut u8);
pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8);
pub fn chain_id(out_ptr: *mut u8);
pub fn value_transferred(out_ptr: *mut u8);
pub fn now(out_ptr: *mut u8);
pub fn minimum_balance(out_ptr: *mut u8);
......@@ -447,7 +448,7 @@ impl HostFn for HostFnImpl {
}
impl_wrapper_for! {
[u8; 32] => block_number, balance, value_transferred, now, minimum_balance;
[u8; 32] => block_number, balance, value_transferred, now, minimum_balance, chain_id;
[u8; 20] => address, caller;
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment