diff --git a/tuxedo-template-runtime/src/lib.rs b/tuxedo-template-runtime/src/lib.rs index a5e1f3033..6a2c8a389 100644 --- a/tuxedo-template-runtime/src/lib.rs +++ b/tuxedo-template-runtime/src/lib.rs @@ -44,6 +44,7 @@ pub use kitties; pub use money; pub use poe; pub use runtime_upgrade; +pub use timestamp; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats diff --git a/wallet/src/cli.rs b/wallet/src/cli.rs index 094ca94f0..143243c3f 100644 --- a/wallet/src/cli.rs +++ b/wallet/src/cli.rs @@ -98,6 +98,9 @@ pub enum Command { /// Show the complete list of UTXOs known to the wallet. ShowAllOutputs, + + /// Show the latest on-chain timestamp. + ShowTimestamp, } #[derive(Debug, Args)] diff --git a/wallet/src/main.rs b/wallet/src/main.rs index e298c7189..044c4dddc 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -16,6 +16,7 @@ mod money; mod output_filter; mod rpc; mod sync; +mod timestamp; use cli::{Cli, Command}; @@ -71,10 +72,10 @@ async fn main() -> anyhow::Result<()> { // The filter function that will determine whether the local database should track a given utxo // is based on whether that utxo is privately owned by a key that is in our keystore. let keystore_filter = |v: &OuterVerifier| -> bool { - matches![ - v, - OuterVerifier::Sr25519Signature(Sr25519Signature { owner_pubkey }) if crate::keystore::has_key(&keystore, owner_pubkey) - ] + matches![v, + OuterVerifier::Sr25519Signature(Sr25519Signature { owner_pubkey }) + if crate::keystore::has_key(&keystore, owner_pubkey) + ] || matches![v, OuterVerifier::UpForGrabs(UpForGrabs)] // used for timestamp }; if !sled::Db::was_recovered(&db) { @@ -166,6 +167,10 @@ async fn main() -> anyhow::Result<()> { Ok(()) } + Some(Command::ShowTimestamp) => { + println!("Timestamp: {}", timestamp::get_timestamp(&db)?); + Ok(()) + } None => { log::info!("No Wallet Command invoked. Exiting."); Ok(()) diff --git a/wallet/src/money.rs b/wallet/src/money.rs index d4fb2e728..7b0558034 100644 --- a/wallet/src/money.rs +++ b/wallet/src/money.rs @@ -145,3 +145,21 @@ pub async fn get_coin_from_storage( Ok((coin_in_storage, utxo.verifier)) } + +/// Apply a transaction to the local database, storing the new coins. +pub(crate) fn apply_transaction( + db: &Db, + tx_hash: ::Output, + index: u32, + output: &Output, +) -> anyhow::Result<()> { + let amount = output.payload.extract::>()?.0; + let output_ref = OutputRef { tx_hash, index }; + match output.verifier { + OuterVerifier::Sr25519Signature(Sr25519Signature { owner_pubkey }) => { + // Add it to the global unspent_outputs table + crate::sync::add_unspent_output(db, &output_ref, &owner_pubkey, &amount) + } + _ => Err(anyhow!("{:?}", ())), + } +} diff --git a/wallet/src/sync.rs b/wallet/src/sync.rs index bc2d0ef10..2ca963121 100644 --- a/wallet/src/sync.rs +++ b/wallet/src/sync.rs @@ -20,12 +20,12 @@ use sled::Db; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, Hash}; use tuxedo_core::{ + dynamic_typing::UtxoData, types::{Input, OutputRef}, - verifier::Sr25519Signature, }; use jsonrpsee::http_client::HttpClient; -use runtime::{money::Coin, Block, OuterVerifier, Transaction}; +use runtime::{money::Coin, timestamp::Timestamp, Block, OuterVerifier, Transaction}; /// The identifier for the blocks tree in the db. const BLOCKS: &str = "blocks"; @@ -262,23 +262,15 @@ async fn apply_transaction bool>( .filter(|o| filter(&o.verifier)) .enumerate() { - // For now the wallet only supports simple coins, so skip anything else - let amount = match output.payload.extract::>() { - Ok(Coin(amount)) => amount, - Err(_) => continue, - }; - - let output_ref = OutputRef { - tx_hash, - index: index as u32, - }; - - match output.verifier { - OuterVerifier::Sr25519Signature(Sr25519Signature { owner_pubkey }) => { - // Add it to the global unspent_outputs table - add_unspent_output(db, &output_ref, &owner_pubkey, &amount)?; + // For now the wallet only supports simple coins and timestamp + match output.payload.type_id { + Coin::<0>::TYPE_ID => { + crate::money::apply_transaction(db, tx_hash, index as u32, output)?; + } + Timestamp::TYPE_ID => { + crate::timestamp::apply_transaction(db, output)?; } - _ => return Err(anyhow!("{:?}", ())), + _ => continue, } } @@ -292,7 +284,7 @@ async fn apply_transaction bool>( } /// Add a new output to the database updating all tables. -fn add_unspent_output( +pub(crate) fn add_unspent_output( db: &Db, output_ref: &OutputRef, owner_pubkey: &H256, diff --git a/wallet/src/timestamp.rs b/wallet/src/timestamp.rs new file mode 100644 index 000000000..d5da075a0 --- /dev/null +++ b/wallet/src/timestamp.rs @@ -0,0 +1,27 @@ +//! Wallet features related to on-chain timestamps. + +use anyhow::anyhow; +use parity_scale_codec::{Decode, Encode}; +use runtime::{timestamp::Timestamp, OuterVerifier}; +use sled::Db; +use tuxedo_core::types::Output; + +/// The identifier for the current timestamp in the db. +const TIMESTAMP: &str = "timestamp"; + +pub(crate) fn apply_transaction(db: &Db, output: &Output) -> anyhow::Result<()> { + let timestamp = output.payload.extract::()?.time; + let timestamp_tree = db.open_tree(TIMESTAMP)?; + timestamp_tree.insert([0], timestamp.encode())?; + Ok(()) +} + +/// Apply a transaction to the local database, storing the new timestamp. +pub(crate) fn get_timestamp(db: &Db) -> anyhow::Result { + let timestamp_tree = db.open_tree(TIMESTAMP)?; + let timestamp = timestamp_tree + .get([0])? + .ok_or_else(|| anyhow!("Could not find timestamp in database."))?; + u64::decode(&mut ×tamp[..]) + .map_err(|_| anyhow!("Could not decode timestamp from database.")) +}