Skip to content

Commit

Permalink
Ordinal addresses (ordinals#1163)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Jan 5, 2023
1 parent 00662c6 commit ec4ac11
Show file tree
Hide file tree
Showing 17 changed files with 859 additions and 52 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [".", "test-bitcoincore-rpc"]
anyhow = { version = "1.0.56", features = ["backtrace"] }
axum = "0.6.1"
axum-server = "0.4.0"
bech32 = "0.9.1"
bitcoin = { version = "0.29.1", features = ["rand"] }
boilerplate = { version = "0.2.3", features = ["axum"] }
chrono = "0.4.19"
Expand Down
170 changes: 170 additions & 0 deletions src/any_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use super::*;

#[derive(Debug, PartialEq)]
pub(crate) enum AnyAddress {
Cardinal(Address),
Ordinal(OrdinalAddress),
}

impl AnyAddress {
pub(crate) fn is_valid_for_network(&self, network: Network) -> bool {
match self {
Self::Ordinal(address) => address.is_valid_for_network(network),
Self::Cardinal(address) => address.is_valid_for_network(network),
}
}

pub(crate) fn is_ordinal(&self) -> bool {
matches!(self, Self::Ordinal(_))
}
}

lazy_static! {
static ref ORDINAL_ADDRESS_REGEX: Regex = Regex::new(OrdinalAddress::PATTERN).unwrap();
}

impl FromStr for AnyAddress {
type Err = Error;

fn from_str(s: &str) -> Result<Self> {
if ORDINAL_ADDRESS_REGEX.is_match(s) {
Ok(Self::Ordinal(s.parse()?))
} else {
Ok(Self::Cardinal(s.parse()?))
}
}
}

impl From<AnyAddress> for Address {
fn from(any: AnyAddress) -> Address {
match any {
AnyAddress::Ordinal(address) => address.into(),
AnyAddress::Cardinal(address) => address,
}
}
}

impl Display for AnyAddress {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Ordinal(address) => write!(f, "{}", address),
Self::Cardinal(address) => write!(f, "{}", address),
}
}
}

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

#[test]
fn is_valid_for_network() {
assert!(AnyAddress::Ordinal(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse()
.unwrap()
)
.is_valid_for_network(Network::Bitcoin));

assert!(!AnyAddress::Ordinal(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse()
.unwrap()
)
.is_valid_for_network(Network::Testnet));

assert!(AnyAddress::Cardinal(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse()
.unwrap()
)
.is_valid_for_network(Network::Bitcoin));

assert!(!AnyAddress::Cardinal(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse()
.unwrap()
)
.is_valid_for_network(Network::Testnet));
}

#[test]
fn is_ordinal() {
assert!(AnyAddress::Ordinal(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse()
.unwrap()
)
.is_ordinal());

assert!(!AnyAddress::Cardinal(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse()
.unwrap()
)
.is_ordinal());
}

#[test]
fn from_str() {
assert_eq!(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse::<AnyAddress>()
.unwrap(),
AnyAddress::Ordinal(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse()
.unwrap()
),
);

assert_eq!(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse::<AnyAddress>()
.unwrap(),
AnyAddress::Cardinal(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse()
.unwrap()
),
);
}

#[test]
fn cardinal_into() {
let expected = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse::<Address>()
.unwrap();
let actual: Address = AnyAddress::Cardinal(expected.clone()).into();
assert_eq!(actual, expected);
}

#[test]
fn ordinal_into() {
let expected = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse::<Address>()
.unwrap();
let ordinal = OrdinalAddress::try_from(expected.clone()).unwrap();
let actual: Address = AnyAddress::Ordinal(ordinal).into();
assert_eq!(actual, expected);
}

#[test]
fn display() {
assert_eq!(
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
.parse::<AnyAddress>()
.unwrap()
.to_string(),
"ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw"
);

assert_eq!(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
.parse::<AnyAddress>()
.unwrap()
.to_string(),
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
);
}
}
10 changes: 5 additions & 5 deletions src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ pub(crate) enum Chain {
}

impl Chain {
pub(crate) fn network(self) -> bitcoin::Network {
pub(crate) fn network(self) -> Network {
match self {
Self::Mainnet => bitcoin::Network::Bitcoin,
Self::Testnet => bitcoin::Network::Testnet,
Self::Signet => bitcoin::Network::Signet,
Self::Regtest => bitcoin::Network::Regtest,
Self::Mainnet => Network::Bitcoin,
Self::Testnet => Network::Testnet,
Self::Signet => Network::Signet,
Self::Regtest => Network::Regtest,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/index/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl From<Block> for BlockData {
}
}

pub struct Updater {
pub(crate) struct Updater {
cache: HashMap<OutPointArray, Vec<u8>>,
height: u64,
index_sats: bool,
Expand Down
6 changes: 3 additions & 3 deletions src/inscription/content_type.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::*;

pub const HTML: &str = "text/html;charset=utf-8";
pub const SVG: &str = "image/svg+xml";
pub const TEXT: &str = "text/plain;charset=utf-8";
pub(crate) const HTML: &str = "text/html;charset=utf-8";
pub(crate) const SVG: &str = "image/svg+xml";
pub(crate) const TEXT: &str = "text/plain;charset=utf-8";

const TABLE: &[(&str, bool, &[&str])] = &[
("image/apng", true, &["apng"]),
Expand Down
11 changes: 10 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use {
self::{
any_address::AnyAddress,
arguments::Arguments,
blocktime::Blocktime,
content::Content,
Expand All @@ -21,9 +22,12 @@ use {
height::Height,
index::{Index, List},
inscription::Inscription,
object::Object,
options::Options,
ordinal_address::OrdinalAddress,
outgoing::Outgoing,
rarity::Rarity,
representation::Representation,
sat::Sat,
sat_point::SatPoint,
subcommand::Subcommand,
Expand All @@ -35,7 +39,8 @@ use {
consensus::{self, Decodable, Encodable},
hash_types::BlockHash,
hashes::Hash,
Address, Amount, Block, OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Txid,
util::address::{Payload, WitnessVersion},
Address, Amount, Block, Network, OutPoint, Script, Sequence, Transaction, TxIn, TxOut, Txid,
},
bitcoincore_rpc::{Client, RpcApi},
chain::Chain,
Expand Down Expand Up @@ -76,6 +81,7 @@ mod test;
#[cfg(test)]
use self::test::*;

mod any_address;
mod arguments;
mod blocktime;
mod chain;
Expand All @@ -86,9 +92,12 @@ mod epoch;
mod height;
mod index;
mod inscription;
mod object;
mod options;
mod ordinal_address;
mod outgoing;
mod rarity;
mod representation;
mod sat;
mod sat_point;
mod subcommand;
Expand Down
Loading

0 comments on commit ec4ac11

Please sign in to comment.