Skip to content

Commit

Permalink
Add AcceptXMR-Server
Browse files Browse the repository at this point in the history
  • Loading branch information
busyboredom committed Mar 20, 2023
1 parent 7e19049 commit b5344f5
Show file tree
Hide file tree
Showing 147 changed files with 5,840 additions and 135 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Book](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field)

## [Unreleased]

### Added
- A batteries-included payment gateway built around the core library.

## [0.12.0] - 2023-03-18

### Added
Expand All @@ -35,7 +38,7 @@ Book](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field)
- Make the `recv()` method of `Subscriber` an `async` method. Please use
`blocking_recv()` if you need to block while waiting.
- Make the `recv_timeout` method of `Subscriber` an `async` method.
- Change the `Output` of `Subsciber`'s `Future` impl to `Option<Invoice>`
- Change the `Output` of `Subscriber`'s `Future` impl to `Option<Invoice>`
instead of `Result<Invoice>`.

### Removed
Expand Down
74 changes: 5 additions & 69 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,70 +1,6 @@
[package]
name = "acceptxmr"
version = "0.12.0"
edition = "2021"
rust-version = "1.65"
license = "MIT OR Apache-2.0"
description = "Accept monero in your application."
repository = "https://github.com/busyboredom/acceptxmr"
readme = "README.md"
keywords = ["crypto", "gateway", "monero", "payment", "xmr"]
categories = ["cryptography::cryptocurrencies"]
[workspace]

[lib]
name = "acceptxmr"
path = "src/lib.rs"

[dependencies]
bincode = { version = "^2.0.0-rc.2", optional = true }
hex = "0.4"
http = "0.2"
hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] }
hyper-rustls = { version = "0.23", features = ["http2"] }
indexmap = "1"
log = "0.4"
md-5 = "0.10"
monero = "0.18"
rand = "0.8"
rand_chacha = "0.3"
serde = {version = "1", features = ["derive"], optional = true }
serde_json = "1"
sled = { version = "0.34", optional = true }
sqlite = { version = "0.30", optional = true }
strum = { version = "0.24", features = ["derive"] }
thiserror = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }

[features]
bincode = ["dep:bincode"]
in-memory = []
serde = ["dep:serde"]
sled = ["bincode", "dep:sled"]
sqlite = ["bincode", "dep:sqlite"]

[dev-dependencies]
actix = "0.13"
actix-files = "0.6"
actix-session = { version = "0.7", features = ["cookie-session"] }
actix-web = "4"
actix-web-actors = "4"
bytestring = "1"
env_logger = "0.10"
handlebars = { version = "4", features = ["dir_source"] }
httpmock = "0.6"
qrcode = "0.12"
serde = "1"
tempfile = "3"
test-case = "3"
# This is a workaround to enable features in tests.
acceptxmr = { path = ".", features = ["sled", "in-memory", "sqlite"] }

[[example]]
name = "custom_storage"

[[example]]
name = "nojs"
required-features = ["serde", "in-memory"]

