Skip to content

Commit

Permalink
Added staking EVM precompiles (#425)
Browse files Browse the repository at this point in the history
  • Loading branch information
akru authored Sep 22, 2021
1 parent 124f3a9 commit ae46758
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 13 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"runtime/shibuya",
"frame/block-reward",
"frame/custom-signatures",
"precompiles/staking",
]

[profile.release]
Expand Down
37 changes: 37 additions & 0 deletions precompiles/staking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "pallet-precompile-staking"
version = "0.1.0"
authors = ["Stake Technologies <[email protected]>"]
edition = "2018"
license = "Apache-2.0"
homepage = "https://astar.network"
repository = "https://github.com/PlasmNetwork/Astar"
description = "Collator staking EVM precompiles"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
evm = { version="0.30.0", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm = { git="https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-collator-selection = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.9", default-features = false }

[features]
default = ["std"]
std = [
"codec/std",
"evm/std",
"sp-std/std",
"sp-core/std",
"frame-support/std",
"frame-system/std",
"pallet-evm/std",
"pallet-session/std",
"pallet-collator-selection/std",
]
21 changes: 21 additions & 0 deletions precompiles/staking/Staking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: BSD-3-Clause

pragma solidity >=0.7.0;

interface Staking {
/*
* @dev Set session keys of function caller.
*/
function set_keys(bytes calldata keys) external;

/*
* @dev Removes any session keys of the function caller.
*/
function purge_keys() external;

/*
* @dev Register function caller as collation candidate.
* @note Collation staking deposit will be locked.
*/
function register_as_candidate() external;
}
101 changes: 101 additions & 0 deletions precompiles/staking/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Astar collator staking interface.
#![cfg_attr(not(feature = "std"), no_std)]

use codec::Decode;
use evm::{executor::PrecompileOutput, Context, ExitError, ExitSucceed};
use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo};
use pallet_evm::{AddressMapping, GasWeightMapping, Precompile};
use sp_std::{marker::PhantomData, vec::Vec};

pub struct Staking<R>(PhantomData<R>);

impl<R> Staking<R>
where
R: pallet_session::Config + pallet_collator_selection::Config,
R::Call: From<pallet_session::Call<R>> + From<pallet_collator_selection::Call<R>>,
{
fn set_keys(keys: Vec<u8>) -> Result<R::Call, ExitError> {
let keys = <R as pallet_session::Config>::Keys::decode(&mut &keys[..])
.map_err(|_| ExitError::Other("Unable to decode session keys".into()))?;
Ok(pallet_session::Call::<R>::set_keys(keys, Default::default()).into())
}

fn purge_keys() -> R::Call {
pallet_session::Call::<R>::purge_keys().into()
}

fn register_as_candidate() -> R::Call {
pallet_collator_selection::Call::<R>::register_as_candidate().into()
}
}

impl<R> Precompile for Staking<R>
where
R: pallet_evm::Config + pallet_session::Config + pallet_collator_selection::Config,
R::Call: From<pallet_session::Call<R>>
+ From<pallet_collator_selection::Call<R>>
+ Dispatchable<PostInfo = PostDispatchInfo>
+ GetDispatchInfo,
<R::Call as Dispatchable>::Origin: From<Option<R::AccountId>>,
{
fn execute(
input: &[u8],
target_gas: Option<u64>,
context: &Context,
) -> Result<PrecompileOutput, ExitError> {
const SELECTOR_SIZE_BYTES: usize = 4;

if input.len() < SELECTOR_SIZE_BYTES {
return Err(ExitError::Other("input length less than 4 bytes".into()));
}

// ======= Staking.sol:Staking =======
// Function signatures:
// bcb24ddc: set_keys(bytes)
// 321c9b7a: purge_keys()
// d09b6ba5: register_as_candidate()
let call = match input[0..SELECTOR_SIZE_BYTES] {
[0xbc, 0xb2, 0x4d, 0xdc] => {
if input.len() < SELECTOR_SIZE_BYTES + 32 * 2 {
return Err(ExitError::Other("input length less than 36 bytes".into()));
}
// Low level argument parsing
let len_offset = SELECTOR_SIZE_BYTES + 32;
let keys_offset = len_offset + 32;
let keys_len = sp_core::U256::from_big_endian(&input[len_offset..keys_offset]);
let keys = input[keys_offset..(keys_offset + keys_len.as_usize())].to_vec();
Self::set_keys(keys)?
}
[0x32, 0x1c, 0x9b, 0x7a] => Self::purge_keys(),
[0xd0, 0x9b, 0x6b, 0xa5] => Self::register_as_candidate(),
_ => {
return Err(ExitError::Other(
"No method at selector given selector".into(),
))
}
};

let info = call.get_dispatch_info();
if let Some(gas_limit) = target_gas {
let required_gas = R::GasWeightMapping::weight_to_gas(info.weight);
if required_gas > gas_limit {
return Err(ExitError::OutOfGas);
}
}

let origin = R::AddressMapping::into_account_id(context.caller);
let post_info = call
.dispatch(Some(origin).into())
.map_err(|_| ExitError::Other("Method call via EVM failed".into()))?;

let gas_used =
R::GasWeightMapping::weight_to_gas(post_info.actual_weight.unwrap_or(info.weight));
Ok(PrecompileOutput {
exit_status: ExitSucceed::Stopped,
cost: gas_used,
output: Default::default(),
logs: Default::default(),
})
}
}
21 changes: 12 additions & 9 deletions runtime/shibuya/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build = "build.rs"
[dependencies]
# third-party dependencies
codec = { package = "parity-scale-codec", version = "2.0", default-features = false, features = ["derive"] }
evm = { version="0.30.0", default-features=false, features=["with-codec"] }
evm = { version = "0.30.0", default-features = false, features = ["with-codec"] }
smallvec = "1.6.1"

