Skip to content

Commit

Permalink
Democracy Precompile events (#1872)
Browse files Browse the repository at this point in the history
* Proposed event

* Seconded

* StandardVote event

* Delegated event

* Undelegated event + costs

* Fix typo

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>
  • Loading branch information
nanocryk and 4meta5 committed Nov 4, 2022
1 parent 87d9142 commit 53b87a8
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 16 deletions.
38 changes: 38 additions & 0 deletions precompiles/pallet-democracy/DemocracyInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,42 @@ interface Democracy {
///
/// @param encodedProposal The scale-encoded proposal whose hash has been submitted on-chain.
function noteImminentPreimage(bytes memory encodedProposal) external;

/// @dev A motion has been proposed by a public account.
/// @custom:selector d89e173ca5c9fd0ec38f2b01995c4f1748210f686fa189a6b8d189c210444924
/// @param proposalIndex uint32 Index of the proposal.
/// @param deposit uint256 Amount of tokens deposited.
event Proposed(uint32 indexed proposalIndex, uint256 deposit);

/// @dev An account has seconded a proposal.
/// @custom:selector e1613d7e3f54885ef3ffdb714435193b9b80818bd3381f108a4d4b21e842654a
/// @param proposalIndex uint32 Index of the proposal.
/// @param seconder address Address of the seconder.
event Seconded(uint32 indexed proposalIndex, address seconder);

/// @dev An account made a standard vote.
/// @custom:selector 057363260bf880d3658601ecff97e75b67a22f38b7066c0e47e2d170477579c3
/// @param referendumIndex uint32 Index of the referendum.
/// @param voter address Address of the voter.
/// @param aye bool Is it a vote for or against the referendum.
/// @param voteAmount uint256 Amount used to vote.
/// @param conviction uint8 Conviction of the vote.
event StandardVote(
uint32 indexed referendumIndex,
address voter,
bool aye,
uint256 voteAmount,
uint8 conviction
);

/// @dev An account delegated some voting power to another account
/// @custom:selector 4bc154dd35d6a5cb9206482ecb473cdbf2473006d6bce728b9cc0741bcc59ea2
/// @param who address Address of the delegator.
/// @param target address Address of the delegatee.
event Delegated(address indexed who, address target);

/// @dev An account undelegated.
/// @custom:selector 42176493fdfcada70cc1bcf321c9a2314e9571a9fe53c54a5385a1eeac8bc1d7
/// @param who address Address of the delegator.
event Undelegated(address indexed who);
}
98 changes: 87 additions & 11 deletions precompiles/pallet-democracy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ type DemocracyOf<Runtime> = pallet_democracy::Pallet<Runtime>;
pub const ENCODED_PROPOSAL_SIZE_LIMIT: u32 = 2u32.pow(16);
type GetEncodedProposalSizeLimit = ConstU32<ENCODED_PROPOSAL_SIZE_LIMIT>;

/// Solidity selector of the Proposed log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_PROPOSED: [u8; 32] = keccak256!("Proposed(uint32,uint256)");

/// Solidity selector of the Seconded log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_SECONDED: [u8; 32] = keccak256!("Seconded(uint32,address)");

/// Solidity selector of the StandardVote log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_STANDARD_VOTE: [u8; 32] =
keccak256!("StandardVote(uint32,address,bool,uint256,uint8)");

/// Solidity selector of the Delegated log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_DELEGATED: [u8; 32] = keccak256!("Delegated(address,address)");

/// Solidity selector of the Undelegated log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_UNDELEGATED: [u8; 32] = keccak256!("Undelegated(address)");

/// A precompile to wrap the functionality from pallet democracy.
///
/// Grants evm-based DAOs the right to vote making them first-class citizens.
Expand Down Expand Up @@ -168,6 +184,10 @@ where
// The dispatchable wrappers are next. They dispatch a Substrate inner Call.
#[precompile::public("propose(bytes32,uint256)")]
fn propose(handle: &mut impl PrecompileHandle, proposal_hash: H256, value: U256) -> EvmResult {
handle.record_log_costs_manual(2, 32)?;
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
let prop_count = DemocracyOf::<Runtime>::public_prop_count();

let proposal_hash = proposal_hash.into();
let value = Self::u256_to_amount(value).in_field("value")?;

Expand All @@ -184,6 +204,14 @@ where

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

log2(
handle.context().address,
SELECTOR_LOG_PROPOSED,
H256::from_low_u64_be(prop_count as u64), // proposal index,
EvmDataWriter::new().write::<U256>(value.into()).build(),
)
.record(handle)?;

Ok(())
}

Expand All @@ -193,6 +221,7 @@ where
prop_index: SolidityConvert<U256, u32>,
seconds_upper_bound: SolidityConvert<U256, u32>,
) -> EvmResult {
handle.record_log_costs_manual(2, 32)?;
let prop_index = prop_index.converted();
let seconds_upper_bound = seconds_upper_bound.converted();

Expand All @@ -209,6 +238,16 @@ where

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

log2(
handle.context().address,
SELECTOR_LOG_SECONDED,
H256::from_low_u64_be(prop_index as u64), // proposal index,
EvmDataWriter::new()
.write::<Address>(handle.context().caller.into())
.build(),
)
.record(handle)?;

Ok(())
}

