From a7963cb7f813114749b70c57ab140e8d5ff26e29 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 17 Jan 2022 16:15:55 -0800 Subject: [PATCH 1/2] Rename to ord start renaming readme --- .github/workflows/ci.yaml | 2 +- Cargo.lock | 34 +++++++++++----------- Cargo.toml | 4 +-- README.md | 61 +++++++++++++++++++++++---------------- tests/integration.rs | 2 +- 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6ec16e77f1..85ea05852f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,7 +41,7 @@ jobs: - name: Check Lockfile run: | - cargo update --locked --package sat-tracker + cargo update --locked --package ord - name: Test run: cargo test --all diff --git a/Cargo.lock b/Cargo.lock index 99cf91ffe3..b374dc8a45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,23 @@ dependencies = [ "autocfg", ] +[[package]] +name = "ord" +version = "0.0.0" +dependencies = [ + "bitcoin", + "dirs", + "env_logger", + "executable-path", + "integer-cbrt", + "integer-sqrt", + "log", + "redb", + "structopt", + "tempfile", + "unindent", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -345,23 +362,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "sat-tracker" -version = "0.0.0" -dependencies = [ - "bitcoin", - "dirs", - "env_logger", - "executable-path", - "integer-cbrt", - "integer-sqrt", - "log", - "redb", - "structopt", - "tempfile", - "unindent", -] - [[package]] name = "secp256k1" version = "0.20.3" diff --git a/Cargo.toml b/Cargo.toml index ec9d2e89a6..1a55507048 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "sat-tracker" -description = "Satoshi tracker" +name = "ord" +description = "Bitcoin satoshi ordinal number utility" version = "0.0.0" license = "CC0-1.0" edition = "2021" diff --git a/README.md b/README.md index 5051c05e41..7c661bc19f 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,49 @@ -# Sat Tracker +# Satoshi Ordinals -A scheme for assigning serial numbers to satoshis upon creation and tracking -them across subsequent transactions. - -Satoshi serial numbers can be used as an addressing scheme for NFTs. +A scheme for assigning ordinal numbers to satoshis and tracking them across +transactions, and a command-line utility, `ord` for querying information about +ordinals. +Ordinal numbers can be used as an addressing scheme for NFTs. In such a scheme, +the NFT creator would create a message that assigned a new NFT Y to the satoshi +with ordinal X. The owner of the UTXO containing the satoshi with ordinal X +owns NFT Y, and can transfer that ownership to another person with a +transaction that sends ordinal Y to a UTXO that the new owner controls. The +current owner can sign a message proving that they own a given UTXO, wich also +serves as proof of ownership of all the NFTs assigned to satoshis within that +UTXO. ## Numbering -Satoshis are numbered in ascending order, starting at 0 in the genesis block, -and ending with 2099999997689999, mined in block 6929999, the last block with a -subsidy. +Satoshis are assigned ordinal numbers in the order in which they are mined. +Ordinals start at 0, for the first satoshi of the genesis block, and end with +2099999997689999, the only satoshi mined in block 6929999, the last block with +a subsidy. -Satoshi numbers only depend on how many satoshis could have been created in -previous blocks, not how many were *actually* created. +Ordinals depend only on how many satoshis *could* have been mined in previous +blocks, not how many were *actually* mined. In particular, this means that block 124724, which underpaid the block subsidy -by one satoshi, does not reduce the serial numbers of satoshis in subsequent -blocks. +by one satoshi, does not reduce the ordinals of satoshis in subsequent blocks. -The `range` command gives the half-open range of satoshis mined in the block at +The `range` command gives the half-open range of ordinals mined in the block at a given height: ``` -$ sat-tracker range 0 +$ ord range 0 [0,5000000000) ``` See [src/range.rs](src/range.rs) for the numbering algorithm. - ## Transfer -Satoshis input to a transaction are transferred to the transaction outputs -according to the order and value of the inputs and outputs. Satoshis paid as -fees are assigned in the same fashion to the outputs of the coinbase -transaction. +The ordinal numbers on satoshis input to a transaction are transferred to the +transaction outputs in first-in-first-out order. + +Satoshis paid as fees are considered to be inputs to the coinbase transaction, +after an implicit input containing the block subsidy, in the same order that +their parent transactions appear in the block. ```rust fn transfer(transaction: Transaction) { @@ -57,11 +65,14 @@ fn transfer(transaction: Transaction) { } ``` +If the coinbase transaction underpays the block subsidy or fees, those ordinal +numbers are destroyed. + The `find` command, as of yet unfinished, gives the current outpoint containing -a given satoshi as of a given height: +the satoshi with a given ordinal at a given height: ``` -$ sat-tracker find --blocksdir ~/.bicoin/blocks 0 0 +$ ord find --blocksdir ~/.bicoin/blocks 0 0 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0 ``` @@ -77,7 +88,7 @@ to fix, and would require an overhaul of the name trait. The `traits` command prints out the traits of a given satoshi: ``` -$ sat-tracker traits 0 +$ ord traits 0 even square cube @@ -100,10 +111,10 @@ ever be spent. The `name` command finds the satoshi with the given name: ``` -$ sat-tracker name nvtdijuwxlo +$ ord name nvtdijuwxlo 0 -$ sat-tracker name hello +$ ord name hello 2099999993937872 -$ sat-tracker name '' +$ ord name '' 2099999997689999 ``` diff --git a/tests/integration.rs b/tests/integration.rs index 022d82fd12..9e553ee6ce 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -96,7 +96,7 @@ impl Test { fs::create_dir(&blocksdir)?; populate_blockfile(File::create(blocksdir.join("blk00000.dat"))?, 1)?; - let output = Command::new(executable_path("sat-tracker")) + let output = Command::new(executable_path("ord")) .current_dir(&self.tempdir) .args(self.args) .output()?; From 8dd9c9c1be7ac9b0be6b7e1f2f749a7262bfe589 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 17 Jan 2022 16:24:35 -0800 Subject: [PATCH 2/2] Rename "satoshi number" to "ordinal number" --- README.md | 22 ++++++++++++---------- src/arguments.rs | 10 +++++----- src/find.rs | 12 ++++++------ src/main.rs | 12 ++++++------ src/traits.rs | 32 ++++++++++++++++---------------- tests/traits.rs | 13 +++++++++++-- 6 files changed, 56 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 7c661bc19f..bc2e62eed7 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,9 @@ fn transfer(transaction: Transaction) { } ``` -If the coinbase transaction underpays the block subsidy or fees, those ordinal -numbers are destroyed. +If the coinbase transaction underpays the block subsidy or fees, those +satoshis, along with their ordinal numbers, are destroyed and taken out of +circulation. The `find` command, as of yet unfinished, gives the current outpoint containing the satoshi with a given ordinal at a given height: @@ -79,13 +80,14 @@ $ ord find --blocksdir ~/.bicoin/blocks 0 0 ## Traits -Satoshis have traits, based on their number. +Satoshis have traits, based on their ordinal. NB: Traits should be considered *UNSTABLE*. In particular, the satoshis with short names will not be available for quite some time, which might be desirable to fix, and would require an overhaul of the name trait. -The `traits` command prints out the traits of a given satoshi: +The `traits` command prints out the traits of the satoshi with the given +ordinal: ``` $ ord traits 0 @@ -102,13 +104,13 @@ block: 0 ## Names -Each satoshi is assigned a name, consisting of lowercase ASCII characters. -Satoshi 0 has name `nvtdijuwxlo`, and names get shorter as the satoshi number -gets larger. This is to ensure that short names aren't locked in the genesis -block output which is unspendable, and other outputs, which are unlikely to -ever be spent. +Each satoshi is assigned a name, consisting of lowercase ASCII characters, +based on its ordinal. Satoshi 0 has name `nvtdijuwxlo`, and names get shorter +as the ordinal number gets larger. This is to ensure that short names aren't +locked in the genesis block output which is unspendable, and other outputs, +which are unlikely to ever be spent. -The `name` command finds the satoshi with the given name: +The `name` command prints the ordinal of the satoshi with the given name: ``` $ ord name nvtdijuwxlo diff --git a/src/arguments.rs b/src/arguments.rs index dd5c715013..bcb193fedf 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -5,7 +5,7 @@ pub enum Arguments { Find { #[structopt(long)] blocksdir: Option, - n: u64, + ordinal: u64, height: u64, }, Name { @@ -18,7 +18,7 @@ pub enum Arguments { }, Supply, Traits { - n: u64, + ordinal: u64, }, } @@ -27,13 +27,13 @@ impl Arguments { match self { Self::Find { blocksdir, - n, + ordinal, height, - } => crate::find::run(blocksdir, n, height), + } => crate::find::run(blocksdir, 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 { n } => crate::traits::run(n), + Self::Traits { ordinal } => crate::traits::run(ordinal), } } } diff --git a/src/find.rs b/src/find.rs index 68c0b6c9d7..581cea9440 100644 --- a/src/find.rs +++ b/src/find.rs @@ -5,7 +5,7 @@ const HEIGHTS: &str = "heights"; const BLOCK_OFFSETS: &str = "block_offsets"; const HEIGHTS_TO_HASHES: &str = "height_to_hashes"; -pub(crate) fn run(blocksdir: Option, n: u64, at_height: u64) -> Result<()> { +pub(crate) fn run(blocksdir: Option, ordinal: u64, at_height: u64) -> Result<()> { let blocksdir = if let Some(blocksdir) = blocksdir { blocksdir } else if cfg!(target_os = "macos") { @@ -107,7 +107,7 @@ pub(crate) fn run(blocksdir: Option, n: u64, at_height: u64) -> Result< write.commit()?; } - let height = n / (50 * 100_000_000); + let height = ordinal / (50 * 100_000_000); assert!(height < 100); assert!(at_height == height); @@ -136,15 +136,15 @@ pub(crate) fn run(blocksdir: Option, n: u64, at_height: u64) -> Result< let block = Block::consensus_decode(bytes)?; - let position = n % (50 * 100_000_000); + let position = ordinal % (50 * 100_000_000); - let mut n = 0; + let mut ordinal = 0; for (i, output) in block.txdata[0].output.iter().enumerate() { - if n + output.value >= position { + if ordinal + output.value >= position { println!("{}:{}", block.txdata[0].txid(), i); break; } - n += output.value; + ordinal += output.value; } Ok(()) diff --git a/src/main.rs b/src/main.rs index bd7d763427..31ddb1dd4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,8 +47,8 @@ fn subsidy(height: u64) -> u64 { } } -fn name(n: u64) -> String { - let mut x = SUPPLY - n - 1; +fn name(ordinal: u64) -> String { + let mut x = SUPPLY - ordinal - 1; let mut name = String::new(); while x > 0 { name.push( @@ -62,11 +62,11 @@ fn name(n: u64) -> String { name.chars().rev().collect() } -fn population(mut n: u64) -> u64 { +fn population(mut ordinal: u64) -> u64 { let mut population = 0; - while n > 0 { - population += n & 1; - n >>= 1; + while ordinal > 0 { + population += ordinal & 1; + ordinal >>= 1; } population } diff --git a/src/traits.rs b/src/traits.rs index a31e102306..23677fdba9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,30 +1,30 @@ use super::*; -pub(crate) fn run(n: u64) -> Result { - if n > 2099999997689999 { - return Err("Invalid satoshi".into()); +pub(crate) fn run(ordinal: u64) -> Result { + if ordinal > 2099999997689999 { + return Err("Invalid ordinal".into()); } - if n % 2 == 0 { + if ordinal % 2 == 0 { println!("even"); } else { println!("odd"); } - let isqrt = n.integer_sqrt(); - if isqrt * isqrt == n { + let isqrt = ordinal.integer_sqrt(); + if isqrt * isqrt == ordinal { println!("square"); } - let icbrt = n.integer_cbrt(); - if icbrt * icbrt * icbrt == n { + let icbrt = ordinal.integer_cbrt(); + if icbrt * icbrt * icbrt == ordinal { println!("cube"); } - let digits = n.to_string().chars().collect::>(); + let digits = ordinal.to_string().chars().collect::>(); let pi = std::f64::consts::PI.to_string().replace('.', ""); - let s = n.to_string(); + let s = ordinal.to_string(); if s == pi[..s.len()] { println!("pi"); } @@ -44,18 +44,18 @@ pub(crate) fn run(n: u64) -> Result { digits.len() ); - println!("population: {}", population(n)); + println!("population: {}", population(ordinal)); - println!("name: {}", name(n)); + println!("name: {}", name(ordinal)); - if let Some(character) = char::from_u32((n % 0x110000) as u32) { + if let Some(character) = char::from_u32((ordinal % 0x110000) as u32) { println!("character: {:?}", character); } let mut block = 0; let mut mined = 0; loop { - if n == mined { + if ordinal == mined { println!("shiny"); } @@ -63,7 +63,7 @@ pub(crate) fn run(n: u64) -> Result { mined += subsidy; - if mined > n { + if mined > ordinal { println!("block: {}", block); break; } @@ -71,7 +71,7 @@ pub(crate) fn run(n: u64) -> Result { block += 1; } - if n == 623624999999999 { + if ordinal == 623624999999999 { println!("illusive"); } else if block == 124724 { println!("cursed"); diff --git a/tests/traits.rs b/tests/traits.rs index a0612c2952..4c423bec41 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -1,9 +1,9 @@ use super::*; -fn traits(n: u64) -> Result> { +fn traits(ordinal: u64) -> Result> { Ok( Test::new()? - .args(&["traits", &n.to_string()]) + .args(&["traits", &ordinal.to_string()]) .ignore_stdout() .run_with_stdout()? .lines() @@ -12,6 +12,15 @@ fn traits(n: u64) -> Result> { ) } +#[test] +fn invalid_ordinal() -> Result { + Test::new()? + .args(&["traits", "2099999997690000"]) + .expected_stderr("error: Invalid ordinal\n") + .expected_status(1) + .run() +} + #[test] fn even() -> Result { assert!(traits(0)?.contains("even"));