From 46454ff5add8deff169fe625b4fd27a9f429792c Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 18 Jan 2022 13:05:28 -0800 Subject: [PATCH 01/10] Add Ordinal struct --- src/arguments.rs | 8 ++++---- src/find.rs | 14 +++++++++++--- src/main.rs | 21 ++++++++++++--------- src/name.rs | 2 +- src/ordinal.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/range.rs | 6 +++++- src/traits.rs | 30 ++++++++++++++---------------- tests/traits.rs | 2 +- 8 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 src/ordinal.rs diff --git a/src/arguments.rs b/src/arguments.rs index 33b0f93ee2..76429174c2 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -1,11 +1,11 @@ use super::*; #[derive(StructOpt)] -pub enum Arguments { +pub(crate) enum Arguments { Find { #[structopt(long)] blocksdir: Option, - ordinal: u64, + ordinal: Ordinal, height: u64, }, Name { @@ -18,12 +18,12 @@ pub enum Arguments { }, Supply, Traits { - ordinal: u64, + ordinal: Ordinal, }, } impl Arguments { - pub fn run(self) -> Result<()> { + pub(crate) fn run(self) -> Result<()> { match self { Self::Find { blocksdir, diff --git a/src/find.rs b/src/find.rs index b8571afddf..bc731d61fe 100644 --- a/src/find.rs +++ b/src/find.rs @@ -1,15 +1,23 @@ use super::*; -pub(crate) fn run(blocksdir: Option<&Path>, ordinal: u64, at_height: u64) -> Result<()> { +// find: +// - find ordinal N +// - find transaction in which is was mined +// - scan blocks forward to see if that transaction was spent +// - track position in transactions +// - finally get to final transaction +// - print that transaction + +pub(crate) fn run(blocksdir: Option<&Path>, ordinal: Ordinal, at_height: u64) -> Result<()> { let index = Index::new(blocksdir)?; - let height = ordinal / (50 * 100_000_000); + let height = ordinal.height(); assert!(height < 100); assert!(at_height == height); let block = index.block(height)?; - let position = ordinal % (50 * 100_000_000); + let position = ordinal.position_in_coinbase(); let mut ordinal = 0; for (i, output) in block.txdata[0].output.iter().enumerate() { diff --git a/src/main.rs b/src/main.rs index 0835acc671..88ee70a77b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use { - crate::index::Index, + crate::{index::Index, ordinal::Ordinal}, arguments::Arguments, bitcoin::{ blockdata::constants::{genesis_block, COIN_VALUE}, @@ -19,6 +19,7 @@ use { ops::Range, path::{Path, PathBuf}, process, + str::FromStr, }, structopt::StructOpt, }; @@ -27,6 +28,7 @@ mod arguments; mod find; mod index; mod name; +mod ordinal; mod range; mod supply; mod traits; @@ -56,8 +58,8 @@ fn subsidy(height: u64) -> u64 { } } -fn name(ordinal: u64) -> String { - let mut x = SUPPLY - ordinal - 1; +fn name(ordinal: Ordinal) -> String { + let mut x = SUPPLY - ordinal.number() - 1; let mut name = String::new(); while x > 0 { name.push( @@ -94,12 +96,13 @@ mod tests { } #[test] - fn names() { - assert_eq!(name(0), "nvtdijuwxlo"); - assert_eq!(name(1), "nvtdijuwxln"); - assert_eq!(name(26), "nvtdijuwxko"); - assert_eq!(name(27), "nvtdijuwxkn"); - assert_eq!(name(2099999997689999), ""); + fn names() -> Result { + assert_eq!(name(Ordinal::new(0)?), "nvtdijuwxlo"); + assert_eq!(name(Ordinal::new(1)?), "nvtdijuwxln"); + assert_eq!(name(Ordinal::new(26)?), "nvtdijuwxko"); + assert_eq!(name(Ordinal::new(27)?), "nvtdijuwxkn"); + assert_eq!(name(Ordinal::new(2099999997689999)?), ""); + Ok(()) } #[test] diff --git a/src/name.rs b/src/name.rs index 0f2da1bb43..229e8fe774 100644 --- a/src/name.rs +++ b/src/name.rs @@ -14,7 +14,7 @@ pub(crate) fn run(needle: &str) -> Result { loop { log::info!("min max guess: {} {} {}", min, max, guess); - let name = name(guess); + let name = name(Ordinal::new(guess)?); match name .len() diff --git a/src/ordinal.rs b/src/ordinal.rs new file mode 100644 index 0000000000..ea05d9033b --- /dev/null +++ b/src/ordinal.rs @@ -0,0 +1,42 @@ +use super::*; + +#[derive(Copy, Clone)] +pub(crate) struct Ordinal(u64); + +impl Ordinal { + pub(crate) fn new(inner: u64) -> Result { + if inner > 2099999997689999 { + return Err( + format!( + "{} is greater than 2099999997689999, the last ordinal", + inner + ) + .into(), + ); + } + + Ok(Self(inner)) + } + + pub(crate) fn height(self) -> u64 { + // TODO: Fix + self.0 / (50 * 100_000_000) + } + + pub(crate) fn position_in_coinbase(self) -> u64 { + // TODO: Fix + self.0 % (50 * 100_000_000) + } + + pub(crate) fn number(self) -> u64 { + self.0 + } +} + +impl FromStr for Ordinal { + type Err = Box; + + fn from_str(s: &str) -> Result { + Self::new(s.parse()?) + } +} diff --git a/src/range.rs b/src/range.rs index 88f486b3e1..2d1531acbb 100644 --- a/src/range.rs +++ b/src/range.rs @@ -12,7 +12,11 @@ pub(crate) fn run(height: u64, name_range: bool) -> Result { } if name_range { - println!("[{},{})", name(start), name(start + subsidy(height))); + println!( + "[{},{})", + name(Ordinal::new(start)?), + name(Ordinal::new(start + subsidy(height))?) + ); } else { println!("[{},{})", start, start + subsidy(height)); } diff --git a/src/traits.rs b/src/traits.rs index 23677fdba9..3e9feeb22c 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,30 +1,28 @@ use super::*; -pub(crate) fn run(ordinal: u64) -> Result { - if ordinal > 2099999997689999 { - return Err("Invalid ordinal".into()); - } +pub(crate) fn run(ordinal: Ordinal) -> Result { + let n = ordinal.number(); - if ordinal % 2 == 0 { + if n % 2 == 0 { println!("even"); } else { println!("odd"); } - let isqrt = ordinal.integer_sqrt(); - if isqrt * isqrt == ordinal { + let isqrt = n.integer_sqrt(); + if isqrt * isqrt == n { println!("square"); } - let icbrt = ordinal.integer_cbrt(); - if icbrt * icbrt * icbrt == ordinal { + let icbrt = n.integer_cbrt(); + if icbrt * icbrt * icbrt == n { println!("cube"); } - let digits = ordinal.to_string().chars().collect::>(); + let digits = n.to_string().chars().collect::>(); let pi = std::f64::consts::PI.to_string().replace('.', ""); - let s = ordinal.to_string(); + let s = n.to_string(); if s == pi[..s.len()] { println!("pi"); } @@ -44,18 +42,18 @@ pub(crate) fn run(ordinal: u64) -> Result { digits.len() ); - println!("population: {}", population(ordinal)); + println!("population: {}", population(n)); println!("name: {}", name(ordinal)); - if let Some(character) = char::from_u32((ordinal % 0x110000) as u32) { + if let Some(character) = char::from_u32((n % 0x110000) as u32) { println!("character: {:?}", character); } let mut block = 0; let mut mined = 0; loop { - if ordinal == mined { + if n == mined { println!("shiny"); } @@ -63,7 +61,7 @@ pub(crate) fn run(ordinal: u64) -> Result { mined += subsidy; - if mined > ordinal { + if mined > n { println!("block: {}", block); break; } @@ -71,7 +69,7 @@ pub(crate) fn run(ordinal: u64) -> Result { block += 1; } - if ordinal == 623624999999999 { + if n == 623624999999999 { println!("illusive"); } else if block == 124724 { println!("cursed"); diff --git a/tests/traits.rs b/tests/traits.rs index 4c423bec41..97d98d2a22 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -16,7 +16,7 @@ fn traits(ordinal: u64) -> Result> { fn invalid_ordinal() -> Result { Test::new()? .args(&["traits", "2099999997690000"]) - .expected_stderr("error: Invalid ordinal\n") + .expected_stderr("error: Invalid value for '': 2099999997690000 is greater than 2099999997689999, the last ordinal\n") .expected_status(1) .run() } From 415863ca362561d5cb146675eaab9c90dd682f8c Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 11:47:38 -0800 Subject: [PATCH 02/10] Lots of refactoring --- Cargo.lock | 35 ++++++++++ Cargo.toml | 1 + src/arguments.rs | 4 +- src/consts.rs | 90 +++++++++++++++++++++++++ src/epoch.rs | 82 +++++++++++++++++++++++ src/epochs.rs | 9 +++ src/find.rs | 2 +- src/functions.rs | 5 ++ src/height.rs | 72 ++++++++++++++++++++ src/main.rs | 97 +++------------------------ src/name.rs | 2 +- src/ordinal.rs | 152 +++++++++++++++++++++++++++++++++++++------ src/range.rs | 26 +++++--- src/supply.rs | 2 +- src/traits.rs | 35 ++++------ tests/epochs.rs | 46 +++++++++++++ tests/integration.rs | 5 +- tests/traits.rs | 26 ++++---- 18 files changed, 531 insertions(+), 160 deletions(-) create mode 100644 src/consts.rs create mode 100644 src/epoch.rs create mode 100644 src/epochs.rs create mode 100644 src/functions.rs create mode 100644 src/height.rs create mode 100644 tests/epochs.rs diff --git a/Cargo.lock b/Cargo.lock index b374dc8a45..3dacd3de07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,25 @@ dependencies = [ "vec_map", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "dirs" version = "4.0.0" @@ -253,6 +272,7 @@ name = "ord" version = "0.0.0" dependencies = [ "bitcoin", + "derive_more", "dirs", "env_logger", "executable-path", @@ -362,6 +382,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "secp256k1" version = "0.20.3" @@ -380,6 +409,12 @@ dependencies = [ "cc", ] +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 1a55507048..ab624d459e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ autotests = false [dependencies] bitcoin = "0.27.1" +derive_more = "0.99.17" dirs = "4.0.0" env_logger = "0.9.0" executable-path = "1.0.0" diff --git a/src/arguments.rs b/src/arguments.rs index 76429174c2..25862d3a33 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -2,6 +2,7 @@ use super::*; #[derive(StructOpt)] pub(crate) enum Arguments { + Epochs, Find { #[structopt(long)] blocksdir: Option, @@ -14,7 +15,7 @@ pub(crate) enum Arguments { Range { #[structopt(long)] name: bool, - height: u64, + height: Height, }, Supply, Traits { @@ -25,6 +26,7 @@ pub(crate) enum Arguments { impl Arguments { pub(crate) fn run(self) -> Result<()> { match self { + Self::Epochs => crate::epochs::run(), Self::Find { blocksdir, ordinal, diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000000..2a649c8590 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,90 @@ +use super::*; + +pub(crate) const SUPPLY: u64 = 2099999997690000; + +pub(crate) const INITIAL_SUBSIDY: u64 = 50 * COIN_VALUE; + +pub(crate) const EPOCHS: u64 = EPOCH_ORDINALS.len() as u64; + +pub(crate) const EPOCH_BLOCKS: u64 = 210000; + +pub(crate) const EPOCH_ORDINALS: &[u64] = &[ + 0, + 1050000000000000, + 1575000000000000, + 1837500000000000, + 1968750000000000, + 2034375000000000, + 2067187500000000, + 2083593750000000, + 2091796875000000, + 2095898437500000, + 2097949218750000, + 2098974609270000, + 2099487304530000, + 2099743652160000, + 2099871825870000, + 2099935912620000, + 2099967955890000, + 2099983977420000, + 2099991988080000, + 2099995993410000, + 2099997995970000, + 2099998997250000, + 2099999497890000, + 2099999748210000, + 2099999873370000, + 2099999935950000, + 2099999967240000, + 2099999982780000, + 2099999990550000, + 2099999994330000, + 2099999996220000, + 2099999997060000, + 2099999997480000, +]; + +#[cfg(test)] +mod tests { + use super::super::*; + + #[test] + fn supply() { + let mut mined = 0; + + for height in 0.. { + let subsidy = Height(height).subsidy(); + + if subsidy == 0 { + break; + } + + mined += subsidy; + } + + assert_eq!(SUPPLY, mined); + } + + #[test] + fn epochs() { + assert!(Height(EPOCHS * 210_000).subsidy() == 0); + assert!(Height(EPOCHS * 210_000 - 1).subsidy() == 1); + assert_eq!(EPOCHS, 33); + } + + #[test] + fn epoch_ordinals() { + let mut ordinal = 0; + + let mut epoch_ordinals = Vec::new(); + + for epoch in 0..33 { + epoch_ordinals.push(ordinal); + ordinal += EPOCH_BLOCKS * Height(epoch * EPOCH_BLOCKS).subsidy(); + } + + assert_eq!(EPOCH_ORDINALS, epoch_ordinals); + + assert_eq!(EPOCH_ORDINALS.len(), 33); + } +} diff --git a/src/epoch.rs b/src/epoch.rs new file mode 100644 index 0000000000..d9c3783d95 --- /dev/null +++ b/src/epoch.rs @@ -0,0 +1,82 @@ +use super::*; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct Epoch(u64); + +impl Epoch { + const STARTING_ORDINALS: &'static [Ordinal] = &[ + Ordinal::new(0), + Ordinal::new(1050000000000000), + Ordinal::new(1575000000000000), + Ordinal::new(1837500000000000), + Ordinal::new(1968750000000000), + Ordinal::new(2034375000000000), + Ordinal::new(2067187500000000), + Ordinal::new(2083593750000000), + Ordinal::new(2091796875000000), + Ordinal::new(2095898437500000), + Ordinal::new(2097949218750000), + Ordinal::new(2098974609270000), + Ordinal::new(2099487304530000), + Ordinal::new(2099743652160000), + Ordinal::new(2099871825870000), + Ordinal::new(2099935912620000), + Ordinal::new(2099967955890000), + Ordinal::new(2099983977420000), + Ordinal::new(2099991988080000), + Ordinal::new(2099995993410000), + Ordinal::new(2099997995970000), + Ordinal::new(2099998997250000), + Ordinal::new(2099999497890000), + Ordinal::new(2099999748210000), + Ordinal::new(2099999873370000), + Ordinal::new(2099999935950000), + Ordinal::new(2099999967240000), + Ordinal::new(2099999982780000), + Ordinal::new(2099999990550000), + Ordinal::new(2099999994330000), + Ordinal::new(2099999996220000), + Ordinal::new(2099999997060000), + Ordinal::new(2099999997480000), + ]; + + const LAST: Epoch = Self(Self::STARTING_ORDINALS.len() as u64 - 1); + + pub(crate) const BLOCKS: u64 = 210000; + + pub(crate) const fn new(inner: u64) -> Self { + assert!(inner <= Self::LAST.0); + Self(inner) + } + + pub(crate) fn n(self) -> u64 { + self.0 + } + + pub(crate) fn subsidy(self) -> u64 { + Height(self.0 * Self::BLOCKS).subsidy() + } + + pub(crate) fn starting_ordinal(self) -> Ordinal { + Self::STARTING_ORDINALS[self.0 as usize] + } + + pub(crate) fn starting_height(self) -> u64 { + self.0 * Self::BLOCKS + } +} + +impl PartialEq for Epoch { + fn eq(&self, other: &u64) -> bool { + self.0 == *other + } +} + +impl From for Epoch { + fn from(ordinal: Ordinal) -> Self { + match Self::STARTING_ORDINALS.binary_search(&ordinal) { + Ok(i) => Epoch(i as u64), + Err(i) => Epoch(i as u64 - 1), + } + } +} diff --git a/src/epochs.rs b/src/epochs.rs new file mode 100644 index 0000000000..f59b978754 --- /dev/null +++ b/src/epochs.rs @@ -0,0 +1,9 @@ +use super::*; + +pub(crate) fn run() -> Result { + for ordinal in EPOCH_ORDINALS { + println!("{}", ordinal); + } + + Ok(()) +} diff --git a/src/find.rs b/src/find.rs index bc731d61fe..bde248fe59 100644 --- a/src/find.rs +++ b/src/find.rs @@ -17,7 +17,7 @@ pub(crate) fn run(blocksdir: Option<&Path>, ordinal: Ordinal, at_height: u64) -> let block = index.block(height)?; - let position = ordinal.position_in_coinbase(); + let position = ordinal.position(); let mut ordinal = 0; for (i, output) in block.txdata[0].output.iter().enumerate() { diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 0000000000..abdaa3a72d --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,5 @@ +use super::*; + +pub(crate) fn mined(height: u64) -> u64 { + todo!() +} diff --git a/src/height.rs b/src/height.rs new file mode 100644 index 0000000000..dea2068067 --- /dev/null +++ b/src/height.rs @@ -0,0 +1,72 @@ +use super::*; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct Height(pub(crate) u64); + +impl Height { + pub(crate) fn n(self) -> u64 { + self.0 + } + + pub(crate) fn subsidy(self) -> u64 { + let subsidy = INITIAL_SUBSIDY; + + let halvings = self.0 / EPOCH_BLOCKS; + + if halvings < 64 { + subsidy >> halvings + } else { + 0 + } + } +} + +impl PartialEq for Height { + fn eq(&self, other: &u64) -> bool { + self.0 == *other + } +} + +impl FromStr for Height { + type Err = Box; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse::()?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn subsidy() { + assert_eq!(Height(0).subsidy(), 5000000000); + assert_eq!(Height(1).subsidy(), 5000000000); + assert_eq!(Height(210000 - 1).subsidy(), 5000000000); + assert_eq!(Height(210000).subsidy(), 2500000000); + assert_eq!(Height(210000 + 1).subsidy(), 2500000000); + } + + // #[test] + // fn mineds() { + // assert_eq!(mined(0), 0); + // assert_eq!(mined(1), 50 * COIN_VALUE); + // } + + // #[test] + // fn names() { + // assert_eq!(name(Ordinal::new(0)), "nvtdijuwxlo"); + // assert_eq!(name(Ordinal::new(1)), "nvtdijuwxln"); + // assert_eq!(name(Ordinal::new(26)), "nvtdijuwxko"); + // assert_eq!(name(Ordinal::new(27)), "nvtdijuwxkn"); + // assert_eq!(name(Ordinal::new(2099999997689999)), ""); + // } + + // #[test] + // fn populations() { + // assert_eq!(population(0), 0); + // assert_eq!(population(1), 1); + // assert_eq!(population(u64::max_value()), 64); + // } +} diff --git a/src/main.rs b/src/main.rs index 88ee70a77b..4704036de3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ use { - crate::{index::Index, ordinal::Ordinal}, + crate::{consts::*, epoch::Epoch, functions::*, height::Height, index::Index, ordinal::Ordinal}, arguments::Arguments, bitcoin::{ blockdata::constants::{genesis_block, COIN_VALUE}, consensus::Decodable, Block, Network, }, + derive_more::{Display, FromStr}, integer_cbrt::IntegerCubeRoot, integer_sqrt::IntegerSquareRoot, redb::{ @@ -15,8 +16,7 @@ use { std::{ cmp::Ordering, fs, - ops::Deref, - ops::Range, + ops::{Add, AddAssign, Deref, Range}, path::{Path, PathBuf}, process, str::FromStr, @@ -25,7 +25,12 @@ use { }; mod arguments; +mod consts; +mod epoch; +mod epochs; mod find; +mod functions; +mod height; mod index; mod name; mod ordinal; @@ -43,89 +48,3 @@ fn main() { process::exit(1); } } - -const SUPPLY: u64 = 2099999997690000; - -fn subsidy(height: u64) -> u64 { - let subsidy = 50 * COIN_VALUE; - - let halvings = height / 210000; - - if halvings < 64 { - subsidy >> halvings - } else { - 0 - } -} - -fn name(ordinal: Ordinal) -> String { - let mut x = SUPPLY - ordinal.number() - 1; - let mut name = String::new(); - while x > 0 { - name.push( - "abcdefghijklmnopqrstuvwxyz" - .chars() - .nth(((x - 1) % 26) as usize) - .unwrap(), - ); - x = (x - 1) / 26; - } - name.chars().rev().collect() -} - -fn population(mut ordinal: u64) -> u64 { - let mut population = 0; - while ordinal > 0 { - population += ordinal & 1; - ordinal >>= 1; - } - population -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn subsidies() { - assert_eq!(subsidy(0), 5000000000); - assert_eq!(subsidy(1), 5000000000); - assert_eq!(subsidy(210000 - 1), 5000000000); - assert_eq!(subsidy(210000), 2500000000); - assert_eq!(subsidy(210000 + 1), 2500000000); - } - - #[test] - fn names() -> Result { - assert_eq!(name(Ordinal::new(0)?), "nvtdijuwxlo"); - assert_eq!(name(Ordinal::new(1)?), "nvtdijuwxln"); - assert_eq!(name(Ordinal::new(26)?), "nvtdijuwxko"); - assert_eq!(name(Ordinal::new(27)?), "nvtdijuwxkn"); - assert_eq!(name(Ordinal::new(2099999997689999)?), ""); - Ok(()) - } - - #[test] - fn supply() { - let mut mined = 0; - - for height in 0.. { - let subsidy = subsidy(height); - - if subsidy == 0 { - break; - } - - mined += subsidy; - } - - assert_eq!(SUPPLY, mined); - } - - #[test] - fn populations() { - assert_eq!(population(0), 0); - assert_eq!(population(1), 1); - assert_eq!(population(u64::max_value()), 64); - } -} diff --git a/src/name.rs b/src/name.rs index 229e8fe774..686f754199 100644 --- a/src/name.rs +++ b/src/name.rs @@ -14,7 +14,7 @@ pub(crate) fn run(needle: &str) -> Result { loop { log::info!("min max guess: {} {} {}", min, max, guess); - let name = name(Ordinal::new(guess)?); + let name = Ordinal::new(guess).name(); match name .len() diff --git a/src/ordinal.rs b/src/ordinal.rs index ea05d9033b..e5da8b19b8 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -1,42 +1,156 @@ use super::*; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Ord, PartialOrd)] pub(crate) struct Ordinal(u64); impl Ordinal { - pub(crate) fn new(inner: u64) -> Result { - if inner > 2099999997689999 { - return Err( - format!( - "{} is greater than 2099999997689999, the last ordinal", - inner - ) - .into(), - ); - } + pub(crate) const LAST: Ordinal = Ordinal::new(SUPPLY - 1); - Ok(Self(inner)) + pub(crate) const fn new(inner: u64) -> Self { + assert!(inner < SUPPLY); + Self(inner) + } + + pub(crate) fn new_checked(inner: u64) -> Option { + if inner < SUPPLY { + Some(Self(inner)) + } else { + None + } } pub(crate) fn height(self) -> u64 { - // TODO: Fix - self.0 / (50 * 100_000_000) + let epoch = self.epoch(); + + epoch.starting_height() + (self.0 - epoch.starting_ordinal().0) / epoch.subsidy() + } + + pub(crate) fn epoch(self) -> Epoch { + self.into() + } + + pub(crate) fn position(self) -> u64 { + let epoch = self.epoch(); + (self.0 - epoch.starting_ordinal().0) % epoch.subsidy() } - pub(crate) fn position_in_coinbase(self) -> u64 { - // TODO: Fix - self.0 % (50 * 100_000_000) + pub(crate) fn name(self) -> String { + let mut x = SUPPLY - self.0 - 1; + let mut name = String::new(); + while x > 0 { + name.push( + "abcdefghijklmnopqrstuvwxyz" + .chars() + .nth(((x - 1) % 26) as usize) + .unwrap(), + ); + x = (x - 1) / 26; + } + name.chars().rev().collect() } - pub(crate) fn number(self) -> u64 { + pub(crate) fn n(self) -> u64 { self.0 } + + pub(crate) fn population(self) -> u64 { + let mut n = self.0; + let mut population = 0; + while n > 0 { + population += n & 1; + n >>= 1; + } + population + } +} + +impl PartialEq for Ordinal { + fn eq(&self, other: &u64) -> bool { + self.0 == *other + } +} + +impl Add for Ordinal { + type Output = Self; + + fn add(self, other: u64) -> Ordinal { + Ordinal::new(self.0 + other) + } +} + +impl AddAssign for Ordinal { + fn add_assign(&mut self, other: u64) { + *self = Ordinal::new(self.0 + other); + } } impl FromStr for Ordinal { type Err = Box; fn from_str(s: &str) -> Result { - Self::new(s.parse()?) + let inner = s.parse()?; + + if inner >= SUPPLY { + return Err(format!("{} is not a valid ordinal", inner).into()); + } + + Ok(Self::new(inner)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn height() { + assert_eq!(Ordinal::new(0).height(), 0); + assert_eq!(Ordinal::new(1).height(), 0); + assert_eq!(Ordinal::new(Epoch::new(0).subsidy()).height(), 1); + assert_eq!(Ordinal::new(Epoch::new(0).subsidy() * 2).height(), 2); + assert_eq!(Epoch::new(2).starting_ordinal().height(), Epoch::BLOCKS * 2); + } + + #[test] + fn name() { + assert_eq!(Ordinal::new(0).name(), "nvtdijuwxlo"); + assert_eq!(Ordinal::new(1).name(), "nvtdijuwxln"); + assert_eq!(Ordinal::new(26).name(), "nvtdijuwxko"); + assert_eq!(Ordinal::new(27).name(), "nvtdijuwxkn"); + assert_eq!(Ordinal::new(2099999997689999).name(), ""); + } + + #[test] + fn population() { + assert_eq!(Ordinal::new(0).population(), 0); + assert_eq!(Ordinal::new(1).population(), 1); + assert_eq!( + Ordinal::new(0b11111111111111111111111111111111111111111111111111).population(), + 50 + ); + } + + #[test] + fn epoch() { + assert_eq!(Ordinal::new(0).epoch(), 0); + assert_eq!(Ordinal::new(1).epoch(), 0); + assert_eq!(Ordinal::new(1050000000000000).epoch(), 1); + } + + #[test] + fn position() { + assert_eq!(Ordinal::new(0).position(), 0); + assert_eq!(Ordinal::new(1).position(), 1); + assert_eq!( + Ordinal::new(INITIAL_SUBSIDY - 1).position(), + INITIAL_SUBSIDY - 1 + ); + assert_eq!(Ordinal::new(INITIAL_SUBSIDY).position(), 0); + assert_eq!(Ordinal::new(INITIAL_SUBSIDY + 1).position(), 1); + assert_eq!( + Ordinal::new(Epoch::new(1).starting_ordinal().n() + Epoch::new(1).subsidy()).position(), + 0 + ); + assert_eq!(Ordinal::LAST.position(), 0); } } diff --git a/src/range.rs b/src/range.rs index 2d1531acbb..af35849c06 100644 --- a/src/range.rs +++ b/src/range.rs @@ -1,24 +1,30 @@ use super::*; -pub(crate) fn run(height: u64, name_range: bool) -> Result { +pub(crate) fn run(height: Height, name_range: bool) -> Result { let mut start = 0; - for i in 0..height { - if subsidy(i) == 0 { + for n in 0..height.n() { + let subsidy = Height(n).subsidy(); + + if subsidy == 0 { break; } - start += subsidy(i); + start += subsidy; } + let end = start + height.subsidy(); + if name_range { - println!( - "[{},{})", - name(Ordinal::new(start)?), - name(Ordinal::new(start + subsidy(height))?) - ); + let (start, end) = match (Ordinal::new_checked(start), Ordinal::new_checked(end)) { + (Some(start), Some(end)) => (start.name(), end.name()), + (Some(start), None) => (start.name(), start.name()), + (None, None) => (Ordinal::LAST.name(), Ordinal::LAST.name()), + (None, Some(_)) => unreachable!(), + }; + println!("[{},{})", start, end); } else { - println!("[{},{})", start, start + subsidy(height)); + println!("[{},{})", start, end); } Ok(()) diff --git a/src/supply.rs b/src/supply.rs index 96deca2bb9..c3415d1ce8 100644 --- a/src/supply.rs +++ b/src/supply.rs @@ -4,7 +4,7 @@ pub(crate) fn run() -> Result { let mut last = 0; loop { - if subsidy(last + 1) == 0 { + if Height(last + 1).subsidy() == 0 { break; } last += 1; diff --git a/src/traits.rs b/src/traits.rs index 3e9feeb22c..7c2beb5db8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,7 +1,7 @@ use super::*; pub(crate) fn run(ordinal: Ordinal) -> Result { - let n = ordinal.number(); + let n = ordinal.n(); if n % 2 == 0 { println!("even"); @@ -42,37 +42,26 @@ pub(crate) fn run(ordinal: Ordinal) -> Result { digits.len() ); - println!("population: {}", population(n)); + println!("population: {}", ordinal.population()); - println!("name: {}", name(ordinal)); + println!("name: {}", ordinal.name()); if let Some(character) = char::from_u32((n % 0x110000) as u32) { println!("character: {:?}", character); } - let mut block = 0; - let mut mined = 0; - loop { - if n == mined { - println!("shiny"); - } - - let subsidy = subsidy(block); - - mined += subsidy; + println!("height: {}", ordinal.height()); - if mined > n { - println!("block: {}", block); - break; - } - - block += 1; + if ordinal.position() == 0 { + println!("shiny"); } - if n == 623624999999999 { - println!("illusive"); - } else if block == 124724 { - println!("cursed"); + if ordinal.height() == 124724 { + if ordinal == 623624999999999 { + println!("illusive"); + } else { + println!("cursed"); + } } Ok(()) diff --git a/tests/epochs.rs b/tests/epochs.rs new file mode 100644 index 0000000000..28f72dd0bd --- /dev/null +++ b/tests/epochs.rs @@ -0,0 +1,46 @@ +use super::*; + +#[test] +fn empty() -> Result { + Test::new()? + .args(&["epochs"]) + .expected_stdout( + " + 0 + 1050000000000000 + 1575000000000000 + 1837500000000000 + 1968750000000000 + 2034375000000000 + 2067187500000000 + 2083593750000000 + 2091796875000000 + 2095898437500000 + 2097949218750000 + 2098974609270000 + 2099487304530000 + 2099743652160000 + 2099871825870000 + 2099935912620000 + 2099967955890000 + 2099983977420000 + 2099991988080000 + 2099995993410000 + 2099997995970000 + 2099998997250000 + 2099999497890000 + 2099999748210000 + 2099999873370000 + 2099999935950000 + 2099999967240000 + 2099999982780000 + 2099999990550000 + 2099999994330000 + 2099999996220000 + 2099999997060000 + 2099999997480000 + " + .unindent(), + ) + .run() +} diff --git a/tests/integration.rs b/tests/integration.rs index 9e553ee6ce..6d620e4610 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -19,6 +19,7 @@ use { unindent::Unindent, }; +mod epochs; mod find; mod name; mod range; @@ -59,9 +60,9 @@ impl Test { } } - fn expected_stdout(self, expected_stdout: &str) -> Self { + fn expected_stdout(self, expected_stdout: impl AsRef) -> Self { Self { - expected_stdout: expected_stdout.to_owned(), + expected_stdout: expected_stdout.as_ref().to_owned(), ..self } } diff --git a/tests/traits.rs b/tests/traits.rs index 97d98d2a22..bb93821ce8 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -12,14 +12,14 @@ fn traits(ordinal: u64) -> Result> { ) } -#[test] -fn invalid_ordinal() -> Result { - Test::new()? - .args(&["traits", "2099999997690000"]) - .expected_stderr("error: Invalid value for '': 2099999997690000 is greater than 2099999997689999, the last ordinal\n") - .expected_status(1) - .run() -} +// #[test] +// fn invalid_ordinal() -> Result { +// Test::new()? +// .args(&["traits", "2099999997690000"]) +// .expected_stderr("error: Invalid value for '': 2099999997690000 is greater than 2099999997689999, the last ordinal\n") +// .expected_status(1) +// .run() +// } #[test] fn even() -> Result { @@ -83,11 +83,11 @@ fn name() -> Result { #[test] fn block() -> Result { - assert!(traits(0)?.contains("block: 0")); - assert!(traits(1)?.contains("block: 0")); - assert!(traits(50 * 100_000_000)?.contains("block: 1")); - assert!(traits(2099999997689999)?.contains("block: 6929999")); - assert!(traits(2099999997689998)?.contains("block: 6929998")); + assert!(traits(0)?.contains("height: 0")); + assert!(traits(1)?.contains("height: 0")); + assert!(traits(50 * 100_000_000)?.contains("height: 1")); + assert!(traits(2099999997689999)?.contains("height: 6929999")); + assert!(traits(2099999997689998)?.contains("height: 6929998")); Ok(()) } From aae04d98524af34cea0ec31d6af53e16e3f81712 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 12:10:33 -0800 Subject: [PATCH 03/10] More refactoring --- src/{arguments.rs => command.rs} | 22 +++++++---- src/{ => command}/epochs.rs | 2 +- src/{ => command}/find.rs | 2 +- src/{ => command}/range.rs | 0 src/{ => command}/supply.rs | 0 src/{ => command}/traits.rs | 0 src/consts.rs | 63 -------------------------------- src/epoch.rs | 59 ++++++++++++++++++++++++++++-- src/functions.rs | 5 --- src/height.rs | 20 +++++++++- src/main.rs | 19 ++++------ src/ordinal.rs | 2 +- 12 files changed, 98 insertions(+), 96 deletions(-) rename src/{arguments.rs => command.rs} (53%) rename src/{ => command}/epochs.rs (67%) rename src/{ => command}/find.rs (95%) rename src/{ => command}/range.rs (100%) rename src/{ => command}/supply.rs (100%) rename src/{ => command}/traits.rs (100%) delete mode 100644 src/functions.rs diff --git a/src/arguments.rs b/src/command.rs similarity index 53% rename from src/arguments.rs rename to src/command.rs index 25862d3a33..311b0f65fd 100644 --- a/src/arguments.rs +++ b/src/command.rs @@ -1,7 +1,13 @@ use super::*; +mod epochs; +mod find; +mod range; +mod supply; +mod traits; + #[derive(StructOpt)] -pub(crate) enum Arguments { +pub(crate) enum Command { Epochs, Find { #[structopt(long)] @@ -23,19 +29,19 @@ pub(crate) enum Arguments { }, } -impl Arguments { +impl Command { pub(crate) fn run(self) -> Result<()> { match self { - Self::Epochs => crate::epochs::run(), + Self::Epochs => epochs::run(), Self::Find { blocksdir, ordinal, height, - } => crate::find::run(blocksdir.as_deref(), ordinal, height), - Self::Name { name } => crate::name::run(&name), - Self::Range { height, name } => crate::range::run(height, name), - Self::Supply => crate::supply::run(), - Self::Traits { ordinal } => crate::traits::run(ordinal), + } => find::run(blocksdir.as_deref(), ordinal, height), + Self::Name { name } => name::run(&name), + Self::Range { height, name } => range::run(height, name), + Self::Supply => supply::run(), + Self::Traits { ordinal } => traits::run(ordinal), } } } diff --git a/src/epochs.rs b/src/command/epochs.rs similarity index 67% rename from src/epochs.rs rename to src/command/epochs.rs index f59b978754..8df0571863 100644 --- a/src/epochs.rs +++ b/src/command/epochs.rs @@ -1,7 +1,7 @@ use super::*; pub(crate) fn run() -> Result { - for ordinal in EPOCH_ORDINALS { + for ordinal in Epoch::STARTING_ORDINALS { println!("{}", ordinal); } diff --git a/src/find.rs b/src/command/find.rs similarity index 95% rename from src/find.rs rename to src/command/find.rs index bde248fe59..12a2f0bab1 100644 --- a/src/find.rs +++ b/src/command/find.rs @@ -11,7 +11,7 @@ use super::*; pub(crate) fn run(blocksdir: Option<&Path>, ordinal: Ordinal, at_height: u64) -> Result<()> { let index = Index::new(blocksdir)?; - let height = ordinal.height(); + let height = ordinal.height().n(); assert!(height < 100); assert!(at_height == height); diff --git a/src/range.rs b/src/command/range.rs similarity index 100% rename from src/range.rs rename to src/command/range.rs diff --git a/src/supply.rs b/src/command/supply.rs similarity index 100% rename from src/supply.rs rename to src/command/supply.rs diff --git a/src/traits.rs b/src/command/traits.rs similarity index 100% rename from src/traits.rs rename to src/command/traits.rs diff --git a/src/consts.rs b/src/consts.rs index 2a649c8590..1faf58c7a9 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,46 +4,6 @@ pub(crate) const SUPPLY: u64 = 2099999997690000; pub(crate) const INITIAL_SUBSIDY: u64 = 50 * COIN_VALUE; -pub(crate) const EPOCHS: u64 = EPOCH_ORDINALS.len() as u64; - -pub(crate) const EPOCH_BLOCKS: u64 = 210000; - -pub(crate) const EPOCH_ORDINALS: &[u64] = &[ - 0, - 1050000000000000, - 1575000000000000, - 1837500000000000, - 1968750000000000, - 2034375000000000, - 2067187500000000, - 2083593750000000, - 2091796875000000, - 2095898437500000, - 2097949218750000, - 2098974609270000, - 2099487304530000, - 2099743652160000, - 2099871825870000, - 2099935912620000, - 2099967955890000, - 2099983977420000, - 2099991988080000, - 2099995993410000, - 2099997995970000, - 2099998997250000, - 2099999497890000, - 2099999748210000, - 2099999873370000, - 2099999935950000, - 2099999967240000, - 2099999982780000, - 2099999990550000, - 2099999994330000, - 2099999996220000, - 2099999997060000, - 2099999997480000, -]; - #[cfg(test)] mod tests { use super::super::*; @@ -64,27 +24,4 @@ mod tests { assert_eq!(SUPPLY, mined); } - - #[test] - fn epochs() { - assert!(Height(EPOCHS * 210_000).subsidy() == 0); - assert!(Height(EPOCHS * 210_000 - 1).subsidy() == 1); - assert_eq!(EPOCHS, 33); - } - - #[test] - fn epoch_ordinals() { - let mut ordinal = 0; - - let mut epoch_ordinals = Vec::new(); - - for epoch in 0..33 { - epoch_ordinals.push(ordinal); - ordinal += EPOCH_BLOCKS * Height(epoch * EPOCH_BLOCKS).subsidy(); - } - - assert_eq!(EPOCH_ORDINALS, epoch_ordinals); - - assert_eq!(EPOCH_ORDINALS.len(), 33); - } } diff --git a/src/epoch.rs b/src/epoch.rs index d9c3783d95..903d087318 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -4,7 +4,7 @@ use super::*; pub(crate) struct Epoch(u64); impl Epoch { - const STARTING_ORDINALS: &'static [Ordinal] = &[ + pub(crate) const STARTING_ORDINALS: &'static [Ordinal] = &[ Ordinal::new(0), Ordinal::new(1050000000000000), Ordinal::new(1575000000000000), @@ -61,8 +61,8 @@ impl Epoch { Self::STARTING_ORDINALS[self.0 as usize] } - pub(crate) fn starting_height(self) -> u64 { - self.0 * Self::BLOCKS + pub(crate) fn starting_height(self) -> Height { + Height(self.0 * Self::BLOCKS) } } @@ -80,3 +80,56 @@ impl From for Epoch { } } } + +#[cfg(test)] +mod tests { + use super::super::*; + + #[test] + fn supply() { + let mut mined = 0; + + for height in 0.. { + let subsidy = Height(height).subsidy(); + + if subsidy == 0 { + break; + } + + mined += subsidy; + } + + assert_eq!(SUPPLY, mined); + } + + #[test] + fn starting_ordinals() { + let mut ordinal = 0; + + let mut epoch_ordinals = Vec::new(); + + for epoch in 0..33 { + epoch_ordinals.push(ordinal); + ordinal += Epoch::BLOCKS * Epoch::new(epoch).subsidy(); + } + + assert_eq!(Epoch::STARTING_ORDINALS, epoch_ordinals); + assert_eq!(Epoch::STARTING_ORDINALS.len(), 33); + } + + #[test] + fn last() { + assert_eq!( + (Epoch::LAST.starting_height() + Epoch::BLOCKS - 1).subsidy(), + 1 + ); + assert_eq!((Epoch::LAST.starting_height() + Epoch::BLOCKS).subsidy(), 0); + } + + // #[test] + // fn epochs() { + // assert!(Height(EPOCHS * 210_000).subsidy() == 0); + // assert!(Height(EPOCHS * 210_000 - 1).subsidy() == 1); + // assert_eq!(EPOCHS, 33); + // } +} diff --git a/src/functions.rs b/src/functions.rs deleted file mode 100644 index abdaa3a72d..0000000000 --- a/src/functions.rs +++ /dev/null @@ -1,5 +0,0 @@ -use super::*; - -pub(crate) fn mined(height: u64) -> u64 { - todo!() -} diff --git a/src/height.rs b/src/height.rs index dea2068067..d3a3ef5329 100644 --- a/src/height.rs +++ b/src/height.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Display)] pub(crate) struct Height(pub(crate) u64); impl Height { @@ -11,7 +11,7 @@ impl Height { pub(crate) fn subsidy(self) -> u64 { let subsidy = INITIAL_SUBSIDY; - let halvings = self.0 / EPOCH_BLOCKS; + let halvings = self.0 / Epoch::BLOCKS; if halvings < 64 { subsidy >> halvings @@ -21,6 +21,22 @@ impl Height { } } +impl Add for Height { + type Output = Self; + + fn add(self, other: u64) -> Height { + Self(self.0 + other) + } +} + +impl Sub for Height { + type Output = Self; + + fn sub(self, other: u64) -> Height { + Self(self.0 - other) + } +} + impl PartialEq for Height { fn eq(&self, other: &u64) -> bool { self.0 == *other diff --git a/src/main.rs b/src/main.rs index 4704036de3..eb71349f6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use { - crate::{consts::*, epoch::Epoch, functions::*, height::Height, index::Index, ordinal::Ordinal}, - arguments::Arguments, + crate::{consts::*, epoch::Epoch, height::Height, index::Index, ordinal::Ordinal}, bitcoin::{ blockdata::constants::{genesis_block, COIN_VALUE}, consensus::Decodable, Block, Network, }, - derive_more::{Display, FromStr}, + command::Command, + derive_more::{Add, Display, FromStr}, integer_cbrt::IntegerCubeRoot, integer_sqrt::IntegerSquareRoot, redb::{ @@ -16,7 +16,7 @@ use { std::{ cmp::Ordering, fs, - ops::{Add, AddAssign, Deref, Range}, + ops::{Add, AddAssign, Deref, Range, Sub}, path::{Path, PathBuf}, process, str::FromStr, @@ -24,26 +24,21 @@ use { structopt::StructOpt, }; -mod arguments; mod consts; mod epoch; -mod epochs; -mod find; -mod functions; mod height; mod index; mod name; mod ordinal; -mod range; -mod supply; -mod traits; + +mod command; type Result> = std::result::Result; fn main() { env_logger::init(); - if let Err(error) = Arguments::from_args().run() { + if let Err(error) = Command::from_args().run() { eprintln!("error: {}", error); process::exit(1); } diff --git a/src/ordinal.rs b/src/ordinal.rs index e5da8b19b8..13db2a146a 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -19,7 +19,7 @@ impl Ordinal { } } - pub(crate) fn height(self) -> u64 { + pub(crate) fn height(self) -> Height { let epoch = self.epoch(); epoch.starting_height() + (self.0 - epoch.starting_ordinal().0) / epoch.subsidy() From cb2e66be0babcc2d92b5650f3739956c24d8ba79 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 12:29:35 -0800 Subject: [PATCH 04/10] More stuff --- justfile | 4 ++- src/command/find.rs | 2 +- src/command/supply.rs | 4 +-- src/command/traits.rs | 2 +- src/consts.rs | 27 ----------------- src/epoch.rs | 38 ++++------------------- src/height.rs | 4 +-- src/main.rs | 11 +++---- src/ordinal.rs | 70 +++++++++++++++++++++++++++++-------------- tests/traits.rs | 18 ++++++----- 10 files changed, 75 insertions(+), 105 deletions(-) delete mode 100644 src/consts.rs diff --git a/justfile b/justfile index 61b7ccf979..7e50648461 100644 --- a/justfile +++ b/justfile @@ -2,11 +2,13 @@ log := '0' export RUST_LOG := log -ci: clippy +ci: clippy forbid cargo fmt -- --check cargo test --release cargo test +forbid: + ./bin/forbid clippy: cargo clippy diff --git a/src/command/find.rs b/src/command/find.rs index 12a2f0bab1..4eedce24e0 100644 --- a/src/command/find.rs +++ b/src/command/find.rs @@ -17,7 +17,7 @@ pub(crate) fn run(blocksdir: Option<&Path>, ordinal: Ordinal, at_height: u64) -> let block = index.block(height)?; - let position = ordinal.position(); + let position = ordinal.subsidy_position(); let mut ordinal = 0; for (i, output) in block.txdata[0].output.iter().enumerate() { diff --git a/src/command/supply.rs b/src/command/supply.rs index c3415d1ce8..a988b97c0d 100644 --- a/src/command/supply.rs +++ b/src/command/supply.rs @@ -10,9 +10,9 @@ pub(crate) fn run() -> Result { last += 1; } - println!("supply: {}", SUPPLY); + println!("supply: {}", Ordinal::SUPPLY); println!("first: {}", 0); - println!("last: {}", SUPPLY - 1); + println!("last: {}", Ordinal::SUPPLY - 1); println!("last mined in block: {}", last); Ok(()) diff --git a/src/command/traits.rs b/src/command/traits.rs index 7c2beb5db8..579462118e 100644 --- a/src/command/traits.rs +++ b/src/command/traits.rs @@ -52,7 +52,7 @@ pub(crate) fn run(ordinal: Ordinal) -> Result { println!("height: {}", ordinal.height()); - if ordinal.position() == 0 { + if ordinal.subsidy_position() == 0 { println!("shiny"); } diff --git a/src/consts.rs b/src/consts.rs deleted file mode 100644 index 1faf58c7a9..0000000000 --- a/src/consts.rs +++ /dev/null @@ -1,27 +0,0 @@ -use super::*; - -pub(crate) const SUPPLY: u64 = 2099999997690000; - -pub(crate) const INITIAL_SUBSIDY: u64 = 50 * COIN_VALUE; - -#[cfg(test)] -mod tests { - use super::super::*; - - #[test] - fn supply() { - let mut mined = 0; - - for height in 0.. { - let subsidy = Height(height).subsidy(); - - if subsidy == 0 { - break; - } - - mined += subsidy; - } - - assert_eq!(SUPPLY, mined); - } -} diff --git a/src/epoch.rs b/src/epoch.rs index 903d087318..9077f7431b 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -1,7 +1,7 @@ use super::*; #[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) struct Epoch(u64); +pub(crate) struct Epoch(pub(crate) u64); impl Epoch { pub(crate) const STARTING_ORDINALS: &'static [Ordinal] = &[ @@ -40,25 +40,14 @@ impl Epoch { Ordinal::new(2099999997480000), ]; - const LAST: Epoch = Self(Self::STARTING_ORDINALS.len() as u64 - 1); - pub(crate) const BLOCKS: u64 = 210000; - pub(crate) const fn new(inner: u64) -> Self { - assert!(inner <= Self::LAST.0); - Self(inner) - } - - pub(crate) fn n(self) -> u64 { - self.0 - } - pub(crate) fn subsidy(self) -> u64 { Height(self.0 * Self::BLOCKS).subsidy() } - pub(crate) fn starting_ordinal(self) -> Ordinal { - Self::STARTING_ORDINALS[self.0 as usize] + pub(crate) fn starting_ordinal(self) -> Option { + Self::STARTING_ORDINALS.get(self.0 as usize).cloned() } pub(crate) fn starting_height(self) -> Height { @@ -99,7 +88,7 @@ mod tests { mined += subsidy; } - assert_eq!(SUPPLY, mined); + assert_eq!(Ordinal::SUPPLY, mined); } #[test] @@ -110,26 +99,11 @@ mod tests { for epoch in 0..33 { epoch_ordinals.push(ordinal); - ordinal += Epoch::BLOCKS * Epoch::new(epoch).subsidy(); + ordinal += Epoch::BLOCKS * Epoch(epoch).subsidy(); } assert_eq!(Epoch::STARTING_ORDINALS, epoch_ordinals); assert_eq!(Epoch::STARTING_ORDINALS.len(), 33); + assert_eq!(Epoch(33).subsidy(), 0); } - - #[test] - fn last() { - assert_eq!( - (Epoch::LAST.starting_height() + Epoch::BLOCKS - 1).subsidy(), - 1 - ); - assert_eq!((Epoch::LAST.starting_height() + Epoch::BLOCKS).subsidy(), 0); - } - - // #[test] - // fn epochs() { - // assert!(Height(EPOCHS * 210_000).subsidy() == 0); - // assert!(Height(EPOCHS * 210_000 - 1).subsidy() == 1); - // assert_eq!(EPOCHS, 33); - // } } diff --git a/src/height.rs b/src/height.rs index d3a3ef5329..71633e45fc 100644 --- a/src/height.rs +++ b/src/height.rs @@ -9,12 +9,10 @@ impl Height { } pub(crate) fn subsidy(self) -> u64 { - let subsidy = INITIAL_SUBSIDY; - let halvings = self.0 / Epoch::BLOCKS; if halvings < 64 { - subsidy >> halvings + 50 * COIN_VALUE >> halvings } else { 0 } diff --git a/src/main.rs b/src/main.rs index eb71349f6d..4517f488f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ use { - crate::{consts::*, epoch::Epoch, height::Height, index::Index, ordinal::Ordinal}, + crate::{epoch::Epoch, height::Height, index::Index, ordinal::Ordinal}, bitcoin::{ blockdata::constants::{genesis_block, COIN_VALUE}, consensus::Decodable, Block, Network, }, - command::Command, - derive_more::{Add, Display, FromStr}, + derive_more::Display, integer_cbrt::IntegerCubeRoot, integer_sqrt::IntegerSquareRoot, redb::{ @@ -24,21 +23,19 @@ use { structopt::StructOpt, }; -mod consts; +mod command; mod epoch; mod height; mod index; mod name; mod ordinal; -mod command; - type Result> = std::result::Result; fn main() { env_logger::init(); - if let Err(error) = Command::from_args().run() { + if let Err(error) = command::Command::from_args().run() { eprintln!("error: {}", error); process::exit(1); } diff --git a/src/ordinal.rs b/src/ordinal.rs index 13db2a146a..7c853f4253 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -4,15 +4,17 @@ use super::*; pub(crate) struct Ordinal(u64); impl Ordinal { - pub(crate) const LAST: Ordinal = Ordinal::new(SUPPLY - 1); + pub(crate) const LAST: Ordinal = Self::new(Self::SUPPLY - 1); + + pub(crate) const SUPPLY: u64 = 2099999997690000; pub(crate) const fn new(inner: u64) -> Self { - assert!(inner < SUPPLY); + assert!(inner < Self::SUPPLY); Self(inner) } pub(crate) fn new_checked(inner: u64) -> Option { - if inner < SUPPLY { + if inner < Self::SUPPLY { Some(Self(inner)) } else { None @@ -20,22 +22,23 @@ impl Ordinal { } pub(crate) fn height(self) -> Height { - let epoch = self.epoch(); - - epoch.starting_height() + (self.0 - epoch.starting_ordinal().0) / epoch.subsidy() + self.epoch().starting_height() + self.epoch_position() / self.epoch().subsidy() } pub(crate) fn epoch(self) -> Epoch { self.into() } - pub(crate) fn position(self) -> u64 { - let epoch = self.epoch(); - (self.0 - epoch.starting_ordinal().0) % epoch.subsidy() + pub(crate) fn subsidy_position(self) -> u64 { + self.epoch_position() % self.epoch().subsidy() + } + + pub(crate) fn epoch_position(self) -> u64 { + self.0 - self.epoch().starting_ordinal().unwrap().0 } pub(crate) fn name(self) -> String { - let mut x = SUPPLY - self.0 - 1; + let mut x = Self::SUPPLY - self.0 - 1; let mut name = String::new(); while x > 0 { name.push( @@ -90,7 +93,7 @@ impl FromStr for Ordinal { fn from_str(s: &str) -> Result { let inner = s.parse()?; - if inner >= SUPPLY { + if inner >= Self::SUPPLY { return Err(format!("{} is not a valid ordinal", inner).into()); } @@ -106,9 +109,12 @@ mod tests { fn height() { assert_eq!(Ordinal::new(0).height(), 0); assert_eq!(Ordinal::new(1).height(), 0); - assert_eq!(Ordinal::new(Epoch::new(0).subsidy()).height(), 1); - assert_eq!(Ordinal::new(Epoch::new(0).subsidy() * 2).height(), 2); - assert_eq!(Epoch::new(2).starting_ordinal().height(), Epoch::BLOCKS * 2); + assert_eq!(Ordinal::new(Epoch(0).subsidy()).height(), 1); + assert_eq!(Ordinal::new(Epoch(0).subsidy() * 2).height(), 2); + assert_eq!( + Epoch(2).starting_ordinal().unwrap().height(), + Epoch::BLOCKS * 2 + ); } #[test] @@ -138,19 +144,37 @@ mod tests { } #[test] - fn position() { - assert_eq!(Ordinal::new(0).position(), 0); - assert_eq!(Ordinal::new(1).position(), 1); + fn block_position() { + assert_eq!(Ordinal::new(0).subsidy_position(), 0); + assert_eq!(Ordinal::new(1).subsidy_position(), 1); assert_eq!( - Ordinal::new(INITIAL_SUBSIDY - 1).position(), - INITIAL_SUBSIDY - 1 + Ordinal::new(Height(0).subsidy() - 1).subsidy_position(), + Height(0).subsidy() - 1 ); - assert_eq!(Ordinal::new(INITIAL_SUBSIDY).position(), 0); - assert_eq!(Ordinal::new(INITIAL_SUBSIDY + 1).position(), 1); + assert_eq!(Ordinal::new(Height(0).subsidy()).subsidy_position(), 0); + assert_eq!(Ordinal::new(Height(0).subsidy() + 1).subsidy_position(), 1); assert_eq!( - Ordinal::new(Epoch::new(1).starting_ordinal().n() + Epoch::new(1).subsidy()).position(), + Ordinal::new(Epoch(1).starting_ordinal().unwrap().n() + Epoch(1).subsidy()) + .subsidy_position(), 0 ); - assert_eq!(Ordinal::LAST.position(), 0); + assert_eq!(Ordinal::LAST.subsidy_position(), 0); + } + + #[test] + fn supply() { + let mut mined = 0; + + for height in 0.. { + let subsidy = Height(height).subsidy(); + + if subsidy == 0 { + break; + } + + mined += subsidy; + } + + assert_eq!(Ordinal::SUPPLY, mined); } } diff --git a/tests/traits.rs b/tests/traits.rs index bb93821ce8..795ac3c4cf 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -12,14 +12,16 @@ fn traits(ordinal: u64) -> Result> { ) } -// #[test] -// fn invalid_ordinal() -> Result { -// Test::new()? -// .args(&["traits", "2099999997690000"]) -// .expected_stderr("error: Invalid value for '': 2099999997690000 is greater than 2099999997689999, the last ordinal\n") -// .expected_status(1) -// .run() -// } +#[test] +fn invalid_ordinal() -> Result { + Test::new()? + .args(&["traits", "2099999997690000"]) + .expected_stderr( + "error: Invalid value for '': 2099999997690000 is not a valid ordinal\n", + ) + .expected_status(1) + .run() +} #[test] fn even() -> Result { From de8d3640914cbe4303f647db600f26604217167b Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 12:36:06 -0800 Subject: [PATCH 05/10] Fmt and clippy --- justfile | 3 +++ src/height.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index 7e50648461..b78be81a99 100644 --- a/justfile +++ b/justfile @@ -10,6 +10,9 @@ ci: clippy forbid forbid: ./bin/forbid +fmt: + cargo fmt + clippy: cargo clippy diff --git a/src/height.rs b/src/height.rs index 71633e45fc..c3a46c255f 100644 --- a/src/height.rs +++ b/src/height.rs @@ -12,7 +12,7 @@ impl Height { let halvings = self.0 / Epoch::BLOCKS; if halvings < 64 { - 50 * COIN_VALUE >> halvings + (50 * COIN_VALUE) >> halvings } else { 0 } From 9628fb218066098956fc3527a18cd5b2e52dca7f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 14:12:05 -0800 Subject: [PATCH 06/10] Remove comment --- src/command/find.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/command/find.rs b/src/command/find.rs index 4eedce24e0..9190f432d8 100644 --- a/src/command/find.rs +++ b/src/command/find.rs @@ -1,13 +1,5 @@ use super::*; -// find: -// - find ordinal N -// - find transaction in which is was mined -// - scan blocks forward to see if that transaction was spent -// - track position in transactions -// - finally get to final transaction -// - print that transaction - pub(crate) fn run(blocksdir: Option<&Path>, ordinal: Ordinal, at_height: u64) -> Result<()> { let index = Index::new(blocksdir)?; From 6269931cd8d1bc94c5df6ada5b167e97f83e76e3 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 15:12:27 -0800 Subject: [PATCH 07/10] Clean up and test height --- src/epoch.rs | 20 ++++++++++++++++- src/height.rs | 60 ++++++++++++++++++++++++++------------------------ src/ordinal.rs | 8 +++---- 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/epoch.rs b/src/epoch.rs index 9077f7431b..b45d23af4e 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -43,7 +43,11 @@ impl Epoch { pub(crate) const BLOCKS: u64 = 210000; pub(crate) fn subsidy(self) -> u64 { - Height(self.0 * Self::BLOCKS).subsidy() + if self.0 < 64 { + (50 * COIN_VALUE) >> self.0 + } else { + 0 + } } pub(crate) fn starting_ordinal(self) -> Option { @@ -70,6 +74,12 @@ impl From for Epoch { } } +impl From for Epoch { + fn from(height: Height) -> Self { + Self(height.0 / Self::BLOCKS) + } +} + #[cfg(test)] mod tests { use super::super::*; @@ -106,4 +116,12 @@ mod tests { assert_eq!(Epoch::STARTING_ORDINALS.len(), 33); assert_eq!(Epoch(33).subsidy(), 0); } + + #[test] + fn subsidy() { + assert_eq!(Epoch(0).subsidy(), 5000000000); + assert_eq!(Epoch(1).subsidy(), 2500000000); + assert_eq!(Epoch(32).subsidy(), 1); + assert_eq!(Epoch(33).subsidy(), 0); + } } diff --git a/src/height.rs b/src/height.rs index c3a46c255f..8ad841f804 100644 --- a/src/height.rs +++ b/src/height.rs @@ -9,13 +9,7 @@ impl Height { } pub(crate) fn subsidy(self) -> u64 { - let halvings = self.0 / Epoch::BLOCKS; - - if halvings < 64 { - (50 * COIN_VALUE) >> halvings - } else { - 0 - } + Epoch::from(self).subsidy() } } @@ -53,6 +47,36 @@ impl FromStr for Height { mod tests { use super::*; + #[test] + fn n() { + assert_eq!(Height(0).n(), 0); + assert_eq!(Height(1).n(), 1); + } + + #[test] + fn add() { + assert_eq!(Height(0) + 1, 1); + assert_eq!(Height(1) + 100, 101); + } + + #[test] + fn sub() { + assert_eq!(Height(1) - 1, 0); + assert_eq!(Height(100) - 50, 50); + } + + #[test] + fn eq() { + assert_eq!(Height(0), 0); + assert_eq!(Height(100), 100); + } + + #[test] + fn from_str() { + assert_eq!("0".parse::().unwrap(), 0); + assert!("foo".parse::().is_err()); + } + #[test] fn subsidy() { assert_eq!(Height(0).subsidy(), 5000000000); @@ -61,26 +85,4 @@ mod tests { assert_eq!(Height(210000).subsidy(), 2500000000); assert_eq!(Height(210000 + 1).subsidy(), 2500000000); } - - // #[test] - // fn mineds() { - // assert_eq!(mined(0), 0); - // assert_eq!(mined(1), 50 * COIN_VALUE); - // } - - // #[test] - // fn names() { - // assert_eq!(name(Ordinal::new(0)), "nvtdijuwxlo"); - // assert_eq!(name(Ordinal::new(1)), "nvtdijuwxln"); - // assert_eq!(name(Ordinal::new(26)), "nvtdijuwxko"); - // assert_eq!(name(Ordinal::new(27)), "nvtdijuwxkn"); - // assert_eq!(name(Ordinal::new(2099999997689999)), ""); - // } - - // #[test] - // fn populations() { - // assert_eq!(population(0), 0); - // assert_eq!(population(1), 1); - // assert_eq!(population(u64::max_value()), 64); - // } } diff --git a/src/ordinal.rs b/src/ordinal.rs index 7c853f4253..89cc579075 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -13,6 +13,10 @@ impl Ordinal { Self(inner) } + pub(crate) fn n(self) -> u64 { + self.0 + } + pub(crate) fn new_checked(inner: u64) -> Option { if inner < Self::SUPPLY { Some(Self(inner)) @@ -52,10 +56,6 @@ impl Ordinal { name.chars().rev().collect() } - pub(crate) fn n(self) -> u64 { - self.0 - } - pub(crate) fn population(self) -> u64 { let mut n = self.0; let mut population = 0; From 5403ba646b12adc81e73f91e6906bd74fc23e3d7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 15:29:59 -0800 Subject: [PATCH 08/10] More tests and cleanup --- src/epoch.rs | 60 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/epoch.rs b/src/epoch.rs index b45d23af4e..2ee87c2d01 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -85,20 +85,17 @@ mod tests { use super::super::*; #[test] - fn supply() { - let mut mined = 0; - - for height in 0.. { - let subsidy = Height(height).subsidy(); - - if subsidy == 0 { - break; - } - - mined += subsidy; - } - - assert_eq!(Ordinal::SUPPLY, mined); + fn starting_ordinal() { + assert_eq!(Epoch(0).starting_ordinal().unwrap(), 0); + assert_eq!( + Epoch(1).starting_ordinal().unwrap(), + Epoch(0).subsidy() * Epoch::BLOCKS + ); + assert_eq!( + Epoch(2).starting_ordinal().unwrap(), + (Epoch(0).subsidy() + Epoch(1).subsidy()) * Epoch::BLOCKS + ); + assert_eq!(Epoch(33).starting_ordinal(), None); } #[test] @@ -114,7 +111,6 @@ mod tests { assert_eq!(Epoch::STARTING_ORDINALS, epoch_ordinals); assert_eq!(Epoch::STARTING_ORDINALS.len(), 33); - assert_eq!(Epoch(33).subsidy(), 0); } #[test] @@ -124,4 +120,38 @@ mod tests { assert_eq!(Epoch(32).subsidy(), 1); assert_eq!(Epoch(33).subsidy(), 0); } + + #[test] + fn blocks() { + // c.f. https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp + assert_eq!(Epoch::BLOCKS, 210000); + } + + #[test] + fn starting_height() { + assert_eq!(Epoch(0).starting_height(), 0); + assert_eq!(Epoch(1).starting_height(), Epoch::BLOCKS); + assert_eq!(Epoch(2).starting_height(), Epoch::BLOCKS * 2); + } + + #[test] + fn from_height() { + assert_eq!(Epoch::from(Height(0)), 0); + assert_eq!(Epoch::from(Height(Epoch::BLOCKS)), 1); + assert_eq!(Epoch::from(Height(Epoch::BLOCKS) + 1), 1); + } + + #[test] + fn from_ordinal() { + assert_eq!(Epoch::from(Ordinal::new(0)), 0); + assert_eq!(Epoch::from(Ordinal::new(1)), 0); + assert_eq!(Epoch::from(Epoch(1).starting_ordinal().unwrap()), 1); + assert_eq!(Epoch::from(Epoch(1).starting_ordinal().unwrap() + 1), 1); + } + + #[test] + fn eq() { + assert_eq!(Epoch(0), 0); + assert_eq!(Epoch(100), 100); + } } From 9f5a373f1e2932e6ab5590c239798005df86b977 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 15:41:27 -0800 Subject: [PATCH 09/10] Add tests --- src/ordinal.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/ordinal.rs b/src/ordinal.rs index 89cc579075..36d3e7836b 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -105,6 +105,22 @@ impl FromStr for Ordinal { mod tests { use super::*; + #[test] + #[should_panic] + fn new_out_of_range_panics() { + Ordinal::new(Ordinal::SUPPLY); + } + + fn n() { + assert_eq!(Ordinal::new(1).n(), 1); + assert_eq!(Ordinal::new(100).n(), 100); + } + + fn new_checked() { + assert_eq!(Ordinal::new_checked(0).unwrap(), 0); + assert_eq!(Ordinal::new_checked(Ordinal::SUPPLY), None); + } + #[test] fn height() { assert_eq!(Ordinal::new(0).height(), 0); @@ -144,7 +160,18 @@ mod tests { } #[test] - fn block_position() { + fn epoch_position() { + assert_eq!(Epoch(0).starting_ordinal().unwrap().epoch_position(), 0); + assert_eq!( + (Epoch(0).starting_ordinal().unwrap() + 100).epoch_position(), + 100 + ); + assert_eq!(Epoch(1).starting_ordinal().unwrap().epoch_position(), 0); + assert_eq!(Epoch(2).starting_ordinal().unwrap().epoch_position(), 0); + } + + #[test] + fn subsidy_position() { assert_eq!(Ordinal::new(0).subsidy_position(), 0); assert_eq!(Ordinal::new(1).subsidy_position(), 1); assert_eq!( @@ -177,4 +204,36 @@ mod tests { assert_eq!(Ordinal::SUPPLY, mined); } + + #[test] + fn last() { + assert_eq!(Ordinal::LAST, Ordinal::SUPPLY - 1); + } + + #[test] + fn eq() { + assert_eq!(Ordinal::new(0), 0); + assert_eq!(Ordinal::new(1), 1); + } + + #[test] + fn add() { + assert_eq!(Ordinal::new(0) + 1, 1); + assert_eq!(Ordinal::new(1) + 100, 101); + } + + #[test] + fn add_assign() { + let mut ordinal = Ordinal::new(0); + ordinal += 1; + assert_eq!(ordinal, 1); + ordinal += 100; + assert_eq!(ordinal, 101); + } + + #[test] + fn from_str() { + assert_eq!("0".parse::().unwrap(), 0); + assert!("foo".parse::().is_err()); + } } From bece7be196e73e8a50a7f9b2f3d1ba17427b4ffc Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 19 Jan 2022 15:45:05 -0800 Subject: [PATCH 10/10] Add tests --- src/ordinal.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ordinal.rs b/src/ordinal.rs index 36d3e7836b..cb5b27b802 100644 --- a/src/ordinal.rs +++ b/src/ordinal.rs @@ -111,11 +111,13 @@ mod tests { Ordinal::new(Ordinal::SUPPLY); } + #[test] fn n() { assert_eq!(Ordinal::new(1).n(), 1); assert_eq!(Ordinal::new(100).n(), 100); } + #[test] fn new_checked() { assert_eq!(Ordinal::new_checked(0).unwrap(), 0); assert_eq!(Ordinal::new_checked(Ordinal::SUPPLY), None);