1
// Copyright 2019-2022 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
239
#[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 as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
64
	Runtime::RuntimeCall: From<AuthorMappingCall<Runtime>>,
65
	Runtime::Hash: From<H256>,
66
	Runtime::AccountId: Into<H160>,
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
2

            
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
2

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

            
84
2
		Ok(())
85
2
	}
86

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

            
97
2
		log::trace!(
98
			target: "author-mapping-precompile",
99
			"Updating author id {:?} for {:?}", old_nimbus_id, new_nimbus_id
100
		);
101

            
102
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
103
2
		let call = AuthorMappingCall::<Runtime>::update_association {
104
2
			old_nimbus_id,
105
2
			new_nimbus_id,
106
2
		};
107
2

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

            
110
2
		Ok(())
111
2
	}
112

            
113
	#[precompile::public("clearAssociation(bytes32)")]
114
	#[precompile::public("clear_association(bytes32)")]
115
2
	fn clear_association(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult {
116
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id).into();
117
2

            
118
2
		log::trace!(
119
			target: "author-mapping-precompile",
120
			"Clearing author id {:?}", nimbus_id
121
		);
122

            
123
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
124
2
		let call = AuthorMappingCall::<Runtime>::clear_association { nimbus_id };
125
2

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

            
128
2
		Ok(())
129
2
	}
130

            
131
	#[precompile::public("removeKeys()")]
132
	#[precompile::public("remove_keys()")]
133
1
	fn remove_keys(handle: &mut impl PrecompileHandle) -> EvmResult {
134
1
		log::trace!(
135
			target: "author-mapping-precompile",
136
			"Removing keys"
137
		);
138

            
139
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
140
1
		let call = AuthorMappingCall::<Runtime>::remove_keys {};
141
1

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

            
144
1
		Ok(())
145
1
	}
146

            
147
	#[precompile::public("setKeys(bytes)")]
148
	#[precompile::public("set_keys(bytes)")]
149
3
	fn set_keys(
150
3
		handle: &mut impl PrecompileHandle,
151
3
		keys: BoundedBytes<GetKeysSize<Runtime>>,
152
3
	) -> EvmResult {
153
3
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
154
3
		let call = AuthorMappingCall::<Runtime>::set_keys { keys: keys.into() };
155
3

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

            
158
3
		Ok(())
159
3
	}
160

            
161
	#[precompile::public("nimbusIdOf(address)")]
162
	#[precompile::view]
163
2
	fn nimbus_id_of(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<H256> {
164
2
		// Storage item: NimbusLookup:
165
2
		// Blake2_128(16) + AccountId(20) + NimbusId(32)
166
2
		handle.record_db_read::<Runtime>(68)?;
167
2
		let account = Runtime::AddressMapping::into_account_id(address.0);
168
2

            
169
2
		let nimbus_id = pallet_author_mapping::Pallet::<Runtime>::nimbus_id_of(&account)
170
2
			.map(|x| H256::from(sp_core::sr25519::Public::from(x).0))
171
2
			.unwrap_or(H256::zero());
172
2
		Ok(nimbus_id)
173
2
	}
174

            
175
	#[precompile::public("addressOf(bytes32)")]
176
	#[precompile::view]
177
2
	fn address_of(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult<Address> {
178
2
		// Storage item: MappingWithDeposit:
179
2
		// Blake2_128(16) + NimbusId(32) + RegistrationInfo(20 + 16 + VrfId(32))
180
2
		handle.record_db_read::<Runtime>(116)?;
181

            
182
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
183
2
		let nimbus_id: NimbusId = nimbus_id.into();
184
2

            
185
2
		let address: H160 = pallet_author_mapping::Pallet::<Runtime>::account_id_of(&nimbus_id)
186
2
			.map(|x| x.into())
187
2
			.unwrap_or(H160::zero());
188
2

            
189
2
		Ok(Address(address))
190
2
	}
191

            
192
	#[precompile::public("keysOf(bytes32)")]
193
	#[precompile::view]
194
2
	fn keys_of(handle: &mut impl PrecompileHandle, nimbus_id: H256) -> EvmResult<UnboundedBytes> {
195
2
		// Storage item: MappingWithDeposit:
196
2
		// Blake2_128(16) + NimbusId(32) + RegistrationInfo(20 + 16 + VrfId(32))
197
2
		handle.record_db_read::<Runtime>(116)?;
198

            
199
2
		let nimbus_id = sp_core::sr25519::Public::unchecked_from(nimbus_id);
200
2
		let nimbus_id: NimbusId = nimbus_id.into();
201
2

            
202
2
		let keys = pallet_author_mapping::Pallet::<Runtime>::keys_of(&nimbus_id)
203
2
			.map(|x| x.encode())
204
2
			.unwrap_or_default();
205
2

            
206
2
		Ok(keys.into())
207
2
	}
208
}