use crate::cesr::number::Number;
use crate::cesr::tholder::{Tholder, TholderSith};
use crate::cesr::Versionage;
use crate::keri::core::eventing::{ample, MAX_INT_THOLD};
use crate::keri::core::serdering::{SadValue, SerderKERI};
use crate::keri::{versify, Ilks, KERIError};
use indexmap::IndexMap;
use num_bigint::BigUint;
use serde_json::Value;
use std::collections::HashSet;
pub struct RotateEventBuilder {
pre: String,
keys: Vec<String>,
dig: String,
ilk: String,
sn: usize,
isith: Option<TholderSith>,
ndigs: Vec<String>,
nsith: Option<TholderSith>,
toad: Option<usize>,
wits: Vec<String>,
cuts: Vec<String>,
adds: Vec<String>,
data: Vec<SadValue>,
version: String,
kind: String,
intive: bool,
}
impl RotateEventBuilder {
pub fn new(pre: String, keys: Vec<String>, dig: String) -> Self {
Self {
pre,
keys,
dig,
ilk: Ilks::ROT.to_string(),
sn: 1,
isith: None,
ndigs: Vec::new(),
nsith: None,
toad: None,
wits: Vec::new(),
cuts: Vec::new(),
adds: Vec::new(),
data: Vec::new(),
version: "KERI10JSON000000_".to_string(),
kind: "JSON".to_string(),
intive: false,
}
}
pub fn with_ilk(mut self, ilk: String) -> Self {
self.ilk = ilk;
self
}
pub fn with_sn(mut self, sn: usize) -> Self {
self.sn = sn;
self
}
pub fn with_isith(mut self, isith: TholderSith) -> Self {
self.isith = Some(isith);
self
}
pub fn with_ndigs(mut self, ndigs: Vec<String>) -> Self {
self.ndigs = ndigs;
self
}
pub fn with_next_keys(mut self, ndigs: Vec<String>) -> Self {
self.ndigs = ndigs;
self
}
pub fn with_nsith(mut self, nsith: TholderSith) -> Self {
self.nsith = Some(nsith);
self
}
pub fn with_toad(mut self, toad: usize) -> Self {
self.toad = Some(toad);
self
}
pub fn with_wits(mut self, wits: Vec<String>) -> Self {
self.wits = wits;
self
}
pub fn with_cuts(mut self, cuts: Vec<String>) -> Self {
self.cuts = cuts;
self
}
pub fn with_adds(mut self, adds: Vec<String>) -> Self {
self.adds = adds;
self
}
pub fn with_data(mut self, data: Vec<SadValue>) -> Self {
self.data = data;
self
}
pub fn with_version(mut self, version: String) -> Self {
self.version = version;
self
}
pub fn with_kind(mut self, kind: String) -> Self {
self.kind = kind;
self
}
pub fn with_intive(mut self, intive: bool) -> Self {
self.intive = intive;
self
}
pub fn build(self) -> Result<SerderKERI, KERIError> {
if self.ilk != Ilks::ROT && self.ilk != Ilks::DRT {
return Err(KERIError::ValueError(format!(
"Invalid ilk = {} for rot or drt.",
self.ilk
)));
}
let sner = Number::from_num(&BigUint::from(self.sn))?;
if sner.num() < 1 {
return Err(KERIError::ValueError(format!(
"Invalid sn = 0x{} for rot or drt.",
sner.numh()
)));
}
let tholder = match self.isith {
Some(sith) => Tholder::new(None, None, Some(sith))?,
None => {
let default_isith =
std::cmp::max(1, (self.keys.len() as f64 / 2.0).ceil() as usize);
Tholder::new(None, None, Some(TholderSith::Integer(default_isith)))?
}
};
if let Some(num) = tholder.num() {
if num < 1 {
return Err(KERIError::ValueError(format!(
"Invalid sith = {} less than 1.",
num
)));
}
}
if tholder.size() > self.keys.len() {
return Err(KERIError::ValueError(format!(
"Invalid sith = {:?} for keys = {:?}",
tholder.sith(),
self.keys
)));
}
let ntholder = match self.nsith {
Some(sith) => Tholder::new(None, None, Some(sith))?,
None => {
let default_nsith =
std::cmp::max(0, (self.ndigs.len() as f64 / 2.0).ceil() as usize);
Tholder::new(None, None, Some(TholderSith::Integer(default_nsith)))?
}
};
if ntholder.size() > self.ndigs.len() {
return Err(KERIError::ValueError(format!(
"Invalid nsith = {:?} for ndigs = {:?}",
ntholder.sith(),
self.ndigs
)));
}
let wits = self.wits;
let witset: HashSet<_> = wits.iter().collect();
if witset.len() != wits.len() {
return Err(KERIError::ValueError(format!(
"Invalid wits = {:?}, has duplicates.",
wits
)));
}
let cuts = self.cuts;
let cutset: HashSet<_> = cuts.iter().collect();
if cutset.len() != cuts.len() {
return Err(KERIError::ValueError(format!(
"Invalid cuts = {:?}, has duplicates.",
cuts
)));
}
for cut in &cuts {
if !wits.contains(cut) {
return Err(KERIError::ValueError(format!(
"Invalid cuts = {:?}, not all members in wits.",
cuts
)));
}
}
let adds = self.adds;
let addset: HashSet<_> = adds.iter().collect();
if addset.len() != adds.len() {
return Err(KERIError::ValueError(format!(
"Invalid adds = {:?}, has duplicates.",
adds
)));
}
for add in &adds {
if wits.contains(add) {
return Err(KERIError::ValueError(format!(
"Intersecting wits = {:?} and adds = {:?}.",
wits, adds
)));
}
}
for add in &adds {
if cuts.contains(add) {
return Err(KERIError::ValueError(format!(
"Intersecting cuts = {:?} and adds = {:?}.",
cuts, adds
)));
}
}
let newitset: HashSet<String> = wits
.iter()
.filter(|wit| !cuts.contains(wit))
.cloned()
.chain(adds.clone())
.collect();
if newitset.len() != (wits.len() - cuts.len() + adds.len()) {
return Err(KERIError::ValueError(format!(
"Invalid member combination among wits = {:?}, cuts = {:?}, and adds = {:?}.",
wits, cuts, adds
)));
}
let toader = if let Some(toad) = self.toad {
Number::from_num(&BigUint::from(toad))?
} else if newitset.is_empty() {
Number::from_num(&BigUint::from(0usize))?
} else {
Number::from_num(&BigUint::from(ample(newitset.len())))?
};
if !newitset.is_empty() {
if toader.num() < 1 || toader.num() as usize > newitset.len() {
return Err(KERIError::ValueError(format!(
"Invalid toad = {} for wits = {:?}",
toader.num(),
newitset
)));
}
} else if toader.num() != 0 {
return Err(KERIError::ValueError(format!(
"Invalid toad = {} for wits = {:?}",
toader.num(),
newitset
)));
}
let vs = versify("KERI", &Versionage::from(self.version), &self.kind, 0)?;
let mut ked = IndexMap::new();
ked.insert("v".to_string(), SadValue::String(vs));
ked.insert("t".to_string(), SadValue::String(self.ilk));
ked.insert("d".to_string(), SadValue::String("".to_string()));
ked.insert("i".to_string(), SadValue::String(self.pre));
ked.insert("s".to_string(), SadValue::String(sner.numh()));
ked.insert("p".to_string(), SadValue::String(self.dig));
let kt =
if self.intive && tholder.num().is_some() && tholder.num().unwrap() <= MAX_INT_THOLD {
Value::Number(serde_json::Number::from(tholder.num().unwrap() as u64))
} else {
match &tholder.sith() {
TholderSith::Integer(n) => Value::Number(serde_json::Number::from(*n as u64)),
TholderSith::HexString(s) => Value::String(s.clone()),
TholderSith::Json(s) => serde_json::from_str(s).map_err(|e| {
KERIError::ValueError(format!(
"Invalid tholder = {} for keys = {:?}",
s, self.keys
))
})?,
TholderSith::Weights(w) => serde_json::to_value(w).map_err(|e| {
KERIError::ValueError(format!(
"Invalid tholder = {:?} for keys = {:?}",
w, self.keys
))
})?,
}
};
match kt {
Value::Number(n) => {
if let Some(_) = n.as_u64() {
ked.insert("kt".to_string(), SadValue::Number(n));
}
}
Value::String(s) => {
ked.insert("kt".to_string(), SadValue::String(s.to_string()));
()
}
_ => {
if let Some(num) = tholder.num() {
ked.insert("kt".to_string(), SadValue::String(num.to_string()));
} else {
ked.insert("kt".to_string(), SadValue::String(kt.to_string()));
}
}
};
let key_values = self
.keys
.iter()
.map(|k| SadValue::String(k.clone()))
.collect();
ked.insert("k".to_string(), SadValue::Array(key_values));
let nt = if self.intive
&& ntholder.num().is_some()
&& ntholder.num().unwrap() <= MAX_INT_THOLD
{
Value::Number(serde_json::Number::from(ntholder.num().unwrap() as u64))
} else {
match &ntholder.sith() {
TholderSith::Integer(n) => Value::Number(serde_json::Number::from(*n as u64)),
TholderSith::HexString(s) => Value::String(s.clone()),
TholderSith::Json(s) => serde_json::from_str(s).map_err(|e| {
KERIError::ValueError(format!(
"Invalid tholder = {} for keys = {:?}",
s, self.keys
))
})?,
TholderSith::Weights(w) => serde_json::to_value(w).map_err(|e| {
KERIError::ValueError(format!(
"Invalid tholder = {:?} for keys = {:?}",
w, self.keys
))
})?,
}
};
match nt {
Value::Number(n) => {
if let Some(_) = n.as_u64() {
ked.insert("nt".to_string(), SadValue::Number(n));
}
}
Value::String(s) => {
ked.insert("nt".to_string(), SadValue::String(s.to_string()));
()
}
_ => {
if let Some(num) = tholder.num() {
ked.insert("nt".to_string(), SadValue::String(num.to_string()));
} else {
ked.insert("nt".to_string(), SadValue::String(nt.to_string()));
}
}
};
let ndig_values = self
.ndigs
.iter()
.map(|n| SadValue::String(n.clone()))
.collect();
ked.insert("n".to_string(), SadValue::Array(ndig_values));
let bt = if self.intive && toader.num() <= MAX_INT_THOLD as u128 {
Value::Number(serde_json::Number::from(toader.num() as u64))
} else {
Value::String(toader.numh().to_string())
};
match bt {
Value::Number(n) => {
if let Some(_) = n.as_u64() {
ked.insert("bt".to_string(), SadValue::Number(n));
}
}
Value::String(s) => {
ked.insert("bt".to_string(), SadValue::String(s));
()
}
_ => {
ked.insert(
"bt".to_string(),
SadValue::String(toader.numh().to_string()),
);
()
}
};
let cut_values = cuts.iter().map(|n| SadValue::String(n.clone())).collect();
ked.insert("br".to_string(), SadValue::Array(cut_values));
let add_values = adds.iter().map(|n| SadValue::String(n.clone())).collect();
ked.insert("ba".to_string(), SadValue::Array(add_values));
if !self.data.is_empty() {
ked.insert("a".to_string(), SadValue::Array(self.data.clone())); }
let serder = SerderKERI::from_sad_and_saids(&ked, None)?;
Ok(serder)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cesr::diger::Diger;
use crate::cesr::mtr_dex;
use crate::cesr::signing::signer::Signer;
use crate::Matter;
use std::error::Error;
#[test]
fn test_rotation_event_builder_basic() -> Result<(), Box<dyn Error>> {
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let dig = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let keys = vec!["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA".to_string()];
let serder = RotateEventBuilder::new(pre.clone(), keys, dig.clone()).build()?;
let ked = serder.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::ROT);
assert_eq!(ked["i"].as_str().unwrap(), pre);
assert_eq!(ked["p"].as_str().unwrap(), dig);
assert_eq!(ked["s"].as_str().unwrap(), "1");
Ok(())
}
#[test]
fn test_rotation_event_builder_with_witnesses() -> Result<(), Box<dyn Error>> {
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let dig = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let keys = vec!["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA".to_string()];
let wits = vec![
"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha".to_string(),
"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM".to_string(),
"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX".to_string(),
];
let cuts = vec!["BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM".to_string()];
let adds = vec!["BMusuVxC3AuXkqXAD-2UN4PWr2Eu_7oX3UXxrtXASh-0".to_string()];
let serder = RotateEventBuilder::new(pre.clone(), keys, dig.clone())
.with_wits(wits)
.with_cuts(cuts)
.with_adds(adds)
.with_toad(2)
.build()?;
let ked = serder.ked();
assert_eq!(ked["bt"].as_str().unwrap(), "2");
let br = ked["br"].as_array().unwrap();
assert_eq!(br.len(), 1);
assert_eq!(
br[0].as_str().unwrap(),
"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM"
);
let ba = ked["ba"].as_array().unwrap();
assert_eq!(ba.len(), 1);
assert_eq!(
ba[0].as_str().unwrap(),
"BMusuVxC3AuXkqXAD-2UN4PWr2Eu_7oX3UXxrtXASh-0"
);
Ok(())
}
#[test]
fn test_rotation_event_builder_with_next_keys() -> Result<(), Box<dyn Error>> {
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let dig = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let keys = vec!["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA".to_string()];
let seed = b"\x83B~\x04\x94\xe3\xceUQy\x11f\x0c\x93]\x1e\xbf\xacQ\xb5\xd6Y^\xa2E\xfa\x015\
\x98Y\xdd\xe8";
let signer = Signer::new(Some(seed), Some(mtr_dex::ED25519_SEED), Some(true))?;
let ndigs = vec![Diger::new(
Some(&signer.verfer().qb64b()),
Some(mtr_dex::BLAKE3_256),
None,
None,
)?
.qb64()];
let serder = RotateEventBuilder::new(pre.clone(), keys, dig.clone())
.with_next_keys(ndigs.clone())
.with_sn(2)
.build()?;
let ked = serder.ked();
assert_eq!(ked["s"].as_str().unwrap(), "2");
let n = ked["n"].as_array().unwrap();
assert_eq!(n.len(), 1);
assert_eq!(n[0].as_str().unwrap(), ndigs[0]);
Ok(())
}
#[test]
fn test_rotation_event_builder_with_intive() -> Result<(), Box<dyn Error>> {
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let dig = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let keys = vec!["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA".to_string()];
let seed = b"\x83B~\x04\x94\xe3\xceUQy\x11f\x0c\x93]\x1e\xbf\xacQ\xb5\xd6Y^\xa2E\xfa\x015\
\x98Y\xdd\xe8";
let signer = Signer::new(Some(seed), Some(mtr_dex::ED25519_SEED), Some(true))?;
let ndigs = vec![Diger::new(
Some(&signer.verfer().qb64b()),
Some(mtr_dex::BLAKE3_256),
None,
None,
)?
.qb64()];
let serder = RotateEventBuilder::new(pre.clone(), keys, dig.clone())
.with_next_keys(ndigs)
.with_intive(true)
.build()?;
let ked = serder.ked();
assert!(ked["kt"].is_number());
assert_eq!(ked["kt"].as_u64().unwrap(), 1);
assert!(ked["nt"].is_number());
assert_eq!(ked["nt"].as_u64().unwrap(), 1);
assert!(ked["bt"].is_number());
assert_eq!(ked["bt"].as_u64().unwrap(), 0);
Ok(())
}
#[test]
fn test_rotation_transferable_not_abandoned() -> Result<(), Box<dyn Error>> {
let seed0 = b"\x9f\x82\xad\xf4\xa9\xff\xda\xbc\xed\x39\xb6\xc8\x29\xcb\x6a\xb0\x08\x85\x5a\xcb\xc4\x19\x39\xbb\x74\xdc\x70\x8a\x38\xb6\x3c\x99";
let signer0 = Signer::new(Some(&seed0[..]), Some(mtr_dex::ED25519_SEED), Some(true))?;
assert_eq!(signer0.code(), mtr_dex::ED25519_SEED);
assert_eq!(signer0.verfer().code(), mtr_dex::ED25519);
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let keys1 = vec!["DB4GWvru73jWZKpNgMQp8ayDRin0NG0Ymn_RXQP_v-PQ".to_string()];
let seed2 = b"\xbe\x96\x02\xa9\x88\xce\xf9O\x1e\x0fo\xc0\xff\x98\xb6\xfa\x1e\xa2y\xf2e\xf9AL\x1aeK\xafj\xa1pB";
let signer2 = Signer::new(Some(&seed2[..]), Some(mtr_dex::ED25519_SEED), Some(true))?;
assert_eq!(signer2.code(), mtr_dex::ED25519_SEED);
assert_eq!(signer2.verfer().code(), mtr_dex::ED25519);
let keys2 = vec![Diger::new(
Some(&signer2.verfer().qb64b()),
Some(mtr_dex::BLAKE3_256),
None,
None,
)?
.qb64()];
let said = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let serder1 = RotateEventBuilder::new(pre.clone(), keys1.clone(), said.clone())
.with_ndigs(keys2.clone())
.with_sn(1)
.build()?;
let ked = serder1.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::ROT);
assert_eq!(ked["i"].as_str().unwrap(), pre);
assert_eq!(ked["s"].as_str().unwrap(), "1");
assert_eq!(ked["p"].as_str().unwrap(), said);
assert_eq!(ked["kt"].as_str().unwrap(), "1");
assert_eq!(ked["nt"].as_str().unwrap(), "1");
let n = ked["n"].as_array().unwrap();
assert_eq!(n.len(), 1);
assert_eq!(n[0].as_str().unwrap(), keys2[0]);
assert_eq!(ked["bt"].as_str().unwrap(), "0");
let expected_raw = b"{\"v\":\"KERI10JSON000160_\",\"t\":\"rot\",\"d\":\"EFl8nvRCbN2xQJI75nBXp-gaXuHJw8zheVjw\
MN_rB-pb\",\"i\":\"DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH\",\"s\":\"1\",\"p\":\"EJ\
QUyxnzIAtmZPoq9f4fExeGN0qfJmaFnUEKTwIiTBPj\",\"kt\":\"1\",\"k\":[\"DB4GWvru73jWZKpNg\
MQp8ayDRin0NG0Ymn_RXQP_v-PQ\"],\"nt\":\"1\",\"n\":[\"EIsKL3B6Zz5ICGxCQp-SoLXjwOrdlSb\
LJrEn21c2zVaU\"],\"bt\":\"0\",\"br\":[],\"ba\":[],\"a\":[]}";
assert_eq!(ked["v"].as_str().unwrap(), "KERI10JSON000160_");
assert_eq!(ked["t"].as_str().unwrap(), "rot");
Ok(())
}
#[test]
fn test_rotation_transferable_not_abandoned_intive() -> Result<(), Box<dyn Error>> {
let seed0 = b"\x9f\x82\xad\xf4\xa9\xff\xda\xbc\xed\x39\xb6\xc8\x29\xcb\x6a\xb0\x08\x85\x5a\xcb\xc4\x19\x39\xbb\x74\xdc\x70\x8a\x38\xb6\x3c\x99";
let signer0 = Signer::new(Some(&seed0[..]), Some(mtr_dex::ED25519_SEED), Some(true))?;
assert_eq!(signer0.code(), mtr_dex::ED25519_SEED);
assert_eq!(signer0.verfer().code(), mtr_dex::ED25519);
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let keys1 = vec!["DB4GWvru73jWZKpNgMQp8ayDRin0NG0Ymn_RXQP_v-PQ".to_string()];
let seed2 = b"\xbe\x96\x02\xa9\x88\xce\xf9O\x1e\x0fo\xc0\xff\x98\xb6\xfa\x1e\xa2y\xf2e\xf9AL\x1aeK\xafj\xa1pB";
let signer2 = Signer::new(Some(&seed2[..]), Some(mtr_dex::ED25519_SEED), Some(true))?;
assert_eq!(signer2.code(), mtr_dex::ED25519_SEED);
assert_eq!(signer2.verfer().code(), mtr_dex::ED25519);
let keys2 = vec![Diger::new(
Some(&signer2.verfer().qb64b()),
Some(mtr_dex::BLAKE3_256),
None,
None,
)?
.qb64()];
let said = "EY2L3ycqK9645aEeQKP941xojSiuiHsw4Y6yTW-DpRXs".to_string();
let serder1 = RotateEventBuilder::new(pre.clone(), keys1.clone(), said.clone())
.with_ndigs(keys2.clone())
.with_sn(1)
.with_intive(true)
.build()?;
let ked = serder1.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::ROT);
assert_eq!(ked["i"].as_str().unwrap(), pre);
assert_eq!(ked["s"].as_str().unwrap(), "1");
assert_eq!(ked["p"].as_str().unwrap(), said);
assert!(ked["kt"].is_number());
assert_eq!(ked["kt"].as_u64().unwrap(), 1);
assert!(ked["nt"].is_number());
assert_eq!(ked["nt"].as_u64().unwrap(), 1);
let n = ked["n"].as_array().unwrap();
assert_eq!(n.len(), 1);
assert_eq!(n[0].as_str().unwrap(), keys2[0]);
assert!(ked["bt"].is_number());
assert_eq!(ked["bt"].as_u64().unwrap(), 0);
let expected_raw = b"{\"v\":\"KERI10JSON00015a_\",\"t\":\"rot\",\"d\":\"ECauhEzA4DJDXVDnNQiGQ0sKXa6sx_GgS8Eb\
dzm4E-kQ\",\"i\":\"DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH\",\"s\":\"1\",\"p\":\"EJ\
QUyxnzIAtmZPoq9f4fExeGN0qfJmaFnUEKTwIiTBPj\",\"kt\":1,\"k\":[\"DB4GWvru73jWZKpNgMQ\
p8ayDRin0NG0Ymn_RXQP_v-PQ\"],\"nt\":1,\"n\":[\"EIsKL3B6Zz5ICGxCQp-SoLXjwOrdlSbLJrE\
n21c2zVaU\"],\"bt\":0,\"br\":[],\"ba\":[],\"a\":[]}";
assert_eq!(ked["v"].as_str().unwrap(), "KERI10JSON00015a_");
assert_eq!(ked["t"].as_str().unwrap(), "rot");
Ok(())
}
#[test]
fn test_rotation_with_witnesses() -> Result<(), Box<dyn Error>> {
let pre = "DFs8BBx86uytIM0D2BhsE5rrqVIT8ef8mflpNceHo4XH".to_string();
let keys1 = vec!["DB4GWvru73jWZKpNgMQp8ayDRin0NG0Ymn_RXQP_v-PQ".to_string()];
let dig = "EJQUyxnzIAtmZPoq9f4fExeGN0qfJmaFnUEKTwIiTBPj".to_string();
let wits = vec![
"BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha".to_string(),
"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM".to_string(),
"BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX".to_string(),
];
let cuts = vec!["BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM".to_string()];
let adds = vec!["BMusuVxC3AuXkqXAD-2UN4PWr2Eu_7oX3UXxrtXASh-0".to_string()];
let seed2 = b"\xbe\x96\x02\xa9\x88\xce\xf9O\x1e\x0fo\xc0\xff\x98\xb6\xfa\x1e\xa2y\xf2e\xf9AL\x1aeK\xafj\xa1pB";
let signer2 = Signer::new(Some(&seed2[..]), Some(mtr_dex::ED25519_SEED), Some(true))?;
let keys2 = vec![Diger::new(
Some(&signer2.verfer().qb64b()),
Some(mtr_dex::BLAKE3_256),
None,
None,
)?
.qb64()];
let serder = RotateEventBuilder::new(pre.clone(), keys1.clone(), dig.clone())
.with_ndigs(keys2)
.with_wits(wits)
.with_cuts(cuts)
.with_adds(adds)
.with_toad(2)
.build()?;
let ked = serder.ked();
assert_eq!(ked["t"].as_str().unwrap(), Ilks::ROT);
assert_eq!(ked["i"].as_str().unwrap(), pre);
assert_eq!(ked["p"].as_str().unwrap(), dig);
assert_eq!(ked["bt"].as_str().unwrap(), "2");
let br = ked["br"].as_array().unwrap();
assert_eq!(br.len(), 1);
assert_eq!(
br[0].as_str().unwrap(),
"BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM"
);
let ba = ked["ba"].as_array().unwrap();
assert_eq!(ba.len(), 1);
assert_eq!(
ba[0].as_str().unwrap(),
"BMusuVxC3AuXkqXAD-2UN4PWr2Eu_7oX3UXxrtXASh-0"
);
Ok(())
}
}