geph5_broker_protocol/
lib.rs

1use std::{collections::BTreeMap, fmt::Display, net::SocketAddr};
2
3use async_trait::async_trait;
4use bytes::Bytes;
5use mizaru2::{
6    BlindedClientToken, BlindedSignature, ClientToken, SingleBlindedSignature,
7    SingleUnblindedSignature, UnblindedSignature,
8};
9use nanorpc::{nanorpc_derive, JrpcRequest, JrpcResponse};
10mod route;
11pub use route::*;
12mod exit;
13pub use exit::*;
14mod signed;
15use serde::{Deserialize, Serialize};
16pub use signed::*;
17mod mac;
18pub mod puzzle;
19pub use mac::*;
20mod bridge;
21pub use bridge::*;
22use thiserror::Error;
23
24#[nanorpc_derive]
25#[async_trait]
26pub trait BrokerProtocol {
27    async fn opaque_abtest(&self, test: String, id: u64) -> bool;
28
29    async fn call_geph_payments(&self, jrpc_req: JrpcRequest)
30        -> Result<JrpcResponse, GenericError>;
31
32    async fn get_mizaru_subkey(&self, level: AccountLevel, epoch: u16) -> Bytes;
33
34    async fn get_auth_token(&self, credential: Credential) -> Result<String, AuthError>;
35    async fn get_user_info(&self, auth_token: String) -> Result<Option<UserInfo>, AuthError>;
36    async fn get_user_info_by_cred(
37        &self,
38        credential: Credential,
39    ) -> Result<Option<UserInfo>, AuthError>;
40
41    async fn get_connect_token(
42        &self,
43        auth_token: String,
44        level: AccountLevel,
45        epoch: u16,
46        blind_token: BlindedClientToken,
47    ) -> Result<BlindedSignature, AuthError>;
48
49    async fn get_bw_token(
50        &self,
51        auth_token: String,
52        blind_token: BlindedClientToken,
53    ) -> Result<SingleBlindedSignature, AuthError>;
54
55    async fn consume_bw_token(
56        &self,
57        token: ClientToken,
58        sig: SingleUnblindedSignature,
59    ) -> Result<(), AuthError>;
60
61    async fn get_exits(&self) -> Result<StdcodeSigned<ExitList>, GenericError>;
62    async fn get_free_exits(&self) -> Result<StdcodeSigned<ExitList>, GenericError>;
63
64    /// Gets the network status. This is the newer endpoint that clients should use.
65    async fn get_net_status(&self) -> Result<JsonSigned<NetStatus>, GenericError>;
66
67    async fn get_routes(
68        &self,
69        token: ClientToken,
70        sig: UnblindedSignature,
71        exit_b2e: SocketAddr,
72    ) -> Result<RouteDescriptor, GenericError>;
73    async fn get_routes_v2(&self, args: GetRoutesArgs) -> Result<RouteDescriptor, GenericError>;
74
75    async fn insert_exit(
76        &self,
77        descriptor: Mac<StdcodeSigned<ExitDescriptor>>,
78    ) -> Result<(), GenericError>;
79
80    async fn insert_exit_v2(
81        &self,
82        descriptor: Mac<JsonSigned<(ExitDescriptor, ExitMetadata)>>,
83    ) -> Result<(), GenericError>;
84
85    async fn insert_bridge(&self, descriptor: Mac<BridgeDescriptor>) -> Result<(), GenericError>;
86
87    async fn incr_stat(&self, stat: String, value: i32);
88
89    async fn set_stat(&self, stat: String, value: f64);
90
91    async fn upload_available(&self, data: AvailabilityData);
92
93    async fn get_puzzle(&self) -> (String, u16);
94
95    async fn register_user_secret(
96        &self,
97        puzzle: String,
98        solution: String,
99    ) -> Result<String, GenericError>;
100
101    async fn upgrade_to_secret(&self, cred: Credential) -> Result<String, AuthError>;
102
103    async fn delete_account(&self, secret: String) -> Result<(), GenericError>;
104
105    async fn get_news(&self, lang: String) -> Result<Vec<LegacyNewsItem>, GenericError>;
106
107    async fn raw_price_points(&self) -> Result<Vec<(u32, u32)>, GenericError>;
108    async fn basic_price_points(&self) -> Result<Vec<(u32, u32)>, GenericError>;
109    async fn basic_mb_limit(&self) -> u32;
110
111    async fn payment_methods(&self) -> Result<Vec<String>, GenericError>;
112
113    async fn create_payment(
114        &self,
115        auth_token: String,
116        days: u32,
117        method: String,
118    ) -> Result<String, GenericError>;
119
120    async fn create_basic_payment(
121        &self,
122        auth_token: String,
123        days: u32,
124        method: String,
125    ) -> Result<String, GenericError>;
126
127    async fn get_free_voucher(&self, secret: String) -> Result<Option<VoucherInfo>, GenericError>;
128
129    // Redeem a voucher/gift card code to add credit to the user's account
130    async fn redeem_voucher(&self, secret: String, code: String) -> Result<i32, GenericError>;
131
132    // Upload debug information for troubleshooting purposes
133    async fn upload_debug_pack(
134        &self,
135        email: Option<String>,
136        logs: String,
137    ) -> Result<(), GenericError>;
138}
139
140#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
141pub struct VoucherInfo {
142    pub code: String,
143    pub explanation: BTreeMap<String, String>,
144}
145
146#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
147pub struct GetRoutesArgs {
148    pub token: ClientToken,
149    pub sig: UnblindedSignature,
150    pub exit_b2e: SocketAddr,
151    pub client_metadata: serde_json::Value,
152}
153
154#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
155pub struct AvailabilityData {
156    pub listen: String,
157    pub country: String,
158    pub asn: String,
159    pub success: bool,
160}
161
162#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
163pub struct UserInfo {
164    pub user_id: u64,
165    pub plus_expires_unix: Option<u64>,
166    #[serde(default)]
167    pub recurring: bool,
168    #[serde(default)]
169    pub bw_consumption: Option<BwConsumptionInfo>,
170}
171
172#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
173pub struct BwConsumptionInfo {
174    pub mb_used: u32,
175    pub mb_limit: u32,
176    pub renew_unix: u64,
177}
178
179#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
180pub enum AccountLevel {
181    Free,
182    Plus,
183}
184
185#[derive(Clone, Debug, Error, Serialize, Deserialize)]
186#[serde(rename_all = "snake_case")]
187pub enum AuthError {
188    #[error("rate limited")]
189    RateLimited,
190    #[error("incorrect credentials")]
191    Forbidden,
192    #[error("wrong level")]
193    WrongLevel,
194}
195
196#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
197#[serde(rename_all = "snake_case")]
198#[derive(Default)]
199pub enum Credential {
200    #[default]
201    TestDummy,
202    LegacyUsernamePassword {
203        username: String,
204        password: String,
205    },
206    Secret(String),
207}
208
209pub const DOMAIN_EXIT_DESCRIPTOR: &str = "exit-descriptor";
210
211pub const DOMAIN_NET_STATUS: &str = "net-status";
212
213#[derive(Serialize, Deserialize, Clone, Debug)]
214#[serde(transparent)]
215pub struct GenericError(pub String);
216
217impl Display for GenericError {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        self.0.fmt(f)
220    }
221}
222
223impl<T: Into<anyhow::Error>> From<T> for GenericError {
224    fn from(value: T) -> Self {
225        Self(value.into().to_string())
226    }
227}
228
229#[derive(Serialize, Deserialize, Clone, Debug)]
230pub struct LegacyNewsItem {
231    pub title: String,
232    pub date_unix: u64,
233    pub contents: String,
234}