drk: moved contracts sql stuff from their client to drk directly

Also prefixed table name with their corresponding contract idfff
This commit is contained in:
skoupidi
2024-01-30 15:34:12 +02:00
parent 42a6b92ec6
commit 6b74cebdd0
11 changed files with 215 additions and 195 deletions

1
Cargo.lock generated
View File

@@ -2655,6 +2655,7 @@ dependencies = [
"darkfi_dao_contract",
"darkfi_money_contract",
"easy-parallel",
"lazy_static",
"log",
"prettytable-rs",
"rand 0.8.5",

View File

@@ -19,6 +19,7 @@ darkfi-serial = {path = "../../src/serial"}
# Misc
blake3 = "1.5.0"
bs58 = "0.5.0"
lazy_static = "1.4.0"
log = "0.4.20"
prettytable-rs = "0.10.0"
rand = "0.8.5"

View File

@@ -103,7 +103,7 @@
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS dao_daos (
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_daos (
dao_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name BLOB UNIQUE NOT NULL,
proposer_limit BLOB NOT NULL,
@@ -124,12 +124,12 @@ CREATE TABLE IF NOT EXISTS dao_daos (
);
-- The merkle tree containing DAO bullas
CREATE TABLE IF NOT EXISTS dao_trees (
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_trees (
daos_tree BLOB NOT NULL,
proposals_tree BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS dao_proposals (
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_proposals (
proposal_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
dao_id INTEGER NOT NULL,
-- Public key of person that would receive the funds
@@ -152,7 +152,7 @@ CREATE TABLE IF NOT EXISTS dao_proposals (
FOREIGN KEY(dao_id) REFERENCES dao_daos(dao_id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS dao_votes (
CREATE TABLE IF NOT EXISTS Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj_dao_votes (
vote_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
proposal_id INTEGER NOT NULL,
vote_option INTEGER NOT NULL,

View File

@@ -3,17 +3,17 @@
-- TODO: The tables should be prefixed with ContractId to prevent collision
-- Arbitrary info that is potentially useful
CREATE TABLE IF NOT EXISTS money_info (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_info (
last_scanned_slot INTEGER NOT NULL
);
-- The Merkle tree containing coins
CREATE TABLE IF NOT EXISTS money_tree (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_tree (
tree BLOB NOT NULL
);
-- The keypairs in our wallet
CREATE TABLE IF NOT EXISTS money_keys (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_keys (
key_id INTEGER PRIMARY KEY NOT NULL,
is_default INTEGER NOT NULL,
public BLOB NOT NULL,
@@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS money_keys (
);
-- The coins we have the information to and can spend
CREATE TABLE IF NOT EXISTS money_coins (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_coins (
coin BLOB PRIMARY KEY NOT NULL,
is_spent INTEGER NOT NULL,
value BLOB NOT NULL,
@@ -38,19 +38,19 @@ CREATE TABLE IF NOT EXISTS money_coins (
);
-- Arbitrary tokens
CREATE TABLE IF NOT EXISTS money_tokens (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_tokens (
mint_authority BLOB PRIMARY KEY NOT NULL,
token_id BLOB NOT NULL,
is_frozen INTEGER NOT NULL
);
-- The token aliases in our wallet
CREATE TABLE IF NOT EXISTS money_aliases (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_money_aliases (
alias BLOB PRIMARY KEY NOT NULL,
token_id BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS transactions_history (
CREATE TABLE IF NOT EXISTS BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o_transactions_history (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
transaction_hash TEXT UNIQUE NOT NULL,
status TEXT NOT NULL,

View File

@@ -18,6 +18,7 @@
use std::{collections::HashMap, fmt};
use lazy_static::lazy_static;
use rand::rngs::OsRng;
use rusqlite::types::Value;
@@ -29,22 +30,7 @@ use darkfi::{
Error, Result,
};
use darkfi_dao_contract::{
client::{
make_mint_call, DaoProposeCall, DaoProposeStakeInput, DaoVoteCall, DaoVoteInput,
DAO_DAOS_COL_APPROVAL_RATIO_BASE, DAO_DAOS_COL_APPROVAL_RATIO_QUOT,
DAO_DAOS_COL_BULLA_BLIND, DAO_DAOS_COL_CALL_INDEX, DAO_DAOS_COL_DAO_ID,
DAO_DAOS_COL_GOV_TOKEN_ID, DAO_DAOS_COL_LEAF_POSITION, DAO_DAOS_COL_NAME,
DAO_DAOS_COL_PROPOSER_LIMIT, DAO_DAOS_COL_QUORUM, DAO_DAOS_COL_SECRET,
DAO_DAOS_COL_TX_HASH, DAO_DAOS_TABLE, DAO_PROPOSALS_COL_AMOUNT,
DAO_PROPOSALS_COL_BULLA_BLIND, DAO_PROPOSALS_COL_CALL_INDEX, DAO_PROPOSALS_COL_DAO_ID,
DAO_PROPOSALS_COL_LEAF_POSITION, DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
DAO_PROPOSALS_COL_PROPOSAL_ID, DAO_PROPOSALS_COL_RECV_PUBLIC,
DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID, DAO_PROPOSALS_COL_TX_HASH, DAO_PROPOSALS_TABLE,
DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE, DAO_TREES_TABLE,
DAO_VOTES_COL_ALL_VOTE_BLIND, DAO_VOTES_COL_ALL_VOTE_VALUE, DAO_VOTES_COL_CALL_INDEX,
DAO_VOTES_COL_PROPOSAL_ID, DAO_VOTES_COL_TX_HASH, DAO_VOTES_COL_VOTE_OPTION,
DAO_VOTES_COL_YES_VOTE_BLIND, DAO_VOTES_TABLE,
},
client::{make_mint_call, DaoProposeCall, DaoProposeStakeInput, DaoVoteCall, DaoVoteInput},
model::{DaoAuthCall, DaoBulla, DaoMintParams, DaoProposeParams, DaoVoteParams},
DaoFunction, DAO_CONTRACT_ZKAS_DAO_MINT_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_INPUT_NS,
DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS, DAO_CONTRACT_ZKAS_DAO_VOTE_INPUT_NS,
@@ -73,6 +59,62 @@ use crate::{
Drk,
};
// Wallet SQL table constant names. These have to represent the `wallet.sql`
// SQL schema. Table names are prefixed with the contract ID to avoid collisions.
lazy_static! {
pub static ref DAO_DAOS_TABLE: String = format!("{}_dao_daos", DAO_CONTRACT_ID.to_string());
pub static ref DAO_TREES_TABLE: String = format!("{}_dao_trees", DAO_CONTRACT_ID.to_string());
pub static ref DAO_COINS_TABLE: String = format!("{}_dao_coins", DAO_CONTRACT_ID.to_string());
pub static ref DAO_PROPOSALS_TABLE: String =
format!("{}_dao_proposals", DAO_CONTRACT_ID.to_string());
pub static ref DAO_VOTES_TABLE: String = format!("{}_dao_votes", DAO_CONTRACT_ID.to_string());
}
// DAO_DAOS_TABLE
pub const DAO_DAOS_COL_DAO_ID: &str = "dao_id";
pub const DAO_DAOS_COL_NAME: &str = "name";
pub const DAO_DAOS_COL_PROPOSER_LIMIT: &str = "proposer_limit";
pub const DAO_DAOS_COL_QUORUM: &str = "quorum";
pub const DAO_DAOS_COL_APPROVAL_RATIO_BASE: &str = "approval_ratio_base";
pub const DAO_DAOS_COL_APPROVAL_RATIO_QUOT: &str = "approval_ratio_quot";
pub const DAO_DAOS_COL_GOV_TOKEN_ID: &str = "gov_token_id";
pub const DAO_DAOS_COL_SECRET: &str = "secret";
pub const DAO_DAOS_COL_BULLA_BLIND: &str = "bulla_blind";
pub const DAO_DAOS_COL_LEAF_POSITION: &str = "leaf_position";
pub const DAO_DAOS_COL_TX_HASH: &str = "tx_hash";
pub const DAO_DAOS_COL_CALL_INDEX: &str = "call_index";
// DAO_TREES_TABLE
pub const DAO_TREES_COL_DAOS_TREE: &str = "daos_tree";
pub const DAO_TREES_COL_PROPOSALS_TREE: &str = "proposals_tree";
// DAO_COINS_TABLE
pub const _DAO_COINS_COL_COIN_ID: &str = "coin_id";
pub const _DAO_COINS_COL_DAO_ID: &str = "dao_id";
// DAO_PROPOSALS_TABLE
pub const DAO_PROPOSALS_COL_PROPOSAL_ID: &str = "proposal_id";
pub const DAO_PROPOSALS_COL_DAO_ID: &str = "dao_id";
pub const DAO_PROPOSALS_COL_RECV_PUBLIC: &str = "recv_public";
pub const DAO_PROPOSALS_COL_AMOUNT: &str = "amount";
pub const DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID: &str = "sendcoin_token_id";
pub const DAO_PROPOSALS_COL_BULLA_BLIND: &str = "bulla_blind";
pub const DAO_PROPOSALS_COL_LEAF_POSITION: &str = "leaf_position";
pub const DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE: &str = "money_snapshot_tree";
pub const DAO_PROPOSALS_COL_TX_HASH: &str = "tx_hash";
pub const DAO_PROPOSALS_COL_CALL_INDEX: &str = "call_index";
pub const _DAO_PROPOSALS_COL_OUR_VOTE_ID: &str = "our_vote_id";
// DAO_VOTES_TABLE
pub const _DAO_VOTES_COL_VOTE_ID: &str = "vote_id";
pub const DAO_VOTES_COL_PROPOSAL_ID: &str = "proposal_id";
pub const DAO_VOTES_COL_VOTE_OPTION: &str = "vote_option";
pub const DAO_VOTES_COL_YES_VOTE_BLIND: &str = "yes_vote_blind";
pub const DAO_VOTES_COL_ALL_VOTE_VALUE: &str = "all_vote_value";
pub const DAO_VOTES_COL_ALL_VOTE_BLIND: &str = "all_vote_blind";
pub const DAO_VOTES_COL_TX_HASH: &str = "tx_hash";
pub const DAO_VOTES_COL_CALL_INDEX: &str = "call_index";
#[derive(SerialEncodable, SerialDecodable, Clone)]
pub struct DaoProposalInfo {
pub dest: PublicKey,
@@ -319,7 +361,7 @@ impl Drk {
/// Initialize wallet with tables for the DAO contract.
pub async fn initialize_dao(&self) -> WalletDbResult<()> {
// Initialize DAO wallet schema
let wallet_schema = include_str!("../../../src/contract/dao/wallet.sql");
let wallet_schema = include_str!("../dao.sql");
self.wallet.exec_batch_sql(wallet_schema).await?;
// Check if we have to initialize the Merkle trees.
@@ -327,7 +369,11 @@ impl Drk {
// a bit better and safer.
// For now, on success, we don't care what's returned, but in the future
// we should actually check it.
if self.wallet.query_single(DAO_TREES_TABLE, &[DAO_TREES_COL_DAOS_TREE], &[]).await.is_err()
if self
.wallet
.query_single(&DAO_TREES_TABLE, &[DAO_TREES_COL_DAOS_TREE], &[])
.await
.is_err()
{
eprintln!("Initializing DAO Merkle trees");
let tree = MerkleTree::new(100);
@@ -345,13 +391,13 @@ impl Drk {
proposals_tree: &MerkleTree,
) -> WalletDbResult<()> {
// First we remove old records
let query = format!("DELETE FROM {};", DAO_TREES_TABLE);
let query = format!("DELETE FROM {};", *DAO_TREES_TABLE);
self.wallet.exec_sql(&query, &[]).await?;
// then we insert the new one
let query = format!(
"INSERT INTO {} ({}, {}) VALUES (?1, ?2);",
DAO_TREES_TABLE, DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE,
*DAO_TREES_TABLE, DAO_TREES_COL_DAOS_TREE, DAO_TREES_COL_PROPOSALS_TREE,
);
self.wallet
.exec_sql(&query, rusqlite::params![serialize(daos_tree), serialize(proposals_tree)])
@@ -360,7 +406,7 @@ impl Drk {
/// Fetch DAO Merkle trees from the wallet.
pub async fn get_dao_trees(&self) -> Result<(MerkleTree, MerkleTree)> {
let row = match self.wallet.query_single(DAO_TREES_TABLE, &[], &[]).await {
let row = match self.wallet.query_single(&DAO_TREES_TABLE, &[], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
@@ -395,7 +441,7 @@ impl Drk {
/// Fetch all known DAOs from the wallet.
pub async fn get_daos(&self) -> Result<Vec<Dao>> {
let rows = match self.wallet.query_multiple(DAO_DAOS_TABLE, &[], &[]).await {
let rows = match self.wallet.query_multiple(&DAO_DAOS_TABLE, &[], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!("[get_daos] DAOs retrieval failed: {e:?}")))
@@ -611,7 +657,7 @@ impl Drk {
let rows = match self
.wallet
.query_multiple(
DAO_PROPOSALS_TABLE,
&DAO_PROPOSALS_TABLE,
&[],
convert_named_params! {(DAO_PROPOSALS_COL_DAO_ID, dao_id)},
)
@@ -822,7 +868,7 @@ impl Drk {
for dao in daos {
let query = format!(
"UPDATE {} SET {} = ?1, {} = ?2, {} = ?3 WHERE {} = {};",
DAO_DAOS_TABLE,
*DAO_DAOS_TABLE,
DAO_DAOS_COL_LEAF_POSITION,
DAO_DAOS_COL_TX_HASH,
DAO_DAOS_COL_CALL_INDEX,
@@ -849,7 +895,7 @@ impl Drk {
for dao in daos {
let query = format!(
"UPDATE {} SET {} = ?1, {} = ?2, {} = ?3 WHERE {} = {};",
DAO_DAOS_TABLE,
*DAO_DAOS_TABLE,
DAO_DAOS_COL_LEAF_POSITION,
DAO_DAOS_COL_TX_HASH,
DAO_DAOS_COL_CALL_INDEX,
@@ -877,7 +923,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9);",
DAO_PROPOSALS_TABLE,
*DAO_PROPOSALS_TABLE,
DAO_PROPOSALS_COL_DAO_ID,
DAO_PROPOSALS_COL_RECV_PUBLIC,
DAO_PROPOSALS_COL_AMOUNT,
@@ -923,7 +969,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);",
DAO_VOTES_TABLE,
*DAO_VOTES_TABLE,
DAO_VOTES_COL_PROPOSAL_ID,
DAO_VOTES_COL_VOTE_OPTION,
DAO_VOTES_COL_YES_VOTE_BLIND,
@@ -983,14 +1029,14 @@ impl Drk {
/// Reset all DAO proposals in the wallet.
pub async fn reset_dao_proposals(&self) -> WalletDbResult<()> {
eprintln!("Resetting DAO proposals");
let query = format!("DELETE FROM {};", DAO_PROPOSALS_TABLE);
let query = format!("DELETE FROM {};", *DAO_PROPOSALS_TABLE);
self.wallet.exec_sql(&query, &[]).await
}
/// Reset all DAO votes in the wallet.
pub async fn reset_dao_votes(&self) -> WalletDbResult<()> {
eprintln!("Resetting DAO votes");
let query = format!("DELETE FROM {};", DAO_VOTES_TABLE);
let query = format!("DELETE FROM {};", *DAO_VOTES_TABLE);
self.wallet.exec_sql(&query, &[]).await
}
@@ -1011,7 +1057,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);",
DAO_DAOS_TABLE,
*DAO_DAOS_TABLE,
DAO_DAOS_COL_NAME,
DAO_DAOS_COL_PROPOSER_LIMIT,
DAO_DAOS_COL_QUORUM,
@@ -1049,7 +1095,7 @@ impl Drk {
let row = match self
.wallet
.query_single(
DAO_DAOS_TABLE,
&DAO_DAOS_TABLE,
&[DAO_DAOS_COL_DAO_ID],
convert_named_params! {(DAO_DAOS_COL_NAME, alias_filter)},
)
@@ -1155,7 +1201,7 @@ impl Drk {
let row = match self
.wallet
.query_single(
DAO_PROPOSALS_TABLE,
&DAO_PROPOSALS_TABLE,
&[],
convert_named_params! {(DAO_PROPOSALS_COL_PROPOSAL_ID, proposal_id)},
)
@@ -1187,7 +1233,7 @@ impl Drk {
let rows = match self
.wallet
.query_multiple(
DAO_VOTES_TABLE,
&DAO_VOTES_TABLE,
&[],
convert_named_params! {(DAO_VOTES_COL_PROPOSAL_ID, proposal_id)},
)

View File

@@ -18,23 +18,13 @@
use std::{collections::HashMap, str::FromStr};
use lazy_static::lazy_static;
use rand::rngs::OsRng;
use rusqlite::types::Value;
use darkfi::{tx::Transaction, zk::halo2::Field, Error, Result};
use darkfi_money_contract::{
client::{
MoneyNote, OwnCoin, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
MONEY_ALIASES_TABLE, MONEY_COINS_COL_COIN, MONEY_COINS_COL_IS_SPENT,
MONEY_COINS_COL_LEAF_POSITION, MONEY_COINS_COL_MEMO, MONEY_COINS_COL_NULLIFIER,
MONEY_COINS_COL_SECRET, MONEY_COINS_COL_SERIAL, MONEY_COINS_COL_SPEND_HOOK,
MONEY_COINS_COL_TOKEN_BLIND, MONEY_COINS_COL_TOKEN_ID, MONEY_COINS_COL_USER_DATA,
MONEY_COINS_COL_VALUE, MONEY_COINS_COL_VALUE_BLIND, MONEY_COINS_TABLE,
MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE, MONEY_KEYS_COL_IS_DEFAULT,
MONEY_KEYS_COL_KEY_ID, MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET, MONEY_KEYS_TABLE,
MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
},
client::{MoneyNote, OwnCoin},
model::{
Coin, MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1, MoneyTransferParamsV1, Output,
},
@@ -56,13 +46,66 @@ use crate::{
kaching, Drk,
};
// Wallet SQL table constant names. These have to represent the `wallet.sql`
// SQL schema. Table names are prefixed with the contract ID to avoid collisions.
lazy_static! {
pub static ref MONEY_INFO_TABLE: String =
format!("{}_money_info", MONEY_CONTRACT_ID.to_string());
pub static ref MONEY_TREE_TABLE: String =
format!("{}_money_tree", MONEY_CONTRACT_ID.to_string());
pub static ref MONEY_KEYS_TABLE: String =
format!("{}_money_keys", MONEY_CONTRACT_ID.to_string());
pub static ref MONEY_COINS_TABLE: String =
format!("{}_money_coins", MONEY_CONTRACT_ID.to_string());
pub static ref MONEY_TOKENS_TABLE: String =
format!("{}_money_tokens", MONEY_CONTRACT_ID.to_string());
pub static ref MONEY_ALIASES_TABLE: String =
format!("{}_money_aliases", MONEY_CONTRACT_ID.to_string());
}
// MONEY_INFO_TABLE
pub const MONEY_INFO_COL_LAST_SCANNED_SLOT: &str = "last_scanned_slot";
// MONEY_TREE_TABLE
pub const MONEY_TREE_COL_TREE: &str = "tree";
// MONEY_KEYS_TABLE
pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
pub const MONEY_KEYS_COL_SECRET: &str = "secret";
// MONEY_COINS_TABLE
pub const MONEY_COINS_COL_COIN: &str = "coin";
pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
pub const MONEY_COINS_COL_SERIAL: &str = "serial";
pub const MONEY_COINS_COL_VALUE: &str = "value";
pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
pub const MONEY_COINS_COL_SPEND_HOOK: &str = "spend_hook";
pub const MONEY_COINS_COL_USER_DATA: &str = "user_data";
pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
pub const MONEY_COINS_COL_SECRET: &str = "secret";
pub const MONEY_COINS_COL_NULLIFIER: &str = "nullifier";
pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
pub const MONEY_COINS_COL_MEMO: &str = "memo";
// MONEY_TOKENS_TABLE
pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
// MONEY_ALIASES_TABLE
pub const MONEY_ALIASES_COL_ALIAS: &str = "alias";
pub const MONEY_ALIASES_COL_TOKEN_ID: &str = "token_id";
pub const BALANCE_BASE10_DECIMALS: usize = 8;
impl Drk {
/// Initialize wallet with tables for the Money contract.
pub async fn initialize_money(&self) -> WalletDbResult<()> {
// Initialize Money wallet schema
let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
let wallet_schema = include_str!("../money.sql");
self.wallet.exec_batch_sql(wallet_schema).await?;
// Check if we have to initialize the Merkle tree.
@@ -84,7 +127,7 @@ impl Drk {
if self.last_scanned_slot().await.is_err() {
let query = format!(
"INSERT INTO {} ({}) VALUES (?1);",
MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
*MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
);
self.wallet.exec_sql(&query, rusqlite::params![0]).await?;
}
@@ -102,7 +145,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
MONEY_KEYS_TABLE,
*MONEY_KEYS_TABLE,
MONEY_KEYS_COL_IS_DEFAULT,
MONEY_KEYS_COL_PUBLIC,
MONEY_KEYS_COL_SECRET
@@ -129,7 +172,7 @@ impl Drk {
let row = match self
.wallet
.query_single(
MONEY_KEYS_TABLE,
&MONEY_KEYS_TABLE,
&[MONEY_KEYS_COL_SECRET],
convert_named_params! {(MONEY_KEYS_COL_IS_DEFAULT, 1)},
)
@@ -156,7 +199,7 @@ impl Drk {
let row = match self
.wallet
.query_single(
MONEY_KEYS_TABLE,
&MONEY_KEYS_TABLE,
&[MONEY_KEYS_COL_PUBLIC],
convert_named_params! {(MONEY_KEYS_COL_IS_DEFAULT, 1)},
)
@@ -182,21 +225,21 @@ impl Drk {
pub async fn set_default_address(&self, idx: usize) -> WalletDbResult<()> {
// First we update previous default record
let is_default = 0;
let query = format!("UPDATE {} SET {} = ?1", MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT,);
let query = format!("UPDATE {} SET {} = ?1", *MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT,);
self.wallet.exec_sql(&query, rusqlite::params![is_default]).await?;
// and then we set the new one
let is_default = 1;
let query = format!(
"UPDATE {} SET {} = ?1 WHERE {} = ?2",
MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_KEY_ID,
*MONEY_KEYS_TABLE, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_KEY_ID,
);
self.wallet.exec_sql(&query, rusqlite::params![is_default, idx]).await
}
/// Fetch all pukeys from the wallet.
pub async fn addresses(&self) -> Result<Vec<(u64, PublicKey, SecretKey, u64)>> {
let rows = match self.wallet.query_multiple(MONEY_KEYS_TABLE, &[], &[]).await {
let rows = match self.wallet.query_multiple(&MONEY_KEYS_TABLE, &[], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
@@ -239,16 +282,18 @@ impl Drk {
/// Fetch all secret keys from the wallet.
pub async fn get_money_secrets(&self) -> Result<Vec<SecretKey>> {
let rows =
match self.wallet.query_multiple(MONEY_KEYS_TABLE, &[MONEY_KEYS_COL_SECRET], &[]).await
{
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
"[get_money_secrets] Secret keys retrieval failed: {e:?}"
)))
}
};
let rows = match self
.wallet
.query_multiple(&MONEY_KEYS_TABLE, &[MONEY_KEYS_COL_SECRET], &[])
.await
{
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
"[get_money_secrets] Secret keys retrieval failed: {e:?}"
)))
}
};
let mut secrets = Vec::with_capacity(rows.len());
@@ -288,7 +333,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
MONEY_KEYS_TABLE,
*MONEY_KEYS_TABLE,
MONEY_KEYS_COL_IS_DEFAULT,
MONEY_KEYS_COL_PUBLIC,
MONEY_KEYS_COL_SECRET
@@ -331,11 +376,11 @@ impl Drk {
/// The boolean in the returned tuple notes if the coin was marked as spent.
pub async fn get_coins(&self, fetch_spent: bool) -> Result<Vec<(OwnCoin, bool)>> {
let query = if fetch_spent {
self.wallet.query_multiple(MONEY_COINS_TABLE, &[], &[]).await
self.wallet.query_multiple(&MONEY_COINS_TABLE, &[], &[]).await
} else {
self.wallet
.query_multiple(
MONEY_COINS_TABLE,
&MONEY_COINS_TABLE,
&[],
convert_named_params! {(MONEY_COINS_COL_IS_SPENT, false)},
)
@@ -444,7 +489,7 @@ impl Drk {
eprintln!("Generating alias {alias} for Token: {token_id}");
let query = format!(
"INSERT OR REPLACE INTO {} ({}, {}) VALUES (?1, ?2);",
MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
*MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS, MONEY_ALIASES_COL_TOKEN_ID,
);
self.wallet
.exec_sql(&query, rusqlite::params![serialize(&alias), serialize(&token_id)])
@@ -458,7 +503,7 @@ impl Drk {
alias_filter: Option<String>,
token_id_filter: Option<TokenId>,
) -> Result<HashMap<String, TokenId>> {
let rows = match self.wallet.query_multiple(MONEY_ALIASES_TABLE, &[], &[]).await {
let rows = match self.wallet.query_multiple(&MONEY_ALIASES_TABLE, &[], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
@@ -512,8 +557,10 @@ impl Drk {
/// Remove provided alias record from the wallet database.
pub async fn remove_alias(&self, alias: String) -> WalletDbResult<()> {
eprintln!("Removing alias: {alias}");
let query =
format!("DELETE FROM {} WHERE {} = ?1;", MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS,);
let query = format!(
"DELETE FROM {} WHERE {} = ?1;",
*MONEY_ALIASES_TABLE, MONEY_ALIASES_COL_ALIAS,
);
self.wallet.exec_sql(&query, rusqlite::params![serialize(&alias)]).await
}
@@ -522,7 +569,7 @@ impl Drk {
let is_spend = 0;
let query = format!(
"UPDATE {} SET {} = ?1 WHERE {} = ?2",
MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN,
*MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN,
);
self.wallet.exec_sql(&query, rusqlite::params![is_spend, serialize(&coin.inner())]).await
}
@@ -530,19 +577,19 @@ impl Drk {
/// Replace the Money Merkle tree in the wallet.
pub async fn put_money_tree(&self, tree: &MerkleTree) -> WalletDbResult<()> {
// First we remove old record
let query = format!("DELETE FROM {};", MONEY_TREE_TABLE);
let query = format!("DELETE FROM {};", *MONEY_TREE_TABLE);
self.wallet.exec_sql(&query, &[]).await?;
// then we insert the new one
let query =
format!("INSERT INTO {} ({}) VALUES (?1);", MONEY_TREE_TABLE, MONEY_TREE_COL_TREE,);
format!("INSERT INTO {} ({}) VALUES (?1);", *MONEY_TREE_TABLE, MONEY_TREE_COL_TREE,);
self.wallet.exec_sql(&query, rusqlite::params![serialize(tree)]).await
}
/// Fetch the Money Merkle tree from the wallet.
pub async fn get_money_tree(&self) -> Result<MerkleTree> {
let row =
match self.wallet.query_single(MONEY_TREE_TABLE, &[MONEY_TREE_COL_TREE], &[]).await {
match self.wallet.query_single(&MONEY_TREE_TABLE, &[MONEY_TREE_COL_TREE], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(
@@ -562,7 +609,7 @@ impl Drk {
pub async fn last_scanned_slot(&self) -> WalletDbResult<u64> {
let ret = self
.wallet
.query_single(MONEY_INFO_TABLE, &[MONEY_INFO_COL_LAST_SCANNED_SLOT], &[])
.query_single(&MONEY_INFO_TABLE, &[MONEY_INFO_COL_LAST_SCANNED_SLOT], &[])
.await?;
let Value::Integer(slot) = ret[0] else {
return Err(WalletDbError::ParseColumnValueError);
@@ -677,7 +724,7 @@ impl Drk {
// into the wallet
let query = format!(
"INSERT INTO {} ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13);",
MONEY_COINS_TABLE,
*MONEY_COINS_TABLE,
MONEY_COINS_COL_COIN,
MONEY_COINS_COL_IS_SPENT,
MONEY_COINS_COL_SERIAL,
@@ -722,7 +769,7 @@ impl Drk {
for token_id in freezes {
let query = format!(
"UPDATE {} SET {} = 1 WHERE {} = ?1;",
MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID,
*MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_TOKEN_ID,
);
if let Err(e) =
@@ -745,7 +792,7 @@ impl Drk {
pub async fn mark_spent_coin(&self, coin: &Coin) -> WalletDbResult<()> {
let query = format!(
"UPDATE {} SET {} = ?1 WHERE {} = ?2;",
MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
*MONEY_COINS_TABLE, MONEY_COINS_COL_IS_SPENT, MONEY_COINS_COL_COIN
);
let is_spent = 1;
self.wallet.exec_sql(&query, rusqlite::params![is_spent, serialize(&coin.inner())]).await
@@ -785,7 +832,7 @@ impl Drk {
/// Reset the Money coins in the wallet
pub async fn reset_money_coins(&self) -> WalletDbResult<()> {
eprintln!("Resetting coins");
let query = format!("DELETE FROM {};", MONEY_COINS_TABLE);
let query = format!("DELETE FROM {};", *MONEY_COINS_TABLE);
self.wallet.exec_sql(&query, &[]).await?;
eprintln!("Successfully reset coins");

View File

@@ -32,12 +32,12 @@ use darkfi::{
util::encoding::base64,
Error, Result,
};
use darkfi_money_contract::client::{MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE};
use darkfi_sdk::crypto::ContractId;
use darkfi_serial::{deserialize, serialize};
use super::{
use crate::{
error::{WalletDbError, WalletDbResult},
money::{MONEY_INFO_COL_LAST_SCANNED_SLOT, MONEY_INFO_TABLE},
Drk,
};
@@ -179,7 +179,7 @@ impl Drk {
// Write this slot into `last_scanned_slot`
let query =
format!("UPDATE {} SET {} = ?1;", MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT);
format!("UPDATE {} SET {} = ?1;", *MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT);
if let Err(e) = self.wallet.exec_sql(&query, rusqlite::params![block.header.height]).await {
return Err(Error::RusqliteError(format!(
"[scan_block_money] Update last scanned slot failed: {e:?}"
@@ -264,7 +264,7 @@ impl Drk {
// This might be a bit intense, but we accept it for now.
let query = format!(
"UPDATE {} SET {} = ?1;",
MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
*MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_SLOT
);
self.wallet.exec_sql(&query, rusqlite::params![sl]).await?;
}

View File

@@ -27,11 +27,7 @@ use darkfi::{
Error, Result,
};
use darkfi_money_contract::{
client::{
token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder,
MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY, MONEY_TOKENS_COL_TOKEN_ID,
MONEY_TOKENS_TABLE,
},
client::{token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder},
MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
};
use darkfi_sdk::{
@@ -41,7 +37,14 @@ use darkfi_sdk::{
};
use darkfi_serial::{deserialize, serialize, Encodable};
use crate::{error::WalletDbResult, money::BALANCE_BASE10_DECIMALS, Drk};
use crate::{
error::WalletDbResult,
money::{
BALANCE_BASE10_DECIMALS, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY,
MONEY_TOKENS_COL_TOKEN_ID, MONEY_TOKENS_TABLE,
},
Drk,
};
impl Drk {
/// Import a token mint authority into the wallet
@@ -51,7 +54,7 @@ impl Drk {
let query = format!(
"INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
MONEY_TOKENS_TABLE,
*MONEY_TOKENS_TABLE,
MONEY_TOKENS_COL_MINT_AUTHORITY,
MONEY_TOKENS_COL_TOKEN_ID,
MONEY_TOKENS_COL_IS_FROZEN,
@@ -66,7 +69,7 @@ impl Drk {
}
pub async fn list_tokens(&self) -> Result<Vec<(TokenId, SecretKey, bool)>> {
let rows = match self.wallet.query_multiple(MONEY_TOKENS_TABLE, &[], &[]).await {
let rows = match self.wallet.query_multiple(&MONEY_TOKENS_TABLE, &[], &[]).await {
Ok(r) => r,
Err(e) => {
return Err(Error::RusqliteError(format!(

View File

@@ -16,9 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use lazy_static::lazy_static;
use rusqlite::types::Value;
use darkfi::{tx::Transaction, Error, Result};
use darkfi_sdk::crypto::MONEY_CONTRACT_ID;
use darkfi_serial::{deserialize, serialize};
use crate::{
@@ -28,8 +30,11 @@ use crate::{
};
// Wallet SQL table constant names. These have to represent the `wallet.sql`
// SQL schema.
const WALLET_TXS_HISTORY_TABLE: &str = "transactions_history";
// SQL schema. Table names are prefixed with the contract ID to avoid collisions.
lazy_static! {
pub static ref WALLET_TXS_HISTORY_TABLE: String =
format!("{}_transactions_history", MONEY_CONTRACT_ID.to_string());
}
const WALLET_TXS_HISTORY_COL_TX_HASH: &str = "transaction_hash";
const WALLET_TXS_HISTORY_COL_STATUS: &str = "status";
const WALLET_TXS_HISTORY_COL_TX: &str = "tx";
@@ -39,7 +44,7 @@ impl Drk {
pub async fn insert_tx_history_record(&self, tx: &Transaction) -> WalletDbResult<()> {
let query = format!(
"INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
WALLET_TXS_HISTORY_TABLE,
*WALLET_TXS_HISTORY_TABLE,
WALLET_TXS_HISTORY_COL_TX_HASH,
WALLET_TXS_HISTORY_COL_STATUS,
WALLET_TXS_HISTORY_COL_TX,
@@ -65,7 +70,7 @@ impl Drk {
let row = match self
.wallet
.query_single(
WALLET_TXS_HISTORY_TABLE,
&WALLET_TXS_HISTORY_TABLE,
&[],
convert_named_params! {(WALLET_TXS_HISTORY_COL_TX_HASH, tx_hash)},
)
@@ -107,7 +112,7 @@ impl Drk {
let rows = self
.wallet
.query_multiple(
WALLET_TXS_HISTORY_TABLE,
&WALLET_TXS_HISTORY_TABLE,
&[WALLET_TXS_HISTORY_COL_TX_HASH, WALLET_TXS_HISTORY_COL_STATUS],
&[],
)
@@ -139,7 +144,9 @@ impl Drk {
) -> WalletDbResult<()> {
let query = format!(
"UPDATE {} SET {} = ?1 WHERE {} = ?2;",
WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS, WALLET_TXS_HISTORY_COL_TX_HASH,
*WALLET_TXS_HISTORY_TABLE,
WALLET_TXS_HISTORY_COL_STATUS,
WALLET_TXS_HISTORY_COL_TX_HASH,
);
self.wallet.exec_sql(&query, rusqlite::params![status, tx_hash]).await
}
@@ -162,7 +169,7 @@ impl Drk {
let txs_hashes_string = format!("{:?}", txs_hashes).replace('[', "(").replace(']', ")");
let query = format!(
"UPDATE {} SET {} = ?1 WHERE {} IN {};",
WALLET_TXS_HISTORY_TABLE,
*WALLET_TXS_HISTORY_TABLE,
WALLET_TXS_HISTORY_COL_STATUS,
WALLET_TXS_HISTORY_COL_TX_HASH,
txs_hashes_string
@@ -175,7 +182,7 @@ impl Drk {
pub async fn update_all_tx_history_records_status(&self, status: &str) -> WalletDbResult<()> {
let query = format!(
"UPDATE {} SET {} = ?1",
WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS,
*WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS,
);
self.wallet.exec_sql(&query, rusqlite::params![status]).await
}

View File

@@ -39,49 +39,3 @@ pub use exec::DaoExecCall;
pub mod auth_xfer;
pub use auth_xfer::DaoAuthMoneyTransferCall;
// Wallet SQL table constant names. These have to represent the SQL schema.
pub const DAO_DAOS_TABLE: &str = "dao_daos";
pub const DAO_DAOS_COL_DAO_ID: &str = "dao_id";
pub const DAO_DAOS_COL_NAME: &str = "name";
pub const DAO_DAOS_COL_PROPOSER_LIMIT: &str = "proposer_limit";
pub const DAO_DAOS_COL_QUORUM: &str = "quorum";
pub const DAO_DAOS_COL_APPROVAL_RATIO_BASE: &str = "approval_ratio_base";
pub const DAO_DAOS_COL_APPROVAL_RATIO_QUOT: &str = "approval_ratio_quot";
pub const DAO_DAOS_COL_GOV_TOKEN_ID: &str = "gov_token_id";
pub const DAO_DAOS_COL_SECRET: &str = "secret";
pub const DAO_DAOS_COL_BULLA_BLIND: &str = "bulla_blind";
pub const DAO_DAOS_COL_LEAF_POSITION: &str = "leaf_position";
pub const DAO_DAOS_COL_TX_HASH: &str = "tx_hash";
pub const DAO_DAOS_COL_CALL_INDEX: &str = "call_index";
pub const DAO_TREES_TABLE: &str = "dao_trees";
pub const DAO_TREES_COL_DAOS_TREE: &str = "daos_tree";
pub const DAO_TREES_COL_PROPOSALS_TREE: &str = "proposals_tree";
pub const DAO_COINS_TABLE: &str = "dao_coins";
pub const DAO_COINS_COL_COIN_ID: &str = "coin_id";
pub const DAO_COINS_COL_DAO_ID: &str = "dao_id";
pub const DAO_PROPOSALS_TABLE: &str = "dao_proposals";
pub const DAO_PROPOSALS_COL_PROPOSAL_ID: &str = "proposal_id";
pub const DAO_PROPOSALS_COL_DAO_ID: &str = "dao_id";
pub const DAO_PROPOSALS_COL_RECV_PUBLIC: &str = "recv_public";
pub const DAO_PROPOSALS_COL_AMOUNT: &str = "amount";
pub const DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID: &str = "sendcoin_token_id";
pub const DAO_PROPOSALS_COL_BULLA_BLIND: &str = "bulla_blind";
pub const DAO_PROPOSALS_COL_LEAF_POSITION: &str = "leaf_position";
pub const DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE: &str = "money_snapshot_tree";
pub const DAO_PROPOSALS_COL_TX_HASH: &str = "tx_hash";
pub const DAO_PROPOSALS_COL_CALL_INDEX: &str = "call_index";
pub const DAO_PROPOSALS_COL_OUR_VOTE_ID: &str = "our_vote_id";
pub const DAO_VOTES_TABLE: &str = "dao_votes";
pub const DAO_VOTES_COL_VOTE_ID: &str = "vote_id";
pub const DAO_VOTES_COL_PROPOSAL_ID: &str = "proposal_id";
pub const DAO_VOTES_COL_VOTE_OPTION: &str = "vote_option";
pub const DAO_VOTES_COL_YES_VOTE_BLIND: &str = "yes_vote_blind";
pub const DAO_VOTES_COL_ALL_VOTE_VALUE: &str = "all_vote_value";
pub const DAO_VOTES_COL_ALL_VOTE_BLIND: &str = "all_vote_blind";
pub const DAO_VOTES_COL_TX_HASH: &str = "tx_hash";
pub const DAO_VOTES_COL_CALL_INDEX: &str = "call_index";

View File

@@ -56,45 +56,6 @@ pub mod token_freeze_v1;
/// `Money::PoWRewardV1` API
pub mod pow_reward_v1;
// Wallet SQL table constant names. These have to represent the `wallet.sql`
// SQL schema.
// TODO: They should also be prefixed with the contract ID to avoid collisions.
pub const MONEY_INFO_TABLE: &str = "money_info";
pub const MONEY_INFO_COL_LAST_SCANNED_SLOT: &str = "last_scanned_slot";
pub const MONEY_TREE_TABLE: &str = "money_tree";
pub const MONEY_TREE_COL_TREE: &str = "tree";
pub const MONEY_KEYS_TABLE: &str = "money_keys";
pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
pub const MONEY_KEYS_COL_SECRET: &str = "secret";
pub const MONEY_COINS_TABLE: &str = "money_coins";
pub const MONEY_COINS_COL_COIN: &str = "coin";
pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
pub const MONEY_COINS_COL_SERIAL: &str = "serial";
pub const MONEY_COINS_COL_VALUE: &str = "value";
pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
pub const MONEY_COINS_COL_SPEND_HOOK: &str = "spend_hook";
pub const MONEY_COINS_COL_USER_DATA: &str = "user_data";
pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
pub const MONEY_COINS_COL_SECRET: &str = "secret";
pub const MONEY_COINS_COL_NULLIFIER: &str = "nullifier";
pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
pub const MONEY_COINS_COL_MEMO: &str = "memo";
pub const MONEY_TOKENS_TABLE: &str = "money_tokens";
pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
pub const MONEY_ALIASES_TABLE: &str = "money_aliases";
pub const MONEY_ALIASES_COL_ALIAS: &str = "alias";
pub const MONEY_ALIASES_COL_TOKEN_ID: &str = "token_id";
/// `MoneyNote` holds the inner attributes of a `Coin`
/// It does not store the public key since it's encrypted for that key,
/// and so is not needed to infer the coin attributes.