Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pallets/signet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pallet-signet"
version = "1.3.0"
version = "1.4.0"
authors = ["Signet"]
edition = "2021"
license = "Apache-2.0"
Expand Down
25 changes: 25 additions & 0 deletions pallets/signet/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ mod benches {
#[benchmark]
fn respond() {
let responder: T::AccountId = whitelisted_caller();
Signers::<T>::insert(&responder, ());

let mut ids: Vec<[u8; 32]> = Vec::with_capacity(MAX_BATCH_SIZE as usize);
let mut sigs: Vec<Signature> = Vec::with_capacity(MAX_BATCH_SIZE as usize);
Expand Down Expand Up @@ -171,6 +172,7 @@ mod benches {
#[benchmark]
fn respond_error() {
let responder: T::AccountId = whitelisted_caller();
Signers::<T>::insert(&responder, ());

let mut errs: Vec<ErrorResponse> = Vec::with_capacity(MAX_BATCH_SIZE as usize);

Expand Down Expand Up @@ -198,6 +200,7 @@ mod benches {
#[benchmark]
fn respond_bidirectional() {
let responder: T::AccountId = whitelisted_caller();
Signers::<T>::insert(&responder, ());

let request_id: [u8; 32] = [7u8; 32];
let output_vec = vec![8u8; MAX_SERIALIZED_OUTPUT_LENGTH as usize];
Expand All @@ -222,6 +225,28 @@ mod benches {
);
}

#[benchmark]
fn add_signer() {
let who: T::AccountId = whitelisted_caller();

#[extrinsic_call]
add_signer(RawOrigin::Root, who.clone());

assert!(Signers::<T>::contains_key(&who));
}

#[benchmark]
fn remove_signer() {
let who: T::AccountId = whitelisted_caller();
Signers::<T>::insert(&who, ());
SignerCount::<T>::put(1);

#[extrinsic_call]
remove_signer(RawOrigin::Root, who.clone());

assert!(!Signers::<T>::contains_key(&who));
}

#[benchmark]
fn pause() {
setup_config::<T>();
Expand Down
65 changes: 62 additions & 3 deletions pallets/signet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use ethereum::{AccessListItem, EIP1559TransactionMessage, TransactionAction};
use frame_support::{
dispatch::Pays,
pallet_prelude::*,
traits::{Currency, ExistenceRequirement},
PalletId,
Expand Down Expand Up @@ -32,6 +33,9 @@ const MAX_ERROR_MESSAGE_LENGTH: u32 = 1024;
/// Maximum batch sizes
const MAX_BATCH_SIZE: u32 = 100;

/// Maximum number of authorized responder (MPC signer) accounts.
const MAX_SIGNERS: u32 = 64;

/// Hard upper bound for chain ID length (used as BoundedVec bound)
pub const MAX_CHAIN_ID_LENGTH: u32 = 128;

Expand Down Expand Up @@ -130,6 +134,14 @@ pub mod pallet {
#[pallet::getter(fn signet_config)]
pub type SignetConfig<T: Config> = StorageValue<_, SignetConfigData<BalanceOf<T>>, OptionQuery>;

/// Accounts authorized to submit signature responses.
#[pallet::storage]
pub type Signers<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, (), OptionQuery>;

/// Number of authorized signer accounts currently registered.
#[pallet::storage]
pub type SignerCount<T: Config> = StorageValue<_, u32, ValueQuery>;

// ========================================
// Events
// ========================================
Expand Down Expand Up @@ -205,6 +217,11 @@ pub mod pallet {
serialized_output: Vec<u8>,
signature: Signature,
},

/// A new responder account has been authorized.
SignerAdded { who: T::AccountId },
/// A responder account has been deauthorized.
SignerRemoved { who: T::AccountId },
}

// ========================================
Expand All @@ -229,6 +246,14 @@ pub mod pallet {
InvalidAddress,
/// Priority fee cannot exceed max fee per gas (EIP-1559 requirement)
InvalidGasPrice,
/// Responder is not in the authorized signer set.
NotAuthorizedSigner,
/// The account is already an authorized signer.
SignerAlreadyExists,
/// The account is not an authorized signer.
SignerNotFound,
/// The maximum number of authorized signers has been reached.
TooManySigners,
}

// ========================================
Expand Down Expand Up @@ -387,13 +412,14 @@ pub mod pallet {

/// Respond to signature requests (batch support)
#[pallet::call_index(4)]
#[pallet::weight(<T as Config>::WeightInfo::respond())]
#[pallet::weight((<T as Config>::WeightInfo::respond(), Pays::No))]
pub fn respond(
origin: OriginFor<T>,
request_ids: BoundedVec<[u8; 32], ConstU32<MAX_BATCH_SIZE>>,
signatures: BoundedVec<Signature, ConstU32<MAX_BATCH_SIZE>>,
) -> DispatchResult {
let responder = ensure_signed(origin)?;
ensure!(Signers::<T>::contains_key(&responder), Error::<T>::NotAuthorizedSigner);

ensure!(request_ids.len() == signatures.len(), Error::<T>::InvalidInputLength);

Expand All @@ -410,12 +436,13 @@ pub mod pallet {

/// Report signature generation errors (batch support)
#[pallet::call_index(5)]
#[pallet::weight(<T as Config>::WeightInfo::respond_error())]
#[pallet::weight((<T as Config>::WeightInfo::respond_error(), Pays::No))]
pub fn respond_error(
origin: OriginFor<T>,
errors: BoundedVec<ErrorResponse, ConstU32<MAX_BATCH_SIZE>>,
) -> DispatchResult {
let responder = ensure_signed(origin)?;
ensure!(Signers::<T>::contains_key(&responder), Error::<T>::NotAuthorizedSigner);

for error in errors {
Self::deposit_event(Event::SignatureError {
Expand All @@ -430,14 +457,15 @@ pub mod pallet {

/// Provide a read response with signature
#[pallet::call_index(6)]
#[pallet::weight(<T as Config>::WeightInfo::respond_bidirectional())]
#[pallet::weight((<T as Config>::WeightInfo::respond_bidirectional(), Pays::No))]
pub fn respond_bidirectional(
origin: OriginFor<T>,
request_id: [u8; 32],
serialized_output: BoundedVec<u8, ConstU32<MAX_SERIALIZED_OUTPUT_LENGTH>>,
signature: Signature,
) -> DispatchResult {
let responder = ensure_signed(origin)?;
ensure!(Signers::<T>::contains_key(&responder), Error::<T>::NotAuthorizedSigner);

Self::deposit_event(Event::RespondBidirectionalEvent {
request_id,
Expand Down Expand Up @@ -484,6 +512,37 @@ pub mod pallet {
Self::deposit_event(Event::Unpaused);
Ok(())
}

/// Authorize an account to submit signature responses.
#[pallet::call_index(9)]
#[pallet::weight(<T as Config>::WeightInfo::add_signer())]
pub fn add_signer(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;

ensure!(!Signers::<T>::contains_key(&who), Error::<T>::SignerAlreadyExists);
ensure!(SignerCount::<T>::get() < MAX_SIGNERS, Error::<T>::TooManySigners);

Signers::<T>::insert(&who, ());
SignerCount::<T>::mutate(|n| *n = n.saturating_add(1));

Self::deposit_event(Event::SignerAdded { who });
Ok(())
}

/// Remove an account from the signer allowlist.
#[pallet::call_index(10)]
#[pallet::weight(<T as Config>::WeightInfo::remove_signer())]
pub fn remove_signer(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
T::UpdateOrigin::ensure_origin(origin)?;

ensure!(Signers::<T>::contains_key(&who), Error::<T>::SignerNotFound);

Signers::<T>::remove(&who);
SignerCount::<T>::mutate(|n| *n = n.saturating_sub(1));

Self::deposit_event(Event::SignerRemoved { who });
Ok(())
}
}

// Helper functions
Expand Down
1 change: 1 addition & 0 deletions pallets/signet/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod signer_allowlist;
mod test_cases;
pub mod utils;

Expand Down
Loading
Loading