Skip to content

Commit

Permalink
Merge branch 'main' into remove-dkg-substrate
Browse files Browse the repository at this point in the history
  • Loading branch information
salman01zp authored Dec 12, 2023
2 parents cb1f56a + af6032a commit 3d196ab
Show file tree
Hide file tree
Showing 19 changed files with 1,075 additions and 492 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

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

3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,6 @@ ethereum = { version = "0.14.0", default-features = false }
evm-gasometer = { git = "https://github.com/rust-blockchain/evm", rev = "b7b82c7e1fc57b7449d6dfa6826600de37cc1e65", default-features = false }
evm-runtime = { git = "https://github.com/rust-blockchain/evm", rev = "b7b82c7e1fc57b7449d6dfa6826600de37cc1e65", default-features = false }

# XCM dependencies
xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false }

# RPC related dependencies
jsonrpsee = { version = "0.16.2", features = ["server"] }
pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" }
Expand Down
1 change: 1 addition & 0 deletions node/src/distributions/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use super::testnet::{get_git_root, read_contents, read_contents_to_evm_accounts}
fn read_contents_to_substrate_accounts(path_str: &str) -> BTreeMap<AccountId, f64> {
let mut path = get_git_root();
path.push(path_str);
println!("Path {:?}", path_str);
let json = read_contents(&path);
let json_obj = json.as_object().expect("should be an object");
let mut accounts_map = BTreeMap::new();
Expand Down
2 changes: 2 additions & 0 deletions node/src/distributions/testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub fn read_contents(path: &Path) -> Value {
pub fn read_contents_to_evm_accounts(path_str: &str) -> Vec<H160> {
let mut path = get_git_root();
path.push(path_str);
println!("Path {:?}", path_str);
let json = read_contents(&path);
let mut accounts = Vec::new();
for address in json.as_array().expect("should be an object") {
Expand All @@ -47,6 +48,7 @@ pub fn read_contents_to_evm_accounts(path_str: &str) -> Vec<H160> {
fn read_contents_to_substrate_accounts(path_str: &str) -> Vec<AccountId> {
let mut path = get_git_root();
path.push(path_str);
println!("Path {:?}", path_str);
let json = read_contents(&path);
let mut accounts = Vec::new();
for address in json.as_array().expect("should be an object") {
Expand Down
12 changes: 3 additions & 9 deletions pallets/dkg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ license = { workspace = true }
repository = { workspace = true }

[dependencies]
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
parity-scale-codec = { workspace = true }
Expand All @@ -31,19 +30,14 @@ default = ["std"]
std = [
"scale-info/std",
"sp-runtime/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"sp-core/std",
"sp-std/std",
"tangle-primitives/std",
"pallet-balances/std",
"sp-io/std",
"sp-application-crypto/std",
"sp-keystore/std"
]
try-runtime = ["frame-support/try-runtime"]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
]
189 changes: 166 additions & 23 deletions pallets/dkg/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ use crate::types::BalanceOf;
use frame_support::{ensure, pallet_prelude::DispatchResult, sp_runtime::Saturating};
use frame_system::pallet_prelude::BlockNumberFor;
use parity_scale_codec::Encode;
use sp_core::ecdsa;
use sp_io::{hashing::keccak_256, EcdsaVerifyError};
use sp_core::{ecdsa, sr25519};
use sp_io::{crypto::sr25519_verify, hashing::keccak_256, EcdsaVerifyError};
use sp_std::{default::Default, vec::Vec};
use tangle_primitives::jobs::*;

/// Expected signature length
pub const SIGNATURE_LENGTH: usize = 65;
/// Expected key length for ecdsa
const ECDSA_KEY_LENGTH: usize = 33;
/// Expected key length for sr25519
const SCHNORR_KEY_LENGTH: usize = 32;

impl<T: Config> Pallet<T> {
/// Calculates the fee for a given job submission based on the provided fee information.
Expand Down Expand Up @@ -75,10 +77,31 @@ impl<T: Config> Pallet<T> {
}
}

/// Verifies the generated DKG key based on the provided DKG verification information.
/// Verifies a generated DKG (Distributed Key Generation) key based on the provided DKG result.
///
/// The verification process includes generating required signers, validating signatures, and
/// ensuring a sufficient number of unique signers are present.
/// The verification process depends on the key type specified in the DKG result.
/// It dispatches the verification to the appropriate function for the specified key type (ECDSA
/// or Schnorr).
///
/// # Arguments
///
/// * `data` - The DKG result containing participants, keys, and signatures.
///
/// # Returns
///
/// Returns a `DispatchResult` indicating whether the DKG key verification was successful
/// or encountered an error.
fn verify_generated_dkg_key(data: DKGResult) -> DispatchResult {
match data.key_type {
DkgKeyType::Ecdsa => Self::verify_generated_dkg_key_ecdsa(data),
DkgKeyType::Schnorr => Self::verify_generated_dkg_key_schnorr(data),
}
}

/// Verifies the generated DKG key for ECDSA signatures.
///
/// This function includes generating required signers, validating signatures, and ensuring a
/// sufficient number of unique signers are present.
///
/// # Arguments
///
Expand All @@ -88,48 +111,124 @@ impl<T: Config> Pallet<T> {
///
/// Returns a `DispatchResult` indicating whether the DKG key verification was successful or
/// encountered an error.
fn verify_generated_dkg_key(data: DKGResult) -> DispatchResult {
// generate the required signers
fn verify_generated_dkg_key_ecdsa(data: DKGResult) -> DispatchResult {
// Ensure participants and signatures are not empty
ensure!(!data.participants.is_empty(), Error::<T>::NoParticipantsFound);
ensure!(!data.signatures.is_empty(), Error::<T>::NoSignaturesFound);

// Generate the required ECDSA signers
let maybe_signers = data
.participants
.iter()
.map(|x| {
ecdsa::Public(
Self::to_slice_33(&x.encode())
Self::to_slice_33(x)
.unwrap_or_else(|| panic!("Failed to convert input to ecdsa public key")),
)
})
.collect::<Vec<ecdsa::Public>>();

ensure!(!maybe_signers.is_empty(), Error::<T>::NoParticipantsFound);
ensure!(!data.keys_and_signatures.is_empty(), Error::<T>::NoSignaturesFound);

let mut known_signers: Vec<ecdsa::Public> = Default::default();
let signed_pub_key: Vec<u8> =
data.keys_and_signatures.first().expect("Cannot be empty").clone().0;

for (key, signature) in data.keys_and_signatures {
// ensure the required signer signature exists
for signature in data.signatures {
// Ensure the required signer signature exists
let (maybe_authority, success) =
Self::verify_signer_from_set_ecdsa(maybe_signers.clone(), &key, &signature);
Self::verify_signer_from_set_ecdsa(maybe_signers.clone(), &data.key, &signature);

if success {
// sanity check, everyone signed the same key
ensure!(key == signed_pub_key, Error::<T>::InvalidSignatureData);

let authority = maybe_authority.ok_or(Error::<T>::CannotRetreiveSigner)?;

// Ensure no duplicate signatures
ensure!(!known_signers.contains(&authority), Error::<T>::DuplicateSignature);

known_signers.push(authority);
}
}

// Ensure a sufficient number of unique signers are present
ensure!(known_signers.len() > data.threshold.into(), Error::<T>::NotEnoughSigners);

Ok(())
}

/// Verifies the generated DKG key for Schnorr signatures.
///
/// This function includes generating required signers, validating signatures, and ensuring a
/// sufficient number of unique signers are present.
///
/// # Arguments
///
/// * `data` - The DKG verification information containing participants, keys, and signatures.
///
/// # Returns
///
/// Returns a `DispatchResult` indicating whether the DKG key verification was successful or
/// encountered an error.
fn verify_generated_dkg_key_schnorr(data: DKGResult) -> DispatchResult {
// Ensure participants and signatures are not empty
ensure!(!data.participants.is_empty(), Error::<T>::NoParticipantsFound);
ensure!(!data.signatures.is_empty(), Error::<T>::NoSignaturesFound);

// Generate the required Schnorr signers
let maybe_signers = data
.participants
.iter()
.map(|x| {
sr25519::Public(
Self::to_slice_32(x)
.unwrap_or_else(|| panic!("Failed to convert input to sr25519 public key")),
)
})
.collect::<Vec<sr25519::Public>>();

ensure!(!maybe_signers.is_empty(), Error::<T>::NoParticipantsFound);

let mut known_signers: Vec<sr25519::Public> = Default::default();

for signature in data.signatures {
// Convert the signature from bytes to sr25519::Signature
let signature: sr25519::Signature =
signature.as_slice().try_into().map_err(|_| Error::<T>::CannotRetreiveSigner)?;

let msg = data.key.encode();
let hash = keccak_256(&msg);

for signer in maybe_signers.clone() {
// Verify the Schnorr signature
if sr25519_verify(&signature, &hash, &signer) {
ensure!(!known_signers.contains(&signer), Error::<T>::DuplicateSignature);

known_signers.push(signer);
}
}
}

// Ensure a sufficient number of unique signers are present
ensure!(known_signers.len() > data.threshold.into(), Error::<T>::NotEnoughSigners);

Ok(())
}

/// Verifies a DKG (Distributed Key Generation) signature based on the provided DKG signature
/// result.
///
/// The verification process depends on the key type specified in the DKG signature result.
/// It dispatches the verification to the appropriate function for the specified key type (ECDSA
/// or Schnorr).
///
/// # Arguments
///
/// * `data` - The DKG signature result containing the message data, signature, signing key, and
/// key type.
fn verify_dkg_signature(data: DKGSignatureResult) -> DispatchResult {
match data.key_type {
DkgKeyType::Ecdsa => Self::verify_dkg_signature_ecdsa(data),
DkgKeyType::Schnorr => Self::verify_dkg_signature_schnorr(data),
}
}

/// Verifies the DKG signature result by recovering the ECDSA public key from the provided data
/// and signature.
///
Expand All @@ -139,25 +238,59 @@ impl<T: Config> Pallet<T> {
/// # Arguments
///
/// * `data` - The DKG signature result containing the message data and ECDSA signature.
///
/// # Returns
///
/// Returns a `DispatchResult` indicating whether the DKG signature verification was successful
/// or encountered an error.
fn verify_dkg_signature(data: DKGSignatureResult) -> DispatchResult {
fn verify_dkg_signature_ecdsa(data: DKGSignatureResult) -> DispatchResult {
// Recover the ECDSA public key from the provided data and signature
let recovered_key = Self::recover_ecdsa_pub_key(&data.data, &data.signature)
.map_err(|_| Error::<T>::InvalidSignature)?;

// Extract the expected key from the provided signing key
let expected_key: Vec<_> = data.signing_key.iter().skip(1).cloned().collect();
// The recovered key is 64 bytes uncompressed. The first 32 bytes represent the compressed
// portion of the key.
let signer = &recovered_key[..32];

// Ensure that the recovered key matches the expected signing key
ensure!(expected_key == signer, Error::<T>::SigningKeyMismatch);

Ok(())
}

/// Verifies the DKG signature result for Schnorr signatures.
///
/// This function uses the Schnorr signature algorithm to verify the provided signature
/// based on the message data, signature, and signing key in the DKG signature result.
///
/// # Arguments
///
/// * `data` - The DKG signature result containing the message data, Schnorr signature, and
/// signing key.
fn verify_dkg_signature_schnorr(data: DKGSignatureResult) -> DispatchResult {
// Convert the signature from bytes to sr25519::Signature
let signature: sr25519::Signature = data
.signature
.as_slice()
.try_into()
.map_err(|_| Error::<T>::CannotRetreiveSigner)?;

// Encode the message data and compute its keccak256 hash
let msg = data.data.encode();
let hash = keccak_256(&msg);

// Verify the Schnorr signature using sr25519_verify
if !sr25519_verify(
&signature,
&hash,
&sr25519::Public(
Self::to_slice_32(&data.signing_key)
.unwrap_or_else(|| panic!("Failed to convert input to sr25519 public key")),
),
) {
return Err(Error::<T>::InvalidSignature.into())
}

Ok(())
}

/// Recovers the ECDSA public key from a given message and signature.
///
/// # Arguments
Expand Down Expand Up @@ -235,7 +368,17 @@ impl<T: Config> Pallet<T> {

return Some(key)
}
None
}

/// Utility function to create slice of fixed size
pub fn to_slice_32(val: &[u8]) -> Option<[u8; 32]> {
if val.len() == SCHNORR_KEY_LENGTH {
let mut key = [0u8; SCHNORR_KEY_LENGTH];
key[..SCHNORR_KEY_LENGTH].copy_from_slice(val);

return Some(key)
}
None
}
}
Loading

0 comments on commit 3d196ab

Please sign in to comment.