Skip to content

Commit

Permalink
feat: --dev flag for daemon to auto bootstrap (#157)
Browse files Browse the repository at this point in the history
* feat: --dev flag for daemon to auto bootstrap

* chore: implement dev profile creation on dev bootsrap

---------

Co-authored-by: vindard <[email protected]>
  • Loading branch information
bodymindarts and vindard authored May 8, 2023
1 parent 5fd30db commit e9a6741
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 11 deletions.
8 changes: 8 additions & 0 deletions proto/admin/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ option go_package = "github.com/GaloyMoney/terraform-provider-briaadmin/client/p

service AdminService {
rpc Bootstrap(BootstrapRequest) returns (BootstrapResponse) {}
rpc DevBootstrap(DevBootstrapRequest) returns (DevBootstrapResponse) {}
rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse) {}
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {}
}
Expand All @@ -15,6 +16,13 @@ message BootstrapResponse {
AdminApiKey key = 1;
}

message DevBootstrapRequest {}

message DevBootstrapResponse {
AdminApiKey admin_key = 1;
ProfileApiKey profile_key = 2;
}

message AdminApiKey {
string id = 1;
string name = 2;
Expand Down
36 changes: 33 additions & 3 deletions src/admin/app.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,61 @@
use crate::{account::*, ledger::Ledger, profile::*};
use tracing::instrument;

use super::{error::*, keys::*};
use crate::{account::*, ledger::Ledger, primitives::bitcoin, profile::*};

const BOOTSTRAP_KEY_NAME: &str = "admin_bootstrap_key";
const DEV_ACCOUNT_NAME: &str = "dev";

pub struct AdminApp {
keys: AdminApiKeys,
accounts: Accounts,
profiles: Profiles,
ledger: Ledger,
pool: sqlx::PgPool,
network: bitcoin::Network,
}

impl AdminApp {
pub fn new(pool: sqlx::PgPool) -> Self {
pub fn new(pool: sqlx::PgPool, network: bitcoin::Network) -> Self {
Self {
keys: AdminApiKeys::new(&pool),
accounts: Accounts::new(&pool),
profiles: Profiles::new(&pool),
ledger: Ledger::new(&pool),
pool,
network,
}
}
}

impl AdminApp {
#[instrument(name = "admin_app.dev_bootstrap", skip(self), err)]
pub async fn dev_bootstrap(&self) -> Result<(AdminApiKey, ProfileApiKey), AdminApiError> {
if self.network == bitcoin::Network::Bitcoin {
return Err(AdminApiError::BadNetworkForDev);
}
let admin_key = self.bootstrap().await?;

let mut tx = self.pool.begin().await?;
let account = self
.accounts
.create_in_tx(&mut tx, DEV_ACCOUNT_NAME.to_owned())
.await?;
self.ledger
.create_journal_for_account(&mut tx, account.id, account.name.clone())
.await?;
let profile = self
.profiles
.create_in_tx(&mut tx, account.id, account.name)
.await?;
let profile_key = self
.profiles
.create_key_for_profile_in_tx(&mut tx, profile, true)
.await?;
tx.commit().await?;
Ok((admin_key, profile_key))
}

#[instrument(name = "admin_app.bootstrap", skip(self), err)]
pub async fn bootstrap(&self) -> Result<AdminApiKey, AdminApiError> {
self.keys.create(BOOTSTRAP_KEY_NAME.to_string()).await
Expand Down Expand Up @@ -56,7 +86,7 @@ impl AdminApp {
.await?;
let key = self
.profiles
.create_key_for_profile_in_tx(&mut tx, profile)
.create_key_for_profile_in_tx(&mut tx, profile, false)
.await?;
tx.commit().await?;
Ok(key)
Expand Down
2 changes: 2 additions & 0 deletions src/admin/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub enum AdminApiError {
SqlxLedgerError(#[from] SqlxLedgerError),
#[error("AdminApiError - BriaError: {0}")]
BriaError(BriaError),
#[error("AdminApiError - BadNetworkForDev")]
BadNetworkForDev,
}

impl From<BriaError> for AdminApiError {
Expand Down
10 changes: 8 additions & 2 deletions src/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ pub mod error;
mod keys;
mod server;

use crate::primitives::bitcoin;

pub use app::*;
pub use config::*;
pub use error::*;
use keys::*;
pub use server::*;

pub async fn run(pool: sqlx::PgPool, config: AdminApiConfig) -> Result<(), AdminApiError> {
let app = AdminApp::new(pool);
pub async fn run(
pool: sqlx::PgPool,
config: AdminApiConfig,
network: bitcoin::Network,
) -> Result<(), AdminApiError> {
let app = AdminApp::new(pool, network);
server::start(config, app).await?;
Ok(())
}
22 changes: 22 additions & 0 deletions src/admin/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ impl AdminService for Admin {
}))
}

#[instrument(skip_all, err)]
async fn dev_bootstrap(
&self,
_request: Request<DevBootstrapRequest>,
) -> Result<Response<DevBootstrapResponse>, Status> {
let (admin_key, profile_key) = self.app.dev_bootstrap().await?;
let name = admin_key.name;
Ok(Response::new(DevBootstrapResponse {
admin_key: Some(AdminApiKey {
id: admin_key.id.to_string(),
name: name.clone(),
key: admin_key.key,
}),
profile_key: Some(ProfileApiKey {
profile_id: profile_key.id.to_string(),
name,
key: profile_key.key,
account_id: admin_key.id.to_string(),
}),
}))
}

#[instrument(skip_all, err)]
async fn create_account(
&self,
Expand Down
2 changes: 1 addition & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl App {
let mut tx = self.pool.begin().await?;
let key = self
.profiles
.create_key_for_profile_in_tx(&mut tx, found_profile)
.create_key_for_profile_in_tx(&mut tx, found_profile, false)
.await?;
tx.commit().await?;
Ok(key)
Expand Down
21 changes: 21 additions & 0 deletions src/cli/admin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl Default for AdminApiClientConfig {
pub struct AdminApiClient {
config: AdminApiClientConfig,
key: String,

bria_home: String,
}

Expand Down Expand Up @@ -71,6 +72,26 @@ impl AdminApiClient {
Ok(())
}

pub async fn dev_bootstrap(&self) -> anyhow::Result<()> {
let request = tonic::Request::new(proto::DevBootstrapRequest {});
let response = self.connect().await?.dev_bootstrap(request).await?;
let response_inner = response.into_inner();

let admin_key = response_inner
.admin_key
.context("No admin key in response")?;
token_store::store_admin_token(&self.bria_home, &admin_key.key)?;
print_admin_api_key(admin_key);

let profile_key = response_inner
.profile_key
.context("No profile key in response")?;
token_store::store_profile_token(&self.bria_home, &profile_key.key)?;
print_account(profile_key);

Ok(())
}

pub async fn account_create(&self, name: String) -> anyhow::Result<()> {
let request = tonic::Request::new(proto::CreateAccountRequest { name });
let response = self
Expand Down
44 changes: 42 additions & 2 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ enum Command {
/// Connection string for the Postgres
#[clap(env = "PG_CON", default_value = "")]
db_con: String,
/// Flag to auto-bootstrap for dev
#[clap(long)]
dev: bool,
},
/// Subcommand to interact with Admin API
Admin {
Expand Down Expand Up @@ -464,10 +467,11 @@ pub async fn run() -> anyhow::Result<()> {
config,
crash_report_config,
db_con,
dev,
} => {
let config = Config::from_path(config, EnvOverride { db_con })?;
match (
run_cmd(&cli.bria_home, config.clone()).await,
run_cmd(&cli.bria_home, config.clone(), dev).await,
crash_report_config,
) {
(Err(e), Some(true)) => {
Expand Down Expand Up @@ -696,6 +700,7 @@ async fn run_cmd(
blockchain,
app,
}: Config,
dev: bool,
) -> anyhow::Result<()> {
crate::tracing::init_tracer(tracing)?;
token_store::store_daemon_pid(bria_home, std::process::id())?;
Expand All @@ -706,9 +711,10 @@ async fn run_cmd(

let admin_send = send.clone();
let admin_pool = pool.clone();
let network = blockchain.network;
handles.push(tokio::spawn(async move {
let _ = admin_send.try_send(
super::admin::run(admin_pool, admin)
super::admin::run(admin_pool, admin, network)
.await
.context("Admin server error"),
);
Expand All @@ -721,10 +727,44 @@ async fn run_cmd(
.context("Api server error"),
);
}));

if dev {
let bria_home_string = bria_home.to_string();
tokio::spawn(async move {
let admin_client = admin_client::AdminApiClient::new(
bria_home_string,
admin_client::AdminApiClientConfig::default(),
"".to_string(),
);

let mut retries = 5;
let delay = tokio::time::Duration::from_secs(1);
while retries > 0 {
let dev_bootstrap_result = admin_client.dev_bootstrap().await;
match dev_bootstrap_result {
Ok(_) => {
println!("Dev bootstrap completed successfully");
break;
}
Err(e) => {
eprintln!("Dev bootstrap failed: {:?}.\nRetrying...", e);
retries -= 1;
if retries > 0 {
tokio::time::sleep(delay).await;
} else {
eprintln!("Dev bootstrap failed after retries: {:?}", e);
}
}
}
}
});
}

let reason = receive.recv().await.expect("Didn't receive msg");
for handle in handles {
handle.abort();
}

reason
}

Expand Down
11 changes: 9 additions & 2 deletions src/profile/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use uuid::Uuid;
use super::entity::*;
use crate::{error::*, primitives::*};

const BRIA_DEV_KEY: &str = "bria_dev_000000000000000000000";

pub struct Profiles {
pool: Pool<Postgres>,
}
Expand Down Expand Up @@ -84,9 +86,14 @@ impl Profiles {
&self,
tx: &mut sqlx::Transaction<'_, Postgres>,
profile: Profile,
dev: bool,
) -> Result<ProfileApiKey, BriaError> {
let code = Alphanumeric.sample_string(&mut rand::thread_rng(), 64);
let key = format!("bria_{code}");
let key = if dev {
BRIA_DEV_KEY.to_string()
} else {
let code = Alphanumeric.sample_string(&mut rand::thread_rng(), 64);
format!("bria_{code}")
};
let record = sqlx::query!(
r#"INSERT INTO bria_profile_api_keys (encrypted_key, profile_id)
VALUES (crypt($1, gen_salt('bf')), (SELECT id FROM bria_profiles WHERE id = $2)) RETURNING (id)"#,
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub async fn create_test_account(pool: &sqlx::PgPool) -> anyhow::Result<Profile>
"TEST_{}",
Alphanumeric.sample_string(&mut rand::thread_rng(), 32)
);
let app = AdminApp::new(pool.clone());
let app = AdminApp::new(pool.clone(), bitcoin::Network::Regtest);

let profile_key = app.create_account(name.clone()).await?;
Ok(Profile {
Expand Down

0 comments on commit e9a6741

Please sign in to comment.