Skip to content

Commit

Permalink
brc20 utxo assets index (#34)
Browse files Browse the repository at this point in the history
* feat: add utxo index for transferable assets.

* feat: add BRC20 utxo transferable assets and sat range interfaces

* add REST interface for sat range rarity.

* fix: BRC20 Transfer check failed.
  • Loading branch information
wanyvic authored Mar 31, 2024
1 parent e4da5d1 commit 3eab454
Show file tree
Hide file tree
Showing 24 changed files with 928 additions and 434 deletions.
31 changes: 21 additions & 10 deletions src/index.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::okx::datastore::brc20::redb::table::{
get_balance, get_balances, get_token_info, get_tokens_info, get_transaction_receipts,
get_transferable, get_transferable_by_tick,
get_transferable_assets_by_account, get_transferable_assets_by_account_ticker,
get_transferable_assets_by_outpoint,
};
use crate::okx::datastore::ord::redb::table::{
get_collection_inscription_id, get_collections_of_inscription, get_transaction_operations,
Expand Down Expand Up @@ -95,13 +96,13 @@ define_table! { WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, u32, u128 }
// new
define_table! { ORD_TX_TO_OPERATIONS, &TxidValue, &[u8] }
define_table! { COLLECTIONS_KEY_TO_INSCRIPTION_ID, &str, InscriptionIdValue }
define_table! { COLLECTIONS_INSCRIPTION_ID_TO_KINDS, InscriptionIdValue, &[u8] }
define_multimap_table! { COLLECTIONS_INSCRIPTION_ID_TO_KINDS, InscriptionIdValue, &[u8] }

define_table! { BRC20_BALANCES, &str, &[u8] }
define_table! { BRC20_TOKEN, &str, &[u8] }
define_table! { BRC20_EVENTS, &TxidValue, &[u8] }
define_table! { BRC20_TRANSFERABLELOG, &str, &[u8] }
define_table! { BRC20_INSCRIBE_TRANSFER, InscriptionIdValue, &[u8] }
define_table! { BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS, &SatPointValue, &[u8] }
define_multimap_table! { BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS, &str, &SatPointValue }

#[derive(Debug, PartialEq)]
pub enum List {
Expand Down Expand Up @@ -359,14 +360,14 @@ impl Index {
// new ord tables
tx.open_table(ORD_TX_TO_OPERATIONS)?;
tx.open_table(COLLECTIONS_KEY_TO_INSCRIPTION_ID)?;
tx.open_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?;
tx.open_multimap_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?;

// brc20 tables
tx.open_multimap_table(BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS)?;
tx.open_table(BRC20_BALANCES)?;
tx.open_table(BRC20_TOKEN)?;
tx.open_table(BRC20_EVENTS)?;
tx.open_table(BRC20_TRANSFERABLELOG)?;
tx.open_table(BRC20_INSCRIBE_TRANSFER)?;
tx.open_table(BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS)?;

{
let mut outpoint_to_sat_ranges = tx.open_table(OUTPOINT_TO_SAT_RANGES)?;
Expand Down Expand Up @@ -637,7 +638,7 @@ impl Index {
total_bytes,
COLLECTIONS_KEY_TO_INSCRIPTION_ID,
);
insert_table_info(
insert_multimap_table_info(
&mut tables,
&wtx,
total_bytes,
Expand All @@ -646,8 +647,18 @@ impl Index {
insert_table_info(&mut tables, &wtx, total_bytes, BRC20_BALANCES);
insert_table_info(&mut tables, &wtx, total_bytes, BRC20_TOKEN);
insert_table_info(&mut tables, &wtx, total_bytes, BRC20_EVENTS);
insert_table_info(&mut tables, &wtx, total_bytes, BRC20_TRANSFERABLELOG);
insert_table_info(&mut tables, &wtx, total_bytes, BRC20_INSCRIBE_TRANSFER);
insert_table_info(
&mut tables,
&wtx,
total_bytes,
BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS,
);
insert_multimap_table_info(
&mut tables,
&wtx,
total_bytes,
BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS,
);

for table in wtx.list_tables()? {
assert!(tables.contains_key(table.name()));
Expand Down
2 changes: 1 addition & 1 deletion src/index/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ impl Entry for OutPoint {
}
}

pub(super) type SatPointValue = [u8; 44];
pub(crate) type SatPointValue = [u8; 44];

impl Entry for SatPoint {
type Value = SatPointValue;
Expand Down
112 changes: 111 additions & 1 deletion src/index/extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Index {
tick: brc20::Tick,
script_key: ScriptKey,
rtx: &Rtx,
) -> Result<Option<Vec<brc20::TransferableLog>>> {
) -> Result<Option<Vec<(SatPoint, brc20::TransferableLog)>>> {
let transferable_utxo_assets = rtx.brc20_get_tick_transferable_by_address(&tick, script_key)?;

if transferable_utxo_assets.is_empty() {
Expand Down Expand Up @@ -267,4 +267,114 @@ impl Index {
)
}
}

pub(crate) fn list_sat_range(
rtx: &Rtx,
outpoint: OutPoint,
index_sats: bool,
) -> Result<Option<Vec<SatRange>>> {
if !index_sats || outpoint == unbound_outpoint() {
return Ok(None);
}

let sat_ranges = rtx.list_sat_range(outpoint.store())?;

match sat_ranges {
Some(sat_ranges) => Ok(Some(
sat_ranges
.chunks_exact(11)
.map(|chunk| SatRange::load(chunk.try_into().unwrap()))
.collect(),
)),
None => Ok(None),
}
}

pub(crate) fn calculate_rarity_for_sat_range(sat_range: SatRange) -> Vec<(Sat, Rarity)> {
let start_sat = Sat(sat_range.0);
let end_sat = Sat(sat_range.1);

let start_height = if start_sat.third() > 0 {
start_sat.height().0 + 1
} else {
start_sat.height().0
};
let end_height = if end_sat.third() > 0 {
end_sat.height().0
} else {
end_sat.height().0 - 1
};

let mut result = Vec::new();
for height in start_height..=end_height {
let sat = Height(height).starting_sat();
let rarity = sat.rarity();
result.push((sat, rarity));
}
result
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_rarity_for_sat_range_mythic() {
let sat_range: SatRange = (0, 100);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(0), Rarity::Mythic)]);
let sat_range: SatRange = (1, 100);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![]);
}
#[test]
fn test_legendary_sat() {
let sat_range: SatRange = (
Height(SUBSIDY_HALVING_INTERVAL * 6).starting_sat().0,
Height(SUBSIDY_HALVING_INTERVAL * 6).starting_sat().0 + 1,
);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(2067187500000000), Rarity::Legendary)]);
}
#[test]
fn test_epic_sat() {
let sat_range: SatRange = (
Height(SUBSIDY_HALVING_INTERVAL).starting_sat().0,
Height(SUBSIDY_HALVING_INTERVAL).starting_sat().0 + 1,
);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(1050000000000000), Rarity::Epic)]);
}

