wip
This commit is contained in:
parent
4183fe6ec8
commit
aca2b9e8d5
|
@ -485,8 +485,10 @@ dependencies = [
|
|||
"quick-protobuf",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_arrays",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
@ -3055,6 +3057,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
|
@ -3078,6 +3089,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
|
@ -3682,6 +3702,40 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
|
@ -4204,6 +4258,15 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
|
|
@ -33,7 +33,9 @@ owo-colors = "4.1.0"
|
|||
quick-protobuf = "0.8.1"
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
serde_arrays = "0.1.0"
|
||||
sqlx = { version = "0.8.2", features = ["sqlite", "macros", "runtime-tokio"] }
|
||||
tokio = { version = "1.40.0", features = ["full", "tracing"] }
|
||||
toml = "0.8.20"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
|
187
README.md
187
README.md
|
@ -1,9 +1,15 @@
|
|||
# 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
|
||||
establish our key dependencies.
|
||||
This is a demo of a CL node that is able to:
|
||||
|
||||
- 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
|
||||
|
||||
|
@ -21,158 +27,35 @@ Run binary `x`:
|
|||
cargo run --bin <x>
|
||||
```
|
||||
|
||||
There are three binaries:
|
||||
## Current stack
|
||||
|
||||
- `admin` - admin controls: create, add, revoke ephemeral keys
|
||||
- `server` - run the signing server
|
||||
- `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.
|
||||
- Comms stack : rust-libp2p
|
||||
- Persistent storage : sqlx with sqlite
|
||||
|
||||
## Design
|
||||
|
||||
### Data
|
||||
See the design docs.
|
||||
|
||||
#### Persistent keys
|
||||
## TODO
|
||||
|
||||
These are the signing keys available to the machine and found at startup of the
|
||||
service.
|
||||
|
||||
#### Ephemeral keys
|
||||
|
||||
Perhaps also called proxy keys.
|
||||
|
||||
An ephemeral key consists of the following properties:
|
||||
|
||||
1. Verification key
|
||||
1. Persistent key
|
||||
1. Expires at
|
||||
1. Signature
|
||||
|
||||
### Signing Server Actions
|
||||
|
||||
#### Add
|
||||
|
||||
An add is performed by "admin" to create new ephemeral keys.
|
||||
|
||||
An `add` request has the following fields
|
||||
|
||||
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
|
||||
- [ ] api
|
||||
- [ ] global
|
||||
- [ ] terms
|
||||
- [x] simple
|
||||
- [ ] confer
|
||||
- [x] simple
|
||||
- [ ] watch
|
||||
- [ ] register
|
||||
- [ ] channel
|
||||
- [ ] UNPACK
|
||||
- [ ] config
|
||||
- [ ] UNPACK
|
||||
- [ ] storage
|
||||
- [ ] ephemeral
|
||||
- [ ] UNPACK
|
||||
- [ ] persistent
|
||||
- [ ] UNPACK
|
||||
- [ ] l1
|
||||
- [ ] Mock
|
||||
- [ ] UNPACK
|
||||
- [ ] Via "kupmios"
|
||||
|
|
216
app/admin.rs
216
app/admin.rs
|
@ -1,9 +1,10 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use ed25519_dalek::{Signature, SigningKey};
|
||||
// use ed25519_dalek::{Signature, SigningKey};
|
||||
|
||||
use cll2v0::{
|
||||
db,
|
||||
keys::{self},
|
||||
// db,
|
||||
config,
|
||||
// keys::{self}
|
||||
};
|
||||
|
||||
/// cll2v0 is a playground for rust libraries.
|
||||
|
@ -17,117 +18,122 @@ struct Args {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
/// Add new ephemeral key to an existing persistent key
|
||||
Add {
|
||||
ephemeral_key: String,
|
||||
persistent_key: String,
|
||||
expires_at: i64,
|
||||
},
|
||||
/// Revoke an existing ephemeral key
|
||||
Revoke { ephemeral_key: String },
|
||||
/// Check persistent keys are available and display
|
||||
Check,
|
||||
/// Gen from seed some sensible ephemeral key args to be used with `add`
|
||||
Gen { seed: u8 },
|
||||
/// Sign a message
|
||||
Sign {
|
||||
signing_key: String,
|
||||
message: String,
|
||||
},
|
||||
/// Verify a message
|
||||
Verify {
|
||||
verifying_key: String,
|
||||
message: String,
|
||||
signature: String,
|
||||
},
|
||||
/// Generate a default config
|
||||
GenConfig, // /// Add new ephemeral key to an existing persistent key
|
||||
// Add {
|
||||
// ephemeral_key: String,
|
||||
// persistent_key: String,
|
||||
// expires_at: i64,
|
||||
// },
|
||||
// /// Revoke an existing ephemeral key
|
||||
// Revoke { ephemeral_key: String },
|
||||
// /// Check persistent keys are available and display
|
||||
// Check,
|
||||
// /// Gen from seed some sensible ephemeral key args to be used with `add`
|
||||
// Gen { seed: u8 },
|
||||
// /// Sign a message
|
||||
// Sign {
|
||||
// signing_key: String,
|
||||
// message: String,
|
||||
// },
|
||||
// /// Verify a message
|
||||
// Verify {
|
||||
// verifying_key: String,
|
||||
// message: String,
|
||||
// signature: String,
|
||||
// },
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let (_, pool) = db::start_db().await?;
|
||||
// let (_, pool) = db::start_db().await?;
|
||||
|
||||
match args.cmd {
|
||||
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);
|
||||
Some(Command::GenConfig) => {
|
||||
let c = config::Config::default().to_string()?;
|
||||
println!("{}", c);
|
||||
}
|
||||
// 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 => {
|
||||
println!("See help");
|
||||
}
|
||||
|
|
|
@ -10,15 +10,14 @@ use libp2p::{
|
|||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use cll2v0::{
|
||||
keys,
|
||||
messages::{MyRequest, MyResult},
|
||||
api, keys,
|
||||
protocol::{mk_swarm, MyBehaviourEvent},
|
||||
};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
/// cll2v0 is a playground for rust libraries.
|
||||
/// This is signing service.
|
||||
/// CLL2 client
|
||||
/// Configured to act as a unpersistant node.
|
||||
#[derive(Parser)]
|
||||
#[command(arg_required_else_help(true), version, about)]
|
||||
struct Args {
|
||||
|
@ -100,11 +99,7 @@ pub async fn start(params: ClientParams) -> Result<(), Box<dyn Error>> {
|
|||
Ok(Some(line)) = stdin.next_line() => {
|
||||
println!("MESSAGE: {}", line);
|
||||
let bytes = line.into_bytes();
|
||||
let req = MyRequest {
|
||||
key: keypair.clone().public().to_bytes(),
|
||||
body: bytes.clone(),
|
||||
sig: keys::sign(&mut key.clone(), bytes).to_vec(),
|
||||
};
|
||||
let req = api::Req::Global(api::global::Req::Terms) ;
|
||||
println!("REQ : {:?}", req);
|
||||
swarm.behaviour_mut().req_res.send_request(
|
||||
&server_peer_id,
|
||||
|
@ -147,11 +142,11 @@ pub async fn start(params: ClientParams) -> Result<(), Box<dyn Error>> {
|
|||
})) => {
|
||||
// println!("response : {:?} {:?} {:?}", peer, request_id, "" );
|
||||
match response {
|
||||
MyResult::Okay(response) => {
|
||||
println!("OKAY : {}", hex::encode(response.sig.clone()),);
|
||||
api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(terms))) => {
|
||||
println!("TERMS :: OKAY : {:?}", terms);
|
||||
}
|
||||
MyResult::Fail(response) => {
|
||||
println!("FAIL : {}", response);
|
||||
res => {
|
||||
println!("FAIL : {:?}", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::error::Error;
|
||||
|
||||
use ed25519_dalek::{ed25519::signature::SignerMut, Signature};
|
||||
use futures::prelude::*;
|
||||
use libp2p::{
|
||||
mdns,
|
||||
|
@ -10,8 +9,7 @@ use libp2p::{
|
|||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use cll2v0::{
|
||||
db, keys,
|
||||
messages::{MyRequest, MyResponse, MyResult},
|
||||
api, config, keys,
|
||||
protocol::{mk_swarm, MyBehaviourEvent},
|
||||
};
|
||||
|
||||
|
@ -34,6 +32,9 @@ enum Command {
|
|||
|
||||
#[derive(clap::Args, Debug)]
|
||||
pub struct ServerParams {
|
||||
/// Config file
|
||||
#[arg(long, default_value = "./config.toml")]
|
||||
pub config: String,
|
||||
/// Server listening address
|
||||
#[arg(long, default_value = "/ip4/0.0.0.0/tcp/52321")]
|
||||
pub listen_on: String,
|
||||
|
@ -60,9 +61,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
||||
let ServerParams {
|
||||
config: config_path,
|
||||
skey: skey_hex,
|
||||
listen_on,
|
||||
} = params;
|
||||
|
||||
let c = config::Config::read(&config_path)?;
|
||||
println!("CONFIG ");
|
||||
println!("{:?}", c);
|
||||
if true {
|
||||
let _ = tracing_subscriber::fmt()
|
||||
.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();
|
||||
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)?;
|
||||
swarm.listen_on(listen_on.parse()?)?;
|
||||
|
@ -93,33 +99,18 @@ pub async fn start(params: ServerParams) -> Result<(), Box<dyn Error>> {
|
|||
request, channel, ..
|
||||
},
|
||||
})) => {
|
||||
// println!("Req : {:?} {:?} {:?}", peer, channel, hex::encode(request.body.clone()), );
|
||||
println!("REQSIG : {}", hex::encode(request.sig.clone()),);
|
||||
let MyRequest { key, body, sig } = request;
|
||||
|
||||
// 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")
|
||||
let res = match request {
|
||||
api::Req::Global(api::global::Req::Terms) => {
|
||||
api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(
|
||||
api::global::terms::Terms::default(),
|
||||
)))
|
||||
}
|
||||
api::Req::Channel => todo!("Not yet implemented"),
|
||||
_ => api::Res::Global(api::global::Res::Terms(api::global::terms::Res::Okay(
|
||||
api::global::terms::Terms::default(),
|
||||
))),
|
||||
};
|
||||
} else {
|
||||
println!("err1");
|
||||
}
|
||||
} else {
|
||||
println!("err2");
|
||||
}
|
||||
}
|
||||
let _ = swarm.behaviour_mut().req_res.send_response(channel, res);
|
||||
}
|
||||
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]> {
|
||||
println!("{}", b.len());
|
||||
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 db;
|
||||
pub mod prelude;
|
||||
// pub mod db;
|
||||
pub mod api;
|
||||
pub mod config;
|
||||
pub mod hash;
|
||||
pub mod keys;
|
||||
pub mod messages;
|
||||
//pub mod protobuf;
|
||||
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 crate::messages::{MyRequest, MyResult};
|
||||
use crate::api::{Req, Res};
|
||||
|
||||
#[derive(NetworkBehaviour)]
|
||||
pub struct MyBehaviour {
|
||||
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(
|
||||
|
@ -30,10 +30,9 @@ pub fn mk_swarm(
|
|||
.with_behaviour(|key| {
|
||||
let mdns =
|
||||
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 req_res =
|
||||
request_response::cbor::Behaviour::<MyRequest, MyResult>::new(protocol, config);
|
||||
let req_res = request_response::cbor::Behaviour::<Req, Res>::new(protocol, config);
|
||||
Ok(MyBehaviour { req_res, mdns })
|
||||
})?
|
||||
.with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(u64::MAX)))
|
||||
|
|
Loading…
Reference in New Issue