Skip to content
Merged
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
2 changes: 1 addition & 1 deletion frame/account-mapping/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pallet-account-mapping"
version = "0.1.0"
version = "0.2.0"
license = "Apache-2.0"
description = "Stateful mapping between Substrate AccountId32 and EVM H160 addresses, with alias registration and multichain identity support."
authors = { workspace = true }
Expand Down
11 changes: 11 additions & 0 deletions frame/account-mapping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ pub mod pallet {
CommitmentMismatch,
/// The ZK proof for a private link dispatch is invalid.
InvalidProof,
/// Attempted to register a Substrate-native signature scheme as a chain link scheme.
/// Chain links are exclusively for external, non-Substrate ecosystems.
SubstrateNativeSchemeNotAllowed,
}

// ──────────────────────────────────────────────
Expand All @@ -386,6 +389,14 @@ pub mod pallet {
scheme: SignatureScheme,
) -> DispatchResult {
ensure_root(origin)?;
// Chain links are exclusively for external, non-Substrate-native ecosystems.
// Substrate accounts are already self-proving via their AccountId32; linking
// them with a chain link is meaningless and would create a confusing identity
// model. See SignatureScheme::is_for_external_chain() for the invariant.
ensure!(
scheme.is_for_external_chain(),
Error::<T>::SubstrateNativeSchemeNotAllowed
);
SupportedChains::<T>::insert(chain_id, scheme.clone());
Self::deposit_event(Event::SupportedChainAdded { chain_id, scheme });
Ok(())
Expand Down
41 changes: 40 additions & 1 deletion frame/account-mapping/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,24 @@ pub const MAX_WHITELIST_SIZE: u32 = 20;
/// Maximum length for metadata strings (bio, avatar CID, etc.).
pub const MAX_METADATA_LEN: u32 = 128;

/// Opaque chain identifier used as storage key for cross-chain links.
///
/// The `u32` namespace is split into two ranges using the high bit (bit 31):
///
/// | Bit 31 | Range | Standard | Examples |
/// |--------|-------|----------|---------|
/// | `0` | `0x0000_0000 – 0x7FFF_FFFF` | EIP-155 (EVM chain IDs) | Ethereum = 1, Polygon = 137 |
/// | `1` | `0x8000_0000 – 0xFFFF_FFFF` | SLIP-0044 (HD wallet coin types) | Bitcoin = `0x8000_0000`, Solana = `0x8000_01F5` |
///
/// Use `SLIP0044_NAMESPACE | <coin_type>` to build SLIP-0044 identifiers.
/// Canonical constants for well-known chains are defined in `protocol-core`.
/// Governance MUST follow this convention when registering chains via `add_supported_chain`.
pub type ChainId = u32;

/// Bitmask to set bit 31, placing a coin type in the SLIP-0044 namespace.
/// Example: `SLIP0044_NAMESPACE | 0` = Bitcoin, `SLIP0044_NAMESPACE | 501` = Solana.
pub const SLIP0044_NAMESPACE: u32 = 0x8000_0000;

pub type ExternalAddr = BoundedVec<u8, ConstU32<MAX_EXTERNAL_ADDR_LEN>>;

#[derive(
Expand All @@ -27,13 +44,35 @@ pub type ExternalAddr = BoundedVec<u8, ConstU32<MAX_EXTERNAL_ADDR_LEN>>;
scale_codec::DecodeWithMemTracking
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
/// Signature verification scheme used to prove ownership of a cross-chain wallet.
///
/// # Design invariant
///
/// Every variant MUST represent an **external, non-Substrate-native** signing scheme.
/// Substrate-native schemes (Sr25519, Ed25519-Substrate, etc.) MUST NOT be added here
/// because an `AccountId32` is already self-proving in the Substrate key model — no
/// cross-chain proof of ownership is required or meaningful. `add_supported_chain`
/// enforces this invariant at the governance call level via `is_for_external_chain()`.
pub enum SignatureScheme {
/// Ethereum-style EIP-191 (ECDSA over Keccak256).
Eip191,
/// Solana-style Ed25519 (raw signature of the message).
/// Solana-style raw Ed25519 (not the Substrate Ed25519 wrapper).
Ed25519,
}

impl SignatureScheme {
/// Returns `true` if this scheme belongs to a non-Substrate-native ecosystem.
///
/// All current variants are external. Any future variant for a Substrate-native
/// scheme MUST return `false` here so that `add_supported_chain` rejects it.
pub fn is_for_external_chain(&self) -> bool {
match self {
SignatureScheme::Eip191 => true,
SignatureScheme::Ed25519 => true,
}
}
}

#[derive(
Encode,
Decode,
Expand Down
Loading