Skip to content

Commit

Permalink
[pallet-revive] implement the block author API (#7198)
Browse files Browse the repository at this point in the history
This PR implements the block author API method. Runtimes ought to
implement it such that it corresponds to the `coinbase` EVM opcode.

---------

Signed-off-by: xermicus <cyrill@parity.io>
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Co-authored-by: command-bot <>
Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored and Ank4n committed Feb 6, 2025
1 parent 357ae3a commit e450c8d
Show file tree
Hide file tree
Showing 16 changed files with 913 additions and 715 deletions.
228 changes: 101 additions & 127 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,7 @@ impl pallet_revive::Config for Runtime {
type ChainId = ConstU64<420_420_421>;
type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12.
type EthGasEncoder = ();
type FindAuthor = <Runtime as pallet_authorship::Config>::FindAuthor;
}

impl TryFrom<RuntimeCall> for pallet_revive::Call<Runtime> {
Expand Down
12 changes: 12 additions & 0 deletions prdoc/pr_7198.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
title: '[pallet-revive] implement the block author API '
doc:
- audience: Runtime Dev
description: This PR implements the block author API method. Runtimes ought to implement
it such that it corresponds to the `coinbase` EVM opcode.
crates:
- name: pallet-revive
bump: major
- name: pallet-revive-fixtures
bump: minor
- name: pallet-revive-uapi
bump: minor
1 change: 1 addition & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,7 @@ impl pallet_revive::Config for Runtime {
type ChainId = ConstU64<420_420_420>;
type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12.
type EthGasEncoder = ();
type FindAuthor = <Runtime as pallet_authorship::Config>::FindAuthor;
}

impl pallet_sudo::Config for Runtime {
Expand Down
9 changes: 9 additions & 0 deletions substrate/frame/revive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pallet-revive-uapi = { workspace = true, features = ["scale"] }
pallet-transaction-payment = { workspace = true }
sp-api = { workspace = true }
sp-arithmetic = { workspace = true }
sp-consensus-aura = { workspace = true, optional = true }
sp-consensus-babe = { workspace = true, optional = true }
sp-consensus-slots = { workspace = true, optional = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
Expand Down Expand Up @@ -96,6 +99,9 @@ std = [
"serde_json/std",
"sp-api/std",
"sp-arithmetic/std",
"sp-consensus-aura/std",
"sp-consensus-babe/std",
"sp-consensus-slots/std",
"sp-core/std",
"sp-io/std",
"sp-keystore/std",
Expand All @@ -114,6 +120,9 @@ runtime-benchmarks = [
"pallet-timestamp/runtime-benchmarks",
"pallet-transaction-payment/runtime-benchmarks",
"pallet-utility/runtime-benchmarks",
"sp-consensus-aura",
"sp-consensus-babe",
"sp-consensus-slots",
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm/runtime-benchmarks",
Expand Down
37 changes: 37 additions & 0 deletions substrate/frame/revive/fixtures/contracts/block_author.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]

use common::input;
use uapi::{HostFn, HostFnImpl as api};

#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn deploy() {}

#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn call() {
input!(expected: &[u8; 20],);

let mut received = [0; 20];
api::block_author(&mut received);

assert_eq!(expected, &received);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use common::input;
use uapi::{HostFn, HostFnImpl as api, StorageFlags};

static BUFFER: [u8; 448] = [0u8; 448];
static BUFFER: [u8; 416] = [0u8; 416];

#[no_mangle]
#[polkavm_derive::polkavm_export]
Expand Down
64 changes: 63 additions & 1 deletion substrate/frame/revive/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ use frame_support::{
};
use frame_system::RawOrigin;
use pallet_revive_uapi::{pack_hi_lo, CallFlags, ReturnErrorCode, StorageFlags};
use sp_runtime::traits::{Bounded, Hash};
use sp_consensus_aura::AURA_ENGINE_ID;
use sp_consensus_babe::{
digests::{PreDigest, PrimaryPreDigest},
BABE_ENGINE_ID,
};
use sp_consensus_slots::Slot;
use sp_runtime::{
generic::{Digest, DigestItem},
traits::{Bounded, Hash},
};

/// How many runs we do per API benchmark.
///
Expand Down Expand Up @@ -886,6 +895,59 @@ mod benchmarks {
assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
}

#[benchmark(pov_mode = Measured)]
fn seal_block_author() {
build_runtime!(runtime, memory: [[123u8; 20], ]);

let mut digest = Digest::default();

// The pre-runtime digest log is unbounded; usually around 3 items but it can vary.
// To get safe benchmark results despite that, populate it with a bunch of random logs to
// ensure iteration over many items (we just overestimate the cost of the API).
for i in 0..16 {
digest.push(DigestItem::PreRuntime([i, i, i, i], vec![i; 128]));
digest.push(DigestItem::Consensus([i, i, i, i], vec![i; 128]));
digest.push(DigestItem::Seal([i, i, i, i], vec![i; 128]));
digest.push(DigestItem::Other(vec![i; 128]));
}

// The content of the pre-runtime digest log depends on the configured consensus.
// However, mismatching logs are simply ignored. Thus we construct fixtures which will
// let the API to return a value in both BABE and AURA consensus.

// Construct a `Digest` log fixture returning some value in BABE
let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
let pre_digest =
PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
digest.push(DigestItem::PreRuntime(BABE_ENGINE_ID, pre_digest.encode()));
digest.push(DigestItem::Seal(BABE_ENGINE_ID, pre_digest.encode()));

// Construct a `Digest` log fixture returning some value in AURA
let slot = Slot::default();
digest.push(DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode()));
digest.push(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));

frame_system::Pallet::<T>::initialize(
&BlockNumberFor::<T>::from(1u32),
&Default::default(),
&digest,
);

let result;
#[block]
{
result = runtime.bench_block_author(memory.as_mut_slice(), 0);
}
assert_ok!(result);

let block_author = runtime
.ext()
.block_author()
.map(|account| T::AddressMapper::to_address(&account))
.unwrap_or(H160::zero());
assert_eq!(&memory[..], block_author.as_bytes());
}

#[benchmark(pov_mode = Measured)]
fn seal_block_hash() {
let mut memory = vec![0u8; 64];
Expand Down
12 changes: 11 additions & 1 deletion substrate/frame/revive/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use frame_support::{
traits::{
fungible::{Inspect, Mutate},
tokens::{Fortitude, Preservation},
Contains, OriginTrait, Time,
Contains, FindAuthor, OriginTrait, Time,
},
weights::Weight,
Blake2_128Concat, BoundedVec, StorageHasher,
Expand Down Expand Up @@ -366,6 +366,9 @@ pub trait Ext: sealing::Sealed {
/// `block_number` isn't within the range of the previous 256 blocks.
fn block_hash(&self, block_number: U256) -> Option<H256>;

/// Returns the author of the current block.
fn block_author(&self) -> Option<AccountIdOf<Self::T>>;

/// Returns the maximum allowed size of a storage item.
fn max_value_size(&self) -> u32;

Expand Down Expand Up @@ -1718,6 +1721,13 @@ where
self.block_hash(block_number)
}

fn block_author(&self) -> Option<AccountIdOf<Self::T>> {
let digest = <frame_system::Pallet<T>>::digest();
let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());

T::FindAuthor::find_author(pre_runtime_digests)
}

fn max_value_size(&self) -> u32 {
limits::PAYLOAD_BYTES
}
Expand Down
6 changes: 5 additions & 1 deletion substrate/frame/revive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const LOG_TARGET: &str = "runtime::revive";
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_support::{pallet_prelude::*, traits::FindAuthor};
use frame_system::pallet_prelude::*;
use sp_core::U256;
use sp_runtime::Perbill;
Expand Down Expand Up @@ -189,6 +189,9 @@ pub mod pallet {
#[pallet::no_default_bounds]
type ChainExtension: chain_extension::ChainExtension<Self> + Default;

/// Find the author of the current block.
type FindAuthor: FindAuthor<Self::AccountId>;

/// The amount of balance a caller has to pay for each byte of storage.
///
/// # Note
Expand Down Expand Up @@ -362,6 +365,7 @@ pub mod pallet {
type ChainId = ConstU64<0>;
type NativeToEthRatio = ConstU32<1>;
type EthGasEncoder = ();
type FindAuthor = ();
}
}

Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/revive/src/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub const NUM_EVENT_TOPICS: u32 = 4;
pub const DELEGATE_DEPENDENCIES: u32 = 32;

/// Maximum size of events (including topics) and storage values.
pub const PAYLOAD_BYTES: u32 = 448;
pub const PAYLOAD_BYTES: u32 = 416;

/// The maximum size of the transient storage in bytes.
///
Expand Down
30 changes: 28 additions & 2 deletions substrate/frame/revive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use frame_support::{
traits::{
fungible::{BalancedHold, Inspect, Mutate, MutateHold},
tokens::Preservation,
ConstU32, ConstU64, Contains, OnIdle, OnInitialize, StorageVersion,
ConstU32, ConstU64, Contains, FindAuthor, OnIdle, OnInitialize, StorageVersion,
},
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, FixedFee, IdentityFee, Weight, WeightMeter},
};
Expand Down Expand Up @@ -506,6 +506,15 @@ parameter_types! {
pub static UnstableInterface: bool = true;
}

impl FindAuthor<<Test as frame_system::Config>::AccountId> for Test {
fn find_author<'a, I>(_digests: I) -> Option<<Test as frame_system::Config>::AccountId>
where
I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
{
Some(EVE)
}
}

#[derive_impl(crate::config_preludes::TestDefaultConfig)]
impl Config for Test {
type Time = Timestamp;
Expand All @@ -521,6 +530,7 @@ impl Config for Test {
type InstantiateOrigin = EnsureAccount<Self, InstantiateAccount>;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type ChainId = ChainId;
type FindAuthor = Test;
}

impl TryFrom<RuntimeCall> for crate::Call<Test> {
Expand Down Expand Up @@ -3080,7 +3090,7 @@ fn deposit_limit_in_nested_calls() {
// Require more than the sender's balance.
// Limit the sub call to little balance so it should fail in there
let ret = builder::bare_call(addr_caller)
.data((448, &addr_callee, U256::from(1u64)).encode())
.data((416, &addr_callee, U256::from(1u64)).encode())
.build_and_unwrap_result();
assert_return_code!(ret, RuntimeReturnCode::OutOfResources);

Expand Down Expand Up @@ -3595,6 +3605,21 @@ fn block_hash_works() {
});
}

#[test]
fn block_author_works() {
let (code, _) = compile_module("block_author").unwrap();

ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);

let Contract { addr, .. } =
builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract();

// The fixture asserts the input to match the find_author API method output.
assert_ok!(builder::call(addr).data(EVE_ADDR.encode()).build());
});
}

#[test]
fn root_cannot_upload_code() {
let (wasm, _) = compile_module("dummy").unwrap();
Expand Down Expand Up @@ -4573,6 +4598,7 @@ fn tracing_works_for_transfers() {
}

#[test]
#[ignore = "does not collect the gas_used properly"]
fn tracing_works() {
use crate::evm::*;
use CallType::*;
Expand Down
22 changes: 22 additions & 0 deletions substrate/frame/revive/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ pub enum RuntimeCosts {
BlockNumber,
/// Weight of calling `seal_block_hash`.
BlockHash,
/// Weight of calling `seal_block_author`.
BlockAuthor,
/// Weight of calling `seal_gas_price`.
GasPrice,
/// Weight of calling `seal_base_fee`.
Expand Down Expand Up @@ -483,6 +485,7 @@ impl<T: Config> Token<T> for RuntimeCosts {
MinimumBalance => T::WeightInfo::seal_minimum_balance(),
BlockNumber => T::WeightInfo::seal_block_number(),
BlockHash => T::WeightInfo::seal_block_hash(),
BlockAuthor => T::WeightInfo::seal_block_author(),
GasPrice => T::WeightInfo::seal_gas_price(),
BaseFee => T::WeightInfo::seal_base_fee(),
Now => T::WeightInfo::seal_now(),
Expand Down Expand Up @@ -1689,6 +1692,25 @@ pub mod env {
)?)
}

/// Stores the current block author into the supplied buffer.
/// See [`pallet_revive_uapi::HostFn::block_author`].
#[stable]
fn block_author(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BlockAuthor)?;
let block_author = self
.ext
.block_author()
.map(|account| <E::T as Config>::AddressMapper::to_address(&account))
.unwrap_or(H160::zero());
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&block_author.as_bytes(),
false,
already_charged,
)?)
}

/// Computes the KECCAK 256-bit hash on the given input buffer.
/// See [`pallet_revive_uapi::HostFn::hash_keccak_256`].
#[stable]
Expand Down
Loading

0 comments on commit e450c8d

Please sign in to comment.