diff --git a/Cargo.lock b/Cargo.lock index 26317f6937..48fa6aaae7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -690,7 +690,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -702,6 +702,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ + "block-padding 0.2.1", "generic-array 0.14.6", ] @@ -723,6 +724,12 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "blocking" version = "1.2.0" @@ -2260,7 +2267,7 @@ dependencies = [ "rlp", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "triehash", ] @@ -2303,7 +2310,7 @@ dependencies = [ "rlp", "scale-info", "serde", - "sha3", + "sha3 0.10.6", ] [[package]] @@ -2337,7 +2344,7 @@ dependencies = [ "environmental", "evm-core", "primitive-types", - "sha3", + "sha3 0.10.6", ] [[package]] @@ -4876,13 +4883,16 @@ dependencies = [ "pallet-evm-precompile-bn128", "pallet-evm-precompile-call-permit", "pallet-evm-precompile-collective", + "pallet-evm-precompile-conviction-voting", "pallet-evm-precompile-crowdloan-rewards", "pallet-evm-precompile-democracy", "pallet-evm-precompile-dispatch", "pallet-evm-precompile-modexp", "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-preimage", "pallet-evm-precompile-proxy", "pallet-evm-precompile-randomness", + "pallet-evm-precompile-referenda", "pallet-evm-precompile-relay-encoder", "pallet-evm-precompile-sha3fips", "pallet-evm-precompile-simple", @@ -4923,7 +4933,7 @@ dependencies = [ "scale-info", "serde", "session-keys-primitives", - "sha3", + "sha3 0.10.6", "smallvec", "sp-api", "sp-block-builder", @@ -5003,7 +5013,7 @@ dependencies = [ "clap", "libsecp256k1", "primitive-types", - "sha3", + "sha3 0.10.6", "sp-runtime", "tiny-bip39", "url", @@ -5229,7 +5239,7 @@ dependencies = [ "sc-network", "sc-utils", "serde", - "sha3", + "sha3 0.10.6", "sp-api", "sp-block-builder", "sp-blockchain", @@ -5255,7 +5265,7 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "serde", - "sha3", + "sha3 0.10.6", "sp-api", "sp-blockchain", "sp-io", @@ -5367,7 +5377,7 @@ dependencies = [ "scale-info", "serde", "session-keys-primitives", - "sha3", + "sha3 0.10.6", "smallvec", "sp-api", "sp-block-builder", @@ -5515,7 +5525,7 @@ dependencies = [ "serde", "serde_json", "session-keys-primitives", - "sha3", + "sha3 0.10.6", "sp-api", "sp-block-builder", "sp-blockchain", @@ -5651,13 +5661,16 @@ dependencies = [ "pallet-evm-precompile-bn128", "pallet-evm-precompile-call-permit", "pallet-evm-precompile-collective", + "pallet-evm-precompile-conviction-voting", "pallet-evm-precompile-crowdloan-rewards", "pallet-evm-precompile-democracy", "pallet-evm-precompile-dispatch", "pallet-evm-precompile-modexp", "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-preimage", "pallet-evm-precompile-proxy", "pallet-evm-precompile-randomness", + "pallet-evm-precompile-referenda", "pallet-evm-precompile-relay-encoder", "pallet-evm-precompile-sha3fips", "pallet-evm-precompile-simple", @@ -5697,7 +5710,7 @@ dependencies = [ "scale-info", "serde", "session-keys-primitives", - "sha3", + "sha3 0.10.6", "smallvec", "sp-api", "sp-block-builder", @@ -5761,7 +5774,7 @@ dependencies = [ "digest 0.10.5", "multihash-derive", "sha2 0.10.6", - "sha3", + "sha3 0.10.6", "unsigned-varint", ] @@ -6847,7 +6860,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "slices", "sp-core", "sp-io", @@ -6875,7 +6888,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "slices", "sp-core", "sp-io", @@ -6922,7 +6935,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "slices", "sp-core", "sp-io", @@ -6952,7 +6965,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "similar-asserts", "slices", "sp-core", @@ -6961,6 +6974,33 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-evm-precompile-conviction-voting" +version = "0.1.0" +dependencies = [ + "derive_more", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "log", + "num_enum", + "pallet-balances", + "pallet-conviction-voting", + "pallet-evm", + "pallet-timestamp", + "parity-scale-codec", + "precompile-utils", + "rustc-hex", + "scale-info", + "serde", + "sha3 0.9.1", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-evm-precompile-crowdloan-rewards" version = "0.6.0" @@ -6985,7 +7025,7 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7057,7 +7097,34 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm-precompile-preimage" +version = "0.1.0" +dependencies = [ + "derive_more", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "log", + "num_enum", + "pallet-balances", + "pallet-evm", + "pallet-preimage", + "pallet-timestamp", + "parity-scale-codec", + "precompile-utils", + "rustc-hex", + "scale-info", + "serde", + "sha3 0.9.1", "sp-core", "sp-io", "sp-runtime", @@ -7085,7 +7152,7 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7121,6 +7188,35 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-evm-precompile-referenda" +version = "0.1.0" +dependencies = [ + "derive_more", + "fp-evm", + "frame-support", + "frame-system", + "hex-literal", + "log", + "num_enum", + "pallet-balances", + "pallet-evm", + "pallet-preimage", + "pallet-referenda", + "pallet-scheduler", + "pallet-timestamp", + "parity-scale-codec", + "precompile-utils", + "rustc-hex", + "scale-info", + "serde", + "sha3 0.9.1", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-evm-precompile-relay-encoder" version = "0.1.0" @@ -7143,7 +7239,7 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7194,7 +7290,7 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7225,7 +7321,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7258,7 +7354,7 @@ dependencies = [ "rustc-hex", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-core", "sp-io", "sp-runtime", @@ -7290,7 +7386,7 @@ dependencies = [ "precompile-utils", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "slices", "sp-core", "sp-io", @@ -9651,7 +9747,7 @@ dependencies = [ "precompile-utils-macro", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "similar-asserts", "sp-core", "sp-io", @@ -9672,7 +9768,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "sha3", + "sha3 0.10.6", "sp-core", "sp-std", "syn", @@ -11924,6 +12020,18 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.0", +] + [[package]] name = "sha3" version = "0.10.6" @@ -12368,7 +12476,7 @@ dependencies = [ "byteorder", "digest 0.10.5", "sha2 0.10.6", - "sha3", + "sha3 0.10.6", "sp-std", "twox-hash", ] @@ -14729,7 +14837,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sha3", + "sha3 0.10.6", "sp-io", "sp-runtime", "sp-std", diff --git a/Cargo.toml b/Cargo.toml index 780934d6b8..ae51c346a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,11 +17,14 @@ members = [ "precompiles/batch", "precompiles/call-permit", "precompiles/collective", + "precompiles/conviction-voting", "precompiles/crowdloan-rewards", "precompiles/pallet-democracy", "precompiles/parachain-staking", + "precompiles/preimage", "precompiles/proxy", "precompiles/randomness", + "precompiles/referenda", "precompiles/relay-encoder", "precompiles/utils", "precompiles/utils/macro", diff --git a/precompiles/conviction-voting/Cargo.toml b/precompiles/conviction-voting/Cargo.toml new file mode 100644 index 0000000000..26214880c4 --- /dev/null +++ b/precompiles/conviction-voting/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "pallet-evm-precompile-conviction-voting" +authors = [ "PureStake" ] +description = "A Precompile to make pallet-conviction-voting calls encoding accessible to pallet-evm" +edition = "2021" +version = "0.1.0" + +[dependencies] +log = "0.4" +num_enum = { version = "0.5.3", default-features = false } +rustc-hex = { version = "2.0.1", default-features = false } + +# Moonbeam +precompile-utils = { path = "../utils", default-features = false } + +# Substrate +frame-support = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +frame-system = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-conviction-voting = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +parity-scale-codec = { version = "3.0.0", default-features = false, features = [ "derive" ] } +sp-core = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-runtime = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-std = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +# Frontier +fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false, features = [ "forbid-evm-reentrancy" ] } + +[dev-dependencies] +derive_more = "0.99" +hex-literal = "0.3.3" +serde = "1.0.100" +sha3 = "0.9" + +# Moonbeam +precompile-utils = { path = "../utils", features = [ "testing" ] } + +# Substrate +pallet-balances = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +pallet-timestamp = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +scale-info = { version = "2.0", default-features = false, features = [ "derive" ] } +sp-io = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +[features] +default = [ "std" ] +std = [ + "fp-evm/std", + "frame-support/std", + "frame-system/std", + "pallet-conviction-voting/std", + "pallet-evm/std", + "parity-scale-codec/std", + "parity-scale-codec/std", + "precompile-utils/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/precompiles/conviction-voting/ConvictionVoting.sol b/precompiles/conviction-voting/ConvictionVoting.sol new file mode 100644 index 0000000000..a35670b31d --- /dev/null +++ b/precompiles/conviction-voting/ConvictionVoting.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// @dev The Conviction Voting contract's address. +address constant Conviction_Voting_ADDRESS = 0x0000000000000000000000000000000000000812; + +/// @dev The Conviction Voting contract's instance. +ConvictionVoting constant Conviction_Voting_CONTRACT = ConvictionVoting( + Conviction_Voting_ADDRESS +); + +/// @author The Moonbeam Team +/// @title Pallet Conviction Voting Interface +/// @title The interface through which solidity contracts will interact with the Conviction Voting pallet +/// @custom:address 0x0000000000000000000000000000000000000812 +interface ConvictionVoting { + /// @dev Defines the conviction multiplier type. + /// The values start at `0` and are represented as `uint8`. + /// None => 0.1x votes, unlocked. + /// Locked1x => 1x votes, locked for an enactment period following a successful vote. + /// Locked2x => 2x votes, locked for 2x enactment periods following a successful vote + /// Locked3x => 3x votes, locked for 4x... + /// Locked4x => 4x votes, locked for 8x..., + /// Locked5x => 5x votes, locked for 16x... + /// Locked6x => 6x votes, locked for 32x... + enum Conviction { + None, + Locked1x, + Locked2x, + Locked3x, + Locked4x, + Locked5x, + Locked6x + } + + /// @dev Vote yes in a poll. + /// @custom:selector da9df518 + /// @param pollIndex Index of poll + /// @param voteAmount Balance locked for vote + /// @param conviction Conviction multiplier for length of vote lock + function voteYes( + uint32 pollIndex, + uint256 voteAmount, + Conviction conviction + ) external; + + /// @dev Vote no in a poll. + /// @custom:selector cc600eba + /// @param pollIndex Index of poll + /// @param voteAmount Balance locked for vote + /// @param conviction Conviction multiplier for length of vote lock + function voteNo( + uint32 pollIndex, + uint256 voteAmount, + Conviction conviction + ) external; + + /// @dev Remove vote in poll + /// @custom:selector 79cae220 + /// @param pollIndex Index of the poll + function removeVote(uint32 pollIndex) external; + + /// @dev Remove vote in poll for other voter + /// @custom:selector cbcb9276 + //// @param target The voter to have vote removed. The removed vote must already be expired. + /// @param trackId The trackId + /// @param pollIndex the poll index + function removeOtherVote( + address target, + uint16 trackId, + uint32 pollIndex + ) external; + + /// @dev Delegate to a representative for the vote trackId + /// @custom:selector 681750e8 + /// @param trackId The trackId + /// @param representative The representative for the trackId + /// @param conviction The conviction multiplier + /// @param amount delegated to representative for this vote trackId + function delegate( + uint16 trackId, + address representative, + Conviction conviction, + uint256 amount + ) external; + + /// @dev Undelegate for the trackId + /// @custom:selector 98be4094 + /// @param trackId The trackId + function undelegate(uint16 trackId) external; + + /// @dev Unlock tokens locked for trackId + /// @custom:selector 4259d98c + /// @param trackId The trackId + /// @param target The target address + function unlock(uint16 trackId, address target) external; +} diff --git a/precompiles/conviction-voting/src/lib.rs b/precompiles/conviction-voting/src/lib.rs new file mode 100644 index 0000000000..90bedd252c --- /dev/null +++ b/precompiles/conviction-voting/src/lib.rs @@ -0,0 +1,292 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +use fp_evm::PrecompileHandle; +use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; +use frame_support::traits::{Currency, Polling}; +use pallet_conviction_voting::Call as ConvictionVotingCall; +use pallet_conviction_voting::{AccountVote, Conviction, Tally, Vote}; +use pallet_evm::AddressMapping; +use precompile_utils::prelude::*; +use sp_core::{H160, U256}; +use sp_runtime::traits::StaticLookup; +use sp_std::marker::PhantomData; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; +type IndexOf = <::Polls as Polling< + Tally< + <::Currency as Currency< + ::AccountId, + >>::Balance, + ::MaxTurnout, + >, +>>::Index; +type ClassOf = <::Polls as Polling< + Tally< + <::Currency as Currency< + ::AccountId, + >>::Balance, + ::MaxTurnout, + >, +>>::Class; + +/// Direction of vote +enum VoteDirection { + Yes, + No, + #[allow(unused)] + Abstain, +} + +/// A precompile to wrap the functionality from pallet-conviction-voting. +pub struct ConvictionVotingPrecompile(PhantomData); + +#[precompile_utils::precompile] +impl ConvictionVotingPrecompile +where + Runtime: pallet_conviction_voting::Config + pallet_evm::Config + frame_system::Config, + BalanceOf: TryFrom, + ::RuntimeCall: + Dispatchable + GetDispatchInfo, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + From>, + ::RuntimeCall: From>, + IndexOf: TryFrom, + ClassOf: TryFrom, +{ + /// Internal helper function for vote* extrinsics exposed in this precompile. + fn vote( + handle: &mut impl PrecompileHandle, + poll_index: u32, + vote: VoteDirection, + vote_amount: U256, + conviction: u8, + ) -> EvmResult { + let poll_index = Self::u32_to_index(poll_index).in_field("pollIndex")?; + let vote_amount = Self::u256_to_amount(vote_amount).in_field("voteAmount")?; + let conviction = Self::u8_to_conviction(conviction).in_field("conviction")?; + + let aye = match vote { + VoteDirection::Yes => true, + VoteDirection::No => false, + _ => return Err(RevertReason::custom("Abstain not supported").into()), + }; + + let vote = AccountVote::Standard { + vote: Vote { aye, conviction }, + balance: vote_amount, + }; + + log::trace!(target: "conviction-voting-precompile", + "Voting {:?} on poll {:?}, with conviction {:?}", + aye, poll_index, conviction + ); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::vote { poll_index, vote }.into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + /// Vote yes in a poll. + /// + /// Parameters: + /// * poll_index: Index of poll + /// * vote_amount: Balance locked for vote + /// * conviction: Conviction multiplier for length of vote lock + #[precompile::public("voteYes(uint32,uint256,uint8)")] + fn vote_yes( + handle: &mut impl PrecompileHandle, + poll_index: u32, + vote_amount: U256, + conviction: u8, + ) -> EvmResult { + Self::vote( + handle, + poll_index, + VoteDirection::Yes, + vote_amount, + conviction, + ) + } + + /// Vote no in a poll. + /// + /// Parameters: + /// * poll_index: Index of poll + /// * vote_amount: Balance locked for vote + /// * conviction: Conviction multiplier for length of vote lock + #[precompile::public("voteNo(uint32,uint256,uint8)")] + fn vote_no( + handle: &mut impl PrecompileHandle, + poll_index: u32, + vote_amount: U256, + conviction: u8, + ) -> EvmResult { + Self::vote( + handle, + poll_index, + VoteDirection::No, + vote_amount, + conviction, + ) + } + + #[precompile::public("removeVote(uint32)")] + fn remove_vote(handle: &mut impl PrecompileHandle, poll_index: u32) -> EvmResult { + let index = Self::u32_to_index(poll_index).in_field("pollIndex")?; + + log::trace!( + target: "conviction-voting-precompile", + "Removing vote from poll {:?}", + index + ); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::remove_vote { class: None, index }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + #[precompile::public("removeOtherVote(address,uint16,uint32)")] + fn remove_other_vote( + handle: &mut impl PrecompileHandle, + target: Address, + track_id: u16, + poll_index: u32, + ) -> EvmResult { + let class = Self::u16_to_track_id(track_id).in_field("trackId")?; + let index = Self::u32_to_index(poll_index).in_field("pollIndex")?; + + let target = Runtime::AddressMapping::into_account_id(target.into()); + let target: ::Source = + Runtime::Lookup::unlookup(target.clone()); + + log::trace!( + target: "conviction-voting-precompile", + "Removing other vote from poll {:?}", + index + ); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::remove_other_vote { + target, + class, + index, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + #[precompile::public("delegate(uint16,address,uint8,uint256)")] + fn delegate( + handle: &mut impl PrecompileHandle, + track_id: u16, + representative: Address, + conviction: u8, + amount: U256, + ) -> EvmResult { + let class = Self::u16_to_track_id(track_id).in_field("trackId")?; + let amount = Self::u256_to_amount(amount).in_field("amount")?; + let conviction = Self::u8_to_conviction(conviction).in_field("conviction")?; + + log::trace!(target: "conviction-voting-precompile", + "Delegating vote to {:?} with balance {:?} and conviction {:?}", + representative, amount, conviction + ); + + let representative = Runtime::AddressMapping::into_account_id(representative.into()); + let to: ::Source = + Runtime::Lookup::unlookup(representative.clone()); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::delegate { + class, + to, + conviction, + balance: amount, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + #[precompile::public("undelegate(uint16)")] + fn undelegate(handle: &mut impl PrecompileHandle, track_id: u16) -> EvmResult { + let class = Self::u16_to_track_id(track_id).in_field("trackId")?; + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::undelegate { class }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + #[precompile::public("unlock(uint16,address)")] + fn unlock(handle: &mut impl PrecompileHandle, track_id: u16, target: Address) -> EvmResult { + let class = Self::u16_to_track_id(track_id).in_field("trackId")?; + let target: H160 = target.into(); + let target = Runtime::AddressMapping::into_account_id(target); + let target: ::Source = + Runtime::Lookup::unlookup(target.clone()); + + log::trace!( + target: "conviction-voting-precompile", + "Unlocking conviction-voting tokens for {:?}", target + ); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let call = ConvictionVotingCall::::unlock { class, target }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + fn u8_to_conviction(conviction: u8) -> MayRevert { + conviction + .try_into() + .map_err(|_| RevertReason::custom("Must be an integer between 0 and 6 included").into()) + } + fn u32_to_index(index: u32) -> MayRevert> { + index + .try_into() + .map_err(|_| RevertReason::value_is_too_large("index type").into()) + } + fn u16_to_track_id(class: u16) -> MayRevert> { + class + .try_into() + .map_err(|_| RevertReason::value_is_too_large("trackId type").into()) + } + fn u256_to_amount(value: U256) -> MayRevert> { + value + .try_into() + .map_err(|_| RevertReason::value_is_too_large("balance type").into()) + } +} diff --git a/precompiles/conviction-voting/src/mock.rs b/precompiles/conviction-voting/src/mock.rs new file mode 100644 index 0000000000..fbbc8b86ff --- /dev/null +++ b/precompiles/conviction-voting/src/mock.rs @@ -0,0 +1,249 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Test utilities +use super::*; +use frame_support::{ + construct_runtime, parameter_types, + traits::{Everything, PollStatus, Polling, TotalIssuanceOf}, + weights::Weight, +}; +use pallet_conviction_voting::TallyOf; +use pallet_evm::{EnsureAddressNever, EnsureAddressRoot}; +use precompile_utils::{precompile_set::*, testing::MockAccount}; +use sp_core::{H256, U256}; +use sp_runtime::{ + traits::{BlakeTwo256, ConstU32, IdentityLookup}, + DispatchError, Perbill, +}; +use sp_std::collections::btree_map::BTreeMap; + +pub type AccountId = MockAccount; +pub type Balance = u128; +pub type BlockNumber = u32; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + ConvictionVoting: pallet_conviction_voting, + } +); + +parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} +impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub BlockGasLimit: U256 = U256::max_value(); + pub PrecompilesValue: Precompiles = Precompiles::new(); + pub const WeightPerGas: Weight = Weight::from_ref_time(1); +} + +pub type Precompiles = + PrecompileSetBuilder, ConvictionVotingPrecompile>,)>; + +pub type PCall = ConvictionVotingPrecompileCall; + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = AccountId; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = Precompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = (); + type OnChargeTransaction = (); + type BlockGasLimit = BlockGasLimit; + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum TestPollState { + Ongoing(TallyOf, u8), + Completed(u64, bool), +} +use TestPollState::*; + +parameter_types! { + pub static Polls: BTreeMap = vec![ + (1, Completed(1, true)), + (2, Completed(2, false)), + (3, Ongoing(Tally::from_parts(0, 0, 0), 0)), + ].into_iter().collect(); +} + +pub struct TestPolls; +impl Polling> for TestPolls { + type Index = u8; + type Votes = u128; + type Moment = u32; + type Class = u8; + fn classes() -> Vec { + vec![0, 1, 2] + } + fn as_ongoing(index: u8) -> Option<(TallyOf, Self::Class)> { + Polls::get().remove(&index).and_then(|x| { + if let TestPollState::Ongoing(t, c) = x { + Some((t, c)) + } else { + None + } + }) + } + fn access_poll( + index: Self::Index, + f: impl FnOnce(PollStatus<&mut TallyOf, u32, u8>) -> R, + ) -> R { + let mut polls = Polls::get(); + let entry = polls.get_mut(&index); + let r = match entry { + Some(Ongoing(ref mut tally_mut_ref, class)) => { + f(PollStatus::Ongoing(tally_mut_ref, *class)) + } + Some(Completed(when, succeeded)) => f(PollStatus::Completed( + (*when).try_into().unwrap(), + *succeeded, + )), + None => f(PollStatus::None), + }; + Polls::set(polls); + r + } + fn try_access_poll( + index: Self::Index, + f: impl FnOnce(PollStatus<&mut TallyOf, u32, u8>) -> Result, + ) -> Result { + let mut polls = Polls::get(); + let entry = polls.get_mut(&index); + let r = match entry { + Some(Ongoing(ref mut tally_mut_ref, class)) => { + f(PollStatus::Ongoing(tally_mut_ref, *class)) + } + Some(Completed(when, succeeded)) => f(PollStatus::Completed( + (*when).try_into().unwrap(), + *succeeded, + )), + None => f(PollStatus::None), + }?; + Polls::set(polls); + Ok(r) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_ongoing(class: Self::Class) -> Result { + let mut polls = Polls::get(); + let i = polls.keys().rev().next().map_or(0, |x| x + 1); + polls.insert(i, Ongoing(Tally::new(0), class)); + Polls::set(polls); + Ok(i) + } + + #[cfg(feature = "runtime-benchmarks")] + fn end_ongoing(index: Self::Index, approved: bool) -> Result<(), ()> { + let mut polls = Polls::get(); + match polls.get(&index) { + Some(Ongoing(..)) => {} + _ => return Err(()), + } + let now = frame_system::Pallet::::block_number(); + polls.insert(index, Completed(now, approved)); + Polls::set(polls); + Ok(()) + } +} + +impl pallet_conviction_voting::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = pallet_balances::Pallet; + type VoteLockingPeriod = ConstU32<3>; + type MaxVotes = ConstU32<3>; + type WeightInfo = (); + type MaxTurnout = TotalIssuanceOf; + type Polls = TestPolls; +} + +// genesis builder TODO diff --git a/precompiles/conviction-voting/src/tests.rs b/precompiles/conviction-voting/src/tests.rs new file mode 100644 index 0000000000..e0bfa7b747 --- /dev/null +++ b/precompiles/conviction-voting/src/tests.rs @@ -0,0 +1,42 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . +use crate::mock::*; +use precompile_utils::testing::*; + +#[test] +fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() { + for file in ["ConvictionVoting.sol"] { + for solidity_fn in solidity::get_selectors(file) { + assert_eq!( + solidity_fn.compute_selector_hex(), + solidity_fn.docs_selector, + "documented selector for '{}' did not match for file '{}'", + solidity_fn.signature(), + file, + ); + + let selector = solidity_fn.compute_selector(); + if !PCall::supports_selector(selector) { + panic!( + "failed decoding selector 0x{:x} => '{}' as Action for file '{}'", + selector, + solidity_fn.signature(), + file, + ) + } + } + } +} diff --git a/precompiles/preimage/Cargo.toml b/precompiles/preimage/Cargo.toml new file mode 100644 index 0000000000..c84fcf3ee1 --- /dev/null +++ b/precompiles/preimage/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "pallet-evm-precompile-preimage" +authors = [ "PureStake" ] +description = "A Precompile to make pallet-preimage calls encoding accessible to pallet-evm" +edition = "2021" +version = "0.1.0" + +[dependencies] +log = "0.4" +num_enum = { version = "0.5.3", default-features = false } +rustc-hex = { version = "2.0.1", default-features = false } + +# Moonbeam +precompile-utils = { path = "../utils", default-features = false } + +# Substrate +frame-support = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +frame-system = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-preimage = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +parity-scale-codec = { version = "3.0.0", default-features = false, features = [ "derive" ] } +sp-core = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-runtime = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-std = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +# Frontier +fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false, features = [ "forbid-evm-reentrancy" ] } + +[dev-dependencies] +derive_more = "0.99" +hex-literal = "0.3.3" +serde = "1.0.100" +sha3 = "0.9" + +# Moonbeam +precompile-utils = { path = "../utils", features = [ "testing" ] } + +# Substrate +pallet-balances = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +pallet-timestamp = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +scale-info = { version = "2.0", default-features = false, features = [ "derive" ] } +sp-io = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +[features] +default = [ "std" ] +std = [ + "fp-evm/std", + "frame-support/std", + "frame-system/std", + "pallet-evm/std", + "pallet-preimage/std", + "parity-scale-codec/std", + "parity-scale-codec/std", + "precompile-utils/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/precompiles/preimage/Preimage.sol b/precompiles/preimage/Preimage.sol new file mode 100644 index 0000000000..52ce716914 --- /dev/null +++ b/precompiles/preimage/Preimage.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// @dev The Preimage contract's address. +address constant Preimage_ADDRESS = 0x0000000000000000000000000000000000000813; + +/// @dev The Preimage contract's instance. +Preimage constant Preimage_CONTRACT = Preimage(Preimage_ADDRESS); + +/// @author The Moonbeam Team +/// @title Pallet Preimage Interface +/// @title The interface through which solidity contracts will interact with the Preimage pallet +/// @custom:address 0x0000000000000000000000000000000000000813 +interface Preimage { + /// @dev Register a Preimage on-chain. + /// @custom:selector cb00f603 + /// @param encodedProposal The preimage to be registered on-chain + function notePreimage(bytes memory encodedProposal) external; + + /// @dev Clear an unrequested preimage from storage. + /// @custom:selector 02e71b45 + /// @param hash The preimage to be cleared from storage + function unnotePreimage(bytes32 hash) external; +} diff --git a/precompiles/preimage/src/lib.rs b/precompiles/preimage/src/lib.rs new file mode 100644 index 0000000000..58d7145ef4 --- /dev/null +++ b/precompiles/preimage/src/lib.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +use fp_evm::PrecompileHandle; +use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; +use frame_support::traits::ConstU32; +use pallet_evm::AddressMapping; +use pallet_preimage::Call as PreimageCall; +use precompile_utils::prelude::*; +use sp_core::H256; +use sp_std::marker::PhantomData; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub const ENCODED_PROPOSAL_SIZE_LIMIT: u32 = 2u32.pow(16); +type GetEncodedProposalSizeLimit = ConstU32; + +/// A precompile to wrap the functionality from pallet-preimage. +pub struct PreimagePrecompile(PhantomData); + +#[precompile_utils::precompile] +impl PreimagePrecompile +where + Runtime: pallet_preimage::Config + pallet_evm::Config + frame_system::Config, + ::Hash: TryFrom, + ::RuntimeCall: + Dispatchable + GetDispatchInfo, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + From>, + ::RuntimeCall: From>, +{ + /// Register a preimage on-chain. + /// + /// Parameters: + /// * encoded_proposal: The preimage registered on-chain + #[precompile::public("notePreimage(bytes)")] + fn note_preimage( + handle: &mut impl PrecompileHandle, + encoded_proposal: BoundedBytes, + ) -> EvmResult { + let bytes = encoded_proposal.into(); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = PreimageCall::::note_preimage { bytes }.into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + /// Clear an unrequested preimage from the runtime storage. + /// + /// Parameters: + /// * hash: The preimage cleared from storage + #[precompile::public("unnotePreimage(bytes32)")] + fn unnote_preimage(handle: &mut impl PrecompileHandle, hash: H256) -> EvmResult { + let hash: Runtime::Hash = hash + .try_into() + .map_err(|_| RevertReason::custom("H256 is Runtime::Hash").in_field("hash"))?; + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = PreimageCall::::unnote_preimage { hash }.into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } +} diff --git a/precompiles/preimage/src/mock.rs b/precompiles/preimage/src/mock.rs new file mode 100644 index 0000000000..f81e10ad66 --- /dev/null +++ b/precompiles/preimage/src/mock.rs @@ -0,0 +1,148 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Test utilities +use super::*; +use frame_support::{construct_runtime, parameter_types, traits::Everything, weights::Weight}; +use frame_system::EnsureRoot; +use pallet_evm::{EnsureAddressNever, EnsureAddressRoot}; +use precompile_utils::{precompile_set::*, testing::MockAccount}; +use sp_core::{H256, U256}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +pub type AccountId = MockAccount; +pub type Balance = u128; +pub type BlockNumber = u32; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Preimage: pallet_preimage, + } +); + +parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} +impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub BlockGasLimit: U256 = U256::max_value(); + pub PrecompilesValue: Precompiles = Precompiles::new(); + pub const WeightPerGas: Weight = Weight::from_ref_time(1); +} + +pub type Precompiles = + PrecompileSetBuilder, PreimagePrecompile>,)>; + +pub type PCall = PreimagePrecompileCall; + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = AccountId; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = Precompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = (); + type OnChargeTransaction = (); + type BlockGasLimit = BlockGasLimit; + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_preimage::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type BaseDeposit = ExistentialDeposit; + type ByteDeposit = ExistentialDeposit; +} + +// genesis builder TODO diff --git a/precompiles/preimage/src/tests.rs b/precompiles/preimage/src/tests.rs new file mode 100644 index 0000000000..8508f747d7 --- /dev/null +++ b/precompiles/preimage/src/tests.rs @@ -0,0 +1,42 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . +use crate::mock::*; +use precompile_utils::testing::*; + +#[test] +fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() { + for file in ["Preimage.sol"] { + for solidity_fn in solidity::get_selectors(file) { + assert_eq!( + solidity_fn.compute_selector_hex(), + solidity_fn.docs_selector, + "documented selector for '{}' did not match for file '{}'", + solidity_fn.signature(), + file, + ); + + let selector = solidity_fn.compute_selector(); + if !PCall::supports_selector(selector) { + panic!( + "failed decoding selector 0x{:x} => '{}' as Action for file '{}'", + selector, + solidity_fn.signature(), + file, + ) + } + } + } +} diff --git a/precompiles/referenda/Cargo.toml b/precompiles/referenda/Cargo.toml new file mode 100644 index 0000000000..d91094f1f9 --- /dev/null +++ b/precompiles/referenda/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-evm-precompile-referenda" +authors = [ "PureStake" ] +description = "A Precompile to make pallet-referenda calls encoding accessible to pallet-evm" +edition = "2021" +version = "0.1.0" + +[dependencies] +log = "0.4" +num_enum = { version = "0.5.3", default-features = false } +rustc-hex = { version = "2.0.1", default-features = false } + +# Moonbeam +precompile-utils = { path = "../utils", default-features = false } + +# Substrate +frame-support = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +frame-system = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-referenda = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +parity-scale-codec = { version = "3.0.0", default-features = false, features = [ "derive" ] } +sp-core = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-runtime = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +sp-std = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +# Frontier +fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false } +pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.32", default-features = false, features = [ "forbid-evm-reentrancy" ] } + +[dev-dependencies] +derive_more = "0.99" +hex-literal = "0.3.3" +pallet-preimage = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +pallet-scheduler = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +serde = "1.0.100" +sha3 = "0.9" + +# Moonbeam +precompile-utils = { path = "../utils", features = [ "testing" ] } + +# Substrate +pallet-balances = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +pallet-timestamp = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32" } +scale-info = { version = "2.0", default-features = false, features = [ "derive" ] } +sp-io = { git = "https://github.com/purestake/substrate", branch = "moonbeam-polkadot-v0.9.32", default-features = false } + +[features] +default = [ "std" ] +std = [ + "fp-evm/std", + "frame-support/std", + "frame-system/std", + "pallet-evm/std", + "pallet-referenda/std", + "parity-scale-codec/std", + "parity-scale-codec/std", + "precompile-utils/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/precompiles/referenda/Referenda.sol b/precompiles/referenda/Referenda.sol new file mode 100644 index 0000000000..5039ba767a --- /dev/null +++ b/precompiles/referenda/Referenda.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// @dev The Referenda contract's address. +address constant REFERENDA_ADDRESS = 0x0000000000000000000000000000000000000811; + +/// @dev The Referenda contract's instance. +Referenda constant REFERENDA_CONTRACT = Referenda(REFERENDA_ADDRESS); + +/// @author The Moonbeam Team +/// @title Pallet Referenda Interface +/// @title The interface through which solidity contracts will interact with the Referenda pallet +/// @custom:address 0x0000000000000000000000000000000000000811 +interface Referenda { + /// Return the total referendum count + /// @custom:selector 3a42ee31 + function referendumCount() external view returns (uint256); + + /// Return the submission deposit for all referenda + /// @custom:selector aa14c39a + function submissionDeposit() external view returns (uint256); + + /// Return the total count of deciding referenda per track + /// @param trackId The track identifier + /// @custom:selector 983d6425 + function decidingCount(uint16 trackId) external view returns (uint256); + + /// Return the trackIds + /// @return trackIds Identifiers for all tracks (and origins) + /// @custom:selector cc17da14 + function trackIds() external view returns (uint16[] memory trackIds); + + /// Return the governance parameters configured for the input TrackId + /// @param trackId The track identifier + /// @custom:selector 34038146 + function trackInfo(uint16 trackId) + external + view + returns ( + string memory, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + bytes memory, + bytes memory + ); + + /// @dev Submit a referenda + /// @custom:selector 95f9ed68 + /// @param trackId The trackId corresponding to the origin from which the proposal is to be + /// dispatched. The trackId => origin mapping lives in `runtime/governance/tracks.rs` + /// @param hash Hash of the proposal preimage + /// @param block Block number at which this will be executed + function submitAt( + uint16 trackId, + bytes memory hash, + uint32 block + ) external; + + /// @dev Submit a referenda + /// @custom:selector 0a1ecbe9 + /// @param trackId The trackId corresponding to the origin from which the proposal is to be + /// dispatched. The trackId => origin mapping lives in `runtime/governance/tracks.rs` + /// @param hash Hash of the proposal preimage + /// @param block Block number after which this will be executed + function submitAfter( + uint16 trackId, + bytes memory hash, + uint32 block + ) external; + + /// @dev Post the Decision Deposit for a referendum + /// @custom:selector 245ce18d + /// @param index The index of the ongoing referendum that is not yet deciding + function placeDecisionDeposit(uint32 index) external; + + /// @dev Refund the Decision Deposit for a closed referendum back to the depositor + /// @custom:selector 1325d528 + /// @param index The index of a closed referendum with decision deposit still locked + function refundDecisionDeposit(uint32 index) external; +} diff --git a/precompiles/referenda/src/lib.rs b/precompiles/referenda/src/lib.rs new file mode 100644 index 0000000000..7c7abda8af --- /dev/null +++ b/precompiles/referenda/src/lib.rs @@ -0,0 +1,275 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +use fp_evm::PrecompileHandle; +use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; +use frame_support::traits::{ + schedule::DispatchTime, Bounded, ConstU32, Currency, Get, OriginTrait, +}; +use pallet_evm::AddressMapping; +use pallet_referenda::{Call as ReferendaCall, DecidingCount, ReferendumCount, TracksInfo}; +use parity_scale_codec::Encode; +use precompile_utils::prelude::*; +use sp_core::U256; +use sp_std::{boxed::Box, marker::PhantomData, vec::Vec}; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub const CALL_DATA_LIMIT: u32 = 2u32.pow(16); + +type GetCallDataLimit = ConstU32; +type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; +type TrackIdOf = <::Tracks as TracksInfo< + BalanceOf, + ::BlockNumber, +>>::Id; +type BoundedCallOf = Bounded<::RuntimeCall>; + +type OriginOf = + <::RuntimeOrigin as OriginTrait>::PalletsOrigin; + +/// A precompile to wrap the functionality from pallet-referenda. +pub struct ReferendaPrecompile>(PhantomData<(Runtime, GovOrigin)>); + +#[precompile_utils::precompile] +impl ReferendaPrecompile +where + Runtime: pallet_referenda::Config + pallet_evm::Config + frame_system::Config, + OriginOf: From, + ::RuntimeCall: + Dispatchable + GetDispatchInfo, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + From>, + ::RuntimeCall: From>, + Runtime::BlockNumber: Into, + TrackIdOf: TryFrom + TryInto, + BalanceOf: Into, + GovOrigin: TryFrom, +{ + // The accessors are first. They directly return their result. + #[precompile::public("referendumCount()")] + #[precompile::view] + fn referendum_count(handle: &mut impl PrecompileHandle) -> EvmResult { + // Fetch data from pallet + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let ref_count = ReferendumCount::::get(); + log::trace!(target: "referendum-precompile", "Referendum count is {:?}", ref_count); + + Ok(ref_count.into()) + } + + #[precompile::public("submissionDeposit()")] + #[precompile::view] + fn submission_deposit(handle: &mut impl PrecompileHandle) -> EvmResult { + // Fetch data from pallet + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let submission_deposit = Runtime::SubmissionDeposit::get(); + log::trace!(target: "referendum-precompile", "Submission deposit is {:?}", submission_deposit); + + Ok(submission_deposit.into()) + } + + #[precompile::public("decidingCount(uint16)")] + #[precompile::view] + fn deciding_count(handle: &mut impl PrecompileHandle, track_id: u16) -> EvmResult { + // Fetch data from pallet + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let track_id: TrackIdOf = track_id + .try_into() + .map_err(|_| RevertReason::value_is_too_large("Track id type").into()) + .in_field("trackId")?; + let deciding_count = DecidingCount::::get(track_id); + log::trace!( + target: "referendum-precompile", "Track {:?} deciding count is {:?}", + track_id, + deciding_count + ); + + Ok(deciding_count.into()) + } + + #[precompile::public("trackIds()")] + #[precompile::view] + fn track_ids(handle: &mut impl PrecompileHandle) -> EvmResult> { + // Fetch data from runtime + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let track_ids: Vec = Runtime::Tracks::tracks() + .into_iter() + .filter_map(|x| { + if let Ok(track_id) = x.0.try_into() { + Some(track_id) + } else { + None + } + }) + .collect(); + + Ok(track_ids) + } + + #[precompile::public("trackInfo(uint16)")] + #[precompile::view] + fn track_info( + handle: &mut impl PrecompileHandle, + track_id: u16, + ) -> EvmResult<( + UnboundedBytes, + U256, + U256, + U256, + U256, + U256, + U256, + UnboundedBytes, + UnboundedBytes, + )> { + // Fetch data from runtime + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let track_id: TrackIdOf = track_id + .try_into() + .map_err(|_| RevertReason::value_is_too_large("Track id type").into()) + .in_field("trackId")?; + let tracks = Runtime::Tracks::tracks(); + let index = tracks + .binary_search_by_key(&track_id, |x| x.0) + .unwrap_or_else(|x| x); + let track_info = &tracks[index].1; + + Ok(( + track_info.name.as_bytes().into(), + track_info.max_deciding.into(), + track_info.decision_deposit.into(), + track_info.prepare_period.into(), + track_info.decision_period.into(), + track_info.confirm_period.into(), + track_info.min_enactment_period.into(), + track_info.min_approval.encode().into(), + track_info.min_support.encode().into(), + )) + } + + /// Propose a referendum on a privileged action. + /// + /// Parameters: + /// * track_id: The trackId for the origin from which the proposal is to be dispatched. + /// * proposal: The proposed runtime call. + /// * block_number: Block number at which proposal is dispatched. + #[precompile::public("submitAt(uint16,bytes,uint32)")] + fn submit_at( + handle: &mut impl PrecompileHandle, + track_id: u16, + proposal: BoundedBytes, + block_number: u32, + ) -> EvmResult { + let proposal_origin: GovOrigin = track_id.try_into().map_err(|_| { + RevertReason::custom("Origin does not exist for TrackId").in_field("trackId") + })?; + let proposal_origin: Box> = Box::new(proposal_origin.into()); + let proposal: BoundedCallOf = Bounded::Inline( + frame_support::BoundedVec::try_from(proposal.as_bytes().to_vec()).map_err(|_| { + RevertReason::custom("Proposal input is not a runtime call").in_field("proposal") + })?, + ); + let enactment_moment = DispatchTime::At(block_number.into()); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = ReferendaCall::::submit { + proposal_origin, + proposal, + enactment_moment, + } + .into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + /// Propose a referendum on a privileged action. + /// + /// Parameters: + /// * track_id: The trackId for the origin from which the proposal is to be dispatched. + /// * proposal: The proposed runtime call. + /// * block_number: Block number after which proposal is dispatched. + #[precompile::public("submitAfter(uint16,bytes,uint32)")] + fn submit_after( + handle: &mut impl PrecompileHandle, + track_id: u16, + proposal: BoundedBytes, + block_number: u32, + ) -> EvmResult { + let origin: GovOrigin = track_id.try_into().map_err(|_| { + RevertReason::custom("Origin does not exist for TrackId").in_field("trackId") + })?; + let proposal_origin: Box> = Box::new(origin.into()); + let proposal: BoundedCallOf = Bounded::Inline( + frame_support::BoundedVec::try_from(proposal.as_bytes().to_vec()).map_err(|_| { + RevertReason::custom("Proposal input is not a runtime call").in_field("proposal") + })?, + ); + let enactment_moment = DispatchTime::After(block_number.into()); + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = ReferendaCall::::submit { + proposal_origin, + proposal, + enactment_moment, + } + .into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + /// Post the Decision Deposit for a referendum. + /// + /// Parameters: + /// * index: The index of the submitted referendum whose Decision Deposit is yet to be posted. + #[precompile::public("placeDecisionDeposit(uint32)")] + fn place_decision_deposit(handle: &mut impl PrecompileHandle, index: u32) -> EvmResult { + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = ReferendaCall::::place_decision_deposit { index }.into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + Ok(()) + } + + /// Refund the Decision Deposit for a closed referendum back to the depositor. + /// + /// Parameters: + /// * index: The index of a closed referendum whose Decision Deposit has not yet been refunded. + #[precompile::public("refundDecisionDeposit(uint32)")] + fn refund_decision_deposit(handle: &mut impl PrecompileHandle, index: u32) -> EvmResult { + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let call = ReferendaCall::::refund_decision_deposit { index }.into(); + + >::try_dispatch(handle, Some(origin).into(), call)?; + Ok(()) + } +} diff --git a/precompiles/referenda/src/mock.rs b/precompiles/referenda/src/mock.rs new file mode 100644 index 0000000000..146564e686 --- /dev/null +++ b/precompiles/referenda/src/mock.rs @@ -0,0 +1,381 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! A minimal precompile runtime including the pallet-randomness pallet +use super::*; +use frame_support::{ + construct_runtime, parameter_types, + traits::{ConstU32, EqualPrivilegeOnly, Everything, SortedMembers, VoteTally}, + weights::Weight, +}; +use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy, RawOrigin}; +use pallet_evm::{EnsureAddressNever, EnsureAddressRoot}; +use pallet_referenda::{impl_tracksinfo_get, Curve, TrackInfo}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use precompile_utils::{precompile_set::*, testing::*}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; +use sp_std::convert::{TryFrom, TryInto}; + +pub type AccountId = MockAccount; +pub type Balance = u128; +pub type BlockNumber = u32; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Evm: pallet_evm::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Preimage: pallet_preimage, + Scheduler: pallet_scheduler, + Referenda: pallet_referenda, + } +); + +parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const SS58Prefix: u8 = 42; +} +impl frame_system::Config for Runtime { + type BaseCallFilter = Everything; + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = BlockNumber; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = sp_runtime::generic::Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 0; +} +impl pallet_balances::Config for Runtime { + type MaxReserves = (); + type ReserveIdentifier = [u8; 4]; + type MaxLocks = (); + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +pub type TestPrecompiles = PrecompileSetBuilder< + R, + ( + PrecompileAt, ReferendaPrecompile, LimitRecursionTo<1>>, + RevertPrecompile>, + ), +>; + +pub type PCall = ReferendaPrecompileCall; + +parameter_types! { + pub PrecompilesValue: TestPrecompiles = TestPrecompiles::new(); + pub const WeightPerGas: Weight = Weight::from_ref_time(1); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = AccountId; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = TestPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = (); + type OnChargeTransaction = (); + type BlockGasLimit = (); + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type FindAuthor = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_preimage::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type BaseDeposit = (); + type ByteDeposit = (); +} +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumBlockWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<100>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +parameter_types! { + pub static AlarmInterval: u32 = 1; +} +pub struct OneToFive; +impl SortedMembers for OneToFive { + fn sorted_members() -> Vec { + vec![ + Alice.into(), + Bob.into(), + Charlie.into(), + David.into(), + Zero.into(), + ] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &AccountId) {} +} + +pub struct TestTracksInfo; +impl TracksInfo for TestTracksInfo { + type Id = u8; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, TrackInfo)] { + static DATA: [(u8, TrackInfo); 2] = [ + ( + 0u8, + TrackInfo { + name: "root", + max_deciding: 1, + decision_deposit: 10, + prepare_period: 4, + decision_period: 4, + confirm_period: 2, + min_enactment_period: 4, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(100), + }, + }, + ), + ( + 1u8, + TrackInfo { + name: "none", + max_deciding: 3, + decision_deposit: 1, + prepare_period: 2, + decision_period: 2, + confirm_period: 1, + min_enactment_period: 2, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(95), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(90), + ceil: Perbill::from_percent(100), + }, + }, + ), + ]; + &DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => Ok(0), + frame_system::RawOrigin::None => Ok(1), + _ => Err(()), + } + } else { + Err(()) + } + } +} +impl_tracksinfo_get!(TestTracksInfo, u128, u32); + +#[derive(Encode, Debug, Decode, TypeInfo, Eq, PartialEq, Clone, MaxEncodedLen)] +pub struct Tally { + pub ayes: u32, + pub nays: u32, +} + +impl VoteTally for Tally { + fn new(_: Class) -> Self { + Self { ayes: 0, nays: 0 } + } + + fn ayes(&self, _: Class) -> u32 { + self.ayes + } + + fn support(&self, _: Class) -> Perbill { + Perbill::from_percent(self.ayes) + } + + fn approval(&self, _: Class) -> Perbill { + if self.ayes + self.nays > 0 { + Perbill::from_rational(self.ayes, self.ayes + self.nays) + } else { + Perbill::zero() + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn unanimity(_: Class) -> Self { + Self { ayes: 100, nays: 0 } + } + + #[cfg(feature = "runtime-benchmarks")] + fn rejection(_: Class) -> Self { + Self { ayes: 0, nays: 100 } + } + + #[cfg(feature = "runtime-benchmarks")] + fn from_requirements(support: Perbill, approval: Perbill, _: Class) -> Self { + let ayes = support.mul_ceil(100u32); + let nays = ((ayes as u64) * 1_000_000_000u64 / approval.deconstruct() as u64) as u32 - ayes; + Self { ayes, nays } + } + + #[cfg(feature = "runtime-benchmarks")] + fn setup(_: Class, _: Perbill) {} +} +parameter_types! { + pub const SubmissionDeposit: u128 = 10; +} +impl pallet_referenda::Config for Runtime { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = pallet_balances::Pallet; + type SubmitOrigin = EnsureSigned; + type CancelOrigin = EnsureSignedBy; + type KillOrigin = EnsureRoot; + type Slash = (); + type Votes = u32; + type Tally = Tally; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<3>; + type UndecidingTimeout = ConstU32<20>; + type AlarmInterval = AlarmInterval; + type Tracks = TestTracksInfo; + type Preimages = Preimage; +} + +pub struct GovOrigin; +impl TryFrom for GovOrigin { + type Error = (); + fn try_from(_i: u16) -> Result { + Ok(GovOrigin) + } +} + +impl From for OriginCaller { + fn from(_o: GovOrigin) -> OriginCaller { + OriginCaller::system(RawOrigin::Root) + } +} + +/// Externality builder for pallet referenda mock runtime +pub(crate) struct ExtBuilder { + /// Balance amounts per AccountId + balances: Vec<(AccountId, Balance)>, +} + +impl Default for ExtBuilder { + fn default() -> ExtBuilder { + ExtBuilder { + balances: Vec::new(), + } + } +} + +impl ExtBuilder { + #[allow(dead_code)] + pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { + self.balances = balances; + self + } + + #[allow(dead_code)] + pub(crate) fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .expect("Frame system builds valid default genesis config"); + + pallet_balances::GenesisConfig:: { + balances: self.balances, + } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} diff --git a/precompiles/referenda/src/tests.rs b/precompiles/referenda/src/tests.rs new file mode 100644 index 0000000000..6c7a3070e5 --- /dev/null +++ b/precompiles/referenda/src/tests.rs @@ -0,0 +1,42 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . +use crate::mock::*; +use precompile_utils::testing::*; + +#[test] +fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() { + for file in ["Referenda.sol"] { + for solidity_fn in solidity::get_selectors(file) { + assert_eq!( + solidity_fn.compute_selector_hex(), + solidity_fn.docs_selector, + "documented selector for '{}' did not match for file '{}'", + solidity_fn.signature(), + file, + ); + + let selector = solidity_fn.compute_selector(); + if !PCall::supports_selector(selector) { + panic!( + "failed decoding selector 0x{:x} => '{}' as Action for file '{}'", + selector, + solidity_fn.signature(), + file, + ) + } + } + } +} diff --git a/runtime/moonbase/Cargo.toml b/runtime/moonbase/Cargo.toml index f8237e312b..0fdac7b933 100644 --- a/runtime/moonbase/Cargo.toml +++ b/runtime/moonbase/Cargo.toml @@ -47,11 +47,14 @@ pallet-evm-precompile-balances-erc20 = { path = "../../precompiles/balances-erc2 pallet-evm-precompile-batch = { path = "../../precompiles/batch", default-features = false } pallet-evm-precompile-call-permit = { path = "../../precompiles/call-permit", default-features = false } pallet-evm-precompile-collective = { path = "../../precompiles/collective", default-features = false } +pallet-evm-precompile-conviction-voting = { path = "../../precompiles/conviction-voting", default-features = false } pallet-evm-precompile-crowdloan-rewards = { path = "../../precompiles/crowdloan-rewards", default-features = false } pallet-evm-precompile-democracy = { path = "../../precompiles/pallet-democracy", default-features = false } pallet-evm-precompile-parachain-staking = { path = "../../precompiles/parachain-staking", default-features = false } +pallet-evm-precompile-preimage = { path = "../../precompiles/preimage", default-features = false } pallet-evm-precompile-proxy = { path = "../../precompiles/proxy", default-features = false } pallet-evm-precompile-randomness = { path = "../../precompiles/randomness", default-features = false } +pallet-evm-precompile-referenda = { path = "../../precompiles/referenda", default-features = false } pallet-evm-precompile-relay-encoder = { path = "../../precompiles/relay-encoder", default-features = false } pallet-evm-precompile-xcm-transactor = { path = "../../precompiles/xcm-transactor", default-features = false } pallet-evm-precompile-xcm-utils = { path = "../../precompiles/xcm-utils", default-features = false } @@ -208,9 +211,12 @@ std = [ "pallet-evm-precompile-batch/std", "pallet-evm-precompile-call-permit/std", "pallet-evm-precompile-collective/std", + "pallet-evm-precompile-conviction-voting/std", "pallet-evm-precompile-democracy/std", "pallet-evm-precompile-parachain-staking/std", + "pallet-evm-precompile-preimage/std", "pallet-evm-precompile-randomness/std", + "pallet-evm-precompile-referenda/std", "pallet-evm-precompile-xcm-transactor/std", "pallet-evm-precompile-xcm-utils/std", "pallet-evm-precompile-xtokens/std", diff --git a/runtime/moonbase/src/governance/origins.rs b/runtime/moonbase/src/governance/origins.rs index c133b5749b..6f131bbd30 100644 --- a/runtime/moonbase/src/governance/origins.rs +++ b/runtime/moonbase/src/governance/origins.rs @@ -39,11 +39,10 @@ pub mod custom_origins { ReferendumKiller, } - // where is this used, comment out and check TODO - impl TryFrom for Origin { + impl TryFrom for Origin { type Error = (); /// TrackId => Origin - fn try_from(value: u8) -> Result { + fn try_from(value: u16) -> Result { match value { 1 => Ok(Origin::WhitelistedCaller), 2 => Ok(Origin::GeneralAdmin), @@ -54,14 +53,6 @@ pub mod custom_origins { } } - impl TryFrom for Origin { - type Error = (); - /// TrackId => Origin - fn try_from(value: u16) -> Result { - (value as u8).try_into() - } - } - impl Into for Origin { /// Origin => TrackId fn into(self) -> u16 { @@ -79,9 +70,8 @@ pub mod custom_origins { macro_rules! has_consistent_conversions { ( $o:expr ) => { let origin_as_u16 = >::into($o); - let u16_as_u8: u8 = origin_as_u16.try_into().unwrap(); - let u8_as_origin: Origin = u16_as_u8.try_into().unwrap(); - assert_eq!($o, u8_as_origin); + let u16_as_origin: Origin = origin_as_u16.try_into().unwrap(); + assert_eq!($o, u16_as_origin); }; } has_consistent_conversions!(Origin::WhitelistedCaller); diff --git a/runtime/moonbase/src/lib.rs b/runtime/moonbase/src/lib.rs index 7617359b1e..fe84e64abc 100644 --- a/runtime/moonbase/src/lib.rs +++ b/runtime/moonbase/src/lib.rs @@ -807,6 +807,9 @@ fn is_governance_precompile(precompile_name: &precompiles::PrecompileName) -> bo | PrecompileName::CouncilInstance | PrecompileName::TechCommitteeInstance | PrecompileName::TreasuryCouncilInstance + | PrecompileName::ReferendaPrecompile + | PrecompileName::ConvictionVotingPrecompile + | PrecompileName::PreimagePrecompile | PrecompileName::OpenTechCommitteeInstance, ) } diff --git a/runtime/moonbase/src/precompiles.rs b/runtime/moonbase/src/precompiles.rs index bf385e2635..d4f6c42438 100644 --- a/runtime/moonbase/src/precompiles.rs +++ b/runtime/moonbase/src/precompiles.rs @@ -28,12 +28,15 @@ use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_call_permit::CallPermitPrecompile; use pallet_evm_precompile_collective::CollectivePrecompile; +use pallet_evm_precompile_conviction_voting::ConvictionVotingPrecompile; use pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompile; use pallet_evm_precompile_democracy::DemocracyPrecompile; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile; +use pallet_evm_precompile_preimage::PreimagePrecompile; use pallet_evm_precompile_proxy::ProxyPrecompile; use pallet_evm_precompile_randomness::RandomnessPrecompile; +use pallet_evm_precompile_referenda::ReferendaPrecompile; use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; @@ -118,6 +121,12 @@ type MoonbasePrecompilesAt = ( PrecompileAt, CollectivePrecompile>, PrecompileAt, CollectivePrecompile>, PrecompileAt, CollectivePrecompile>, + PrecompileAt< + AddressU64<2065>, + ReferendaPrecompile, + >, + PrecompileAt, ConvictionVotingPrecompile>, + PrecompileAt, PreimagePrecompile>, PrecompileAt, CollectivePrecompile>, ); diff --git a/runtime/moonbase/tests/integration_test.rs b/runtime/moonbase/tests/integration_test.rs index 30be22f0c5..4f37f516ce 100644 --- a/runtime/moonbase/tests/integration_test.rs +++ b/runtime/moonbase/tests/integration_test.rs @@ -2946,7 +2946,7 @@ fn precompile_existence() { let precompiles = Precompiles::new(); let precompile_addresses: std::collections::BTreeSet<_> = vec![ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, - 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2068, + 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, ] .into_iter() .map(H160::from_low_u64_be) diff --git a/runtime/moonriver/Cargo.toml b/runtime/moonriver/Cargo.toml index 94d361088c..31136e67a8 100644 --- a/runtime/moonriver/Cargo.toml +++ b/runtime/moonriver/Cargo.toml @@ -45,11 +45,14 @@ pallet-evm-precompile-balances-erc20 = { path = "../../precompiles/balances-erc2 pallet-evm-precompile-batch = { path = "../../precompiles/batch", default-features = false } pallet-evm-precompile-call-permit = { path = "../../precompiles/call-permit", default-features = false } pallet-evm-precompile-collective = { path = "../../precompiles/collective", default-features = false } +pallet-evm-precompile-conviction-voting = { path = "../../precompiles/conviction-voting", default-features = false } pallet-evm-precompile-crowdloan-rewards = { path = "../../precompiles/crowdloan-rewards", default-features = false } pallet-evm-precompile-democracy = { path = "../../precompiles/pallet-democracy", default-features = false } pallet-evm-precompile-parachain-staking = { path = "../../precompiles/parachain-staking", default-features = false } +pallet-evm-precompile-preimage = { path = "../../precompiles/preimage", default-features = false } pallet-evm-precompile-proxy = { path = "../../precompiles/proxy", default-features = false } pallet-evm-precompile-randomness = { path = "../../precompiles/randomness", default-features = false } +pallet-evm-precompile-referenda = { path = "../../precompiles/referenda", default-features = false } pallet-evm-precompile-relay-encoder = { path = "../../precompiles/relay-encoder", default-features = false } pallet-evm-precompile-xcm-transactor = { path = "../../precompiles/xcm-transactor", default-features = false } pallet-evm-precompile-xcm-utils = { path = "../../precompiles/xcm-utils", default-features = false } @@ -204,9 +207,12 @@ std = [ "pallet-evm-precompile-batch/std", "pallet-evm-precompile-call-permit/std", "pallet-evm-precompile-collective/std", + "pallet-evm-precompile-conviction-voting/std", "pallet-evm-precompile-democracy/std", "pallet-evm-precompile-parachain-staking/std", + "pallet-evm-precompile-preimage/std", "pallet-evm-precompile-randomness/std", + "pallet-evm-precompile-referenda/std", "pallet-evm-precompile-xcm-transactor/std", "pallet-evm-precompile-xcm-utils/std", "pallet-evm-precompile-xtokens/std", diff --git a/runtime/moonriver/src/governance/origins.rs b/runtime/moonriver/src/governance/origins.rs index c0e85c0f54..6f131bbd30 100644 --- a/runtime/moonriver/src/governance/origins.rs +++ b/runtime/moonriver/src/governance/origins.rs @@ -39,10 +39,10 @@ pub mod custom_origins { ReferendumKiller, } - impl TryFrom for Origin { + impl TryFrom for Origin { type Error = (); /// TrackId => Origin - fn try_from(value: u8) -> Result { + fn try_from(value: u16) -> Result { match value { 1 => Ok(Origin::WhitelistedCaller), 2 => Ok(Origin::GeneralAdmin), @@ -53,14 +53,6 @@ pub mod custom_origins { } } - impl TryFrom for Origin { - type Error = (); - /// TrackId => Origin - fn try_from(value: u16) -> Result { - (value as u8).try_into() - } - } - impl Into for Origin { /// Origin => TrackId fn into(self) -> u16 { @@ -78,9 +70,8 @@ pub mod custom_origins { macro_rules! has_consistent_conversions { ( $o:expr ) => { let origin_as_u16 = >::into($o); - let u16_as_u8: u8 = origin_as_u16.try_into().unwrap(); - let u8_as_origin: Origin = u16_as_u8.try_into().unwrap(); - assert_eq!($o, u8_as_origin); + let u16_as_origin: Origin = origin_as_u16.try_into().unwrap(); + assert_eq!($o, u16_as_origin); }; } has_consistent_conversions!(Origin::WhitelistedCaller); diff --git a/runtime/moonriver/src/precompiles.rs b/runtime/moonriver/src/precompiles.rs index 11258a3fa7..78cf7960a7 100644 --- a/runtime/moonriver/src/precompiles.rs +++ b/runtime/moonriver/src/precompiles.rs @@ -28,11 +28,14 @@ use pallet_evm_precompile_blake2::Blake2F; use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; use pallet_evm_precompile_call_permit::CallPermitPrecompile; use pallet_evm_precompile_collective::CollectivePrecompile; +use pallet_evm_precompile_conviction_voting::ConvictionVotingPrecompile; use pallet_evm_precompile_crowdloan_rewards::CrowdloanRewardsPrecompile; use pallet_evm_precompile_democracy::DemocracyPrecompile; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile; +use pallet_evm_precompile_preimage::PreimagePrecompile; use pallet_evm_precompile_randomness::RandomnessPrecompile; +use pallet_evm_precompile_referenda::ReferendaPrecompile; use pallet_evm_precompile_relay_encoder::RelayEncoderPrecompile; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; @@ -128,6 +131,12 @@ pub type MoonriverPrecompiles = PrecompileSetBuilder< PrecompileAt, CollectivePrecompile>, PrecompileAt, CollectivePrecompile>, PrecompileAt, CollectivePrecompile>, + PrecompileAt< + AddressU64<2065>, + ReferendaPrecompile, + >, + PrecompileAt, ConvictionVotingPrecompile>, + PrecompileAt, PreimagePrecompile>, PrecompileAt, CollectivePrecompile>, ), >, diff --git a/runtime/moonriver/tests/integration_test.rs b/runtime/moonriver/tests/integration_test.rs index 4c3c963598..3d54984638 100644 --- a/runtime/moonriver/tests/integration_test.rs +++ b/runtime/moonriver/tests/integration_test.rs @@ -2748,7 +2748,7 @@ fn precompile_existence() { let precompiles = Precompiles::new(); let precompile_addresses: std::collections::BTreeSet<_> = vec![ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1024, 1026, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, - 2056, 2057, 2058, 2060, 2062, 2063, 2064, 2068, + 2056, 2057, 2058, 2060, 2062, 2063, 2064, 2065, 2066, 2067, 2068, ] .into_iter() .map(H160::from_low_u64_be)