Skip to content

Commit

Permalink
support btc domain index (#49)
Browse files Browse the repository at this point in the history
* add btc domain indexer

* fix error

* update time crate

* add domain/:base64_domain interface

* add unit test for domain_list options

* update validate regex unit test

* fix domain regex pattern

* fix domain unit test

* remove block height check for btc

* update log

* update comment

* fix unit test error

* check domain name

* fix comment

* fix compile error

* fix valid name error

* cargo fmt

* fix unit test

* fix unit test

* fix check domain name method

* update unit test

* cargo clippy

* update clippy

* update btc domain to btc name

* fix compile error

* cargo clippy

* update

* update rust version to 1.70

* fix cargo clippy

* fix cargo fmt

---------

Co-authored-by: linfeng.yuan <linfeng.yuan@okg.com>
  • Loading branch information
DogLi and linfeng.yuan authored Sep 12, 2024
1 parent 2c88b0f commit 573ef8f
Show file tree
Hide file tree
Showing 33 changed files with 692 additions and 463 deletions.
16 changes: 12 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ autotests = false
homepage = "https://github.com/ordinals/ord"
repository = "https://github.com/ordinals/ord"
autobins = false
rust-version = "1.67"
rust-version = "1.70"
build = "build.rs"

[package.metadata.deb]
Expand Down
2 changes: 1 addition & 1 deletion src/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ mod tests {
assert_eq!(Epoch::from(Sat(1)), 0);
assert_eq!(Epoch::from(Epoch(1).starting_sat()), 1);
assert_eq!(Epoch::from(Epoch(1).starting_sat() + 1), 1);
assert_eq!(Epoch::from(Sat(u64::max_value())), 33);
assert_eq!(Epoch::from(Sat(u64::MAX)), 33);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/height.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod tests {
u64::from(SUBSIDY_HALVING_INTERVAL) * 5000000000 + 2500000000
);
assert_eq!(
Height(u32::max_value()).starting_sat(),
Height(u32::MAX).starting_sat(),
*Epoch::STARTING_SATS.last().unwrap()
);
}
Expand Down
6 changes: 4 additions & 2 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ pub struct Index {
path: PathBuf,
started: DateTime<Utc>,
unrecoverably_reorged: AtomicBool,
pub domain_list: Vec<String>,
}

impl Index {
Expand Down Expand Up @@ -422,6 +423,7 @@ impl Index {
path,
started: Utc::now(),
unrecoverably_reorged: AtomicBool::new(false),
domain_list: options.btc_domain_list.clone(),
})
}

Expand Down Expand Up @@ -1213,7 +1215,7 @@ impl Index {
};

self
.get_children_by_sequence_number_paginated(sequence_number, usize::max_value(), 0)
.get_children_by_sequence_number_paginated(sequence_number, usize::MAX, 0)
.map(|(children, _more)| children)
}

Expand Down Expand Up @@ -4536,7 +4538,7 @@ mod tests {
i,
if i == 1 { 0 } else { 1 },
0,
inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(),
inscription("text/plain;charset=utf-8", format!("hello {}", i)).to_witness(),
)], // for the first inscription use coinbase, otherwise use the previous tx
..Default::default()
});
Expand Down
12 changes: 12 additions & 0 deletions src/index/rtx.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use crate::okx::datastore::ord::btc_name::BtcName;

pub(crate) struct Rtx<'a>(pub(crate) redb::ReadTransaction<'a>);

Expand Down Expand Up @@ -174,6 +175,17 @@ impl Rtx<'_> {
get_collection_inscription_id(&table, &district.to_collection_key())
}

pub(crate) fn btc_name_to_inscription_id(
&self,
btc_name: &str,
domain_list: &[String],
) -> Result<Option<InscriptionId>> {
let btc_name_raw = btc_name.as_bytes().to_vec();
let domain = BtcName::parse(&btc_name_raw, domain_list)?;
let table = self.0.open_table(COLLECTIONS_KEY_TO_INSCRIPTION_ID)?;
get_collection_inscription_id(&table, &domain.to_collection_key())
}