Expand All @@ -221,29 +260,47 @@ where
vote_amount: U256,
conviction: SolidityConvert<U256, u8>,
) -> EvmResult {
handle.record_log_costs_manual(2, 32 * 4)?;
let ref_index = ref_index.converted();
let vote_amount = Self::u256_to_amount(vote_amount).in_field("voteAmount")?;
let vote_amount_balance = Self::u256_to_amount(vote_amount).in_field("voteAmount")?;

let conviction: Conviction = conviction.converted().try_into().map_err(|_| {
RevertReason::custom("Must be an integer between 0 and 6 included")
.in_field("conviction")
})?;
let conviction_enum: Conviction =
conviction.clone().converted().try_into().map_err(|_| {
RevertReason::custom("Must be an integer between 0 and 6 included")
.in_field("conviction")
})?;

let vote = AccountVote::Standard {
vote: Vote { aye, conviction },
balance: vote_amount,
vote: Vote {
aye,
conviction: conviction_enum,
},
balance: vote_amount_balance,
};

log::trace!(target: "democracy-precompile",
"Voting {:?} on referendum #{:?}, with conviction {:?}",
aye, ref_index, conviction
aye, ref_index, conviction_enum
);

let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let call = DemocracyCall::<Runtime>::vote { ref_index, vote };

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

log2(
handle.context().address,
SELECTOR_LOG_STANDARD_VOTE,
H256::from_low_u64_be(ref_index as u64), // referendum index,
EvmDataWriter::new()
.write::<Address>(handle.context().caller.into())
.write::<bool>(aye)
.write::<U256>(vote_amount)
.write::<u8>(conviction.converted())
.build(),
)
.record(handle)?;

Ok(())
}