#[test]
fn test_rare_sat() {
let sat_range: SatRange = (
Height(DIFFCHANGE_INTERVAL).starting_sat().0,
Height(DIFFCHANGE_INTERVAL).starting_sat().0 + 1,
);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(10080000000000), Rarity::Rare)]);
}

#[test]
fn test_two_rarity_sat() {
let sat_range: SatRange = (0, 4999999999);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(0), Rarity::Mythic)]);
let sat_range: SatRange = (0, 5000000000);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(0), Rarity::Mythic)]);
let sat_range: SatRange = (0, 5000000001);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(
rarity,
vec![
(Sat(0), Rarity::Mythic),
(Sat(5000000000), Rarity::Uncommon)
]
);
let sat_range: SatRange = (1, 5000000001);
let rarity = Index::calculate_rarity_for_sat_range(sat_range);
assert_eq!(rarity, vec![(Sat(5000000000), Rarity::Uncommon)]);
}
}
40 changes: 33 additions & 7 deletions src/index/rtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ impl Rtx<'_> {
&self,
inscription_id: InscriptionId,
) -> Result<Option<Vec<CollectionKind>>> {
let table = self.0.open_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?;
let table = self
.0
.open_multimap_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?;
get_collections_of_inscription(&table, &inscription_id)
}

