Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ordinal number terminology #46

Merged
merged 2 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 17 additions & 17 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
79 changes: 46 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -57,27 +65,32 @@ fn transfer(transaction: Transaction) {
}
```

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
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
```


## 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:

```
$ sat-tracker traits 0
$ ord traits 0
even
square
cube
Expand All @@ -91,19 +104,19 @@ 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:

```
$ sat-tracker name nvtdijuwxlo
$ ord name nvtdijuwxlo
0
$ sat-tracker name hello
$ ord name hello
2099999993937872
$ sat-tracker name ''
$ ord name ''
2099999997689999
```
10 changes: 5 additions & 5 deletions src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub enum Arguments {
Find {
#[structopt(long)]
blocksdir: Option<PathBuf>,
n: u64,
ordinal: u64,
height: u64,
},
Name {
Expand All @@ -18,7 +18,7 @@ pub enum Arguments {
},
Supply,
Traits {
n: u64,
ordinal: u64,
},
}

Expand All @@ -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),
}
}
}
12 changes: 6 additions & 6 deletions src/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>, n: u64, at_height: u64) -> Result<()> {
pub(crate) fn run(blocksdir: Option<PathBuf>, ordinal: u64, at_height: u64) -> Result<()> {
let blocksdir = if let Some(blocksdir) = blocksdir {
blocksdir
} else if cfg!(target_os = "macos") {
Expand Down Expand Up @@ -107,7 +107,7 @@ pub(crate) fn run(blocksdir: Option<PathBuf>, 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);

Expand Down Expand Up @@ -136,15 +136,15 @@ pub(crate) fn run(blocksdir: Option<PathBuf>, 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(())
Expand Down
12 changes: 6 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
}
Expand Down
32 changes: 16 additions & 16 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -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::<Vec<char>>();
let digits = ordinal.to_string().chars().collect::<Vec<char>>();

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");
}
Expand All @@ -44,34 +44,34 @@ 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");
}

let subsidy = subsidy(block);

mined += subsidy;

if mined > n {
if mined > ordinal {
println!("block: {}", block);
break;
}

block += 1;
}

if n == 623624999999999 {
if ordinal == 623624999999999 {
println!("illusive");
} else if block == 124724 {
println!("cursed");
Expand Down
2 changes: 1 addition & 1 deletion tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand Down
Loading