pallet_evm_precompile_author_mapping/
lib.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Precompile to interact with pallet author mapping through an evm precompile.
18
19#![cfg_attr(not(feature = "std"), no_std)]
20
21use fp_evm::PrecompileHandle;
22use frame_support::{
23	dispatch::{GetDispatchInfo, PostDispatchInfo},
24	traits::Get,
25};
26use nimbus_primitives::NimbusId;
27use pallet_author_mapping::Call as AuthorMappingCall;
28use pallet_evm::AddressMapping;
29use parity_scale_codec::Encode;
30use precompile_utils::prelude::*;
31use sp_core::crypto::UncheckedFrom;
32use sp_core::{H160, H256};
33use sp_runtime::traits::Dispatchable;
34use sp_std::marker::PhantomData;
35
36#[cfg(test)]
37mod mock;
38#[cfg(test)]
39mod tests;
40
41/// A precompile to wrap the functionality from pallet author mapping.
42pub struct AuthorMappingPrecompile<Runtime>(PhantomData<Runtime>);
43
44/// Bound for the keys size.
45/// Pallet will check that the size exactly matches, but we want to bound the parser to
46/// not accept larger bytes.
47pub struct GetKeysSize<R>(PhantomData<R>);
48
49impl<R: pallet_author_mapping::Config> Get<u32> for GetKeysSize<R> {
50	fn get() -> u32 {
51		pallet_author_mapping::pallet::keys_size::<R>()
52			.try_into()
53			.expect("keys size to fit in u32")
54	}
55}
56
57#[precompile_utils::precompile]
58#[precompile::test_concrete_types(mock::Runtime)]
59impl<Runtime> AuthorMappingPrecompile<Runtime>
60where
61	Runtime: pallet_author_mapping::Config + pallet_evm::Config + frame_system::Config,
62	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
63	Runtime::RuntimeCall: From<AuthorMappingCall<Runtime>>,
64	Runtime::Hash: From<H256>,
65	Runtime::AccountId: Into<H160>,
66	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
67{
68	// The dispatchable wrappers are next. They dispatch a Substrate inner Call.
69	#[precompile::public("addAssociation(bytes32)")]
70	#[precompile::public("add_association(bytes32)")]
71	fn add_association(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult {
72		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id).into();
73
74		log::trace!(
75			target: "author-mapping-precompile",
76			"Associating author id {:?}", nimbus_id
77		);
78
79		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
80		let call = AuthorMappingCall::<Runtime>::add_association { nimbus_id };
81
82		RuntimeHelper::<Runtime>::try_dispatch(
83			handle,
84			frame_system::RawOrigin::Signed(origin).into(),
85			call,
86			0,
87		)?;
88
89		Ok(())
90	}
91
92	#[precompile::public("updateAssociation(bytes32,bytes32)")]
93	#[precompile::public("update_association(bytes32,bytes32)")]
94	fn update_association(
95		handle: &mut impl PrecompileHandle,
96		old_nimbus_id: H256,
97		new_nimbus_id: H256,
98	) -> EvmResult {
99		let old_nimbus_id = sp_core::sr25519::Public::unchecked_from(old_nimbus_id).into();
100		let new_nimbus_id = sp_core::sr25519::Public::unchecked_from(new_nimbus_id).into();
101
102		log::trace!(
103			target: "author-mapping-precompile",
104			"Updating author id {:?} for {:?}", old_nimbus_id, new_nimbus_id
105		);
106
107		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
108		let call = AuthorMappingCall::<Runtime>::update_association {
109			old_nimbus_id,
110			new_nimbus_id,
111		};
112
113		RuntimeHelper::<Runtime>::try_dispatch(
114			handle,
115			frame_system::RawOrigin::Signed(origin).into(),
116			call,
117			0,
118		)?;
119
120		Ok(())
121	}
122
123	#[precompile::public("clearAssociation(bytes32)")]
124	#[precompile::public("clear_association(bytes32)")]
125	fn clear_association(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult {
126		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id).into();
127
128		log::trace!(
129			target: "author-mapping-precompile",
130			"Clearing author id {:?}", nimbus_id
131		);
132
133		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
134		let call = AuthorMappingCall::<Runtime>::clear_association { nimbus_id };
135
136		RuntimeHelper::<Runtime>::try_dispatch(
137			handle,
138			frame_system::RawOrigin::Signed(origin).into(),
139			call,
140			0,
141		)?;
142
143		Ok(())
144	}
145
146	#[precompile::public("removeKeys()")]
147	#[precompile::public("remove_keys()")]
148	fn remove_keys(handle: &mut impl PrecompileHandle) -> EvmResult {
149		log::trace!(
150			target: "author-mapping-precompile",
151			"Removing keys"
152		);
153
154		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
155		let call = AuthorMappingCall::<Runtime>::remove_keys {};
156
157		RuntimeHelper::<Runtime>::try_dispatch(
158			handle,
159			frame_system::RawOrigin::Signed(origin).into(),
160			call,
161			0,
162		)?;
163
164		Ok(())
165	}
166
167	#[precompile::public("setKeys(bytes)")]
168	#[precompile::public("set_keys(bytes)")]
169	fn set_keys(
170		handle: &mut impl PrecompileHandle,
171		keys: BoundedBytes<GetKeysSize<Runtime>>,
172	) -> EvmResult {
173		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
174		let call = AuthorMappingCall::<Runtime>::set_keys { keys: keys.into() };
175
176		RuntimeHelper::<Runtime>::try_dispatch(
177			handle,
178			frame_system::RawOrigin::Signed(origin).into(),
179			call,
180			0,
181		)?;
182
183		Ok(())
184	}
185
186	#[precompile::public("nimbusIdOf(address)")]
187	#[precompile::view]
188	fn nimbus_id_of(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<H256> {
189		// Storage item: NimbusLookup:
190		// Blake2_128(16) + AccountId(20) + NimbusId(32)
191		handle.record_db_read::<Runtime>(68)?;
192		let account = Runtime::AddressMapping::into_account_id(address.0);
193
194		let nimbus_id = pallet_author_mapping::Pallet::<Runtime>::nimbus_id_of(&account)
195			.map(|x| H256::from(sp_core::sr25519::Public::from(x).0))
196			.unwrap_or(H256::zero());
197		Ok(nimbus_id)
198	}
199
200	#[precompile::public("addressOf(bytes32)")]
201	#[precompile::view]
202	fn address_of(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult<Address> {
203		// Storage item: MappingWithDeposit:
204		// Blake2_128(16) + NimbusId(32) + RegistrationInfo(20 + 16 + VrfId(32))
205		handle.record_db_read::<Runtime>(116)?;
206
207		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
208		let nimbus_id: NimbusId = nimbus_id.into();
209
210		let address: H160 = pallet_author_mapping::Pallet::<Runtime>::account_id_of(&nimbus_id)
211			.map(|x| x.into())
212			.unwrap_or(H160::zero());
213
214		Ok(Address(address))
215	}
216
217	#[precompile::public("keysOf(bytes32)")]
218	#[precompile::view]
219	fn keys_of(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult<UnboundedBytes> {
220		// Storage item: MappingWithDeposit:
221		// Blake2_128(16) + NimbusId(32) + RegistrationInfo(20 + 16 + VrfId(32))
222		handle.record_db_read::<Runtime>(116)?;
223
224		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
225		let nimbus_id: NimbusId = nimbus_id.into();
226
227		let keys = pallet_author_mapping::Pallet::<Runtime>::keys_of(&nimbus_id)
228			.map(|x| x.encode())
229			.unwrap_or_default();
230
231		Ok(keys.into())
232	}
233}