#![cfg(any(test, feature = "testutils"))]
extern crate std;
use crate::auth::{self, epoch};
use crate::contract::AxelarGatewayClient;
use srb_std::{assert_last_emitted_event, assert_ok};
use ed25519_dalek::{Signature, Signer, SigningKey};
use rand::Rng;
use soroban_sdk::Symbol;
use soroban_sdk::{testutils::Address as _, Address};
use soroban_sdk::{testutils::BytesN as _, vec, xdr::ToXdr, Bytes, BytesN, Env, String, Vec};
use srb_trfc::types::{
CommandType, Message, Proof, ProofSignature, ProofSigner, WeightedSigner, WeightedSigners,
};
use srb_std::traits::IntoVec;
const DESTINATION_CHAIN: &str = "ethereum";
const DESTINATION_ADDRESS: &str = "0x4EFE356BEDeCC817cb89B4E9b796dB8bC188DC59";
#[derive(Clone, Debug)]
pub struct TestSignerSet {
pub signer_keys: std::vec::Vec<SigningKey>,
pub signers: WeightedSigners,
pub domain_separator: BytesN<32>,
}
pub fn initialize(
env: &Env,
client: &AxelarGatewayClient,
owner: Address,
operator: Address,
previous_signers_retention: u32,
num_signers: u32,
) -> TestSignerSet {
let signer_set = generate_signers_set(env, num_signers, BytesN::random(env));
let initial_signers = vec![&env, signer_set.signers.clone()];
let minimum_rotation_delay = 0;
client.initialize(
&owner,
&operator,
&signer_set.domain_separator,
&minimum_rotation_delay,
&(previous_signers_retention as u64),
&initial_signers,
);
signer_set
}
pub fn get_approve_hash(env: &Env, messages: Vec<Message>) -> BytesN<32> {
env.crypto()
.keccak256(&(CommandType::ApproveMessages, messages).to_xdr(env))
.into()
}
pub fn generate_test_message(env: &Env) -> (Message, Bytes) {
let mut rng = rand::thread_rng();
let len = rng.gen_range(0..20);
let mut payload = std::vec![0u8; len];
rng.fill(&mut payload[..]);
let payload = Bytes::from_slice(env, &payload[..]);
(
Message {
source_chain: String::from_str(env, DESTINATION_CHAIN),
message_id: String::from_str(env, "test"),
source_address: String::from_str(env, DESTINATION_ADDRESS),
contract_address: Address::generate(env),
payload_hash: env.crypto().keccak256(&payload).into(),
},
payload,
)
}
pub fn randint(a: u32, b: u32) -> u32 {
rand::thread_rng().gen_range(a..b)
}
pub fn generate_signers_set(
env: &Env,
num_signers: u32,
domain_separator: BytesN<32>,
) -> TestSignerSet {
let mut rng = rand::thread_rng();
let mut signer_keypair: std::vec::Vec<_> = (0..num_signers)
.map(|_| {
let signing_key = SigningKey::generate(&mut rng);
let weight = rng.gen_range(1..10) as u128;
(signing_key, weight)
})
.collect();
signer_keypair.sort_by(|a, b| {
a.0.verifying_key()
.to_bytes()
.cmp(&b.0.verifying_key().to_bytes())
});
let total_weight = signer_keypair.iter().map(|(_, w)| w).sum::<u128>();
let signer_vec: std::vec::Vec<WeightedSigner> = signer_keypair
.iter()
.map(|(signing_key, w)| WeightedSigner {
signer: BytesN::<32>::from_array(env, &signing_key.verifying_key().to_bytes()),
weight: *w,
})
.collect();
let threshold = rng.gen_range(1..=total_weight);
let signers = WeightedSigners {
signers: signer_vec.into_vec(env),
threshold,
nonce: BytesN::<32>::from_array(env, &[0; 32]),
};
TestSignerSet {
signer_keys: signer_keypair
.into_iter()
.map(|(signing_key, _)| signing_key)
.collect(),
signers,
domain_separator,
}
}
pub fn generate_proof(env: &Env, data_hash: BytesN<32>, signer_set: TestSignerSet) -> Proof {
let signers_hash = env
.crypto()
.keccak256(&signer_set.signers.clone().to_xdr(env));
let mut msg: Bytes = signer_set.domain_separator.into();
msg.extend_from_array(&signers_hash.to_array());
msg.extend_from_array(&data_hash.to_array());
let msg_hash = env.crypto().keccak256(&msg);
let threshold = signer_set.signers.threshold as usize;
let proof_signers: std::vec::Vec<_> = signer_set
.signer_keys
.iter()
.zip(signer_set.signers.signers.iter())
.enumerate()
.map(|(i, (signing_key, weighted_signer))| {
if i > threshold {
return ProofSigner {
signer: weighted_signer,
signature: ProofSignature::Unsigned,
};
}
let signature: Signature = signing_key.sign(&msg_hash.to_array());
ProofSigner {
signer: weighted_signer,
signature: ProofSignature::Signed(BytesN::<64>::from_array(
env,
&signature.to_bytes(),
)),
}
})
.collect();
Proof {
signers: proof_signers.into_vec(env),
threshold: signer_set.signers.threshold,
nonce: signer_set.signers.nonce,
}
}
pub fn rotate_signers(env: &Env, contract_id: &Address, new_signers: TestSignerSet) {
let mut epoch_val: u64 = 0;
env.as_contract(contract_id, || {
epoch_val = assert_ok!(epoch(env)) + 1;
assert_ok!(auth::rotate_signers(env, &new_signers.signers, false));
});
assert_last_emitted_event(
env,
contract_id,
(
Symbol::new(&env, "signers_rotated"),
epoch_val,
new_signers.signers.hash(env),
),
(),
);
}