Skip to main content

bb_ops/syscalls/peers/
lookup.rs

1//! `AddressBook::Lookup(peer) → addresses` — custom op registered via
2//! `bb::register_op!` that resolves every multiaddr bound to a peer
3//! from the engine's [`bb_runtime::framework::AddressBook`].
4//!
5//! Returns the full ordered slice; downstream Components that need a
6//! single address pick one explicitly. Unknown or empty-address-list
7//! peers surface as `OpError` so the recording surface notices the
8//! missing seed.
9
10use bb_ir::proto::onnx::NodeProto;
11use bb_runtime::atomic::DispatchResult;
12use bb_runtime::bus::{OpError, OpErrorKind};
13use bb_runtime::ids::PeerId;
14use bb_runtime::runtime::RuntimeResourceRef;
15use bb_runtime::slot_value::SlotValue;
16use bb_runtime::syscall::values::{AddressVecValue, PeerIdValue};
17
18/// `(domain, op_type)` registration key.
19pub const DOMAIN: &str = "ai.bytesandbrains.address_book";
20/// Op type name.
21pub const OP_TYPE: &str = "Lookup";
22/// Output port carrying the resolved `Vec<Address>`.
23pub const PORT_ADDRESSES: &str = "addresses";
24
25/// Invoke fn — read every address for `peer` and emit them on the
26/// `addresses` output. Missing peer or empty list → `OpError` with
27/// `ExecutionFailed`; missing or mistyped `peer` input → `OpError`
28/// with the matching `MissingSlot` / `TypeMismatch` kind.
29pub fn invoke(
30    _node: &NodeProto,
31    inputs: &[(&str, &dyn SlotValue)],
32    ctx: &mut RuntimeResourceRef<'_>,
33) -> Result<DispatchResult, OpError> {
34    let peer = downcast_peer(inputs)?;
35    let addrs = ctx
36        .peers
37        .addresses
38        .lookup(peer)
39        .map(|s| s.to_vec())
40        .ok_or_else(|| OpError {
41            kind: OpErrorKind::ExecutionFailed,
42            reason: "address_book_lookup_miss",
43            detail: format!("AddressBook::Lookup: peer {peer} not in address book"),
44        })?;
45    Ok(DispatchResult::Immediate(vec![(
46        PORT_ADDRESSES.to_string(),
47        Box::new(AddressVecValue(addrs)) as Box<dyn SlotValue>,
48    )]))
49}
50
51fn downcast_peer(inputs: &[(&str, &dyn SlotValue)]) -> Result<PeerId, OpError> {
52    let (_, value) = inputs
53        .iter()
54        .find(|(n, _)| *n == "peer")
55        .ok_or_else(|| OpError {
56            kind: OpErrorKind::MissingSlot,
57            reason: "missing_peer",
58            detail: "AddressBook::Lookup: required input `peer` is absent".into(),
59        })?;
60    value
61        .as_any()
62        .downcast_ref::<PeerIdValue>()
63        .map(|p| p.0)
64        .ok_or_else(|| OpError {
65            kind: OpErrorKind::TypeMismatch,
66            reason: "expected_peer_id",
67            detail: "AddressBook::Lookup: input `peer` is not a PeerId".into(),
68        })
69}
70
71
72bb_derive::register_op! {
73    domain: "ai.bytesandbrains.address_book",
74    op_type: "Lookup",
75    invoke: invoke,
76}