Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dispatch Lockdrop account - Precompile #1142

Merged
merged 22 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 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 @@ -299,6 +299,7 @@ pallet-evm-precompile-xvm = { path = "./precompiles/xvm", default-features = fal
pallet-evm-precompile-dapps-staking = { path = "./precompiles/dapps-staking", default-features = false }
pallet-evm-precompile-dapp-staking-v3 = { path = "./precompiles/dapp-staking-v3", default-features = false }
pallet-evm-precompile-unified-accounts = { path = "./precompiles/unified-accounts", default-features = false }
pallet-evm-precompile-dispatch-lockdrop = { path = "./precompiles/dispatch-lockdrop", default-features = false }

pallet-chain-extension-xvm = { path = "./chain-extensions/xvm", default-features = false }
pallet-chain-extension-assets = { path = "./chain-extensions/pallet-assets", default-features = false }
Expand Down
58 changes: 58 additions & 0 deletions precompiles/dispatch-lockdrop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[package]
name = "pallet-evm-precompile-dispatch-lockdrop"
description = "Evm Precompile to dispatch calls for lockdrop accounts"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
fp-evm = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
hex-literal = { workspace = true }
libsecp256k1 = { workspace = true, features = ["hmac", "static-context"] }
log = { workspace = true }
pallet-evm = { workspace = true }
pallet-evm-precompile-dispatch = { workspace = true }
parity-scale-codec = { workspace = true }
precompile-utils = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

[dev-dependencies]
astar-primitives = { workspace = true }
ethers = { workspace = true }
frame-system = { workspace = true }
pallet-balances = { workspace = true }
pallet-timestamp = { workspace = true }
pallet-utility = { workspace = true }
precompile-utils = { workspace = true, features = ["testing"] }
scale-info = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

[features]
default = ["std"]
std = [
"log/std",
"libsecp256k1/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-std/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"astar-primitives/std",
"precompile-utils/std",
"pallet-evm/std",
"pallet-balances/std",
"pallet-timestamp/std",
]
20 changes: 20 additions & 0 deletions precompiles/dispatch-lockdrop/DispatchLockdrop.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.8.0;

/**
* @title Dispatch Lockdrop calls interface.
*/

/// Interface to dispatch lockdrop calls precompiled contract
/// Pre-deployed at the address 0x0000000000000000000000000000000000005007
interface RescueLockdrop {
/**
* @dev Dispatch lockdrop call
* @param call - SCALE-encoded call arguments
* @param pubkey - full ECDSA pubkey 64 bytes
* @return boolean confirming whether the call got successfully dispatched
*/
function dispatch_lockdrop_call(
bytes calldata call,
bytes calldata pubkey
) external returns (bool);
}
130 changes: 130 additions & 0 deletions precompiles/dispatch-lockdrop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// This file is part of Astar.

// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// Astar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Astar is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Astar. If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use core::marker::PhantomData;
use fp_evm::PrecompileHandle;
use frame_support::pallet_prelude::IsType;
use frame_support::weights::Weight;
use frame_support::{codec::DecodeLimit as _, traits::Get};
use frame_support::{
dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo},
traits::ConstU32,
};
use frame_system::Config;
use pallet_evm::GasWeightMapping;
use pallet_evm_precompile_dispatch::DispatchValidateT;
use precompile_utils::prelude::{revert, BoundedBytes, RuntimeHelper, UnboundedBytes};
use precompile_utils::EvmResult;
use sp_core::{crypto::AccountId32, H160, H256};
use sp_io::hashing::keccak_256;
use sp_std::vec::Vec;

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

pub const LOG_TARGET: &str = "precompile::dispatch-lockdrop";

// ECDSA PublicKey
type ECDSAPublic = ConstU32<64>;

// `DecodeLimit` specifies the max depth a call can use when decoding, as unbounded depth
// can be used to overflow the stack.
// Default value is 8, which is the same as in XCM call decoding.
pub struct DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit = ConstU32<8>>(
PhantomData<(Runtime, DispatchValidator, DecodeLimit)>,
);

#[precompile_utils::precompile]
impl<Runtime, DispatchValidator, DecodeLimit>
DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit>
where
Runtime: pallet_evm::Config,
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<Runtime as Config>::AccountId: IsType<AccountId32>,
<Runtime as Config>::AccountId: From<[u8; 32]>,
DispatchValidator:
DispatchValidateT<<Runtime as Config>::AccountId, <Runtime as Config>::RuntimeCall>,
DecodeLimit: Get<u32>,
{
#[precompile::public("dispatch_lockdrop_call(bytes,bytes)")]
fn dispatch_lockdrop_call(
handle: &mut impl PrecompileHandle,
call: UnboundedBytes,
pubkey: BoundedBytes<ECDSAPublic>,
) -> EvmResult<bool> {
log::trace!(
target: LOG_TARGET,
"raw arguments: call: {:?}, pubkey: {:?}",
call,
pubkey
);

let caller: H160 = handle.context().caller.into();
let input: Vec<u8> = call.into();

// Record a fixed amount of weight to ensure there is no free execution
handle.record_cost(Runtime::GasWeightMapping::weight_to_gas(
Weight::from_parts(1_000_000_000u64, 0),
))?;

// Ensure that the caller matches the public key
if caller != Self::get_evm_address_from_pubkey(pubkey.as_bytes()) {
let message: &str = "caller does not match the public key";
log::trace!(target: LOG_TARGET, "{}", message);
return Err(revert(message));
}

// Derive the account id from the public key
let origin = Self::get_account_id_from_pubkey(pubkey.as_bytes())
.ok_or(revert("could not derive AccountId from pubkey"))?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to cover this case in unit tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a tests for it. Using the same pubkey & AccountId as from old pallet eth-sig


// Decode the call
let call = Runtime::RuntimeCall::decode_with_depth_limit(DecodeLimit::get(), &mut &*input)
.map_err(|_| revert("could not decode call"))?;

// Validate the call - ensure that the call is allowed in filter
DispatchValidator::validate_before_dispatch(&origin, &call)
.map_or_else(|| Ok(()), |_| Err(revert("invalid Call")))?;

// Dispatch the call and handle the cost
RuntimeHelper::<Runtime>::try_dispatch::<Runtime::RuntimeCall>(
handle,
Some(origin).into(),
call,
)?;

Ok(true)
}

fn get_account_id_from_pubkey(pubkey: &[u8]) -> Option<<Runtime as Config>::AccountId> {
libsecp256k1::PublicKey::parse_slice(pubkey, None)
.map(|k| sp_io::hashing::blake2_256(k.serialize_compressed().as_ref()).into())
.ok()
}

fn get_evm_address_from_pubkey(pubkey: &[u8]) -> H160 {
H160::from(H256::from_slice(&keccak_256(pubkey)))
}
}
Loading
Loading