Skip to content

Commit

Permalink
Support Circulating Supply Calculation
Browse files Browse the repository at this point in the history
- Modified relevant functions and structures to accommodate the calculation of circulating supply for multiple assets.
- Introduced `AssetCirculatingSupply` and `AssetCirculatingSupplyDiffs`.
- Overloaded operators for these classes to streamline calculations and data handling.
  • Loading branch information
KashProtocol committed Jan 7, 2024
1 parent 64369da commit bbcc788
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 69 deletions.
5 changes: 3 additions & 2 deletions indexes/utxoindex/src/core/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use kash_index_core::indexed_utxos::BalanceByScriptPublicKey;
use parking_lot::RwLock;
use std::{collections::HashSet, fmt::Debug, sync::Arc};

use crate::model::AssetCirculatingSupply;
use crate::{
errors::UtxoIndexResult,
model::{UtxoChanges, UtxoSetByScriptPublicKey},
Expand All @@ -20,7 +21,7 @@ pub trait UtxoIndexApi: Send + Sync + Debug {
/// Retrieve circulating supply from the utxoindex db.
///
/// Note: Use a read lock when accessing this method
fn get_circulating_supply(&self) -> StoreResult<u64>;
fn get_circulating_supply(&self) -> StoreResult<AssetCirculatingSupply>;

/// Retrieve utxos by script public keys supply from the utxoindex db.
///
Expand Down Expand Up @@ -66,7 +67,7 @@ impl UtxoIndexProxy {
Self { inner }
}

pub async fn get_circulating_supply(self) -> StoreResult<u64> {
pub async fn get_circulating_supply(self) -> StoreResult<AssetCirculatingSupply> {
spawn_blocking(move || self.inner.read().get_circulating_supply()).await.unwrap()
}

Expand Down
96 changes: 94 additions & 2 deletions indexes/utxoindex/src/core/model/supply.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,96 @@
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Sub, SubAssign};

/// Represents the circulating supply for each asset type.
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
pub struct AssetCirculatingSupply {
pub ksh_supply: u64,
pub kusd_supply: u64,
pub krv_supply: u64,
}

impl PartialEq for AssetCirculatingSupply {
fn eq(&self, other: &Self) -> bool {
self.ksh_supply == other.ksh_supply && self.kusd_supply == other.kusd_supply && self.krv_supply == other.krv_supply
}
}

impl AddAssign<AssetCirculatingSupplyDiffs> for AssetCirculatingSupply {
fn add_assign(&mut self, other: AssetCirculatingSupplyDiffs) {
// Adjusting ksh_supply considering positive and negative differences
self.ksh_supply = if other.ksh_supply_diff.is_negative() {
// If the difference is negative, subtract its absolute value
self.ksh_supply.checked_sub(other.ksh_supply_diff.wrapping_abs() as u64).expect("Underflow in ksh_supply")
} else {
// If the difference is positive, add it directly
self.ksh_supply.checked_add(other.ksh_supply_diff as u64).expect("Overflow in ksh_supply")
};

// Adjusting kusd_supply with the same logic as ksh_supply
self.kusd_supply = if other.kusd_supply_diff.is_negative() {
self.kusd_supply.checked_sub(other.kusd_supply_diff.wrapping_abs() as u64).expect("Underflow in kusd_supply")
} else {
self.kusd_supply.checked_add(other.kusd_supply_diff as u64).expect("Overflow in kusd_supply")
};

// Adjusting krv_supply with the same logic as ksh_supply
self.krv_supply = if other.krv_supply_diff.is_negative() {
self.krv_supply.checked_sub(other.krv_supply_diff.wrapping_abs() as u64).expect("Underflow in krv_supply")
} else {
self.krv_supply.checked_add(other.krv_supply_diff as u64).expect("Overflow in krv_supply")
};
}
}

#[derive(Clone, Copy, Debug, Default)]
pub struct AssetCirculatingSupplyDiffs {
pub ksh_supply_diff: i64,
pub kusd_supply_diff: i64,
pub krv_supply_diff: i64,
}

impl Add for AssetCirculatingSupplyDiffs {
type Output = Self;

fn add(self, other: Self) -> Self {
Self {
ksh_supply_diff: self.ksh_supply_diff + other.ksh_supply_diff,
kusd_supply_diff: self.kusd_supply_diff + other.kusd_supply_diff,
krv_supply_diff: self.krv_supply_diff + other.krv_supply_diff,
}
}
}

impl Sub for AssetCirculatingSupplyDiffs {
type Output = Self;

fn sub(self, other: Self) -> Self {
Self {
ksh_supply_diff: self.ksh_supply_diff - other.ksh_supply_diff,
kusd_supply_diff: self.kusd_supply_diff - other.kusd_supply_diff,
krv_supply_diff: self.krv_supply_diff - other.krv_supply_diff,
}
}
}

impl AddAssign for AssetCirculatingSupplyDiffs {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}

impl SubAssign for AssetCirculatingSupplyDiffs {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}

impl AssetCirculatingSupplyDiffs {
/// Returns true if the circulating supply difference is zero.
pub fn is_unchanged(&self) -> bool {
self.ksh_supply_diff == 0 && self.kusd_supply_diff == 0 && self.krv_supply_diff == 0
}
}

/// Type for circulating supply
pub type CirculatingSupply = u64;
/// Type for circulating supply difference
pub type CirculatingSupplyDiff = i64; // As i64 since circulating supply diff can go negative.
35 changes: 16 additions & 19 deletions indexes/utxoindex/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::model::AssetCirculatingSupply;
use crate::{
api::UtxoIndexApi,
errors::{UtxoIndexError, UtxoIndexResult},
model::{CirculatingSupply, UtxoChanges, UtxoSetByScriptPublicKey},
model::{UtxoChanges, UtxoSetByScriptPublicKey},
stores::store_manager::Store,
update_container::UtxoIndexChanges,
IDENT,
Expand Down Expand Up @@ -44,7 +45,7 @@ impl UtxoIndex {

impl UtxoIndexApi for UtxoIndex {
/// Retrieve circulating supply from the utxoindex db.
fn get_circulating_supply(&self) -> StoreResult<u64> {
fn get_circulating_supply(&self) -> StoreResult<AssetCirculatingSupply> {
trace!("[{0}] retrieving circulating supply", IDENT);

self.store.get_circulating_supply()
Expand Down Expand Up @@ -87,12 +88,7 @@ impl UtxoIndexApi for UtxoIndex {
// Commit changed utxo state to db
self.store.update_utxo_state(&utxoindex_changes.utxo_changes.added, &utxoindex_changes.utxo_changes.removed, false)?;

// Commit circulating supply change (if monotonic) to db.
if utxoindex_changes.supply_change > 0 {
//we force monotonic here
let _circulating_supply =
self.store.update_circulating_supply(utxoindex_changes.supply_change as CirculatingSupply, false)?;
}
let _circulating_supply = self.store.update_circulating_supply(utxoindex_changes.supply_change, false)?;

// Commit new consensus virtual tips.
self.store.set_tips(utxoindex_changes.tips, false)?; //we expect new tips with every virtual!
Expand Down Expand Up @@ -141,23 +137,20 @@ impl UtxoIndexApi for UtxoIndex {
let session = futures::executor::block_on(consensus.session_blocking());

let consensus_tips = session.get_virtual_parents();
let mut circulating_supply: CirculatingSupply = 0;
let mut circulating_supply = AssetCirculatingSupply::default();

//Initial batch is without specified seek and none-skipping.
let mut virtual_utxo_batch = session.get_virtual_utxos(None, RESYNC_CHUNK_SIZE, false);
let mut current_chunk_size = virtual_utxo_batch.len();
trace!("[{0}] resyncing with batch of {1} utxos from consensus db", IDENT, current_chunk_size);
// While loop stops resync attempts from an empty utxo db, and unneeded processing when the utxo state size happens to be a multiple of [`RESYNC_CHUNK_SIZE`]
while current_chunk_size > 0 {
// Potential optimization TODO: iterating virtual utxos into an [UtxoIndexChanges] struct is a bit of overhead (i.e. a potentially unneeded loop),
// but some form of pre-iteration is done to extract and commit circulating supply separately.

let mut utxoindex_changes = UtxoIndexChanges::new(); //reset changes.

let next_outpoint_from = Some(virtual_utxo_batch.last().expect("expected a last outpoint").0);
utxoindex_changes.add_utxos_from_vector(virtual_utxo_batch);

circulating_supply += utxoindex_changes.supply_change as CirculatingSupply;
circulating_supply += utxoindex_changes.supply_change;

self.store.update_utxo_state(&utxoindex_changes.utxo_changes.added, &utxoindex_changes.utxo_changes.removed, true)?;

Expand All @@ -170,9 +163,6 @@ impl UtxoIndexApi for UtxoIndex {
trace!("[{0}] resyncing with batch of {1} utxos from consensus db", IDENT, current_chunk_size);
}

// Commit to the the remaining stores.

trace!("[{0}] committing circulating supply {1} from consensus db", IDENT, circulating_supply);
self.store.insert_circulating_supply(circulating_supply, true)?;

trace!("[{0}] committing consensus tips {consensus_tips:?} from consensus db", IDENT);
Expand Down Expand Up @@ -213,7 +203,8 @@ impl ConsensusResetHandler for UtxoIndexConsensusResetHandler {

#[cfg(test)]
mod tests {
use crate::{api::UtxoIndexApi, model::CirculatingSupply, testutils::virtual_change_emulator::VirtualChangeEmulator, UtxoIndex};
use crate::model::AssetCirculatingSupply;
use crate::{api::UtxoIndexApi, testutils::virtual_change_emulator::VirtualChangeEmulator, UtxoIndex};
use kash_consensus::{
config::Config,
consensus::test_consensus::TestConsensus,
Expand All @@ -223,6 +214,7 @@ mod tests {
},
params::DEVNET_PARAMS,
};
use kash_consensus_core::asset_type::AssetType;
use kash_consensus_core::{
api::ConsensusApi,
utxo::{utxo_collection::UtxoCollection, utxo_diff::UtxoDiff},
Expand Down Expand Up @@ -278,10 +270,15 @@ mod tests {
// Test the sync from scratch via consensus db.
let consensus_utxos = tc.get_virtual_utxos(None, usize::MAX, false); // `usize::MAX` to ensure to get all.
let mut i = 0;
let mut consensus_supply: CirculatingSupply = 0;
let mut consensus_supply: AssetCirculatingSupply = AssetCirculatingSupply::default();
let consensus_utxo_set_size = consensus_utxos.len();
for (tx_outpoint, utxo_entry) in consensus_utxos.into_iter() {
consensus_supply += utxo_entry.amount;
match utxo_entry.asset_type {
AssetType::KSH => consensus_supply.ksh_supply += utxo_entry.amount,
AssetType::KUSD => consensus_supply.kusd_supply += utxo_entry.amount,
AssetType::KRV => consensus_supply.kusd_supply += utxo_entry.amount,
}

let indexed_utxos = utxoindex
.read()
.get_utxos_by_script_public_keys(HashSet::from_iter(vec![utxo_entry.script_public_key.clone()]))
Expand Down
15 changes: 12 additions & 3 deletions indexes/utxoindex/src/stores/store_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use kash_core::trace;
use kash_database::prelude::{CachePolicy, StoreResult, DB};
use kash_index_core::indexed_utxos::BalanceByScriptPublicKey;

use crate::model::{AssetCirculatingSupply, AssetCirculatingSupplyDiffs};
use crate::{
model::UtxoSetByScriptPublicKey,
stores::{
Expand Down Expand Up @@ -71,19 +72,27 @@ impl Store {
res
}

pub fn get_circulating_supply(&self) -> StoreResult<u64> {
pub fn get_circulating_supply(&self) -> StoreResult<AssetCirculatingSupply> {
self.circulating_supply_store.get()
}

pub fn update_circulating_supply(&mut self, circulating_supply_diff: u64, try_reset_on_err: bool) -> StoreResult<u64> {
pub fn update_circulating_supply(
&mut self,
circulating_supply_diff: AssetCirculatingSupplyDiffs,
try_reset_on_err: bool,
) -> StoreResult<AssetCirculatingSupply> {
let res = self.circulating_supply_store.update_circulating_supply(circulating_supply_diff);
if try_reset_on_err && res.is_err() {
self.delete_all()?;
}
res
}

pub fn insert_circulating_supply(&mut self, circulating_supply: u64, try_reset_on_err: bool) -> StoreResult<()> {
pub fn insert_circulating_supply(
&mut self,
circulating_supply: AssetCirculatingSupply,
try_reset_on_err: bool,
) -> StoreResult<()> {
let res = self.circulating_supply_store.insert(circulating_supply);
if try_reset_on_err && res.is_err() {
self.delete_all()?;
Expand Down
31 changes: 15 additions & 16 deletions indexes/utxoindex/src/stores/supply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,25 @@ use kash_database::{
registry::DatabaseStorePrefixes,
};

use crate::model::CirculatingSupply;
use crate::model::AssetCirculatingSupply;
use crate::model::AssetCirculatingSupplyDiffs;

/// Reader API for `UtxoIndexTipsStore`.
/// Reader API for `CirculatingSupplyStore`.
pub trait CirculatingSupplyStoreReader {
fn get(&self) -> StoreResult<u64>;
fn get(&self) -> StoreResult<AssetCirculatingSupply>;
}

pub trait CirculatingSupplyStore: CirculatingSupplyStoreReader {
fn update_circulating_supply(&mut self, to_add: CirculatingSupply) -> StoreResult<u64>;
fn insert(&mut self, circulating_supply: u64) -> StoreResult<()>;
fn update_circulating_supply(&mut self, to_add: AssetCirculatingSupplyDiffs) -> StoreResult<AssetCirculatingSupply>;
fn insert(&mut self, circulating_supply: AssetCirculatingSupply) -> StoreResult<()>;
fn remove(&mut self) -> StoreResult<()>;
}

/// A DB + cache implementation of `UtxoIndexTipsStore` trait
#[derive(Clone)]
pub struct DbCirculatingSupplyStore {
db: Arc<DB>,
access: CachedDbItem<u64>,
access: CachedDbItem<AssetCirculatingSupply>,
}

impl DbCirculatingSupplyStore {
Expand All @@ -32,25 +33,23 @@ impl DbCirculatingSupplyStore {
}

impl CirculatingSupplyStoreReader for DbCirculatingSupplyStore {
fn get(&self) -> StoreResult<u64> {
fn get(&self) -> StoreResult<AssetCirculatingSupply> {
self.access.read()
}
}

impl CirculatingSupplyStore for DbCirculatingSupplyStore {
fn update_circulating_supply(&mut self, to_add: CirculatingSupply) -> StoreResult<u64> {
if to_add == 0 {
return self.get();
}

let circulating_supply = self.access.update(DirectDbWriter::new(&self.db), move |circulating_supply| {
circulating_supply + (to_add) //note: this only works because we force monotonic in `UtxoIndex::update`.
fn update_circulating_supply(&mut self, supply_diff: AssetCirculatingSupplyDiffs) -> StoreResult<AssetCirculatingSupply> {
let new_supplies = self.access.update(DirectDbWriter::new(&self.db), move |mut current_supplies: AssetCirculatingSupply| {
// Apply the changes directly to the mutable value
current_supplies += supply_diff;
current_supplies
});

circulating_supply
new_supplies
}

fn insert(&mut self, circulating_supply: CirculatingSupply) -> StoreResult<()> {
fn insert(&mut self, circulating_supply: AssetCirculatingSupply) -> StoreResult<()> {
self.access.write(DirectDbWriter::new(&self.db), &circulating_supply)
}

Expand Down
Loading

0 comments on commit bbcc788

Please sign in to comment.