[[example]]
name = "websockets"
required-features = ["serde", "in-memory"]
members = [
"server",
"library"
]
104 changes: 44 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,84 @@
[![BuildStatus](https://github.com/busyboredom/acceptxmr/workflows/CI/badge.svg)](https://img.shields.io/github/actions/workflow/status/busyboredom/acceptxmr/ci.yml?branch=main)
[![Crates.io](https://img.shields.io/crates/v/acceptxmr.svg)](https://crates.io/crates/acceptxmr)
[![Documentation](https://docs.rs/acceptxmr/badge.svg)](https://docs.rs/acceptxmr)
[![MSRV](https://img.shields.io/badge/MSRV-1.65.0-blue)](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)

# `AcceptXMR`: Accept Monero in Your Application
`AcceptXMR` aims to provide a simple, reliable, and efficient means to track
monero payments.

This library aims to provide a simple, reliable, and efficient means to track monero payments.
To track payments, `AcceptXMR` generates subaddresses using your private view
key and primary address. It then watches for monero sent to that subaddress
using a monero daemon of your choosing, updating the UI in realtime and
optionally performing a configurable callback once payment is confirmed.

To track payments, the `PaymentGateway` generates subaddresses using your private view key and
primary address. It then watches for monero sent to that subaddress using a monero daemon of your
choosing, your private view key and your primary address.
For a batteries-included payment gateway, see
[`AcceptXMR-Server`](./server/).

Use this library at your own risk, it is young and unproven.
For a slim & performant library to use in your rust applications, see
[`AcceptXMR`](./library/).

## Key Advantages
* View pair only, no hot wallet.
* Subaddress based.
* Pending invoices can be stored persistently, enabling recovery from power loss.
* Pending invoices can be stored persistently, enabling recovery from power
loss.
* Number of confirmations is configurable per-invoice.
* Ignores transactions with timelocks.
* Payment can occur over multiple transactions.

## Security
`AcceptXMR` is non-custodial, and does not require a hot wallet. However, it
does require your private view key and primary address for scanning outputs. If
keeping these private is important to you, please take appropriate precautions
to secure the platform you run your application on.

`AcceptXMR` is non-custodial, and does not require a hot wallet. However, it does require your
private view key and primary address for scanning outputs. If keeping these private is important
to you, please take appropriate precautions to secure the platform you run your application on.

Also note that anonymity networks like TOR are not currently supported for RPC calls. This
means that your network traffic will reveal that you are interacting with the monero network.
Also note that anonymity networks like TOR are not currently supported for RPC
calls. This means that your network traffic will reveal that you are interacting
with the monero network.

## Reliability
`AcceptXMR` strives for reliability, but that attempt may not be successful. It
is young and unproven, and relies on several crates which are undergoing rapid
changes themselves. For example, one of the built-in storage layer
implementations ([`Sled`](https://docs.rs/sled)) is still in beta.

This library strives for reliability, but that attempt may not be successful. `AcceptXMR` is young
and unproven, and relies on several crates which are undergoing rapid changes themselves For
example, the primary storage layer implementation ([`Sled`](https://docs.rs/sled)) is still in beta.

That said, this payment gateway should survive unexpected power loss thanks to the ability to flush
pending invoices to disk each time new blocks/transactions are scanned. A best effort is made to
keep the scanning thread free any of potential panics, and RPC calls in the scanning thread are
logged on failure and repeated next scan. In the event that an error does occur, the liberal use of
logging within this library will hopefully facilitate a speedy diagnosis an correction.
That said, `AcceptXMR` can survive unexpected power loss thanks to the ability
to flush pending invoices to disk each time new blocks/transactions are scanned.
A best effort is made to keep the scanning thread free any of potential panics,
and RPC calls in the scanning thread are logged on failure and repeated next
scan. In the event that an error does occur, the liberal use of logging within
`AcceptXMR` will hopefully facilitate a speedy diagnosis an correction.

Use this library at your own risk.
Use `AcceptXMR` at your own risk.

## Performance
It is recommended that you host your own monero daemon on the same local
network. Network and daemon slowness are the primary cause of high invoice
update latency in the majority of use cases.

It is strongly recommended that you host your own monero daemon on the same local network. Network
and daemon slowness are the primary cause of high invoice update latency in the majority of use
cases.

To reduce the average latency before receiving invoice updates, you may also consider lowering
the `PaymentGateway`'s `scan_interval` below the default of 1 second:
```rust
use acceptxmr::PaymentGateway;
use std::time::Duration;

let private_view_key =
"ad2093a5705b9f33e6f0f0c1bc1f5f639c756cdfc168c8f2ac6127ccbdab3a03";
let primary_address =
"4613YiHLM6JMH4zejMB2zJY5TwQCxL8p65ufw8kBP5yxX9itmuGLqp1dS4tkVoTxjyH3aYhYNrtGHbQzJQP5bFus3KHVdmf";

let store = InMemory::new();
To reduce the average latency before receiving invoice updates, you may also
consider lowering the gateway's scanning interval below the default of 1 second.
If using the `AcceptXMR` library, this can be done using the `scan_interval`
method of the `PaymentGatewayBuilder`. If using the standalone
`AcceptXMR-Server`, the scanning interval can be set in config.

let payment_gateway = PaymentGateway::builder(
private_view_key.to_string(),
primary_address.to_string(),
store
)
.scan_interval(Duration::from_millis(100)) // Scan for updates every 100 ms.
.build()?;
```

Please note that `scan_interval` is the minimum time between scanning for updates. If your
daemon's response time is already greater than your `scan_interval` or if your CPU is unable to
scan new transactions fast enough, reducing your `scan_interval` will do nothing.
Note that lowering the gateway's scanning interval will do nothing if latency to
your chosen node is slower than the scan interval.

## License

Licensed under either of

* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)

at your option.

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

### Donations

AcceptXMR is a hobby project which generates no revenue for the developer(s).
Donations from generous users and community members help keep it economically
viable to work on.
Expand Down
70 changes: 70 additions & 0 deletions library/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
[package]
name = "acceptxmr"
version = "0.12.0"
edition = "2021"
rust-version = "1.65"
license = "MIT OR Apache-2.0"
description = "Accept monero in your application."
repository = "https://github.com/busyboredom/acceptxmr"
readme = "README.md"
keywords = ["crypto", "gateway", "monero", "payment", "xmr"]
categories = ["cryptography::cryptocurrencies"]

[lib]
name = "acceptxmr"
path = "src/lib.rs"

[dependencies]
bincode = { version = "^2.0.0-rc.2", optional = true }
hex = "0.4"
http = "0.2"
hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] }
hyper-rustls = { version = "0.23", features = ["http2"] }
indexmap = "1"
log = "0.4"
md-5 = "0.10"
monero = "0.18"
rand = "0.8"
rand_chacha = "0.3"
serde = {version = "1", features = ["derive"], optional = true }
serde_json = "1"
sled = { version = "0.34", optional = true }
sqlite = { version = "0.30", optional = true }
strum = { version = "0.24", features = ["derive"] }
thiserror = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }

[features]
bincode = ["dep:bincode"]
in-memory = []
serde = ["dep:serde"]
sled = ["bincode", "dep:sled"]
sqlite = ["bincode", "dep:sqlite"]

[dev-dependencies]
actix = "0.13"
actix-files = "0.6"
actix-session = { version = "0.7", features = ["cookie-session"] }
actix-web = "4"
actix-web-actors = "4"
bytestring = "1"
env_logger = "0.10"
handlebars = { version = "4", features = ["dir_source"] }
httpmock = "0.6"
qrcode = "0.12"
serde = "1"
tempfile = "3"
test-case = "3"
# This is a workaround to enable features in tests.
acceptxmr = { path = ".", features = ["sled", "in-memory", "sqlite"] }

[[example]]
name = "custom_storage"

[[example]]
name = "nojs"
required-features = ["serde", "in-memory"]

[[example]]
name = "websockets"
required-features = ["serde", "in-memory"]
69 changes: 69 additions & 0 deletions library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[![BuildStatus](https://github.com/busyboredom/acceptxmr/workflows/CI/badge.svg)](https://img.shields.io/github/actions/workflow/status/busyboredom/acceptxmr/ci.yml?branch=main)
[![Crates.io](https://img.shields.io/crates/v/acceptxmr.svg)](https://crates.io/crates/acceptxmr)
[![Documentation](https://docs.rs/acceptxmr/badge.svg)](https://docs.rs/acceptxmr)
[![MSRV](https://img.shields.io/badge/MSRV-1.65.0-blue)](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)

# `AcceptXMR`: Accept Monero in Your Application
`AcceptXMR` is a library for building payment gateways.

For a batteries-included gateway, please see
[`AcceptXMR-Server`](../server/).

## Getting Started

To use `AcceptXMR` in your rust project, first add it to your `Cargo.toml`. For
example if you intend to use the `Sqlite` storage backend and need `serde`
support, you should add this to your `Cargo.toml`:
```toml
[dependencies]
acceptxmr = { version = "0.12", features = ["serde", "sqlite"] }
```
You can then create and run a `PaymentGateway`:
```rust
use acceptxmr::{PaymentGateway, storage::stores::Sqlite};
use std::time::Duration;

let private_view_key =
"ad2093a5705b9f33e6f0f0c1bc1f5f639c756cdfc168c8f2ac6127ccbdab3a03";
let primary_address =
"4613YiHLM6JMH4zejMB2zJY5TwQCxL8p65ufw8kBP5yxX9itmuGLqp1dS4tkVoTxjyH3aYhYNrtGHbQzJQP5bFus3KHVdmf";

let store = Sqlite::new("AcceptXMR_DB", "invoices")?;

let payment_gateway = PaymentGateway::builder(
private_view_key.to_string(),
primary_address.to_string(),
store
)
.daemon_url("https://node.example.com") // Specify a node.
.scan_interval(Duration::from_millis(500)) // Scan for updates every 500 ms.
.build()?;

payment_gateway.run()?;
```
Finally, you can create invoices and subscribe to them so you know when they get
paid:
```rust
// Oh hey, a customer is checking out!
let invoice_id = payment_gateway.new_invoice(
100 * 10 ** 9, // We'll charge 100 millineros,
0, // require 0 confirmations,
10, // expire in 10 blocks,
"Large Cheese Pizza".to_string() // and get the order right.
)?;

// We can now subscribe to updates to the pizza invoice.
let subscriber = payment_gateway.subscribe(invoice_id)?
.expect("invoice doesn't exist");

// Have we been paid yet?
let update = subscriber.recv().await.expect("channel closed");

if update.is_confirmed() {
// Great, ship the pizza and stop tracking the invoice.
println!("Invoice for \"{}\" paid", update.description());
payment_gateway.remove_invoice(invoice_id)?;
}
```
For more detailed documentation, see [docs.rs](https://docs.rs/acceptxmr) or the
[examples](./examples/).
File renamed without changes.
Loading

0 comments on commit b5344f5

Please sign in to comment.