Skip to content

Commit

Permalink
feat: Add Nostr address support (#86)
Browse files Browse the repository at this point in the history
* feat: Introduce np-nostr for Nostr primitive types

* feat: Add Nostr address support to frame-babel

* test: Fix build errors for tests with all features

* test: Add transfer to Nostr address test
  • Loading branch information
conr2d authored Oct 28, 2024
1 parent 3765dbc commit eb8db91
Show file tree
Hide file tree
Showing 11 changed files with 352 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"primitives/babel",
"primitives/cosmos",
"primitives/ethereum",
"primitives/nostr",
"primitives/runtime",
"runtime/common",
"vendor/composable/composable-support",
Expand Down Expand Up @@ -52,6 +53,7 @@ noir-runtime-common = { path = "runtime/common", default-features = false }
np-babel = { path = "primitives/babel", default-features = false }
np-cosmos = { path = "primitives/cosmos", default-features = false }
np-ethereum = { path = "primitives/ethereum", default-features = false }
np-nostr = { path = "primitives/nostr", default-features = false }
np-runtime = { path = "primitives/runtime", default-features = false }
pallet-cosmos = { path = "frame/cosmos", default-features = false }
pallet-cosmos-types = { path = "frame/cosmos/types", default-features = false }
Expand Down
6 changes: 6 additions & 0 deletions frame/babel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pallet-assets = { git = "https://github.com/paritytech/polkadot-sdk", branch = "
pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
sp-keyring = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
# frontier
pallet-ethereum = { git = "https://github.com/noirhq/frontier", branch = "stable2409" }
pallet-evm = { git = "https://github.com/noirhq/frontier", branch = "stable2409" }
Expand Down Expand Up @@ -139,6 +140,9 @@ ethereum = [
"pallet-evm-precompile-simple",
"precompile-utils",
]
nostr = [
"np-babel/nostr",
]
pallet = [
"cosmos",
"ethereum",
Expand All @@ -147,6 +151,7 @@ pallet = [
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-ethereum?/runtime-benchmarks",
"pallet-evm?/runtime-benchmarks",
Expand All @@ -156,6 +161,7 @@ runtime-benchmarks = [
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-assets/try-runtime",
"pallet-balances/try-runtime",
"pallet-ethereum?/try-runtime",
"pallet-evm?/try-runtime",
Expand Down
15 changes: 14 additions & 1 deletion frame/babel/src/extensions/unify_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ impl<T: Config> UnifyAccount<T> {
T::AddressMap::try_insert(who, VarAddress::Cosmos(address))
.map_err(|_| "account unification failed: cosmos")?;
}
#[cfg(feature = "nostr")]
{
let address = np_babel::NostrAddress::from(public);
let interim = address.clone().into_account_truncating();
T::DrainBalance::drain_balance(&interim, who)?;
T::AddressMap::try_insert(who, VarAddress::Nostr(address))
.map_err(|_| "account unification failed: cosmos")?;
}
}
Ok(())
}
Expand Down Expand Up @@ -207,6 +215,11 @@ mod tests {
let cosmos = VarAddress::Cosmos(dev_public().into());
assert_eq!(<MockConfig as Config>::AddressMap::find_key(cosmos), Some(who.clone()));
let ethereum = VarAddress::Ethereum(dev_public().into());
assert_eq!(<MockConfig as Config>::AddressMap::find_key(ethereum), Some(who));
assert_eq!(<MockConfig as Config>::AddressMap::find_key(ethereum), Some(who.clone()));
#[cfg(feature = "nostr")]
{
let nostr = VarAddress::Nostr(dev_public().into());
assert_eq!(<MockConfig as Config>::AddressMap::find_key(nostr), Some(who));
}
}
}
18 changes: 12 additions & 6 deletions frame/babel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ extern crate alloc;

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

#[cfg(feature = "cosmos")]
pub mod cosmos;
Expand Down Expand Up @@ -60,10 +62,11 @@ pub mod pallet {
use pallet_cosmos_x_auth_signing::sign_verifiable_tx::traits::SigVerifiableTx;
use pallet_evm::{AddressMapping as _, FrameSystemAccountProvider};
use pallet_multimap::traits::{UniqueMap, UniqueMultimap};
use sp_core::ecdsa;
use sp_runtime::{
traits::{AtLeast32BitUnsigned, One, Saturating, StaticLookup, UniqueSaturatedInto},
AccountId32,
use sp_core::{ecdsa, H256};
#[cfg(feature = "nostr")]
use sp_runtime::traits::AccountIdConversion;
use sp_runtime::traits::{
AtLeast32BitUnsigned, One, Saturating, StaticLookup, UniqueSaturatedInto,
};

type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
Expand Down Expand Up @@ -106,7 +109,7 @@ pub mod pallet {
where
OriginFor<T>: Into<Result<pallet_ethereum::RawOrigin, OriginFor<T>>>
+ Into<Result<pallet_cosmos::RawOrigin, OriginFor<T>>>,
T::AccountId: TryInto<ecdsa::Public> + From<AccountId32>,
T::AccountId: TryInto<ecdsa::Public> + From<H256>,
T::RuntimeOrigin: From<pallet_ethereum::RawOrigin> + From<pallet_cosmos::RawOrigin>,
{
#[pallet::call_index(0)]
Expand Down Expand Up @@ -262,7 +265,10 @@ pub mod pallet {
<T as pallet_cosmos::Config>::AddressMapping::into_account_id(address.into()),
VarAddress::Ethereum(address) =>
<T as pallet_evm::Config>::AddressMapping::into_account_id(address.into()),
VarAddress::Polkadot(address) => address.into(),
VarAddress::Polkadot(address) => H256::from(<[u8; 32]>::from(address)).into(),
#[cfg(feature = "nostr")]
VarAddress::Nostr(ref address) =>
T::AddressMap::find_key(&dest).unwrap_or(address.into_account_truncating()),
};

match id {
Expand Down
28 changes: 21 additions & 7 deletions frame/babel/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,16 @@ use pallet_cosmos_x_wasm::msgs::{
};
use pallet_cosmwasm::instrument::CostRules;
use pallet_multimap::traits::UniqueMap;
use sp_core::{ConstU128, H256};
use sp_core::{ConstU128, Pair, H256};
use sp_runtime::{
traits::{IdentityLookup, TryConvert},
BoundedVec,
BoundedVec, BuildStorage,
};

type AccountId = AccountId32<MultiSigner>;
type Balance = u128;
type AssetId = u32;
type Hash = H256;
pub type AccountId = AccountId32<MultiSigner>;
pub type Balance = u128;
pub type AssetId = u32;
pub type Hash = H256;

#[frame_support::runtime]
mod runtime {
Expand Down Expand Up @@ -157,6 +157,8 @@ impl pallet_assets::Config for Test {
type MetadataDepositBase = ConstU128<0>;
type MetadataDepositPerByte = ConstU128<0>;
type ApprovalDeposit = ConstU128<0>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}

#[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)]
Expand Down Expand Up @@ -348,5 +350,17 @@ impl frame_babel::Config for Test {

impl unify_account::Config for Test {
type AddressMap = AddressMap;
type DrainBalance = ();
type DrainBalance = Balances;
}

pub fn alice() -> AccountId {
sp_keyring::sr25519::Keyring::Alice.pair().public().into()
}

pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<Test> { balances: vec![(alice(), 10000)] }
.assimilate_storage(&mut t)
.unwrap();
t.into()
}
81 changes: 81 additions & 0 deletions frame/babel/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// This file is part of Noir.

// Copyright (c) Haderech Pte. Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use crate::{mock::*, *};
use frame_support::{assert_ok, traits::fungible::Inspect};
use np_babel::EthereumAddress;
use sp_core::ecdsa;
use sp_runtime::traits::AccountIdConversion;

fn dev_public() -> ecdsa::Public {
const_hex::decode_to_array(
b"02509540919faacf9ab52146c9aa40db68172d83777250b28e4679176e49ccdd9f",
)
.unwrap()
.into()
}

#[test]
fn transfer_to_ethereum_address_works() {
let account = AccountId::from(dev_public());
let address = EthereumAddress::from(dev_public());
let interim = address.into_account_truncating();

new_test_ext().execute_with(|| {
assert_ok!(Babel::transfer(
RuntimeOrigin::signed(alice()),
None,
VarAddress::Ethereum(address),
100
));
assert_eq!(Balances::balance(&interim), 100);
assert_eq!(Balances::balance(&account), 0);

assert_ok!(UnifyAccount::<Test>::unify_ecdsa(&account));
assert_eq!(Balances::balance(&interim), 0);
assert_eq!(Balances::balance(&account), 100);
});
}

#[cfg(feature = "nostr")]
#[test]
fn transfer_to_nostr_address_works() {
use core::str::FromStr;
use np_babel::NostrAddress;

let account = AccountId::from(dev_public());
let interim = AccountId::new(dev_public()[1..].try_into().unwrap());
let address =
NostrAddress::from_str("npub12z25pyvl4t8e4dfpgmy65sxmdqtjmqmhwfgt9rjx0ytkujwvmk0s2yfk08")
.unwrap();

new_test_ext().execute_with(|| {
assert_ok!(Babel::transfer(
RuntimeOrigin::signed(alice()),
None,
VarAddress::Nostr(address),
100
));
assert_eq!(Balances::balance(&interim), 100);
assert_eq!(Balances::balance(&account), 0);

assert_ok!(UnifyAccount::<Test>::unify_ecdsa(&account));
assert_eq!(Balances::balance(&interim), 0);
assert_eq!(Balances::balance(&account), 100);
});
}
5 changes: 5 additions & 0 deletions primitives/babel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ publish = false
[dependencies]
np-cosmos = { workspace = true, default-features = false, optional = true }
np-ethereum = { workspace = true, default-features = false, optional = true }
np-nostr = { workspace = true, default-features = false, optional = true }
parity-scale-codec = { version = "3.6", default-features = false, features = ["derive"] }
scale-info = { version = "2.11", default-features = false, features = ["derive"] }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
Expand All @@ -20,6 +21,7 @@ default = ["std", "cosmos", "ethereum"]
std = [
"np-cosmos?/std",
"np-ethereum?/std",
"np-nostr?/std",
"parity-scale-codec/std",
"scale-info/std",
"serde/std",
Expand All @@ -37,3 +39,6 @@ cosmos = [
ethereum = [
"np-ethereum",
]
nostr = [
"np-nostr",
]
14 changes: 14 additions & 0 deletions primitives/babel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ pub use np_cosmos::Address as CosmosAddress;
pub use np_ethereum as ethereum;
#[cfg(feature = "ethereum")]
pub use np_ethereum::Address as EthereumAddress;
#[cfg(feature = "nostr")]
pub use np_nostr as nostr;
#[cfg(feature = "nostr")]
pub use np_nostr::Address as NostrAddress;
pub use sp_core::crypto::AccountId32;

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)]
Expand All @@ -43,6 +47,8 @@ pub enum VarAddress {
Cosmos(CosmosAddress),
#[cfg(feature = "ethereum")]
Ethereum(EthereumAddress),
#[cfg(feature = "nostr")]
Nostr(NostrAddress),
}

impl VarAddress {
Expand All @@ -54,6 +60,9 @@ impl VarAddress {
if cfg!(feature = "ethereum") {
n += 1;
}
if cfg!(feature = "nostr") {
n += 1;
}
n
}

Expand All @@ -66,4 +75,9 @@ impl VarAddress {
pub fn ethereum(public: ecdsa::Public) -> Self {
Self::Ethereum(EthereumAddress::from(public))
}

#[cfg(feature = "nostr")]
pub fn nostr(public: ecdsa::Public) -> Self {
Self::Nostr(NostrAddress::from(public))
}
}
36 changes: 36 additions & 0 deletions primitives/nostr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "np-nostr"
version = "0.4.0"
authors = ["Haderech Pte. Ltd."]
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/noirhq/noir.git"
publish = false

[dependencies]
bech32 = { version = "0.11", default-features = false, optional = true }
buidl = { version = "0.1.1", default-features = false, features = ["derive"] }
parity-scale-codec = { version = "3.6", default-features = false, features = ["derive"] }
scale-info = { version = "2.11", default-features = false, features = ["derive"] }
serde = { version = "1.0", default-features = false, optional = true }
sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }

[dev-dependencies]
const-hex = { version = "1.12", default-features = false }

[features]
default = ["std"]
std = [
"bech32/std",
"buidl/std",
"parity-scale-codec/std",
"scale-info/std",
"serde/std",
"sp-core/std",
"sp-runtime/std",
]
serde = [
"dep:serde",
"bech32/alloc",
]
Loading

0 comments on commit eb8db91

Please sign in to comment.