wip
This commit is contained in:
parent
4cfb0b30d6
commit
97ae66956d
89
binding.h
89
binding.h
|
@ -1,4 +1,3 @@
|
|||
#ifndef __APPLE__
|
||||
typedef char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int uint16_t;
|
||||
|
@ -10,9 +9,9 @@ typedef long int uint32_t;
|
|||
#ifndef __cplusplus
|
||||
typedef char bool;
|
||||
#endif
|
||||
#endif
|
||||
typedef void *DartPostCObjectFnType;
|
||||
|
||||
|
||||
#define QR_DATA_SIZE 256
|
||||
|
||||
#define MAX_ATTEMPTS 10
|
||||
|
@ -22,29 +21,99 @@ typedef void *DartPostCObjectFnType;
|
|||
typedef struct CResult_u8 {
|
||||
uint8_t value;
|
||||
char *error;
|
||||
uint32_t len;
|
||||
} CResult_u8;
|
||||
|
||||
typedef struct CResult_u32 {
|
||||
uint32_t value;
|
||||
char *error;
|
||||
uint32_t len;
|
||||
} CResult_u32;
|
||||
|
||||
typedef struct CResult_____c_char {
|
||||
char *value;
|
||||
char *error;
|
||||
uint32_t len;
|
||||
} CResult_____c_char;
|
||||
|
||||
typedef struct CResult_u64 {
|
||||
uint64_t value;
|
||||
char *error;
|
||||
uint32_t len;
|
||||
} CResult_u64;
|
||||
|
||||
typedef struct CResult______u8 {
|
||||
const uint8_t *value;
|
||||
char *error;
|
||||
uint32_t len;
|
||||
} CResult______u8;
|
||||
|
||||
#define Account_VT_ID 4
|
||||
|
||||
#define Account_VT_NAME 6
|
||||
|
||||
#define Account_VT_BALANCE 8
|
||||
|
||||
#define AccountVec_VT_ACCOUNTS 4
|
||||
|
||||
#define Balance_VT_SHIELDED 4
|
||||
|
||||
#define Balance_VT_UNCONFIRMED_SPENT 6
|
||||
|
||||
#define Balance_VT_UNDER_CONFIRMED 10
|
||||
|
||||
#define Balance_VT_EXCLUDED 12
|
||||
|
||||
#define Balance_VT_SAPLING 14
|
||||
|
||||
#define Balance_VT_ORCHARD 16
|
||||
|
||||
#define Height_VT_HEIGHT 4
|
||||
|
||||
#define Height_VT_TIMESTAMP 6
|
||||
|
||||
#define ShieldedNote_VT_VALUE 8
|
||||
|
||||
#define ShieldedNote_VT_SPENT 16
|
||||
|
||||
#define ShieldedNoteVec_VT_NOTES 4
|
||||
|
||||
#define AdressbookEntry_VT_ID_ADDRESS 4
|
||||
|
||||
#define AdressbookEntry_VT_ADDRESS 8
|
||||
|
||||
#define Addressbook_VT_CONTACTS 6
|
||||
|
||||
#define AccountBalance_VT_COIN 4
|
||||
|
||||
#define AccountBalance_VT_ID_ACCOUNT 6
|
||||
|
||||
#define AccountBalance_VT_TBALANCE 12
|
||||
|
||||
#define ZMessage_VT_TX_ID 6
|
||||
|
||||
#define ZMessage_VT_INCOMING 8
|
||||
|
||||
#define ZMessage_VT_SENDER 10
|
||||
|
||||
#define ZMessage_VT_RECIPIENT 12
|
||||
|
||||
#define ZMessage_VT_SUBJECT 14
|
||||
|
||||
#define ZMessage_VT_BODY 16
|
||||
|
||||
#define ZMessage_VT_READ 22
|
||||
|
||||
#define ZMessages_VT_MESSAGES 4
|
||||
|
||||
void dummy_export(void);
|
||||
|
||||
void dart_post_cobject(DartPostCObjectFnType ptr);
|
||||
|
||||
void deallocate_str(char *s);
|
||||
|
||||
void deallocate_bytes(uint8_t *ptr, uint32_t len);
|
||||
|
||||
struct CResult_u8 init_wallet(uint8_t coin, char *db_path);
|
||||
|
||||
struct CResult_u8 migrate_db(uint8_t coin, char *db_path);
|
||||
|
@ -184,6 +253,22 @@ struct CResult_u32 save_send_template(uint8_t coin, char *template_);
|
|||
|
||||
struct CResult_u8 delete_send_template(uint8_t coin, uint32_t id);
|
||||
|
||||
struct CResult______u8 get_account_list(uint8_t coin);
|
||||
|
||||
struct CResult_u32 get_available_account_id(uint8_t coin, uint32_t id);
|
||||
|
||||
struct CResult_____c_char get_t_addr(uint8_t coin, uint32_t id);
|
||||
|
||||
struct CResult_____c_char get_sk(uint8_t coin, uint32_t id);
|
||||
|
||||
struct CResult_u8 update_account_name(uint8_t coin, uint32_t id, char *name);
|
||||
|
||||
struct CResult______u8 get_balances(uint8_t coin, uint32_t id, uint32_t confirmed_height);
|
||||
|
||||
struct CResult______u8 get_db_height(uint8_t coin);
|
||||
|
||||
struct CResult______u8 get_notes(uint8_t coin, uint32_t id);
|
||||
|
||||
bool has_cuda(void);
|
||||
|
||||
bool has_metal(void);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::coinconfig::{init_coin, CoinConfig, MEMPOOL, MEMPOOL_RUNNER};
|
||||
use crate::db::FullEncryptedBackup;
|
||||
use crate::note_selection::TransactionReport;
|
||||
use crate::{ChainError, TransactionPlan, Tx};
|
||||
use crate::{ChainError, DbAdapter, TransactionPlan, Tx};
|
||||
use allo_isolate::{ffi, IntoDart};
|
||||
use android_logger::Config;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -9,7 +9,8 @@ use log::Level;
|
|||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use rusqlite::Connection;
|
||||
use tokio::sync::Semaphore;
|
||||
use zcash_primitives::transaction::builder::Progress;
|
||||
use crate::template::SendTemplate;
|
||||
|
@ -41,12 +42,14 @@ fn to_cresult<T>(res: Result<T, anyhow::Error>) -> CResult<T> {
|
|||
match res {
|
||||
Ok(v) => CResult {
|
||||
value: v,
|
||||
len: 0,
|
||||
error: std::ptr::null_mut::<c_char>(),
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
CResult {
|
||||
value: unsafe { std::mem::zeroed() },
|
||||
len: 0,
|
||||
error: to_c_str(e),
|
||||
}
|
||||
}
|
||||
|
@ -64,11 +67,39 @@ fn log_error(res: Result<(), anyhow::Error>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_cresult_bytes(res: Result<Vec<u8>, anyhow::Error>) -> CResult<*const u8> {
|
||||
match res {
|
||||
Ok(v) => {
|
||||
let ptr = v.as_ptr();
|
||||
let len = v.len();
|
||||
std::mem::forget(v);
|
||||
CResult {
|
||||
value: ptr,
|
||||
len: len as u32,
|
||||
error: std::ptr::null_mut::<c_char>(),
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
CResult {
|
||||
value: unsafe { std::mem::zeroed() },
|
||||
len: 0,
|
||||
error: to_c_str(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn deallocate_str(s: *mut c_char) {
|
||||
let _ = CString::from_raw(s);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn deallocate_bytes(ptr: *mut u8, len: u32) {
|
||||
drop(Vec::from_raw_parts(ptr, len as usize, len as usize));
|
||||
}
|
||||
|
||||
fn try_init_logger() {
|
||||
android_logger::init_once(
|
||||
Config::default()
|
||||
|
@ -90,6 +121,7 @@ fn try_init_logger() {
|
|||
pub struct CResult<T> {
|
||||
value: T,
|
||||
error: *mut c_char,
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -796,6 +828,86 @@ pub unsafe extern "C" fn delete_send_template(coin: u8, id: u32) -> CResult<u8>
|
|||
to_cresult(res())
|
||||
}
|
||||
|
||||
fn with_account<T, F: Fn(u8, u32, &Connection) -> anyhow::Result<T>>(coin: u8, id: u32, f: F) -> anyhow::Result<T> {
|
||||
let c = CoinConfig::get(coin);
|
||||
let db = c.db()?;
|
||||
let connection = &db.connection;
|
||||
f(coin, id, connection)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_account_list(coin: u8) -> CResult<*const u8> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let accounts = crate::db::read::get_account_list(connection)?;
|
||||
Ok(accounts)
|
||||
};
|
||||
to_cresult_bytes(with_account(coin, 0, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_available_account_id(coin: u8, id: u32) -> CResult<u32> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let new_id = crate::db::read::get_available_account_id(connection, id)?;
|
||||
Ok(new_id)
|
||||
};
|
||||
to_cresult(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_t_addr(coin: u8, id: u32) -> CResult<*mut c_char> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let address = crate::db::read::get_t_addr(connection, id)?;
|
||||
Ok(address)
|
||||
};
|
||||
to_cresult_str(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_sk(coin: u8, id: u32) -> CResult<*mut c_char> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let sk = crate::db::read::get_sk(connection, id)?;
|
||||
Ok(sk)
|
||||
};
|
||||
to_cresult_str(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn update_account_name(coin: u8, id: u32, name: *mut c_char) -> CResult<u8> {
|
||||
from_c_str!(name);
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
crate::db::read::update_account_name(connection, id, &name)?;
|
||||
Ok(0)
|
||||
};
|
||||
to_cresult(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_balances(coin: u8, id: u32, confirmed_height: u32) -> CResult<*const u8> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let data = crate::db::read::get_balances(connection, id, confirmed_height)?;
|
||||
Ok(data)
|
||||
};
|
||||
to_cresult_bytes(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_db_height(coin: u8) -> CResult<*const u8> {
|
||||
let res = |coin: u8, _id: u32, connection: &Connection| {
|
||||
let data = crate::db::read::get_db_height(connection)?;
|
||||
Ok(data)
|
||||
};
|
||||
to_cresult_bytes(with_account(coin, 0, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_notes(coin: u8, id: u32) -> CResult<*const u8> {
|
||||
let res = |coin: u8, id: u32, connection: &Connection| {
|
||||
let data = crate::db::read::get_notes(connection, id)?;
|
||||
Ok(data)
|
||||
};
|
||||
to_cresult_bytes(with_account(coin, id, res))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn has_cuda() -> bool {
|
||||
crate::gpu::has_cuda()
|
||||
|
|
|
@ -26,6 +26,8 @@ use zcash_primitives::zip32::{DiversifierIndex, ExtendedFullViewingKey};
|
|||
|
||||
mod backup;
|
||||
mod migration;
|
||||
pub mod data_generated;
|
||||
pub mod read;
|
||||
|
||||
pub use backup::FullEncryptedBackup;
|
||||
use crate::template::SendTemplate;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,315 @@
|
|||
use rusqlite::{Connection, OptionalExtension, params};
|
||||
use anyhow::Result;
|
||||
use crate::db::data_generated::fb::*;
|
||||
use crate::DbAdapter;
|
||||
|
||||
pub fn has_account(connection: &Connection) -> Result<bool> {
|
||||
let res = connection.query_row("SELECT 1 FROM accounts", [], |_| { Ok(()) }).optional()?;
|
||||
Ok(res.is_some())
|
||||
}
|
||||
|
||||
pub fn get_account_list(connection: &Connection) -> Result<Vec<u8>> {
|
||||
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||
let mut stmt = connection.prepare("WITH notes AS (SELECT a.id_account, a.name, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account), \
|
||||
accounts2 AS (SELECT id_account, name, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) \
|
||||
SELECT a.id_account, a.name, a.balance FROM accounts2 a")?;
|
||||
let rows = stmt.query_map([], |row| {
|
||||
let id: u32 = row.get("id_account")?;
|
||||
let name: String = row.get("name")?;
|
||||
let balance: i64 = row.get("balance")?;
|
||||
let name = builder.create_string(&name);
|
||||
let account = Account::create(&mut builder, &AccountArgs {
|
||||
id,
|
||||
name: Some(name),
|
||||
balance: balance as u64,
|
||||
});
|
||||
Ok(account)
|
||||
})?;
|
||||
let mut accounts = vec![];
|
||||
for r in rows {
|
||||
accounts.push(r?);
|
||||
}
|
||||
let accounts = builder.create_vector(&accounts);
|
||||
let accounts = AccountVec::create(&mut builder, &AccountVecArgs { accounts: Some(accounts) });
|
||||
builder.finish(accounts, None);
|
||||
let data = builder.finished_data().to_vec();
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_available_account_id(connection: &Connection, id: u32) -> Result<u32> {
|
||||
let r = connection.query_row("SELECT 1 FROM accounts WHERE id_account = ?1", [id], |_| { Ok(()) }).optional()?;
|
||||
if r.is_some() { return Ok(id) }
|
||||
let r = connection.query_row("SELECT MAX(id_account) FROM accounts", [], |row| {
|
||||
let id: Option<u32> = row.get(0)?;
|
||||
Ok(id)
|
||||
})?.unwrap_or(0);
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub fn get_t_addr(connection: &Connection, id: u32) -> Result<String> {
|
||||
let address = connection.query_row("SELECT address FROM taddrs WHERE account = ?1", [id], |row| {
|
||||
let address: String = row.get(0)?;
|
||||
Ok(address)
|
||||
})?;
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
pub fn get_sk(connection: &Connection, id: u32) -> Result<String> {
|
||||
let sk = connection.query_row("SELECT sk FROM accounts WHERE id_account = ?1", [id], |row| {
|
||||
let sk: Option<String> = row.get(0)?;
|
||||
Ok(sk.unwrap_or(String::new()))
|
||||
})?;
|
||||
Ok(sk)
|
||||
}
|
||||
|
||||
pub fn update_account_name(connection: &Connection, id: u32, name: &str) -> Result<()> {
|
||||
connection.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1", params![id, name])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_balances(connection: &Connection, id: u32, confirmed_height: u32) -> Result<Vec<u8>> {
|
||||
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||
let shielded = connection.query_row(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL",
|
||||
params![id], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?; // funds not spent yet
|
||||
let unconfirmed_spent = connection.query_row(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0",
|
||||
params![id], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?; // funds used in unconfirmed tx
|
||||
let balance = shielded + unconfirmed_spent;
|
||||
let under_confirmed = connection.query_row(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL AND height > ?2",
|
||||
params![id, confirmed_height], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?; // funds received but not old enough
|
||||
let excluded = connection.query_row(
|
||||
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL \
|
||||
AND height <= ?2 AND excluded",
|
||||
params![id, confirmed_height], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?; // funds excluded from spending
|
||||
let sapling = connection.query_row(
|
||||
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 0 AND height <= ?2",
|
||||
params![id, confirmed_height], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?;
|
||||
let orchard = connection.query_row(
|
||||
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 1 AND height <= ?2",
|
||||
params![id, confirmed_height], |row| {
|
||||
let value: Option<i64> = row.get(0)?;
|
||||
Ok(value.unwrap_or(0) as u64)
|
||||
})?;
|
||||
|
||||
let balance = Balance::create(&mut builder, &BalanceArgs {
|
||||
shielded,
|
||||
unconfirmed_spent,
|
||||
balance,
|
||||
under_confirmed,
|
||||
excluded,
|
||||
sapling,
|
||||
orchard
|
||||
});
|
||||
builder.finish(balance, None);
|
||||
let data = builder.finished_data().to_vec();
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_db_height(connection: &Connection) -> Result<Vec<u8>> {
|
||||
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||
let height = connection.query_row(
|
||||
"SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)",
|
||||
[], |row| {
|
||||
let height: u32 = row.get(0)?;
|
||||
let timestamp: u32 = row.get(1)?;
|
||||
let height = Height::create(&mut builder, &HeightArgs {
|
||||
height,
|
||||
timestamp,
|
||||
});
|
||||
Ok(height)
|
||||
}).optional()?;
|
||||
let data = height.map(|h| {
|
||||
builder.finish(h, None);
|
||||
builder.finished_data().to_vec()
|
||||
}).unwrap_or(vec![]);
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_notes(connection: &Connection, id: u32) -> Result<Vec<u8>> {
|
||||
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||
let mut stmt = connection.prepare(
|
||||
"SELECT n.id_note, n.height, n.value, t.timestamp, n.orchard, n.excluded, n.spent FROM received_notes n, transactions t \
|
||||
WHERE n.account = ?1 AND (n.spent IS NULL OR n.spent = 0) \
|
||||
AND n.tx = t.id_tx ORDER BY n.height DESC")?;
|
||||
let rows = stmt.query_map(params![id], |row| {
|
||||
let id: u32 = row.get("id_note")?;
|
||||
let height: u32 = row.get("height")?;
|
||||
let value: i64 = row.get("value")?;
|
||||
let timestamp: u32 = row.get("timestamp")?;
|
||||
let orchard: u8 = row.get("orchard")?;
|
||||
let excluded: Option<bool> = row.get("excluded")?;
|
||||
let spent: Option<u32> = row.get("spent")?;
|
||||
let note = ShieldedNote::create(&mut builder, &ShieldedNoteArgs {
|
||||
id,
|
||||
height,
|
||||
value: value as u64,
|
||||
timestamp,
|
||||
orchard: orchard == 1,
|
||||
excluded: excluded.unwrap_or(false),
|
||||
spent: spent.is_some()
|
||||
});
|
||||
Ok(note)
|
||||
})?;
|
||||
let mut notes = vec![];
|
||||
for r in rows {
|
||||
notes.push(r?);
|
||||
}
|
||||
let notes = builder.create_vector(¬es);
|
||||
let notes = ShieldedNoteVec::create(&mut builder, &ShieldedNoteVecArgs { notes: Some(notes) });
|
||||
builder.finish(notes, None);
|
||||
let data = builder.finished_data().to_vec();
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
|
||||
/* final id = row['id_note'];
|
||||
final height = row['height'];
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final orchard = row['orchard'] != 0;
|
||||
final excluded = (row['excluded'] ?? 0) != 0;
|
||||
final spent = row['spent'] == 0;
|
||||
|
||||
|
||||
|
||||
|
||||
final rows = await db.rawQuery("SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)");
|
||||
if (rows.isNotEmpty) {
|
||||
final row = rows.first;
|
||||
final height = row['height'] as int;
|
||||
final timestampEpoch = row['timestamp'] as int;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(timestampEpoch * 1000);
|
||||
final blockInfo = BlockInfo(height, timestamp);
|
||||
return blockInfo;
|
||||
}
|
||||
return null;
|
||||
|
||||
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 0",
|
||||
[id])) ?? 0;
|
||||
}
|
||||
|
||||
Future<int> getOrchardBalance() async {
|
||||
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 1",
|
||||
[id])) ?? 0;
|
||||
|
||||
final balance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0)",
|
||||
[id])) ?? 0;
|
||||
final shieldedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL",
|
||||
[id])) ?? 0;
|
||||
final unconfirmedSpentBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0",
|
||||
[id])) ?? 0;
|
||||
final underConfirmedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL AND height > ?2",
|
||||
[id, confirmHeight])) ?? 0;
|
||||
final excludedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL "
|
||||
"AND height <= ?2 AND excluded",
|
||||
[id, confirmHeight])) ?? 0;
|
||||
|
||||
|
||||
"", [id]);
|
||||
await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1",
|
||||
|
||||
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT address FROM taddrs WHERE account = ?1", [id]);
|
||||
final taddress = res1.isNotEmpty ? res1[0]['address'] : "";
|
||||
return taddress;
|
||||
|
||||
|
||||
// check that the account still exists
|
||||
// if not, pick any account
|
||||
// if there are none, return 0
|
||||
Future<AccountId?> getAvailableAccountId() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT 1 FROM accounts WHERE id_account = ?1", [id]);
|
||||
if (res1.isNotEmpty)
|
||||
return AccountId(coin, id);
|
||||
final List<Map> res2 = await db.rawQuery(
|
||||
"SELECT id_account FROM accounts", []);
|
||||
if (res2.isNotEmpty) {
|
||||
final id = res2[0]['id_account'];
|
||||
return AccountId(coin, id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<List<Account>> getAccountList() async {
|
||||
List<Account> accounts = [];
|
||||
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"WITH notes AS (SELECT a.id_account, a.name, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account),"
|
||||
"accounts2 AS (SELECT id_account, name, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) "
|
||||
"SELECT a.id_account, a.name, a.balance FROM accounts2 a",
|
||||
[]);
|
||||
for (var r in res) {
|
||||
final int id = r['id_account'];
|
||||
final account = Account(
|
||||
coin,
|
||||
id,
|
||||
r['name'],
|
||||
r['balance'],
|
||||
0,
|
||||
null);
|
||||
accounts.add(account);
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
// check that the account still exists
|
||||
// if not, pick any account
|
||||
// if there are none, return 0
|
||||
Future<AccountId?> getAvailableAccountId() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT 1 FROM accounts WHERE id_account = ?1", [id]);
|
||||
if (res1.isNotEmpty)
|
||||
return AccountId(coin, id);
|
||||
final List<Map> res2 = await db.rawQuery(
|
||||
"SELECT id_account FROM accounts", []);
|
||||
if (res2.isNotEmpty) {
|
||||
final id = res2[0]['id_account'];
|
||||
return AccountId(coin, id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> getTAddr() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT address FROM taddrs WHERE account = ?1", [id]);
|
||||
final taddress = res1.isNotEmpty ? res1[0]['address'] : "";
|
||||
return taddress;
|
||||
}
|
||||
|
||||
Future<String?> getSK() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT sk FROM accounts WHERE id_account = ?1", [id]);
|
||||
final sk = res1.isNotEmpty ? res1[0]['address'] : null;
|
||||
return sk;
|
||||
}
|
||||
|
||||
Future<void> changeAccountName(String name) async {
|
||||
await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1",
|
||||
[id, name]);
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue