Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding ethereum h160 style account support #2206

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ thiserror = "1.0.40"
apache-avro = { version = "0.14.0", default-features = false }
rand = "0.8.5"
parking_lot = "0.12.1"
libsecp256k1 = { version = "0.7", default-features = false }

# substrate wasm
parity-scale-codec = { version = "3.6.12", default-features = false }
Expand Down
5 changes: 4 additions & 1 deletion common/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ sp-std = { workspace = true }
numtoa = { workspace = true }
sp-externalities = { workspace = true }
sp-runtime-interface = { workspace = true }
libsecp256k1 = { workspace = true, features = ["hmac"] }
log = "0.4.22"

[features]
default = ['std']
runtime-benchmarks = []
std = [
'libsecp256k1/std',
'parity-scale-codec/std',
'frame-support/std',
'frame-system/std',
Expand All @@ -47,4 +50,4 @@ std = [
'sp-externalities/std',
'sp-runtime-interface/std'
]
test = []
test = []
3 changes: 3 additions & 0 deletions common/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ pub mod offchain;
#[cfg(feature = "runtime-benchmarks")]
/// Benchmarking helper trait
pub mod benchmarks;

/// Signatures
pub mod signatures;
3 changes: 2 additions & 1 deletion common/primitives/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use sp_runtime::{
};
use sp_std::{boxed::Box, vec::Vec};

use crate::signatures::UnifiedSignature;
use frame_support::dispatch::DispatchResultWithPostInfo;

/// Some way of identifying an account on the chain. We intentionally make it equivalent
Expand All @@ -31,7 +32,7 @@ pub type BlockNumber = u32;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;

/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = MultiSignature;
pub type Signature = UnifiedSignature;

/// Index of a transaction in the chain.
pub type Index = u32;
Expand Down
255 changes: 255 additions & 0 deletions common/primitives/src/signatures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "serde")]
use frame_support::{Deserialize, Serialize};
use frame_support::{
__private::{codec, RuntimeDebug},
pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo},
};
use sp_core::{
crypto,
crypto::{AccountId32, FromEntropy},
ecdsa, ed25519,
hexdisplay::HexDisplay,
sr25519, ByteArray, H256,
};
use scale_info::prelude::format;
use sp_runtime::{
traits,
traits::{Lazy, Verify},
MultiSignature,
};

/// Signature verify that can work with any known signature types.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Eq, PartialEq, Clone, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub enum UnifiedSignature {
/// An Ed25519 signature.
Ed25519(ed25519::Signature),
/// An Sr25519 signature.
Sr25519(sr25519::Signature),
/// An ECDSA/SECP256k1 signature.
Ecdsa(ecdsa::Signature),
}

impl From<ed25519::Signature> for UnifiedSignature {
fn from(x: ed25519::Signature) -> Self {
Self::Ed25519(x)
}
}

impl TryFrom<UnifiedSignature> for ed25519::Signature {
type Error = ();
fn try_from(m: UnifiedSignature) -> Result<Self, Self::Error> {
if let UnifiedSignature::Ed25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}

impl From<sr25519::Signature> for UnifiedSignature {
fn from(x: sr25519::Signature) -> Self {
Self::Sr25519(x)
}
}

impl TryFrom<UnifiedSignature> for sr25519::Signature {
type Error = ();
fn try_from(m: UnifiedSignature) -> Result<Self, Self::Error> {
if let UnifiedSignature::Sr25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}

impl From<ecdsa::Signature> for UnifiedSignature {
fn from(x: ecdsa::Signature) -> Self {
Self::Ecdsa(x)
}
}

impl TryFrom<UnifiedSignature> for ecdsa::Signature {
type Error = ();
fn try_from(m: UnifiedSignature) -> Result<Self, Self::Error> {
if let UnifiedSignature::Ecdsa(x) = m {
Ok(x)
} else {
Err(())
}
}
}

impl Verify for UnifiedSignature {
type Signer = UnifiedSigner;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
match (self, signer) {
(Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) {
Ok(signer) => sig.verify(msg, &signer),
Err(()) => false,
},
(Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) {
Ok(signer) => sig.verify(msg, &signer),
Err(()) => false,
},
(Self::Ecdsa(ref sig), who) => {
log::info!(target:"ETHEREUM", "inside ecdsa signature verifier 0x{:?}",HexDisplay::from(&msg.get()));
let m = eth_message(&format!("<Frequency>0x{:?}</Frequency>", HexDisplay::from(&msg.get())));
log::info!(target:"ETHEREUM", "prefixed hashed 0x{:?}",HexDisplay::from(&m));
match sp_io::crypto::secp256k1_ecdsa_recover(sig.as_ref(), &m) {
Ok(pubkey) => {
let mut hashed = sp_io::hashing::keccak_256(pubkey.as_ref());
hashed[..12].fill(0);
log::info!(target:"ETHEREUM", "eth hashed={:?} who={:?}",
HexDisplay::from(&hashed),HexDisplay::from(<dyn AsRef<[u8; 32]>>::as_ref(who)),
);
&hashed == <dyn AsRef<[u8; 32]>>::as_ref(who)
},
_ => false,
}
},
}
}
}
fn eth_message(message: &str) -> [u8; 32] {
let prefixed = format!("{}{}{}", "\x19Ethereum Signed Message:\n", message.len(), message);
log::info!(target:"ETHEREUM", "prefixed {:?}",prefixed);
sp_io::hashing::keccak_256(prefixed.as_bytes())
}
/// Public key for any known crypto algorithm.
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UnifiedSigner {
/// An Ed25519 identity.
Ed25519(ed25519::Public),
/// An Sr25519 identity.
Sr25519(sr25519::Public),
/// An SECP256k1/ECDSA identity (12 bytes of zeros + 20 bytes of ethereum address).
Ecdsa(ecdsa::Public),
}

impl FromEntropy for UnifiedSigner {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
Ok(match input.read_byte()? % 3 {
0 => Self::Ed25519(FromEntropy::from_entropy(input)?),
1 => Self::Sr25519(FromEntropy::from_entropy(input)?),
2.. => Self::Ecdsa(FromEntropy::from_entropy(input)?),
})
}
}

