Skip to content

Commit

Permalink
Merge pull request #73 from getlipa/feature/implement-backup-client
Browse files Browse the repository at this point in the history
Implement Backup client
  • Loading branch information
danielgranhao authored Nov 10, 2023
2 parents 015ade0 + 0e2ed39 commit 627f33f
Show file tree
Hide file tree
Showing 16 changed files with 828 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ members = [
"graphql",
"honey-badger",
"mole",
"squirrel",
]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ and always returns the latest state of them when requested.
## Crow
Crows are known for their love to collect stuff.
The library allows to register for and list withdraw collect offers (e.g. Lightning Address).

## Squirrel
Squirrels love to store nuts for the winter.
The library allows backups of local data to be created.
4 changes: 2 additions & 2 deletions chameleon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl ExchangeRateProvider {
.ok_or_invalid_input("Unknown currency")?
.sats_per_unit;

Ok(rate)
Ok(rate as u32)
}

pub fn query_all_exchange_rates(&self) -> Result<Vec<ExchangeRate>> {
Expand All @@ -57,7 +57,7 @@ impl ExchangeRateProvider {
.map(|c| {
Ok(ExchangeRate {
currency_code: c.currency_code,
sats_per_unit: c.sats_per_unit,
sats_per_unit: c.sats_per_unit as u32,
updated_at: parse_from_rfc3339(&c.conversion_rate_updated_at)?,
})
})
Expand Down
2 changes: 1 addition & 1 deletion graphql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[dependencies]
chrono = { version = "0.4.24", default-features = false, features = ["std"] }
graphql_client = { version = "0.13.0", features = ["reqwest-blocking"]}
graphql_client = { version = "0.13.0", features = ["reqwest-blocking", "reqwest"]}
log = "0.4.17"
reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls"]}
serde = { version = "1.0", features = ["derive"]}
Expand Down
14 changes: 14 additions & 0 deletions graphql/schemas/operations.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,17 @@ query MigrationBalance($nodePubKey: String) {
mutation MigrateFunds($invoice: String, $base16InvoiceSignature: String, $ldkNodePubKey: String) {
migrate_funds(invoice: $invoice, base16InvoiceSignature: $base16InvoiceSignature, ldkNodePubKey: $ldkNodePubKey)
}

mutation CreateBackup($encryptedBackup: String!, $schemaName: String!, $schemaVersion: String!) {
create_backup(encryptedBackup: $encryptedBackup, schemaName: $schemaName, schemaVersion: $schemaVersion) {
updatedAt
}
}

mutation RecoverBackup($schemaName: String!) {
recover_backup(schemaName: $schemaName) {
encryptedBackup
schemaVersion
updatedAt
}
}
133 changes: 101 additions & 32 deletions graphql/schemas/schema_wallet_read.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,99 @@ enum ChallengeSignatureType {
SECP256K1
}

type CreateBackupResponse {
schemaName: String
schemaVersion: String
updatedAt: DateTime
walletId: String
}

scalar DateTime

type MigrationBalanceResponse {
balanceAmountSat: BigInteger!
}

input PayFailedInput {
failedAt: DateTime!
paymentHash: String!
reason: PayFailureReason!
}

enum PayFailureReason {
NO_ROUTE
UNKNOWN
}

input PayInitiatedInput {
executedAt: DateTime!
paidAmountMSat: BigInteger!
paymentHash: String!
processStartedAt: DateTime!
requestedAmountMSat: BigInteger!
satsPerUserCurrency: Int!
source: PaySource!
userCurrency: String!
}

enum PaySource {
CAMERA
CLIPBOARD
MANUAL
NFC
}

input PaySucceededInput {
confirmedAt: DateTime!
lnFeesPaidMSat: BigInteger!
paymentHash: String!
}

input PaymentTelemetryEventsInput {
payFailed: PayFailedInput
payInitiated: PayInitiatedInput
paySucceeded: PaySucceededInput
requestInitiated: RequestInitiatedInput
requestSucceeded: RequestSucceededInput
}

type RecoverBackupResponse {
encryptedBackup: String
schemaVersion: String
updatedAt: DateTime
walletId: String
}

type RegisterTopupResponse {
email: String
nodePubKey: String!
walletPubKeyId: String!
}

type ReportPaymentTelemetryResponse {
payFailed: String
payInitiated: String
paySucceeded: String
requestInitiated: String
requestSucceeded: String
}

input RequestInitiatedInput {
createdAt: DateTime!
enteredAmountMSat: BigInteger!
paymentHash: String!
requestCurrency: String!
satsPerUserCurrency: Int!
userCurrency: String!
}

input RequestSucceededInput {
channelOpeningFeeMSat: BigInteger!
paidAmountMSat: BigInteger!
paymentHash: String!
paymentReceivedAt: DateTime!
}

type SessionPermit {
accessToken: String
refreshToken: String
Expand Down Expand Up @@ -732,23 +813,6 @@ enum cursor_ordering {
DESC
}

scalar float8

"""
Boolean expression to compare columns of type "float8". All fields are combined with logical 'AND'.
"""
input float8_comparison_exp {
_eq: float8
_gt: float8
_gte: float8
_in: [float8!]
_is_null: Boolean
_lt: float8
_lte: float8
_neq: float8
_nin: [float8!]
}

"""mutation root"""
type mutation_root {
"""
Expand Down Expand Up @@ -801,6 +865,7 @@ type mutation_root {
where: accepted_terms_conditions_bool_exp
): accepted_terms_conditions
accept_wallet_acl_by_pk(pk_columns: AcceptWalletPkRequestInput!): WalletAcl
create_backup(encryptedBackup: String!, schemaName: String!, schemaVersion: String!): CreateBackupResponse

"""
delete data from the table: "channel_manager"
Expand Down Expand Up @@ -895,12 +960,14 @@ type mutation_root {
on_conflict: channel_monitor_on_conflict
): channel_monitor
migrate_funds(base16InvoiceSignature: String, invoice: String, ldkNodePubKey: String): Boolean!
recover_backup(schemaName: String!): RecoverBackupResponse
refresh_session(refreshToken: String!): TokenContainer
refresh_session_v2(refreshToken: String!): SessionPermit
register_email(email: String): WalletEmail
register_node(nodePubKey: String): WalletNode
register_notification_token(language: String!, notificationToken: String!): Token
register_topup(email: String, orderId: String!): RegisterTopupResponse
report_payment_telemetry(events: PaymentTelemetryEventsInput, telemetryId: String!): ReportPaymentTelemetryResponse
start_prepared_session(challenge: String!, challengeSignature: String!, challengeSignatureType: ChallengeSignatureType, preparedPermissionToken: String!): TokenContainer
start_prepared_session_v2(challenge: String!, challengeSignature: String!, challengeSignatureType: ChallengeSignatureType, preparedPermissionToken: String!): SessionPermit
start_session(authPubKey: String!, challenge: String!, challengeSignature: String!, challengeSignatureType: ChallengeSignatureType, signedAuthPubKey: String!, walletPubKey: String!): TokenContainer
Expand Down Expand Up @@ -966,6 +1033,7 @@ type query_root {
where: accepted_terms_conditions_bool_exp
): [accepted_terms_conditions!]!
auth_challenge: String
backup_service_version: String

"""
fetch data from the table: "channel_manager"
Expand Down Expand Up @@ -1063,6 +1131,7 @@ type query_root {
): currency
migration_balance(nodePubKey: String): MigrationBalanceResponse
notification_service_version: String
payment_service_version: String
prepare_wallet_session(challenge: String!, ownerPubKeyId: String!, signature: String!): String

"""
Expand Down Expand Up @@ -1540,14 +1609,14 @@ columns and relationships of "topup"
type topup {
additionalInfo: String
amountSat: bigint!
amountUserCurrency: float8!
amountUserCurrency: numeric!
createdAt: timestamptz!
exchangeFeeRate: float8!
exchangeFeeUserCurrency: float8!
exchangeRate: float8!
exchangeFeeRate: numeric!
exchangeFeeUserCurrency: numeric!
exchangeRate: numeric!
expiresAt: timestamptz
id: uuid!
lightningFeeUserCurrency: float8!
lightningFeeUserCurrency: numeric!
lnurl: String
nodePubKey: String!
status: topup_status_enum!
Expand All @@ -1563,14 +1632,14 @@ input topup_bool_exp {
_or: [topup_bool_exp!]
additionalInfo: String_comparison_exp
amountSat: bigint_comparison_exp
amountUserCurrency: float8_comparison_exp
amountUserCurrency: numeric_comparison_exp
createdAt: timestamptz_comparison_exp
exchangeFeeRate: float8_comparison_exp
exchangeFeeUserCurrency: float8_comparison_exp
exchangeRate: float8_comparison_exp
exchangeFeeRate: numeric_comparison_exp
exchangeFeeUserCurrency: numeric_comparison_exp
exchangeRate: numeric_comparison_exp
expiresAt: timestamptz_comparison_exp
id: uuid_comparison_exp
lightningFeeUserCurrency: float8_comparison_exp
lightningFeeUserCurrency: numeric_comparison_exp
lnurl: String_comparison_exp
nodePubKey: String_comparison_exp
status: topup_status_enum_comparison_exp
Expand Down Expand Up @@ -1676,14 +1745,14 @@ input topup_stream_cursor_input {
input topup_stream_cursor_value_input {
additionalInfo: String
amountSat: bigint
amountUserCurrency: float8
amountUserCurrency: numeric
createdAt: timestamptz
exchangeFeeRate: float8
exchangeFeeUserCurrency: float8
exchangeRate: float8
exchangeFeeRate: numeric
exchangeFeeUserCurrency: numeric
exchangeRate: numeric
expiresAt: timestamptz
id: uuid
lightningFeeUserCurrency: float8
lightningFeeUserCurrency: numeric
lnurl: String
nodePubKey: String
status: topup_status_enum
Expand Down
45 changes: 44 additions & 1 deletion graphql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use crate::errors::*;
pub use perro;
pub use reqwest;

use graphql_client::reqwest::post_graphql_blocking;
use graphql_client::reqwest::{post_graphql, post_graphql_blocking};
use graphql_client::Response;
use perro::{permanent_failure, runtime_error, MapToError, OptionToError};
use reqwest::blocking::Client;
Expand Down Expand Up @@ -37,6 +37,25 @@ pub fn build_client(access_token: Option<&str>) -> Result<Client> {
Ok(client)
}

pub fn build_async_client(access_token: Option<&str>) -> Result<reqwest::Client> {
let user_agent = "graphql-rust/0.12.0";
let timeout = Duration::from_secs(20);

let mut builder = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(timeout);
if let Some(access_token) = access_token {
let value = HeaderValue::from_str(&format!("Bearer {access_token}"))
.map_to_permanent_failure("Failed to build header value from str")?;
builder = builder.default_headers(std::iter::once((AUTHORIZATION, value)).collect());
}

let client = builder
.build()
.map_to_permanent_failure("Failed to build a async reqwest client")?;
Ok(client)
}

pub fn post_blocking<Query: graphql_client::GraphQLQuery>(
client: &Client,
backend_url: &String,
Expand All @@ -61,6 +80,30 @@ pub fn post_blocking<Query: graphql_client::GraphQLQuery>(
get_response_data(response, backend_url)
}

pub async fn post<Query: graphql_client::GraphQLQuery>(
client: &reqwest::Client,
backend_url: &String,
variables: Query::Variables,
) -> Result<Query::ResponseData> {
let response = match post_graphql::<Query, _>(client, backend_url, variables).await {
Ok(r) => r,
Err(e) => {
if is_502_status(e.status()) || e.to_string().contains("502") {
// checking for the error containing 502 because reqwest is unexpectedly returning a decode error instead of status error
return Err(runtime_error(
GraphQlRuntimeErrorCode::RemoteServiceUnavailable,
"The remote server returned status 502",
));
}
return Err(runtime_error(
GraphQlRuntimeErrorCode::NetworkError,
"Failed to execute the query",
));
}
};
get_response_data(response, backend_url)
}

pub fn parse_from_rfc3339(rfc3339: &str) -> Result<SystemTime> {
let datetime = chrono::DateTime::parse_from_rfc3339(rfc3339).map_to_runtime_error(
GraphQlRuntimeErrorCode::CorruptData,
Expand Down
19 changes: 18 additions & 1 deletion graphql/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use graphql_client::GraphQLQuery;

#[allow(non_camel_case_types)]
type bytea = String;
type DateTime = String;
#[allow(non_camel_case_types)]
type numeric = u32;
type numeric = float8;
#[allow(non_camel_case_types)]
type timestamptz = String;
#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -190,3 +191,19 @@ pub struct MigrationBalance;
response_derives = "Debug"
)]
pub struct MigrateFunds;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schemas/schema_wallet_read.graphql",
query_path = "schemas/operations.graphql",
response_derives = "Debug"
)]
pub struct CreateBackup;

#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schemas/schema_wallet_read.graphql",
query_path = "schemas/operations.graphql",
response_derives = "Debug"
)]
pub struct RecoverBackup;
1 change: 1 addition & 0 deletions honey-badger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ log = "0.4.17"
rand = "0.8.5"
secp256k1 = { version = "0.27.0", features = ["global-context"] }
serde_json = "1.0"
tokio = "1.32.0"

graphql = { path = "../graphql" }

Expand Down
Loading

0 comments on commit 627f33f

Please sign in to comment.