use crate::bytewords::*;
use crate::sskr_shares::*;
use anyhow::{anyhow, bail, Error};
use bip39::{Language, Mnemonic};
use dcbor::CBOR;
use sskr::sskr_combine;
use std::collections::HashMap;
pub fn recover(lines: Vec<String>) -> Result<Mnemonic, Error> {
let mut shares: Vec<Vec<u8>> = vec![];
for line in lines {
let bytes = byteword_string_to_bytes(line)?;
let cbor = CBOR::from_data(bytes.as_slice())?;
let cbor_bytes = cbor.expect_tagged_value(309)?;
let share = cbor_bytes.expect_byte_string()?;
shares.push(share.to_vec());
}
let mut share_ids: Vec<u16> = vec![];
let mut share_meta: Vec<[usize; 5]> = vec![];
for share in shares.clone() {
let (id, meta) = share_metadata(&share)?;
share_ids.push(id);
share_meta.push(meta);
}
let mut shares_by_universe: HashMap<(u16, usize, usize), Vec<usize>> = HashMap::new();
for i in 0..shares.len() {
let key = (share_ids[i], share_meta[i][1], share_meta[i][2]);
if !shares_by_universe.contains_key(&key) {
shares_by_universe.insert(key, vec![]);
}
shares_by_universe.get_mut(&key).unwrap().push(i);
}
for ((identifier, group_threshold, _group_count), share_indices) in &shares_by_universe {
let mut shares_by_group: HashMap<usize, Vec<(usize, Vec<u8>)>> = HashMap::new();
for &i in share_indices {
let share_group_num = share_meta[i][0];
if !shares_by_group.contains_key(&share_group_num) {
shares_by_group.insert(share_group_num, vec![]);
}
shares_by_group
.get_mut(&share_group_num)
.unwrap()
.push((i, shares[i].to_vec()));
}
let mut recoverable_groups: Vec<usize> = vec![];
for (group_num, shares) in &shares_by_group {
let member_threshold = share_meta[shares[0].0][4];
if shares
.iter()
.any(|(i, _share)| share_meta[*i][4] != member_threshold)
{
bail!(
"Mismatched share member thresholds in group {}, shares don't go together",
group_num + 1
);
}
if shares.len() >= member_threshold {
recoverable_groups.push(*group_num);
}
}
if recoverable_groups.len() < *group_threshold {
println!(
"{}: Not enough groups, need to satisfy at least {} but only {} are satisfied ({})",
identifier,
group_threshold,
recoverable_groups.len(),
recoverable_groups
.iter()
.map(|g| (g + 1).to_string())
.collect::<Vec<String>>()
.join(" and ")
);
continue;
}
let mut shares_for_recovery: Vec<Vec<u8>> = vec![];
for group_num in recoverable_groups.iter().take(*group_threshold) {
let group_shares = shares_by_group[&group_num]
.iter()
.map(|(_i, share)| share.to_vec())
.collect::<Vec<Vec<u8>>>();
for share in group_shares {
shares_for_recovery.push(share);
}
}
match sskr_combine(&shares_for_recovery) {
Ok(secret) => match Mnemonic::from_entropy(secret.data(), Language::English) {
Ok(mnemonic) => {
println!("{}: Successfully recovered!", identifier);
return Ok(mnemonic);
}
Err(e) => {
println!(
"{}: Recovered entropy 0x{} but unable to make mnemonic: {}",
identifier,
hex::encode(secret.data()),
e
);
continue;
}
},
Err(e) => {
println!("{}: Error during SSKR combination: {}", identifier, e);
continue;
}
}
}
Err(anyhow!(
"Not enough shares that belong together, unable to recover :("
))
}