/// NOTE: This implementations is required by `SimpleAddressDeterminer`,
/// we convert the hash into some AccountId, it's fine to use any scheme.
impl<T: Into<H256>> crypto::UncheckedFrom<T> for UnifiedSigner {
fn unchecked_from(x: T) -> Self {
ed25519::Public::unchecked_from(x.into()).into()
}
}

impl AsRef<[u8]> for UnifiedSigner {
fn as_ref(&self) -> &[u8] {
match *self {
Self::Ed25519(ref who) => who.as_ref(),
Self::Sr25519(ref who) => who.as_ref(),
Self::Ecdsa(ref who) => who.as_ref(),
}
}
}

impl traits::IdentifyAccount for UnifiedSigner {
type AccountId = AccountId32;
fn into_account(self) -> AccountId32 {
match self {
Self::Ed25519(who) => <[u8; 32]>::from(who).into(),
Self::Sr25519(who) => <[u8; 32]>::from(who).into(),
Self::Ecdsa(who) => {
log::info!(target:"ETHEREUM", "inside ecdsa into_account");
let decompressed = libsecp256k1::PublicKey::parse_slice(
who.as_ref(),
Some(libsecp256k1::PublicKeyFormat::Compressed),
)
.expect("Wrong compressed public key provided")
.serialize();
let mut m = [0u8; 64];
m.copy_from_slice(&decompressed[1..65]);
let mut hashed = sp_io::hashing::keccak_256(m.as_ref());
hashed[..12].fill(0);
hashed.into()
},
}
}
}

impl From<ed25519::Public> for UnifiedSigner {
fn from(x: ed25519::Public) -> Self {
Self::Ed25519(x)
}
}

impl TryFrom<UnifiedSigner> for ed25519::Public {
type Error = ();
fn try_from(m: UnifiedSigner) -> Result<Self, Self::Error> {
if let UnifiedSigner::Ed25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}

impl From<sr25519::Public> for UnifiedSigner {
fn from(x: sr25519::Public) -> Self {
Self::Sr25519(x)
}
}

impl TryFrom<UnifiedSigner> for sr25519::Public {
type Error = ();
fn try_from(m: UnifiedSigner) -> Result<Self, Self::Error> {
if let UnifiedSigner::Sr25519(x) = m {
Ok(x)
} else {
Err(())
}
}
}

impl From<ecdsa::Public> for UnifiedSigner {
fn from(x: ecdsa::Public) -> Self {
Self::Ecdsa(x)
}
}

impl TryFrom<UnifiedSigner> for ecdsa::Public {
type Error = ();
fn try_from(m: UnifiedSigner) -> Result<Self, Self::Error> {
if let UnifiedSigner::Ecdsa(x) = m {
Ok(x)
} else {
Err(())
}
}
}

#[cfg(feature = "std")]
impl std::fmt::Display for UnifiedSigner {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Self::Ed25519(ref who) => write!(fmt, "ed25519: {}", who),
Self::Sr25519(ref who) => write!(fmt, "sr25519: {}", who),
Self::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who),
}
}
}

impl Into<UnifiedSignature> for MultiSignature {
fn into(self: MultiSignature) -> UnifiedSignature {
match self {
MultiSignature::Ed25519(who) => UnifiedSignature::Ed25519(who),
MultiSignature::Sr25519(who) => UnifiedSignature::Sr25519(who),
MultiSignature::Ecdsa(who) => UnifiedSignature::Ecdsa(who),
}
}
}
2 changes: 1 addition & 1 deletion e2e/.mocharc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"parallel": true,
"require": ["scaffolding/globalHooks.ts", "scaffolding/rootHooks.ts", "scaffolding/extrinsicHelpers.ts"],
"import": "tsx/esm",
"spec": ["./{,!(node_modules|load-tests)/**}/*.test.ts"],
"spec": ["./passkey/*.test.ts"],
"timeout": 20000
}
Loading
Loading