mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-23 00:48:11 -07:00
global: snapshot
This commit is contained in:
@@ -114,8 +114,8 @@ impl AllCohortMetrics {
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.realized.compute_rest_part2(
|
||||
@@ -132,8 +132,8 @@ impl AllCohortMetrics {
|
||||
starting_indexes,
|
||||
&self.realized.minimal.sopr.value_created.raw.height,
|
||||
&self.realized.minimal.sopr.value_destroyed.raw.height,
|
||||
up_to_1h_value_created,
|
||||
up_to_1h_value_destroyed,
|
||||
under_1h_value_created,
|
||||
under_1h_value_destroyed,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ impl ExtendedAdjustedCohortMetrics {
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
@@ -82,8 +82,8 @@ impl ExtendedAdjustedCohortMetrics {
|
||||
starting_indexes,
|
||||
&self.inner.realized.minimal.sopr.value_created.raw.height,
|
||||
&self.inner.realized.minimal.sopr.value_destroyed.raw.height,
|
||||
up_to_1h_value_created,
|
||||
up_to_1h_value_destroyed,
|
||||
under_1h_value_created,
|
||||
under_1h_value_destroyed,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ pub struct AdjustedSopr<M: StorageMode = Rw> {
|
||||
impl AdjustedSopr {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
value_created: cfg.import("adjusted_value_created", Version::ZERO)?,
|
||||
value_destroyed: cfg.import("adjusted_value_destroyed", Version::ZERO)?,
|
||||
value_created_sum: cfg.import("adjusted_value_created", Version::ONE)?,
|
||||
value_destroyed_sum: cfg.import("adjusted_value_destroyed", Version::ONE)?,
|
||||
ratio: cfg.import("adjusted_sopr", Version::ONE)?,
|
||||
value_created: cfg.import("adj_value_created", Version::ZERO)?,
|
||||
value_destroyed: cfg.import("adj_value_destroyed", Version::ZERO)?,
|
||||
value_created_sum: cfg.import("adj_value_created", Version::ONE)?,
|
||||
value_destroyed_sum: cfg.import("adj_value_destroyed", Version::ONE)?,
|
||||
ratio: cfg.import("asopr", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,21 +39,21 @@ impl AdjustedSopr {
|
||||
starting_indexes: &Indexes,
|
||||
base_value_created: &impl ReadableVec<Height, Cents>,
|
||||
base_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
up_to_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
up_to_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Compute value_created = base.value_created - up_to_1h.value_created
|
||||
// Compute value_created = base.value_created - under_1h.value_created
|
||||
self.value_created.height.compute_subtract(
|
||||
starting_indexes.height,
|
||||
base_value_created,
|
||||
up_to_1h_value_created,
|
||||
under_1h_value_created,
|
||||
exit,
|
||||
)?;
|
||||
self.value_destroyed.height.compute_subtract(
|
||||
starting_indexes.height,
|
||||
base_value_destroyed,
|
||||
up_to_1h_value_destroyed,
|
||||
under_1h_value_destroyed,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ impl RealizedFull {
|
||||
&profit_value_destroyed,
|
||||
);
|
||||
let profit = RealizedProfit {
|
||||
rel_to_rcap: cfg.import("realized_profit_rel_to_realized_cap", Version::new(2))?,
|
||||
rel_to_rcap: cfg.import("realized_profit_rel_to_rcap", Version::new(2))?,
|
||||
value_created: cfg.import("profit_value_created", v0)?,
|
||||
value_destroyed: profit_value_destroyed,
|
||||
value_created_sum: cfg.import("profit_value_created", v1)?,
|
||||
@@ -173,7 +173,7 @@ impl RealizedFull {
|
||||
&loss_value_destroyed,
|
||||
);
|
||||
let loss = RealizedLoss {
|
||||
rel_to_rcap: cfg.import("realized_loss_rel_to_realized_cap", Version::new(2))?,
|
||||
rel_to_rcap: cfg.import("realized_loss_rel_to_rcap", Version::new(2))?,
|
||||
value_created: cfg.import("loss_value_created", v0)?,
|
||||
value_destroyed: loss_value_destroyed,
|
||||
value_created_sum: cfg.import("loss_value_created", v1)?,
|
||||
@@ -192,15 +192,15 @@ impl RealizedFull {
|
||||
// Net PnL
|
||||
let net_pnl = RealizedNetPnl {
|
||||
rel_to_rcap: cfg
|
||||
.import("net_realized_pnl_rel_to_realized_cap", Version::new(2))?,
|
||||
.import("net_realized_pnl_rel_to_rcap", Version::new(2))?,
|
||||
cumulative: cfg.import("net_realized_pnl_cumulative", v1)?,
|
||||
sum_extended: cfg.import("net_realized_pnl", v1)?,
|
||||
delta: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
delta_extended: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
change_1m_rel_to_rcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_realized_cap", Version::new(4))?,
|
||||
.import("net_pnl_change_1m_rel_to_rcap", Version::new(4))?,
|
||||
change_1m_rel_to_mcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_market_cap", Version::new(4))?,
|
||||
.import("net_pnl_change_1m_rel_to_mcap", Version::new(4))?,
|
||||
};
|
||||
|
||||
// SOPR
|
||||
@@ -214,7 +214,7 @@ impl RealizedFull {
|
||||
let peak_regret = RealizedPeakRegret {
|
||||
value: cfg.import("realized_peak_regret", Version::new(2))?,
|
||||
rel_to_rcap: cfg
|
||||
.import("realized_peak_regret_rel_to_realized_cap", Version::new(2))?,
|
||||
.import("realized_peak_regret_rel_to_rcap", Version::new(2))?,
|
||||
};
|
||||
|
||||
// Investor
|
||||
@@ -241,7 +241,7 @@ impl RealizedFull {
|
||||
profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?,
|
||||
cap_delta_extended: cfg.import("realized_cap_delta", Version::new(5))?,
|
||||
cap_raw: cfg.import("cap_raw", v0)?,
|
||||
cap_rel_to_own_mcap: cfg.import("realized_cap_rel_to_own_market_cap", v1)?,
|
||||
cap_rel_to_own_mcap: cfg.import("realized_cap_rel_to_own_mcap", v1)?,
|
||||
price_ratio_percentiles: RatioPerBlockPercentiles::forced_import(
|
||||
cfg.db,
|
||||
&realized_price_name,
|
||||
|
||||
@@ -10,12 +10,12 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
/// Extended relative metrics for own market cap (extended && rel_to_all).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeExtendedOwnMarketCap<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_own_market_cap")]
|
||||
pub unrealized_profit_rel_to_own_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_own_market_cap")]
|
||||
pub unrealized_loss_rel_to_own_market_cap: PercentPerBlock<BasisPoints32, M>,
|
||||
#[traversable(wrap = "unrealized/net_pnl", rename = "rel_to_own_market_cap")]
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_own_mcap")]
|
||||
pub unrealized_profit_rel_to_own_mcap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_own_mcap")]
|
||||
pub unrealized_loss_rel_to_own_mcap: PercentPerBlock<BasisPoints32, M>,
|
||||
#[traversable(wrap = "unrealized/net_pnl", rename = "rel_to_own_mcap")]
|
||||
pub net_unrealized_pnl_rel_to_own_mcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
impl RelativeExtendedOwnMarketCap {
|
||||
@@ -23,12 +23,12 @@ impl RelativeExtendedOwnMarketCap {
|
||||
let v2 = Version::new(2);
|
||||
|
||||
Ok(Self {
|
||||
unrealized_profit_rel_to_own_market_cap: cfg
|
||||
.import("unrealized_profit_rel_to_own_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_own_market_cap: cfg
|
||||
.import("unrealized_loss_rel_to_own_market_cap", Version::new(3))?,
|
||||
net_unrealized_pnl_rel_to_own_market_cap: cfg
|
||||
.import("net_unrealized_pnl_rel_to_own_market_cap", Version::new(3))?,
|
||||
unrealized_profit_rel_to_own_mcap: cfg
|
||||
.import("unrealized_profit_rel_to_own_mcap", v2)?,
|
||||
unrealized_loss_rel_to_own_mcap: cfg
|
||||
.import("unrealized_loss_rel_to_own_mcap", Version::new(3))?,
|
||||
net_unrealized_pnl_rel_to_own_mcap: cfg
|
||||
.import("net_unrealized_pnl_rel_to_own_mcap", Version::new(3))?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,21 +39,21 @@ impl RelativeExtendedOwnMarketCap {
|
||||
own_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.unrealized_profit_rel_to_own_market_cap
|
||||
self.unrealized_profit_rel_to_own_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.profit.raw.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_own_market_cap
|
||||
self.unrealized_loss_rel_to_own_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
max_from,
|
||||
&unrealized.loss.raw.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.net_unrealized_pnl_rel_to_own_market_cap
|
||||
self.net_unrealized_pnl_rel_to_own_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBps32>(
|
||||
max_from,
|
||||
&unrealized.net_pnl.usd.height,
|
||||
|
||||
@@ -11,15 +11,15 @@ use crate::{
|
||||
/// Full relative metrics (sth/lth/all tier).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeFull<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "supply/in_profit", rename = "rel_to_own_supply")]
|
||||
pub supply_in_profit_rel_to_own_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_loss", rename = "rel_to_own_supply")]
|
||||
pub supply_in_loss_rel_to_own_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_profit", rename = "rel_to_own")]
|
||||
pub supply_in_profit_rel_to_own: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_loss", rename = "rel_to_own")]
|
||||
pub supply_in_loss_rel_to_own: PercentPerBlock<BasisPoints16, M>,
|
||||
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_market_cap")]
|
||||
pub unrealized_profit_rel_to_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_market_cap")]
|
||||
pub unrealized_loss_rel_to_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_mcap")]
|
||||
pub unrealized_profit_rel_to_mcap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_mcap")]
|
||||
pub unrealized_loss_rel_to_mcap: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl RelativeFull {
|
||||
@@ -28,13 +28,13 @@ impl RelativeFull {
|
||||
let v2 = Version::new(2);
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit_rel_to_own_supply: cfg
|
||||
.import("supply_in_profit_rel_to_own_supply", v1)?,
|
||||
supply_in_loss_rel_to_own_supply: cfg.import("supply_in_loss_rel_to_own_supply", v1)?,
|
||||
unrealized_profit_rel_to_market_cap: cfg
|
||||
.import("unrealized_profit_rel_to_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_market_cap: cfg
|
||||
.import("unrealized_loss_rel_to_market_cap", v2)?,
|
||||
supply_in_profit_rel_to_own: cfg
|
||||
.import("supply_in_profit_rel_to_own", v1)?,
|
||||
supply_in_loss_rel_to_own: cfg.import("supply_in_loss_rel_to_own", v1)?,
|
||||
unrealized_profit_rel_to_mcap: cfg
|
||||
.import("unrealized_profit_rel_to_mcap", v2)?,
|
||||
unrealized_loss_rel_to_mcap: cfg
|
||||
.import("unrealized_loss_rel_to_mcap", v2)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ impl RelativeFull {
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply_in_profit_rel_to_own_supply
|
||||
self.supply_in_profit_rel_to_own
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&supply.in_profit.sats.height,
|
||||
&supply.total.sats.height,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss_rel_to_own_supply
|
||||
self.supply_in_loss_rel_to_own
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&supply.in_loss.sats.height,
|
||||
@@ -61,14 +61,14 @@ impl RelativeFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.unrealized_profit_rel_to_market_cap
|
||||
self.unrealized_profit_rel_to_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.profit.raw.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_market_cap
|
||||
self.unrealized_loss_rel_to_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.loss.raw.usd.height,
|
||||
|
||||
@@ -11,22 +11,22 @@ use crate::distribution::metrics::{ImportConfig, SupplyCore};
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeToAll<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "supply", rename = "rel_to_circulating")]
|
||||
pub supply_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
pub supply_rel_to_circulating: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_profit", rename = "rel_to_circulating")]
|
||||
pub supply_in_profit_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
pub supply_in_profit_rel_to_circulating: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_loss", rename = "rel_to_circulating")]
|
||||
pub supply_in_loss_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
pub supply_in_loss_rel_to_circulating: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl RelativeToAll {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
supply_rel_to_circulating_supply: cfg
|
||||
.import("supply_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_profit_rel_to_circulating_supply: cfg
|
||||
.import("supply_in_profit_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_loss_rel_to_circulating_supply: cfg
|
||||
.import("supply_in_loss_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_rel_to_circulating: cfg
|
||||
.import("supply_rel_to_circulating", Version::ONE)?,
|
||||
supply_in_profit_rel_to_circulating: cfg
|
||||
.import("supply_in_profit_rel_to_circulating", Version::ONE)?,
|
||||
supply_in_loss_rel_to_circulating: cfg
|
||||
.import("supply_in_loss_rel_to_circulating", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,21 +37,21 @@ impl RelativeToAll {
|
||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply_rel_to_circulating_supply
|
||||
self.supply_rel_to_circulating
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&supply.total.sats.height,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_profit_rel_to_circulating_supply
|
||||
self.supply_in_profit_rel_to_circulating
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&supply.in_profit.sats.height,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss_rel_to_circulating_supply
|
||||
self.supply_in_loss_rel_to_circulating
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&supply.in_loss.sats.height,
|
||||
|
||||
@@ -16,23 +16,23 @@ use crate::distribution::metrics::ImportConfig;
|
||||
#[derive(Traversable)]
|
||||
pub struct SupplyBase<M: StorageMode = Rw> {
|
||||
pub total: AmountPerBlock<M>,
|
||||
pub halved: LazyAmountPerBlock,
|
||||
pub half: LazyAmountPerBlock,
|
||||
}
|
||||
|
||||
impl SupplyBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = cfg.import("supply", Version::ZERO)?;
|
||||
|
||||
let supply_halved = LazyAmountPerBlock::from_block_source::<
|
||||
let supply_half = LazyAmountPerBlock::from_block_source::<
|
||||
HalveSats,
|
||||
HalveSatsToBitcoin,
|
||||
HalveCents,
|
||||
HalveDollars,
|
||||
>(&cfg.name("supply_halved"), &supply, cfg.version);
|
||||
>(&cfg.name("supply_half"), &supply, cfg.version);
|
||||
|
||||
Ok(Self {
|
||||
total: supply,
|
||||
halved: supply_halved,
|
||||
half: supply_half,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPointsSigned32, Cents, Height, Version};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::RatioPerBlock;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct UnrealizedMinimal<M: StorageMode = Rw> {
|
||||
pub nupl: RatioPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
impl UnrealizedMinimal {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
nupl: cfg.import("nupl", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
spot_price: &impl ReadableVec<Height, Cents>,
|
||||
realized_price: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.nupl.bps.height.compute_transform2(
|
||||
max_from,
|
||||
spot_price,
|
||||
realized_price,
|
||||
|(i, price, realized_price, ..)| {
|
||||
let p = price.as_u128();
|
||||
if p == 0 {
|
||||
(i, BasisPointsSigned32::ZERO)
|
||||
} else {
|
||||
let rp = realized_price.as_u128();
|
||||
let nupl_bps = ((p as i128 - rp as i128) * 10000) / p as i128;
|
||||
(i, BasisPointsSigned32::from(nupl_bps as i32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user