Skip to content

Commit 1a620d6

Browse files
committed
remove fees & block external
1 parent 5147b04 commit 1a620d6

File tree

3 files changed

+56
-263
lines changed

3 files changed

+56
-263
lines changed

node/src/mev_shield/author.rs

Lines changed: 24 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ use chacha20poly1305::{
22
KeyInit, XChaCha20Poly1305, XNonce,
33
aead::{Aead, Payload},
44
};
5-
use frame_system_rpc_runtime_api::AccountNonceApi;
65
use ml_kem::{EncodedSizeUser, KemCore, MlKem768};
76
use node_subtensor_runtime as runtime;
87
use rand::rngs::OsRng;
9-
use sp_api::ProvideRuntimeApi;
108
use sp_core::blake2_256;
11-
use sp_runtime::{AccountId32, KeyTypeId};
9+
use sp_runtime::KeyTypeId;
1210
use std::sync::{Arc, Mutex};
1311
use subtensor_macros::freeze_struct;
1412
use tokio::time::sleep;
@@ -131,7 +129,7 @@ const AURA_KEY_TYPE: KeyTypeId = KeyTypeId(*b"aura");
131129

132130
/// Start background tasks:
133131
/// - per-slot ML‑KEM key rotation
134-
/// - at ~announce_at_ms announce the next key bytes on chain,
132+
/// - at ~announce_at_ms announce the next key bytes on chain (as an UNSIGNED tx),
135133
pub fn spawn_author_tasks<B, C, Pool>(
136134
task_spawner: &sc_service::SpawnTaskHandle,
137135
client: Arc<C>,
@@ -141,13 +139,7 @@ pub fn spawn_author_tasks<B, C, Pool>(
141139
) -> ShieldContext
142140
where
143141
B: sp_runtime::traits::Block,
144-
C: sc_client_api::HeaderBackend<B>
145-
+ sc_client_api::BlockchainEvents<B>
146-
+ ProvideRuntimeApi<B>
147-
+ Send
148-
+ Sync
149-
+ 'static,
150-
C::Api: AccountNonceApi<B, AccountId32, u32>,
142+
C: sc_client_api::HeaderBackend<B> + sc_client_api::BlockchainEvents<B> + Send + Sync + 'static,
151143
Pool: sc_transaction_pool_api::TransactionPool<Block = B> + Send + Sync + 'static,
152144
B::Extrinsic: From<sp_runtime::OpaqueExtrinsic>,
153145
{
@@ -156,25 +148,20 @@ where
156148
timing: timing.clone(),
157149
};
158150

151+
// Only run these tasks on nodes that actually have an Aura key in their keystore.
159152
let aura_keys: Vec<sp_core::sr25519::Public> = keystore.sr25519_public_keys(AURA_KEY_TYPE);
153+
if aura_keys.is_empty() {
154+
log::warn!(
155+
target: "mev-shield",
156+
"spawn_author_tasks: no local Aura sr25519 key in keystore; \
157+
this node will NOT announce MEV-Shield keys"
158+
);
159+
return ctx;
160+
}
160161

161-
let local_aura_pub = match aura_keys.first().copied() {
162-
Some(k) => k,
163-
None => {
164-
log::warn!(
165-
target: "mev-shield",
166-
"spawn_author_tasks: no local Aura sr25519 key in keystore; \
167-
this node will NOT announce MEV-Shield keys"
168-
);
169-
return ctx;
170-
}
171-
};
172-
173-
let aura_account: AccountId32 = local_aura_pub.into();
174162
let ctx_clone = ctx.clone();
175163
let client_clone = client.clone();
176164
let pool_clone = pool.clone();
177-
let keystore_clone = keystore.clone();
178165

179166
// Slot tick / key-announce loop.
180167
task_spawner.spawn(
@@ -243,37 +230,17 @@ where
243230
}
244231
};
245232

