diff --git a/src/add_bond_invoice.md b/src/add_bond_invoice.md index c0f25f5..184f6df 100644 --- a/src/add_bond_invoice.md +++ b/src/add_bond_invoice.md @@ -16,7 +16,7 @@ Mostro sends a single `add-bond-invoice` message to the non-slashed counterparty [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-bond-invoice", "payload": { @@ -35,6 +35,7 @@ Mostro sends a single `add-bond-invoice` message to the non-slashed counterparty } } }, + null, null ] ``` @@ -58,13 +59,13 @@ deadline = slashed_at + bond_payout_claim_window_days * 86_400 ## Counterparty → Mostro (reply) -The counterparty replies with a Gift wrap Nostr event whose rumor content carries the bolt11 inside the standard `payment_request` array. The invoice carries its own amount, so the array has two elements (per [Payment Request Array Structure](./overview.md#payment-request-array-structure)): +The counterparty replies with a message whose content carries the bolt11 inside the standard `payment_request` array. The invoice carries its own amount, so the array has two elements (per [Payment Request Array Structure](./overview.md#payment-request-array-structure)): ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-bond-invoice", "payload": { @@ -75,7 +76,8 @@ The counterparty replies with a Gift wrap Nostr event whose rumor content carrie } } }, - "" + "", + ["", ""] ] ``` @@ -111,7 +113,7 @@ Sent by Mostro to the counterparty immediately after successfully receiving and [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "bond-invoice-accepted", "payload": { @@ -127,6 +129,7 @@ Sent by Mostro to the counterparty immediately after successfully receiving and } } }, + null, null ] ``` @@ -141,7 +144,7 @@ Sent by Mostro to the counterparty when the Lightning payment to their invoice h [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "bond-payout-completed", "payload": { @@ -157,6 +160,7 @@ Sent by Mostro to the counterparty when the Lightning payment to their invoice h } } }, + null, null ] ``` diff --git a/src/admin_add_solver.md b/src/admin_add_solver.md index ed24d64..1677591 100644 --- a/src/admin_add_solver.md +++ b/src/admin_add_solver.md @@ -32,13 +32,14 @@ The default remains `read-write` for backward compatibility. [ { "order": { - "version": 1, + "version": 2, "action": "admin-add-solver", "payload": { "text_message": "npub1qqq884wtp2jn96lqhqlnarl4kk3rmvrc9z2nmrvqujx3m4l2ea5qd5d0fq" } } }, + null, null ] ``` @@ -49,13 +50,14 @@ The default remains `read-write` for backward compatibility. [ { "order": { - "version": 1, + "version": 2, "action": "admin-add-solver", "payload": { "text_message": "npub1qqq884wtp2jn96lqhqlnarl4kk3rmvrc9z2nmrvqujx3m4l2ea5qd5d0fq:read" } } }, + null, null ] ``` @@ -68,11 +70,12 @@ Mostro sends this message to the admin: [ { "order": { - "version": 1, + "version": 2, "action": "admin-add-solver", "payload": null } }, + null, null ] ``` diff --git a/src/admin_cancel_order.md b/src/admin_cancel_order.md index 51714f0..ea3e0b5 100644 --- a/src/admin_cancel_order.md +++ b/src/admin_cancel_order.md @@ -6,12 +6,13 @@ An admin can cancel an order, most of the time this is done when admin is solvin [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-cancel", "payload": null } }, + null, null ] ``` @@ -24,7 +25,7 @@ When solving a dispute, the admin can optionally slash one or both parties' bond [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-cancel", "payload": { @@ -35,6 +36,7 @@ When solving a dispute, the admin can optionally slash one or both parties' bond } } }, + null, null ] ``` @@ -61,12 +63,13 @@ Mostro will send this message to the both parties buyer/seller and to the admin: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-canceled", "payload": null } }, + null, null ] ``` diff --git a/src/admin_settle_order.md b/src/admin_settle_order.md index 3801c74..96d41e0 100644 --- a/src/admin_settle_order.md +++ b/src/admin_settle_order.md @@ -6,12 +6,13 @@ An admin can settle an order, most of the time this is done when admin is solvin [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-settle", "payload": null } }, + null, null ] ``` @@ -24,7 +25,7 @@ When solving a dispute, the admin can optionally slash one or both parties' bond [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-settle", "payload": { @@ -35,6 +36,7 @@ When solving a dispute, the admin can optionally slash one or both parties' bond } } }, + null, null ] ``` @@ -61,12 +63,13 @@ Mostro will send this message to the both parties buyer/seller and to the admin: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-settled", "payload": null } }, + null, null ] ``` diff --git a/src/bond_slashed.md b/src/bond_slashed.md index 54f2e25..83690a9 100644 --- a/src/bond_slashed.md +++ b/src/bond_slashed.md @@ -16,7 +16,7 @@ The `bond-slashed` action is a notification Mostro sends to a bonded party when [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "bond-slashed", "payload": { @@ -34,6 +34,7 @@ The `bond-slashed` action is a notification Mostro sends to a bonded party when } } }, + null, null ] ``` diff --git a/src/cancel.md b/src/cancel.md index c039915..ae29bdd 100644 --- a/src/cancel.md +++ b/src/cancel.md @@ -1,35 +1,37 @@ # Cancel Order -A user can cancel an order created by himself and with status `pending` sending action `cancel`, the rumor's content of the message will look like this: +A user can cancel an order created by himself and with status `pending` sending action `cancel`, the decrypted content of the message will look like this: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cancel", "payload": null } }, - "" + "", + ["", ""] ] ``` ## Mostro response -Mostro will send a message with action `cancel` confirming the order was canceled, here an example of rumor's content of the message: +Mostro will send a message with action `cancel` confirming the order was canceled, here an example of decrypted content of the message: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "canceled", "payload": null } }, + null, null ] ``` @@ -97,12 +99,13 @@ A user can cancel an `active` order, but will need the counterparty to agree, le [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cancel", "payload": null } }, + null, null ] ``` @@ -113,12 +116,13 @@ Mostro will send this message to the seller: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cooperative-cancel-initiated-by-you", "payload": null } }, + null, null ] ``` @@ -129,12 +133,13 @@ And this message to the buyer: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cooperative-cancel-initiated-by-peer", "payload": null } }, + null, null ] ``` @@ -145,12 +150,13 @@ The buyer can accept the cooperative cancellation sending this message: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cancel", "payload": null } }, + null, null ] ``` @@ -161,12 +167,13 @@ And Mostro will send this message to both parties: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "cooperative-cancel-accepted", "payload": null } }, + null, null ] ``` diff --git a/src/chat.md b/src/chat.md index dd76df0..7ffd2d3 100644 --- a/src/chat.md +++ b/src/chat.md @@ -2,6 +2,12 @@ To communicate directly, both the buyer and the seller do not use the current `Message` scheme explained [here](https://mostro.network/protocol/overview.html), as this communication excludes the Mostro daemon. To preserve user privacy, we use a simplified version of NIP-59 that allows us to hide the metadata of both parties from outside observers. However, this variant only contains a single event inside the wrapper. The inner event includes the sender’s trade pubkey and the corresponding signature to maintain the authenticity of the sender. +> **Scope note:** the [v1 → v2 transport migration](./transport_migration.md) +> applies only to messages exchanged **with the Mostro daemon**. This +> peer-to-peer chat never goes through the daemon, so it keeps the +> kind-`1059` scheme described below and is **not** affected by the +> v0.19.0 removal of the v1 transport. + ## Shared Key The messages between parties have a unique feature: instead of directing the events containing these messages to the counterparty’s trade pubkey, we direct them to a unique pubkey known only to both parties. diff --git a/src/dispute.md b/src/dispute.md index 68ea5f3..c673245 100644 --- a/src/dispute.md +++ b/src/dispute.md @@ -6,13 +6,14 @@ A user can start a dispute in an order with status `active` or `fiat-sent` sendi [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "dispute", "payload": null } }, - "" + "", + ["", ""] ] ``` @@ -24,7 +25,7 @@ Mostro will send this message to the seller: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "dispute-initiated-by-you", "payload": { @@ -32,6 +33,7 @@ Mostro will send this message to the seller: } } }, + null, null ] ``` @@ -42,7 +44,7 @@ And here is the message to the buyer: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "dispute-initiated-by-peer", "payload": { @@ -50,6 +52,7 @@ And here is the message to the buyer: } } }, + null, null ] ``` @@ -88,12 +91,13 @@ Mostro admin will see the dispute and can take it using the dispute `Id` from `d [ { "dispute": { - "version": 1, + "version": 2, "id": "", "action": "admin-take-dispute", "payload": null } }, + null, null ] ``` @@ -104,7 +108,7 @@ Mostro will send a confirmation message to the admin with the order details: [ { "dispute": { - "version": 1, + "version": 2, "id": "", "action": "admin-took-dispute", "payload": { @@ -125,6 +129,7 @@ Mostro will send a confirmation message to the admin with the order details: } } }, + null, null ] ``` @@ -135,7 +140,7 @@ Then mostrod send messages to each trade participant, the buyer and seller for t [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-took-dispute", "payload": { @@ -145,6 +150,7 @@ Then mostrod send messages to each trade participant, the buyer and seller for t } } }, + null, null ] ``` diff --git a/src/dispute_chat.md b/src/dispute_chat.md index 95423e7..c7b3c2b 100644 --- a/src/dispute_chat.md +++ b/src/dispute_chat.md @@ -2,6 +2,12 @@ The dispute chat uses the same shared key encryption scheme as the [Peer-to-peer Chat](./chat.md). Instead of computing a shared key between buyer and seller, each party computes an independent shared key with the admin who took the dispute. +> **Scope note:** like the peer-to-peer chat, the dispute chat events +> below keep the kind-`1059` scheme and are **not** affected by the +> [v1 → v2 transport migration](./transport_migration.md) — only the +> `admin-took-dispute` message from Mostro (next section) travels over +> the daemon transport. + ## Establishing the shared key When an admin takes a dispute, Mostro sends an `admin-took-dispute` message to each party (buyer and seller) containing the admin's pubkey: @@ -10,7 +16,7 @@ When an admin takes a dispute, Mostro sends an `admin-took-dispute` message to e [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "admin-took-dispute", "payload": { @@ -20,6 +26,7 @@ When an admin takes a dispute, Mostro sends an `admin-took-dispute` message to e } } }, + null, null ] ``` diff --git a/src/fiatsent.md b/src/fiatsent.md index 27d7f80..dd27df2 100644 --- a/src/fiatsent.md +++ b/src/fiatsent.md @@ -1,11 +1,11 @@ # Fiat sent -After the buyer sends the fiat money to the seller, the buyer should send a message in a Gift wrap Nostr event to Mostro indicating that the fiat money was sent, message in the first element of the rumor's content would look like this: +After the buyer sends the fiat money to the seller, the buyer should send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) indicating that the fiat money was sent, message in the first element of the decrypted content would look like this: ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "fiat-sent", "payload": null @@ -20,7 +20,7 @@ In most of the cases after complete a range order, a child order needs to be cre ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "fiat-sent", "payload": { @@ -37,7 +37,7 @@ Mostro send messages to both parties confirming `fiat-sent` action and sending a ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "fiat-sent-ok", "payload": { @@ -54,7 +54,7 @@ And here an example of the message from Mostro to the seller: ```json { "order": { - "version": 1, + "version": 2, "id": "", "pubkey": "", "action": "fiat-sent-ok", diff --git a/src/last_trade_index.md b/src/last_trade_index.md index 9bf87d6..55ddbd0 100644 --- a/src/last_trade_index.md +++ b/src/last_trade_index.md @@ -4,17 +4,18 @@ Defines the `last-trade-index` action used to retrieve the user's last `trade_in ## Request -Client sends a Gift wrap Nostr event to Mostro with the following rumor's content. The request sends a `null` payload to indicate that the client is querying for the last trade index. +Client sends a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content. The request sends a `null` payload to indicate that the client is querying for the last trade index. ```json [ { "restore": { - "version": 1, + "version": 2, "action": "last-trade-index", "payload": null } }, + null, null ] ``` @@ -26,7 +27,7 @@ Mostro responds with the user's last trade index as a u32 directly in the `trade ```json { "restore": { - "version": 1, + "version": 2, "action": "last-trade-index", "trade_index": 42, "payload": null @@ -48,7 +49,7 @@ Client requests the last trade index and receives `7`, meaning the next trade th ```json { "restore": { - "version": 1, + "version": 2, "action": "last-trade-index", "trade_index": 7, "payload": null diff --git a/src/new_buy_order.md b/src/new_buy_order.md index 0633c78..fef1cc8 100644 --- a/src/new_buy_order.md +++ b/src/new_buy_order.md @@ -1,11 +1,11 @@ # Creating a new buy order -To create a new buy order the user should send a Gift wrap Nostr event to Mostro with the following message: +To create a new buy order the user should send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following message: ```json { "order": { - "version": 1, + "version": 2, "action": "new-order", "trade_index": 1, "payload": { @@ -29,15 +29,23 @@ The nostr event will look like this: ```json { "id": "", - "kind": 1059, - "pubkey": "", - "content": "", - "tags": [["p", "Mostro's pubkey"]], + "kind": 14, + "pubkey": "", + "content": "", + "tags": [ + ["p", ""], + ["expiration", ""] + ], "created_at": 1234567890, - "sig": "" + "sig": "" } ``` +On the deprecated v1 transport the same content array travels inside a +[NIP-59 gift wrap](https://github.com/nostr-protocol/nips/blob/master/59.md) +(kind `1059`) instead — see [Keys management](./key_management.md) for the +v1 envelope. + ## Optional: anti-abuse maker bond When the Mostro node has bonds enabled and `apply_to` is `"make"` or `"both"`, the maker must lock a bond **before** the order is published. Instead of a `new-order` confirmation, Mostro first responds with a [`pay-bond-invoice`](./pay_bond_invoice.md#maker-bond) message asking the maker to pay a small hold invoice (typically ~1% of the trade amount). The order is **not visible on Nostr** until the bond HTLC is accepted. @@ -50,13 +58,13 @@ If the maker never pays the bond invoice it expires and no order is created. See ## Confirmation message -Mostro will send back a nip59 event as a confirmation message, the message in the rumor looks like the following: +Mostro will send back a confirmation message, the decrypted content looks like the following: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "new-order", "payload": { @@ -77,6 +85,7 @@ Mostro will send back a nip59 event as a confirmation message, the message in th } } }, + null, null ] ``` diff --git a/src/new_buy_order_ln_address.md b/src/new_buy_order_ln_address.md index 6279240..c09cd2c 100644 --- a/src/new_buy_order_ln_address.md +++ b/src/new_buy_order_ln_address.md @@ -1,12 +1,12 @@ # Creating a new order -Creating buy order with a [lightning address](https://github.com/andrerfneves/lightning-address) would make the process way faster and easy going, to acomplish the buyer should send a Gift wrap Nostr event to Mostro with the following rumor's content: +Creating buy order with a [lightning address](https://github.com/andrerfneves/lightning-address) would make the process way faster and easy going, to acomplish the buyer should send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "action": "new-order", "trade_index": 1, "payload": { @@ -24,7 +24,8 @@ Creating buy order with a [lightning address](https://github.com/andrerfneves/li } } }, - "" + "", + ["", ""] ] ``` @@ -33,24 +34,32 @@ The nostr event will look like this: ```json { "id": "", - "kind": 1059, - "pubkey": "", - "content": "", - "tags": [["p", "Mostro's pubkey"]], + "kind": 14, + "pubkey": "", + "content": "", + "tags": [ + ["p", ""], + ["expiration", ""] + ], "created_at": 1234567890, - "sig": "" + "sig": "" } ``` +On the deprecated v1 transport the same content array travels inside a +[NIP-59 gift wrap](https://github.com/nostr-protocol/nips/blob/master/59.md) +(kind `1059`) instead — see [Keys management](./key_management.md) for the +v1 envelope. + ## Confirmation message -Mostro will send back a nip59 event as a confirmation message to the user like the following: +Mostro will send back a confirmation message to the user like the following: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "new-order", "payload": { @@ -71,6 +80,7 @@ Mostro will send back a nip59 event as a confirmation message to the user like t } } }, + null, null ] ``` diff --git a/src/new_sell_order.md b/src/new_sell_order.md index cf89341..20ef2dd 100644 --- a/src/new_sell_order.md +++ b/src/new_sell_order.md @@ -1,11 +1,11 @@ # Creating a new sell order -To create a new sell order the user should send a Gift wrap Nostr event to Mostro, the message should look like this: +To create a new sell order the user should send a message to Mostro (wrapped in the [active transport](./overview.md#transports)), the message should look like this: ```json { "order": { - "version": 1, + "version": 2, "action": "new-order", "trade_index": 1, "payload": { @@ -38,15 +38,23 @@ The event to send to Mostro would look like this: ```json { "id": "", - "kind": 1059, - "pubkey": "", - "content": "", - "tags": [["p", "Mostro's pubkey"]], + "kind": 14, + "pubkey": "", + "content": "", + "tags": [ + ["p", ""], + ["expiration", ""] + ], "created_at": 1234567890, - "sig": "" + "sig": "" } ``` +On the deprecated v1 transport the same content array travels inside a +[NIP-59 gift wrap](https://github.com/nostr-protocol/nips/blob/master/59.md) +(kind `1059`) instead — see [Keys management](./key_management.md) for the +v1 envelope. + ## Optional: anti-abuse maker bond When the Mostro node has bonds enabled and `apply_to` is `"make"` or `"both"`, the maker must lock a bond **before** the order is published. Instead of a `new-order` confirmation, Mostro first responds with a [`pay-bond-invoice`](./pay_bond_invoice.md#maker-bond) message asking the maker to pay a small hold invoice (typically ~1% of the trade amount). The order is **not visible on Nostr** until the bond HTLC is accepted. @@ -59,13 +67,13 @@ If the maker never pays the bond invoice it expires and no order is created. See ## Confirmation message -Mostro will send back a nip59 event as a confirmation message to the user like the following (unencrypted rumor's content example): +Mostro will send back a confirmation message to the user like the following (unencrypted decrypted content example): ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "new-order", "payload": { @@ -83,6 +91,7 @@ Mostro will send back a nip59 event as a confirmation message to the user like t } } }, + null, null ] ``` diff --git a/src/new_sell_range_order.md b/src/new_sell_range_order.md index c221718..4b7b1c7 100644 --- a/src/new_sell_range_order.md +++ b/src/new_sell_range_order.md @@ -1,11 +1,11 @@ # Creating a new sell range order -To create a new range order the user should send a Gift wrap Nostr event to Mostro with the following message: +To create a new range order the user should send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following message: ```json { "order": { - "version": 1, + "version": 2, "action": "new-order", "trade_index": 1, "payload": { @@ -40,13 +40,13 @@ When a taker takes a slice of the range order, the bond obligation is reduced pr ## Confirmation message -Mostro will send back a nip59 event as a confirmation message to the user like the following: +Mostro will send back a confirmation message to the user like the following: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "new-order", "payload": { @@ -66,6 +66,7 @@ Mostro will send back a nip59 event as a confirmation message to the user like t } } }, + null, null ] ``` diff --git a/src/orders.md b/src/orders.md index 8188921..ece7c6b 100644 --- a/src/orders.md +++ b/src/orders.md @@ -1,6 +1,6 @@ # Request order details -Clients can request detailed information for existing orders by sending a nip59 Gift wrap message with the action `orders`. This is useful for refreshing stale UI state or restoring a session from the mnemonic seed on a new device. +Clients can request detailed information for existing orders by sending a message with the action `orders` (wrapped in the [active transport](./overview.md#transports)). This is useful for refreshing stale UI state or restoring a session from the mnemonic seed on a new device. ## Request message @@ -10,7 +10,7 @@ The client sends a message where the payload object includes an `ids` array of o [ { "order": { - "version": 1, + "version": 2, "request_id": 8721, "action": "orders", "payload": { @@ -21,6 +21,7 @@ The client sends a message where the payload object includes an `ids` array of o } } }, + null, null ] ``` @@ -38,7 +39,7 @@ Mostro replies with the same action and includes a structured payload describing [ { "order": { - "version": 1, + "version": 2, "request_id": 8721, "action": "orders", "payload": { @@ -79,6 +80,7 @@ Mostro replies with the same action and includes a structured payload describing } } }, + null, null ] ``` diff --git a/src/other_events.md b/src/other_events.md index 5f33bf9..56536d1 100644 --- a/src/other_events.md +++ b/src/other_events.md @@ -119,7 +119,7 @@ This event contains specific data about a Mostro instance. The instance is ident ], [ "protocol_version", - "1" + "2" ], [ "hold_invoice_expiration_window", @@ -217,7 +217,7 @@ Below is an explanation of the meaning of some of the labels in this event, all - `fiat_currencies_accepted`: Fiat currencies accepted by the Mostro. If no currency is specified, all are accepted. - `max_orders_per_response`: Maximum complete orders data per response in orders action. - `fee`: The fee percentage charged by the instance. For example, "0.006" means a 0.6% fee. -- `pow`: The Proof of Work required of incoming events. +- `pow`: The Proof of Work required of incoming events. On protocol v2 this is the **base** difficulty: a node may require a higher, unadvertised difficulty from trade keys it has never seen before — see [anti-spam gates](./transport_migration.md#anti-spam-gates-on-v2). - `protocol_version`: The Mostro protocol (wire transport) this node speaks — `"1"` for NIP-59 gift wrap (kind `1059`, DEPRECATED) or `"2"` for NIP-44 direct messages (kind `14`). A node speaks exactly one; clients read this tag to pick the matching wire format. See the [client migration guide](./transport_migration.md). - `hold_invoice_expiration_window`: The maximum time, in seconds, for the hold invoice issued by Mostro to be paid by the seller. - `hold_invoice_cltv_delta`: The number of blocks in which the Mostro hold invoice will expire. diff --git a/src/overview.md b/src/overview.md index 225bc80..a9f0f4b 100644 --- a/src/overview.md +++ b/src/overview.md @@ -63,7 +63,7 @@ Here an example of a `new-order` order **_message_**: ```json { "order": { - "version": 1, + "version": 2, "id": "", "request_id": 123456, "trade_index": 1, diff --git a/src/pay_bond_invoice.md b/src/pay_bond_invoice.md index 233bb65..b371b13 100644 --- a/src/pay_bond_invoice.md +++ b/src/pay_bond_invoice.md @@ -22,7 +22,7 @@ The message's content has the same shape as `pay-invoice`; only the action discr [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "pay-bond-invoice", "payload": { @@ -43,6 +43,7 @@ The message's content has the same shape as `pay-invoice`; only the action discr } } }, + null, null ] ``` @@ -100,7 +101,7 @@ The action and wire shape are identical to the taker case: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "pay-bond-invoice", "payload": { @@ -121,6 +122,7 @@ The action and wire shape are identical to the taker case: } } }, + null, null ] ``` diff --git a/src/payment_failed.md b/src/payment_failed.md index d8d0035..2aa29bc 100644 --- a/src/payment_failed.md +++ b/src/payment_failed.md @@ -12,7 +12,7 @@ When Mostro cannot pay the buyer's Lightning invoice, it sends this message to t [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "payment-failed", "payload": { @@ -23,6 +23,7 @@ When Mostro cannot pay the buyer's Lightning invoice, it sends this message to t } } }, + null, null ] ``` @@ -46,7 +47,7 @@ When Mostro cannot pay the buyer's Lightning invoice, it sends this message to t [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -58,6 +59,7 @@ When Mostro cannot pay the buyer's Lightning invoice, it sends this message to t } } }, + null, null ] ``` diff --git a/src/release.md b/src/release.md index a299743..fc2c802 100644 --- a/src/release.md +++ b/src/release.md @@ -1,18 +1,19 @@ # Release -After confirming the buyer sent the fiat money, the seller should send a message to Mostro indicating that sats should be delivered to the buyer, the message inside rumor's content will look like this: +After confirming the buyer sent the fiat money, the seller should send a message to Mostro indicating that sats should be delivered to the buyer, the message content will look like this: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "request_id": "123456", "action": "release", "payload": null } }, + null, null ] ``` @@ -25,13 +26,14 @@ Here an example of the Mostro response to the seller: [ { "order": { - "version": 1, + "version": 2, "id": "", "request_id": "123456", "action": "hold-invoice-payment-settled", "payload": null } }, + null, null ] ``` @@ -42,12 +44,13 @@ And a message to the buyer to let him know that the sats were released: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "released", "payload": null } }, + null, null ] ``` @@ -62,12 +65,13 @@ Right after seller releases sats, Mostro will attempt to pay the buyer's Lightni [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "purchase-completed", "payload": null } }, + null, null ] ``` @@ -112,7 +116,7 @@ If the order is a range order probably after release a child order would need to [ { "order": { - "version": 1, + "version": 2, "id": "4fd93fc9-e909-4fc9-acef-9976122b5dfa", "action": "release", "payload": { @@ -120,6 +124,7 @@ If the order is a range order probably after release a child order would need to } } }, + null, null ] ``` @@ -130,7 +135,7 @@ Mostro will send to the maker the newly child order created with the same `trade [ { "order": { - "version": 1, + "version": 2, "id": "4fd93fc9-e909-4fc9-acef-9976122b5dfa", "action": "new-order", "trade_index": , @@ -153,6 +158,7 @@ Mostro will send to the maker the newly child order created with the same `trade } } }, + null, null ] ``` diff --git a/src/restore_session.md b/src/restore_session.md index 2e98be6..a85d07f 100644 --- a/src/restore_session.md +++ b/src/restore_session.md @@ -4,17 +4,18 @@ To restore a session from the mnemonic seed on a new device (e.g., moving from m ## Request -Client sends a Gift wrap Nostr event to Mostro with the following rumor's content: +Client sends a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "restore": { - "version": 1, + "version": 2, "action": "restore-session", "payload": null } }, + null, null ] ``` @@ -27,7 +28,7 @@ Mostro will respond with a message containing all non-finalized orders (e.g., st [ { "restore": { - "version": 1, + "version": 2, "action": "restore-session", "payload": { "restore_data": { @@ -61,6 +62,7 @@ Mostro will respond with a message containing all non-finalized orders (e.g., st } } }, + null, null ] ``` @@ -85,7 +87,7 @@ When switching to desktop, after restoring the mnemonic, the client sends `resto [ { "restore": { - "version": 1, + "version": 2, "action": "restore-session", "payload": { "restore_data": { @@ -102,6 +104,7 @@ When switching to desktop, after restoring the mnemonic, the client sends `resto } } }, + null, null ] ``` diff --git a/src/seller_pay_hold_invoice.md b/src/seller_pay_hold_invoice.md index 4930996..14d7efd 100644 --- a/src/seller_pay_hold_invoice.md +++ b/src/seller_pay_hold_invoice.md @@ -1,12 +1,12 @@ # Seller pays hold invoice -When the seller is the maker and the order was taken by a buyer, Mostro will send to the seller a message asking to pay the hold invoice, the rumor's content of the message will look like this: +When the seller is the maker and the order was taken by a buyer, Mostro will send to the seller a message asking to pay the hold invoice, the decrypted content of the message will look like this: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "pay-invoice", "payload": { @@ -27,17 +27,18 @@ When the seller is the maker and the order was taken by a buyer, Mostro will sen } } }, - "" + "", + ["", ""] ] ``` -After the hold invoice is paid and the buyer already sent the invoice to receive the sats, Mostro will send a new message to seller with the following rumor's content: +After the hold invoice is paid and the buyer already sent the invoice to receive the sats, Mostro will send a new message to seller with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "buyer-took-order", "payload": { @@ -58,7 +59,8 @@ After the hold invoice is paid and the buyer already sent the invoice to receive } } }, - "" + "", + ["", ""] ] ``` @@ -68,7 +70,7 @@ Mostro also send a message to the buyer, this way they can both write to each ot [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "hold-invoice-payment-accepted", "payload": { @@ -89,6 +91,7 @@ Mostro also send a message to the buyer, this way they can both write to each ot } } }, + null, null ] ``` @@ -100,7 +103,7 @@ Mostro send this message to the seller: ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "waiting-buyer-invoice", "payload": null @@ -113,7 +116,7 @@ And this message to the buyer: ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -138,7 +141,7 @@ Now buyer sends the invoice to Mostro: ```json { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { diff --git a/src/take_buy.md b/src/take_buy.md index ac829fc..587d85b 100644 --- a/src/take_buy.md +++ b/src/take_buy.md @@ -1,19 +1,20 @@ # Taking a buy order -To take an order the seller will send to Mostro a message with the following rumor's content: +To take an order the seller will send to Mostro a message with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-buy", "trade_index": 1, "payload": null } }, - "" + "", + ["", ""] ] ``` @@ -22,15 +23,23 @@ The event to send to Mostro would look like this: ```json { "id": "", - "kind": 1059, - "pubkey": "", - "content": "", - "tags": [["p", "Mostro's pubkey"]], + "kind": 14, + "pubkey": "", + "content": "", + "tags": [ + ["p", ""], + ["expiration", ""] + ], "created_at": 1234567890, - "sig": "" + "sig": "" } ``` +On the deprecated v1 transport the same content array travels inside a +[NIP-59 gift wrap](https://github.com/nostr-protocol/nips/blob/master/59.md) +(kind `1059`) instead — see [Keys management](./key_management.md) for the +v1 envelope. + ## Optional: anti-abuse bond step When the receiving Mostro node has bonds enabled, the seller (taker) first receives a [`pay-bond-invoice`](./pay_bond_invoice.md) message; the published NIP-33 order event remains `pending` (re-takeability is preserved — see [order event](./order_event.md)) and the bond message's embedded `SmallOrder` carries `status: pending` (the daemon tracks the internal `waiting-taker-bond` state in its database only — it is never emitted on the wire). Only after the bond HTLC is `Accepted` does Mostro send the `pay-invoice` message described below. @@ -45,7 +54,7 @@ Mostro respond to the seller with a message with the following content: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "pay-invoice", "payload": { @@ -66,6 +75,7 @@ Mostro respond to the seller with a message with the following content: } } }, + null, null ] ``` @@ -108,12 +118,13 @@ And send a message to the buyer with the following content: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "waiting-seller-to-pay", "payload": null } }, + null, null ] ``` @@ -126,12 +137,13 @@ After seller pays the hold invoice Mostro send a message to the seller with the [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "waiting-buyer-invoice", "payload": null } }, + null, null ] ``` @@ -141,7 +153,7 @@ Mostro sends a message to the buyer with the following content: [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -158,6 +170,7 @@ Mostro sends a message to the buyer with the following content: } } }, + null, null ] ``` @@ -170,7 +183,7 @@ Buyer sends the LN invoice to Mostro. [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -181,6 +194,7 @@ Buyer sends the LN invoice to Mostro. } } }, + null, null ] ``` diff --git a/src/take_buy_range_order.md b/src/take_buy_range_order.md index c363e6f..f6ec5d9 100644 --- a/src/take_buy_range_order.md +++ b/src/take_buy_range_order.md @@ -1,12 +1,12 @@ # Taking a buy range order -If the order fiat amount is a range like `10-20` the seller must indicate a fiat amount to take the order, seller will send a message in a Gift wrap Nostr event to Mostro with the following rumor's content: +If the order fiat amount is a range like `10-20` the seller must indicate a fiat amount to take the order, seller will send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-buy", "trade_index": 1, @@ -15,6 +15,7 @@ If the order fiat amount is a range like `10-20` the seller must indicate a fiat } } }, + null, null ] ``` diff --git a/src/take_sell.md b/src/take_sell.md index 9fb5050..720ac2d 100644 --- a/src/take_sell.md +++ b/src/take_sell.md @@ -1,19 +1,20 @@ # Taking a sell order -If the order amount is `0` the buyer doesn't know the exact amount to create the invoice, buyer will send a message in a Gift wrap Nostr event to Mostro with the following rumor's content: +If the order amount is `0` the buyer doesn't know the exact amount to create the invoice, buyer will send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-sell", "trade_index": 1, "payload": null } }, - "" + "", + ["", ""] ] ``` @@ -23,13 +24,13 @@ When the receiving Mostro node has bonds enabled, the buyer (taker) first receiv ## Mostro response -In order to continue the buyer needs to send a lightning network invoice to Mostro, in this case the amount of the order is `0`, so Mostro will need to calculate the amount of sats for this order, then Mostro will send back a message asking for a LN invoice indicating the correct amount of sats that the invoice should have, here the rumor's content of the message: +In order to continue the buyer needs to send a lightning network invoice to Mostro, in this case the amount of the order is `0`, so Mostro will need to calculate the amount of sats for this order, then Mostro will send back a message asking for a LN invoice indicating the correct amount of sats that the invoice should have, here the decrypted content of the message: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -46,6 +47,7 @@ In order to continue the buyer needs to send a lightning network invoice to Most } } }, + null, null ] ``` @@ -84,13 +86,13 @@ Mostro updates the addressable event with `d` tag `` to change the sta ## Buyer sends LN invoice -The buyer sends a Gift wrap Nostr event to Mostro with the lightning invoice, the action should be the same the buyer just received in the last message from Mostro (`add-invoice`), here the rumor's content of the event for an invoice with no amount: +The buyer sends a message to Mostro with the lightning invoice, the action should be the same the buyer just received in the last message from Mostro (`add-invoice`), here the decrypted content of the event for an invoice with no amount: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -102,7 +104,8 @@ The buyer sends a Gift wrap Nostr event to Mostro with the lightning invoice, th } } }, - "" + "", + ["", ""] ] ``` @@ -110,18 +113,19 @@ If the invoice includes an amount, the last element of the `payment_request` arr ## Mostro response -Mostro send a Gift wrap Nostr event to the buyer with a wrapped `order` in the rumor's content, it would look like this: +Mostro sends a message to the buyer with a wrapped `order` in the decrypted content, it would look like this: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "waiting-seller-to-pay", "payload": null } }, + null, null ] ``` diff --git a/src/take_sell_ln_address.md b/src/take_sell_ln_address.md index 622ee6c..f86d3d9 100644 --- a/src/take_sell_ln_address.md +++ b/src/take_sell_ln_address.md @@ -1,12 +1,12 @@ # Taking a sell order with a lightning address -The buyer can use a [lightning address](https://github.com/andrerfneves/lightning-address) to receive funds and avoid to manually create and send lightning invoices on each trade, to acomplish this the buyer will send a message in a Gift wrap Nostr event to Mostro with the following rumor's content: +The buyer can use a [lightning address](https://github.com/andrerfneves/lightning-address) to receive funds and avoid to manually create and send lightning invoices on each trade, to acomplish this the buyer will send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-sell", "trade_index": 1, @@ -15,7 +15,8 @@ The buyer can use a [lightning address](https://github.com/andrerfneves/lightnin } } }, - "" + "", + ["", ""] ] ``` @@ -24,30 +25,39 @@ The event to send to Mostro would look like this: ```json { "id": "", - "kind": 1059, - "pubkey": "", - "content": "", - "tags": [["p", "Mostro's pubkey"]], + "kind": 14, + "pubkey": "", + "content": "", + "tags": [ + ["p", ""], + ["expiration", ""] + ], "created_at": 1234567890, - "sig": "" + "sig": "" } ``` +On the deprecated v1 transport the same content array travels inside a +[NIP-59 gift wrap](https://github.com/nostr-protocol/nips/blob/master/59.md) +(kind `1059`) instead — see [Keys management](./key_management.md) for the +v1 envelope. + ## Mostro response -Mostro send a Gift wrap Nostr event to the buyer with a wrapped `order` in the rumor's content, it would look like this: +Mostro sends a message to the buyer with a wrapped `order` in the decrypted content, it would look like this: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "waiting-seller-to-pay", "payload": null } }, - "" + "", + ["", ""] ] ``` diff --git a/src/take_sell_range_order.md b/src/take_sell_range_order.md index 15f6f02..01b04b4 100644 --- a/src/take_sell_range_order.md +++ b/src/take_sell_range_order.md @@ -1,12 +1,12 @@ # Taking a sell range order -If the order fiat amount is a range like `10-20` the buyer must indicate a fiat amount to take the order, buyer will send a message in a Gift wrap Nostr event to Mostro with the following rumor's content: +If the order fiat amount is a range like `10-20` the buyer must indicate a fiat amount to take the order, buyer will send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-sell", "trade_index": 1, @@ -15,19 +15,20 @@ If the order fiat amount is a range like `10-20` the buyer must indicate a fiat } } }, - "" + "", + ["", ""] ] ``` ## Mostro response -In order to continue the buyer needs to send a lightning network invoice to Mostro, in this case the amount of the order is `0`, so Mostro will need to calculate the amount of sats for this order, then Mostro will send back a message asking for a LN invoice indicating the correct amount of sats that the invoice should have, here the rumor's content of the message: +In order to continue the buyer needs to send a lightning network invoice to Mostro, in this case the amount of the order is `0`, so Mostro will need to calculate the amount of sats for this order, then Mostro will send back a message asking for a LN invoice indicating the correct amount of sats that the invoice should have, here the decrypted content of the message: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "add-invoice", "payload": { @@ -49,6 +50,7 @@ In order to continue the buyer needs to send a lightning network invoice to Most } } }, + null, null ] ``` @@ -87,13 +89,13 @@ Mostro updates the addressable event with `d` tag `` to change the sta ## Using a lightning address -The buyer can use a [lightning address](https://github.com/andrerfneves/lightning-address) to receive funds and avoid to create and send lightning invoices on each trade, with a range order we set the fiat amount as the third element of the `payment_request` array, to acomplish this the buyer will send a message in a Gift wrap Nostr event to Mostro with the following rumor's content: +The buyer can use a [lightning address](https://github.com/andrerfneves/lightning-address) to receive funds and avoid to create and send lightning invoices on each trade, with a range order we set the fiat amount as the third element of the `payment_request` array, to acomplish this the buyer will send a message to Mostro (wrapped in the [active transport](./overview.md#transports)) with the following decrypted content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "take-sell", "payload": { @@ -101,6 +103,7 @@ The buyer can use a [lightning address](https://github.com/andrerfneves/lightnin } } }, + null, null ] ``` diff --git a/src/transport_migration.md b/src/transport_migration.md index 1f60135..c7b5222 100644 --- a/src/transport_migration.md +++ b/src/transport_migration.md @@ -43,11 +43,17 @@ their absence as v1. branch on it. 2. **Subscribe to the right kind**: `1059` for v1, `14` for v2 (authored by the node, `#p`-tagged to your trade keys for node replies). -3. **Wrap/unwrap with the matching path.** `mostro-core` **0.13.0** ships - both — `wrap_message_with(transport, …)` / `unwrap_incoming(event, …)` +3. **Wrap/unwrap with the matching path.** `mostro-core` (since **0.13.0**) + ships both — `wrap_message_with(transport, …)` / `unwrap_incoming(event, …)` dispatch on the transport (or event kind), so a client holding both - paths needs only to pass the node's transport. + paths needs only to pass the node's transport. Since **0.14.1** the + gift-wrap variant is marked deprecated at the library level as well. 4. **Set `version: 2`** in the message on the v2 transport (`1` on v1). + Note that the event **kind is authoritative**: the receiver picks the + unwrap path from the event kind (`1059` vs `14`), and does not reject a + message whose inner `version` value doesn't match the transport — the + field is informative on the wire. Set it correctly anyway; future + versions may validate it. 5. **On v2, build the 3-element content tuple** — message, trade signature (or `null`), identity proof `["", ""]` (or `null` for full-privacy mode). The identity proof is a signature over the @@ -61,6 +67,41 @@ Full-privacy mode and reputation mode work the same way as in v1: omit the identity key (proof and trade signature both `null`) for full privacy, or include them to maintain reputation. +## Anti-spam gates on v2 + +The point of making the trade key the visible author is that a node can +filter garbage **before** paying the NIP-44 decrypt cost. Since v0.18.0 +mostrod applies the following gates to every incoming kind-`14` event, in +this order: + +1. **Base proof of work** — the event must meet the node's NIP-13 + difficulty, advertised as the `pow` tag on the + [kind-`38385` info event](./other_events.md#mostro-instance-status). +2. **Replay guard** — an event `id` already seen within the last 60 + seconds is dropped. +3. **First-contact proof of work** — if the sender pubkey (the trade key) + is not in the node's *active set*, the event must meet a separate, + typically higher difficulty (`pow_first_contact`, falling back to the + base `pow` when the operator doesn't set one). The active set contains + the trade keys of every non-terminal order — orders under dispute + included — plus the solver keys of active disputes, and is refreshed + periodically (every 60 seconds by default), so keys engaged in a trade + keep the cheap fast path for follow-up messages. +4. **Freshness window** — after decryption, the inner message timestamp + must be at most 10 seconds old, or the message is dropped. (This check + predates v2 and applies on both transports.) + +**All gate rejections are silent.** The node drops the event without +sending any `cant-do` reply — answering unauthenticated spam would itself +be an amplification vector — so a client cannot rely on an error response +to detect that it was gated. If a first message gets no reply, mine more +proof of work and retry with a fresh timestamp. + +> **Discovery caveat:** the info event currently advertises only the base +> `pow`; there is no tag for the first-contact difficulty. A brand-new +> trade key that mines only the advertised `pow` can be silently dropped +> by a node configured with a stricter `pow_first_contact`. + ## Release timeline - **v0.18.0** — protocol v2 ships. Default `transport = "gift-wrap"`, so diff --git a/src/user_rating.md b/src/user_rating.md index d21f204..0b7ec53 100644 --- a/src/user_rating.md +++ b/src/user_rating.md @@ -1,28 +1,29 @@ # User rating -After a successful trade Mostro send a Gift wrap Nostr event to both parties to let them know they can rate each other, here an example how the message look like: +After a successful trade Mostro sends a message to both parties to let them know they can rate each other, here an example how the message look like: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "rate", "payload": null } }, + null, null ] ``` -After a Mostro client receive this message, the user can rate the other party, the rating is a number between 1 and 5, to rate the client must receive user's input and create a new Gift wrap Nostr event to send to Mostro with this content: +After a Mostro client receive this message, the user can rate the other party, the rating is a number between 1 and 5, to rate the client must receive user's input and create a new message to send to Mostro with this content: ```json [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "rate-user", "payload": { @@ -30,6 +31,7 @@ After a Mostro client receive this message, the user can rate the other party, t } } }, + null, null ] ``` @@ -42,7 +44,7 @@ If Mostro received the correct message, it will send back a confirmation message [ { "order": { - "version": 1, + "version": 2, "id": "", "action": "rate-received", "payload": { @@ -50,6 +52,7 @@ If Mostro received the correct message, it will send back a confirmation message } } }, + null, null ] ```