From 0f6fcae69c710fc57a669e63fc8e63fb677b9143 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 2 Feb 2024 16:08:47 -0800 Subject: [PATCH 01/12] Move sat, degree, epoch, height into ordinals crate --- Cargo.lock | 2 +- Cargo.toml | 1 - crates/ordinals/Cargo.toml | 3 +- {src => crates/ordinals/src}/decimal_sat.rs | 2 +- {src => crates/ordinals/src}/degree.rs | 10 +- {src => crates/ordinals/src}/epoch.rs | 12 +- {src => crates/ordinals/src}/height.rs | 10 +- crates/ordinals/src/lib.rs | 18 +++ {src => crates/ordinals/src}/rarity.rs | 8 +- {src => crates/ordinals/src}/sat.rs | 130 +++++++++++++------- src/lib.rs | 22 +--- src/subcommand/server.rs | 6 +- src/subcommand/wallet/sats.rs | 2 +- src/test.rs | 1 + tests/epochs.rs | 2 +- tests/json_api.rs | 2 +- tests/lib.rs | 3 +- tests/traits.rs | 2 +- tests/wallet/sats.rs | 2 +- 19 files changed, 141 insertions(+), 97 deletions(-) rename {src => crates/ordinals/src}/decimal_sat.rs (96%) rename {src => crates/ordinals/src}/degree.rs (91%) rename {src => crates/ordinals/src}/epoch.rs (95%) rename {src => crates/ordinals/src}/height.rs (93%) rename {src => crates/ordinals/src}/rarity.rs (96%) rename {src => crates/ordinals/src}/sat.rs (84%) diff --git a/Cargo.lock b/Cargo.lock index a2e40eff74..aa240359f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2167,7 +2167,6 @@ dependencies = [ "clap", "criterion", "ctrlc", - "derive_more", "dirs", "env_logger", "executable-path", @@ -2240,6 +2239,7 @@ name = "ordinals" version = "0.0.2" dependencies = [ "bitcoin", + "derive_more", "serde", "serde_json", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 55efe10fda..b25d88d599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ chrono = { version = "0.4.19", features = ["serde"] } ciborium = "0.2.1" clap = { version = "4.4.2", features = ["derive"] } ctrlc = { version = "3.2.1", features = ["termination"] } -derive_more = "0.99.17" dirs = "5.0.0" env_logger = "0.10.0" futures = "0.3.21" diff --git a/crates/ordinals/Cargo.toml b/crates/ordinals/Cargo.toml index be5cfc29ed..4dbbe14e18 100644 --- a/crates/ordinals/Cargo.toml +++ b/crates/ordinals/Cargo.toml @@ -9,8 +9,9 @@ license = "CC0-1.0" rust-version = "1.67" [dependencies] -serde = { version = "1.0.137", features = ["derive"] } bitcoin = { version = "0.30.1", features = ["rand"] } +derive_more = "0.99.17" +serde = { version = "1.0.137", features = ["derive"] } thiserror = "1.0.56" [dev-dependencies] diff --git a/src/decimal_sat.rs b/crates/ordinals/src/decimal_sat.rs similarity index 96% rename from src/decimal_sat.rs rename to crates/ordinals/src/decimal_sat.rs index a47dd3af15..346598adc3 100644 --- a/src/decimal_sat.rs +++ b/crates/ordinals/src/decimal_sat.rs @@ -1,7 +1,7 @@ use super::*; #[derive(PartialEq, Debug)] -pub(crate) struct DecimalSat { +pub struct DecimalSat { height: Height, offset: u64, } diff --git a/src/degree.rs b/crates/ordinals/src/degree.rs similarity index 91% rename from src/degree.rs rename to crates/ordinals/src/degree.rs index fb1d77157b..058e7a3176 100644 --- a/src/degree.rs +++ b/crates/ordinals/src/degree.rs @@ -1,11 +1,11 @@ use super::*; #[derive(PartialEq, Debug)] -pub(crate) struct Degree { - pub(crate) hour: u32, - pub(crate) minute: u32, - pub(crate) second: u32, - pub(crate) third: u64, +pub struct Degree { + pub hour: u32, + pub minute: u32, + pub second: u32, + pub third: u64, } impl Display for Degree { diff --git a/src/epoch.rs b/crates/ordinals/src/epoch.rs similarity index 95% rename from src/epoch.rs rename to crates/ordinals/src/epoch.rs index d8157897d8..9e7b861ee9 100644 --- a/src/epoch.rs +++ b/crates/ordinals/src/epoch.rs @@ -1,10 +1,10 @@ use super::*; #[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Serialize, PartialOrd)] -pub(crate) struct Epoch(pub(crate) u32); +pub struct Epoch(pub u32); impl Epoch { - pub(crate) const STARTING_SATS: [Sat; 34] = [ + pub const STARTING_SATS: [Sat; 34] = [ Sat(0), Sat(1050000000000000), Sat(1575000000000000), @@ -40,9 +40,9 @@ impl Epoch { Sat(2099999997480000), Sat(Sat::SUPPLY), ]; - pub(crate) const FIRST_POST_SUBSIDY: Epoch = Self(33); + pub const FIRST_POST_SUBSIDY: Epoch = Self(33); - pub(crate) fn subsidy(self) -> u64 { + pub fn subsidy(self) -> u64 { if self < Self::FIRST_POST_SUBSIDY { (50 * COIN_VALUE) >> self.0 } else { @@ -50,13 +50,13 @@ impl Epoch { } } - pub(crate) fn starting_sat(self) -> Sat { + pub fn starting_sat(self) -> Sat { *Self::STARTING_SATS .get(usize::try_from(self.0).unwrap()) .unwrap_or_else(|| Self::STARTING_SATS.last().unwrap()) } - pub(crate) fn starting_height(self) -> Height { + pub fn starting_height(self) -> Height { Height(self.0 * SUBSIDY_HALVING_INTERVAL) } } diff --git a/src/height.rs b/crates/ordinals/src/height.rs similarity index 93% rename from src/height.rs rename to crates/ordinals/src/height.rs index e96a66f8f4..ffae6b5bb2 100644 --- a/src/height.rs +++ b/crates/ordinals/src/height.rs @@ -1,25 +1,25 @@ use super::*; #[derive(Copy, Clone, Debug, Display, FromStr, Ord, Eq, Serialize, PartialEq, PartialOrd)] -pub(crate) struct Height(pub(crate) u32); +pub struct Height(pub u32); impl Height { - pub(crate) fn n(self) -> u32 { + pub fn n(self) -> u32 { self.0 } - pub(crate) fn subsidy(self) -> u64 { + pub fn subsidy(self) -> u64 { Epoch::from(self).subsidy() } - pub(crate) fn starting_sat(self) -> Sat { + pub fn starting_sat(self) -> Sat { let epoch = Epoch::from(self); let epoch_starting_sat = epoch.starting_sat(); let epoch_starting_height = epoch.starting_height(); epoch_starting_sat + u64::from(self.n() - epoch_starting_height.n()) * epoch.subsidy() } - pub(crate) fn period_offset(self) -> u32 { + pub fn period_offset(self) -> u32 { self.0 % DIFFCHANGE_INTERVAL } } diff --git a/crates/ordinals/src/lib.rs b/crates/ordinals/src/lib.rs index 1899ca3c8b..a5048e0b96 100644 --- a/crates/ordinals/src/lib.rs +++ b/crates/ordinals/src/lib.rs @@ -1,23 +1,41 @@ //! Types for interoperating with ordinals and inscriptions. use { + bitcoin::constants::{COIN_VALUE, DIFFCHANGE_INTERVAL, SUBSIDY_HALVING_INTERVAL}, bitcoin::{ consensus::{Decodable, Encodable}, OutPoint, }, + derive_more::{Display, FromStr}, serde::{Deserialize, Deserializer, Serialize, Serializer}, std::{ + cmp, fmt::{self, Display, Formatter}, io, + ops::{Add, AddAssign, Sub}, str::FromStr, }, thiserror::Error, }; +pub const CYCLE_EPOCHS: u32 = 6; + +pub use decimal_sat::DecimalSat; +pub use degree::Degree; +pub use epoch::Epoch; +pub use height::Height; +pub use rarity::Rarity; +pub use sat::Sat; pub use sat_point::SatPoint; #[doc(hidden)] pub use self::deserialize_from_str::DeserializeFromStr; +mod decimal_sat; +mod degree; mod deserialize_from_str; +mod epoch; +mod height; +mod rarity; +mod sat; mod sat_point; diff --git a/src/rarity.rs b/crates/ordinals/src/rarity.rs similarity index 96% rename from src/rarity.rs rename to crates/ordinals/src/rarity.rs index 6700fc47be..f79d4228c9 100644 --- a/src/rarity.rs +++ b/crates/ordinals/src/rarity.rs @@ -85,11 +85,17 @@ impl FromStr for Rarity { "epic" => Ok(Self::Epic), "legendary" => Ok(Self::Legendary), "mythic" => Ok(Self::Mythic), - _ => Err(anyhow!("invalid rarity: {s}")), + _ => Err(Error::InvalidRarity(s.into())), } } } +#[derive(Debug, Error)] +pub enum Error { + #[error("invalid rarity `{0}`")] + InvalidRarity(String), +} + impl Serialize for Rarity { fn serialize(&self, serializer: S) -> Result where diff --git a/src/sat.rs b/crates/ordinals/src/sat.rs similarity index 84% rename from src/sat.rs rename to crates/ordinals/src/sat.rs index 1d736b7210..652dc3d820 100644 --- a/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -5,71 +5,71 @@ use super::*; pub struct Sat(pub u64); impl Sat { - pub(crate) const LAST: Self = Self(Self::SUPPLY - 1); - pub(crate) const SUPPLY: u64 = 2099999997690000; + pub const LAST: Self = Self(Self::SUPPLY - 1); + pub const SUPPLY: u64 = 2099999997690000; - pub(crate) fn n(self) -> u64 { + pub fn n(self) -> u64 { self.0 } - pub(crate) fn degree(self) -> Degree { + pub fn degree(self) -> Degree { self.into() } - pub(crate) fn height(self) -> Height { + pub fn height(self) -> Height { self.epoch().starting_height() + u32::try_from(self.epoch_position() / self.epoch().subsidy()).unwrap() } - pub(crate) fn cycle(self) -> u32 { + pub fn cycle(self) -> u32 { Epoch::from(self).0 / CYCLE_EPOCHS } - pub(crate) fn nineball(self) -> bool { + pub fn nineball(self) -> bool { self.n() >= 50 * COIN_VALUE * 9 && self.n() < 50 * COIN_VALUE * 10 } - pub(crate) fn percentile(self) -> String { + pub fn percentile(self) -> String { format!("{}%", (self.0 as f64 / Self::LAST.0 as f64) * 100.0) } - pub(crate) fn epoch(self) -> Epoch { + pub fn epoch(self) -> Epoch { self.into() } - pub(crate) fn period(self) -> u32 { + pub fn period(self) -> u32 { self.height().n() / DIFFCHANGE_INTERVAL } - pub(crate) fn third(self) -> u64 { + pub fn third(self) -> u64 { self.epoch_position() % self.epoch().subsidy() } - pub(crate) fn epoch_position(self) -> u64 { + pub fn epoch_position(self) -> u64 { self.0 - self.epoch().starting_sat().0 } - pub(crate) fn decimal(self) -> DecimalSat { + pub fn decimal(self) -> DecimalSat { self.into() } - pub(crate) fn rarity(self) -> Rarity { + pub fn rarity(self) -> Rarity { self.into() } /// `Sat::rarity` is expensive and is called frequently when indexing. /// Sat::is_common only checks if self is `Rarity::Common` but is /// much faster. - pub(crate) fn common(self) -> bool { + pub fn common(self) -> bool { let epoch = self.epoch(); (self.0 - epoch.starting_sat().0) % epoch.subsidy() != 0 } - pub(crate) fn coin(self) -> bool { + pub fn coin(self) -> bool { self.n() % COIN_VALUE == 0 } - pub(crate) fn name(self) -> String { + pub fn name(self) -> String { let mut x = Self::SUPPLY - self.0; let mut name = String::new(); while x > 0 { @@ -84,42 +84,36 @@ impl Sat { name.chars().rev().collect() } - fn from_name(s: &str) -> Result { + fn from_name(s: &str) -> Result { let mut x = 0; for c in s.chars() { match c { 'a'..='z' => { x = x * 26 + c as u64 - 'a' as u64 + 1; if x > Self::SUPPLY { - bail!("sat name out of range"); + return Err(Error::SatName(s.into())); } } - _ => bail!("invalid character in sat name: {c}"), + _ => return Err(Error::Character(c.into())), } } Ok(Sat(Self::SUPPLY - x)) } - fn from_degree(degree: &str) -> Result { - let (cycle_number, rest) = degree - .split_once('°') - .ok_or_else(|| anyhow!("missing degree symbol"))?; + fn from_degree(degree: &str) -> Result { + let (cycle_number, rest) = degree.split_once('°').ok_or(Error::MissingDegree)?; let cycle_number = cycle_number.parse::()?; - let (epoch_offset, rest) = rest - .split_once('′') - .ok_or_else(|| anyhow!("missing minute symbol"))?; + let (epoch_offset, rest) = rest.split_once('′').ok_or(Error::MissingMinute)?; let epoch_offset = epoch_offset.parse::()?; if epoch_offset >= SUBSIDY_HALVING_INTERVAL { - bail!("invalid epoch offset"); + return Err(Error::EpochOffset); } - let (period_offset, rest) = rest - .split_once('″') - .ok_or_else(|| anyhow!("missing second symbol"))?; + let (period_offset, rest) = rest.split_once('″').ok_or(Error::MissingSecond)?; let period_offset = period_offset.parse::()?; if period_offset >= DIFFCHANGE_INTERVAL { - bail!("invalid period offset"); + return Err(Error::PeriodOffset); } let cycle_start_epoch = cycle_number * CYCLE_EPOCHS; @@ -131,7 +125,7 @@ impl Sat { let relationship = period_offset + SUBSIDY_HALVING_INTERVAL * CYCLE_EPOCHS - epoch_offset; if relationship % HALVING_INCREMENT != 0 { - bail!("relationship between epoch offset and period offset must be multiple of 336"); + return Err(Error::EpochPeriodMismatch); } let epochs_since_cycle_start = relationship % DIFFCHANGE_INTERVAL / HALVING_INCREMENT; @@ -146,39 +140,37 @@ impl Sat { }; if !rest.is_empty() { - bail!("trailing characters"); + return Err(Error::TrailingCharacters); } if block_offset >= height.subsidy() { - bail!("invalid block offset"); + return Err(Error::BlockOffset(block_offset.to_string())); } Ok(height.starting_sat() + block_offset) } - fn from_decimal(decimal: &str) -> Result { - let (height, offset) = decimal - .split_once('.') - .ok_or_else(|| anyhow!("missing period"))?; + fn from_decimal(decimal: &str) -> Result { + let (height, offset) = decimal.split_once('.').ok_or(Error::MissingPeriod)?; let height = Height(height.parse()?); let offset = offset.parse::()?; if offset >= height.subsidy() { - bail!("invalid block offset"); + return Err(Error::BlockOffset(offset.to_string())); } Ok(height.starting_sat() + offset) } - fn from_percentile(percentile: &str) -> Result { + fn from_percentile(percentile: &str) -> Result { if !percentile.ends_with('%') { - bail!("invalid percentile: {}", percentile); + return Err(Error::Percentile(percentile.into())); } let percentile = percentile[..percentile.len() - 1].parse::()?; if percentile < 0.0 { - bail!("invalid percentile: {}", percentile); + return Err(Error::Percentile(percentile.to_string())); } let last = Sat::LAST.n() as f64; @@ -186,7 +178,7 @@ impl Sat { let n = (percentile / 100.0 * last).round(); if n > last { - bail!("invalid percentile: {}", percentile); + return Err(Error::Percentile(percentile.to_string())); } #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] @@ -194,6 +186,52 @@ impl Sat { } } +#[derive(Debug, Error)] +pub enum Error { + #[error("invalid sat `{0}`")] + Sat(String), + #[error("sat name out of range `{0}`")] + SatName(String), + #[error("invalid character in sat name `{0}`")] + Character(String), + #[error("invalid percentil `{0}`")] + Percentile(String), + #[error("invalid block offset `{0}`")] + BlockOffset(String), + #[error("missing period")] + MissingPeriod, + #[error("trailing characters")] + TrailingCharacters, + #[error("missing degree symbol")] + MissingDegree, + #[error("missing minute symbol")] + MissingMinute, + #[error("missing second symbol")] + MissingSecond, + #[error("invalid period offset")] + PeriodOffset, + #[error("invalid epoch offset")] + EpochOffset, + #[error("relationship between epoch offset and period offset must be multiple of 336")] + EpochPeriodMismatch, + #[error("error parsing integer")] + ParseInt, + #[error("error parsing float")] + ParseFloat, +} + +impl From for Error { + fn from(_value: std::num::ParseIntError) -> Self { + Self::ParseInt + } +} + +impl From for Error { + fn from(_value: std::num::ParseFloatError) -> Self { + Self::ParseFloat + } +} + impl PartialEq for Sat { fn eq(&self, other: &u64) -> bool { self.0 == *other @@ -223,7 +261,7 @@ impl AddAssign for Sat { impl FromStr for Sat { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { if s.chars().any(|c| c.is_ascii_lowercase()) { Self::from_name(s) } else if s.contains('°') { @@ -235,7 +273,7 @@ impl FromStr for Sat { } else { let sat = Self(s.parse()?); if sat > Self::LAST { - Err(anyhow!("invalid sat")) + Err(Error::Sat(s.into())) } else { Ok(sat) } diff --git a/src/lib.rs b/src/lib.rs index a2c18bf780..a178e1f4b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,10 +17,6 @@ use { blocktime::Blocktime, config::Config, decimal::Decimal, - decimal_sat::DecimalSat, - degree::Degree, - epoch::Epoch, - height::Height, inscriptions::{media, teleburn, Charm, Media, ParsedEnvelope}, representation::Representation, runes::{Etching, Pile, SpacedRune}, @@ -32,9 +28,7 @@ use { bitcoin::{ address::{Address, NetworkUnchecked}, blockdata::{ - constants::{ - COIN_VALUE, DIFFCHANGE_INTERVAL, MAX_SCRIPT_ELEMENT_SIZE, SUBSIDY_HALVING_INTERVAL, - }, + constants::{DIFFCHANGE_INTERVAL, MAX_SCRIPT_ELEMENT_SIZE, SUBSIDY_HALVING_INTERVAL}, locktime::absolute::LockTime, }, consensus::{self, Decodable, Encodable}, @@ -49,10 +43,9 @@ use { chrono::{DateTime, TimeZone, Utc}, ciborium::Value, clap::{ArgGroup, Parser}, - derive_more::{Display, FromStr}, html_escaper::{Escape, Trusted}, lazy_static::lazy_static, - ordinals::{DeserializeFromStr, SatPoint}, + ordinals::{DeserializeFromStr, Epoch, Height, Rarity, Sat, SatPoint}, regex::Regex, serde::{Deserialize, Deserializer, Serialize, Serializer}, std::{ @@ -64,7 +57,6 @@ use { io::{self, Cursor, Read}, mem, net::ToSocketAddrs, - ops::{Add, AddAssign, Sub}, path::{Path, PathBuf}, process, str::FromStr, @@ -87,9 +79,7 @@ pub use self::{ inscriptions::{Envelope, Inscription, InscriptionId}, object::Object, options::Options, - rarity::Rarity, runes::{Edict, Rune, RuneId, Runestone}, - sat::Sat, wallet::transaction_builder::{Target, TransactionBuilder}, }; @@ -115,20 +105,14 @@ mod blocktime; pub mod chain; mod config; mod decimal; -mod decimal_sat; -mod degree; -mod epoch; mod fee_rate; -mod height; pub mod index; mod inscriptions; mod object; mod options; pub mod outgoing; -pub mod rarity; mod representation; pub mod runes; -pub mod sat; mod server_config; pub mod subcommand; mod tally; @@ -137,8 +121,6 @@ pub mod wallet; type Result = std::result::Result; -const CYCLE_EPOCHS: u32 = 6; - static SHUTTING_DOWN: AtomicBool = AtomicBool::new(false); static LISTENERS: Mutex> = Mutex::new(Vec::new()); static INDEXER: Mutex>> = Mutex::new(Option::None); diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 0d597b3076..5b3b2d4a1f 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2807,7 +2807,7 @@ mod tests { TestServer::new().assert_response( "/range/=/0", StatusCode::BAD_REQUEST, - "Invalid URL: invalid digit found in string", + "Invalid URL: error parsing integer", ); } @@ -2816,7 +2816,7 @@ mod tests { TestServer::new().assert_response( "/range/0/=", StatusCode::BAD_REQUEST, - "Invalid URL: invalid digit found in string", + "Invalid URL: error parsing integer", ); } @@ -2884,7 +2884,7 @@ mod tests { TestServer::new().assert_response( "/sat/2099999997690000", StatusCode::BAD_REQUEST, - "Invalid URL: invalid sat", + "Invalid URL: invalid sat `2099999997690000`", ); } diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index e279a45b9e..1a1fedd6ba 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -278,7 +278,7 @@ mod tests { sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") .unwrap_err() .to_string(), - "failed to parse sat from string \"===\" on line 2: invalid digit found in string", + "failed to parse sat from string \"===\" on line 2: error parsing integer", ) } diff --git a/src/test.rs b/src/test.rs index 1abe8b19d9..8c4e623625 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,6 +2,7 @@ pub(crate) use { super::*, bitcoin::{ blockdata::{opcodes, script, script::PushBytesBuf}, + constants::COIN_VALUE, ScriptBuf, Witness, }, pretty_assertions::assert_eq as pretty_assert_eq, diff --git a/tests/epochs.rs b/tests/epochs.rs index ef49c266d0..5dd93cf7c7 100644 --- a/tests/epochs.rs +++ b/tests/epochs.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::epochs::Output, ord::Sat}; +use {super::*, ord::subcommand::epochs::Output, ordinals::Sat}; #[test] fn empty() { diff --git a/tests/json_api.rs b/tests/json_api.rs index a0c0c4fd90..e7c690948b 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -166,7 +166,7 @@ fn get_inscription() { parent: None, previous: None, rune: None, - sat: Some(ord::Sat(50 * COIN_VALUE)), + sat: Some(ordinals::Sat(50 * COIN_VALUE)), satpoint: SatPoint::from_str(&format!("{}:{}:{}", reveal, 0, 0)).unwrap(), timestamp: 2, } diff --git a/tests/lib.rs b/tests/lib.rs index 7f4cb7fef6..09ffbeab50 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -13,7 +13,6 @@ use { ord::{ chain::Chain, outgoing::Outgoing, - rarity::Rarity, subcommand::runes::RuneInfo, templates::{ block::BlockJson, blocks::BlocksJson, inscription::InscriptionJson, @@ -22,7 +21,7 @@ use { }, Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, }, - ordinals::SatPoint, + ordinals::{Rarity, SatPoint}, pretty_assertions::assert_eq as pretty_assert_eq, regex::Regex, reqwest::{StatusCode, Url}, diff --git a/tests/traits.rs b/tests/traits.rs index 3eb4aa6fa1..3274397cfb 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::traits::Output, ord::Rarity}; +use {super::*, ord::subcommand::traits::Output, ordinals::Rarity}; #[test] fn traits_command_prints_sat_traits() { diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index 86c91c4d66..e221021bc3 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -75,7 +75,7 @@ fn sats_from_tsv_parse_error() { .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( - "error: failed to parse sat from string \"===\" on line 1: invalid digit found in string\n", + "error: failed to parse sat from string \"===\" on line 1: error parsing integer\n", ) .run_and_extract_stdout(); } From c0708a13e3ca990ceba25e75c56ceac2db5571d1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 2 Feb 2024 16:12:27 -0800 Subject: [PATCH 02/12] Amend --- crates/ordinals/src/rarity.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/ordinals/src/rarity.rs b/crates/ordinals/src/rarity.rs index f79d4228c9..fba10b5169 100644 --- a/crates/ordinals/src/rarity.rs +++ b/crates/ordinals/src/rarity.rs @@ -75,7 +75,7 @@ impl From for Rarity { } impl FromStr for Rarity { - type Err = Error; + type Err = String; fn from_str(s: &str) -> Result { match s { @@ -85,17 +85,11 @@ impl FromStr for Rarity { "epic" => Ok(Self::Epic), "legendary" => Ok(Self::Legendary), "mythic" => Ok(Self::Mythic), - _ => Err(Error::InvalidRarity(s.into())), + _ => Err(format!("invalid rarity `{s}`")), } } } -#[derive(Debug, Error)] -pub enum Error { - #[error("invalid rarity `{0}`")] - InvalidRarity(String), -} - impl Serialize for Rarity { fn serialize(&self, serializer: S) -> Result where From 0acfa23d073f47343f27778ef02f486bb7f5c7df Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 2 Feb 2024 16:14:44 -0800 Subject: [PATCH 03/12] Amend --- crates/ordinals/src/rarity.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/ordinals/src/rarity.rs b/crates/ordinals/src/rarity.rs index fba10b5169..f62b708cc0 100644 --- a/crates/ordinals/src/rarity.rs +++ b/crates/ordinals/src/rarity.rs @@ -172,13 +172,6 @@ mod tests { case("mythic", Rarity::Mythic); } - #[test] - fn from_str_err() { - "abc".parse::().unwrap_err(); - - "".parse::().unwrap_err(); - } - #[test] fn conversions_with_u8() { for &expected in &[ @@ -196,4 +189,9 @@ mod tests { assert_eq!(Rarity::try_from(6), Err(6)); } + + #[test] + fn error() { + assert_eq!("foo".parse::().unwrap_err(), "invalid rarity `foo`"); + } } From a86daee7e3ea7e57282e17b5da50a86d52dbc58d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 2 Feb 2024 16:45:58 -0800 Subject: [PATCH 04/12] Amend --- crates/ordinals/src/sat.rs | 138 ++++++++++++++++++++++------------ src/subcommand/server.rs | 4 +- src/subcommand/wallet/sats.rs | 2 +- tests/wallet/sats.rs | 2 +- 4 files changed, 93 insertions(+), 53 deletions(-) diff --git a/crates/ordinals/src/sat.rs b/crates/ordinals/src/sat.rs index 652dc3d820..a519aa9fb0 100644 --- a/crates/ordinals/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -91,7 +91,7 @@ impl Sat { 'a'..='z' => { x = x * 26 + c as u64 - 'a' as u64 + 1; if x > Self::SUPPLY { - return Err(Error::SatName(s.into())); + return Err(Error::Name(s.into())); } } _ => return Err(Error::Character(c.into())), @@ -101,19 +101,41 @@ impl Sat { } fn from_degree(degree: &str) -> Result { - let (cycle_number, rest) = degree.split_once('°').ok_or(Error::MissingDegree)?; - let cycle_number = cycle_number.parse::()?; + let (cycle_number, rest) = degree + .split_once('°') + .ok_or_else(|| Error::MissingDegree(degree.into()))?; + + let cycle_number = cycle_number.parse::().map_err(|err| Error::ParseInt { + s: degree.to_string(), + err, + })?; + + let (epoch_offset, rest) = rest + .split_once('′') + .ok_or_else(|| Error::MissingMinute(rest.into()))?; + + let epoch_offset = epoch_offset.parse::().map_err(|err| Error::ParseInt { + s: degree.to_string(), + err, + })?; - let (epoch_offset, rest) = rest.split_once('′').ok_or(Error::MissingMinute)?; - let epoch_offset = epoch_offset.parse::()?; if epoch_offset >= SUBSIDY_HALVING_INTERVAL { - return Err(Error::EpochOffset); + return Err(Error::EpochOffset(epoch_offset.to_string())); } - let (period_offset, rest) = rest.split_once('″').ok_or(Error::MissingSecond)?; - let period_offset = period_offset.parse::()?; + let (period_offset, rest) = rest + .split_once('″') + .ok_or_else(|| Error::MissingSecond(rest.into()))?; + + let period_offset = period_offset + .parse::() + .map_err(|err| Error::ParseInt { + s: degree.to_string(), + err, + })?; + if period_offset >= DIFFCHANGE_INTERVAL { - return Err(Error::PeriodOffset); + return Err(Error::PeriodOffset(period_offset.to_string())); } let cycle_start_epoch = cycle_number * CYCLE_EPOCHS; @@ -125,7 +147,7 @@ impl Sat { let relationship = period_offset + SUBSIDY_HALVING_INTERVAL * CYCLE_EPOCHS - epoch_offset; if relationship % HALVING_INCREMENT != 0 { - return Err(Error::EpochPeriodMismatch); + return Err(Error::EpochPeriodMismatch(relationship.to_string())); } let epochs_since_cycle_start = relationship % DIFFCHANGE_INTERVAL / HALVING_INCREMENT; @@ -135,12 +157,18 @@ impl Sat { let height = Height(epoch * SUBSIDY_HALVING_INTERVAL + epoch_offset); let (block_offset, rest) = match rest.split_once('‴') { - Some((block_offset, rest)) => (block_offset.parse::()?, rest), + Some((block_offset, rest)) => ( + block_offset.parse::().map_err(|err| Error::ParseInt { + s: block_offset.to_string(), + err, + })?, + rest, + ), None => (0, rest), }; if !rest.is_empty() { - return Err(Error::TrailingCharacters); + return Err(Error::TrailingCharacters(rest.to_string())); } if block_offset >= height.subsidy() { @@ -151,9 +179,19 @@ impl Sat { } fn from_decimal(decimal: &str) -> Result { - let (height, offset) = decimal.split_once('.').ok_or(Error::MissingPeriod)?; - let height = Height(height.parse()?); - let offset = offset.parse::()?; + let (height, offset) = decimal + .split_once('.') + .ok_or_else(|| Error::MissingPeriod(decimal.into()))?; + + let height = Height(height.parse().map_err(|err| Error::ParseInt { + s: height.to_string(), + err, + })?); + + let offset = offset.parse::().map_err(|err| Error::ParseInt { + s: offset.to_string(), + err, + })?; if offset >= height.subsidy() { return Err(Error::BlockOffset(offset.to_string())); @@ -167,7 +205,12 @@ impl Sat { return Err(Error::Percentile(percentile.into())); } - let percentile = percentile[..percentile.len() - 1].parse::()?; + let percentile = percentile[..percentile.len() - 1] + .parse::() + .map_err(|err| Error::ParseFloat { + s: percentile.to_string(), + err, + })?; if percentile < 0.0 { return Err(Error::Percentile(percentile.to_string())); @@ -191,45 +234,39 @@ pub enum Error { #[error("invalid sat `{0}`")] Sat(String), #[error("sat name out of range `{0}`")] - SatName(String), + Name(String), #[error("invalid character in sat name `{0}`")] Character(String), #[error("invalid percentil `{0}`")] Percentile(String), #[error("invalid block offset `{0}`")] BlockOffset(String), - #[error("missing period")] - MissingPeriod, + #[error("missing period `{0}`")] + MissingPeriod(String), #[error("trailing characters")] - TrailingCharacters, - #[error("missing degree symbol")] - MissingDegree, - #[error("missing minute symbol")] - MissingMinute, - #[error("missing second symbol")] - MissingSecond, - #[error("invalid period offset")] - PeriodOffset, - #[error("invalid epoch offset")] - EpochOffset, - #[error("relationship between epoch offset and period offset must be multiple of 336")] - EpochPeriodMismatch, - #[error("error parsing integer")] - ParseInt, - #[error("error parsing float")] - ParseFloat, -} - -impl From for Error { - fn from(_value: std::num::ParseIntError) -> Self { - Self::ParseInt - } -} - -impl From for Error { - fn from(_value: std::num::ParseFloatError) -> Self { - Self::ParseFloat - } + TrailingCharacters(String), + #[error("missing degree symbol `{0}`")] + MissingDegree(String), + #[error("missing minute symbol `{0}`")] + MissingMinute(String), + #[error("missing second symbol `{0}`")] + MissingSecond(String), + #[error("invalid period offset `{0}`")] + PeriodOffset(String), + #[error("invalid epoch offset `{0}`")] + EpochOffset(String), + #[error("relationship between epoch offset and period offset must be multiple of 336 `{0}`")] + EpochPeriodMismatch(String), + #[error("error parsing `{s}`: {err}")] + ParseInt { + s: String, + err: std::num::ParseIntError, + }, + #[error("error parsing `{s}`: {err}")] + ParseFloat { + s: String, + err: std::num::ParseFloatError, + }, } impl PartialEq for Sat { @@ -271,7 +308,10 @@ impl FromStr for Sat { } else if s.contains('.') { Self::from_decimal(s) } else { - let sat = Self(s.parse()?); + let sat = Self(s.parse().map_err(|err| Error::ParseInt { + s: s.to_string(), + err, + })?); if sat > Self::LAST { Err(Error::Sat(s.into())) } else { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 5b3b2d4a1f..cb1be9ea4b 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2807,7 +2807,7 @@ mod tests { TestServer::new().assert_response( "/range/=/0", StatusCode::BAD_REQUEST, - "Invalid URL: error parsing integer", + "Invalid URL: error parsing `=`: invalid digit found in string", ); } @@ -2816,7 +2816,7 @@ mod tests { TestServer::new().assert_response( "/range/0/=", StatusCode::BAD_REQUEST, - "Invalid URL: error parsing integer", + "Invalid URL: error parsing `=`: invalid digit found in string", ); } diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 1a1fedd6ba..267be51840 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -278,7 +278,7 @@ mod tests { sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") .unwrap_err() .to_string(), - "failed to parse sat from string \"===\" on line 2: error parsing integer", + "failed to parse sat from string \"===\" on line 2: error parsing `===`: invalid digit found in string", ) } diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index e221021bc3..c5b1c2386d 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -75,7 +75,7 @@ fn sats_from_tsv_parse_error() { .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( - "error: failed to parse sat from string \"===\" on line 1: error parsing integer\n", + "error: failed to parse sat from string \"===\" on line 1: error parsing `===`: invalid digit found in string\n", ) .run_and_extract_stdout(); } From 15581c782268c40059708ab29af511e05a612450 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 2 Feb 2024 16:48:08 -0800 Subject: [PATCH 05/12] Amend --- crates/ordinals/src/sat.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ordinals/src/sat.rs b/crates/ordinals/src/sat.rs index a519aa9fb0..6d763a6e9b 100644 --- a/crates/ordinals/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -91,10 +91,10 @@ impl Sat { 'a'..='z' => { x = x * 26 + c as u64 - 'a' as u64 + 1; if x > Self::SUPPLY { - return Err(Error::Name(s.into())); + return Err(Error::NameRange(s.into())); } } - _ => return Err(Error::Character(c.into())), + _ => return Err(Error::NameCharacter(c.into())), } } Ok(Sat(Self::SUPPLY - x)) @@ -234,9 +234,9 @@ pub enum Error { #[error("invalid sat `{0}`")] Sat(String), #[error("sat name out of range `{0}`")] - Name(String), + NameRange(String), #[error("invalid character in sat name `{0}`")] - Character(String), + NameCharacter(String), #[error("invalid percentil `{0}`")] Percentile(String), #[error("invalid block offset `{0}`")] From fa27adaeffed19573caa01c8b5db100e2b121af6 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 3 Feb 2024 18:16:22 -0800 Subject: [PATCH 06/12] Amend --- crates/ordinals/src/sat.rs | 185 ++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/crates/ordinals/src/sat.rs b/crates/ordinals/src/sat.rs index 6d763a6e9b..2cbc1e1df3 100644 --- a/crates/ordinals/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -91,10 +91,10 @@ impl Sat { 'a'..='z' => { x = x * 26 + c as u64 - 'a' as u64 + 1; if x > Self::SUPPLY { - return Err(Error::NameRange(s.into())); + return Err(ErrorKind::NameRange.error(s)); } } - _ => return Err(Error::NameCharacter(c.into())), + _ => return Err(ErrorKind::NameCharacter.error(s)), } } Ok(Sat(Self::SUPPLY - x)) @@ -103,39 +103,34 @@ impl Sat { fn from_degree(degree: &str) -> Result { let (cycle_number, rest) = degree .split_once('°') - .ok_or_else(|| Error::MissingDegree(degree.into()))?; + .ok_or_else(|| ErrorKind::MissingDegree.error(degree))?; - let cycle_number = cycle_number.parse::().map_err(|err| Error::ParseInt { - s: degree.to_string(), - err, - })?; + let cycle_number = cycle_number + .parse::() + .map_err(|_err| ErrorKind::ParseInt.error(degree))?; let (epoch_offset, rest) = rest .split_once('′') - .ok_or_else(|| Error::MissingMinute(rest.into()))?; + .ok_or_else(|| ErrorKind::MissingMinute.error(degree))?; - let epoch_offset = epoch_offset.parse::().map_err(|err| Error::ParseInt { - s: degree.to_string(), - err, - })?; + let epoch_offset = epoch_offset + .parse::() + .map_err(|_err| ErrorKind::ParseInt.error(degree))?; if epoch_offset >= SUBSIDY_HALVING_INTERVAL { - return Err(Error::EpochOffset(epoch_offset.to_string())); + return Err(ErrorKind::EpochOffset.error(degree)); } let (period_offset, rest) = rest .split_once('″') - .ok_or_else(|| Error::MissingSecond(rest.into()))?; + .ok_or_else(|| ErrorKind::MissingSecond.error(degree))?; let period_offset = period_offset .parse::() - .map_err(|err| Error::ParseInt { - s: degree.to_string(), - err, - })?; + .map_err(|_err| ErrorKind::ParseInt.error(degree))?; if period_offset >= DIFFCHANGE_INTERVAL { - return Err(Error::PeriodOffset(period_offset.to_string())); + return Err(ErrorKind::PeriodOffset.error(degree)); } let cycle_start_epoch = cycle_number * CYCLE_EPOCHS; @@ -147,7 +142,7 @@ impl Sat { let relationship = period_offset + SUBSIDY_HALVING_INTERVAL * CYCLE_EPOCHS - epoch_offset; if relationship % HALVING_INCREMENT != 0 { - return Err(Error::EpochPeriodMismatch(relationship.to_string())); + return Err(ErrorKind::EpochPeriodMismatch.error(degree)); } let epochs_since_cycle_start = relationship % DIFFCHANGE_INTERVAL / HALVING_INCREMENT; @@ -158,21 +153,20 @@ impl Sat { let (block_offset, rest) = match rest.split_once('‴') { Some((block_offset, rest)) => ( - block_offset.parse::().map_err(|err| Error::ParseInt { - s: block_offset.to_string(), - err, - })?, + block_offset + .parse::() + .map_err(|_err| ErrorKind::ParseInt.error(degree))?, rest, ), None => (0, rest), }; if !rest.is_empty() { - return Err(Error::TrailingCharacters(rest.to_string())); + return Err(ErrorKind::TrailingCharacters.error(degree)); } if block_offset >= height.subsidy() { - return Err(Error::BlockOffset(block_offset.to_string())); + return Err(ErrorKind::BlockOffset.error(degree)); } Ok(height.starting_sat() + block_offset) @@ -181,20 +175,20 @@ impl Sat { fn from_decimal(decimal: &str) -> Result { let (height, offset) = decimal .split_once('.') - .ok_or_else(|| Error::MissingPeriod(decimal.into()))?; + .ok_or_else(|| ErrorKind::MissingPeriod.error(decimal))?; - let height = Height(height.parse().map_err(|err| Error::ParseInt { - s: height.to_string(), - err, - })?); + let height = Height( + height + .parse() + .map_err(|_err| ErrorKind::ParseInt.error(decimal))?, + ); - let offset = offset.parse::().map_err(|err| Error::ParseInt { - s: offset.to_string(), - err, - })?; + let offset = offset + .parse::() + .map_err(|_err| ErrorKind::ParseInt.error(decimal))?; if offset >= height.subsidy() { - return Err(Error::BlockOffset(offset.to_string())); + return Err(ErrorKind::BlockOffset.error(decimal)); } Ok(height.starting_sat() + offset) @@ -202,18 +196,17 @@ impl Sat { fn from_percentile(percentile: &str) -> Result { if !percentile.ends_with('%') { - return Err(Error::Percentile(percentile.into())); + return Err(ErrorKind::Percentile.error(percentile)); } + let percentile_string = percentile; + let percentile = percentile[..percentile.len() - 1] .parse::() - .map_err(|err| Error::ParseFloat { - s: percentile.to_string(), - err, - })?; + .map_err(|_err| ErrorKind::ParseFloat.error(percentile))?; if percentile < 0.0 { - return Err(Error::Percentile(percentile.to_string())); + return Err(ErrorKind::Percentile.error(percentile_string)); } let last = Sat::LAST.n() as f64; @@ -221,7 +214,7 @@ impl Sat { let n = (percentile / 100.0 * last).round(); if n > last { - return Err(Error::Percentile(percentile.to_string())); + return Err(ErrorKind::Percentile.error(percentile_string)); } #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] @@ -230,43 +223,68 @@ impl Sat { } #[derive(Debug, Error)] -pub enum Error { - #[error("invalid sat `{0}`")] - Sat(String), - #[error("sat name out of range `{0}`")] - NameRange(String), - #[error("invalid character in sat name `{0}`")] - NameCharacter(String), - #[error("invalid percentil `{0}`")] - Percentile(String), - #[error("invalid block offset `{0}`")] - BlockOffset(String), - #[error("missing period `{0}`")] - MissingPeriod(String), - #[error("trailing characters")] - TrailingCharacters(String), - #[error("missing degree symbol `{0}`")] - MissingDegree(String), - #[error("missing minute symbol `{0}`")] - MissingMinute(String), - #[error("missing second symbol `{0}`")] - MissingSecond(String), - #[error("invalid period offset `{0}`")] - PeriodOffset(String), - #[error("invalid epoch offset `{0}`")] - EpochOffset(String), - #[error("relationship between epoch offset and period offset must be multiple of 336 `{0}`")] - EpochPeriodMismatch(String), - #[error("error parsing `{s}`: {err}")] - ParseInt { - s: String, - err: std::num::ParseIntError, - }, - #[error("error parsing `{s}`: {err}")] - ParseFloat { - s: String, - err: std::num::ParseFloatError, - }, +pub struct Error { + input: String, + kind: ErrorKind, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "invalid sat `{}`: {}", self.input, self.kind) + } +} + +#[derive(Debug, Error)] +pub enum ErrorKind { + IntegerRange, + NameRange, + NameCharacter, + Percentile, + BlockOffset, + MissingPeriod, + TrailingCharacters, + MissingDegree, + MissingMinute, + MissingSecond, + PeriodOffset, + EpochOffset, + EpochPeriodMismatch, + ParseInt, + ParseFloat, +} + +impl ErrorKind { + fn error(self, input: &str) -> Error { + Error { + input: input.to_string(), + kind: self, + } + } +} + +impl Display for ErrorKind { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::IntegerRange => write!(f, "integer range"), + Self::NameRange => write!(f, "name range"), + Self::NameCharacter => write!(f, "character in sat name"), + Self::Percentile => write!(f, "percentile"), + Self::BlockOffset => write!(f, "block offset"), + Self::MissingPeriod => write!(f, "missing period"), + Self::TrailingCharacters => write!(f, "trailing character"), + Self::MissingDegree => write!(f, "missing degree symbol"), + Self::MissingMinute => write!(f, "missing minute symbol"), + Self::MissingSecond => write!(f, "missing second symbol"), + Self::PeriodOffset => write!(f, "period offset"), + Self::EpochOffset => write!(f, "epoch offset"), + Self::EpochPeriodMismatch => write!( + f, + "relationship between epoch offset and period offset must be multiple of 336" + ), + Self::ParseInt => write!(f, "parsing integer"), + Self::ParseFloat => write!(f, "parsing float"), + } + } } impl PartialEq for Sat { @@ -308,12 +326,9 @@ impl FromStr for Sat { } else if s.contains('.') { Self::from_decimal(s) } else { - let sat = Self(s.parse().map_err(|err| Error::ParseInt { - s: s.to_string(), - err, - })?); + let sat = Self(s.parse().map_err(|_err| ErrorKind::ParseInt.error(s))?); if sat > Self::LAST { - Err(Error::Sat(s.into())) + Err(ErrorKind::IntegerRange.error(s)) } else { Ok(sat) } From c18c46a2bba566809ce230d58a5cbd93bb1b8d91 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 3 Feb 2024 18:23:10 -0800 Subject: [PATCH 07/12] Amend --- src/subcommand/server.rs | 6 +++--- src/subcommand/wallet/sats.rs | 2 +- tests/wallet/sats.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index cb1be9ea4b..34f95025d4 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2807,7 +2807,7 @@ mod tests { TestServer::new().assert_response( "/range/=/0", StatusCode::BAD_REQUEST, - "Invalid URL: error parsing `=`: invalid digit found in string", + "Invalid URL: invalid sat `=`: parsing integer", ); } @@ -2816,7 +2816,7 @@ mod tests { TestServer::new().assert_response( "/range/0/=", StatusCode::BAD_REQUEST, - "Invalid URL: error parsing `=`: invalid digit found in string", + "Invalid URL: invalid sat `=`: parsing integer", ); } @@ -2884,7 +2884,7 @@ mod tests { TestServer::new().assert_response( "/sat/2099999997690000", StatusCode::BAD_REQUEST, - "Invalid URL: invalid sat `2099999997690000`", + "Invalid URL: invalid sat `2099999997690000`: integer range", ); } diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 267be51840..ae6de38a15 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -278,7 +278,7 @@ mod tests { sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") .unwrap_err() .to_string(), - "failed to parse sat from string \"===\" on line 2: error parsing `===`: invalid digit found in string", + "failed to parse sat from string \"===\" on line 2: invalid sat `===`: parsing integer", ) } diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index c5b1c2386d..34333c9327 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -75,7 +75,7 @@ fn sats_from_tsv_parse_error() { .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( - "error: failed to parse sat from string \"===\" on line 1: error parsing `===`: invalid digit found in string\n", + "error: failed to parse sat from string \"===\" on line 1: invalid sat `===`: parsing integer\n", ) .run_and_extract_stdout(); } From 79c9ae6bf87639874e5bba25fae7f8a5eb3cba86 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 12 Feb 2024 14:16:24 -0800 Subject: [PATCH 08/12] Mkae decimajasdf;lkjasdflkj --- crates/ordinals/src/decimal_sat.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ordinals/src/decimal_sat.rs b/crates/ordinals/src/decimal_sat.rs index 346598adc3..185a04dac4 100644 --- a/crates/ordinals/src/decimal_sat.rs +++ b/crates/ordinals/src/decimal_sat.rs @@ -2,8 +2,8 @@ use super::*; #[derive(PartialEq, Debug)] pub struct DecimalSat { - height: Height, - offset: u64, + pub height: Height, + pub offset: u64, } impl From for DecimalSat { From 6cbba53441e126d5f4569b8a99c55adb37533667 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 12 Feb 2024 14:31:51 -0800 Subject: [PATCH 09/12] Homogenize --- crates/ordinals/src/lib.rs | 12 +++---- crates/ordinals/src/sat.rs | 57 ++++++++++++++++++++------------ crates/ordinals/src/sat_point.rs | 2 +- tests/json_api.rs | 2 +- tests/lib.rs | 2 +- 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/crates/ordinals/src/lib.rs b/crates/ordinals/src/lib.rs index a5048e0b96..5f178ade34 100644 --- a/crates/ordinals/src/lib.rs +++ b/crates/ordinals/src/lib.rs @@ -12,6 +12,7 @@ use { cmp, fmt::{self, Display, Formatter}, io, + num::ParseIntError, ops::{Add, AddAssign, Sub}, str::FromStr, }, @@ -20,13 +21,10 @@ use { pub const CYCLE_EPOCHS: u32 = 6; -pub use decimal_sat::DecimalSat; -pub use degree::Degree; -pub use epoch::Epoch; -pub use height::Height; -pub use rarity::Rarity; -pub use sat::Sat; -pub use sat_point::SatPoint; +pub use { + decimal_sat::DecimalSat, degree::Degree, epoch::Epoch, height::Height, rarity::Rarity, sat::Sat, + sat_point::SatPoint, +}; #[doc(hidden)] pub use self::deserialize_from_str::DeserializeFromStr; diff --git a/crates/ordinals/src/sat.rs b/crates/ordinals/src/sat.rs index 2cbc1e1df3..4c4dbc775e 100644 --- a/crates/ordinals/src/sat.rs +++ b/crates/ordinals/src/sat.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, std::num::ParseFloatError}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Ord, PartialOrd, Deserialize, Serialize)] #[serde(transparent)] @@ -107,7 +107,7 @@ impl Sat { let cycle_number = cycle_number .parse::() - .map_err(|_err| ErrorKind::ParseInt.error(degree))?; + .map_err(|source| ErrorKind::ParseInt { source }.error(degree))?; let (epoch_offset, rest) = rest .split_once('′') @@ -115,7 +115,7 @@ impl Sat { let epoch_offset = epoch_offset .parse::() - .map_err(|_err| ErrorKind::ParseInt.error(degree))?; + .map_err(|source| ErrorKind::ParseInt { source }.error(degree))?; if epoch_offset >= SUBSIDY_HALVING_INTERVAL { return Err(ErrorKind::EpochOffset.error(degree)); @@ -127,7 +127,7 @@ impl Sat { let period_offset = period_offset .parse::() - .map_err(|_err| ErrorKind::ParseInt.error(degree))?; + .map_err(|source| ErrorKind::ParseInt { source }.error(degree))?; if period_offset >= DIFFCHANGE_INTERVAL { return Err(ErrorKind::PeriodOffset.error(degree)); @@ -155,7 +155,7 @@ impl Sat { Some((block_offset, rest)) => ( block_offset .parse::() - .map_err(|_err| ErrorKind::ParseInt.error(degree))?, + .map_err(|source| ErrorKind::ParseInt { source }.error(degree))?, rest, ), None => (0, rest), @@ -180,12 +180,12 @@ impl Sat { let height = Height( height .parse() - .map_err(|_err| ErrorKind::ParseInt.error(decimal))?, + .map_err(|source| ErrorKind::ParseInt { source }.error(decimal))?, ); let offset = offset .parse::() - .map_err(|_err| ErrorKind::ParseInt.error(decimal))?; + .map_err(|source| ErrorKind::ParseInt { source }.error(decimal))?; if offset >= height.subsidy() { return Err(ErrorKind::BlockOffset.error(decimal)); @@ -203,7 +203,7 @@ impl Sat { let percentile = percentile[..percentile.len() - 1] .parse::() - .map_err(|_err| ErrorKind::ParseFloat.error(percentile))?; + .map_err(|source| ErrorKind::ParseFloat { source }.error(percentile))?; if percentile < 0.0 { return Err(ErrorKind::Percentile.error(percentile_string)); @@ -230,7 +230,7 @@ pub struct Error { impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "invalid sat `{}`: {}", self.input, self.kind) + write!(f, "failed to parse sat `{}`: {}", self.input, self.kind) } } @@ -249,8 +249,8 @@ pub enum ErrorKind { PeriodOffset, EpochOffset, EpochPeriodMismatch, - ParseInt, - ParseFloat, + ParseInt { source: ParseIntError }, + ParseFloat { source: ParseFloatError }, } impl ErrorKind { @@ -265,24 +265,24 @@ impl ErrorKind { impl Display for ErrorKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Self::IntegerRange => write!(f, "integer range"), - Self::NameRange => write!(f, "name range"), - Self::NameCharacter => write!(f, "character in sat name"), - Self::Percentile => write!(f, "percentile"), - Self::BlockOffset => write!(f, "block offset"), + Self::IntegerRange => write!(f, "invalid integer range"), + Self::NameRange => write!(f, "invalid name range"), + Self::NameCharacter => write!(f, "invalid character in name"), + Self::Percentile => write!(f, "invalid percentile"), + Self::BlockOffset => write!(f, "invalid block offset"), Self::MissingPeriod => write!(f, "missing period"), Self::TrailingCharacters => write!(f, "trailing character"), Self::MissingDegree => write!(f, "missing degree symbol"), Self::MissingMinute => write!(f, "missing minute symbol"), Self::MissingSecond => write!(f, "missing second symbol"), - Self::PeriodOffset => write!(f, "period offset"), - Self::EpochOffset => write!(f, "epoch offset"), + Self::PeriodOffset => write!(f, "invalid period offset"), + Self::EpochOffset => write!(f, "invalid epoch offset"), Self::EpochPeriodMismatch => write!( f, "relationship between epoch offset and period offset must be multiple of 336" ), - Self::ParseInt => write!(f, "parsing integer"), - Self::ParseFloat => write!(f, "parsing float"), + Self::ParseInt { source } => write!(f, "invalid integer: {source}"), + Self::ParseFloat { source } => write!(f, "invalid float: {source}"), } } } @@ -326,7 +326,10 @@ impl FromStr for Sat { } else if s.contains('.') { Self::from_decimal(s) } else { - let sat = Self(s.parse().map_err(|_err| ErrorKind::ParseInt.error(s))?); + let sat = Self( + s.parse() + .map_err(|source| ErrorKind::ParseInt { source }.error(s))?, + ); if sat > Self::LAST { Err(ErrorKind::IntegerRange.error(s)) } else { @@ -749,4 +752,16 @@ mod tests { ); } } + + #[test] + fn error_display() { + assert_eq!( + Error { + input: "foo".into(), + kind: ErrorKind::Percentile + } + .to_string(), + "failed to parse sat `foo`: invalid percentile", + ); + } } diff --git a/crates/ordinals/src/sat_point.rs b/crates/ordinals/src/sat_point.rs index 48c56efe59..5a2375dc26 100644 --- a/crates/ordinals/src/sat_point.rs +++ b/crates/ordinals/src/sat_point.rs @@ -1,4 +1,4 @@ -use {super::*, bitcoin::transaction::ParseOutPointError, std::num::ParseIntError}; +use {super::*, bitcoin::transaction::ParseOutPointError}; /// A satpoint identifies the location of a sat in an output. /// diff --git a/tests/json_api.rs b/tests/json_api.rs index e7c690948b..cece0b6b66 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -166,7 +166,7 @@ fn get_inscription() { parent: None, previous: None, rune: None, - sat: Some(ordinals::Sat(50 * COIN_VALUE)), + sat: Some(Sat(50 * COIN_VALUE)), satpoint: SatPoint::from_str(&format!("{}:{}:{}", reveal, 0, 0)).unwrap(), timestamp: 2, } diff --git a/tests/lib.rs b/tests/lib.rs index 09ffbeab50..9f8d417858 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -21,7 +21,7 @@ use { }, Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, }, - ordinals::{Rarity, SatPoint}, + ordinals::{Rarity, Sat, SatPoint}, pretty_assertions::assert_eq as pretty_assert_eq, regex::Regex, reqwest::{StatusCode, Url}, From 5cedbc39bbae8af13baa917983c83c84dd96d0c7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 12 Feb 2024 14:35:03 -0800 Subject: [PATCH 10/12] Reticulate --- src/subcommand/server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 34f95025d4..e04fee8ef3 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2807,7 +2807,7 @@ mod tests { TestServer::new().assert_response( "/range/=/0", StatusCode::BAD_REQUEST, - "Invalid URL: invalid sat `=`: parsing integer", + "Invalid URL: failed ot parse sat `=`: invalid integer: invalid digit found in string", ); } @@ -2816,7 +2816,7 @@ mod tests { TestServer::new().assert_response( "/range/0/=", StatusCode::BAD_REQUEST, - "Invalid URL: invalid sat `=`: parsing integer", + "Invalid URL: failed to parse sat `=`: invalid integer: invalid digit found in string", ); } @@ -2884,7 +2884,7 @@ mod tests { TestServer::new().assert_response( "/sat/2099999997690000", StatusCode::BAD_REQUEST, - "Invalid URL: invalid sat `2099999997690000`: integer range", + "Invalid URL: failed to parse sat `2099999997690000`: invalid integer range", ); } From b50822055386ea1cc263371365b92a70cf69b265 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 12 Feb 2024 14:35:15 -0800 Subject: [PATCH 11/12] Tweak --- src/subcommand/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index e04fee8ef3..b20182b950 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2807,7 +2807,7 @@ mod tests { TestServer::new().assert_response( "/range/=/0", StatusCode::BAD_REQUEST, - "Invalid URL: failed ot parse sat `=`: invalid integer: invalid digit found in string", + "Invalid URL: failed to parse sat `=`: invalid integer: invalid digit found in string", ); } From 9c8a8eff3d4f5b142a8b01d3f8d6c1df413a3103 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 12 Feb 2024 14:38:17 -0800 Subject: [PATCH 12/12] Tweak --- src/subcommand/wallet/sats.rs | 2 +- tests/wallet/sats.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index ae6de38a15..4e86533698 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -278,7 +278,7 @@ mod tests { sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") .unwrap_err() .to_string(), - "failed to parse sat from string \"===\" on line 2: invalid sat `===`: parsing integer", + "failed to parse sat from string \"===\" on line 2: failed to parse sat `===`: invalid integer: invalid digit found in string", ) } diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index 34333c9327..3eea9b8b03 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -75,7 +75,7 @@ fn sats_from_tsv_parse_error() { .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( - "error: failed to parse sat from string \"===\" on line 1: invalid sat `===`: parsing integer\n", + "error: failed to parse sat from string \"===\" on line 1: failed to parse sat `===`: invalid integer: invalid digit found in string\n", ) .run_and_extract_stdout(); }