pub(crate) fn ord_transaction_id_to_inscription_operations(
&self,
txid: Txid,
Expand Down
2 changes: 1 addition & 1 deletion src/index/updater/inscription_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> {
hidden: inscription.payload.hidden(),
parent: inscription.payload.parent(),
pointer: inscription.payload.pointer(),
reinscription: inscribed_offsets.get(&offset).is_some(),
reinscription: inscribed_offsets.contains_key(&offset),
unbound,
inscription: inscription.payload.clone(),
vindicated: curse.is_some() && jubilant,
Expand Down
4 changes: 2 additions & 2 deletions src/index/updater/rune_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
limit
}
} else {
u128::max_value()
u128::MAX
},
deadline: etching.deadline,
divisibility: etching.divisibility,
Expand Down Expand Up @@ -314,7 +314,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
limit
}
} else {
u128::max_value()
u128::MAX
} - balance,
end: end.and_then(|end| (!burn).then_some(end)),
symbol,
Expand Down
2 changes: 1 addition & 1 deletion src/inscriptions/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ mod tests {
fn chunked_data_is_parsable() {
let mut witness = Witness::new();

witness.push(&inscription("foo", [1; 1040]).append_reveal_script(script::Builder::new()));
witness.push(inscription("foo", [1; 1040]).append_reveal_script(script::Builder::new()));

witness.push([]);

Expand Down
34 changes: 17 additions & 17 deletions src/okx/datastore/brc20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,41 @@ pub use self::{
};
use super::ScriptKey;
use crate::{Result, SatPoint};
use bitcoin::{OutPoint, Txid};
use bitcoin::Txid;
use std::fmt::{Debug, Display};

pub trait Brc20Reader {
type Error: Debug + Display;

fn get_balances(&self, script_key: &ScriptKey) -> Result<Vec<Balance>, Self::Error>;
// fn get_balances(&self, script_key: &ScriptKey) -> Result<Vec<Balance>, Self::Error>;
fn get_balance(
&self,
script_key: &ScriptKey,
tick: &Tick,
) -> Result<Option<Balance>, Self::Error>;

fn get_token_info(&self, tick: &Tick) -> Result<Option<TokenInfo>, Self::Error>;
fn get_tokens_info(&self) -> Result<Vec<TokenInfo>, Self::Error>;
// fn get_tokens_info(&self) -> Result<Vec<TokenInfo>, Self::Error>;

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

fn get_transferable_assets_by_satpoint(
&self,
satpoint: &SatPoint,
) -> Result<Option<TransferableLog>, Self::Error>;
fn get_transferable_assets_by_account(
&self,
script: &ScriptKey,
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
fn get_transferable_assets_by_account_ticker(
&self,
script: &ScriptKey,
tick: &Tick,
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
fn get_transferable_assets_by_outpoint(
&self,
outpoint: OutPoint,
) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
// fn get_transferable_assets_by_account(
// &self,
// script: &ScriptKey,
// ) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
// fn get_transferable_assets_by_account_ticker(
// &self,
// script: &ScriptKey,
// tick: &Tick,
// ) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
// fn get_transferable_assets_by_outpoint(
// &self,
// outpoint: OutPoint,
// ) -> Result<Vec<(SatPoint, TransferableLog)>, Self::Error>;
}

pub trait Brc20ReaderWriter: Brc20Reader {
Expand Down
109 changes: 109 additions & 0 deletions src/okx/datastore/ord/btc_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use {super::*, anyhow::anyhow, regex::Regex};

const BTC_DOMAIN_KEY: &str = r"BTC_NAME";

pub struct BtcName {
pub name: String,
pub domain: String,
}

const DEFAULT_DOMAIN_LIST: [&str; 4] = ["btc", "unisat", "sats", "x"];
impl BtcName {
pub fn parse(bytes: &[u8], domain_list: &[String]) -> Result<Self> {
let domains = if domain_list.is_empty() {
DEFAULT_DOMAIN_LIST.join("|")
} else {
domain_list.join("|")
};
let pattern = format!(r"^(?<name>.+)\.(?<domain>{domains})$");
let content = std::str::from_utf8(bytes)?;
let re = Regex::new(&pattern).unwrap();
if let Some(capture) = re.captures(&content.to_lowercase()) {
let name = &capture["name"];
let domain = &capture["domain"];
if Self::is_name_valid(name) {
return Ok(Self {
name: name.to_string(),
domain: domain.to_string(),
});
}
}
Err(anyhow!("No match found."))
}

/// check the name is valid or not
/// https://docs.btcname.id/docs/overview/chapter-4-thinking-about-.btc-domain-name/calibration-rules
fn is_name_valid(name: &str) -> bool {
let pattern = r"[\.\n ]";
let re = Regex::new(pattern).unwrap();
if re.captures(name).is_some() {
return false;
}
// check if it's json format
if name.contains("{") {
let value: Result<serde_json::Value, _> = serde_json::from_str(name);
return value.is_err();
}
true
}

pub fn to_collection_key(&self) -> String {
format!("{}_{}_{}", BTC_DOMAIN_KEY, self.name, self.domain)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn validate_regex() {
let domain_list = vec![];
let invalid_domains = [
"abc.bitmap",
"btc.com.btc",
"hi.jack.btc",
" jack.btc",
"jack.btc ",
"hi jack.btc",
" jack.btc ",
"jack.btc\n",
"\njack.btc",
"hi\njack.btc",
"\njack.btc\n",
r#"{ "p":"sns", "op":"reg", "name":"jack.btc"}"#,
];
for domain in invalid_domains {
let btc_name = BtcName::parse(domain.as_bytes(), &domain_list);
assert!(btc_name.is_err());
}

let valid_domains = [
"01.btc",
"123456.btc",
"Jack.btc",
"JACK.BTC",
"jack.BtC",
"比特币.btc",
"😀.btc",
"\\jack.btc",
"\tjack.btc",
];
for domain in valid_domains {
let btc_name = BtcName::parse(domain.as_bytes(), &domain_list);
assert!(btc_name.is_ok());
}

for d in DEFAULT_DOMAIN_LIST {
let s = format!("abc.{d}");
let btc_name = BtcName::parse(s.as_bytes(), &domain_list).unwrap();
assert!(DEFAULT_DOMAIN_LIST.contains(&btc_name.domain.as_str()));
assert_eq!(btc_name.name, "abc");
}
// new domain list
let domain_list = vec!["aaa".to_string(), "bbb".to_string()];
let btc_name = BtcName::parse("abc.aaa".as_bytes(), &domain_list).unwrap();
assert_eq!(btc_name.name, "abc");
assert_eq!(btc_name.domain, "aaa");
}
}
2 changes: 2 additions & 0 deletions src/okx/datastore/ord/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fmt::Display;
pub enum CollectionKind {
BitMap,
BRC20,
BtcName,
}
impl Display for CollectionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand All @@ -14,6 +15,7 @@ impl Display for CollectionKind {
"{}",
match self {
CollectionKind::BitMap => String::from("bitmap"),
CollectionKind::BtcName => String::from("btc_name"),
CollectionKind::BRC20 => String::from("brc20"),
}
)
Expand Down
17 changes: 9 additions & 8 deletions src/okx/datastore/ord/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use {
};

pub mod bitmap;
pub mod btc_name;
pub mod collections;
pub mod operation;
pub mod redb;
Expand All @@ -25,15 +26,15 @@ pub trait OrdReader {
chain: Chain,
) -> Result<ScriptKey, Self::Error>;

fn get_transaction_operations(
&self,
txid: &Txid,
) -> Result<Option<Vec<InscriptionOp>>, Self::Error>;
// fn get_transaction_operations(
// &self,
// txid: &Txid,
// ) -> Result<Option<Vec<InscriptionOp>>, Self::Error>;

fn get_collections_of_inscription(
&self,
inscription_id: &InscriptionId,
) -> Result<Option<Vec<CollectionKind>>, Self::Error>;
// fn get_collections_of_inscription(
// &self,
// inscription_id: &InscriptionId,
// ) -> Result<Option<Vec<CollectionKind>>, Self::Error>;

fn get_collection_inscription_id(
&self,
Expand Down
Loading

0 comments on commit 573ef8f

Please sign in to comment.