johnandersen777.bsky.social/atlibs-socialweb-computer-requester
socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons
0
0
use abi::records::temp::BadgeBlueKeys;
use abi::repo::{create, create_record};
use abi::{AtUri, Call, Handle, LexiconType, On, Run};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use crate::calls::{attest, created, typed};
use crate::ids::CallId;
use crate::input::{Args, Route, Step, did};
use crate::nsid::ASSOCIATE_CONFIRM;
#[derive(Default, Serialize, Deserialize)]
pub struct BindKey {
key: String,
}
pub fn bind_key(run: Run<Args>) -> Result<Handle, String> {
let (mut step, on) = Step::<BindKey>::new(run)?;
step.args.signed_in()?;
let me = step.args.me();
match on {
On::Start => step.out.call(CallId::Attest, attest(me, None)),
On::Result(CallId::Attest, result) => {
let key = result?["key"].as_str().filter(|k| k.starts_with("did:key:")).ok_or("attest must return a did:key")?.to_string();
step.out.call(CallId::Create, bind(me, &key, step.args.created_at()?));
step.state.key = key;
}
On::Result(CallId::Create, result) => return finish(step, result),
_ => {}
}
step.handle()
}
/// The badgeBlueKeys record binding `key` (this host's attestation key) to `me`, at the rkey verifiers read.
pub fn bind(me: &str, key: &str, created_at: abi::Datetime) -> Call {
let badge = BadgeBlueKeys {
key_id: key.into(),
name: Some("socialweb.computer requester attestation".into()),
challenge: me.into(),
service: Some("market_requester".into()),
created_at,
};
create_record(me, BadgeBlueKeys::lexicon_type(), Some(&badge_rkey(me, key)), typed(badge))
}
/// Where verifiers look for `key`'s binding to `me`.
pub fn binding(me: &str, key: &str) -> AtUri {
AtUri::new(me, BadgeBlueKeys::lexicon_type(), badge_rkey(me, key))
}
#[derive(Default, Serialize, Deserialize)]
pub struct Associate {
peer: String,
role: String,
}
// Old: did-key-associator main.js doRequesterAssociate / doBidderAssociate.
pub fn associate(run: Run<Args>) -> Result<Handle, String> {
let (mut step, on) = Step::<Associate>::new(run)?;
step.args.signed_in()?;
let me = step.args.me();
match on {
On::Start => {
let peer = did(step.args.arg("peer"), "peer")?;
let role = match step.args.arg("role").as_str().unwrap_or("requester") {
role @ ("requester" | "bidder") => role.to_string(),
_ => return Err("role must be requester or bidder".into()),
};
step.out.call(CallId::Confirm, Route::Proxy(format!("{peer}#{role}_associate")).call(ASSOCIATE_CONFIRM, json!({})));
step.state = Associate { peer, role };
}
On::Result(CallId::Confirm, result) => {
result?;
let Associate { peer, role } = &step.state;
let badge = BadgeBlueKeys {
key_id: peer.clone(),
name: Some(format!("{role}-{}", &peer[peer.len().saturating_sub(8)..])),
challenge: me.into(),
service: Some(format!("{role}_associate")),
created_at: step.args.created_at()?,
};
step.out.call(CallId::Create, create(me, badge));
}
On::Result(CallId::Create, result) => return finish(step, result),
_ => {}
}
step.handle()
}
// The state plus the created record's strong ref.
fn finish<S: Serialize + for<'de> Deserialize<'de> + Default>(mut step: Step<S>, result: Result<Value, String>) -> Result<Handle, String> {
let mut answer = json!(step.state);
answer["record"] = json!(created(result)?);
step.out.finish(answer);
step.handle()
}
fn base58btc(bytes: &[u8]) -> String {
const ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
let mut digits: Vec<u8> = Vec::new();
for &byte in bytes {
let mut carry = byte as u32;
for digit in digits.iter_mut() {
carry += (*digit as u32) << 8;
*digit = (carry % 58) as u8;
carry /= 58;
}
while carry > 0 {
digits.push((carry % 58) as u8);
carry /= 58;
}
}
let zeros = bytes.iter().take_while(|&&b| b == 0).count();
"1".repeat(zeros) + &digits.iter().rev().map(|&d| ALPHABET[d as usize] as char).collect::<String>()
}
// Verifiers (market-atproto keyBoundByBadgeBlueKey) getRecord exactly this rkey:
// base58btc(sha256("did:keyId")[0..24])[0..32].
fn badge_rkey(did: &str, key: &str) -> String {
base58btc(&Sha256::digest(format!("{did}:{key}").as_bytes())[..24]).chars().take(32).collect()
}