246-
// 🔑 Fetch the current on-chain nonce for the Aura account using the best block hash.
247-
let best_hash = client_clone.info().best_hash;
248-
249-
let nonce: u32 = match client_clone
250-
.runtime_api()
251-
.account_nonce(best_hash, aura_account.clone())
252-
{
253-
Ok(n) => n,
254-
Err(e) => {
255-
log::debug!(
256-
target: "mev-shield",
257-
"spawn_author_tasks: failed to fetch account nonce for MEV-Shield author: {e:?}",
258-
);
259-
continue;
260-
}
261-
};
262-
263-
// Submit announce_next_key signed with the Aura key using the correct nonce.
233+
// Submit announce_next_key as an UNSIGNED extrinsic (Origin::None).
264234
if let Err(e) = submit_announce_extrinsic::<B, C, Pool>(
265235
client_clone.clone(),
266236
pool_clone.clone(),
267-
keystore_clone.clone(),
268-
local_aura_pub,
269237
next_pk.clone(),
270-
nonce,
271238
)
272239
.await
273240
{
274241
log::debug!(
275242
target: "mev-shield",
276-
"announce_next_key submit error (nonce={nonce:?}): {e:?}"
243+
"announce_next_key unsigned submit error: {e:?}"
277244
);
278245
}
279246

@@ -305,140 +272,47 @@ where
305272
ctx
306273
}
307274

