Skip to content

Commit

Permalink
add chain id to config and expose via syscall
Browse files Browse the repository at this point in the history
Signed-off-by: xermicus <cyrill@parity.io>
  • Loading branch information
xermicus committed Sep 23, 2024
1 parent 2e4e5bf commit 708bd81
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 35 deletions.
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);
}
25 changes: 17 additions & 8 deletions substrate/frame/revive/src/lib.rs
Original file line number Diff line number Diff line change
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 All @@ -303,7 +310,7 @@ pub mod pallet {
traits::{ConstBool, ConstU32},
};
use frame_system::EnsureSigned;
use sp_core::parameter_types;
use sp_core::{parameter_types, ConstU64};

type AccountId = sp_runtime::AccountId32;
type Balance = u64;
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 @@ -659,8 +667,8 @@ pub mod pallet {

// We can use storage to store items using the available block ref_time with the
// `set_storage` host function.
let max_storage_size: u32 = ((max_block_ref_time /
(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
let max_storage_size: u32 = ((max_block_ref_time
/ (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
new_bytes: max_payload_size,
old_bytes: 0,
})
Expand All @@ -682,8 +690,8 @@ pub mod pallet {
// We can use storage to store events using the available block ref_time with the
// `deposit_event` host function. The overhead of stored events, which is around 100B,
// is not taken into account to simplify calculations, as it does not change much.
let max_events_size: u32 = ((max_block_ref_time /
(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
let max_events_size: u32 = ((max_block_ref_time
/ (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
num_topic: 0,
len: max_payload_size,
})
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 Expand Up @@ -1041,8 +1049,9 @@ where
storage_deposit_limit.saturating_reduce(upload_deposit);
(executable, upload_deposit)
},
Code::Existing(code_hash) =>
(WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
Code::Existing(code_hash) => {
(WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default())
},
};
let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
let mut storage_meter =
Expand Down
19 changes: 17 additions & 2 deletions substrate/frame/revive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ pub mod test_utils {
let code_info_len = CodeInfo::<Test>::max_encoded_len() as u64;
// Calculate deposit to be reserved.
// We add 2 storage items: one for code, other for code_info
DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len) +
DepositPerItem::get().saturating_mul(2)
DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len)
+ DepositPerItem::get().saturating_mul(2)
}
pub fn ensure_stored(code_hash: sp_core::H256) -> usize {
// Assert that code_info is stored
Expand Down 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());
});
}
}
70 changes: 46 additions & 24 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 @@ -563,27 +569,29 @@ impl<'a, E: Ext, M: PolkaVmInstance<E::T>> Runtime<'a, E, M> {
log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
Some(Err(Error::<E::T>::ExecutionFailed.into()))
},
Ok(Finished) =>
Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })),
Ok(Finished) => {
Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }))
},
Ok(Trap) => Some(Err(Error::<E::T>::ContractTrapped.into())),
Ok(Segfault(_)) => Some(Err(Error::<E::T>::ExecutionFailed.into())),
Ok(NotEnoughGas) => Some(Err(Error::<E::T>::OutOfGas.into())),
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,
Ok(Some(return_value)) => {
instance.write_output(return_value);
None
},
Err(TrapReason::Return(ReturnData { flags, data })) =>
Err(TrapReason::Return(ReturnData { flags, data })) => {
match ReturnFlags::from_bits(flags) {
None => Some(Err(Error::<E::T>::InvalidCallFlags.into())),
Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
},
}
},
Err(TrapReason::Termination) => Some(Ok(Default::default())),
Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())),
}
Expand Down Expand Up @@ -679,7 +687,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 +711,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 +828,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 +1030,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 +1045,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 +1544,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,
already_charged,
)?)
}

/// 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 Expand Up @@ -1719,8 +1740,9 @@ pub mod env {
Environment::new(self, memory, id, input_ptr, input_len, output_ptr, output_len_ptr);
let ret = match chain_extension.call(env)? {
RetVal::Converging(val) => Ok(val),
RetVal::Diverging { flags, data } =>
Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })),
RetVal::Diverging { flags, data } => {
Err(TrapReason::Return(ReturnData { flags: flags.bits(), data }))
},
};
self.chain_extension = Some(chain_extension);
ret
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 708bd81

Please sign in to comment.