Skip to content

Commit 6282ffb

Browse files
committed
Announce allowed purposes in gateway
This change signals to relays (see payjoin/ohttp-relay#58) that even if the gateway is not known to the relay, requests are still allowed for the purposes of BIP 77. Because receivers choose the directory (which is its own gateway), but senders choose the relay, before introducing this functionality it this would have been the responsibility of the sender (or more realistically their wallet vendor) to choose an appropriate relay, one that accepts requests on behalf of the receiver's chosen directory. Rationale for adding a UUID: although arguably 64 bits of randomness is more than enough, and even just the string "BIP 77" is likely to be unique and unambiguous, adding a UUID practically ensures an opt-in is really an opt-in as 128 random bits would not be repeated except intentionally. Rationale for format: we just need a set of opaque identifiers, encoding the same way as TLS ALPN does makes sense since that's well specified, binary safe, easy to parse and places readonable limits on string lengths.
1 parent 8649acf commit 6282ffb

1 file changed

Lines changed: 29 additions & 1 deletion

File tree

payjoin-directory/src/lib.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ async fn serve_payjoin_directory(
187187
let mut response = match (parts.method, path_segments.as_slice()) {
188188
(Method::POST, ["", ".well-known", "ohttp-gateway"]) =>
189189
handle_ohttp_gateway(body, pool, ohttp).await,
190-
(Method::GET, ["", ".well-known", "ohttp-gateway"]) => get_ohttp_keys(&ohttp).await,
190+
(Method::GET, ["", ".well-known", "ohttp-gateway"]) =>
191+
handle_ohttp_gateway_get(&ohttp, &query).await,
191192
(Method::POST, ["", ""]) => handle_ohttp_gateway(body, pool, ohttp).await,
192193
(Method::GET, ["", "ohttp-keys"]) => get_ohttp_keys(&ohttp).await,
193194
(Method::POST, ["", id]) => post_fallback_v1(id, query, body, pool).await,
@@ -426,6 +427,16 @@ fn not_found() -> Response<BoxBody<Bytes, hyper::Error>> {
426427
res
427428
}
428429

430+
async fn handle_ohttp_gateway_get(
431+
ohttp: &Arc<Mutex<ohttp::Server>>,
432+
query: &str,
433+
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, HandlerError> {
434+
match query {
435+
"allowed_purposes" => Ok(get_ohttp_allowed_purposes().await),
436+
_ => get_ohttp_keys(ohttp).await,
437+
}
438+
}
439+
429440
async fn get_ohttp_keys(
430441
ohttp: &Arc<Mutex<ohttp::Server>>,
431442
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, HandlerError> {
@@ -440,6 +451,23 @@ async fn get_ohttp_keys(
440451
Ok(res)
441452
}
442453

454+
async fn get_ohttp_allowed_purposes() -> Response<BoxBody<Bytes, hyper::Error>> {
455+
// Encode the magic string in the same format as a TLS ALPN protocol list (a
456+
// U16BE length encoded list of U8 length encoded strings).
457+
//
458+
// The string is just "BIP77" followed by a UUID, that signals to relays
459+
// that this OHTTP gateway will accept any requests associated with this
460+
// purpose.
461+
let mut res = Response::new(full(Bytes::from_static(
462+
b"\x00\x01\x2aBIP77 454403bb-9f7b-4385-b31f-acd2dae20b7e",
463+
)));
464+
465+
res.headers_mut()
466+
.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-ohttp-allowed-purposes"));
467+
468+
res
469+
}
470+
443471
fn empty() -> BoxBody<Bytes, hyper::Error> {
444472
Empty::<Bytes>::new().map_err(|never| match never {}).boxed()
445473
}

0 commit comments

Comments
 (0)