Skip to content

Commit

Permalink
feat: Use AccountId32 for account identifier (#65)
Browse files Browse the repository at this point in the history
* feat: Add recovering key from p256 signature

* wip: Add serde feature to UniversalAddress

* chore: Disable node & runtime build

* feat: Add p256 recovery to runtime

* feat: Replace UniversalSigner with UniversalAddress

* refactor: Remove unused dependency

* build: Re-enable runtime

* build: Re-enable node

* fix: Fix build errors

* Override substrate types

* Separate Ethereum RPC server

* fix: Allow only low-s signature for p256

* refactor: Improve check_origin in webauthn signature

* feat: Re-enable webauthn signature scheme

* style: Use tabs for indent

* refactor: Clean up types

* feat: Add cosmos rpc server

* chore: Bump version to 0.3.0-dev

* test: Fix broken tests

* fix: Fix invalid recovery id with p256 signature

---------

Co-authored-by: Jungyong Um <[email protected]>
  • Loading branch information
conr2d and Jungyong Um authored Jun 12, 2024
1 parent 5812378 commit 8ecf782
Show file tree
Hide file tree
Showing 60 changed files with 4,347 additions and 1,180 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,32 @@ members = [
authors = ["Haderech Pte. Ltd."]
edition = "2021"
repository = "https://github.com/noirhq/noir/"
version = "0.2.0"
version = "0.3.0-dev"

[workspace.dependencies]
array-bytes = "6.2.2"
async-trait = "0.1.66"
base64ct = { version = "1.6.0", default-features = false }
clap = { version = "4.1.8", features = ["derive"] }
ecdsa = "0.16.8"
ethereum = { version = "0.15.0", default-features = false, features = ["with-codec"] }
futures = { version = "0.3.26", features = ["thread-pool"] }
hmac = { version = "0.12.1", default-features = false }
jsonrpsee = { version = "0.22.5", features = ["server"] }
lazy_static = { version = "1.4.0", default-features = false }
log = "0.4.17"
parity-scale-codec = { version = "3.4.0", default-features = false, features = ["derive"] }
p256 = { version = "0.13.0", default-features = false, features = ["ecdsa", "alloc"] }
p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
regex = "1.7.1"
scale-info = { version = "2.3.1", default-features = false, features = ["derive"] }
secp256k1 = { version = "0.28.1", default-features = false, features = ["alloc"] }
serde = { version = "1.0.152", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.94", default-features = false }
sha2 = { version = "0.10.6", default-features = false }
substrate-bip39 = { version = "0.4.4" }
thiserror = "1.0.61"
tiny-bip39 = "1.0.0"
url = "2.5.0"
zeroize = { version = "1.5.7", default-features = false }

# noir
Expand Down
13 changes: 11 additions & 2 deletions core-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use sp_runtime::traits::{IdentifyAccount, Verify};

/// An index to a block.
pub type BlockNumber = u32;
/// An instant or duration in time.
pub type Moment = u64;
/// Block header type as expected by this runtime.
pub type Header = sp_runtime::generic::Header<BlockNumber, sp_runtime::traits::BlakeTwo256>;
/// Block type as expected by this runtime.
Expand All @@ -33,13 +35,20 @@ pub type Hash = sp_core::H256;
/// Balance of an account.
pub type Balance = u128;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = np_runtime::UniversalSignature;
pub type Signature = np_runtime::AuthenticationProof;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// Index of a transaction in the chain.
pub type Nonce = u32;
/// The type for looking up accounts.
pub type AccountIndex = u128;
pub type AccountIndex = ();
/// The address format for describing accounts.
pub type Address = sp_runtime::MultiAddress<AccountId, AccountIndex>;

/// Basic currency unit.
pub const DOLLARS: Balance = 1_000_000_000_000_000_000;
/// Decimals of currency.
pub const DECIMALS: u8 = 18;
/// Symbol of currency.
pub const SYMBOL: &str = "CDT";
1 change: 1 addition & 0 deletions frame/alias/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ std = [
"sp-runtime/std",
"sp-std/std",
]
try-runtime = []
204 changes: 31 additions & 173 deletions frame/alias/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,26 @@

pub mod weights;

pub use pallet::*;

use crate::weights::WeightInfo;
use np_crypto::ecdsa::EcdsaExt;
use np_runtime::AccountName;
pub use pallet::*;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{LookupError, StaticLookup},
DispatchError, MultiAddress,
};
use sp_std::prelude::*;

type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;

/// A generator for tag number that discriminates the same name accounts.
pub trait TagGenerator<T: Config> {
fn tag(id: &T::AccountId, name: &str) -> Result<u16, ()>;
}
use sp_runtime::{BoundedBTreeSet, DispatchError};
use sp_std::{collections::btree_set::BTreeSet, prelude::*};

#[cfg_attr(feature = "std", derive(Hash))]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum AccountAlias {
AccountName(AccountName),
EthereumAddress([u8; 20]),
CosmosAddress([u8; 20]),
}

#[frame_support::pallet]
pub mod pallet {

use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

/// The module's config trait.
#[pallet::config]
Expand All @@ -63,126 +50,14 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
/// The generator for tag number that discriminates the same name accounts.
type TagGenerator: TagGenerator<Self>;
}

#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::call]
impl<T: Config> Pallet<T>
where
T::AccountId: EcdsaExt,
{
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::create_account_name())]
pub fn create_account_name(origin: OriginFor<T>, name: Vec<u8>) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(AccountNameOf::<T>::get(&who).is_none(), Error::<T>::AlreadyExists);
let name =
sp_std::str::from_utf8(&name[..]).map_err(|_| Error::<T>::InvalidNameFormat)?;
let tag =
T::TagGenerator::tag(&who, name).map_err(|_| Error::<T>::TagGenerationFailed)?;
let account_name =
AccountName::new(&name, tag).map_err(|_| Error::<T>::InvalidNameFormat)?;
AccountIdOf::<T>::try_mutate(
AccountAlias::AccountName(account_name),
|maybe_value| -> DispatchResult {
ensure!(maybe_value.is_none(), Error::<T>::InUse);
*maybe_value = Some(who.clone());
Ok(())
},
)?;
AccountNameOf::<T>::insert(&who, account_name);
Self::deposit_event(Event::<T>::AccountNameUpdated {
who,
name: account_name,
deleted: None,
});
Ok(())
}

