use crate::chain::tracker::ChainTracker;
use alloc::sync::Arc;
use bitcoin::hashes::sha256::Hash as Sha256Hash;
use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine};
use bitcoin::secp256k1::PublicKey;
use core::fmt::Debug;
use lightning::chain::keysinterface::EntropySource;
use crate::channel::{Channel, ChannelId, ChannelStub};
use crate::monitor::ChainMonitor;
use crate::node::{NodeConfig, NodeState};
use crate::policy::validator::ValidatorFactory;
use crate::prelude::*;
pub mod model;
pub type Mutations = Vec<(String, (u64, Vec<u8>))>;
pub trait Context {
fn exit(&self) -> Mutations;
}
struct DummyContext;
impl Context for DummyContext {
fn exit(&self) -> Mutations {
vec![]
}
}
#[derive(Clone, Debug)]
pub enum Error {
Unavailable(String),
NotFound(String),
AlreadyExists(String),
Internal(String),
}
pub trait Persist: SendSync {
fn enter(&self, _state: Arc<Mutex<OrderedMap<String, (u64, Vec<u8>)>>>) -> Box<dyn Context> {
Box::new(DummyContext)
}
fn new_node(
&self,
node_id: &PublicKey,
config: &NodeConfig,
state: &NodeState,
) -> Result<(), Error>;
fn update_node(&self, node_id: &PublicKey, state: &NodeState) -> Result<(), Error>;
fn delete_node(&self, node_id: &PublicKey) -> Result<(), Error>;
fn new_channel(&self, node_id: &PublicKey, stub: &ChannelStub) -> Result<(), Error>;
fn new_chain_tracker(
&self,
node_id: &PublicKey,
tracker: &ChainTracker<ChainMonitor>,
) -> Result<(), Error>;
fn update_tracker(
&self,
node_id: &PublicKey,
tracker: &ChainTracker<ChainMonitor>,
) -> Result<(), Error>;
fn get_tracker(
&self,
node_id: PublicKey,
validator_factory: Arc<dyn ValidatorFactory>,
) -> Result<ChainTracker<ChainMonitor>, Error>;
fn update_channel(&self, node_id: &PublicKey, channel: &Channel) -> Result<(), Error>;
fn get_channel(
&self,
node_id: &PublicKey,
channel_id: &ChannelId,
) -> Result<model::ChannelEntry, Error>;
fn get_node_channels(
&self,
node_id: &PublicKey,
) -> Result<Vec<(ChannelId, model::ChannelEntry)>, Error>;
fn update_node_allowlist(
&self,
node_id: &PublicKey,
allowlist: Vec<String>,
) -> Result<(), Error>;
fn get_node_allowlist(&self, node_id: &PublicKey) -> Result<Vec<String>, Error>;
fn get_nodes(&self) -> Result<Vec<(PublicKey, model::NodeEntry)>, Error>;
fn clear_database(&self) -> Result<(), Error>;
}
pub struct DummyPersister;
impl SendSync for DummyPersister {}
#[allow(unused_variables)]
impl Persist for DummyPersister {
fn new_node(
&self,
node_id: &PublicKey,
config: &NodeConfig,
state: &NodeState,
) -> Result<(), Error> {
Ok(())
}
fn update_node(&self, node_id: &PublicKey, state: &NodeState) -> Result<(), Error> {
Ok(())
}
fn delete_node(&self, node_id: &PublicKey) -> Result<(), Error> {
Ok(())
}
fn new_channel(&self, node_id: &PublicKey, stub: &ChannelStub) -> Result<(), Error> {
Ok(())
}
fn new_chain_tracker(
&self,
node_id: &PublicKey,
tracker: &ChainTracker<ChainMonitor>,
) -> Result<(), Error> {
Ok(())
}
fn update_tracker(
&self,
node_id: &PublicKey,
tracker: &ChainTracker<ChainMonitor>,
) -> Result<(), Error> {
Ok(())
}
fn get_tracker(
&self,
node_id: PublicKey,
validator_factory: Arc<dyn ValidatorFactory>,
) -> Result<ChainTracker<ChainMonitor>, Error> {
Err(Error::Internal(format!("get_tracker unimplemented")))
}
fn update_channel(&self, node_id: &PublicKey, channel: &Channel) -> Result<(), Error> {
Ok(())
}
fn get_channel(
&self,
node_id: &PublicKey,
channel_id: &ChannelId,
) -> Result<model::ChannelEntry, Error> {
Err(Error::Internal(format!("get_channel unimplemented")))
}
fn get_node_channels(
&self,
node_id: &PublicKey,
) -> Result<Vec<(ChannelId, model::ChannelEntry)>, Error> {
Ok(Vec::new())
}
fn update_node_allowlist(
&self,
node_id: &PublicKey,
allowlist: Vec<String>,
) -> Result<(), Error> {
Ok(())
}
fn get_node_allowlist(&self, node_id: &PublicKey) -> Result<Vec<String>, Error> {
Ok(Vec::new())
}
fn get_nodes(&self) -> Result<Vec<(PublicKey, model::NodeEntry)>, Error> {
Ok(Vec::new())
}
fn clear_database(&self) -> Result<(), Error> {
Ok(())
}
}
pub trait SeedPersist: Sync + Send {
fn put(&self, key: &str, seed: &[u8]);
fn get(&self, key: &str) -> Option<Vec<u8>>;
fn list(&self) -> Vec<String>;
}
pub struct DummySeedPersister;
impl SeedPersist for DummySeedPersister {
fn put(&self, _key: &str, _seed: &[u8]) {}
fn get(&self, _key: &str) -> Option<Vec<u8>> {
None
}
fn list(&self) -> Vec<String> {
Vec::new()
}
}
pub struct MemorySeedPersister {
seed: Vec<u8>,
}
impl MemorySeedPersister {
pub fn new(seed: Vec<u8>) -> Self {
Self { seed }
}
}
impl SeedPersist for MemorySeedPersister {
fn put(&self, _key: &str, _seed: &[u8]) {
unimplemented!()
}
fn get(&self, _key: &str) -> Option<Vec<u8>> {
Some(self.seed.clone())
}
fn list(&self) -> Vec<String> {
Vec::new()
}
}
#[cfg(feature = "std")]
pub mod fs {
use crate::persist::SeedPersist;
use bitcoin::hashes::hex::{FromHex, ToHex};
use std::fs;
use std::path::PathBuf;
pub struct FileSeedPersister {
path: PathBuf,
}
impl FileSeedPersister {
pub fn new<P: Into<PathBuf>>(path: P) -> Self {
Self { path: path.into() }
}
fn seed_path_for_key(&self, node_id: &str) -> PathBuf {
let mut path = self.path.clone();
path.push(format!("{}.seed", node_id));
path
}
}
impl SeedPersist for FileSeedPersister {
fn put(&self, key: &str, seed: &[u8]) {
write_seed(self.seed_path_for_key(key), seed);
}
fn get(&self, key: &str) -> Option<Vec<u8>> {
read_seed(self.seed_path_for_key(key))
}
fn list(&self) -> Vec<String> {
let mut keys = Vec::new();
for entry in fs::read_dir(&self.path).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if let Some(fileext) = path.extension() {
if fileext == "seed" {
let key = path.file_stem().unwrap().to_str().unwrap();
keys.push(key.to_string());
}
}
}
keys
}
}
fn write_seed(path: PathBuf, seed: &[u8]) {
fs::write(path, seed.to_hex()).unwrap();
}
fn read_seed(path: PathBuf) -> Option<Vec<u8>> {
fs::read_to_string(path).ok().map(|s| Vec::from_hex(&s).expect("bad hex seed"))
}
}
#[derive(Clone)]
pub struct ExternalPersistHelper {
shared_secret: [u8; 32],
last_nonce: [u8; 32],
}
impl ExternalPersistHelper {
pub fn new(shared_secret: [u8; 32]) -> Self {
Self { shared_secret, last_nonce: [0; 32] }
}
pub fn new_nonce(&mut self, entropy_source: &EntropySource) -> [u8; 32] {
let nonce = entropy_source.get_secure_random_bytes();
self.last_nonce = nonce;
nonce
}
pub fn client_hmac(&self, kvs: &Mutations) -> [u8; 32] {
compute_shared_hmac(&self.shared_secret, &[0x01], kvs)
}
pub fn server_hmac(&self, kvs: &Mutations) -> [u8; 32] {
compute_shared_hmac(&self.shared_secret, &[0x02], kvs)
}
pub fn check_hmac(&self, kvs: &Mutations, received_hmac: Vec<u8>) -> bool {
let hmac = compute_shared_hmac(&self.shared_secret, &self.last_nonce, &kvs); received_hmac == hmac
}
}
#[cfg(feature = "std")]
pub use simple_entropy::SimpleEntropy;
#[cfg(feature = "std")]
mod simple_entropy {
use super::EntropySource;
use bitcoin::secp256k1::rand::{self, RngCore};
pub struct SimpleEntropy {}
impl SimpleEntropy {
pub fn new() -> Self {
Self {}
}
}
impl EntropySource for SimpleEntropy {
fn get_secure_random_bytes(&self) -> [u8; 32] {
let mut bytes = [0u8; 32];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut bytes);
bytes
}
}
}
pub fn compute_shared_hmac(secret: &[u8], nonce: &[u8], kvs: &Mutations) -> [u8; 32] {
let mut hmac_engine = HmacEngine::<Sha256Hash>::new(&secret);
hmac_engine.input(secret);
hmac_engine.input(nonce);
for (key, (version, value)) in kvs {
add_to_hmac(&key, *version, &value, &mut hmac_engine);
}
Hmac::from_engine(hmac_engine).into_inner()
}
fn add_to_hmac(key: &str, version: u64, value: &[u8], hmac: &mut HmacEngine<Sha256Hash>) {
hmac.input(key.as_bytes());
hmac.input(&version.to_be_bytes());
hmac.input(&value);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hmac_test() {
let shared_secret = [0; 32];
let mut helper = ExternalPersistHelper::new(shared_secret);
let mut kvs = Mutations::new();
kvs.push(("foo".to_string(), (0, vec![0x01])));
kvs.push(("bar".to_string(), (0, vec![0x02])));
let nonce = helper.new_nonce(&SimpleEntropy::new());
let hmac = compute_shared_hmac(&shared_secret, &nonce, &kvs);
assert!(helper.check_hmac(&kvs, hmac.to_vec()));
kvs.push(("baz".to_string(), (0, vec![0x03])));
assert!(!helper.check_hmac(&kvs, hmac.to_vec()));
}
}