From 7c8d03446cb021a2e02782cda6ed435e96eebcea Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sat, 16 Mar 2024 12:34:47 -0400 Subject: [PATCH] Next --- .gitignore | 3 + migrations/20240314021456_init.sql | 16 +++ src/spends/cat.rs | 8 +- src/spends/standard.rs | 8 +- src/sqlite.rs | 2 + src/sqlite/coin_store.rs | 101 ++++++++++++++++++ src/sqlite/hardened_cat_store.rs | 0 src/sqlite/hardened_key_store.rs | 4 +- src/sqlite/unhardened_cat_store.rs | 0 src/sqlite/unhardened_key_store.rs | 4 +- src/stores.rs | 6 +- src/stores/coin_store.rs | 18 ---- .../{derivation_store.rs => puzzle_store.rs} | 2 +- test.sqlite | Bin 28672 -> 0 bytes 14 files changed, 137 insertions(+), 35 deletions(-) create mode 100644 src/sqlite/coin_store.rs create mode 100644 src/sqlite/hardened_cat_store.rs create mode 100644 src/sqlite/unhardened_cat_store.rs delete mode 100644 src/stores/coin_store.rs rename src/stores/{derivation_store.rs => puzzle_store.rs} (93%) delete mode 100644 test.sqlite diff --git a/.gitignore b/.gitignore index ea8c4bf7..dbfa7e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +test.sqlite +test.sqlite-wal +test.sqlite-shm diff --git a/migrations/20240314021456_init.sql b/migrations/20240314021456_init.sql index 134a4937..ca24460a 100644 --- a/migrations/20240314021456_init.sql +++ b/migrations/20240314021456_init.sql @@ -9,4 +9,20 @@ CREATE TABLE `unhardened_keys` ( `index` INT UNSIGNED NOT NULL PRIMARY KEY, `public_key` BLOB NOT NULL, `p2_puzzle_hash` BLOB NOT NULL +); + +CREATE TABLE `hardened_cats` ( + `puzzle_hash` BLOB NOT NULL PRIMARY KEY, + `asset_id` BLOB NOT NULL, + `public_key` BLOB NOT NULL, + `puzzle_hash` BLOB NOT NULL +); + +CREATE TABLE `coin_states` ( + `coin_id` BLOB NOT NULL PRIMARY KEY, + `parent_coin_info` BLOB NOT NULL, + `puzzle_hash` BLOB NOT NULL, + `amount` BIGINT UNSIGNED NOT NULL, + `created_height` INT UNSIGNED, + `spent_height` INT UNSIGNED ); \ No newline at end of file diff --git a/src/spends/cat.rs b/src/spends/cat.rs index 1e950587..ae75114b 100644 --- a/src/spends/cat.rs +++ b/src/spends/cat.rs @@ -10,7 +10,7 @@ use clvm_utils::{tree_hash, CurriedProgram}; use clvmr::{allocator::NodePtr, serde::node_from_bytes, Allocator}; use thiserror::Error; -use crate::{CatCondition, DerivationStore}; +use crate::{CatCondition, PuzzleStore}; mod issuance; mod raw_spend; @@ -49,7 +49,7 @@ pub async fn construct_cat_spends( standard_puzzle_ptr: NodePtr, cat_puzzle_ptr: NodePtr, peer: &Peer, - derivation_store: &impl DerivationStore, + puzzle_store: &impl PuzzleStore, coins: Vec, conditions: Vec>, asset_id: [u8; 32], @@ -65,12 +65,12 @@ pub async fn construct_cat_spends( for (i, coin) in coins.into_iter().enumerate() { // Coin info. let puzzle_hash = &coin.puzzle_hash; - let index = derivation_store + let index = puzzle_store .puzzle_hash_index(puzzle_hash.into()) .await .expect("cannot spend coin with unknown puzzle hash"); - let synthetic_key = derivation_store + let synthetic_key = puzzle_store .public_key(index) .await .expect("cannot spend coin with unknown public key"); diff --git a/src/spends/standard.rs b/src/spends/standard.rs index f14ca568..6e45174a 100644 --- a/src/spends/standard.rs +++ b/src/spends/standard.rs @@ -5,7 +5,7 @@ use clvm_traits::{clvm_quote, FromNodePtr, ToClvmError, ToNodePtr}; use clvm_utils::CurriedProgram; use clvmr::{allocator::NodePtr, Allocator}; -use crate::{Condition, DerivationStore}; +use crate::{Condition, PuzzleStore}; /// Creates a new coin spend for a given standard transaction coin. pub fn spend_standard_coin( @@ -38,19 +38,19 @@ pub fn spend_standard_coin( pub async fn spend_standard_coins( a: &mut Allocator, standard_puzzle_ptr: NodePtr, - derivation_store: &impl DerivationStore, + puzzle_store: &impl PuzzleStore, coins: Vec, conditions: &[Condition], ) -> Vec { let mut coin_spends = Vec::new(); for (i, coin) in coins.into_iter().enumerate() { let puzzle_hash = &coin.puzzle_hash; - let index = derivation_store + let index = puzzle_store .puzzle_hash_index(puzzle_hash.into()) .await .expect("cannot spend coin with unknown puzzle hash"); - let synthetic_key = derivation_store + let synthetic_key = puzzle_store .public_key(index) .await .expect("cannot spend coin with unknown public key"); diff --git a/src/sqlite.rs b/src/sqlite.rs index 70760574..f7e3f77f 100644 --- a/src/sqlite.rs +++ b/src/sqlite.rs @@ -1,5 +1,7 @@ +mod coin_store; mod hardened_key_store; mod unhardened_key_store; +pub use coin_store::*; pub use hardened_key_store::*; pub use unhardened_key_store::*; diff --git a/src/sqlite/coin_store.rs b/src/sqlite/coin_store.rs new file mode 100644 index 00000000..94398bc3 --- /dev/null +++ b/src/sqlite/coin_store.rs @@ -0,0 +1,101 @@ +use chia_protocol::{Coin, CoinState}; +use sqlx::SqlitePool; + +pub struct CoinStore { + pool: SqlitePool, +} + +impl CoinStore { + pub fn new(pool: SqlitePool) -> Self { + Self { pool } + } + + pub async fn unspent_coins(&self) -> Vec { + let rows = sqlx::query!( + " + SELECT `parent_coin_info`, `puzzle_hash`, `amount` + FROM `coin_states` + WHERE `spent_height` IS NULL + " + ) + .fetch_all(&self.pool) + .await + .unwrap(); + + rows.into_iter() + .map(|row| { + let parent_coin_info = row.parent_coin_info; + let puzzle_hash = row.puzzle_hash; + let amount = row.amount as u64; + + Coin { + parent_coin_info: parent_coin_info.try_into().unwrap(), + puzzle_hash: puzzle_hash.try_into().unwrap(), + amount, + } + }) + .collect() + } + + pub async fn coin_state(&self, coin_id: [u8; 32]) -> CoinState { + let coin_id = coin_id.to_vec(); + + let row = sqlx::query!( + " + SELECT `parent_coin_info`, `puzzle_hash`, `amount`, `created_height`, `spent_height` + FROM `coin_states` + WHERE `coin_id` = ? + ", + coin_id + ) + .fetch_one(&self.pool) + .await + .unwrap(); + + CoinState { + coin: Coin { + parent_coin_info: row.parent_coin_info.try_into().unwrap(), + puzzle_hash: row.puzzle_hash.try_into().unwrap(), + amount: row.amount as u64, + }, + created_height: row.created_height.map(|height| height as u32), + spent_height: row.spent_height.map(|height| height as u32), + } + } + + pub async fn apply_updates(&self, coin_states: Vec) { + let mut tx = self.pool.begin().await.unwrap(); + + for coin_state in coin_states { + let coin_id = coin_state.coin.coin_id().to_vec(); + let parent_coin_info = coin_state.coin.parent_coin_info.to_bytes().to_vec(); + let puzzle_hash = coin_state.coin.puzzle_hash.to_bytes().to_vec(); + let amount = coin_state.coin.amount as i64; + + sqlx::query!( + " + REPLACE INTO `coin_states` ( + `coin_id`, + `parent_coin_info`, + `puzzle_hash`, + `amount`, + `created_height`, + `spent_height` + ) + VALUES (?, ?, ?, ?, ?, ?) + ", + coin_id, + parent_coin_info, + puzzle_hash, + amount, + coin_state.created_height, + coin_state.spent_height + ) + .execute(&mut *tx) + .await + .unwrap(); + } + + tx.commit().await.unwrap(); + } +} diff --git a/src/sqlite/hardened_cat_store.rs b/src/sqlite/hardened_cat_store.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/sqlite/hardened_key_store.rs b/src/sqlite/hardened_key_store.rs index 9096b319..bd121350 100644 --- a/src/sqlite/hardened_key_store.rs +++ b/src/sqlite/hardened_key_store.rs @@ -2,7 +2,7 @@ use chia_bls::{PublicKey, SecretKey}; use chia_wallet::{standard::standard_puzzle_hash, DeriveSynthetic}; use sqlx::SqlitePool; -use crate::{DerivationStore, KeyStore}; +use crate::{KeyStore, PuzzleStore}; pub struct HardenedKeyStore { pool: SqlitePool, @@ -97,7 +97,7 @@ impl KeyStore for HardenedKeyStore { } } -impl DerivationStore for HardenedKeyStore { +impl PuzzleStore for HardenedKeyStore { async fn puzzle_hash(&self, index: u32) -> Option<[u8; 32]> { sqlx::query!( "SELECT `p2_puzzle_hash` FROM `hardened_keys` WHERE `index` = ?", diff --git a/src/sqlite/unhardened_cat_store.rs b/src/sqlite/unhardened_cat_store.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/sqlite/unhardened_key_store.rs b/src/sqlite/unhardened_key_store.rs index 8fbe6d5a..74b73b6c 100644 --- a/src/sqlite/unhardened_key_store.rs +++ b/src/sqlite/unhardened_key_store.rs @@ -2,7 +2,7 @@ use chia_bls::{DerivableKey, PublicKey}; use chia_wallet::{standard::standard_puzzle_hash, DeriveSynthetic}; use sqlx::SqlitePool; -use crate::{DerivationStore, KeyStore}; +use crate::{KeyStore, PuzzleStore}; pub struct UnhardenedKeyStore { pool: SqlitePool, @@ -109,7 +109,7 @@ impl KeyStore for UnhardenedKeyStore { } } -impl DerivationStore for UnhardenedKeyStore { +impl PuzzleStore for UnhardenedKeyStore { async fn puzzle_hash(&self, index: u32) -> Option<[u8; 32]> { sqlx::query!( "SELECT `p2_puzzle_hash` FROM `unhardened_keys` WHERE `index` = ?", diff --git a/src/stores.rs b/src/stores.rs index e78d8107..ad608547 100644 --- a/src/stores.rs +++ b/src/stores.rs @@ -1,9 +1,7 @@ -mod coin_store; -mod derivation_store; mod key_store; +mod puzzle_store; mod transaction_store; -pub use coin_store::*; -pub use derivation_store::*; pub use key_store::*; +pub use puzzle_store::*; pub use transaction_store::*; diff --git a/src/stores/coin_store.rs b/src/stores/coin_store.rs deleted file mode 100644 index 98e69b6c..00000000 --- a/src/stores/coin_store.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::future::Future; - -use chia_protocol::{Coin, CoinState}; - -/// Keeps track of the state of coins in a wallet. -pub trait CoinStore { - /// Applies coin state updates. - fn update_coin_state(&self, coin_states: Vec) -> impl Future + Send; - - /// Gets a list of unspent coins. - fn unspent_coins(&self) -> impl Future> + Send; - - /// Gets the current state of a coin. - fn coin_state(&self, coin_id: [u8; 32]) -> impl Future> + Send; - - /// Gets coin states for a given puzzle hash. - fn is_used(&self, puzzle_hash: [u8; 32]) -> impl Future + Send; -} diff --git a/src/stores/derivation_store.rs b/src/stores/puzzle_store.rs similarity index 93% rename from src/stores/derivation_store.rs rename to src/stores/puzzle_store.rs index ca83c9cc..47794cdf 100644 --- a/src/stores/derivation_store.rs +++ b/src/stores/puzzle_store.rs @@ -3,7 +3,7 @@ use std::future::Future; use crate::KeyStore; /// Keeps track of derived puzzle hashes in a wallet, based on its public keys. -pub trait DerivationStore: KeyStore { +pub trait PuzzleStore: KeyStore { /// Gets the derivation index of a puzzle hash. fn puzzle_hash_index(&self, puzzle_hash: [u8; 32]) -> impl Future> + Send; diff --git a/test.sqlite b/test.sqlite deleted file mode 100644 index e9d3de79e8f6cd1f80cb51f9a8351dd2463bb75e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28672 zcmeI&PiqrF90uUsq)AL@8c}2qdYD^VY9xPZp#`Z)yDh=ZCQUYIPn$Fuo28qyW_Km_ zWP=wKL8u=<@E~~bR45gM-UYpR5KrnC5JB`{aW-j!Sb*{GFF66a2HE*E!|5RT(q?Dny{o7^lj(k-x0{*U~itc$zUcp41-;Jl-|+}Oj= ze(=dyc62vb7}NpJApijgKmY;|fB*y_0D=EXU}xUr6Q-v4)@8G_rcMUs*gQjqhhWm#M#az-I?L6S%|mr5sc ztK^!vda=`d%UaX5vi-gyNlKYyuWh$Jrfyl=+d5UNC8O$S6PUc>^$IIfJlB4!aZ6`A zEVX2ro9(jd7^50>Jn-$zx_!dw%3R(ic5)tq>zO|E+@*0+H2D3Zdm0q zH4Ku>WF#>m_xg2e%T-$5Fs$0YRv2y4vSp7-HMJV;4!=9Nz~sEkE9Aq2n4K7E)cM5T z%b&8pRf6p1L(e&OAwd8F5P$##AOHafKmY;|fB*y_aIyl|d7tN8+^@UOR3E>Z$!c}Y zj74J8;Yd6johFg^TqH6Vi9Rx(wGNwSm)>ec{cH5o-h-bnK2BKLx_>72W$)