#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::update_account_name())]
pub fn update_account_name(origin: OriginFor<T>, new_name: Vec<u8>) -> DispatchResult {
let who = ensure_signed(origin)?;
let account_name = AccountNameOf::<T>::get(&who).ok_or(Error::<T>::NotExists)?;
let new_name =
sp_std::str::from_utf8(&new_name[..]).map_err(|_| Error::<T>::InvalidNameFormat)?;
let tag = T::TagGenerator::tag(&who, new_name)
.map_err(|_| Error::<T>::TagGenerationFailed)?;
let new_account_name =
AccountName::new(&new_name, tag).map_err(|_| Error::<T>::InvalidNameFormat)?;
AccountIdOf::<T>::try_mutate(
AccountAlias::AccountName(new_account_name),
|maybe_value| -> DispatchResult {
ensure!(maybe_value.is_none(), Error::<T>::InUse);
*maybe_value = Some(who.clone());
Ok(())
},
)?;
AccountIdOf::<T>::remove(AccountAlias::AccountName(account_name));
AccountNameOf::<T>::insert(&who, new_account_name);
Self::deposit_event(Event::<T>::AccountNameUpdated {
who,
name: new_account_name,
deleted: Some(account_name),
});
Ok(())
}

#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::connect_aliases())]
pub fn connect_aliases(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::connect_aliases_secp256k1(&who)?;
Ok(())
}

#[pallet::call_index(3)]
#[pallet::weight(T::WeightInfo::force_set_account_name())]
pub fn force_set_account_name(
origin: OriginFor<T>,
dest: AccountIdLookupOf<T>,
name: Vec<u8>,
tag: u16,
) -> DispatchResult {
ensure_root(origin)?;
let dest = T::Lookup::lookup(dest)?;
let name =
sp_std::str::from_utf8(&name[..]).map_err(|_| Error::<T>::InvalidNameFormat)?;
let new_account_name =
AccountName::new(&name, tag).map_err(|_| Error::<T>::InvalidNameFormat)?;
AccountIdOf::<T>::try_mutate(
AccountAlias::AccountName(new_account_name),
|maybe_value| -> DispatchResult {
ensure!(maybe_value.is_none(), Error::<T>::InUse);
*maybe_value = Some(dest.clone());
Ok(())
},
)?;

let past_name = AccountNameOf::<T>::get(&dest);
match past_name {
Some(past_name) => AccountIdOf::<T>::remove(AccountAlias::AccountName(past_name)),
None => (),
};
AccountNameOf::<T>::insert(&dest, new_account_name);
Self::deposit_event(Event::<T>::AccountNameUpdated {
who: dest,
name: new_account_name,
deleted: past_name,
});
Ok(())
}
}
pub struct Pallet<T>(_);

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// An account name was updated.
AccountNameUpdated { who: T::AccountId, name: AccountName, deleted: Option<AccountName> },
/// An ethereum address was published.
EthereumAddressPublished { who: T::AccountId, address: [u8; 20] },
/// An cosmos address was published.
Expand All @@ -191,83 +66,66 @@ pub mod pallet {

#[pallet::error]
pub enum Error<T> {
/// The account name already exists.
AlreadyExists,
/// The account name does not exists.
NotExists,
/// The account name is not available.
InUse,
/// Invalid name foramt.
InvalidNameFormat,
/// Tag generation failed.
TagGenerationFailed,
/// Ethereum address conversion failed.
EthereumAddressConversionFailed,
/// Cosmos address conversion failed.
CosmosAddressConversionFailed,
}

#[pallet::storage]
#[pallet::getter(fn accountid)]
pub type AccountIdOf<T: Config> = StorageMap<_, Blake2_128Concat, AccountAlias, T::AccountId>;

#[pallet::storage]
pub type AccountNameOf<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, AccountName>;
#[pallet::getter(fn aliases)]
pub type AccountAliases<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, BoundedBTreeSet<AccountAlias, ConstU32<2>>>;
}