Expand Down Expand Up @@ -276,6 +333,7 @@ where
conviction: SolidityConvert<U256, u8>,
amount: U256,
) -> EvmResult {
handle.record_log_costs_manual(2, 32)?;
let amount = Self::u256_to_amount(amount).in_field("amount")?;

let conviction: Conviction = conviction.converted().try_into().map_err(|_| {
Expand All @@ -288,9 +346,8 @@ where
representative, conviction, amount
);

let representative = Runtime::AddressMapping::into_account_id(representative.into());
let to: <Runtime::Lookup as StaticLookup>::Source =
Runtime::Lookup::unlookup(representative.clone());
let to = Runtime::AddressMapping::into_account_id(representative.into());
let to: <Runtime::Lookup as StaticLookup>::Source = Runtime::Lookup::unlookup(to.clone());
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let call = DemocracyCall::<Runtime>::delegate {
to,
Expand All @@ -300,17 +357,36 @@ where

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

log2(
handle.context().address,
SELECTOR_LOG_DELEGATED,
handle.context().caller,
EvmDataWriter::new()
.write::<Address>(representative)
.build(),
)
.record(handle)?;

Ok(())
}

#[precompile::public("unDelegate()")]
#[precompile::public("un_delegate()")]
fn un_delegate(handle: &mut impl PrecompileHandle) -> EvmResult {
handle.record_log_costs_manual(2, 0)?;
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let call = DemocracyCall::<Runtime>::undelegate {};

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

log2(
handle.context().address,
SELECTOR_LOG_UNDELEGATED,
handle.context().caller,
[],
)
.record(handle)?;

Ok(())
}

Expand Down
78 changes: 73 additions & 5 deletions precompiles/pallet-democracy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.

use crate::mock::{
events, roll_to,
Account::{self, Alice, Bob, Precompile},
Balances, Call, Democracy, ExtBuilder, Origin, PCall, Precompiles, PrecompilesValue, Runtime,
use crate::{
mock::{
events, roll_to,
Account::{self, Alice, Bob, Precompile},
Balances, Call, Democracy, ExtBuilder, Origin, PCall, Precompiles, PrecompilesValue,
Runtime,
},
SELECTOR_LOG_DELEGATED, SELECTOR_LOG_PROPOSED, SELECTOR_LOG_SECONDED,
SELECTOR_LOG_STANDARD_VOTE, SELECTOR_LOG_UNDELEGATED,
};
use frame_support::{assert_ok, dispatch::Dispatchable, traits::Currency};
use pallet_balances::Event as BalancesEvent;
Expand All @@ -26,7 +31,7 @@ use pallet_democracy::{
PreimageStatus, Vote, VoteThreshold, Voting,
};
use pallet_evm::{Call as EvmCall, Event as EvmEvent};
use precompile_utils::{solidity, testing::*};
use precompile_utils::{prelude::*, solidity, testing::*};
use sp_core::{H160, H256, U256};
use std::{convert::TryInto, str::from_utf8};

Expand Down Expand Up @@ -463,6 +468,15 @@ fn propose_works() {
deposit: 100
}
.into(),
EvmEvent::Log {
log: log2(
Precompile,
SELECTOR_LOG_PROPOSED,
H256::zero(), // proposal index,
EvmDataWriter::new().write::<U256>(100.into()).build(),
)
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down Expand Up @@ -525,6 +539,17 @@ fn second_works() {
prop_index: 0
}
.into(),
EvmEvent::Log {
log: log2(
Precompile,
SELECTOR_LOG_SECONDED,
H256::zero(), // proposal index,
EvmDataWriter::new()
.write::<Address>(H160::from(Alice).into())
.build(),
)
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down Expand Up @@ -582,6 +607,20 @@ fn standard_vote_aye_works() {
}
}
.into(),
EvmEvent::Log {
log: log2(
Precompile,
SELECTOR_LOG_STANDARD_VOTE,
H256::zero(), // referendum index,
EvmDataWriter::new()
.write::<Address>(H160::from(Alice).into())
.write::<bool>(true)
.write::<U256>(100000.into())
.write::<u8>(0)
.build(),
),
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down Expand Up @@ -655,6 +694,20 @@ fn standard_vote_nay_conviction_works() {
}
}
.into(),
EvmEvent::Log {
log: log2(
Precompile,
SELECTOR_LOG_STANDARD_VOTE,
H256::zero(), // referendum index,
EvmDataWriter::new()
.write::<Address>(H160::from(Alice).into())
.write::<bool>(false)
.write::<U256>(100000.into())
.write::<u8>(3)
.build(),
),
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down Expand Up @@ -818,6 +871,17 @@ fn delegate_works() {
target: Bob
}
.into(),
EvmEvent::Log {
log: log2(
Precompile,
SELECTOR_LOG_DELEGATED,
H160::from(Alice),
EvmDataWriter::new()
.write::<Address>(H160::from(Bob).into())
.build(),
),
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down Expand Up @@ -882,6 +946,10 @@ fn undelegate_works() {
}
.into(),
DemocracyEvent::Undelegated { account: Alice }.into(),
EvmEvent::Log {
log: log2(Precompile, SELECTOR_LOG_UNDELEGATED, H160::from(Alice), [],),
}
.into(),
EvmEvent::Executed {
address: Precompile.into()
}
Expand Down
1 change: 1 addition & 0 deletions precompiles/utils/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ impl Default for EvmDataWriter {
/// Adapter to parse data as a first type then convert it to another one.
/// Useful for old precompiles in which Solidity arguments where set larger than
/// the needed Rust type.
#[derive(Clone, Copy, Debug)]
pub struct SolidityConvert<P, C> {
inner: C,
_phantom: PhantomData<P>,
Expand Down

0 comments on commit 53b87a8

Please sign in to comment.