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

            
21
use fp_evm::PrecompileHandle;
22
use frame_support::{
23
	dispatch::{GetDispatchInfo, PostDispatchInfo},
24
	traits::Get,
25
};
26
use nimbus_primitives::NimbusId;
27
use pallet_author_mapping::Call as AuthorMappingCall;
28
use pallet_evm::AddressMapping;
29
use parity_scale_codec::Encode;
30
use precompile_utils::prelude::*;
31
use sp_core::crypto::UncheckedFrom;
32
use sp_core::{H160, H256};
33
use sp_runtime::traits::Dispatchable;
34
use sp_std::marker::PhantomData;
35

            
36
#[cfg(test)]
37
mod mock;
38
#[cfg(test)]
39
mod tests;
40

            
41
/// A precompile to wrap the functionality from pallet author mapping.
42
pub 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.
47
pub struct GetKeysSize<R>(PhantomData<R>);
48

            
49
impl<R: pallet_author_mapping::Config> Get<u32> for GetKeysSize<R> {
50
3
	fn get() -> u32 {
51
3
		pallet_author_mapping::pallet::keys_size::<R>()
52
3
			.try_into()
53
3
			.expect("keys size to fit in u32")
54
3
	}
55
}
56

            
57
31
#[precompile_utils::precompile]
58
#[precompile::test_concrete_types(mock::Runtime)]
59
impl<Runtime> AuthorMappingPrecompile<Runtime>
60
where
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
2
	fn add_association(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult {
72
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id).into();
73

            
74
2
		log::trace!(
75
			target: "author-mapping-precompile",
76
			"Associating author id {:?}", nimbus_id
77
		);
78

            
79
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
80
2
		let call = AuthorMappingCall::<Runtime>::add_association { nimbus_id };
81

            
82
2
		RuntimeHelper::<Runtime>::try_dispatch(
83
2
			handle,
84
2
			frame_system::RawOrigin::Signed(origin).into(),
85
2
			call,
86
			0,
87
		)?;
88

            
89
2
		Ok(())
90
2
	}
91

            
92
	#[precompile::public("updateAssociation(bytes32,bytes32)")]
93
	#[precompile::public("update_association(bytes32,bytes32)")]
94
2
	fn update_association(
95
2
		handle: &mut impl PrecompileHandle,
96
2
		old_nimbus_id: H256,
97
2
		new_nimbus_id: H256,
98
2
	) -> EvmResult {
99
2
		let old_nimbus_id = sp_core::sr25519::Public::unchecked_from(old_nimbus_id).into();
100
2
		let new_nimbus_id = sp_core::sr25519::Public::unchecked_from(new_nimbus_id).into();
101

            
102
2
		log::trace!(
103
			target: "author-mapping-precompile",
104
			"Updating author id {:?} for {:?}", old_nimbus_id, new_nimbus_id
105
		);
106

            
107
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
108
2
		let call = AuthorMappingCall::<Runtime>::update_association {
109
2
			old_nimbus_id,
110
2
			new_nimbus_id,
111
2
		};
112

            
113
2
		RuntimeHelper::<Runtime>::try_dispatch(
114
2
			handle,
115
2
			frame_system::RawOrigin::Signed(origin).into(),
116
2
			call,
117
			0,
118
		)?;
119

            
120
2
		Ok(())
121
2
	}
122

            
123
	#[precompile::public("clearAssociation(bytes32)")]
124
	#[precompile::public("clear_association(bytes32)")]
125
2
	fn clear_association(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult {
126
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id).into();
127

            
128
2
		log::trace!(
129
			target: "author-mapping-precompile",
130
			"Clearing author id {:?}", nimbus_id
131
		);
132

            
133
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
134
2
		let call = AuthorMappingCall::<Runtime>::clear_association { nimbus_id };
135

            
136
2
		RuntimeHelper::<Runtime>::try_dispatch(
137
2
			handle,
138
2
			frame_system::RawOrigin::Signed(origin).into(),
139
2
			call,
140
			0,
141
		)?;
142

            
143
2
		Ok(())
144
2
	}
145

            
146
	#[precompile::public("removeKeys()")]
147
	#[precompile::public("remove_keys()")]
148
1
	fn remove_keys(handle: &mut impl PrecompileHandle) -> EvmResult {
149
1
		log::trace!(
150
			target: "author-mapping-precompile",
151
			"Removing keys"
152
		);
153

            
154
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
155
1
		let call = AuthorMappingCall::<Runtime>::remove_keys {};
156

            
157
1
		RuntimeHelper::<Runtime>::try_dispatch(
158
1
			handle,
159
1
			frame_system::RawOrigin::Signed(origin).into(),
160
1
			call,
161
			0,
162
		)?;
163

            
164
1
		Ok(())
165
1
	}
166

            
167
	#[precompile::public("setKeys(bytes)")]
168
	#[precompile::public("set_keys(bytes)")]
169
3
	fn set_keys(
170
3
		handle: &mut impl PrecompileHandle,
171
3
		keys: BoundedBytes<GetKeysSize<Runtime>>,
172
3
	) -> EvmResult {
173
3
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
174
3
		let call = AuthorMappingCall::<Runtime>::set_keys { keys: keys.into() };
175

            
176
3
		RuntimeHelper::<Runtime>::try_dispatch(
177
3
			handle,
178
3
			frame_system::RawOrigin::Signed(origin).into(),
179
3
			call,
180
			0,
181
		)?;
182

            
183
3
		Ok(())
184
3
	}
185

            
186
	#[precompile::public("nimbusIdOf(address)")]
187
	#[precompile::view]
188
2
	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
2
		handle.record_db_read::<Runtime>(68)?;
192
2
		let account = Runtime::AddressMapping::into_account_id(address.0);
193

            
194
2
		let nimbus_id = pallet_author_mapping::Pallet::<Runtime>::nimbus_id_of(&account)
195
2
			.map(|x| H256::from(sp_core::sr25519::Public::from(x).0))
196
2
			.unwrap_or(H256::zero());
197
2
		Ok(nimbus_id)
198
2
	}
199

            
200
	#[precompile::public("addressOf(bytes32)")]
201
	#[precompile::view]
202
2
	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
2
		handle.record_db_read::<Runtime>(116)?;
206

            
207
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
208
2
		let nimbus_id: NimbusId = nimbus_id.into();
209

            
210
2
		let address: H160 = pallet_author_mapping::Pallet::<Runtime>::account_id_of(&nimbus_id)
211
2
			.map(|x| x.into())
212
2
			.unwrap_or(H160::zero());
213

            
214
2
		Ok(Address(address))
215
2
	}
216

            
217
	#[precompile::public("keysOf(bytes32)")]
218
	#[precompile::view]
219
2
	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
2
		handle.record_db_read::<Runtime>(116)?;
223

            
224
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
225
2
		let nimbus_id: NimbusId = nimbus_id.into();
226

            
227
2
		let keys = pallet_author_mapping::Pallet::<Runtime>::keys_of(&nimbus_id)
228
2
			.map(|x| x.encode())
229
2
			.unwrap_or_default();
230

            
231
2
		Ok(keys.into())
232
2
	}
233
}