use std::time::Duration;
use entropy_programs_runtime::{Config as ProgramConfig, Runtime, SignatureRequest};
use entropy_protocol::{
execute_protocol::{execute_dkg, Channels},
KeyShareWithAuxInfo, Listener, SessionId, ValidatorInfo,
};
use entropy_shared::{HashingAlgorithm, SETUP_TIMEOUT_SECONDS};
use sha1::{Digest as Sha1Digest, Sha1};
use sha2::{Digest as Sha256Digest, Sha256};
use sha3::{Digest as Sha3Digest, Keccak256, Sha3_256};
use sp_core::{hashing::blake2_256, sr25519, Pair};
use subxt::{backend::legacy::LegacyRpcMethods, tx::PairSigner, utils::AccountId32, OnlineClient};
use tokio::time::timeout;
use x25519_dalek::StaticSecret;
use crate::{
chain_api::{
entropy::runtime_types::{
pallet_programs::pallet::ProgramInfo, pallet_registry::pallet::ProgramInstance,
},
EntropyConfig,
},
helpers::substrate::get_program,
signing_client::{protocol_transport::open_protocol_connections, ListenerState},
user::errors::UserErr,
};
pub async fn do_dkg(
validators_info: &Vec<entropy_shared::ValidatorInfo>,
signer: &PairSigner<EntropyConfig, sr25519::Pair>,
x25519_secret_key: &StaticSecret,
state: &ListenerState,
block_number: u32,
) -> Result<KeyShareWithAuxInfo, UserErr> {
let session_id = SessionId::Dkg { block_number };
let account_id = AccountId32(signer.signer().public().0);
let mut converted_validator_info = vec![];
let mut tss_accounts = vec![];
for validator_info in validators_info {
let address_slice: &[u8; 32] = &validator_info
.tss_account
.clone()
.try_into()
.map_err(|_| UserErr::AddressConversionError("Invalid Length".to_string()))?;
let tss_account = AccountId32(*address_slice);
let validator_info = ValidatorInfo {
x25519_public_key: validator_info.x25519_public_key,
ip_address: std::str::from_utf8(&validator_info.ip_address)?.to_string(),
tss_account: tss_account.clone(),
};
converted_validator_info.push(validator_info);
tss_accounts.push(tss_account);
}
let (rx_ready, rx_from_others, listener) =
Listener::new(converted_validator_info.clone(), &account_id);
state
.listeners
.lock()
.map_err(|_| UserErr::SessionError("Error getting lock".to_string()))?
.insert(session_id.clone(), listener);
open_protocol_connections(
&converted_validator_info,
&session_id,
signer.signer(),
state,
x25519_secret_key,
)
.await?;
let channels = {
let ready = timeout(Duration::from_secs(SETUP_TIMEOUT_SECONDS), rx_ready).await?;
let broadcast_out = ready??;
Channels(broadcast_out, rx_from_others)
};
let threshold = (tss_accounts.len() as f32 * 0.75) as usize;
let result =
execute_dkg(session_id, channels, signer.signer(), tss_accounts, threshold).await?;
Ok(result)
}
pub fn check_in_registration_group(
validators_info: &[entropy_shared::ValidatorInfo],
validator_address: &AccountId32,
) -> Result<(), UserErr> {
let is_proper_signer = validators_info
.iter()
.any(|validator_info| validator_info.tss_account == validator_address.0.to_vec());
if !is_proper_signer {
return Err(UserErr::InvalidSigner("Invalid Signer in Signing group"));
}
Ok(())
}
pub async fn compute_hash(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
hashing_algorithm: &HashingAlgorithm,
fuel: u64,
programs_data: &[ProgramInstance],
message: &[u8],
) -> Result<[u8; 32], UserErr> {
match hashing_algorithm {
HashingAlgorithm::Sha1 => {
let mut hasher = <Sha1 as Sha1Digest>::new();
hasher.update(message);
let result = hasher.finalize();
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
Ok(hash)
},
HashingAlgorithm::Sha2 => {
let mut hasher = <Sha256 as Sha256Digest>::new();
hasher.update(message);
let result = hasher.finalize();
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
Ok(hash)
},
HashingAlgorithm::Sha3 => {
let mut hasher = <Sha3_256 as Sha3Digest>::new();
hasher.update(message);
let result = hasher.finalize();
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
Ok(hash)
},
HashingAlgorithm::Keccak => {
let mut hasher = <Keccak256 as Sha3Digest>::new();
hasher.update(message);
let result = hasher.finalize();
let mut hash = [0u8; 32];
hash.copy_from_slice(&result);
Ok(hash)
},
HashingAlgorithm::Blake2_256 => Ok(blake2_256(message)),
HashingAlgorithm::Identity => Ok(message.try_into()?),
HashingAlgorithm::Custom(i) => {
let program_info =
get_program(api, rpc, &programs_data[*i as usize].program_pointer).await?;
evaluate_custom_hash(fuel, program_info, message)
},
_ => Err(UserErr::UnknownHashingAlgorithm),
}
}
pub fn evaluate_program(
fuel: u64,
program_info: ProgramInfo,
signature_request: SignatureRequest,
program_config: Vec<u8>,
oracle_data: Vec<Vec<u8>>,
) -> Result<(), UserErr> {
match program_info.version_number {
0 => {
let mut runtime = Runtime::new(ProgramConfig { fuel });
runtime
.evaluate(
&program_info.bytecode,
&signature_request,
Some(&program_config),
Some(&oracle_data),
)
.map_err(|e| e.into())
},
_ => Err(UserErr::ProgramVersion),
}
}
pub fn evaluate_custom_hash(
fuel: u64,
program_info: ProgramInfo,
message: &[u8],
) -> Result<[u8; 32], UserErr> {
match program_info.version_number {
0 => {
let mut runtime = Runtime::new(ProgramConfig { fuel });
runtime.custom_hash(program_info.bytecode.as_slice(), message).map_err(|e| e.into())
},
_ => Err(UserErr::ProgramVersion),
}
}