impl<T: Config> Pallet<T>
where
T::AccountId: EcdsaExt,
{
// PUBLIC IMMUTABLES

/// Lookup an AccountAlias to get an Id, if exists.
pub fn lookup(alias: &AccountAlias) -> Option<T::AccountId> {
AccountIdOf::<T>::get(alias).map(|x| x)
}

pub fn connect_aliases_secp256k1(who: &T::AccountId) -> Result<(), DispatchError> {
let ethereum_address = who
pub fn alias_secp256k1(who: &T::AccountId) -> Result<(), DispatchError> {
let mut aliases = BTreeSet::new();
let eth = who
.to_eth_address()
.map(|x| x.into())
.ok_or(Error::<T>::EthereumAddressConversionFailed)?;
if AccountIdOf::<T>::get(AccountAlias::EthereumAddress(ethereum_address)).is_none() {
AccountIdOf::<T>::insert(AccountAlias::EthereumAddress(ethereum_address), who);
let eth_alias = AccountAlias::EthereumAddress(eth);
if AccountIdOf::<T>::get(eth_alias).is_none() {
AccountIdOf::<T>::insert(eth_alias, who.clone());
aliases.insert(eth_alias);
Self::deposit_event(Event::<T>::EthereumAddressPublished {
who: who.clone(),
address: ethereum_address,
address: eth,
});
}
let cosmos_address = who
let cosm = who
.to_cosm_address()
.map(|x| x.into())
.ok_or(Error::<T>::CosmosAddressConversionFailed)?;
if AccountIdOf::<T>::get(AccountAlias::CosmosAddress(cosmos_address)).is_none() {
AccountIdOf::<T>::insert(AccountAlias::CosmosAddress(cosmos_address), who);
let cosm_alias = AccountAlias::CosmosAddress(cosm);
if AccountIdOf::<T>::get(cosm_alias).is_none() {
AccountIdOf::<T>::insert(cosm_alias, who.clone());
aliases.insert(cosm_alias);
Self::deposit_event(Event::<T>::CosmosAddressPublished {
who: who.clone(),
address: cosmos_address,
address: cosm,
});
}
Ok(())
}
}

impl<T: Config> StaticLookup for Pallet<T>
where
T::AccountId: EcdsaExt,
{
type Source = MultiAddress<T::AccountId, AccountName>;
type Target = T::AccountId;

fn lookup(a: Self::Source) -> Result<Self::Target, LookupError> {
match a {
MultiAddress::Id(id) => Ok(id),
MultiAddress::Index(name) =>
Self::lookup(&AccountAlias::AccountName(name)).ok_or(LookupError),
_ => Err(LookupError),
if !aliases.is_empty() {
AccountAliases::<T>::insert(
who,
BoundedBTreeSet::try_from(aliases)
.map_err(|_| DispatchError::Other("Too many aliases"))?,
);
}
}

fn unlookup(a: Self::Target) -> Self::Source {
MultiAddress::Id(a)
Ok(())
}
}
28 changes: 2 additions & 26 deletions frame/alias/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,17 @@ use frame_support::{traits::Get, weights::Weight};

/// Weight functions needed for pallet_alias.
pub trait WeightInfo {
fn create_account_name() -> Weight;
fn update_account_name() -> Weight;
fn connect_aliases() -> Weight;
fn force_set_account_name() -> Weight;
fn alias() -> Weight;
}

/// Weights for pallet_alias using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: AccountIdOf (r:1 w:1), AccountNameOf (r:1 w:1)
fn create_account_name() -> Weight {
// Base fee
Weight::from_parts(50_000_000, 0)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(2 as u64))
}
// Storage: AccountIdOf (r:1 w:2), AccountNameOf (r:1 w:1)
fn update_account_name() -> Weight {
// Base fee
Weight::from_parts(100_000_000 as u64, 0)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
// Storage: AccountIdOf (r:1 w:1), AccountNameOf (r:1 w:1)
fn connect_aliases() -> Weight {
fn alias() -> Weight {
// Base fee
Weight::from_parts(50_000_000, 0)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: AccountIdOf (r:1 w:2), AccountNameOf (r:1 w:1)
fn force_set_account_name() -> Weight {
// Base fee
Weight::from_parts(50_000_000, 0)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
}
Loading

0 comments on commit 8ecf782

Please sign in to comment.