diff --git a/Cargo.lock b/Cargo.lock index 48423487c..a133941ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6777,6 +6777,7 @@ dependencies = [ "frame-support", "frame-system", "hex", + "k256 0.11.6", "libsecp256k1", "log", "pallet-session", diff --git a/pallets/dkg-metadata/Cargo.toml b/pallets/dkg-metadata/Cargo.toml index fd085e4da..e54727c81 100644 --- a/pallets/dkg-metadata/Cargo.toml +++ b/pallets/dkg-metadata/Cargo.toml @@ -37,6 +37,7 @@ sp-core = { workspace = true } sp-io = { workspace = true } sp-staking = { workspace = true } sp-keystore = { workspace = true } +k256 = { version = "0.11.5", default-features = false, features = ["arithmetic", "ecdsa"] } [features] default = ["std"] diff --git a/pallets/dkg-metadata/src/lib.rs b/pallets/dkg-metadata/src/lib.rs index e1f3327ce..33519a20e 100644 --- a/pallets/dkg-metadata/src/lib.rs +++ b/pallets/dkg-metadata/src/lib.rs @@ -211,6 +211,13 @@ pub mod pallet { + Into + From + MaxEncodedLen; + /// Convert DKG AuthorityId to a form that would end up in the Merkle Tree. + /// + /// For instance for ECDSA (secp256k1) we want to store uncompressed public keys (65 bytes) + /// and later to Ethereum Addresses (160 bits) to simplify using them on Ethereum chain, + /// but the rest of the Substrate codebase is storing them compressed (33 bytes) for + /// efficiency reasons. + type DKGAuthorityToMerkleLeaf: Convert>; /// Jail lengths for misbehaviours type KeygenJailSentence: Get>; type SigningJailSentence: Get>; @@ -1637,7 +1644,7 @@ impl Pallet { // Hash the external accounts into 32 byte chunks to form the base layer of the merkle tree let mut base_layer: Vec<[u8; 32]> = voters .iter() - .map(|account| account.to_raw_vec()) + .map(|account| T::DKGAuthorityToMerkleLeaf::convert(account.clone())) .map(|account| keccak_256(&account)) .collect(); // Pad base_layer to have length 2^height diff --git a/pallets/dkg-metadata/src/mock.rs b/pallets/dkg-metadata/src/mock.rs index 1eb63191d..76e31e733 100644 --- a/pallets/dkg-metadata/src/mock.rs +++ b/pallets/dkg-metadata/src/mock.rs @@ -29,8 +29,8 @@ use sp_runtime::{ impl_opaque_keys, testing::TestXt, traits::{ - BlakeTwo256, ConvertInto, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, - OpaqueKeys, Verify, + BlakeTwo256, Convert, ConvertInto, Extrinsic as ExtrinsicT, IdentifyAccount, + IdentityLookup, OpaqueKeys, Verify, }, BuildStorage, Percent, Permill, }; @@ -141,6 +141,7 @@ parameter_types! { impl pallet_dkg_metadata::Config for Test { type DKGId = DKGId; + type DKGAuthorityToMerkleLeaf = DKGEcdsaToEthereumAddress; type RuntimeEvent = RuntimeEvent; type OnAuthoritySetChangeHandler = (); type OnDKGPublicKeyChangeHandler = (); @@ -254,3 +255,23 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<(AccountId, DKGId)>) -> Tes ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr)); ext } + +/// Convert DKG secp256k1 public keys into Ethereum addresses +pub struct DKGEcdsaToEthereumAddress; +impl Convert> for DKGEcdsaToEthereumAddress { + fn convert(a: dkg_runtime_primitives::crypto::AuthorityId) -> Vec { + use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; + let _x = VerifyingKey::from_sec1_bytes(sp_core::crypto::ByteArray::as_slice(&a)); + VerifyingKey::from_sec1_bytes(sp_core::crypto::ByteArray::as_slice(&a)) + .map(|pub_key| { + // uncompress the key + let uncompressed = pub_key.to_encoded_point(false); + // convert to ETH address + sp_io::hashing::keccak_256(&uncompressed.as_bytes()[1..])[12..].to_vec() + }) + .map_err(|_| { + log::error!(target: "runtime::dkg_proposals", "Invalid DKG PublicKey format!"); + }) + .unwrap_or_default() + } +} diff --git a/pallets/dkg-proposal-handler/src/mock.rs b/pallets/dkg-proposal-handler/src/mock.rs index 6b6e497d5..ea5559214 100644 --- a/pallets/dkg-proposal-handler/src/mock.rs +++ b/pallets/dkg-proposal-handler/src/mock.rs @@ -287,6 +287,7 @@ parameter_types! { impl pallet_dkg_metadata::Config for Test { type DKGId = DKGId; type RuntimeEvent = RuntimeEvent; + type DKGAuthorityToMerkleLeaf = DKGEcdsaToEthereumAddress; type OnAuthoritySetChangeHandler = (); type OnDKGPublicKeyChangeHandler = (); type OffChainAuthId = dkg_runtime_primitives::offchain::crypto::OffchainAuthId; diff --git a/pallets/dkg-proposals/src/mock.rs b/pallets/dkg-proposals/src/mock.rs index 359f1b1bd..1acf0bd01 100644 --- a/pallets/dkg-proposals/src/mock.rs +++ b/pallets/dkg-proposals/src/mock.rs @@ -168,6 +168,7 @@ parameter_types! { impl pallet_dkg_metadata::Config for Test { type DKGId = DKGId; + type DKGAuthorityToMerkleLeaf = DKGEcdsaToEthereumAddress; type RuntimeEvent = RuntimeEvent; type OnAuthoritySetChangeHandler = DKGProposals; type OnDKGPublicKeyChangeHandler = (); diff --git a/standalone/runtime/src/lib.rs b/standalone/runtime/src/lib.rs index 41b0adbb3..72d35e178 100644 --- a/standalone/runtime/src/lib.rs +++ b/standalone/runtime/src/lib.rs @@ -607,6 +607,7 @@ parameter_types! { impl pallet_dkg_metadata::Config for Runtime { type DKGId = DKGId; + type DKGAuthorityToMerkleLeaf = pallet_dkg_proposals::DKGEcdsaToEthereumAddress; type RuntimeEvent = RuntimeEvent; type OnAuthoritySetChangeHandler = DKGProposals; type OnDKGPublicKeyChangeHandler = ();