tap_wasm/
lib.rs

1//! WebAssembly bindings for the Transaction Authorization Protocol
2//!
3//! This crate provides WebAssembly bindings for the TAP agent, allowing it to be used in
4//! browser and other JavaScript environments. It wraps the tap-agent crate's functionality
5//! with JavaScript-friendly interfaces.
6
7mod util;
8mod wasm_agent;
9
10use js_sys::{Array, Object, Reflect};
11use std::collections::HashMap;
12use std::fmt;
13use tap_agent::did::KeyType as TapKeyType;
14use wasm_bindgen::prelude::*;
15use web_sys::console;
16
17pub use wasm_agent::WasmTapAgent;
18
19/// Set up panic hook for better error messages when debugging in browser
20#[wasm_bindgen(start)]
21pub fn start() -> Result<(), JsValue> {
22    #[cfg(feature = "console_error_panic_hook")]
23    console_error_panic_hook::set_once();
24    Ok(())
25}
26
27/// The type of TAP Messages following the TAP specification
28#[derive(Debug, Clone, Copy, PartialEq)]
29#[wasm_bindgen]
30pub enum MessageType {
31    /// Transaction proposal (TAIP-3)
32    Transfer,
33    /// Payment request message (TAIP-14)
34    Payment,
35    /// Presentation message (TAIP-8)
36    Presentation,
37    /// Authorization response (TAIP-4)
38    Authorize,
39    /// Rejection response (TAIP-4)
40    Reject,
41    /// Settlement notification (TAIP-4)
42    Settle,
43    /// Cancellation message (TAIP-4)
44    Cancel,
45    /// Revert request (TAIP-4)
46    Revert,
47    /// Add agents to transaction (TAIP-5)
48    AddAgents,
49    /// Replace an agent (TAIP-5)
50    ReplaceAgent,
51    /// Remove an agent (TAIP-5)
52    RemoveAgent,
53    /// Update policies (TAIP-7)
54    UpdatePolicies,
55    /// Update party information (TAIP-6)
56    UpdateParty,
57    /// Confirm relationship (TAIP-9)
58    ConfirmRelationship,
59    /// Connect request (TAIP-15)
60    Connect,
61    /// Authorization required response (TAIP-15)
62    AuthorizationRequired,
63    /// Complete message (TAIP-14)
64    Complete,
65    /// Error message
66    Error,
67    /// Unknown message type
68    Unknown,
69}
70
71impl fmt::Display for MessageType {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            MessageType::Transfer => write!(f, "https://tap.rsvp/schema/1.0#Transfer"),
75            MessageType::Payment => write!(f, "https://tap.rsvp/schema/1.0#Payment"),
76            MessageType::Presentation => write!(f, "https://tap.rsvp/schema/1.0#Presentation"),
77            MessageType::Authorize => write!(f, "https://tap.rsvp/schema/1.0#Authorize"),
78            MessageType::Reject => write!(f, "https://tap.rsvp/schema/1.0#Reject"),
79            MessageType::Settle => write!(f, "https://tap.rsvp/schema/1.0#Settle"),
80            MessageType::Cancel => write!(f, "https://tap.rsvp/schema/1.0#Cancel"),
81            MessageType::Revert => write!(f, "https://tap.rsvp/schema/1.0#Revert"),
82            MessageType::AddAgents => write!(f, "https://tap.rsvp/schema/1.0#AddAgents"),
83            MessageType::ReplaceAgent => write!(f, "https://tap.rsvp/schema/1.0#ReplaceAgent"),
84            MessageType::RemoveAgent => write!(f, "https://tap.rsvp/schema/1.0#RemoveAgent"),
85            MessageType::UpdatePolicies => write!(f, "https://tap.rsvp/schema/1.0#UpdatePolicies"),
86            MessageType::UpdateParty => write!(f, "https://tap.rsvp/schema/1.0#UpdateParty"),
87            MessageType::ConfirmRelationship => {
88                write!(f, "https://tap.rsvp/schema/1.0#ConfirmRelationship")
89            }
90            MessageType::Connect => write!(f, "https://tap.rsvp/schema/1.0#Connect"),
91            MessageType::AuthorizationRequired => {
92                write!(f, "https://tap.rsvp/schema/1.0#AuthorizationRequired")
93            }
94            MessageType::Complete => write!(f, "https://tap.rsvp/schema/1.0#Complete"),
95            MessageType::Error => write!(f, "https://tap.rsvp/schema/1.0#Error"),
96            MessageType::Unknown => write!(f, "UNKNOWN"),
97        }
98    }
99}
100
101impl From<&str> for MessageType {
102    fn from(s: &str) -> Self {
103        match s {
104            "https://tap.rsvp/schema/1.0#Transfer" => MessageType::Transfer,
105            "https://tap.rsvp/schema/1.0#Payment" => MessageType::Payment,
106            "https://tap.rsvp/schema/1.0#Presentation" => MessageType::Presentation,
107            "https://tap.rsvp/schema/1.0#Authorize" => MessageType::Authorize,
108            "https://tap.rsvp/schema/1.0#Reject" => MessageType::Reject,
109            "https://tap.rsvp/schema/1.0#Settle" => MessageType::Settle,
110            "https://tap.rsvp/schema/1.0#Cancel" => MessageType::Cancel,
111            "https://tap.rsvp/schema/1.0#Revert" => MessageType::Revert,
112            "https://tap.rsvp/schema/1.0#AddAgents" => MessageType::AddAgents,
113            "https://tap.rsvp/schema/1.0#ReplaceAgent" => MessageType::ReplaceAgent,
114            "https://tap.rsvp/schema/1.0#RemoveAgent" => MessageType::RemoveAgent,
115            "https://tap.rsvp/schema/1.0#UpdatePolicies" => MessageType::UpdatePolicies,
116            "https://tap.rsvp/schema/1.0#UpdateParty" => MessageType::UpdateParty,
117            "https://tap.rsvp/schema/1.0#ConfirmRelationship" => MessageType::ConfirmRelationship,
118            "https://tap.rsvp/schema/1.0#Connect" => MessageType::Connect,
119            "https://tap.rsvp/schema/1.0#AuthorizationRequired" => {
120                MessageType::AuthorizationRequired
121            }
122            "https://tap.rsvp/schema/1.0#Complete" => MessageType::Complete,
123            "https://tap.rsvp/schema/1.0#Error" => MessageType::Error,
124            _ => MessageType::Unknown,
125        }
126    }
127}
128
129/// Key type enumeration for WASM
130#[wasm_bindgen]
131pub enum WasmKeyType {
132    /// Ed25519 key type
133    Ed25519,
134    /// P-256 key type
135    P256,
136    /// Secp256k1 key type
137    Secp256k1,
138}
139
140impl From<WasmKeyType> for TapKeyType {
141    fn from(key_type: WasmKeyType) -> Self {
142        match key_type {
143            WasmKeyType::Ed25519 => TapKeyType::Ed25519,
144            WasmKeyType::P256 => TapKeyType::P256,
145            WasmKeyType::Secp256k1 => TapKeyType::Secp256k1,
146        }
147    }
148}
149
150/// Represents a node on the TAP network that manages multiple agents
151#[wasm_bindgen]
152#[derive(Clone)]
153pub struct TapNode {
154    agents: HashMap<String, WasmTapAgent>,
155    debug: bool,
156}
157
158#[wasm_bindgen]
159impl TapNode {
160    /// Creates a new TapNode
161    #[wasm_bindgen(constructor)]
162    pub fn new(config: JsValue) -> Self {
163        console_error_panic_hook::set_once();
164
165        let debug = if let Ok(debug_prop) = Reflect::get(&config, &JsValue::from_str("debug")) {
166            debug_prop.is_truthy()
167        } else {
168            false
169        };
170
171        TapNode {
172            agents: HashMap::new(),
173            debug,
174        }
175    }
176
177    /// Adds an agent to this node
178    pub fn add_agent(&mut self, agent: WasmTapAgent) -> Result<(), JsValue> {
179        let did = agent.get_did();
180        self.agents.insert(did.clone(), agent);
181
182        if self.debug {
183            console::log_1(&JsValue::from_str(&format!("Added agent {} to node", did)));
184        }
185
186        Ok(())
187    }
188
189    /// Gets an agent by DID
190    pub fn get_agent(&self, did: &str) -> Option<WasmTapAgent> {
191        self.agents.get(did).cloned()
192    }
193
194    /// Lists all agents in this node
195    pub fn list_agents(&self) -> JsValue {
196        let result = Array::new();
197
198        for (did, agent) in &self.agents {
199            let agent_obj = Object::new();
200            Reflect::set(
201                &agent_obj,
202                &JsValue::from_str("did"),
203                &JsValue::from_str(did),
204            )
205            .unwrap();
206
207            if let Some(nickname) = agent.nickname() {
208                Reflect::set(
209                    &agent_obj,
210                    &JsValue::from_str("nickname"),
211                    &JsValue::from_str(&nickname),
212                )
213                .unwrap();
214            }
215
216            result.push(&agent_obj);
217        }
218
219        result.into()
220    }
221
222    /// Removes an agent from this node
223    pub fn remove_agent(&mut self, did: &str) -> bool {
224        let removed = self.agents.remove(did).is_some();
225
226        if removed && self.debug {
227            console::log_1(&JsValue::from_str(&format!(
228                "Removed agent {} from node",
229                did
230            )));
231        }
232
233        removed
234    }
235}
236
237/// Generates a UUID v4
238#[wasm_bindgen]
239pub fn generate_uuid_v4() -> String {
240    uuid::Uuid::new_v4().to_string()
241}