Expand Down Expand Up @@ -219,16 +221,40 @@ impl Rtx<'_> {
&self,
tick: &brc20::Tick,
script_key: ScriptKey,
) -> Result<Vec<brc20::TransferableLog>> {
let table = self.0.open_table(BRC20_TRANSFERABLELOG)?;
get_transferable_by_tick(&table, &script_key, tick)
) -> Result<Vec<(SatPoint, brc20::TransferableLog)>> {
let address_table = self
.0
.open_multimap_table(BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS)?;
let satpoint_table = self.0.open_table(BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS)?;
get_transferable_assets_by_account_ticker(&address_table, &satpoint_table, &script_key, tick)
}

pub(crate) fn brc20_get_all_transferable_by_address(
&self,
script_key: ScriptKey,
) -> Result<Vec<brc20::TransferableLog>> {
let table = self.0.open_table(BRC20_TRANSFERABLELOG)?;
get_transferable(&table, &script_key)
) -> Result<Vec<(SatPoint, brc20::TransferableLog)>> {
let address_table = self
.0
.open_multimap_table(BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS)?;
let satpoint_table = self.0.open_table(BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS)?;
get_transferable_assets_by_account(&address_table, &satpoint_table, &script_key)
}

pub(crate) fn brc20_transferable_assets_on_output_with_satpoints(
&self,
outpoint: OutPoint,
) -> Result<Vec<(SatPoint, brc20::TransferableLog)>> {
let satpoint_to_sequence_number = self.0.open_table(BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS)?;
get_transferable_assets_by_outpoint(&satpoint_to_sequence_number, outpoint)
}

pub(super) fn list_sat_range(&self, outpoint: OutPointValue) -> Result<Option<Vec<u8>>> {
Ok(
self
.0
.open_table(OUTPOINT_TO_SAT_RANGES)?
.get(&outpoint)?
.map(|outpoint| outpoint.value().to_vec()),
)
}
}
8 changes: 5 additions & 3 deletions src/index/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,14 +632,16 @@ impl<'index> Updater<'_> {
ORD_TX_TO_OPERATIONS: &mut wtx.open_table(ORD_TX_TO_OPERATIONS)?,
COLLECTIONS_KEY_TO_INSCRIPTION_ID: &mut wtx.open_table(COLLECTIONS_KEY_TO_INSCRIPTION_ID)?,
COLLECTIONS_INSCRIPTION_ID_TO_KINDS: &mut wtx
.open_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?,
.open_multimap_table(COLLECTIONS_INSCRIPTION_ID_TO_KINDS)?,
SEQUENCE_NUMBER_TO_INSCRIPTION_ENTRY: &mut sequence_number_to_inscription_entry,
OUTPOINT_TO_ENTRY: &mut outpoint_to_entry,
BRC20_BALANCES: &mut wtx.open_table(BRC20_BALANCES)?,
BRC20_TOKEN: &mut wtx.open_table(BRC20_TOKEN)?,
BRC20_EVENTS: &mut wtx.open_table(BRC20_EVENTS)?,
BRC20_TRANSFERABLELOG: &mut wtx.open_table(BRC20_TRANSFERABLELOG)?,
BRC20_INSCRIBE_TRANSFER: &mut wtx.open_table(BRC20_INSCRIBE_TRANSFER)?,
BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS: &mut wtx
.open_table(BRC20_SATPOINT_TO_TRANSFERABLE_ASSETS)?,
BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS: &mut wtx
.open_multimap_table(BRC20_ADDRESS_TICKER_TO_TRANSFERABLE_ASSETS)?,
};

