mostrop2p / mostro Goto Github PK
View Code? Open in Web Editor NEWLightning Network peer-to-peer exchange platform on Nostr
Home Page: https://mostro.network
License: MIT License
Lightning Network peer-to-peer exchange platform on Nostr
Home Page: https://mostro.network
License: MIT License
Upgrade sqlx to latest version
When a dispute begins, the Solver who takes it must write a DM to those involved. The buyer and seller must wait for the Solver to contact them and in the meantime they do not know who will assist them (unless there is only 1 possible solver).
Since the buyer and seller do not know who will handle the dispute, it could happen that a malicious user who manages to learn that they are both in dispute could pose as a solver and write to them asking for personal information or money to "resolve" the dispute, or any other trick.
Describe the solution you would like
That Mostro shows the buyer and seller involved in a dispute the npub of the solver who will handle their dispute so that they can open DM to the solver, or so that they know who will assist them and can verify that the person writing to them is the real solver and not an imposter.
As titled. Using NIP-12 can improve querying of offers. See https://github.com/nobu-maeda/n3xb-nostr-derisk as an example.
Wondering if its possible for Makers to directly publish offers to Nostr relays. And only have Mostro participate as a coordinator / escrow only after a Taker decides to take the offer. This can improve censorship resistance.
Mostro send nip33 events to port 30078, currently we are sending events related with different data, those have a data_label
custom tag, right now we have these possible values order
, dispute
and rating
, in order to have a more efficient subscription we should add a filter rule to get only rating events
Here an example of an event with that custom tag
[
"EVENT",
"RAND",
{
"id": "44cad49530f45150dee36a8c3e5e11e94c8ba2f44ba9960013f0ec6f84e50c2d",
"pubkey": "dbe0b1be7aafd3cfba92d7463edbd4e33b2969f61bd554d37ac56f032e13355a",
"created_at": 1701977550,
"kind": 30078,
"tags": [
[
"d",
"npub1qqqxssz4k6swex94zdg5s4pqx3uqlhwsc2vdzvhjvzk33pcypkhqe9aeq2"
],
[
"total_reviews",
"6"
],
[
"total_rating",
"3.6"
],
[
"last_rating",
"4"
],
[
"max_rate",
"1"
],
[
"min_rate",
"5"
],
[
"data_label",
"rating"
]
],
"content": "",
"sig": "225f73bae421b4f19eb3797ea20b6549afc2e498ae13876e24e79e7b83e2d95bbf0bc278ae89023bdb2d333bec4e564002f3e6e316ff41a7864c4c5e6f48ff1c"
}
]
We should implement that filter here:
https://github.com/MostroP2P/mostro/blob/main/src/app/rate_user.rs#L23-L26
Orders with negative fiat and sats amounts are allowed to be created, and in the case of the negative sats amount a panic is generated when a hodl invoice with negative amount is requested.
Here's a log dump for that event:
2024-01-07T17:02:33.766035Z INFO mostrod::util: DM content: "{\"Order\":{\"version\":1,\"id\":\"d43d4753-618e-416a-b9fc-9ddd57c97c9d\",\"pubkey\":null,\"action\":\"NewOrder\",\"content\":{\"Order\":{\"id\":\"d43d4753-618e-416a-b9fc-9ddd57c97c9d\",\"kind\":\"Sell\",\"status\":\"Pending\",\"amount\":-2000,\"fiat_code\":\"USD\",\"fiat_amount\":5,\"payment_method\":\"Cash\",\"premium\":0,\"created_at\":1704646953}}}}"
2024-01-07T17:02:33.771251Z INFO mostrod::util: Sending event: Event {
id: EventId(
0xf5bc660d3dcd7758418346bdf950682b0e280f6a8650ae4254e25f00d4c5192c,
),
pubkey: XOnlyPublicKey(
d9725eb47dc4441cec52c21b3fa63f630dbdf6458dc3bd091c75b5faf3fecb2e28bf42c5978329f2d144e3e4cedd8d25e94b9fa8ded22a708c4219cb5336e099,
),
created_at: Timestamp(
1704646953,
),
kind: EncryptedDirectMessage,
tags: [
PubKey(
XOnlyPublicKey(
128642fc8945af582de87e69c7d763626a89435cda6ee38ff8e06a0cf21f39498e20aa1b9dd72f537fcb4f4eb0431eeb0bca780cc9934575e7e2d4fb9b77bf8a,
),
None,
),
],
content: "hj/s1pICaVwpRsW3rux8wlSSw2G1JI6iiMQFxCFzdk5EVwL/lhNJJyFnO5IB8anD77TvpDi4FFmEMuhkz6X1/iQ8Xjiq1heAVIYMpDf3QAKRGm7L9++oaK8eLY902I9RGNULfyO5H6r1LTm8V4vvnWcLUb6OtfP7yu+tsm1pFmKAZVl5R9IJO2tvs29oXmtl9EFiwuR89K0iMv1/Wa8WSVb+A92M2aYHDXS7Ro7H91hEdQ6CQlQmWecd26s0lQn9nHCeEYC0bRxT9MhfQPZk57niM1EcGk9axdcCU0C5/ULREfWvBjq9HbWhowJDqLkoXrK6hTYuipqY3Nmpql/6P+Gq36+ulQcVq2xHN5bI6p6+YY54rkctkv0lbgTl92dn1K5JCgpIRuQHSXevRm32T0Ngo+yJyyyNKV1Zst3Keqs=?iv=vlrORIjU6t6JD0k/GORwDA==",
sig: Signature(bb6fa6714b2a64cdeacaa97cf00714925f637de4b7b3bd16fc4cbabbefb7840527e369ec38927d26933a6072a0fef1005fd61562f50b9ba81cedb67c6728d434),
}
thread 'main' panicked at src/lightning/mod.rs:77:14:
Failed to add hold invoice: Status { code: Unknown, message: "payments of negative value are not allowed, value is -2000000", metadata: MetadataMap { headers: {"content-type": "application/grpc"} }, source: None }
I'm not sure what the expected behavior is here, but I'd assume that such order creation requests should be rejected with an error.
When an new buy order have buyer_invoice
with a botl11 LN invoice we should validate:
In all cases we allow users to enter a lightning address instead of a bolt11 invoice.
This validation should be added on actions new-order
and add-invoice
.
I get this when compiling from scratch on a linux VPS with rustc 1.71.0 (8ede3aae2 2023-07-12)
.
Oddly, when compiling locally (mac os) with rustc 1.68.0 (2c8cc3432 2023-03-06)
this error is not thrown.
Compiling mostro v0.7.9 (/home/bilthon/mostro)
error[E0308]: mismatched types
--> src/app/rate_user.rs:82:33
|
82 | relay.send_msg(msg.clone(), false).await.unwrap();
| -------- ^^^^^ expected `Option<Duration>`, found `bool`
| |
| arguments to this method are incorrect
|
= note: expected enum `std::option::Option<std::time::Duration>`
found type `bool`
note: method defined here
--> /home/bilthon/.cargo/git/checkouts/nostr-7fe31184c4acbae8/8261bff/crates/nostr-sdk/src/relay/mod.rs:694:18
|
694 | pub async fn send_msg(&self, msg: ClientMessage, wait: Option<Duration>) -> Result<(), Error> {
| ^^^^^^^^
error[E0308]: mismatched types
--> src/app/rate_user.rs:111:46
|
111 | relay.send_msg(ClientMessage::close(id), false).await.ok()?;
| -------- ^^^^^ expected `Option<Duration>`, found `bool`
| |
| arguments to this method are incorrect
|
= note: expected enum `std::option::Option<std::time::Duration>`
found type `bool`
note: method defined here
--> /home/bilthon/.cargo/git/checkouts/nostr-7fe31184c4acbae8/8261bff/crates/nostr-sdk/src/relay/mod.rs:694:18
|
694 | pub async fn send_msg(&self, msg: ClientMessage, wait: Option<Duration>) -> Result<(), Error> {
| ^^^^^^^^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `mostro` (bin "mostro") due to 2 previous errors
This is with v0.7.9
commit 6f2c043212a68d5da0f7115dad726974d5a1d026
.
In order to have incentive to run a Mostro node we need to set a fee for every order, this should be an small value and can be set on .env
file as a percent, i.g.: FEE=0.006
is 0.6%
, and this should be splitted and charge 0.3%
to each part.
To be done on this issue:
I can see that now when a potential buyer takes an order, mostro will send a DM to the seller, something like this:
🧌 Order Id: f6204501-6868-4919-8b8b-7b15a7746e4f
npub1kk30z9gus7xk3kf3m8zxh9paw4ngw023he9037y2jd7y7msfvvdskj8e60 has taken your order and wants to buy your sats. Get in touch and tell him/her how to send you brl 36 through ibk.
Once you verify you have received the full amount you have to release the sats
This is fine, but works only for english. A better approach IMO would be for mostro to just send a JSON object with the relevant data of this message, and let the client construct the final message, which can also then be localized depending on the user's settings.
{
"order_id": "f6204501-6868-4919-8b8b-7b15a7746e4f",
"buyer": "npub1kk30z9gus7xk3kf3m8zxh9paw4ngw023he9037y2jd7y7msfvvdskj8e60",
}
I removed fiat_amount
, fiat_code
and payment_method
because that's information that the client can obtain just by querying old messages by order id. But they could be included anyways.
This is more flexible and also more in line with the nostr approach of moving the logic to the client.
Hi i think there's typo boss, you are inserting 10 parameters, but using 11. I think you have to remove one.
Line 49 in 34a660f
Message should have a new action payment-failed
and indicate the payment attempts left.
With this information clients could create a message like this:
I tried to send you the sats but the payment of your invoice failed, I will try ${attempts} more times in ${pendingPaymentWindow} minutes window, please check your node/wallet is online
All payments that Mostro have to do using send_payment()
[1] should be added to the scheduler to be executed 30 seconds later.
[1] https://github.com/MostroP2P/mostro/blob/main/src/lightning/mod.rs#L153
Error: error returned from database: (code: 2067) UNIQUE constraint failed: disputes.order_id
Caused by:
(code: 2067) UNIQUE constraint failed: disputes.order_id
We need to set a max routing fee for every payment we do to avoid leaking sats attack
https://github.com/MostroP2P/mostro/blob/main/src/lightning/mod.rs#L153
Here is an implementation on @lnp2pBot that can be used as a reference.
https://github.com/lnp2pBot/bot/blob/main/ln/pay_request.js#L13
Originally posted by bilthon April 24, 2023
Nostr's operation results in the leakage of some metadata about trades to external observers. Although identities in Nostr are pseudonymous, making an effort to cover these tracks would be beneficial.
Mostro currently communicates with a mix of messages of kind 3000 and kind 4. Kind 3000 are replaceable notes used to post orders. These are akin to posting messages in a public board. The information in them is public and they're always issued by mostro so there's not much to protect there. But these order notes are preceded by direct messages (DMs) of kind 4 that carry the order information. Similarly when an order is taken, a DM to mostro is issued by the taker, and subsequently more DMs are sent from mostro to the order maker.
The kind 4 messages are meant to be direct messages, and while the contents of the message are encrypted, an external observer does get the public key information, meaning it is known who communicated to whom. A simple time correlation between these DMs and the order state changes would allow an attacker to infer the Nostr identities of buyer and seller.
Using only relays that support NIP-42 (authenticated messages) could minimize this information leak. However, a motivated attacker could still gather information by setting up one or multiple relays.
A proposed solution involves creating ephemeral identities for both the buyer and seller. These identities would last for the duration of the trade and be discarded afterward. By taking advantage of the fact that identities in Nostr are simply private/public key pairs and relatively cheap to create, the real traders' public keys can be masked.
The root identity of each party can still be communicated to Mostro to maintain the reputation system, but this information would be contained within the encrypted payload of each message.
Mostro is always trying to re-connect unresponsive relays, this is a waste of server resources and also we are generating a lot of useful log lines, I just got 600 MB on logs for only 4 days, we need to stop connecting again and again to unresponsive relays.
Not sure how to do it because I can see on logs that fully responsive relays also get disconnected and connected again, so we can't just stop connecting a relay after a disconnection, we can discuss here different ways of solve this problem.
Here is the output
docker-compose up
Creating network "relay_default" with the default driver
Pulling nostr-relay (scsibug/nostr-rs-relay:)...
latest: Pulling from scsibug/nostr-rs-relay
7bc891379e23: Pull complete
df4d78e13fae: Pull complete
3a6c16603983: Pull complete
5d8c1e92d83d: Pull complete
536d78aa3c67: Pull complete
Digest: sha256:6bd15c99ecf5bcba66438d5f9a1e5b8c0941a359fc7c0a18693b403fd3d56451
Status: Downloaded newer image for scsibug/nostr-rs-relay:latest
Creating nostr-relay ... done
Attaching to nostr-relay
nostr-relay | Mar 21 15:29:31.873 INFO nostr_rs_relay: Starting up from main
nostr-relay | Mar 21 15:29:31.875 INFO nostr_rs_relay::server: listening on: 0.0.0.0:8080
nostr-relay | Mar 21 15:29:31.878 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:32.278 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:33.078 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:34.679 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:37.879 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:44.279 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | Mar 21 15:29:57.080 ERROR r2d2: unable to open database file: /usr/src/app/db/nostr.db
nostr-relay | thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Some("unable to open database file: /usr/src/app/db/nostr.db"))', src/repo/sqlite.rs:949:10
nostr-relay | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
nostr-relay | thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', src/main.rs:51:19
nostr-relay exited with code 101
This happened after I took a sell order. Maybe issue schedule a retry? if the API is down we should eventually give up and let the clients know about this.
DEBUG mostro::app > message: Ok("{\"version\":0,\"pubkey\":\"npub1gwfevg324vdpgdnjr8jjl7cmeyrkg2xd4gv5jsgjvw34c7323yxqux3cul\",\"order_id\":\"db00977a-e4bd-46de-807e-f07ba513bb6f\",\"action\":\"TakeSell\",\"content\":null}")
INFO sqlx::query > UPDATE orders SET master_buyer_pubkey …; rows affected: 1, rows returned: 0, elapsed: 645.750µs
UPDATE
orders
SET
master_buyer_pubkey = ?1
WHERE
id = ?2
INFO sqlx::query > UPDATE orders SET buyer_pubkey …; rows affected: 1, rows returned: 0, elapsed: 403.125µs
UPDATE
orders
SET
buyer_pubkey = ?1
WHERE
id = ?2
INFO sqlx::query > PRAGMA foreign_keys = ON; …; rows affected: 0, rows returned: 0, elapsed: 36.000µs
PRAGMA foreign_keys = ON;
INFO sqlx::query > UPDATE orders SET taken_at …; rows affected: 1, rows returned: 0, elapsed: 1.041ms
UPDATE
orders
SET
taken_at = ?1
WHERE
id = ?2
DEBUG reqwest::connect > starting new connection: https://api.yadio.io/
INFO sqlx::query > SELECT "orders"."id", "orders"."kind", "orders"."event_id", …; rows affected: 1, rows returned: 1, elapsed: 704.209µs
SELECT
"orders"."id",
"orders"."kind",
"orders"."event_id",
"orders"."hash",
"orders"."preimage",
"orders"."creator_pubkey",
"orders"."cancel_initiator_pubkey",
"orders"."buyer_pubkey",
"orders"."master_buyer_pubkey",
"orders"."seller_pubkey",
"orders"."master_seller_pubkey",
"orders"."status",
"orders"."price_from_api",
"orders"."premium",
"orders"."payment_method",
"orders"."amount",
"orders"."min_amount",
"orders"."max_amount",
"orders"."buyer_dispute",
"orders"."seller_dispute",
"orders"."buyer_cooperativecancel",
"orders"."seller_cooperativecancel",
"orders"."fee",
"orders"."routing_fee",
"orders"."fiat_code",
"orders"."fiat_amount",
"orders"."buyer_invoice",
"orders"."range_parent_id",
"orders"."invoice_held_at",
"orders"."taken_at",
"orders"."created_at",
"orders"."buyer_sent_rate",
"orders"."seller_sent_rate"
FROM
"orders"
WHERE
"orders"."id" = ?
LIMIT
1
Error: Something went wrong with API request, try again!
Caused by:
0: error sending request for url (https://api.yadio.io/convert/10/pen/BTC): error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known
1: error trying to connect: dns error: failed to lookup address information: nodename nor servname provided, or not known
2: dns error: failed to lookup address information: nodename nor servname provided, or not known
3: failed to lookup address information: nodename nor servname provided, or not known
I've been testing Mostro a little and I find the 15 minutes timer to send the LN invoice as a buyer is too short. I have failed to do it a bunch of times. It would be great having more time or, even better, having the option to choose that time at order making time.
I use Robosats all the time and I love that you can chose both the public duration of your order (up to 24 hours) as well as the escrow/invoice timer (up to 8 hours). I always set 24 and 8 hours respectively, what, together with their Telegram notifications, allows me to never miss when one of my orders gets taken, even when I fall asleep during the night (I always still have some time to do it in the morning). A similar way to choose those times (at least to increase some the 15 minutes timer for the invoice) on Mostro would be very beneficial, IMO.
This issue have two parts
After a seller takes an order he is asked to paid a hold invoice, add a new cron job that cancel the hold invoice after N seconds and republish the order.
After a buyer takes an order he is asked to add a lightning invoice to receive funds, add a new cron job that after N seconds republish the order.
We already have a job working, take a look to make this new ones
Set a env var to limit publishing orders with a bigger amount
This breaks mostrod
nostril --envelope --dm mostro-pubkey-hex --sec private-key-hex --content '{"version":0,"pubkey":"npub1qqqxssz4k6swex94zdg5s4pqx3uqlhwsc2vdzvhjvzk33pcypkhqe9aeq2","action":"Order","content":{"Order":{"kind":"Sell","status":"Pending","amount":0,"fiat_code":"xxxx","fiat_amount":8,"payment_method":"f2f","premium":0,"created_at":0}}}' | websocat ws://localhost:7000
Mostro response before panic
Error: Price API not answered - retry
FiatSent
message from buyer and send a message to seller to release fundsRelease
message from seller, settle hold invoice and ask them to rate to counterpartyIf users create orders with market price (without sats amount) we need to change the order flow but having in mind that don't break the order flow for orders with sats amount fixed.
Case 1: Sell order published
mostro-cli sendinvoice -o id -i lnbc1...
Case 2: Buy order published
mostro-cli sendinvoice -o id -i lnbc1...
As titled. NIP-78. This can open up content
and tag
field to adopt application specific meanings. See https://github.com/nobu-maeda/n3xb-nostr-derisk as an example.
We need to add a reputation system like the one we use in @lnp2pbot but for this we should save user's reputation as a nip33 event on Nostr. Any time users can query other users reputation.
We should be able to send this fields on the event published
total_reviews: integer
total_rating: integer
last_rating: integer
min_rate: integer
max_rate: integer
The new reputation should be a new index from 1 to 5, calculated using the successful trades and volume in sats.
We can do it the same way we are posting orders, I mean serializing it on content
field, and to calculate we should use the same logic on the lnp2pbot link above.
If you run cargo test
on your local mostro environment it passes all tests, but after each commit on github we always have this errors related with the sqlite database.
error: error returned from database: (code: 14) unable to open database file
--> src/db.rs:98:25
|
98 | let rows_affected = sqlx::query!(
| _________________________^
99 | | r#"
100 | | UPDATE orders
101 | | SET
... |
114 | | order_id
115 | | )
| |_____^
|
= note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info)
Mostro should discard messages from users with content field with a size of 256 bytes in order to avoid DoS attacks.
We are getting full of db update functions, but there is an easy way of update with sqlx, let's start using it instead of create functions that we are using only once and are adding more complexity to the code, here an example:
mostro/src/app/admin_cancel.rs
Lines 51 to 55 in 89a4952
Mostro should have a local directory where we can place settings and database files, by default mostro should look files on $HOME/.mostro/
, this local directory also can be set as a mostro binary parameter like mostro --dir /absolute/path
I think that when a user is in dispute they should not be allowed to create or take any orders until the situation be resolved, to prevent possible scammers from trying to scam several people at the same time.
Disadvantages: Those who are innocent and also involved in the dispute will lose opportunities waiting for it to be resolved.
In FLOW.md, it seems that the Lightning invoice created by Buyer/Taker is same as one by Mostro to Seller/Maker. Is this intentional and some sort of Lightning invoice mechanics that I'm not aware of (perhaps how HODL invoice works?). But not clear to me how so. Flagging to see if this is intentional.
I can take an order created by myself, it is removed from listorders. I continued with the process to see how far it went and when I entered the invoice to receive the sats it came out "Minimal expiration time on invoice"
It shouldn't be allowed to take your own orders.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.