-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
048f6eb
commit 6c8fd87
Showing
4 changed files
with
322 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
use crate::{error::MutinyError, logging::MutinyLogger, storage::MutinyStorage}; | ||
use bitcoin::{secp256k1::Secp256k1, util::bip32::ExtendedPrivKey}; | ||
use bitcoin::{ | ||
util::bip32::{ChildNumber, DerivationPath}, | ||
Network, | ||
}; | ||
use fedimint_client::{derivable_secret::DerivableSecret, ClientArc, FederationInfo}; | ||
use fedimint_core::db::mem_impl::MemDatabase; | ||
use fedimint_core::{api::InviteCode, config::FederationId}; | ||
use fedimint_ln_client::LightningClientInit; | ||
use fedimint_mint_client::MintClientInit; | ||
use fedimint_wallet_client::WalletClientInit; | ||
use lightning::log_info; | ||
use lightning::util::logger::Logger; | ||
use serde::{Deserialize, Serialize}; | ||
use std::{ | ||
collections::HashMap, | ||
str::FromStr, | ||
sync::{atomic::AtomicBool, Arc, RwLock}, | ||
}; | ||
|
||
const FEDIMINT_CLIENT_NONCE: &[u8] = b"Fedimint Client Salt"; | ||
|
||
// This is the FedimintStorage object saved to the DB | ||
#[derive(Debug, Serialize, Deserialize, Clone, Default)] | ||
pub struct FedimintStorage { | ||
pub fedimints: HashMap<String, FedimintIndex>, | ||
#[serde(default)] | ||
pub version: u32, | ||
} | ||
|
||
// This is the FedimintIdentity that refer to a specific node | ||
// Used for public facing identification. | ||
pub struct FedimintIdentity { | ||
pub uuid: String, | ||
pub federation_id: FederationId, | ||
} | ||
|
||
// This is the FedimintIndex reference that is saved to the DB | ||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)] | ||
pub struct FedimintIndex { | ||
pub child_index: u32, | ||
pub federation_code: String, | ||
pub archived: Option<bool>, | ||
} | ||
|
||
impl FedimintIndex { | ||
pub fn is_archived(&self) -> bool { | ||
self.archived.unwrap_or(false) | ||
} | ||
} | ||
|
||
pub(crate) struct FedimintClient<S: MutinyStorage> { | ||
pub _uuid: String, | ||
pub child_index: u32, | ||
pub federation_code: String, | ||
pub fedimint_client: ClientArc, | ||
stopped_components: Arc<RwLock<Vec<bool>>>, | ||
storage: S, | ||
network: Network, | ||
pub(crate) logger: Arc<MutinyLogger>, | ||
stop: Arc<AtomicBool>, | ||
} | ||
|
||
impl<S: MutinyStorage> FedimintClient<S> { | ||
#[allow(clippy::too_many_arguments)] | ||
pub(crate) async fn new( | ||
uuid: String, | ||
fedimint_index: &FedimintIndex, | ||
federation_code: String, | ||
xprivkey: ExtendedPrivKey, | ||
storage: S, | ||
network: Network, | ||
logger: Arc<MutinyLogger>, | ||
) -> Result<Self, MutinyError> { | ||
log_info!(logger, "initializing a new fedimint client: {uuid}"); | ||
|
||
// a list of components that need to be stopped and whether or not they are stopped | ||
let stopped_components = Arc::new(RwLock::new(vec![])); | ||
|
||
let stop = Arc::new(AtomicBool::new(false)); | ||
|
||
log_info!(logger, "Joining federation {}", federation_code); | ||
|
||
let invite_code = InviteCode::from_str(&federation_code) | ||
.map_err(|_| MutinyError::InvalidArgumentsError)?; | ||
|
||
let mut client_builder = fedimint_client::Client::builder(); | ||
client_builder.with_module(WalletClientInit(None)); | ||
client_builder.with_module(MintClientInit); | ||
client_builder.with_module(LightningClientInit); | ||
client_builder.with_database(MemDatabase::new().into()); // TODO not in memory | ||
client_builder.with_primary_module(1); | ||
client_builder.with_federation_info(FederationInfo::from_invite_code(invite_code).await?); | ||
|
||
let secret = create_fedimint_secret(xprivkey, fedimint_index)?; | ||
|
||
let fedimint_client = client_builder.build(secret).await?; | ||
|
||
Ok(FedimintClient { | ||
_uuid: uuid, | ||
child_index: fedimint_index.child_index, | ||
federation_code, | ||
fedimint_client, | ||
stopped_components, | ||
storage, | ||
network, | ||
logger, | ||
stop, | ||
}) | ||
} | ||
|
||
pub fn fedimint_index(&self) -> FedimintIndex { | ||
FedimintIndex { | ||
child_index: self.child_index, | ||
federation_code: self.federation_code.clone(), | ||
archived: Some(false), | ||
} | ||
} | ||
} | ||
|
||
// A fedimint private key will be derived from `m/1'/X'`, where X is the index of a specific fedimint. | ||
// Fedimint will derive further keys from there. | ||
fn create_fedimint_secret( | ||
xprivkey: ExtendedPrivKey, | ||
fedimint_index: &FedimintIndex, | ||
) -> Result<DerivableSecret, MutinyError> { | ||
let context = Secp256k1::new(); | ||
let xpriv = xprivkey.derive_priv( | ||
&context, | ||
&DerivationPath::from(vec![ | ||
ChildNumber::from_hardened_idx(1)?, | ||
ChildNumber::from_hardened_idx(fedimint_index.child_index)?, | ||
]), | ||
)?; | ||
let secret = | ||
DerivableSecret::new_root(&xpriv.private_key.secret_bytes(), FEDIMINT_CLIENT_NONCE); | ||
Ok(secret) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.