wip
This commit is contained in:
parent
4183fe6ec8
commit
aca2b9e8d5
|
@ -485,8 +485,10 @@ dependencies = [
|
||||||
"quick-protobuf",
|
"quick-protobuf",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_arrays",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
@ -3055,6 +3057,15 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_arrays"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.217"
|
version = "1.0.217"
|
||||||
|
@ -3078,6 +3089,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -3682,6 +3702,40 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -4204,6 +4258,15 @@ version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.50.0"
|
version = "0.50.0"
|
||||||
|
|
|
@ -33,7 +33,9 @@ owo-colors = "4.1.0"
|
||||||
quick-protobuf = "0.8.1"
|
quick-protobuf = "0.8.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.213", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
|
serde_arrays = "0.1.0"
|
||||||
sqlx = { version = "0.8.2", features = ["sqlite", "macros", "runtime-tokio"] }
|
sqlx = { version = "0.8.2", features = ["sqlite", "macros", "runtime-tokio"] }
|
||||||
tokio = { version = "1.40.0", features = ["full", "tracing"] }
|
tokio = { version = "1.40.0", features = ["full", "tracing"] }
|
||||||
|
toml = "0.8.20"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
|
187
README.md
187
README.md
|
@ -1,9 +1,15 @@
|
||||||
# CL L2 V0
|
# CL L2 V0
|
||||||
|
|
||||||
This repo is a sandbox for the Cardano Lightning's L2 (V0).
|
## Aims
|
||||||
|
|
||||||
A key aim of this repo is to try and assess available libraries in order to
|
This is a demo of a CL node that is able to:
|
||||||
establish our key dependencies.
|
|
||||||
|
- Manage channels via l1 interface (all l1 txs)
|
||||||
|
- Coordinate channels with other nodes (open, register)
|
||||||
|
- Send and receive cheques
|
||||||
|
|
||||||
|
The repo is a sandbox for the Cardano Lightning's future work. Here we assess
|
||||||
|
available libraries and designs.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -21,158 +27,35 @@ Run binary `x`:
|
||||||
cargo run --bin <x>
|
cargo run --bin <x>
|
||||||
```
|
```
|
||||||
|
|
||||||
There are three binaries:
|
## Current stack
|
||||||
|
|
||||||
- `admin` - admin controls: create, add, revoke ephemeral keys
|
- Comms stack : rust-libp2p
|
||||||
- `server` - run the signing server
|
- Persistent storage : sqlx with sqlite
|
||||||
- `client` - run a client
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
CL depends on the use on signing/verification key pair cryptography. The signing
|
|
||||||
key may be used to handover all the participants funds. The signing key must be
|
|
||||||
handled with care.
|
|
||||||
|
|
||||||
A router node must run their infrastructure on highly available machines, in
|
|
||||||
order to provide good service to their partners. The router might choose to use
|
|
||||||
an infrastructure provider to host their nodes. The router must produce the
|
|
||||||
signatures for cheques and snapshots in the course of standard channel
|
|
||||||
operations. A priori the machine will contain signing keys. What if the machine
|
|
||||||
is compromised?
|
|
||||||
|
|
||||||
An attacker with access to the machine could send all funds to the partner of
|
|
||||||
the channel, or produce a tx that a partner signs, making it a valid mutual tx.
|
|
||||||
In either case, they are at the detriment of the router.
|
|
||||||
|
|
||||||
Instead, we may outsource the signing process to a separate machine - the
|
|
||||||
signing server. This sever can:
|
|
||||||
|
|
||||||
- serve requests to only whitelisted requesters. It can be more easily protected
|
|
||||||
from ddos type attacks and probing by an attacker.
|
|
||||||
- has minimal, relatively fixed, API. It will receive fewer updates each of
|
|
||||||
which can be reviewed more thoroughly, and have shorter/tighter software
|
|
||||||
supply chains, than might be true for the general service on the HA machine.
|
|
||||||
|
|
||||||
In addition, the signing service could be split using, say, FROST.
|
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
### Data
|
See the design docs.
|
||||||
|
|
||||||
#### Persistent keys
|
## TODO
|
||||||
|
|
||||||
These are the signing keys available to the machine and found at startup of the
|
- [ ] api
|
||||||
service.
|
- [ ] global
|
||||||
|
- [ ] terms
|
||||||
#### Ephemeral keys
|
- [x] simple
|
||||||
|
- [ ] confer
|
||||||
Perhaps also called proxy keys.
|
- [x] simple
|
||||||
|
- [ ] watch
|
||||||
An ephemeral key consists of the following properties:
|
- [ ] register
|
||||||
|
- [ ] channel
|
||||||
1. Verification key
|
- [ ] UNPACK
|
||||||
1. Persistent key
|
- [ ] config
|
||||||
1. Expires at
|
- [ ] UNPACK
|
||||||
1. Signature
|
- [ ] storage
|
||||||
|
- [ ] ephemeral
|
||||||
### Signing Server Actions
|
- [ ] UNPACK
|
||||||
|
- [ ] persistent
|
||||||
#### Add
|
- [ ] UNPACK
|
||||||
|
- [ ] l1
|
||||||
An add is performed by "admin" to create new ephemeral keys.
|
- [ ] Mock
|
||||||
|
- [ ] UNPACK
|
||||||
An `add` request has the following fields
|
- [ ] Via "kupmios"
|
||||||
|
|
||||||
1. Verification key
|
|
||||||
2. Persistent key
|
|
||||||
3. Expires at
|
|
||||||
4. Signature
|
|
||||||
|
|
||||||
The request is deemed valid
|
|
||||||
|
|
||||||
1. The persistent key is in the database
|
|
||||||
1. The signature is valid wrt the persistent key, and the payload created by
|
|
||||||
`(verification_key, expires_at)` (with some prefix?)
|
|
||||||
|
|
||||||
The result of a valid `add` request is the ephemeral key is added to the
|
|
||||||
database with the obvious fields.
|
|
||||||
|
|
||||||
The response is either `Ok()` or `Error("help message!")`
|
|
||||||
|
|
||||||
#### Sign
|
|
||||||
|
|
||||||
A sign action is the "standard" action that will occur.
|
|
||||||
|
|
||||||
A `sign` request has the following fields
|
|
||||||
|
|
||||||
1. Verification key
|
|
||||||
2. Payload
|
|
||||||
3. Signature
|
|
||||||
|
|
||||||
The request is deemed valid
|
|
||||||
|
|
||||||
1. The verification key exists in the database
|
|
||||||
1. The verification key has not expired
|
|
||||||
1. The signature is valid wrt the payload and verification key
|
|
||||||
1. The payload is "sensible" (TBC) - this is context specific.
|
|
||||||
|
|
||||||
The response to a valid request
|
|
||||||
|
|
||||||
1. The signature produced by the persistent key associated to the verification
|
|
||||||
key for the same payload.
|
|
||||||
|
|
||||||
#### Revoke
|
|
||||||
|
|
||||||
A revoke is the opposite of add. It is also performed exclusively by admin.
|
|
||||||
|
|
||||||
An `revoke` request has the following fields
|
|
||||||
|
|
||||||
2. Verification key
|
|
||||||
3. Signature
|
|
||||||
|
|
||||||
The request is deemed valid
|
|
||||||
|
|
||||||
1. The verification key is in the database
|
|
||||||
1. The signature is valid wrt the associated persistent key, and the payload
|
|
||||||
created by `verification_key` (with some prefix?)
|
|
||||||
|
|
||||||
The result of a valid `revoke` request is the ephemeral key is removed from the
|
|
||||||
database.
|
|
||||||
|
|
||||||
The response is either `Ok()` or `Error("help message!")`
|
|
||||||
|
|
||||||
#### SIMPLIFICATION
|
|
||||||
|
|
||||||
The `add` and `revoke` endpoints are needed only by admin, and infrequently, or
|
|
||||||
in case of emergency. In contrast, `sign` is needed with relatively high
|
|
||||||
frequency, and called by less trusted machines.
|
|
||||||
|
|
||||||
Thus, we assume that admin accesses the machine directly and simply adds the
|
|
||||||
ephemeral keys to the db. This does not require a valid signature - if the pub
|
|
||||||
key is registered, then a signature request is honored.
|
|
||||||
|
|
||||||
### Client server actions
|
|
||||||
|
|
||||||
#### Demo task
|
|
||||||
|
|
||||||
Some way to trigger the client service to make a request to the server.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
### sqlx
|
|
||||||
|
|
||||||
The sqlx tool has a cli (available via the flake). This can handle migrations
|
|
||||||
provided its opinionated design choices are adopted. See
|
|
||||||
[here](https://docs.rs/sqlx/latest/sqlx/migrate/trait.MigrationSource.html).
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export DATABASE_URL=sqlite:./db/cll2v0.db
|
|
||||||
sqlx database create
|
|
||||||
sqlx migration run
|
|
||||||
```
|
|
||||||
|
|
||||||
The database must be initialised before starting the application.
|
|
||||||
|
|
||||||
### libp2p
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
216
app/admin.rs
216
app/admin.rs
|
@ -1,9 +1,10 @@
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use ed25519_dalek::{Signature, SigningKey};
|
// use ed25519_dalek::{Signature, SigningKey};
|
||||||
|
|
||||||
use cll2v0::{
|
use cll2v0::{
|
||||||
db,
|
// db,
|
||||||
keys::{self},
|
config,
|
||||||
|
// keys::{self}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// cll2v0 is a playground for rust libraries.
|
/// cll2v0 is a playground for rust libraries.
|
||||||
|
@ -17,117 +18,122 @@ struct Args {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Add new ephemeral key to an existing persistent key
|
/// Generate a default config
|
||||||
Add {
|
GenConfig, // /// Add new ephemeral key to an existing persistent key
|
||||||
ephemeral_key: String,
|
// Add {
|
||||||
persistent_key: String,
|
// ephemeral_key: String,
|
||||||
expires_at: i64,
|
// persistent_key: String,
|
||||||
},
|
// expires_at: i64,
|
||||||
/// Revoke an existing ephemeral key
|
// },
|
||||||
Revoke { ephemeral_key: String },
|
// /// Revoke an existing ephemeral key
|
||||||
/// Check persistent keys are available and display
|
// Revoke { ephemeral_key: String },
|
||||||
Check,
|
// /// Check persistent keys are available and display
|
||||||
/// Gen from seed some sensible ephemeral key args to be used with `add`
|
// Check,
|
||||||
Gen { seed: u8 },
|
// /// Gen from seed some sensible ephemeral key args to be used with `add`
|
||||||
/// Sign a message
|
// Gen { seed: u8 },
|
||||||
Sign {
|
// /// Sign a message
|
||||||
signing_key: String,
|
// Sign {
|
||||||
message: String,
|
// signing_key: String,
|
||||||
},
|
// message: String,
|
||||||
/// Verify a message
|
// },
|
||||||
Verify {
|
// /// Verify a message
|
||||||
verifying_key: String,
|
// Verify {
|
||||||
message: String,
|
// verifying_key: String,
|
||||||
signature: String,
|
// message: String,
|
||||||
},
|
// signature: String,
|
||||||
|
// },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let (_, pool) = db::start_db().await?;
|
// let (_, pool) = db::start_db().await?;
|
||||||
|
|
||||||
match args.cmd {
|
match args.cmd {
|
||||||
Some(Command::Add {
|
Some(Command::GenConfig) => {
|
||||||
ephemeral_key,
|
let c = config::Config::default().to_string()?;
|
||||||
persistent_key,
|
println!("{}", c);
|
||||||
expires_at,
|
|
||||||
}) => {
|
|
||||||
println!("Adding new ephemeral key '{ephemeral_key}'");
|
|
||||||
let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
|
|
||||||
let p = keys::from_hex(&persistent_key).expect("cannot parse key");
|
|
||||||
let res = db::add_ephemeral_key(&pool, &e, &p, expires_at).await?;
|
|
||||||
println!("Added new ekey: {}", res);
|
|
||||||
}
|
|
||||||
Some(Command::Revoke { ephemeral_key }) => {
|
|
||||||
let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
|
|
||||||
let res = db::revoke_ephemeral_key(&pool, &e).await?;
|
|
||||||
println!("Revoked key: {}", res);
|
|
||||||
}
|
|
||||||
Some(Command::Check) => {
|
|
||||||
eprintln!("!Check skipped");
|
|
||||||
let res = db::list_persistent_keys(&pool).await?;
|
|
||||||
println!("Persistent keys:");
|
|
||||||
res.into_iter()
|
|
||||||
.for_each(|key| println!("{}", keys::to_hex(&key)));
|
|
||||||
let res = db::list_ephemeral_keys(&pool).await?;
|
|
||||||
println!("ephemeral keys:");
|
|
||||||
res.into_iter()
|
|
||||||
.for_each(|(e, p, t)| println!("{} {} {t}", keys::to_hex(&e), keys::to_hex(&p)))
|
|
||||||
}
|
|
||||||
Some(Command::Gen { seed }) => {
|
|
||||||
let ekey = SigningKey::from_bytes(&[
|
|
||||||
seed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0,
|
|
||||||
]);
|
|
||||||
println!("Ephemeral signing key:");
|
|
||||||
println!("{}", keys::signing_key_to_hex(&ekey));
|
|
||||||
let res = db::list_persistent_keys(&pool).await?;
|
|
||||||
let idx = (seed as usize) % res.len();
|
|
||||||
let pkey = res.get(idx);
|
|
||||||
match pkey {
|
|
||||||
None => eprintln!("No persistent keys found"),
|
|
||||||
Some(pkey) => {
|
|
||||||
let now: usize = std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
let twenty_mins_from_now = 20usize * 60 * 1000 + now;
|
|
||||||
println!("Args");
|
|
||||||
println!(
|
|
||||||
"{} {} {}",
|
|
||||||
keys::to_hex(&ekey.verifying_key()),
|
|
||||||
keys::to_hex(pkey),
|
|
||||||
twenty_mins_from_now
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Command::Sign {
|
|
||||||
signing_key,
|
|
||||||
message,
|
|
||||||
}) => {
|
|
||||||
let mut skey = keys::signing_key_from_hex(&signing_key).unwrap();
|
|
||||||
let msg = hex::decode(message).unwrap();
|
|
||||||
let sig = keys::sign(&mut skey, msg);
|
|
||||||
println!("{}", hex::encode(sig.to_bytes()))
|
|
||||||
}
|
|
||||||
Some(Command::Verify {
|
|
||||||
verifying_key,
|
|
||||||
message,
|
|
||||||
signature,
|
|
||||||
}) => {
|
|
||||||
let vkey = keys::from_hex(&verifying_key).unwrap();
|
|
||||||
let msg = hex::decode(message).unwrap();
|
|
||||||
let sig = Signature::from_bytes(
|
|
||||||
&TryInto::<[u8; 64]>::try_into(hex::decode(signature).unwrap()).unwrap(),
|
|
||||||
);
|
|
||||||
let res = keys::verify(&vkey, &msg, &sig).unwrap();
|
|
||||||
println!("{:?}", res);
|
|
||||||
}
|
}
|
||||||
|
// Some(Command::Add {
|
||||||
|
// ephemeral_key,
|
||||||
|
// persistent_key,
|
||||||
|
// expires_at,
|
||||||
|
// }) => {
|
||||||
|
// println!("Adding new ephemeral key '{ephemeral_key}'");
|
||||||
|
// let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
|
||||||
|
// let p = keys::from_hex(&persistent_key).expect("cannot parse key");
|
||||||
|
// let res = db::add_ephemeral_key(&pool, &e, &p, expires_at).await?;
|
||||||
|
// println!("Added new ekey: {}", res);
|
||||||
|
// }
|
||||||
|
// Some(Command::Revoke { ephemeral_key }) => {
|
||||||
|
// let e = keys::from_hex(&ephemeral_key).expect("cannot parse key");
|
||||||
|
// let res = db::revoke_ephemeral_key(&pool, &e).await?;
|
||||||
|
// println!("Revoked key: {}", res);
|
||||||
|
// }
|
||||||
|
// Some(Command::Check) => {
|
||||||
|
// eprintln!("!Check skipped");
|
||||||
|
// let res = db::list_persistent_keys(&pool).await?;
|
||||||
|
// println!("Persistent keys:");
|
||||||
|
// res.into_iter()
|
||||||
|
// .for_each(|key| println!("{}", keys::to_hex(&key)));
|
||||||
|
// let res = db::list_ephemeral_keys(&pool).await?;
|
||||||
|
// println!("ephemeral keys:");
|
||||||
|
// res.into_iter()
|
||||||
|
// .for_each(|(e, p, t)| println!("{} {} {t}", keys::to_hex(&e), keys::to_hex(&p)))
|
||||||
|
// }
|
||||||
|
// Some(Command::Gen { seed }) => {
|
||||||
|
// let ekey = SigningKey::from_bytes(&[
|
||||||
|
// seed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// 0, 0, 0, 0, 0,
|
||||||
|
// ]);
|
||||||
|
// println!("Ephemeral signing key:");
|
||||||
|
// println!("{}", keys::signing_key_to_hex(&ekey));
|
||||||
|
// let res = db::list_persistent_keys(&pool).await?;
|
||||||
|
// let idx = (seed as usize) % res.len();
|
||||||
|
// let pkey = res.get(idx);
|
||||||
|
// match pkey {
|
||||||
|
// None => eprintln!("No persistent keys found"),
|
||||||
|
// Some(pkey) => {
|
||||||
|
// let now: usize = std::time::SystemTime::now()
|
||||||
|
// .duration_since(std::time::UNIX_EPOCH)
|
||||||
|
// .unwrap()
|
||||||
|
// .as_millis()
|
||||||
|
// .try_into()
|
||||||
|
// .unwrap();
|
||||||
|
// let twenty_mins_from_now = 20usize * 60 * 1000 + now;
|
||||||
|
// println!("Args");
|
||||||
|
// println!(
|
||||||
|
// "{} {} {}",
|
||||||
|
// keys::to_hex(&ekey.verifying_key()),
|
||||||
|
// keys::to_hex(pkey),
|
||||||
|
// twenty_mins_from_now
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Some(Command::Sign {
|
||||||
|
// signing_key,
|
||||||
|
// message,
|
||||||
|
// }) => {
|
||||||
|
// let mut skey = keys::signing_key_from_hex(&signing_key).unwrap();
|
||||||
|
// let msg = hex::decode(message).unwrap();
|
||||||
|
// let sig = keys::sign(&mut skey, msg);
|
||||||
|
// println!("{}", hex::encode(sig.to_bytes()))
|
||||||
|
// }
|
||||||
|
// Some(Command::Verify {
|
||||||
|
// verifying_key,
|
||||||
|
// message,
|
||||||
|
// signature,
|
||||||
|
// }) => {
|
||||||
|
// let vkey = keys::from_hex(&verifying_key).unwrap();
|
||||||
|
// let msg = hex::decode(message).unwrap();
|
||||||
|
// let sig = Signature::from_bytes(
|
||||||
|
// &TryInto::<[u8; 64]>::try_into(hex::decode(signature).unwrap()).unwrap(),
|
||||||
|
// );
|
||||||
|
// let res = keys::verify(&vkey, &msg, &sig).unwrap();
|
||||||
|
// println!("{:?}", res);
|
||||||
|
// }
|
||||||
None => {
|
None => {
|
||||||
println!("See help");
|
println!("See help");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,14 @@ use libp2p::{
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use cll2v0::{
|
use cll2v0::{
|
||||||
keys,
|
api, keys,
|
||||||
messages::{MyRequest, MyResult},
|
|
||||||
protocol::{mk_swarm, MyBehaviourEvent},
|
protocol::{mk_swarm, MyBehaviourEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
/// cll2v0 is a playground for rust libraries.
|
/// CLL2 client
|
||||||
/// This is signing service.
|
/// Configured to act as a unpersistant node.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(arg_required_else_help(true), version, about)]
|
#[command(arg_required_else_help(true), version, about)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
@ -100,11 +99,7 @@ pub async fn start(params: ClientParams) -> Result<(), Box<dyn Error>> {
|
||||||
Ok(Some(line)) = stdin.next_line() => {
|
Ok(Some(line)) = stdin.next_line() => {
|
||||||
println!("MESSAGE: {}", line);
|
println!("MESSAGE: {}", line);
|
||||||
let bytes = line.into_bytes();
|
let bytes = line.into_bytes();
|
||||||
let req = MyRequest {
|
let req = api::Req::Global(api::global::Req::Terms) ;
|
||||||
key: keypair.clone().public().to_bytes(),
|
|
||||||
body: bytes.clone(),
|
|
||||||
sig: keys::sign(&mut key.clone(), bytes).to_vec(),
|
|
||||||
};
|
|
||||||
println!("REQ : {:?}", req);
|
println!("REQ : {:?}", req);
|
||||||
swarm.behaviour_mut().req_res.send_request(
|
swarm.behaviour_mut().req_res.send_request(
|
||||||
&server_peer_id,
|
&server_peer_id,
|
||||||
|
@ -147,11 +142,11 @@ pub async fn start(params: ClientParams) -> Result<(), Box<dyn Error>> {
|
||||||
})) => {
|
})) => {
|
||||||
// println!("response : {:?} {:?} {:?}", peer, request_id, "" );
|
// println!("response : {:?} {:?} {:?}", peer, request_id, "" );
|
||||||
match response {
|
match response {
|
||||||
MyResult::Okay(response) => {
|
api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(terms))) => {
|
||||||
println!("OKAY : {}", hex::encode(response.sig.clone()),);
|
println!("TERMS :: OKAY : {:?}", terms);
|
||||||
}
|
}
|
||||||
MyResult::Fail(response) => {
|
res => {
|
||||||
println!("FAIL : {}", response);
|
println!("FAIL : {:?}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use ed25519_dalek::{ed25519::signature::SignerMut, Signature};
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use libp2p::{
|
use libp2p::{
|
||||||
mdns,
|
mdns,
|
||||||
|
@ -10,8 +9,7 @@ use libp2p::{
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use cll2v0::{
|
use cll2v0::{
|
||||||
db, keys,
|
api, config, keys,
|
||||||
messages::{MyRequest, MyResponse, MyResult},
|
|
||||||
protocol::{mk_swarm, MyBehaviourEvent},
|
protocol::{mk_swarm, MyBehaviourEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +32,9 @@ enum Command {
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
#[derive(clap::Args, Debug)]
|
||||||
pub struct ServerParams {
|
pub struct ServerParams {
|
||||||
|
/// Config file
|
||||||
|
#[arg(long, default_value = "./config.toml")]
|
||||||
|
pub config: String,
|
||||||
/// Server listening address
|
/// Server listening address
|
||||||
#[arg(long, default_value = "/ip4/0.0.0.0/tcp/52321")]
|
#[arg(long, default_value = "/ip4/0.0.0.0/tcp/52321")]
|
||||||
pub listen_on: String,
|
pub listen_on: String,
|
||||||
|
@ -60,9 +61,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
||||||
let ServerParams {
|
let ServerParams {
|
||||||
|
config: config_path,
|
||||||
skey: skey_hex,
|
skey: skey_hex,
|
||||||
listen_on,
|
listen_on,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
|
let c = config::Config::read(&config_path)?;
|
||||||
|
println!("CONFIG ");
|
||||||
|
println!("{:?}", c);
|
||||||
if true {
|
if true {
|
||||||
let _ = tracing_subscriber::fmt()
|
let _ = tracing_subscriber::fmt()
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
|
@ -72,7 +78,7 @@ pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
||||||
let peer_id = libp2p_identity::PublicKey::from(keypair.public()).to_peer_id();
|
let peer_id = libp2p_identity::PublicKey::from(keypair.public()).to_peer_id();
|
||||||
println!("PEER_ID : {}", peer_id);
|
println!("PEER_ID : {}", peer_id);
|
||||||
|
|
||||||
let (keychain, pool) = db::start_db().await?;
|
// let (keychain, pool) = db::start_db().await?;
|
||||||
|
|
||||||
let mut swarm = mk_swarm(&keypair.clone().into(), ProtocolSupport::Inbound)?;
|
let mut swarm = mk_swarm(&keypair.clone().into(), ProtocolSupport::Inbound)?;
|
||||||
swarm.listen_on(listen_on.parse()?)?;
|
swarm.listen_on(listen_on.parse()?)?;
|
||||||
|
@ -93,33 +99,18 @@ pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
||||||
request, channel, ..
|
request, channel, ..
|
||||||
},
|
},
|
||||||
})) => {
|
})) => {
|
||||||
// println!("Req : {:?} {:?} {:?}", peer, channel, hex::encode(request.body.clone()), );
|
let res = match request {
|
||||||
println!("REQSIG : {}", hex::encode(request.sig.clone()),);
|
api::Req::Global(api::global::Req::Terms) => {
|
||||||
let MyRequest { key, body, sig } = request;
|
api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(
|
||||||
|
api::global::terms::Terms::default(),
|
||||||
// FIXME :: MAP ERROR.
|
)))
|
||||||
let sig_arr: [u8; 64] = sig.clone().try_into().unwrap();
|
|
||||||
|
|
||||||
if let Ok(ekey) = keys::from_bytes(key.into()) {
|
|
||||||
if let Ok(_) = ekey.verify_strict(&body, &Signature::from_bytes(&sig_arr)) {
|
|
||||||
if let Ok(pkey) = db::get_persistent_key(&pool, &ekey).await {
|
|
||||||
if let Some(skey) = keychain.get(&pkey.to_bytes()) {
|
|
||||||
let _ = swarm.behaviour_mut().req_res.send_response(
|
|
||||||
channel,
|
|
||||||
MyResult::Okay(MyResponse {
|
|
||||||
sig: skey.clone().sign(&body).to_bytes().into(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!("err0")
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
println!("err1");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("err2");
|
|
||||||
}
|
}
|
||||||
}
|
api::Req::Channel => todo!("Not yet implemented"),
|
||||||
|
_ => api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(
|
||||||
|
api::global::terms::Terms::default(),
|
||||||
|
))),
|
||||||
|
};
|
||||||
|
let _ = swarm.behaviour_mut().req_res.send_response(channel, res);
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
println!("OTHER {:?}", e)
|
println!("OTHER {:?}", e)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
keys = ""
|
||||||
|
|
||||||
|
[api]
|
||||||
|
channel = false
|
||||||
|
|
||||||
|
[api.global.terms.Static]
|
||||||
|
currencies = [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], []]]
|
||||||
|
response_period = 86400000
|
||||||
|
additional_terms = """
|
||||||
|
There are no additional terms
|
||||||
|
"""
|
||||||
|
|
||||||
|
[api.global.terms.Static.initial_amounts]
|
||||||
|
min_funds = 0
|
||||||
|
min_gift = 0
|
||||||
|
|
||||||
|
[api.global.terms.Static.capacity]
|
||||||
|
max_count = 30
|
||||||
|
max_locked = 30
|
||||||
|
max_single = 1000000000
|
||||||
|
max_total = 5000000000
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod global;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Req {
|
||||||
|
Global(global::Req),
|
||||||
|
Channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Global(global::Res),
|
||||||
|
Channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
pub struct Config {
|
||||||
|
global: global::Config,
|
||||||
|
channel: bool,
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod common;
|
||||||
|
pub mod confer;
|
||||||
|
pub mod register;
|
||||||
|
pub mod terms;
|
||||||
|
pub mod watch;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
pub struct Config {
|
||||||
|
terms: terms::Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Req {
|
||||||
|
Terms,
|
||||||
|
Confer(confer::Req),
|
||||||
|
Watch(watch::Req),
|
||||||
|
Register(register::Req),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Terms(terms::Res),
|
||||||
|
Confer(confer::Res),
|
||||||
|
Watch(watch::Res),
|
||||||
|
Register(register::Res),
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct InitialAmounts {
|
||||||
|
pub min_funds: Amount,
|
||||||
|
pub min_gift: Amount,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InitialAmounts {
|
||||||
|
fn default() -> Self {
|
||||||
|
InitialAmounts {
|
||||||
|
min_funds: 0,
|
||||||
|
min_gift: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitialAmounts {
|
||||||
|
pub fn gte(&self, other: &Self) -> bool {
|
||||||
|
self.min_funds >= other.min_funds && self.min_gift >= other.min_gift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Capacity {
|
||||||
|
pub max_count: Natural,
|
||||||
|
pub max_locked: Natural,
|
||||||
|
pub max_single: Amount,
|
||||||
|
pub max_total: Amount,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Capacity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Capacity {
|
||||||
|
max_count: 30,
|
||||||
|
max_locked: 30,
|
||||||
|
max_single: E9,
|
||||||
|
max_total: 5 * E9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Capacity {
|
||||||
|
pub fn gte(&self, other: &Self) -> bool {
|
||||||
|
self.max_count >= other.max_count
|
||||||
|
&& self.max_locked >= other.max_locked
|
||||||
|
&& self.max_single >= other.max_single
|
||||||
|
&& self.max_total >= other.max_total
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::common::{Capacity, InitialAmounts};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Req {
|
||||||
|
pub currency: Currency,
|
||||||
|
pub initial_amounts: InitialAmounts,
|
||||||
|
pub response_period: Milliseconds,
|
||||||
|
pub capacity: Capacity,
|
||||||
|
pub key: VerificationKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Req {
|
||||||
|
/// A channel adhering to `self` will necessarily adhere to other
|
||||||
|
pub fn is_stricter(&self, other: &Self) -> bool {
|
||||||
|
&self.currency == &other.currency
|
||||||
|
&& self.initial_amounts.gte(&other.initial_amounts)
|
||||||
|
&& other.capacity.gte(&self.capacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Okay(VerificationKey),
|
||||||
|
Fail(Fail),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Fail {
|
||||||
|
Suggestion(Req),
|
||||||
|
Other(Option<Text>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn basic_bounds(min_terms: Req, proposed_terms: Req) -> Res {
|
||||||
|
// FIXME :: this is all faff.
|
||||||
|
//
|
||||||
|
// // Bad currency
|
||||||
|
// if min_terms.currency != proposed_terms.currency {
|
||||||
|
// return Res::Fail(ConferFail::Suggestion(Req {
|
||||||
|
// currency: min_terms.currency,
|
||||||
|
// ..proposed_terms
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
// // Bad initialAmount
|
||||||
|
// if !proposed_terms
|
||||||
|
// .initial_amounts
|
||||||
|
// .gte(&min_terms.initial_amounts)
|
||||||
|
// {
|
||||||
|
// return Res::Fail(Fail::Suggestion(Req {
|
||||||
|
// initial_amounts: min_terms.initial_amounts,
|
||||||
|
// ..proposed_terms
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
// // Bad response period
|
||||||
|
// if !proposed_terms
|
||||||
|
// .initial_amounts
|
||||||
|
// .gte(&min_terms.initial_amounts)
|
||||||
|
// {
|
||||||
|
// return Res::Fail(Fail::Suggestion(Req {
|
||||||
|
// initial_amounts: min_terms.initial_amounts,
|
||||||
|
// ..proposed_terms
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
if min_terms.is_stricter(&proposed_terms) {
|
||||||
|
Res::Okay(min_terms.key)
|
||||||
|
} else {
|
||||||
|
Res::Fail(Fail::Other(Some("See terms".to_string())))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct KeyCert {
|
||||||
|
pub l2_key: VerificationKey,
|
||||||
|
pub channel_id: ChannelId,
|
||||||
|
pub expire: Timestamp,
|
||||||
|
pub rank: Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Req {
|
||||||
|
pub my: Signed<KeyCert>,
|
||||||
|
pub your: Option<KeyCert>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Okay(Option<Signed<KeyCert>>),
|
||||||
|
Fail,
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::common::{Capacity, InitialAmounts};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Config {
|
||||||
|
Static(Terms),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config::Static(Terms::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Okay(Terms),
|
||||||
|
Fail,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Terms {
|
||||||
|
pub currencies: Vec<Currency>,
|
||||||
|
pub initial_amounts: InitialAmounts,
|
||||||
|
pub response_period: Milliseconds,
|
||||||
|
pub capacity: Capacity,
|
||||||
|
pub additional_terms: Markdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Terms {
|
||||||
|
fn default() -> Self {
|
||||||
|
Terms {
|
||||||
|
currencies: vec![ada_currency()],
|
||||||
|
initial_amounts: InitialAmounts::default(),
|
||||||
|
response_period: DAY,
|
||||||
|
capacity: Capacity::default(),
|
||||||
|
additional_terms: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Terms {
|
||||||
|
/// A channel adhering to `self` will necessarily adhere to other
|
||||||
|
pub fn is_stricter(&self, other: &Self) -> bool {
|
||||||
|
is_sublist(&self.currencies, &other.currencies)
|
||||||
|
&& self.initial_amounts.gte(&other.initial_amounts)
|
||||||
|
&& other.capacity.gte(&self.capacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_sublist<T>(a: &Vec<T>, b: &Vec<T>) -> bool
|
||||||
|
where
|
||||||
|
T: Eq,
|
||||||
|
{
|
||||||
|
a.iter().all(|x| b.iter().any(|y| x == y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_handle(terms: Terms) -> impl Fn() -> Res {
|
||||||
|
move || Res::Okay(terms.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handler(c: Config) -> impl Fn() -> Res {
|
||||||
|
match c {
|
||||||
|
Config::Static(terms) => static_handle(terms),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Req {
|
||||||
|
pub l1_key: VerificationKey,
|
||||||
|
pub transaction_id: TransactionId,
|
||||||
|
pub output_index: Natural,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Res {
|
||||||
|
Watching,
|
||||||
|
Confirming,
|
||||||
|
Rejected(RejectedMessage),
|
||||||
|
Confirmed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum RejectedMessage {
|
||||||
|
Timeout,
|
||||||
|
FailsTerms,
|
||||||
|
BadKey,
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock response
|
||||||
|
pub fn just_confirm(_req: Req) -> Res {
|
||||||
|
Res::Confirmed
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
// I do not know the idiomatic rust way to go from vectors to fixed length arrays
|
||||||
|
|
||||||
pub fn arr32(b: Vec<u8>) -> anyhow::Result<[u8; 32]> {
|
pub fn arr32(b: Vec<u8>) -> anyhow::Result<[u8; 32]> {
|
||||||
println!("{}", b.len());
|
println!("{}", b.len());
|
||||||
b.try_into().map_err(|_| anyhow::anyhow!("bad input"))
|
b.try_into().map_err(|_| anyhow::anyhow!("bad input"))
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::Read;
|
||||||
|
use toml;
|
||||||
|
|
||||||
|
use crate::api;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub keys: String,
|
||||||
|
pub api: api::Config,
|
||||||
|
// db: db::Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn read(fp: &str) -> Result<Self> {
|
||||||
|
let mut file = std::fs::File::open(fp)?;
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)?;
|
||||||
|
let c: Config = toml::from_str(&contents)?;
|
||||||
|
Ok(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> Result<String> {
|
||||||
|
let x = toml::to_string(&self)?;
|
||||||
|
Ok(x)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
pub mod arr;
|
pub mod arr;
|
||||||
pub mod db;
|
pub mod prelude;
|
||||||
|
// pub mod db;
|
||||||
|
pub mod api;
|
||||||
|
pub mod config;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub mod messages;
|
|
||||||
//pub mod protobuf;
|
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct MyRequest {
|
|
||||||
pub key: [u8; 32],
|
|
||||||
pub body: Vec<u8>,
|
|
||||||
pub sig: Vec<u8>, // FIXME :: fixed length array not supported by serde [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum MyResult {
|
|
||||||
Okay(MyResponse),
|
|
||||||
Fail(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct MyResponse {
|
|
||||||
// sig: [u8; 64],
|
|
||||||
pub sig: Vec<u8>, // FIXME :: fixed length array not supported by serde [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
// type TestnetMagic = i64;
|
|
||||||
//
|
|
||||||
// pub enum NetworkId {
|
|
||||||
// Mainnet,
|
|
||||||
// Testnet(TestnetMagic),
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// type Version = i64;
|
|
||||||
//
|
|
||||||
// type Blake2b224Hash = [u8; 24];
|
|
||||||
// type ScriptHash = Blake2b224Hash;
|
|
||||||
//
|
|
||||||
// type TokenName = Vec<u8>;
|
|
||||||
// type Currency = (ScriptHash, TokenName);
|
|
||||||
//
|
|
||||||
// pub struct Init {
|
|
||||||
// version: Version,
|
|
||||||
// network_id: NetworkId,
|
|
||||||
// currencies: Vec<Currency>,
|
|
||||||
// routing: bool,
|
|
||||||
// htlc: bool,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// type Positive = i64;
|
|
||||||
// type Natural = i64;
|
|
||||||
// type Amount = Natural;
|
|
||||||
// type Markdown = String;
|
|
||||||
// type Milliseconds = Natural;
|
|
||||||
//
|
|
||||||
// type ChannelId = Vec<u8>;
|
|
||||||
//
|
|
||||||
// // type ChannelMessage<msg> = (ChannelId, msg)
|
|
||||||
//
|
|
||||||
// type VerificationKey = [u8; 32];
|
|
||||||
//
|
|
||||||
// pub struct Open {
|
|
||||||
// currency: Currency,
|
|
||||||
// funding_amount: Amount,
|
|
||||||
// gift_amount: Amount,
|
|
||||||
// respond_period: Milliseconds,
|
|
||||||
// minimum_depth: Positive,
|
|
||||||
// max_htlc_amount: Amount,
|
|
||||||
// max_total_htlc_amount: Amount,
|
|
||||||
// max_htlc_count: Positive,
|
|
||||||
// verification_key: VerificationKey,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub struct Accept {
|
|
||||||
// respond_period: Milliseconds,
|
|
||||||
// minimum_depth: Positive,
|
|
||||||
// min_htlc_amount: Amount,
|
|
||||||
// max_htlc_amount: Amount,
|
|
||||||
// max_total_htlc_amount: Amount,
|
|
||||||
// max_htlc_count: Positive,
|
|
||||||
// verification_key: VerificationKey,
|
|
||||||
// }
|
|
301
src/ping.rs
301
src/ping.rs
|
@ -1,301 +0,0 @@
|
||||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
// copy of this software and associated documentation files (the "Software"),
|
|
||||||
// to deal in the Software without restriction, including without limitation
|
|
||||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
// and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
//! Integration tests for the `Behaviour`.
|
|
||||||
|
|
||||||
use std::{io, iter};
|
|
||||||
|
|
||||||
use futures::prelude::*;
|
|
||||||
use libp2p::identity::PeerId;
|
|
||||||
use libp2p::request_response;
|
|
||||||
use libp2p::request_response::ProtocolSupport;
|
|
||||||
use libp2p::swarm::{StreamProtocol, Swarm, SwarmEvent};
|
|
||||||
// use libp2p::swarm_test::SwarmExt;
|
|
||||||
use rand::Rng;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tracing_subscriber::EnvFilter;
|
|
||||||
|
|
||||||
async fn is_response_outbound() {
|
|
||||||
let _ = tracing_subscriber::fmt()
|
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
|
||||||
.try_init();
|
|
||||||
let ping = Ping("ping".to_string().into_bytes());
|
|
||||||
let offline_peer = PeerId::random();
|
|
||||||
|
|
||||||
let mut swarm1 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(
|
|
||||||
[(
|
|
||||||
StreamProtocol::new("/ping/1"),
|
|
||||||
request_response::ProtocolSupport::Full,
|
|
||||||
)],
|
|
||||||
request_response::Config::default(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let request_id1 = swarm1
|
|
||||||
.behaviour_mut()
|
|
||||||
.send_request(&offline_peer, ping.clone());
|
|
||||||
|
|
||||||
match swarm1
|
|
||||||
.next_swarm_event()
|
|
||||||
.await
|
|
||||||
.try_into_behaviour_event()
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
request_response::Event::OutboundFailure {
|
|
||||||
peer,
|
|
||||||
request_id: req_id,
|
|
||||||
error: _error,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
assert_eq!(&offline_peer, &peer);
|
|
||||||
assert_eq!(req_id, request_id1);
|
|
||||||
}
|
|
||||||
e => panic!("Peer: Unexpected event: {e:?}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let request_id2 = swarm1.behaviour_mut().send_request(&offline_peer, ping);
|
|
||||||
|
|
||||||
assert!(!swarm1
|
|
||||||
.behaviour()
|
|
||||||
.is_pending_outbound(&offline_peer, &request_id1));
|
|
||||||
assert!(swarm1
|
|
||||||
.behaviour()
|
|
||||||
.is_pending_outbound(&offline_peer, &request_id2));
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn ping_protocol() {
|
|
||||||
let ping = Ping("ping".to_string().into_bytes());
|
|
||||||
let pong = Pong("pong".to_string().into_bytes());
|
|
||||||
|
|
||||||
let protocols = iter::once((StreamProtocol::new("/ping/1"), ProtocolSupport::Full));
|
|
||||||
let cfg = request_response::Config::default();
|
|
||||||
|
|
||||||
let mut swarm1 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols.clone(), cfg.clone())
|
|
||||||
});
|
|
||||||
let peer1_id = *swarm1.local_peer_id();
|
|
||||||
let mut swarm2 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols, cfg)
|
|
||||||
});
|
|
||||||
let peer2_id = *swarm2.local_peer_id();
|
|
||||||
|
|
||||||
swarm1.listen().with_memory_addr_external().await;
|
|
||||||
swarm2.connect(&mut swarm1).await;
|
|
||||||
|
|
||||||
let expected_ping = ping.clone();
|
|
||||||
let expected_pong = pong.clone();
|
|
||||||
|
|
||||||
let peer1 = async move {
|
|
||||||
loop {
|
|
||||||
match swarm1.next_swarm_event().await.try_into_behaviour_event() {
|
|
||||||
Ok(request_response::Event::Message {
|
|
||||||
peer,
|
|
||||||
message:
|
|
||||||
request_response::Message::Request {
|
|
||||||
request, channel, ..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
assert_eq!(&request, &expected_ping);
|
|
||||||
assert_eq!(&peer, &peer2_id);
|
|
||||||
swarm1
|
|
||||||
.behaviour_mut()
|
|
||||||
.send_response(channel, pong.clone())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Ok(request_response::Event::ResponseSent { peer, .. }) => {
|
|
||||||
assert_eq!(&peer, &peer2_id);
|
|
||||||
}
|
|
||||||
Ok(e) => {
|
|
||||||
panic!("Peer1: Unexpected event: {e:?}")
|
|
||||||
}
|
|
||||||
Err(..) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let num_pings: u8 = rand::thread_rng().gen_range(1..100);
|
|
||||||
|
|
||||||
let peer2 = async {
|
|
||||||
let mut count = 0;
|
|
||||||
|
|
||||||
let mut req_id = swarm2.behaviour_mut().send_request(&peer1_id, ping.clone());
|
|
||||||
assert!(swarm2.behaviour().is_pending_outbound(&peer1_id, &req_id));
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match swarm2
|
|
||||||
.next_swarm_event()
|
|
||||||
.await
|
|
||||||
.try_into_behaviour_event()
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
request_response::Event::Message {
|
|
||||||
peer,
|
|
||||||
message:
|
|
||||||
request_response::Message::Response {
|
|
||||||
request_id,
|
|
||||||
response,
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
count += 1;
|
|
||||||
assert_eq!(&response, &expected_pong);
|
|
||||||
assert_eq!(&peer, &peer1_id);
|
|
||||||
assert_eq!(req_id, request_id);
|
|
||||||
if count >= num_pings {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
req_id = swarm2.behaviour_mut().send_request(&peer1_id, ping.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e => panic!("Peer2: Unexpected event: {e:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async_std::task::spawn(Box::pin(peer1));
|
|
||||||
peer2.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn emits_inbound_connection_closed_failure() {
|
|
||||||
let ping = Ping("ping".to_string().into_bytes());
|
|
||||||
|
|
||||||
let protocols = iter::once((StreamProtocol::new("/ping/1"), ProtocolSupport::Full));
|
|
||||||
let cfg = request_response::Config::default();
|
|
||||||
|
|
||||||
let mut swarm1 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols.clone(), cfg.clone())
|
|
||||||
});
|
|
||||||
let peer1_id = *swarm1.local_peer_id();
|
|
||||||
let mut swarm2 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols, cfg)
|
|
||||||
});
|
|
||||||
let peer2_id = *swarm2.local_peer_id();
|
|
||||||
|
|
||||||
swarm1.listen().with_memory_addr_external().await;
|
|
||||||
swarm2.connect(&mut swarm1).await;
|
|
||||||
|
|
||||||
swarm2.behaviour_mut().send_request(&peer1_id, ping.clone());
|
|
||||||
|
|
||||||
// Wait for swarm 1 to receive request by swarm 2.
|
|
||||||
let _channel = loop {
|
|
||||||
futures::select!(
|
|
||||||
event = swarm1.select_next_some() => match event {
|
|
||||||
SwarmEvent::Behaviour(request_response::Event::Message {
|
|
||||||
peer,
|
|
||||||
message: request_response::Message::Request { request, channel, .. },
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
assert_eq!(&request, &ping);
|
|
||||||
assert_eq!(&peer, &peer2_id);
|
|
||||||
break channel;
|
|
||||||
},
|
|
||||||
SwarmEvent::Behaviour(ev) => panic!("Peer1: Unexpected event: {ev:?}"),
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
event = swarm2.select_next_some() => {
|
|
||||||
if let SwarmEvent::Behaviour(ev) = event {
|
|
||||||
panic!("Peer2: Unexpected event: {ev:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drop swarm 2 in order for the connection between swarm 1 and 2 to close.
|
|
||||||
drop(swarm2);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match swarm1.select_next_some().await {
|
|
||||||
SwarmEvent::Behaviour(request_response::Event::InboundFailure {
|
|
||||||
error: request_response::InboundFailure::ConnectionClosed,
|
|
||||||
..
|
|
||||||
}) => break,
|
|
||||||
SwarmEvent::Behaviour(e) => panic!("Peer1: Unexpected event: {e:?}"),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We expect the substream to be properly closed when response channel is dropped.
|
|
||||||
/// Since the ping protocol used here expects a response, the sender considers this
|
|
||||||
/// early close as a protocol violation which results in the connection being closed.
|
|
||||||
/// If the substream were not properly closed when dropped, the sender would instead
|
|
||||||
/// run into a timeout waiting for the response.
|
|
||||||
async fn emits_inbound_connection_closed_if_channel_is_dropped() {
|
|
||||||
let ping = Ping("ping".to_string().into_bytes());
|
|
||||||
|
|
||||||
let protocols = iter::once((StreamProtocol::new("/ping/1"), ProtocolSupport::Full));
|
|
||||||
let cfg = request_response::Config::default();
|
|
||||||
|
|
||||||
let mut swarm1 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols.clone(), cfg.clone())
|
|
||||||
});
|
|
||||||
let peer1_id = *swarm1.local_peer_id();
|
|
||||||
let mut swarm2 = Swarm::new_ephemeral(|_| {
|
|
||||||
request_response::cbor::Behaviour::<Ping, Pong>::new(protocols, cfg)
|
|
||||||
});
|
|
||||||
let peer2_id = *swarm2.local_peer_id();
|
|
||||||
|
|
||||||
swarm1.listen().with_memory_addr_external().await;
|
|
||||||
swarm2.connect(&mut swarm1).await;
|
|
||||||
|
|
||||||
swarm2.behaviour_mut().send_request(&peer1_id, ping.clone());
|
|
||||||
|
|
||||||
// Wait for swarm 1 to receive request by swarm 2.
|
|
||||||
let event = loop {
|
|
||||||
futures::select!(
|
|
||||||
event = swarm1.select_next_some() => {
|
|
||||||
if let SwarmEvent::Behaviour(request_response::Event::Message {
|
|
||||||
peer,
|
|
||||||
message: request_response::Message::Request { request, channel, .. },
|
|
||||||
..
|
|
||||||
}) = event {
|
|
||||||
assert_eq!(&request, &ping);
|
|
||||||
assert_eq!(&peer, &peer2_id);
|
|
||||||
|
|
||||||
drop(channel);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
event = swarm2.select_next_some() => {
|
|
||||||
if let SwarmEvent::Behaviour(ev) = event {
|
|
||||||
break ev;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let error = match event {
|
|
||||||
request_response::Event::OutboundFailure { error, .. } => error,
|
|
||||||
e => panic!("unexpected event from peer 2: {e:?}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
error,
|
|
||||||
request_response::OutboundFailure::Io(e) if e.kind() == io::ErrorKind::UnexpectedEof,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple Ping-Pong Protocol
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
struct Ping(Vec<u8>);
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
struct Pong(Vec<u8>);
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Use integer incase we switch to i128 or otherwise.
|
||||||
|
pub type Integer = i64;
|
||||||
|
|
||||||
|
/// Non negative integer including 0
|
||||||
|
pub type Natural = u64;
|
||||||
|
|
||||||
|
/// Suggestive of a positive integer. Note that this is not enforced at a type level.
|
||||||
|
pub type Positive = u64;
|
||||||
|
|
||||||
|
/// A natural representing and `amount of currency`
|
||||||
|
pub type Amount = Natural;
|
||||||
|
|
||||||
|
/// A natural representing an index of, say, a cheque or utxo.
|
||||||
|
pub type Index = Natural;
|
||||||
|
|
||||||
|
/// A time length in milliseconds
|
||||||
|
pub type Milliseconds = Integer;
|
||||||
|
|
||||||
|
/// Milliseconds since Epoch
|
||||||
|
pub type Timestamp = Natural;
|
||||||
|
|
||||||
|
// Alias for bytes
|
||||||
|
pub type Blake2b256Hash = [u8; 32];
|
||||||
|
pub type Blake2b224Hash = [u8; 28];
|
||||||
|
pub type KeyHash = Blake2b224Hash;
|
||||||
|
pub type ScriptHash = Blake2b224Hash;
|
||||||
|
pub type TransactionId = Blake2b256Hash;
|
||||||
|
pub type AssetName = Vec<u8>;
|
||||||
|
pub type VerificationKey = [u8; 32];
|
||||||
|
pub type Signature = [u8; 64];
|
||||||
|
pub type ChannelId = [u8; 28];
|
||||||
|
|
||||||
|
pub type Currency = (ScriptHash, AssetName);
|
||||||
|
|
||||||
|
/// To keep types consistent we denote the script hash of ada as `[0;28]`.
|
||||||
|
pub fn ada_currency() -> Currency {
|
||||||
|
([0; 28], [0_u8; 0].to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Markdown = String;
|
||||||
|
pub type Text = String;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Signed<T> {
|
||||||
|
body: T,
|
||||||
|
#[serde(with = "serde_arrays")]
|
||||||
|
signature: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const E3: Natural = 1000 as Natural;
|
||||||
|
pub const E6: Natural = E3 * E3;
|
||||||
|
pub const E9: Natural = E6 * E3;
|
||||||
|
pub const E12: Natural = E9 * E3;
|
||||||
|
|
||||||
|
pub const SECOND: Milliseconds = 1000;
|
||||||
|
pub const MINUTE: Milliseconds = 60 * SECOND;
|
||||||
|
pub const HOUR: Milliseconds = 60 * MINUTE;
|
||||||
|
pub const DAY: Milliseconds = 24 * HOUR;
|
||||||
|
|
||||||
|
pub fn time_add(a: Timestamp, b: Milliseconds) -> Result<Timestamp> {
|
||||||
|
let x = Timestamp::try_from(a as i64 + b)?;
|
||||||
|
Ok(x)
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
syntax="proto3";
|
|
||||||
package messages;
|
|
||||||
|
|
||||||
message SignRequest {
|
|
||||||
bytes vkey = 1;
|
|
||||||
bytes body = 2;
|
|
||||||
bytes sig = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SignResponse {
|
|
||||||
oneof result {
|
|
||||||
Okay okay = 1;
|
|
||||||
Fail fail = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message Okay {
|
|
||||||
bytes sig = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Fail {
|
|
||||||
UnrecognisedKey = 1;
|
|
||||||
ExpiredKey = 2;
|
|
||||||
Other = 3;
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
// Automatically generated rust module for 'messages.proto' file
|
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(unknown_lints)]
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
|
||||||
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
|
|
||||||
use quick_protobuf::sizeofs::*;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum Fail {
|
|
||||||
UnrecognisedKey = 1,
|
|
||||||
ExpiredKey = 2,
|
|
||||||
Other = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Fail {
|
|
||||||
fn default() -> Self {
|
|
||||||
Fail::UnrecognisedKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i32> for Fail {
|
|
||||||
fn from(i: i32) -> Self {
|
|
||||||
match i {
|
|
||||||
1 => Fail::UnrecognisedKey,
|
|
||||||
2 => Fail::ExpiredKey,
|
|
||||||
3 => Fail::Other,
|
|
||||||
_ => Self::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for Fail {
|
|
||||||
fn from(s: &'a str) -> Self {
|
|
||||||
match s {
|
|
||||||
"UnrecognisedKey" => Fail::UnrecognisedKey,
|
|
||||||
"ExpiredKey" => Fail::ExpiredKey,
|
|
||||||
"Other" => Fail::Other,
|
|
||||||
_ => Self::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
|
||||||
pub struct SignRequest<'a> {
|
|
||||||
pub vkey: Cow<'a, [u8]>,
|
|
||||||
pub body: Cow<'a, [u8]>,
|
|
||||||
pub sig: Cow<'a, [u8]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageRead<'a> for SignRequest<'a> {
|
|
||||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
|
||||||
let mut msg = Self::default();
|
|
||||||
while !r.is_eof() {
|
|
||||||
match r.next_tag(bytes) {
|
|
||||||
Ok(10) => msg.vkey = r.read_bytes(bytes).map(Cow::Borrowed)?,
|
|
||||||
Ok(18) => msg.body = r.read_bytes(bytes).map(Cow::Borrowed)?,
|
|
||||||
Ok(26) => msg.sig = r.read_bytes(bytes).map(Cow::Borrowed)?,
|
|
||||||
Ok(t) => { r.read_unknown(bytes, t)?; }
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageWrite for SignRequest<'a> {
|
|
||||||
fn get_size(&self) -> usize {
|
|
||||||
0
|
|
||||||
+ if self.vkey == Cow::Borrowed(b"") { 0 } else { 1 + sizeof_len((&self.vkey).len()) }
|
|
||||||
+ if self.body == Cow::Borrowed(b"") { 0 } else { 1 + sizeof_len((&self.body).len()) }
|
|
||||||
+ if self.sig == Cow::Borrowed(b"") { 0 } else { 1 + sizeof_len((&self.sig).len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
|
||||||
if self.vkey != Cow::Borrowed(b"") { w.write_with_tag(10, |w| w.write_bytes(&**&self.vkey))?; }
|
|
||||||
if self.body != Cow::Borrowed(b"") { w.write_with_tag(18, |w| w.write_bytes(&**&self.body))?; }
|
|
||||||
if self.sig != Cow::Borrowed(b"") { w.write_with_tag(26, |w| w.write_bytes(&**&self.sig))?; }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
|
||||||
pub struct SignResponse<'a> {
|
|
||||||
pub result: messages::mod_SignResponse::OneOfresult<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageRead<'a> for SignResponse<'a> {
|
|
||||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
|
||||||
let mut msg = Self::default();
|
|
||||||
while !r.is_eof() {
|
|
||||||
match r.next_tag(bytes) {
|
|
||||||
Ok(10) => msg.result = messages::mod_SignResponse::OneOfresult::okay(r.read_message::<messages::Okay>(bytes)?),
|
|
||||||
Ok(16) => msg.result = messages::mod_SignResponse::OneOfresult::fail(r.read_enum(bytes)?),
|
|
||||||
Ok(t) => { r.read_unknown(bytes, t)?; }
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageWrite for SignResponse<'a> {
|
|
||||||
fn get_size(&self) -> usize {
|
|
||||||
0
|
|
||||||
+ match self.result {
|
|
||||||
messages::mod_SignResponse::OneOfresult::okay(ref m) => 1 + sizeof_len((m).get_size()),
|
|
||||||
messages::mod_SignResponse::OneOfresult::fail(ref m) => 1 + sizeof_varint(*(m) as u64),
|
|
||||||
messages::mod_SignResponse::OneOfresult::None => 0,
|
|
||||||
} }
|
|
||||||
|
|
||||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
|
||||||
match self.result { messages::mod_SignResponse::OneOfresult::okay(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? },
|
|
||||||
messages::mod_SignResponse::OneOfresult::fail(ref m) => { w.write_with_tag(16, |w| w.write_enum(*m as i32))? },
|
|
||||||
messages::mod_SignResponse::OneOfresult::None => {},
|
|
||||||
} Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod mod_SignResponse {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum OneOfresult<'a> {
|
|
||||||
okay(messages::Okay<'a>),
|
|
||||||
fail(messages::Fail),
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Default for OneOfresult<'a> {
|
|
||||||
fn default() -> Self {
|
|
||||||
OneOfresult::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
|
||||||
pub struct Okay<'a> {
|
|
||||||
pub sig: Cow<'a, [u8]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageRead<'a> for Okay<'a> {
|
|
||||||
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
|
||||||
let mut msg = Self::default();
|
|
||||||
while !r.is_eof() {
|
|
||||||
match r.next_tag(bytes) {
|
|
||||||
Ok(10) => msg.sig = r.read_bytes(bytes).map(Cow::Borrowed)?,
|
|
||||||
Ok(t) => { r.read_unknown(bytes, t)?; }
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MessageWrite for Okay<'a> {
|
|
||||||
fn get_size(&self) -> usize {
|
|
||||||
0
|
|
||||||
+ if self.sig == Cow::Borrowed(b"") { 0 } else { 1 + sizeof_len((&self.sig).len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
|
||||||
if self.sig != Cow::Borrowed(b"") { w.write_with_tag(10, |w| w.write_bytes(&**&self.sig))?; }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
// Automatically generated mod.rs
|
|
||||||
pub mod messages;
|
|
|
@ -8,12 +8,12 @@ use libp2p::{
|
||||||
};
|
};
|
||||||
use libp2p_identity::ed25519::Keypair;
|
use libp2p_identity::ed25519::Keypair;
|
||||||
|
|
||||||
use crate::messages::{MyRequest, MyResult};
|
use crate::api::{Req, Res};
|
||||||
|
|
||||||
#[derive(NetworkBehaviour)]
|
#[derive(NetworkBehaviour)]
|
||||||
pub struct MyBehaviour {
|
pub struct MyBehaviour {
|
||||||
pub mdns: mdns::tokio::Behaviour,
|
pub mdns: mdns::tokio::Behaviour,
|
||||||
pub req_res: request_response::cbor::Behaviour<MyRequest, MyResult>,
|
pub req_res: request_response::cbor::Behaviour<Req, Res>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_swarm(
|
pub fn mk_swarm(
|
||||||
|
@ -30,10 +30,9 @@ pub fn mk_swarm(
|
||||||
.with_behaviour(|key| {
|
.with_behaviour(|key| {
|
||||||
let mdns =
|
let mdns =
|
||||||
mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id())?;
|
mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id())?;
|
||||||
let protocol = [(StreamProtocol::new("/sign-me/1"), protocol_support)];
|
let protocol = [(StreamProtocol::new("/cll2p/1"), protocol_support)];
|
||||||
let config = request_response::Config::default();
|
let config = request_response::Config::default();
|
||||||
let req_res =
|
let req_res = request_response::cbor::Behaviour::<Req, Res>::new(protocol, config);
|
||||||
request_response::cbor::Behaviour::<MyRequest, MyResult>::new(protocol, config);
|
|
||||||
Ok(MyBehaviour { req_res, mdns })
|
Ok(MyBehaviour { req_res, mdns })
|
||||||
})?
|
})?
|
||||||
.with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(u64::MAX)))
|
.with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(u64::MAX)))
|
||||||
|
|
Loading…
Reference in New Issue