// Create a protocol manager to index the block of bitmap data.
Expand Down
53 changes: 18 additions & 35 deletions src/okx/datastore/brc20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ pub(super) mod events;
pub mod redb;
pub(super) mod tick;
pub(super) mod token_info;
pub(super) mod transfer;
pub(super) mod transferable_log;

pub use self::{
balance::Balance, errors::BRC20Error, events::Receipt, events::*, tick::*, token_info::TokenInfo,
transfer::TransferInfo, transferable_log::TransferableLog,
transferable_log::TransferableLog,
};
use super::ScriptKey;
use crate::{InscriptionId, Result};
use bitcoin::Txid;
use crate::{Result, SatPoint};
use bitcoin::{OutPoint, Txid};
use std::fmt::{Debug, Display};

pub trait Brc20Reader {
Expand All @@ -31,22 +30,23 @@ pub trait Brc20Reader {

fn get_transaction_receipts(&self, txid: &Txid) -> Result<Option<Vec<Receipt>>, Self::Error>;

fn get_transferable(&self, script: &ScriptKey) -> Result<Vec<TransferableLog>, Self::Error>;
fn get_transferable_by_tick(
fn get_transferable_assets_by_satpoint(
&self,
satpoint: &SatPoint,
) -> Result<Option<TransferableLog>, Self::Error>;
fn get_transferable_assets_by_account(
&self,
script: &ScriptKey,
tick: &Tick,
) -> Result<Vec<TransferableLog>, Self::Error>;
fn get_transferable_by_id(
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
fn get_transferable_assets_by_account_ticker(
&self,
script: &ScriptKey,
inscription_id: &InscriptionId,
) -> Result<Option<TransferableLog>, Self::Error>;

fn get_inscribe_transfer_inscription(
tick: &Tick,
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
fn get_transferable_assets_by_outpoint(
&self,
inscription_id: &InscriptionId,
) -> Result<Option<TransferInfo>, Self::Error>;
outpoint: OutPoint,
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
}

pub trait Brc20ReaderWriter: Brc20Reader {
Expand All @@ -71,28 +71,11 @@ pub trait Brc20ReaderWriter: Brc20Reader {
receipt: &[Receipt],
) -> Result<(), Self::Error>;

fn insert_transferable(
fn insert_transferable_asset(
&mut self,
script: &ScriptKey,
tick: &Tick,
satpoint: SatPoint,
inscription: &TransferableLog,
) -> Result<(), Self::Error>;

fn remove_transferable(
&mut self,
script: &ScriptKey,
tick: &Tick,
inscription_id: &InscriptionId,
) -> Result<(), Self::Error>;

fn insert_inscribe_transfer_inscription(
&mut self,
inscription_id: &InscriptionId,
transfer_info: TransferInfo,
) -> Result<(), Self::Error>;

fn remove_inscribe_transfer_inscription(
&mut self,
inscription_id: &InscriptionId,
) -> Result<(), Self::Error>;
fn remove_transferable_asset(&mut self, satpoint: SatPoint) -> Result<(), Self::Error>;
}
10 changes: 0 additions & 10 deletions src/okx/datastore/brc20/redb/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
pub mod table;

use super::{LowerTick, ScriptKey, Tick};
use crate::inscriptions::InscriptionId;

fn script_tick_id_key(script: &ScriptKey, tick: &Tick, inscription_id: &InscriptionId) -> String {
format!(
"{}_{}_{}",
script,
tick.to_lowercase().hex(),
inscription_id
)
}

fn min_script_tick_id_key(script: &ScriptKey, tick: &Tick) -> String {
script_tick_key(script, tick)
Expand Down
Loading

0 comments on commit 3eab454

Please sign in to comment.