Skip to content

Commit

Permalink
[pallet-revive] Add chain ID to config an runtime API (#5807)
Browse files Browse the repository at this point in the history
This PR adds the EVM chain ID to Config as well as a corresponding
runtime API so contracts can query it.

Related issue: paritytech/revive#44

---------

Signed-off-by: xermicus <[email protected]>
Co-authored-by: command-bot <>
  • Loading branch information
xermicus authored Sep 23, 2024
1 parent e8dad10 commit 544c2ed
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 23 deletions.
16 changes: 16 additions & 0 deletions prdoc/pr_5807.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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
5 changes: 3 additions & 2 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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 {
Expand Down
37 changes: 37 additions & 0 deletions substrate/frame/revive/fixtures/contracts/chain_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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);
}
12 changes: 10 additions & 2 deletions substrate/frame/revive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 }>;
}
}

Expand Down Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions substrate/frame/revive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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());
});
}
}
55 changes: 37 additions & 18 deletions substrate/frame/revive/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)?;
Expand All @@ -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;
Expand Down Expand Up @@ -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)?);
Expand Down Expand Up @@ -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)?;
Expand All @@ -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,
}))
}));
}
}

Expand Down Expand Up @@ -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)]
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/revive/uapi/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion substrate/frame/revive/uapi/src/host/riscv32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 544c2ed

Please sign in to comment.