308-
/// Build & submit the signed `announce_next_key` extrinsic OFF-CHAIN
275+
/// Build & submit the **unsigned** `announce_next_key` extrinsic OFF-CHAIN
309276
pub async fn submit_announce_extrinsic<B, C, Pool>(
310277
client: Arc<C>,
311278
pool: Arc<Pool>,
312-
keystore: sp_keystore::KeystorePtr,
313-
aura_pub: sp_core::sr25519::Public,
314279
next_public_key: Vec<u8>,
315-
nonce: u32,
316280
) -> anyhow::Result<()>
317281
where
318282
B: sp_runtime::traits::Block,
319283
C: sc_client_api::HeaderBackend<B> + Send + Sync + 'static,
320284
Pool: sc_transaction_pool_api::TransactionPool<Block = B> + Send + Sync + 'static,
321285
B::Extrinsic: From<sp_runtime::OpaqueExtrinsic>,
322-
B::Hash: AsRef<[u8]>,
323286
{
324-
use node_subtensor_runtime as runtime;
325-
use runtime::{RuntimeCall, SignedPayload, UncheckedExtrinsic};
326-
287+
use runtime::{RuntimeCall, UncheckedExtrinsic};
327288
use sc_transaction_pool_api::TransactionSource;
328-
use sp_core::H256;
329289
use sp_runtime::codec::Encode;
330-
use sp_runtime::{
331-
BoundedVec, MultiSignature,
332-
generic::Era,
333-
traits::{ConstU32, TransactionExtension},
334-
};
335-
336-
fn to_h256<H: AsRef<[u8]>>(h: H) -> H256 {
337-
let bytes = h.as_ref();
338-
let mut out = [0u8; 32];
339-
340-
if bytes.is_empty() {
341-
return H256(out);
342-
}
343-
344-
let n = bytes.len().min(32);
345-
let src_start = bytes.len().saturating_sub(n);
346-
let dst_start = 32usize.saturating_sub(n);
347-
348-
let src_slice = bytes.get(src_start..).and_then(|s| s.get(..n));
349-
350-
if let (Some(dst), Some(src)) = (out.get_mut(dst_start..32), src_slice) {
351-
dst.copy_from_slice(src);
352-
H256(out)
353-
} else {
354-
// Extremely defensive fallback.
355-
H256([0u8; 32])
356-
}
357-
}
290+
use sp_runtime::{BoundedVec, traits::ConstU32};
358291

359292
type MaxPk = ConstU32<2048>;
360293
let public_key: BoundedVec<u8, MaxPk> = BoundedVec::try_from(next_public_key)
361294
.map_err(|_| anyhow::anyhow!("public key too long (>2048 bytes)"))?;
362295

363-
// 1) Runtime call carrying the public key bytes.
296+
// Runtime call carrying the public key bytes.
364297
let call = RuntimeCall::MevShield(pallet_shield::Call::announce_next_key { public_key });
365298

366-
// 2) Build the transaction extensions exactly like the runtime.
367-
type Extra = runtime::TransactionExtensions;
368-
let extra: Extra =
369-
(
370-
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
371-
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
372-
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
373-
frame_system::CheckGenesis::<runtime::Runtime>::new(),
374-
frame_system::CheckEra::<runtime::Runtime>::from(Era::Immortal),
375-
node_subtensor_runtime::check_nonce::CheckNonce::<runtime::Runtime>::from(nonce).into(),
376-
frame_system::CheckWeight::<runtime::Runtime>::new(),
377-
node_subtensor_runtime::transaction_payment_wrapper::ChargeTransactionPaymentWrapper::<
378-
runtime::Runtime,
379-
>::new(pallet_transaction_payment::ChargeTransactionPayment::<
380-
runtime::Runtime,
381-
>::from(0u64)),
382-
pallet_subtensor::transaction_extension::SubtensorTransactionExtension::<
383-
runtime::Runtime,
384-
>::new(),
385-
pallet_drand::drand_priority::DrandPriority::<runtime::Runtime>::new(),
386-
frame_metadata_hash_extension::CheckMetadataHash::<runtime::Runtime>::new(false),
387-
);
388-
389-
// 3) Manually construct the `Implicit` tuple that the runtime will also derive.
390-
type Implicit = <Extra as TransactionExtension<RuntimeCall>>::Implicit;
391-
392-
let info = client.info();
393-
let genesis_h256: H256 = to_h256(info.genesis_hash);
394-
395-
let implicit: Implicit = (
396-
(), // CheckNonZeroSender
397-
runtime::VERSION.spec_version, // CheckSpecVersion::Implicit = u32
398-
runtime::VERSION.transaction_version, // CheckTxVersion::Implicit = u32
399-
genesis_h256, // CheckGenesis::Implicit = Hash
400-
genesis_h256, // CheckEra::Implicit (Immortal => genesis hash)
401-
(), // CheckNonce::Implicit = ()
402-
(), // CheckWeight::Implicit = ()
403-
(), // ChargeTransactionPaymentWrapper::Implicit = ()
404-
(), // SubtensorTransactionExtension::Implicit = ()
405-
(), // DrandPriority::Implicit = ()
406-
None, // CheckMetadataHash::Implicit = Option<[u8; 32]>
407-
);
408-
409-
// 4) Build the exact signable payload from call + extra + implicit.
410-
let payload: SignedPayload = SignedPayload::from_raw(call.clone(), extra.clone(), implicit);
411-
412-
// 5) Sign with the local Aura key using the same SCALE bytes the runtime expects.
413-
let sig_opt = payload
414-
.using_encoded(|bytes| keystore.sr25519_sign(AURA_KEY_TYPE, &aura_pub, bytes))
415-
.map_err(|e| anyhow::anyhow!("keystore sr25519_sign error: {e:?}"))?;
416-
417-
let sig = sig_opt
418-
.ok_or_else(|| anyhow::anyhow!("keystore sr25519_sign returned None for Aura key"))?;
419-
420-
let signature: MultiSignature = sig.into();
421-
422-
// 6) Sender address = AccountId32 derived from the Aura sr25519 public key.
423-
let who: AccountId32 = aura_pub.into();
424-
let address = sp_runtime::MultiAddress::Id(who);
425-
426-
// 7) Assemble the signed extrinsic and submit it to the pool.
427-
let uxt: UncheckedExtrinsic = UncheckedExtrinsic::new_signed(call, address, signature, extra);
299+
// Build UNSIGNED extrinsic (origin = None) using Frontier's `new_bare`.
300+
let uxt: UncheckedExtrinsic = UncheckedExtrinsic::new_bare(call);
428301

429302
let xt_bytes = uxt.encode();
430-
let xt_hash = sp_core::hashing::blake2_256(&xt_bytes);
303+
let xt_hash = blake2_256(&xt_bytes);
431304
let xt_hash_hex = hex::encode(xt_hash);
432305

433306
let opaque: sp_runtime::OpaqueExtrinsic = uxt.into();
434307
let xt: <B as sp_runtime::traits::Block>::Extrinsic = opaque.into();
435308

436-
pool.submit_one(info.best_hash, TransactionSource::Local, xt)
309+
let best_hash = client.info().best_hash;
310+
pool.submit_one(best_hash, TransactionSource::Local, xt)
437311
.await?;
438312

439313
log::debug!(
440314
target: "mev-shield",
441-
"announce_next_key submitted: xt=0x{xt_hash_hex}, nonce={nonce:?}",
315+
"announce_next_key (unsigned) submitted: xt=0x{xt_hash_hex}",
442316
);
443317

444318
Ok(())

pallets/shield/src/lib.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,14 @@ pub mod pallet {
249249
.saturating_add(T::DbWeight::get().reads(1_u64))
250250
.saturating_add(T::DbWeight::get().writes(1_u64)),
251251
DispatchClass::Operational,
252-
Pays::Yes
252+
Pays::No
253253
))]
254254
#[allow(clippy::useless_conversion)]
255255
pub fn announce_next_key(
256256
origin: OriginFor<T>,
257257
public_key: BoundedVec<u8, ConstU32<2048>>,
258-
) -> DispatchResultWithPostInfo {
259-
// Only a current Aura validator may call this (signed account ∈ Aura authorities)
260-
T::AuthorityOrigin::ensure_validator(origin)?;
258+
) -> DispatchResult {
259+
ensure_none(origin)?;
261260

262261
const MAX_KYBER768_PK_LENGTH: usize = 1184;
263262
ensure!(
@@ -267,11 +266,7 @@ pub mod pallet {
267266

268267
NextKey::<T>::put(public_key);
269268

270-
// Refund the fee on success by setting pays_fee = Pays::No
271-
Ok(PostDispatchInfo {
272-
actual_weight: None,
273-
pays_fee: Pays::No,
274-
})
269+
Ok(())
275270
}
276271

277272
/// Users submit an encrypted wrapper.
@@ -352,7 +347,7 @@ pub mod pallet {
352347
origin: OriginFor<T>,
353348
id: T::Hash,
354349
reason: BoundedVec<u8, ConstU32<256>>,
355-
) -> DispatchResult {
350+
) -> DispatchResultWithPostInfo {
356351
// Unsigned: only the author node may inject this via ValidateUnsigned.
357352
ensure_none(origin)?;
358353

@@ -364,7 +359,7 @@ pub mod pallet {
364359
// Emit event to notify clients
365360
Self::deposit_event(Event::DecryptionFailed { id, reason });
366361

367-
Ok(())
362+
Ok(().into())
368363
}
369364
}
370365

@@ -387,6 +382,19 @@ pub mod pallet {
387382
_ => InvalidTransaction::Call.into(),
388383
}
389384
}
385+
Call::announce_next_key { public_key, .. } => {
386+
match source {
387+
TransactionSource::Local | TransactionSource::InBlock => {
388+
ValidTransaction::with_tag_prefix("mev-shield-failed")
389+
.priority(10_000u64)
390+
.longevity(4)
391+
.and_provides(public_key) // dedupe by public_key
392+
.propagate(true)
393+
.build()
394+
}
395+
_ => InvalidTransaction::Call.into(),
396+
}
397+
}
390398
_ => InvalidTransaction::Call.into(),
391399
}
392400
}

0 commit comments

Comments
 (0)