# primitives
Expand All @@ -32,16 +32,16 @@ frame-executive = { git = "https://github.com/paritytech/substrate", branch = "p
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-ethereum = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm-precompile-bn128 = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm-precompile-dispatch = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm-precompile-modexp = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm-precompile-simple = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-evm-precompile-sha3fips = { git="https://github.com/PlasmNetwork/frontier", branch="polkadot-v0.9.9", default-features=false }
pallet-ethereum = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm-precompile-bn128 = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm-precompile-dispatch = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm-precompile-modexp = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm-precompile-simple = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-evm-precompile-sha3fips = { git = "https://github.com/PlasmNetwork/frontier", branch = "polkadot-v0.9.9", default-features = false }
pallet-identity = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false, features = ["historical"] }
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
Expand Down Expand Up @@ -69,8 +69,10 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", default-feature
xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "release-v0.9.9" }
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false , branch = "release-v0.9.9" }

# Astar pallets
pallet-block-reward = { path = "../../frame/block-reward", default-features = false }
pallet-custom-signatures = { path = "../../frame/custom-signatures", default-features = false }
pallet-precompile-staking = { path = "../../precompiles/staking", default-features = false }

[build-dependencies]
substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" }
Expand Down Expand Up @@ -110,6 +112,7 @@ std = [
"pallet-evm-precompile-sha3fips/std",
"pallet-identity/std",
"pallet-multisig/std",
"pallet-precompile-staking/std",
"pallet-session/std",
"pallet-utility/std",
"pallet-timestamp/std",
Expand Down
2 changes: 1 addition & 1 deletion runtime/shibuya/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("shibuya"),
impl_name: create_runtime_str!("shibuya"),
authoring_version: 1,
spec_version: 5,
spec_version: 6,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
14 changes: 11 additions & 3 deletions runtime/shibuya/src/precompiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use pallet_evm_precompile_dispatch::Dispatch;
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
use pallet_precompile_staking::Staking;
use sp_core::H160;
use sp_std::fmt::Debug;
use sp_std::marker::PhantomData;
Expand All @@ -23,7 +24,7 @@ impl<R> ShibuyaNetworkPrecompiles<R> {
/// Return all addresses that contain precompiles. This can be used to populate dummy code
/// under the precompile.
pub fn used_addresses<AccountId: From<H160>>() -> impl Iterator<Item = AccountId> {
sp_std::vec![1, 2, 3, 4, 5, 6, 7, 8, 1024, 1025, 1026]
sp_std::vec![1, 2, 3, 4, 5, 6, 7, 8, 1024, 1025, 1026, 20480]
.into_iter()
.map(|x| hash(x).into())
}
Expand All @@ -32,10 +33,15 @@ impl<R> ShibuyaNetworkPrecompiles<R> {
/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet
impl<R: pallet_evm::Config> PrecompileSet for ShibuyaNetworkPrecompiles<R>
impl<R> PrecompileSet for ShibuyaNetworkPrecompiles<R>
where
R::Call: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo + Decode,
R: pallet_evm::Config + pallet_session::Config + pallet_collator_selection::Config,
<R::Call as Dispatchable>::Origin: From<Option<R::AccountId>>,
R::Call: From<pallet_session::Call<R>>
+ From<pallet_collator_selection::Call<R>>
+ Dispatchable<PostInfo = PostDispatchInfo>
+ GetDispatchInfo
+ Decode,
{
fn execute(
address: H160,
Expand All @@ -57,6 +63,8 @@ where
a if a == hash(1024) => Some(Sha3FIPS256::execute(input, target_gas, context)),
a if a == hash(1025) => Some(Dispatch::<R>::execute(input, target_gas, context)),
a if a == hash(1026) => Some(ECRecoverPublicKey::execute(input, target_gas, context)),
// Astar precompiles (starts from 0x5000):
a if a == hash(20480) => Some(Staking::<R>::execute(input, target_gas, context)),
// Default
_ => None,
}
Expand Down

0 comments on commit ae46758

Please sign in to comment.