chore: import ord 0.14.0

This commit is contained in:
mhatal
2025-03-07 00:41:24 -06:00
commit 3682e920f7
316 changed files with 134476 additions and 0 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* -text

138
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
defaults:
run:
shell: bash
env:
RUSTFLAGS: --deny warnings
LANGUAGES: de fr es pt ru zh ja ko fil ar hi it
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust Toolchain Components
uses: actions-rs/toolchain@v1
with:
override: true
toolchain: stable
- uses: Swatinem/rust-cache@v2
- uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: latest
- name: Install mdbook-i18n-helpers
run: cargo install mdbook-i18n-helpers
- name: Install mdbook-linkcheck
run: |
mkdir -p mdbook-linkcheck
cd mdbook-linkcheck
wget https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip
unzip mdbook-linkcheck.x86_64-unknown-linux-gnu.zip
chmod +x mdbook-linkcheck
pwd >> $GITHUB_PATH
- name: Build docs
run: mdbook build docs -d build
- name: Build all translations for docs
run: |
for lang in ${{ env.LANGUAGES }}; do
echo "::group::Building $lang translation"
MDBOOK_BOOK__LANGUAGE=$lang \
mdbook build docs -d build/$lang
mv docs/build/$lang/html docs/build/html/$lang
echo "::endgroup::"
done
- name: Deploy Pages
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/master'
with:
github_token: ${{secrets.GITHUB_TOKEN}}
publish_branch: gh-pages
publish_dir: docs/build/html
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust Toolchain Components
uses: actions-rs/toolchain@v1
with:
components: clippy, rustfmt
override: true
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: Clippy
run: cargo clippy --all --all-targets
- name: Format
run: cargo fmt --all -- --check
- name: Check for Forbidden Words
run: |
sudo apt-get install ripgrep
./bin/forbid
test:
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- windows-latest
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- name: Install Rust Toolchain Components
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: Test
run: cargo test --all
core:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust Toolchain Components
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: Install Bitcoin Core
run: ./bin/install-bitcoin-core-linux
- name: Test
run: cargo test --all -- --ignored

81
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Release
on:
push:
tags:
- '*'
defaults:
run:
shell: bash
jobs:
release:
strategy:
fail-fast: false
matrix:
target:
- aarch64-apple-darwin
- x86_64-apple-darwin
- x86_64-pc-windows-msvc
- x86_64-unknown-linux-gnu
include:
- target: aarch64-apple-darwin
os: macos-latest
target_rustflags: ''
- target: x86_64-apple-darwin
os: macos-latest
target_rustflags: ''
- target: x86_64-pc-windows-msvc
os: windows-latest
target_rustflags: ''
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
target_rustflags: ''
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- name: Install Rust Toolchain Components
uses: actions-rs/toolchain@v1
with:
override: true
target: ${{ matrix.target }}
toolchain: stable
- name: Install Linux Dependencies
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install musl-tools libssl-dev pkg-config
- name: Release Type
id: release-type
run: |
if [[ ${{ github.ref }} =~ ^refs/tags/[0-9]+[.][0-9]+[.][0-9]+$ ]]; then
echo ::set-output name=value::release
else
echo ::set-output name=value::prerelease
fi
- name: Package
id: package
env:
TARGET: ${{ matrix.target }}
REF: ${{ github.ref }}
OS: ${{ matrix.os }}
TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
run: ./bin/package
shell: bash
- name: Publish Archive
uses: softprops/action-gh-release@v0.1.15
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
draft: false
files: ${{ steps.package.outputs.archive }}
prerelease: ${{ steps.release-type.outputs.value == 'prerelease' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
/*.redb
/.idea/
/.vagrant
/docs/build
/fuzz/artifacts
/fuzz/corpus
/fuzz/coverage
/fuzz/target
/ord.log
/target
/test-times.txt
/tmp

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
# ignore everything
/*
# except docs
!/docs

1274
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

6
CONTRIBUTING Normal file
View File

@@ -0,0 +1,6 @@
Contributing
============
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you shall be licensed as in
LICENSE, without any additional terms or conditions.

3988
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

94
Cargo.toml Normal file
View File

@@ -0,0 +1,94 @@
[package]
name = "ord"
description = "◉ Ordinal wallet and block explorer"
version = "0.14.0"
license = "CC0-1.0"
edition = "2021"
autotests = false
homepage = "https://github.com/ordinals/ord"
repository = "https://github.com/ordinals/ord"
autobins = false
rust-version = "1.67"
[package.metadata.deb]
copyright = "The Ord Maintainers"
maintainer = "The Ord Maintainers"
[workspace]
members = [".", "test-bitcoincore-rpc", "crates/*"]
[dependencies]
anyhow = { version = "1.0.56", features = ["backtrace"] }
async-trait = "0.1.72"
axum = { version = "0.6.1", features = ["headers", "http2"] }
axum-server = "0.5.0"
base64 = "0.21.0"
bech32 = "0.9.1"
bip39 = "2.0.0"
bitcoin = { version = "0.30.1", features = ["rand"] }
boilerplate = { version = "1.0.0", features = ["axum"] }
brotli = "3.4.0"
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"
hex = "0.4.3"
html-escaper = "0.2.0"
http = "0.2.6"
humantime = "2.1.0"
hyper = { version = "0.14.24", features = ["client", "http2"] }
indicatif = "0.17.1"
lazy_static = "1.4.0"
log = "0.4.14"
mime = "0.3.16"
mime_guess = "2.0.4"
miniscript = "10.0.0"
mp4 = "0.14.0"
ord-bitcoincore-rpc = "0.17.1"
redb = "1.4.0"
regex = "1.6.0"
rss = "2.0.1"
rust-embed = "8.0.0"
rustls = "0.22.0"
rustls-acme = { version = "0.8.1", features = ["axum"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = { version = "1.0.81", features = ["preserve_order"] }
serde_yaml = "0.9.17"
sha3 = "0.10.8"
sysinfo = "0.30.3"
tempfile = "3.2.0"
tokio = { version = "1.17.0", features = ["rt-multi-thread"] }
tokio-stream = "0.1.9"
tokio-util = {version = "0.7.3", features = ["compat"] }
tower-http = { version = "0.4.0", features = ["compression-br", "compression-gzip", "cors", "set-header"] }
[dev-dependencies]
criterion = "0.5.1"
executable-path = "1.0.0"
pretty_assertions = "1.2.1"
reqwest = { version = "0.11.10", features = ["blocking", "brotli", "json"] }
test-bitcoincore-rpc = { path = "test-bitcoincore-rpc" }
unindent = "0.2.1"
[[bench]]
name = "server"
harness = false
[[bin]]
name = "ord"
path = "src/bin/main.rs"
[lib]
name = "ord"
path = "src/lib.rs"
[[test]]
name = "integration"
path = "tests/lib.rs"
[build-dependencies]
pulldown-cmark = "0.9.2"

121
LICENSE Normal file
View File

@@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

311
README.md Normal file
View File

@@ -0,0 +1,311 @@
`ord`
=====
`ord` is an index, block explorer, and command-line wallet. It is experimental
software with no warranty. See [LICENSE](LICENSE) for more details.
Ordinal theory imbues satoshis with numismatic value, allowing them to
be collected and traded as curios.
Ordinal numbers are serial numbers for satoshis, assigned in the order in which
they are mined, and preserved across transactions.
See [the docs](https://docs.ordinals.com) for documentation and guides.
See [the BIP](bip.mediawiki) for a technical description of the assignment and
transfer algorithm.
See [the project board](https://github.com/users/casey/projects/3/) for
currently prioritized issues.
See [milestones](https://github.com/ordinals/ord/milestones) to get a sense of
where the project is and where it's going.
Join [the Discord server](https://discord.gg/87cjuz4FYg) to chat with fellow
ordinal degenerates.
Donate
------
Ordinals is open-source and community funded. The current lead maintainer of
`ord` is [raphjaph](https://github.com/raphjaph/). Raph's work on `ord` is
entirely funded by donations. If you can, please consider donating!
The donation address is
[bc1qguzk63exy7h5uygg8m2tcenca094a8t464jfyvrmr0s6wkt74wls3zr5m3](https://mempool.space/address/bc1qguzk63exy7h5uygg8m2tcenca094a8t464jfyvrmr0s6wkt74wls3zr5m3).
This address is 2 of 4 multisig wallet with keys held by
[raphjaph](https://twitter.com/raphjaph),
[erin](https://twitter.com/realizingerin),
[rodarmor](https://twitter.com/rodarmor), and
[ordinally](https://twitter.com/veryordinally).
Bitcoin received will go towards funding maintenance and development of `ord`,
as well as hosting costs for [ordinals.com](https://ordinals.com).
Thank you for donating!
Wallet
------
`ord` relies on Bitcoin Core for private key management and transaction signing.
This has a number of implications that you must understand in order to use
`ord` wallet commands safely:
- Bitcoin Core is not aware of inscriptions and does not perform sat
control. Using `bitcoin-cli` commands and RPC calls with `ord` wallets may
lead to loss of inscriptions.
- `ord wallet` commands automatically load the `ord` wallet given by the
`--wallet` option, which defaults to 'ord'. Keep in mind that after running
an `ord wallet` command, an `ord` wallet may be loaded.
- Because `ord` has access to your Bitcoin Core wallets, `ord` should not be
used with wallets that contain a material amount of funds. Keep ordinal and
cardinal wallets segregated.
### Pre-alpha wallet migration
Alpha `ord` wallets are not compatible with wallets created by previous
versions of `ord`. To migrate, use `ord wallet send` from the old wallet to
send sats and inscriptions to addresses generated by the new wallet with `ord
wallet receive`.
Installation
------------
`ord` is written in Rust and can be built from
[source](https://github.com/ordinals/ord). Pre-built binaries are available on the
[releases page](https://github.com/ordinals/ord/releases).
You can install the latest pre-built binary from the command line with:
```sh
curl --proto '=https' --tlsv1.2 -fsLS https://ordinals.com/install.sh | bash -s
```
Once `ord` is installed, you should be able to run `ord --version` on the
command line.
Building
--------
On Debian and Ubuntu, `ord` requires `libssl-dev` when building from source:
```
sudo apt-get install libssl-dev
```
You'll also need Rust:
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
To build `ord` from source:
```
git clone https://github.com/ordinals/ord.git
cd ord
cargo build --release
```
Once built, the `ord` binary can be found at `./target/release/ord`.
`ord` requires `rustc` version 1.67.0 or later. Run `rustc --version` to ensure you have this version. Run `rustup update` to get the latest stable release.
### Homebrew
`ord` is available in [Homebrew](https://brew.sh/):
```
brew install ord
```
### Debian Package
To build a `.deb` package:
```
cargo install cargo-deb
cargo deb
```
Contributing
------------
If you wish to contribute there are a couple things that are helpful to know. We
put a lot of emphasis on proper testing in the code base, with three broad
categories of tests: unit, integration and fuzz. Unit tests can usually be found at
the bottom of a file in a mod block called `tests`. If you add or modify a
function please also add a corresponding test. Integration tests try to test
end-to-end functionality by executing a subcommand of the binary. Those can be
found in the [tests](tests) directory. We don't have a lot of fuzzing but the
basic structure of how we do it can be found in the [fuzz](fuzz) directory.
We strongly recommend installing [just](https://github.com/casey/just) to make
running the tests easier. To run our CI test suite you would do:
```
just ci
```
This corresponds to the commands:
```
cargo fmt -- --check
cargo test --all
cargo test --all -- --ignored
```
Have a look at the [justfile](justfile) to see some more helpful recipes
(commands). Here are a couple more good ones:
```
just fmt
just fuzz
just doc
just watch ltest --all
```
If the tests are failing or hanging, you might need to increase the maximum
number of open files by running `ulimit -n 1024` in your shell before you run
the tests, or in your shell configuration.
We also try to follow a TDD (Test-Driven-Development) approach, which means we
use tests as a way to get visibility into the code. Tests have to run fast for that
reason so that the feedback loop between making a change, running the test and
seeing the result is small. To facilitate that we created a mocked Bitcoin Core
instance in [test-bitcoincore-rpc](./test-bitcoincore-rpc).
Syncing
-------
`ord` requires a synced `bitcoind` node with `-txindex` to build the index of
satoshi locations. `ord` communicates with `bitcoind` via RPC.
If `bitcoind` is run locally by the same user, without additional
configuration, `ord` should find it automatically by reading the `.cookie` file
from `bitcoind`'s datadir, and connecting using the default RPC port.
If `bitcoind` is not on mainnet, is not run by the same user, has a non-default
datadir, or a non-default port, you'll need to pass additional flags to `ord`.
See `ord --help` for details.
`bitcoind` RPC Authentication
-----------------------------
`ord` makes RPC calls to `bitcoind`, which usually requires a username and
password.
By default, `ord` looks a username and password in the cookie file created by
`bitcoind`.
The cookie file path can be configured using `--cookie-file`:
```
ord --cookie-file /path/to/cookie/file server
```
Alternatively, `ord` can be supplied with a username and password on the
command line:
```
ord --bitcoin-rpc-user foo --bitcoin-rpc-pass bar server
```
Using environment variables:
```
export ORD_BITCOIN_RPC_USER=foo
export ORD_BITCOIN_RPC_PASS=bar
ord server
```
Or in the config file:
```yaml
bitcoin_rpc_user: foo
bitcoin_rpc_pass: bar
```
Logging
--------
`ord` uses [env_logger](https://docs.rs/env_logger/latest/env_logger/). Set the
`RUST_LOG` environment variable in order to turn on logging. For example, run
the server and show `info`-level log messages and above:
```
$ RUST_LOG=info cargo run server
```
New Releases
------------
Release commit messages use the following template:
```
Release x.y.z
- Bump version: x.y.z → x.y.z
- Update changelog
- Update dependencies
```
Translations
------------
To translate [the docs](https://docs.ordinals.com) we use this
[mdBook i18n helper](https://github.com/google/mdbook-i18n-helpers).
So read through their [usage guide](https://github.com/google/mdbook-i18n-helpers/blob/main/i18n-helpers/USAGE.md)
to see the structure that translations should follow.
There are some other things to watch out for but feel free to just start a
translation and open a PR. Have a look at [this commit](https://github.com/ordinals/ord/commit/329f31bf6dac207dad001507dd6f18c87fdef355)
for an idea of what to do. A maintainer will also help you integrate it into our
build system.
To align your translated version of the Handbook with reference to commit
[#2427](https://github.com/ordinals/ord/pull/2426), here are some guiding
commands to assist you. It is assumed that your local environment is already
well-configured with [Python](https://www.python.org/),
[Mdbook](https://github.com/rust-lang/mdBook),
[mdBook i18n helper](https://github.com/google/mdbook-i18n-helpers) and that you've clone
this repo.
1. Run the following command to generate a new `pot` file, which is named as
`messages.pot`:
```
MDBOOK_OUTPUT='{"xgettext": {"pot-file": "messages.pot"}}'
mdbook build -d po
```
2. Run `msgmerge` where `xx.po` is your localized language version following
the naming standard of [ISO639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
This process will update the `po` file with the most recent original version:
```
msgmerge --update po/xx.po po/messages.pot
```
3. Look for `#, fuzzy`. The `mdBook-i18n-helper` tool utilizes the `"fuzzy"` tag
to highlight sections that have been recently edited. You can proceed to perform
the translation tasks by editing the `"fuzzy"`part.
4. Execute the `mdbook` command. A demonstration in Chinese (`zh`) is given below:
```
mdbook build docs -d build
MDBOOK_BOOK__LANGUAGE=zh mdbook build docs -d build/zh
mv docs/build/zh/html docs/build/html/zh
python3 -m http.server --directory docs/build/html --bind 127.0.0.1 8080
```
5. Upon verifying everything and ensuring all is in order, you can commit the
modifications and progress to open a Pull Request (PR) on Github.
(**Note**: Please ensure **ONLY** the **'xx.po'** file is pushed, other files
such as '.pot' or files ending in '~' are **unnecessary** and should **NOT** be
included in the Pull Request.

17
Vagrantfile vendored Normal file
View File

@@ -0,0 +1,17 @@
Vagrant.configure("2") do |config|
config.vm.box = "debian/bullseye64"
config.vm.provider "virtualbox" do |v|
v.memory = 1024 * 4
end
config.vm.network "private_network", ip: "192.168.56.4"
config.vm.provision "shell" do |s|
s.inline = ""
Dir.glob("#{Dir.home}/.ssh/*.pub").each do |path|
key = File.read(path).strip
s.inline << "echo '#{key}' >> /root/.ssh/authorized_keys\n"
end
end
end

44
batch.yaml Normal file
View File

@@ -0,0 +1,44 @@
# example batch file
# inscription modes:
# - `separate-outputs`: inscribe on separate postage-sized outputs
# - `shared-output`: inscribe on a single output separated by postage
# - `same-sat`: inscribe on the same sat
mode: separate-outputs
# parent inscription:
parent: 6ac5cacb768794f4fd7a78bf00f2074891fce68bd65c4ff36e77177237aacacai0
# postage for each inscription:
postage: 12345
# sat to inscribe on, can only be used with `same-sat`:
# sat: 5000000000
# inscriptions to inscribe
#
# each inscription has the following fields:
#
# `file`: path to inscription contents
# `metadata`: inscription metadata (optional)
# `metaprotocol`: inscription metaprotocol (optional)
# `destination`: destination for that inscription (optional). Note: If no destination is specified a new wallet change address will be used
inscriptions:
- file: mango.avif
metadata:
title: Delicious Mangos
description: >
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam semper,
ligula ornare laoreet tincidunt, odio nisi euismod tortor, vel blandit
metus est et odio. Nullam venenatis, urna et molestie vestibulum, orci
mi efficitur risus, eu malesuada diam lorem sed velit. Nam fermentum
dolor et luctus euismod.
destination: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
- file: token.json
metaprotocol: brc-20
- file: tulip.png
metadata:
author: Satoshi Nakamoto
destination: bc1pdqrcrxa8vx6gy75mfdfj84puhxffh4fq46h3gkp6jxdd0vjcsdyspfxcv6

16
benches/server.rs Normal file
View File

@@ -0,0 +1,16 @@
use {criterion::Criterion, ord::Index};
fn main() {
let mut criterion = Criterion::default().configure_from_args();
let index = Index::open(&Default::default()).unwrap();
let mut i = 0;
criterion.bench_function("inscription", |b| {
b.iter(|| {
Index::inscription_info_benchmark(&index, i);
i += 1;
});
});
Criterion::default().configure_from_args().final_summary();
}

17
benchmark/checkout Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euxo pipefail
REV=$1
if [[ ! -d ord ]]; then
git clone https://github.com/ordinals/ord.git
fi
cd ord
git fetch --all --prune
git checkout master
git reset --hard origin/master
git checkout `git rev-parse origin/$REV`
./benchmark/run

13
benchmark/run Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euxo pipefail
systemctl stop ord-dev
rm -rf /var/lib/ord-dev
journalctl --unit ord-dev --rotate
journalctl --unit ord-dev --vacuum-time 1s
./bin/update-dev-server

15
bin/benchmark Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euxo pipefail
rm -rf tmp/benchmark
mkdir -p tmp/benchmark
INDEX_SNAPSHOT=$1
HEIGHT_LIMIT=$2
cp $INDEX_SNAPSHOT tmp/benchmark/index.redb
cargo build --release
time ./target/release/ord --data-dir tmp/benchmark --height-limit $HEIGHT_LIMIT index

35
bin/flamegraph Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euxo pipefail
mkdir $1
cd $1
sudo \
CARGO_PROFILE_RELEASE_DEBUG=true \
RUST_LOG=info \
cargo flamegraph \
--deterministic \
--bin ord \
-- \
--chain signet \
--data-dir . \
--height-limit 0 \
index
rm -f flamegraph.svg
/usr/bin/time -o time sudo \
CARGO_PROFILE_RELEASE_DEBUG=true \
RUST_LOG=info \
cargo flamegraph \
--deterministic \
--bin ord \
-- \
--chain signet \
--data-dir . \
--height-limit 5000 \
index
sudo chown -n $UID flamegraph.svg
sudo chown -n $UID index.redb

13
bin/forbid Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
which rg > /dev/null
! rg \
--glob '!bin/forbid' \
--glob '!docs/src/bounty/frequency.tsv' \
--glob '!docs/po/*' \
--ignore-case \
'dbg!|fixme|todo|xxx' \
.

59
bin/graph Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python3
import re, sys
from matplotlib.pyplot import *
from dataclasses import dataclass
@dataclass
class Block:
height: int
ranges: int
time: int
transactions: int
pat = re.compile(
'''Block (?P<height>[0-9]+) at.*with (?P<transactions>[0-9]+) transactions.*
.*Wrote (?P<ranges>[0-9]+) sat ranges from .* outputs in (?P<time>[0-9]+) ms'''
)
blocks = [
Block(**{k : int(v) for k, v in group.items()})
for group in [
match.groupdict() for match in pat.finditer(open(sys.argv[1]).read())
]
]
start = 0
for i in range(len(blocks)):
if blocks[i].height == 1:
start = i
print(f"Skipping {start + 1} blocks from previous sync")
sync = blocks[start:]
_, (a, b, c) = subplots(3)
a.set_xlabel('Height')
a.set_ylabel('Time')
a.plot(
[block.height for block in sync],
[block.time for block in sync],
)
b.set_xlabel('Ranges')
b.set_ylabel('Time')
b.scatter(
[block.ranges for block in sync],
[block.time for block in sync],
)
c.set_xlabel('Tx\'s in block')
c.set_ylabel('Time')
c.scatter(
[block.transactions for block in sync],
[block.time for block in sync],
)
show()

15
bin/install-bitcoin-core-linux Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euxo pipefail
version=24.0.1
wget \
-O bitcoin.tar.gz \
https://bitcoincore.org/bin/bitcoin-core-$version/bitcoin-$version-x86_64-linux-gnu.tar.gz
tar \
-xzvf bitcoin.tar.gz \
-C /usr/local/bin \
--strip-components 2 \
bitcoin-$version/bin/{bitcoin-cli,bitcoind}

44
bin/package Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euxo pipefail
VERSION=${REF#"refs/tags/"}
DIST=`pwd`/dist
echo "Packaging ord $VERSION for $TARGET..."
test -f Cargo.lock || cargo generate-lockfile
echo "Building ord..."
RUSTFLAGS="--deny warnings $TARGET_RUSTFLAGS" \
cargo build --bin ord --target $TARGET --release
EXECUTABLE=target/$TARGET/release/ord
if [[ $OS == windows-latest ]]; then
EXECUTABLE=$EXECUTABLE.exe
fi
echo "Copying release files..."
mkdir -p dist/ord-$VERSION
cp \
$EXECUTABLE \
Cargo.lock \
Cargo.toml \
LICENSE \
README.md \
$DIST/ord-$VERSION
cd $DIST
echo "Creating release archive..."
case $OS in
ubuntu-latest | macos-latest)
ARCHIVE=$DIST/ord-$VERSION-$TARGET.tar.gz
tar czf $ARCHIVE *
echo "::set-output name=archive::$ARCHIVE"
;;
windows-latest)
ARCHIVE=$DIST/ord-$VERSION-$TARGET.zip
7z a $ARCHIVE *
echo "::set-output name=archive::`pwd -W`/ord-$VERSION-$TARGET.zip"
;;
esac

288
bip.mediawiki Normal file
View File

@@ -0,0 +1,288 @@
<pre>
BIP: ?
Layer: Applications
Title: Ordinal Numbers
Author: Casey Rodarmor <casey@rodarmor.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/ordinals/ord/discussions/126
Status: Draft
Type: Informational
Created: 2022-02-02
License: PD
</pre>
== Introduction ==
=== Abstract ===
This document defines a scheme for assigning serial numbers to sats.
=== Copyright ===
This work is placed in the public domain.
=== Motivation ===
Bitcoin has no notion of stable, public accounts or identities. Addresses are
single-use, and wallet accounts are private. Additionally, the use of addresses
or public keys as stable identifiers precludes transfer of ownership or key
rotation.
This proposal is motivated by the desire to provide stable identifiers that may
be used by Bitcoin applications.
== Description ==
=== Design ===
Every sat is serially numbered, starting at 0, in the order in which it is
mined. These numbers are termed "ordinal numbers", or "ordinals", as they are
ordinal numbers in the mathematical sense, giving the order of each sat in the
total supply. The word "ordinal" is nicely unambiguous, as it is not used
elsewhere in the Bitcoin protocol.
The ordinal numbers of sats in transaction inputs are transferred to output
sats in first-in-first-out order, according to the size and order of the
transactions inputs and outputs.
If a transaction is mined with the same transaction ID as outputs currently in
the UTXO set, following the behavior of Bitcoin Core, the new transaction
outputs displace the older UTXO set entries, destroying the sats contained in
any unspent outputs of the first transaction. This rule is required to handle
the two pairs of mainnet transactions with duplicate transaction IDs, namely
the coinbase transactions of blocks 91812/91842, and 91722/91880, mined before
[https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki BIP-34] made
the creation of transactions with duplicate IDs impossible.
For the purposes of the assignment algorithm, the coinbase transaction is
considered to have an implicit input equal in size to the subsidy, followed by
an input for every fee-paying transaction in the block, in the order that those
transactions appear in the block. The implicit subsidy input carries the
block's newly created sats. The implicit fee inputs carry the sats that were
paid as fees in the block's transactions.
Underpaying the subsidy does not change the ordinal numbers of sats mined
in subsequent blocks. Ordinals depend only on how many sats could have been
mined, not how many actually were.
=== Specification ===
Sats are numbered and transferred with the following algorithm:
<pre>
# subsidy of block at given height
def subsidy(height):
return 50 * 100_000_000 >> height // 210_000
# first ordinal of subsidy of block at given height
def first_ordinal(height):
start = 0
for height in range(height):
start += subsidy(height)
return start
# assign ordinals in given block
def assign_ordinals(block):
first = first_ordinal(block.height)
last = first + subsidy(block.height)
coinbase_ordinals = list(range(first, last))
for transaction in block.transactions[1:]:
ordinals = []
for input in transaction.inputs:
ordinals.extend(input.ordinals)
for output in transaction.outputs:
output.ordinals = ordinals[:output.value]
del ordinals[:output.value]
coinbase_ordinals.extend(ordinals)
for output in block.transaction[0].outputs:
output.ordinals = coinbase_ordinals[:output.value]
del coinbase_ordinals[:output.value]
</pre>
=== Terminology and Notation ===
A satpoint may be used to indicate the location of a sat within an output. A
satpoint consists of an outpoint, i.e., a transaction ID and output index, with
the addition of the offset of the ordinal within that output. For example, if
the sat in question is at offset 6 in the first output of a transaction, its
satpoint is:
`680df1e4d43016571e504b0b142ee43c5c0b83398a97bdcfd94ea6f287322d22:0:6`
== Discussion ==
=== Rationale ===
Ordinal numbers are designed to be orthogonal to other aspects of the Bitcoin
protocol, and can thus be used in conjunction with other layer one and layer
applications, even ones that were not designed with ordinal numbers in mind.
Ordinal sats can be secured using current and future script types. They can be
held by single-signature wallets, multi-signature wallets, time-locked, and
height-locked in all the usual ways.
By assigning ordinal numbers to all sats without the need for an explicit
creation step, the anonymity set of ordinal number users is maximized.
Since a sat has an output that contains it, and an output has a public key that
controls it, the owner of a sat can respond to challenges by signing messages
using the address associated with the controlling UTXO. Additionally, a sat can
change hands, or its private key can be rotated without a change of ownership,
by transferring it to a new output.
Ordinals require no changes to blocks, transactions, or network protocols, and
can thus be immediately adopted, or ignored, without impacting existing users.
Ordinals do not have an explicit on-chain footprint. However, a valid objection
is that adoption of ordinals will increase demand for outputs, and thus
increase the size of the UTXO set that full nodes must track. See the
objections section below.
The ordinal number scheme is extremely simple. The specification above is 15
lines of code.
Ordinals are fairly assigned. They are not premined, and are assigned
proportionally to existing bitcoin holders.
Ordinals are as granular as possible, as bitcoin is not capable of tracking
ownership of sub-sat values.
=== Transfer and the Dust Limit ===
Any single-sat transfer can be accomplished in a single transaction, but the
resulting transaction may contain outputs below the dust limit, and thus be
non-standard and difficult to get included in a block. Consider a scenario
where Alice owns an output containing the range of sats [0,10], the current
dust limit is 5 sats, and Alice wishes to send send sat 4 and 6 to Bob, but
retain ordinal 5. Alice could construct a transaction with three outputs of
size 5, 1, and 5, containing sats [0,4], 5, and [6,10], respectively. The
second output is under the dust limit, and so such a transaction would be
non-standard.
This transfer, and indeed any transfer, can be accomplished by breaking the
transfer into multiple transactions, with each transaction performing one or
more splits and merging in padding outputs as needed.
To wit, Alice could perform the desired transfer in two transactions. The first
transaction would send sats [0,4] to Bob, and return as change sat [5,10] to
Alice. The second transaction would take as inputs an output of at least 4
sats, the change input, and an additional input of at least one sat; and create
an output of size 5 to Bob's address, and the remainder as a change output.
Both transactions avoid creating any non-standard outputs, but still accomplish
the same desired transfer of sats.
=== Objections ===
''Privacy: Ordinal numbers are public and thus reduce user privacy.''
The applications using ordinal numbers required them to be public, and reduce
the privacy of only those users that decide to use them.
''Fungibility: Ordinal numbers reduce the fungibility of Bitcoin, as ordinals
received in a transaction may carry with them some public history.''
As anyone can send anyone else any sats, any reasonable person will assume that
a new owner of a particular sat cannot be understood to be the old owner, or
have any particular relationship with the old owner.
''Congestion: Adoption of ordinal numbers will increase the demand for
transactions, and drive up fees.''
Since Bitcoin requires the development of a robust fee market, this is a strong
positive of the proposal.
''UTXO set bloat: Adoption of ordinal numbers will increase the demand for
entries in the UTXO set, and thus increase the size of the UTXO set, which all
full nodes are required to track.''
The dust limit, which makes outputs with small values difficult to create,
should encourage users to create non-dust outputs, and to clean them up once
they no longer have use for the sats that they contain.
=== Security ===
The public key associated with a sat may change. This requires actively
following the blockchain to keep up with key changes, and requires care
compared to a system where public keys are static. However, a system with
static public keys suffers from an inability for keys to be rotated or accounts
to change hands.
Ordinal-aware software must avoid losing valuable sats by unintentionally
relinquishing them in a transaction, either to a non-controlled output or by
using them as fees.
=== Privacy considerations ===
Ordinals are opt-in, and should not impact the privacy of existing users.
Ordinals are themselves public, however, this is required by the fact that many
of the applications that they are intended to enable require public
identifiers.
Ordinal aware software should never mix sats which might have some publicly
visible data associated with their ordinals with sats intended for use in
payments or savings, since this would associate that publicly visible data with
the users otherwise pseudonymous wallet outputs.
=== Fungibility considerations ===
Since any sat can be sent to any address at any time, sats that are transferred,
even those with some public history, should be considered to be fungible with
other sats with no such history.
=== Backward compatibility ===
Ordinal numbers are fully backwards compatible and require no changes to the
bitcoin network.
=== Drawbacks ===
==== Large Index Size ====
Indexes supporting fast queries related to ordinals are slow to build and
consume large amounts of space.
An O(1) index that maps UTXOs to the ordinals that they contain is currently
100 GiB. The same index including spent outputs is 10 TiB.
An O(1) index supporting the opposite mapping, that of individual ordinals to
the UTXO that contains them, is likely to be intractable. However, an O(n)
index where n is the number of times an ordinal has changed hands, is fast and
practical.
==== Large Location Proofs ====
A proof can be constructed that demonstrates that a particular sat is contained
in a particular output, however the proofs are large. Such a proof consists of:
- Block headers
- A Merkle path to the coinbase transaction that created the sat
- The coinbase transaction that created the sat
- And for every spend of that sat:
- The spend transaction
- The transactions that created the inputs before the input that was spent,
to determine the values of the preceding inputs, to determine the position
of the sat
- And, if the sat was used as fees, all prior transaction in the block in
which it was spent, and the coinbase transaction, to determine the location
of the sat in the outputs.
== Reference implementation ==
This document and an implementation of an index that tracks the position of
sats in the main chain are maintained [https://github.com/ordinals/ord here].
== References ==
A variation of this scheme was independently invented a decade ago by jl2012
[https://bitcointalk.org/index.php?topic=117224.0 on the Bitcoin Forum].
For other colored coin proposals see [https://en.bitcoin.it/wiki/Colored_Coins the
Bitcoin Wiki entry].
For aliases, an implementation of short on-chain identifiers, see
[https://github.com/bitcoin/bips/blob/master/bip-0015.mediawiki BIP 15].

36
build.rs Normal file
View File

@@ -0,0 +1,36 @@
use std::{process::Command, str};
fn git_branch() -> Option<String> {
str::from_utf8(
&Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.ok()?
.stdout,
)
.ok()
.map(|branch| branch.into())
}
fn git_commit() -> Option<String> {
str::from_utf8(
&Command::new("git")
.args(["rev-parse", "--verify", "HEAD"])
.output()
.ok()?
.stdout,
)
.ok()
.map(|branch| branch.into())
}
fn main() {
println!(
"cargo:rustc-env=GIT_BRANCH={}",
git_branch().unwrap_or_default()
);
println!(
"cargo:rustc-env=GIT_COMMIT={}",
git_commit().unwrap_or_default()
);
}

15
contrib/initialize-opendime Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euxo pipefail
until [ -f /Volumes/OPENDIME/README.txt ]; do
sleep 1
done
dd if=/dev/urandom of=/Volumes/OPENDIME/entro.bin bs=1024 count=256
until [ -f /Volumes/OPENDIME/address.txt ]; do
sleep 1
done
cat /Volumes/OPENDIME/address.txt | tr -d '\r\n'

24
contrib/raw/justfile Normal file
View File

@@ -0,0 +1,24 @@
create INPUT_TXID INPUT_VOUT OUTPUT_DESTINATION OUTPUT_AMOUNT:
#!/usr/bin/env bash
set -euxo pipefail
bitcoin-cli createrawtransaction \
'[
{
"txid": "{{INPUT_TXID}}",
"vout": {{INPUT_VOUT}}
}
]' \
'[
{
"{{OUTPUT_DESTINATION}}": {{OUTPUT_AMOUNT}}
}
]' \
> raw.hex
sign WALLET_NAME:
bitcoin-cli -rpcwallet={{WALLET_NAME}} signrawtransactionwithwallet `cat raw.hex` > signed.json
send:
bitcoin-cli sendrawtransaction `cat signed.json | jq '.hex' --raw-output`

View File

@@ -0,0 +1,9 @@
[package]
name = "audit-cache"
version = "0.0.0"
edition = "2021"
publish = false
[dependencies]
colored = "2.0.4"
reqwest = { version = "0.11.22", features = ["blocking"] }

View File

@@ -0,0 +1,110 @@
use {
colored::Colorize,
reqwest::{blocking::get, StatusCode},
std::process,
};
const ENDPOINTS: &[(&str, StatusCode, &str, &str)] = &[
// PNG content is cached for one year
(
"/content/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0",
StatusCode::OK,
"HIT",
"public, max-age=31536000, immutable",
),
// HTML content is cached for one year
(
"/content/114c5c87c4d0a7facb2b4bf515a4ad385182c076a5cfcc2982bf2df103ec0fffi0",
StatusCode::OK,
"HIT",
"public, max-age=31536000, immutable",
),
// content respopnses that aren't found aren't cached
(
"/content/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i1",
StatusCode::NOT_FOUND,
"BYPASS",
"no-store",
),
// HTML previews are cached for one year
(
"/preview/114c5c87c4d0a7facb2b4bf515a4ad385182c076a5cfcc2982bf2df103ec0fffi0",
StatusCode::OK,
"HIT",
"public, max-age=31536000, immutable",
),
// non-HTML previews are cached for four hours
(
"/preview/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0",
StatusCode::OK,
"HIT",
"max-age=14400",
),
("/static/index.css", StatusCode::OK, "HIT", "max-age=14400"),
("/static/index.js", StatusCode::OK, "HIT", "max-age=14400"),
("/sat/FOO", StatusCode::BAD_REQUEST, "HIT", "max-age=14400"),
("/", StatusCode::OK, "BYPASS", ""),
("/blockheight", StatusCode::OK, "BYPASS", ""),
];
fn main() {
eprint!("Warming up the cache");
for (endpoint, expected_status_code, _expected_cache_status, _expected_cache_control) in ENDPOINTS
{
let response = get(format!("https://ordinals.com{endpoint}")).unwrap();
assert_eq!(response.status(), *expected_status_code);
eprint!(".");
}
eprintln!();
let mut failures = 0;
for (endpoint, expected_status_code, expected_cache_status, expected_cache_control) in ENDPOINTS {
eprint!("GET {endpoint}");
let response = get(format!("https://ordinals.com{endpoint}")).unwrap();
let status_code = response.status();
eprint!(" {}", status_code.as_u16());
assert_eq!(response.status(), *expected_status_code);
let headers = response.headers();
let mut pass = true;
let cache_status = headers
.get("cf-cache-status")
.map(|value| value.to_str().unwrap().to_string())
.unwrap_or_default();
if cache_status == *expected_cache_status {
eprint!(" {}", cache_status.green());
} else {
eprint!(" {}", cache_status.red());
pass = false;
}
let cache_control = headers
.get("cache-control")
.map(|value| value.to_str().unwrap().to_string())
.unwrap_or_default();
if cache_control == *expected_cache_control {
eprintln!(" {}", cache_control.green());
} else {
eprintln!(" {}", cache_control.red());
pass = false;
}
failures += u32::from(!pass);
}
if failures > 0 {
eprintln!("{failures} failures");
process::exit(1);
}
}

3
deploy/bitcoin.conf Normal file
View File

@@ -0,0 +1,3 @@
datadir=/var/lib/bitcoind
maxmempool=1024
txindex=1

33
deploy/bitcoind.service Normal file
View File

@@ -0,0 +1,33 @@
[Unit]
After=network-online.target
Description=Bitcoin daemon
Documentation=https://github.com/bitcoin/bitcoin/blob/master/doc/init.md
Wants=network-online.target
[Service]
ConfigurationDirectory=bitcoin
ConfigurationDirectoryMode=0710
ExecStart=/usr/local/bin/bitcoind \
-conf=/etc/bitcoin/bitcoin.conf \
-chain=${CHAIN}
ExecStartPre=/bin/chgrp bitcoin /etc/bitcoin
Group=bitcoin
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PermissionsStartOnly=true
PrivateDevices=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=full
Restart=on-failure
RuntimeDirectory=bitcoind
RuntimeDirectoryMode=0710
StateDirectory=bitcoind
StateDirectoryMode=0710
TimeoutStartSec=infinity
TimeoutStopSec=600
Type=simple
User=bitcoin
[Install]
WantedBy=multi-user.target

23
deploy/checkout Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euxo pipefail
BRANCH=$1
REMOTE=$2
CHAIN=$3
DOMAIN=$4
if [[ ! -d $REMOTE ]]; then
mkdir -p $REMOTE
git clone https://github.com/$REMOTE.git $REMOTE
fi
cd $REMOTE
git fetch origin
git checkout -B $BRANCH
git reset --hard origin/$BRANCH
COMMIT=$(git rev-parse --short HEAD)
./deploy/setup $CHAIN $DOMAIN $BRANCH $COMMIT

41
deploy/ord.service Normal file
View File

@@ -0,0 +1,41 @@
[Unit]
After=network.target
Description=Ord server
StartLimitBurst=120
StartLimitIntervalSec=10m
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
Environment=RUST_BACKTRACE=1
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/ord \
--bitcoin-data-dir /var/lib/bitcoind \
--chain ${CHAIN} \
--config-dir /var/lib/ord \
--data-dir /var/lib/ord \
--index-runes \
--index-sats \
server \
--acme-contact mailto:casey@rodarmor.com \
--csp-origin https://${CSP_ORIGIN} \
--http \
--https
Group=ord
LimitNOFILE=65536
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=full
Restart=on-failure
RestartSec=5s
StateDirectory=ord
StateDirectoryMode=0700
TimeoutStopSec=10m
Type=simple
User=ord
WorkingDirectory=/var/lib/ord
[Install]
WantedBy=multi-user.target

159
deploy/setup Executable file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env bash
# This script is idempotent.
set -euxo pipefail
CHAIN=$1
DOMAIN=$2
BRANCH=$3
COMMIT=$4
REVISION="ord-$BRANCH-$COMMIT"
case $CHAIN in
main)
CSP_ORIGIN=ordinals.com
;;
regtest)
CSP_ORIGIN=regtest.ordinals.com
;;
signet)
CSP_ORIGIN=signet.ordinals.com
;;
test)
CSP_ORIGIN=testnet.ordinals.com
;;
*)
echo "Unknown chain: $CHAIN"
exit 1
;;
esac
touch ~/.hushlogin
sed -i -E 's/#?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
mkdir -p \
/etc/systemd/system/bitcoind.service.d \
/etc/systemd/system/ord.service.d
printf "[Service]\nEnvironment=CHAIN=%s\nEnvironment=CSP_ORIGIN=%s\n" $CHAIN $CSP_ORIGIN \
| tee /etc/systemd/system/bitcoind.service.d/override.conf \
> /etc/systemd/system/ord.service.d/override.conf
hostnamectl set-hostname $DOMAIN
apt-get install --yes \
acl \
clang \
curl \
libsqlite3-dev\
libssl-dev \
locales-all \
pkg-config \
ufw \
vim
ufw default allow outgoing
ufw default deny incoming
ufw allow 8080
ufw allow http
ufw allow https
ufw allow ssh
case $CHAIN in
main)
ufw allow 8333
;;
regtest)
ufw allow 18444
;;
signet)
ufw allow 38333
;;
test)
ufw allow 18333
;;
*)
echo "Unknown chain: $CHAIN"
exit 1
;;
esac
ufw --force enable
if ! which bitcoind; then
./bin/install-bitcoin-core-linux
fi
bitcoind --version
if [[ ! -e ~/.cargo/env ]]; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
fi
source ~/.cargo/env
rustup update stable
cargo build --release
install --backup target/release/ord /usr/local/bin/ord
id --user bitcoin || useradd --system bitcoin
id --user ord || useradd --system ord
cp deploy/bitcoind.service /etc/systemd/system/
mkdir -p /etc/bitcoin
cp deploy/bitcoin.conf /etc/bitcoin/bitcoin.conf
if [[ ! -e ~/.bitcoin/bitcoin.conf ]]; then
mkdir -p ~/.bitcoin
ln -s /etc/bitcoin/bitcoin.conf ~/.bitcoin/bitcoin.conf
fi
systemctl daemon-reload
systemctl enable bitcoind
systemctl restart bitcoind
case $CHAIN in
main)
COOKIE_FILE_DIR=/var/lib/bitcoind
;;
regtest)
COOKIE_FILE_DIR=/var/lib/bitcoind/regtest
;;
signet)
COOKIE_FILE_DIR=/var/lib/bitcoind/signet
;;
test)
COOKIE_FILE_DIR=/var/lib/bitcoind/testnet3
;;
*)
echo "Unknown chain: $CHAIN"
exit 1
;;
esac
while [[ ! -f $COOKIE_FILE_DIR/.cookie ]]; do
echo "Waiting for bitcoind…"
sleep 1
done
setfacl -m ord:x /var/lib/bitcoind
setfacl -m ord:x $COOKIE_FILE_DIR
setfacl -dm ord:r $COOKIE_FILE_DIR
setfacl -m ord:r $COOKIE_FILE_DIR/.cookie
journalctl --unit ord --vacuum-time 1s
cp deploy/ord.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable ord
systemctl restart ord
while ! curl --fail https://$DOMAIN/status > /dev/null; do
echo "Waiting for ord at https://$DOMAIN/status…"
sleep 1
done

21
docs/book.toml Normal file
View File

@@ -0,0 +1,21 @@
[book]
title = "Ordinal Theory Handbook"
language = "en"
src = "src"
[build]
build-dir = "build"
create-missing = false
extra-watch-dirs = ["po"]
[output.html]
cname = "docs.ordinals.com"
default-theme = "coal"
git-repository-url = "https://github.com/ordinals/ord"
preferred-dark-theme = "coal"
additional-css = ["language-picker.css"]
[output.linkcheck]
[preprocessor.gettext]
after = ["links"]

8
docs/language-picker.css Normal file
View File

@@ -0,0 +1,8 @@
#language-list {
left: auto;
right: 10px;
}
#language-list a {
color: inherit;
}

3249
docs/po/ar.po Normal file

File diff suppressed because it is too large Load Diff

5485
docs/po/de.po Normal file

File diff suppressed because it is too large Load Diff

5430
docs/po/es.po Normal file

File diff suppressed because it is too large Load Diff

5442
docs/po/fil.po Normal file

File diff suppressed because it is too large Load Diff

5559
docs/po/fr.po Normal file

File diff suppressed because it is too large Load Diff

5215
docs/po/hi.po Normal file

File diff suppressed because it is too large Load Diff

6432
docs/po/it.po Normal file

File diff suppressed because it is too large Load Diff

5121
docs/po/ja.po Normal file

File diff suppressed because it is too large Load Diff

5183
docs/po/ko.po Normal file

File diff suppressed because it is too large Load Diff

5348
docs/po/pt.po Normal file

File diff suppressed because it is too large Load Diff

4303
docs/po/ru.po Normal file

File diff suppressed because it is too large Load Diff

5556
docs/po/zh.po Normal file

File diff suppressed because it is too large Load Diff

9
docs/rtl.css Normal file
View File

@@ -0,0 +1,9 @@
/* rtl.css */
body {
direction: rtl;
}
.rtl-header {
direction: rtl;
}

31
docs/src/SUMMARY.md Normal file
View File

@@ -0,0 +1,31 @@
Summary
=======
[Introduction](introduction.md)
- [Overview](overview.md)
- [Digital Artifacts](digital-artifacts.md)
- [Inscriptions](inscriptions.md)
- [Delegate](inscriptions/delegate.md)
- [Metadata](inscriptions/metadata.md)
- [Pointer](inscriptions/pointer.md)
- [Provenance](inscriptions/provenance.md)
- [Recursion](inscriptions/recursion.md)
- [FAQ](faq.md)
- [Contributing](contributing.md)
- [Donate](donate.md)
- [Guides](guides.md)
- [Explorer](guides/explorer.md)
- [Inscriptions](guides/inscriptions.md)
- [Batch Inscribing](guides/batch-inscribing.md)
- [Sat Hunting](guides/sat-hunting.md)
- [Teleburning](guides/teleburning.md)
- [Collecting](guides/collecting.md)
- [Sparrow Wallet](guides/collecting/sparrow-wallet.md)
- [Testing](guides/testing.md)
- [Moderation](guides/moderation.md)
- [Reindexing](guides/reindexing.md)
- [Bounties](bounties.md)
- [Bounty 0: 100,000 sats Claimed!](bounty/0.md)
- [Bounty 1: 200,000 sats Claimed!](bounty/1.md)
- [Bounty 2: 300,000 sats Claimed!](bounty/2.md)
- [Bounty 3: 400,000 sats](bounty/3.md)

19
docs/src/bounties.md Normal file
View File

@@ -0,0 +1,19 @@
Ordinal Bounty Hunting Hints
============================
- The `ord` wallet can send and receive specific satoshis. Additionally,
ordinal theory is extremely simple. A clever hacker should be able to write
code from scratch to manipulate satoshis using ordinal theory in no time.
- For more information about ordinal theory, check out the [FAQ](./faq.md) for
an overview, the
[BIP](https://github.com/ordinals/ord/blob/master/bip.mediawiki) for the
technical details, and the [ord repo](https://github.com/ordinals/ord) for the
`ord` wallet and block explorer.
- Satoshi was the original developer of ordinal theory. However, he knew that
others would consider it heretical and dangerous, so he hid his knowledge,
and it was lost to the sands of time. This potent theory is only now being
rediscovered. You can help by researching rare satoshis.
Good luck and godspeed!

28
docs/src/bounty/0.md Normal file
View File

@@ -0,0 +1,28 @@
Ordinal Bounty 0
================
Criteria
--------
Send a sat whose ordinal number ends with a zero to the submission address:
✅: [1857578125803250](https://ordinals.com/ordinal/1857578125803250)
❌: [1857578125803251](https://ordinals.com/ordinal/1857578125803251)
The sat must be the first sat of the output you send.
Reward
------
100,000 sats
Submission Address
------------------
[`1PE7u4wbDP2RqfKN6geD1bG57v9Gj9FXm3`](https://mempool.space/address/1PE7u4wbDP2RqfKN6geD1bG57v9Gj9FXm3)
Status
------
Claimed by [@count_null](https://twitter.com/rodarmor/status/1560793241473400833)!

27
docs/src/bounty/1.md Normal file
View File

@@ -0,0 +1,27 @@
Ordinal Bounty 1
================
Criteria
--------
The transaction that submits a UTXO containing the oldest sat, i.e., that with
the lowest number, amongst all submitted UTXOs will be judged the winner.
The bounty is open for submissions until block 753984—the first block of
difficulty adjustment period 374. Submissions included in block 753984 or later
will not be considered.
Reward
------
200,000 sats
Submission Address
------------------
[`145Z7PFHyVrwiMWwEcUmDgFbmUbQSU9aap`](https://mempool.space/address/145Z7PFHyVrwiMWwEcUmDgFbmUbQSU9aap)
Status
------
Claimed by [@ordinalsindex](https://twitter.com/rodarmor/status/1569883266508853251)!

28
docs/src/bounty/2.md Normal file
View File

@@ -0,0 +1,28 @@
Ordinal Bounty 2
================
Criteria
--------
Send an <span class=uncommon>uncommon</span> sat to the submission address:
✅: [347100000000000](https://ordinals.com/sat/347100000000000)
❌: [6685000001337](https://ordinals.com/sat/6685000001337)
Confirm that the submission address has not received transactions before submitting your entry. Only the first successful submission will be rewarded.
Reward
------
300,000 sats
Submission Address
------------------
[`1Hyr94uypwWq5CQffaXHvwUMEyBPp3TUZH`](https://mempool.space/address/1Hyr94uypwWq5CQffaXHvwUMEyBPp3TUZH)
Status
------
Claimed by [@utxoset](https://twitter.com/rodarmor/status/1582424455615172608)!

78
docs/src/bounty/3.md Normal file
View File

@@ -0,0 +1,78 @@
Ordinal Bounty 3
================
Criteria
--------
Ordinal bounty 3 has two parts, both of which are based on *ordinal names*.
Ordinal names are a modified base-26 encoding of ordinal numbers. To avoid
locking short names inside the unspendable genesis block coinbase reward,
ordinal names get *shorter* as the ordinal number gets *longer*. The name of
sat 0, the first sat to be mined is `nvtdijuwxlp` and the name of sat
2,099,999,997,689,999, the last sat to be mined, is `a`.
The bounty is open for submissions until block 840000—the first block after the
fourth halving. Submissions included in block 840000 or later will not be
considered.
Both parts use [frequency.tsv](frequency.tsv), a list of words and the number
of times they occur in the [Google Books Ngram
dataset](http://storage.googleapis.com/books/ngrams/books/datasetsv2.html).
filtered to only include the names of sats which will have been mined by the
end of the submission period, that appear at least 5000 times in the corpus.
`frequency.tsv` is a file of tab-separated values. The first column is the
word, and the second is the number of times it appears in the corpus. The
entries are sorted from least-frequently occurring to most-frequently
occurring.
`frequency.tsv` was compiled using [this
program](https://github.com/casey/onegrams).
To search an `ord` wallet for sats with a name in `frequency.tsv`, use the
following [`ord`](https://github.com/ordinals/ord) command:
```
ord wallet sats --tsv frequency.tsv
```
This command requires the sat index, so `--index-sats` must be passed to ord
when first creating the index.
### Part 0
*Rare sats pair best with rare words.*
The transaction that submits the UTXO containing the sat whose name appears
with the lowest number of occurrences in `frequency.tsv` shall be the winner of
part 0.
### Part 1
*Popularity is the font of value.*
The transaction that submits the UTXO containing the sat whose name appears
with the highest number of occurrences in `frequency.tsv` shall be the winner
of part 1.
### Tie Breaking
In the case of a tie, where two submissions occur with the same frequency, the
earlier submission shall be the winner.
Reward
------
- Part 0: 200,000 sats
- Part 1: 200,000 sats
- Total: 400,000 sats
Submission Address
------------------
[`17m5rvMpi78zG8RUpCRd6NWWMJtWmu65kg`](https://mempool.space/address/17m5rvMpi78zG8RUpCRd6NWWMJtWmu65kg)
Status
------
Unclaimed!

12450
docs/src/bounty/frequency.tsv Normal file

File diff suppressed because it is too large Load Diff

84
docs/src/contributing.md Normal file
View File

@@ -0,0 +1,84 @@
Contributing to `ord`
=====================
Suggested Steps
---------------
1. Find an issue you want to work on.
2. Figure out what would be a good first step towards resolving the issue. This
could be in the form of code, research, a proposal, or suggesting that it be
closed, if it's out of date or not a good idea in the first place.
3. Comment on the issue with an outline of your suggested first step, and
asking for feedback. Of course, you can dive in and start writing code or
tests immediately, but this avoids potentially wasted effort, if the issue
is out of date, not clearly specified, blocked on something else, or
otherwise not ready to implement.
4. If the issue requires a code change or bugfix, open a draft PR with tests,
and ask for feedback. This makes sure that everyone is on the same page
about what needs to be done, or what the first step in solving the issue
should be. Also, since tests are required, writing the tests first makes it
easy to confirm that the change can be tested easily.
5. Mash the keyboard randomly until the tests pass, and refactor until the code
is ready to submit.
6. Mark the PR as ready to review.
7. Revise the PR as needed.
8. And finally, mergies!
Start small
-----------
Small changes will allow you to make an impact
quickly, and if you take the wrong tack, you won't have wasted much time.
Ideas for small issues:
- Add a new test or test case that increases test coverage
- Add or improve documentation
- Find an issue that needs more research, and do that research and summarize it
in a comment
- Find an out-of-date issue and comment that it can be closed
- Find an issue that shouldn't be done, and provide constructive feedback
detailing why you think that is the case
Merge early and often
---------------------
Break up large tasks into multiple smaller steps that individually make
progress. If there's a bug, you can open a PR that adds a failing ignored test.
This can be merged, and the next step can be to fix the bug and unignore the
test. Do research or testing, and report on your results. Break a feature into
small sub-features, and implement them one at a time.
Figuring out how to break down a larger PR into smaller PRs where each can be
merged is an art form well-worth practicing. The hard part is that each PR must
itself be an improvement.
I strive to follow this advice myself, and am always better off when I do.
Small changes are fast to write, review, and merge, which is much more fun than
laboring over a single giant PR that takes forever to write, review, and merge.
Small changes don't take much time, so if you need to stop working on a small
change, you won't have wasted much time as compared to a larger change that
represents many hours of work. Getting a PR in quickly improves the project a
little bit immediately, instead of having to wait a long time for larger
improvement. Small changes are less likely to accumulate merge conflict. As the
Athenians said: *The fast commit what they will, the slow merge what they
must.*
Get help
--------
If you're stuck for more than 15 minutes, ask for help, like a Rust Discord,
Stack Exchange, or in a project issue or discussion.
Practice hypothesis-driven debugging
------------------------------------
Formulate a hypothesis as to what is causing the problem. Figure out how to
test that hypothesis. Perform that tests. If it works, great, you fixed the
issue or now you know how to fix the issue. If not, repeat with a new
hypothesis.
Pay attention to error messages
-------------------------------
Read all error messages and don't tolerate warnings.

View File

@@ -0,0 +1,41 @@
Digital Artifacts
=================
Imagine a physical artifact. A rare coin, say, held safe for untold years in
the dark, secret clutch of a Viking hoard, now dug from the earth by your
grasping hands. It…
…has an owner. You. As long as you keep it safe, nobody can take it from you.
…is complete. It has no missing parts.
…can only be changed by you. If you were a trader, and you made your way to
18th century China, none but you could stamp it with your chop-mark.
…can only be disposed of by you. The sale, trade, or gift is yours to make,
to whomever you wish.
What are digital artifacts? Simply put, they are the digital equivalent of
physical artifacts.
For a digital thing to be a digital artifact, it must be like that coin of
yours:
- Digital artifacts can have owners. A number is not a digital artifact,
because nobody can own it.
- Digital artifacts are complete. An NFT that points to off-chain content
on IPFS or Arweave is incomplete, and thus not a digital artifact.
- Digital artifacts are permissionless. An NFT which cannot be sold without
paying a royalty is not permissionless, and thus not a digital artifact.
- Digital artifacts are uncensorable. Perhaps you can change a database entry
on a centralized ledger today, but maybe not tomorrow, and thus one cannot be
a digital artifact.
- Digital artifacts are immutable. An NFT with an upgrade key is not a digital
artifact.
The definition of a digital artifact is intended to reflect what NFTs *should*
be, sometimes are, and what inscriptions *always* are, by their very nature.

20
docs/src/donate.md Normal file
View File

@@ -0,0 +1,20 @@
Donate
======
Ordinals is open-source and community funded. The current lead maintainer of
`ord` is [raphjaph](https://github.com/raphjaph/). Raph's work on `ord` is
entirely funded by donations. If you can, please consider donating!
The donation address for Bitcoin is
[bc1q8kt9pyd6r27k2840l8g5d7zshz3cg9v6rfda0m248lva3ve5072q3sxelt](https://mempool.space/address/bc1q8kt9pyd6r27k2840l8g5d7zshz3cg9v6rfda0m248lva3ve5072q3sxelt). The donation address for inscriptions is [bc1qn3map8m9hmk5jyqdkkwlwvt335g94zvxwd9aql7q3vdkdw9r5eyqvlvec0](https://mempool.space/address/bc1qn3map8m9hmk5jyqdkkwlwvt335g94zvxwd9aql7q3vdkdw9r5eyqvlvec0).
Both addresses are in a 2 of 4 multisig wallet with keys held by
[raphjaph](https://twitter.com/raphjaph),
[erin](https://twitter.com/realizingerin),
[rodarmor](https://twitter.com/rodarmor), and
[ordinally](https://twitter.com/veryordinally).
Donations received will go towards funding maintenance and development of `ord`,
as well as hosting costs for [ordinals.com](https://ordinals.com).
Thank you for donating!

355
docs/src/faq.md Normal file
View File

@@ -0,0 +1,355 @@
Ordinal Theory FAQ
==================
What is ordinal theory?
-----------------------
Ordinal theory is a protocol for assigning serial numbers to satoshis, the
smallest subdivision of a bitcoin, and tracking those satoshis as they are
spent by transactions.
These serial numbers are large numbers, like this 804766073970493. Every
satoshi, which is ¹⁄₁₀₀₀₀₀₀₀₀ of a bitcoin, has an ordinal number.
Does ordinal theory require a side chain, a separate token, or changes to Bitcoin?
----------------------------------------------------------------------------------
Nope! Ordinal theory works right now, without a side chain, and the only token
needed is bitcoin itself.
What is ordinal theory good for?
--------------------------------
Collecting, trading, and scheming. Ordinal theory assigns identities to
individual satoshis, allowing them to be individually tracked and traded, as
curios and for numismatic value.
Ordinal theory also enables inscriptions, a protocol for attaching arbitrary
content to individual satoshis, turning them into bitcoin-native digital
artifacts.
How does ordinal theory work?
-----------------------------
Ordinal numbers are assigned to satoshis in the order in which they are mined.
The first satoshi in the first block has ordinal number 0, the second has
ordinal number 1, and the last satoshi of the first block has ordinal number
4,999,999,999.
Satoshis live in outputs, but transactions destroy outputs and create new ones,
so ordinal theory uses an algorithm to determine how satoshis hop from the
inputs of a transaction to its outputs.
Fortunately, that algorithm is very simple.
Satoshis transfer in first-in-first-out order. Think of the inputs to a
transaction as being a list of satoshis, and the outputs as a list of slots,
waiting to receive a satoshi. To assign input satoshis to slots, go through
each satoshi in the inputs in order, and assign each to the first available
slot in the outputs.
Let's imagine a transaction with three inputs and two outputs. The inputs are
on the left of the arrow and the outputs are on the right, all labeled with
their values:
```
[2] [1] [3] → [4] [2]
```
Now let's label the same transaction with the ordinal numbers of the satoshis
that each input contains, and question marks for each output slot. Ordinal
numbers are large, so let's use letters to represent them:
```
[a b] [c] [d e f] → [? ? ? ?] [? ?]
```
To figure out which satoshi goes to which output, go through the input satoshis
in order and assign each to a question mark:
```
[a b] [c] [d e f] → [a b c d] [e f]
```
What about fees, you might ask? Good question! Let's imagine the same
transaction, this time with a two satoshi fee. Transactions with fees send more
satoshis in the inputs than are received by the outputs, so to make our
transaction into one that pays fees, we'll remove the second output:
```
[2] [1] [3] → [4]
```
The satoshis <var>e</var> and <var>f</var> now have nowhere to go in the
outputs:
```
[a b] [c] [d e f] → [a b c d]
```
So they go to the miner who mined the block as fees. [The
BIP](https://github.com/ordinals/ord/blob/master/bip.mediawiki) has the details,
but in short, fees paid by transactions are treated as extra inputs to the
coinbase transaction, and are ordered how their corresponding transactions are
ordered in the block. The coinbase transaction of the block might look like
this:
```
[SUBSIDY] [e f] → [SUBSIDY e f]
```
Where can I find the nitty-gritty details?
------------------------------------------
[The BIP!](https://github.com/ordinals/ord/blob/master/bip.mediawiki)
Why are sat inscriptions called "digital artifacts" instead of "NFTs"?
----------------------------------------------------------------------
An inscription is an NFT, but the term "digital artifact" is used instead,
because it's simple, suggestive, and familiar.
The phrase "digital artifact" is highly suggestive, even to someone who has
never heard the term before. In comparison, NFT is an acronym, and doesn't
provide any indication of what it means if you haven't heard the term before.
Additionally, "NFT" feels like financial terminology, and the both word
"fungible" and sense of the word "token" as used in "NFT" is uncommon outside
of financial contexts.
How do sat inscriptions compare to…
-----------------------------------
### Ethereum NFTs?
*Inscriptions are always immutable.*
There is simply no way to for the creator of an inscription, or the owner of an
inscription, to modify it after it has been created.
Ethereum NFTs *can* be immutable, but many are not, and can be changed or
deleted by the NFT contract owner.
In order to make sure that a particular Ethereum NFT is immutable, the contract
code must be audited, which requires detailed knowledge of the EVM and Solidity
semantics.
It is very hard for a non-technical user to determine whether or not a given
Ethereum NFT is mutable or immutable, and Ethereum NFT platforms make no effort
to distinguish whether an NFT is mutable or immutable, and whether the contract
source code is available and has been audited.
*Inscription content is always on-chain.*
There is no way for an inscription to refer to off-chain content. This makes
inscriptions more durable, because content cannot be lost, and scarcer, because
inscription creators must pay fees proportional to the size of the content.
Some Ethereum NFT content is on-chain, but much is off-chain, and is stored on
platforms like IPFS or Arweave, or on traditional, fully centralized web
servers. Content on IPFS is not guaranteed to continue to be available, and
some NFT content stored on IPFS has already been lost. Platforms like Arweave
rely on weak economic assumptions, and will likely fail catastrophically when
these economic assumptions are no longer met. Centralized web servers may
disappear at any time.
It is very hard for a non-technical user to determine where the content of a
given Ethereum NFT is stored.
*Inscriptions are much simpler.*
Ethereum NFTs depend on the Ethereum network and virtual machine, which are
highly complex, constantly changing, and which introduce changes via
backwards-incompatible hard forks.
Inscriptions, on the other hand, depend on the Bitcoin blockchain, which is
relatively simple and conservative, and which introduces changes via
backwards-compatible soft forks.
*Inscriptions are more secure.*
Inscriptions inherit Bitcoin's transaction model, which allow a user to see
exactly which inscriptions are being transferred by a transaction before they
sign it. Inscriptions can be offered for sale using partially signed
transactions, which don't require allowing a third party, such as an exchange
or marketplace, to transfer them on the user's behalf.
By comparison, Ethereum NFTs are plagued with end-user security
vulnerabilities. It is commonplace to blind-sign transactions, grant
third-party apps unlimited permissions over a user's NFTs, and interact with
complex and unpredictable smart contracts. This creates a minefield of hazards
for Ethereum NFT users which are simply not a concern for ordinal theorists.
*Inscriptions are scarcer.*
Inscriptions require bitcoin to mint, transfer, and store. This seems like a
downside on the surface, but the raison d'etre of digital artifacts is to be
scarce and thus valuable.
Ethereum NFTs, on the other hand, can be minted in virtually unlimited
qualities with a single transaction, making them inherently less scarce, and
thus, potentially less valuable.
*Inscriptions do not pretend to support on-chain royalties.*
On-chain royalties are a good idea in theory but not in practice. Royalty
payment cannot be enforced on-chain without complex and invasive restrictions.
The Ethereum NFT ecosystem is currently grappling with confusion around
royalties, and is collectively coming to grips with the reality that on-chain
royalties, which were messaged to artists as an advantage of NFTs, are not
possible, while platforms race to the bottom and remove royalty support.
Inscriptions avoid this situation entirely by making no false promises of
supporting royalties on-chain, thus avoiding the confusion, chaos, and
negativity of the Ethereum NFT situation.
*Inscriptions unlock new markets.*
Bitcoin's market capitalization and liquidity are greater than Ethereum's by a
large margin. Much of this liquidity is not available to Ethereum NFTs, since
many Bitcoiners prefer not to interact with the Ethereum ecosystem due to
concerns related to simplicity, security, and decentralization.
Such Bitcoiners may be more interested in inscriptions than Ethereum NFTs,
unlocking new classes of collector.
*Inscriptions have a richer data model.*
Inscriptions consist of a content type, also known as a MIME type, and content,
which is an arbitrary byte string. This is the same data model used by the web,
and allows inscription content to evolve with the web, and come to support any
kind of content supported by web browsers, without requiring changes to the
underlying protocol.
### RGB and Taro assets?
RGB and Taro are both second-layer asset protocols built on Bitcoin. Compared
to inscriptions, they are much more complicated, but much more featureful.
Ordinal theory has been designed from the ground up for digital artifacts,
whereas the primary use-case of RGB and Taro are fungible tokens, so the user
experience for inscriptions is likely to be simpler and more polished than the
user experience for RGB and Taro NFTs.
RGB and Taro both store content off-chain, which requires additional
infrastructure, and which may be lost. By contrast, inscription content is
stored on-chain, and cannot be lost.
Ordinal theory, RGB, and Taro are all very early, so this is speculation, but
ordinal theory's focus may give it the edge in terms of features for digital
artifacts, including a better content model, and features like globally unique
symbols.
### Counterparty assets?
Counterparty has its own token, XCP, which is required for some functionality,
which makes most bitcoiners regard it as an altcoin, and not an extension or
second layer for bitcoin.
Ordinal theory has been designed from the ground up for digital artifacts,
whereas Counterparty was primarily designed for financial token issuance.
Inscriptions for…
-----------------
### Artists
*Inscriptions are on Bitcoin.* Bitcoin is the digital currency with the highest
status and greatest chance of long-term survival. If you want to guarantee that
your art survives into the future, there is no better way to publish it than as
inscriptions.
*Cheaper on-chain storage.* At $20,000 per BTC and the minimum relay fee of 1
satoshi per vbyte, publishing inscription content costs $50 per 1 million
bytes.
*Inscriptions are early!* Inscriptions are still in development, and have not
yet launched on mainnet. This gives you an opportunity to be an early adopter,
and explore the medium as it evolves.
*Inscriptions are simple.* Inscriptions do not require writing or understanding
smart contracts.
*Inscriptions unlock new liquidity.* Inscriptions are more accessible and
appealing to bitcoin holders, unlocking an entirely new class of collector.
*Inscriptions are designed for digital artifacts.* Inscriptions are designed
from the ground up to support NFTs, and feature a better data model, and
features like globally unique symbols and enhanced provenance.
*Inscriptions do not support on-chain royalties.* This is negative, but only
depending on how you look at it. On-chain royalties have been a boon for
creators, but have also created a huge amount of confusion in the Ethereum NFT
ecosystem. The ecosystem now grapples with this issue, and is engaged in a
race to the bottom, towards a royalties-optional future. Inscriptions have no
support for on-chain royalties, because they are technically infeasible. If you
choose to create inscriptions, there are many ways you can work around this
limitation: withhold a portion of your inscriptions for future sale, to benefit
from future appreciation, or perhaps offer perks for users who respect optional
royalties.
### Collectors
*Inscriptions are simple, clear, and have no surprises.* They are always
immutable and on-chain, with no special due diligence required.
*Inscriptions are on Bitcoin.* You can verify the location and properties of
inscriptions easily with Bitcoin full node that you control.
### Bitcoiners
Let me begin this section by saying: the most important thing that the Bitcoin
network does is decentralize money. All other use-cases are secondary,
including ordinal theory. The developers of ordinal theory understand and
acknowledge this, and believe that ordinal theory helps, at least in a small
way, Bitcoin's primary mission.
Unlike many other things in the altcoin space, digital artifacts have merit.
There are, of course, a great deal of NFTs that are ugly, stupid, and
fraudulent. However, there are many that are fantastically creative, and
creating and collecting art has been a part of the human story since its
inception, and predates even trade and money, which are also ancient
technologies.
Bitcoin provides an amazing platform for creating and collecting digital
artifacts in a secure, decentralized way, that protects users and artists in
the same way that it provides an amazing platform for sending and receiving
value, and for all the same reasons.
Ordinals and inscriptions increase demand for Bitcoin block space, which
increase Bitcoin's security budget, which is vital for safeguarding Bitcoin's
transition to a fee-dependent security model, as the block subsidy is halved
into insignificance.
Inscription content is stored on-chain, and thus the demand for block space for
use in inscriptions is unlimited. This creates a buyer of last resort for *all*
Bitcoin block space. This will help support a robust fee market, which ensures
that Bitcoin remains secure.
Inscriptions also counter the narrative that Bitcoin cannot be extended or used
for new use-cases. If you follow projects like DLCs, Fedimint, Lightning, Taro,
and RGB, you know that this narrative is false, but inscriptions provide a
counter argument which is easy to understand, and which targets a popular and
proven use case, NFTs, which makes it highly legible.
If inscriptions prove, as the authors hope, to be highly sought after digital
artifacts with a rich history, they will serve as a powerful hook for Bitcoin
adoption: come for the fun, rich art, stay for the decentralized digital money.
Inscriptions are an extremely benign source of demand for block space. Unlike,
for example, stablecoins, which potentially give large stablecoin issuers
influence over the future of Bitcoin development, or DeFi, which might
centralize mining by introducing opportunities for MEV, digital art and
collectables on Bitcoin, are unlikely to produce individual entities with
enough power to corrupt Bitcoin. Art is decentralized.
Inscription users and service providers are incentivized to run Bitcoin full
nodes, to publish and track inscriptions, and thus throw their economic weight
behind the honest chain.
Ordinal theory and inscriptions do not meaningfully affect Bitcoin's
fungibility. Bitcoin users can ignore both and be unaffected.
We hope that ordinal theory strengthens and enriches bitcoin, and gives it
another dimension of appeal and functionality, enabling it more effectively
serve its primary use case as humanity's decentralized store of value.

5
docs/src/guides.md Normal file
View File

@@ -0,0 +1,5 @@
Ordinal Theory Guides
=====================
See the table of contents for a list of guides, including a guide to the
explorer, a guide for sat hunters, and a guide to inscriptions.

View File

@@ -0,0 +1,22 @@
Batch Inscribing
================
Multiple inscriptions can be created inscriptions at the same time using the
[pointer field](./../inscriptions/pointer.md). This is especially helpful for
collections, or other cases when multiple inscriptions should share the same
parent, since the parent can passed into a reveal transaction that creates
multiple children.
To create a batch inscription using a batchfile in `batch.yaml`, run the
following command:
```bash
ord wallet inscribe --fee-rate 21 --batch batch.yaml
```
Example `batch.yaml`
--------------------
```yaml
{{#include ../../../batch.yaml}}
```

View File

@@ -0,0 +1,23 @@
Collecting
==========
Currently, [ord](https://github.com/ordinals/ord/) is the only wallet supporting
sat-control and sat-selection, which are required to safely store and send rare
sats and inscriptions, hereafter ordinals.
The recommended way to send, receive, and store ordinals is with `ord`, but if
you are careful, it is possible to safely store, and in some cases send,
ordinals with other wallets.
As a general note, receiving ordinals in an unsupported wallet is not
dangerous. Ordinals can be sent to any bitcoin address, and are safe as long as
the UTXO that contains them is not spent. However, if that wallet is then used
to send bitcoin, it may select the UTXO containing the ordinal as an input, and
send the inscription or spend it to fees.
A [guide](./collecting/sparrow-wallet.md) to creating an `ord`-compatible wallet with [Sparrow Wallet](https://sparrowwallet.com/), is available
in this handbook.
Please note that if you follow this guide, you should not use the wallet you
create to send BTC, unless you perform manual coin-selection to avoid sending
ordinals.

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View File

@@ -0,0 +1,183 @@
Collecting Inscriptions and Ordinals with Sparrow Wallet
=====================
Users who cannot or have not yet set up the [ord](https://github.com/ordinals/ord) wallet can receive inscriptions and ordinals with alternative bitcoin wallets, as long as they are _very_ careful about how they spend from that wallet.
This guide gives some basic steps on how to create a wallet with [Sparrow Wallet](https://sparrowwallet.com/) which is compatible with `ord` and can be later imported into `ord`
## ⚠️⚠️ Warning!! ⚠️⚠️
As a general rule if you take this approach, you should use this wallet with the Sparrow software as a receive-only wallet.
Do not spend any satoshis from this wallet unless you are sure you know what you are doing. You could very easily inadvertently lose access to your ordinals and inscriptions if you don't heed this warning.
## Wallet Setup & Receiving
Download the Sparrow Wallet from the [releases page](https://sparrowwallet.com/download/) for your particular operating system.
Select `File -> New Wallet` and create a new wallet called `ord`.
![](images/wallet_setup_01.png)
Change the `Script Type` to `Taproot (P2TR)` and select the `New or Imported Software Wallet` option.
![](images/wallet_setup_02.png)
Select `Use 12 Words` and then click `Generate New`. Leave the passphrase blank.
![](images/wallet_setup_03.png)
A new 12 word BIP39 seed phrase will be generated for you. Write this down somewhere safe as this is your backup to get access to your wallet. NEVER share or show this seed phrase to anyone else.
Once you have written down the seed phrase click `Confirm Backup`.
![](images/wallet_setup_04.png)
Re-enter the seed phrase which you wrote down, and then click `Create Keystore`.
![](images/wallet_setup_05.png)
Click `Import Keystore`.
![](images/wallet_setup_06.png)
Click `Apply`. Add a password for the wallet if you want to.
![](images/wallet_setup_07.png)
You now have a wallet which is compatible with `ord`, and can be imported into `ord` using the BIP39 Seed Phrase. To receive ordinals or inscriptions, click on the `Receive` tab and copy a new address.
Each time you want to receive you should use a brand-new address, and not re-use existing addresses.
Note that bitcoin is different to some other blockchain wallets, in that this wallet can generate an unlimited number of new addresses. You can generate a new address by clicking on the `Get Next Address` button. You can see all of your addresses in the `Addresses` tab of the app.
You can add a label to each address, so you can keep track of what it was used for.
![](images/wallet_setup_08.png)
## Validating / Viewing Received Inscriptions
Once you have received an inscription you will see a new transaction in the `Transactions` tab of Sparrow, as well as a new UTXO in the `UTXOs` tab.
Initially this transaction may have an "Unconfirmed" status, and you will need to wait for it to be mined into a bitcoin block before it is fully received.
![](images/validating_viewing_01.png)
To track the status of your transaction you can right-click on it, select `Copy Transaction ID` and then paste that transaction id into [mempool.space](https://mempool.space).
![](images/validating_viewing_02.png)
Once the transaction has confirmed, you can validate and view your inscription by heading over to the `UTXOs` tab, finding the UTXO you want to check, right-clicking on the `Output` and selecting `Copy Transaction Output`. This transaction output id can then be pasted into the [ordinals.com](https://ordinals.com) search.
## Freezing UTXO's
As explained above, each of your inscriptions is stored in an Unspent Transaction Output (UTXO). You want to be very careful not to accidentally spend your inscriptions, and one way to make it harder for this to happen is to freeze the UTXO.
To do this, go to the `UTXOs` tab, find the UTXO you want to freeze, right-click on the `Output` and select `Freeze UTXO`.
This UTXO (Inscription) is now un-spendable within the Sparrow Wallet until you unfreeze it.
## Importing into `ord` wallet
For details on setting up Bitcoin Core and the `ord` wallet check out the [Inscriptions Guide](../inscriptions.md)
When setting up `ord`, instead of running `ord wallet create` to create a brand-new wallet, you can import your existing wallet using `ord wallet restore "BIP39 SEED PHRASE"` using the seed phrase you generated with Sparrow Wallet.
There is currently a [bug](https://github.com/ordinals/ord/issues/1589) which causes an imported wallet to not be automatically rescanned against the blockchain. To work around this you will need to manually trigger a rescan using the bitcoin core cli:
`bitcoin-cli -rpcwallet=ord rescanblockchain 767430`
You can then check your wallet's inscriptions using `ord wallet inscriptions`
Note that if you have previously created a wallet with `ord`, then you will already have a wallet with the default name, and will need to give your imported wallet a different name. You can use the `--wallet` parameter in all `ord` commands to reference a different wallet, eg:
`ord --wallet ord_from_sparrow wallet restore "BIP39 SEED PHRASE"`
`ord --wallet ord_from_sparrow wallet inscriptions`
`bitcoin-cli -rpcwallet=ord_from_sparrow rescanblockchain 767430`
## Sending inscriptions with Sparrow Wallet
#### ⚠️⚠️ Warning ⚠️⚠️
While it is highly recommended that you set up a bitcoin core node and run the `ord` software, there are certain limited ways you can send inscriptions out of Sparrow Wallet in a safe way. Please note that this is not recommended, and you should only do this if you fully understand what you are doing.
Using the `ord` software will remove much of the complexity we are describing here, as it is able to automatically and safely handle sending inscriptions in an easy way.
#### ⚠️⚠️ Additional Warning ⚠️⚠️
Don't use your sparrow inscriptions wallet to do general sends of non-inscription bitcoin. You can setup a separate wallet in sparrow if you need to do normal bitcoin transactions, and keep your inscriptions wallet separate.
#### Bitcoin's UTXO model
Before sending any transaction it's important that you have a good mental model for bitcoin's Unspent Transaction Output (UTXO) system. The way Bitcoin works is fundamentally different to many other blockchains such as Ethereum. In Ethereum generally you have a single address in which you store ETH, and you cannot differentiate between any of the ETH - it is just all a single value of the total amount in that address. Bitcoin works very differently in that we generate a new address in the wallet for each receive, and every time you receive sats to an address in your wallet you are creating a new UTXO. Each UTXO can be seen and managed individually. You can select specific UTXO's which you want to spend, and you can choose not to spend certain UTXO's.
Some Bitcoin wallets do not expose this level of detail, and they just show you a single summed up value of all the bitcoin in your wallet. However, when sending inscriptions it is important that you use a wallet like Sparrow which allows for UTXO control.
#### Inspecting your inscription before sending
Like we have previously described inscriptions are inscribed onto sats, and sats are stored within UTXOs. UTXO's are a collection of satoshis with some particular value of the number of satoshis (the output value). Usually (but not always) the inscription will be inscribed on the first satoshi in the UTXO.
When inspecting your inscription before sending the main thing you will want to check is which satoshi in the UTXO your inscription is inscribed on.
To do this, you can follow the [Validating / Viewing Received Inscriptions](./sparrow-wallet.md#validating--viewing-received-inscriptions) described above to find the inscription page for your inscription on ordinals.com
There you will find some metadata about your inscription which looks like the following:
![](images/sending_01.png)
There is a few of important things to check here:
* The `output` identifier matches the identifier of the UTXO you are going to send
* The `offset` of the inscription is `0` (this means that the inscription is located on the first sat in the UTXO)
* the `output_value` has enough sats to cover the transaction fee (postage) for sending the transaction. The exact amount you will need depends on the fee rate you will select for the transaction
If all of the above are true for your inscription, it should be safe for you to send it using the method below.
⚠️⚠️ Be very careful sending your inscription particularly if the `offset` value is not `0`. It is not recommended to use this method if that is the case, as doing so you could accidentally send your inscription to a bitcoin miner unless you know what you are doing.
#### Sending your inscription
To send an inscription navigate to the `UTXOs` tab, and find the UTXO which you previously validated contains your inscription.
If you previously froze the UXTO you will need to right-click on it and unfreeze it.
Select the UTXO you want to send, and ensure that is the _only_ UTXO is selected. You should see `UTXOs 1/1` in the interface. Once you are sure this is the case you can hit `Send Selected`.
![](images/sending_02.png)
You will then be presented with the transaction construction interface. There is a few things you need to check here to make sure that this is a safe send:
* The transaction should have only 1 input, and this should be the UTXO with the label you want to send
* The transaction should have only 1 output, which is the address/label where you want to send the inscription
If your transaction looks any different, for example you have multiple inputs, or multiple outputs then this may not be a safe transfer of your inscription, and you should abandon sending until you understand more, or can import into the `ord` wallet.
You should set an appropriate transaction fee, Sparrow will usually recommend a reasonable one, but you can also check [mempool.space](https://mempool.space) to see what the recommended fee rate is for sending a transaction.
You should add a label for the recipient address, a label like `alice address for inscription #123` would be ideal.
Once you have checked the transaction is a safe transaction using the checks above, and you are confident to send it you can click `Create Transaction`.
![](images/sending_03.png)
Here again you can double check that your transaction looks safe, and once you are confident you can click `Finalize Transaction for Signing`.
![](images/sending_04.png)
Here you can triple check everything before hitting `Sign`.
![](images/sending_05.png)
And then actually you get very very last chance to check everything before hitting `Broadcast Transaction`. Once you broadcast the transaction it is sent to the bitcoin network, and starts being propagated into the mempool.
![](images/sending_06.png)
If you want to track the status of your transaction you can copy the `Transaction Id (Txid)` and paste that into [mempool.space](https://mempool.space)
Once the transaction has confirmed you can check the inscription page on [ordinals.com](https://ordinals.com) to validate that it has moved to the new output location and address.
## Troubleshooting
#### Sparrow wallet is not showing a transaction/UTXO, but I can see it on mempool.space!
Make sure that your wallet is connected to a bitcoin node. To validate this, head into the `Preferences`-> `Server` settings, and click `Edit Existing Connection`.
![](images/troubleshooting_01.png)
From there you can select a node and click `Test Connection` to validate that Sparrow is able to connect successfully.
![](images/troubleshooting_02.png)

119
docs/src/guides/explorer.md Normal file
View File

@@ -0,0 +1,119 @@
Ordinal Explorer
================
The `ord` binary includes a block explorer. We host an instance of the block
explorer on mainnet at [ordinals.com](https://ordinals.com), and on signet at
[signet.ordinals.com](https://signet.ordinals.com).
### Running The Explorer
The server can be run locally with:
`ord server`
To specify a port add the `--http-port` flag:
`ord server --http-port 8080`
To enable the JSON-API endpoints add the `--enable-json-api` or `-j` flag (see
[here](#json-api) for more info):
`ord server --enable-json-api`
To test how your inscriptions will look you can run:
`ord preview <FILE1> <FILE2> ...`
Search
------
The search box accepts a variety of object representations.
### Blocks
Blocks can be searched by hash, for example, the genesis block:
[000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f](https://ordinals.com/search/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f)
### Transactions
Transactions can be searched by hash, for example, the genesis block coinbase
transaction:
[4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b](https://ordinals.com/search/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b)
### Outputs
Transaction outputs can be searched by outpoint, for example, the only output of
the genesis block coinbase transaction:
[4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0](https://ordinals.com/search/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0)
### Sats
Sats can be searched by integer, their position within the entire bitcoin
supply:
[2099994106992659](https://ordinals.com/search/2099994106992659)
By decimal, their block and offset within that block:
[481824.0](https://ordinals.com/search/481824.0)
By degree, their cycle, blocks since the last halving, blocks since the last
difficulty adjustment, and offset within their block:
[1°00″0‴](https://ordinals.com/search/1°00″0‴)
By name, their base 26 representation using the letters "a" through "z":
[ahistorical](https://ordinals.com/search/ahistorical)
Or by percentile, the percentage of bitcoin's supply that has been or will have
been issued when they are mined:
[100%](https://ordinals.com/search/100%)
JSON-API
--------
You can run `ord server` with the `--enable-json-api` flag to access endpoints that
return JSON instead of HTML if you set the HTTP `Accept: application/json`
header. The structure of these objects closely follows
what is shown in the HTML. These endpoints are:
- `/inscription/<INSCRIPTION_ID>`
- `/inscriptions`
- `/inscriptions/block/<BLOCK_HEIGHT>`
- `/inscriptions/block/<BLOCK_HEIGHT>/<PAGE_INDEX>`
- `/inscriptions/<FROM>`
- `/inscriptions/<FROM>/<N>`
- `/output/<OUTPOINT>`
- `/output/<OUTPOINT>`
- `/sat/<SAT>`
To get a list of the latest 100 inscriptions you would do:
```
curl -s -H "Accept: application/json" 'http://0.0.0.0:80/inscriptions'
```
To see information about a UTXO, which includes inscriptions inside it, do:
```
curl -s -H "Accept: application/json" 'http://0.0.0.0:80/output/bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed:0'
```
Which returns:
```
{
"value": 10000,
"script_pubkey": "OP_PUSHNUM_1 OP_PUSHBYTES_32 156cc4878306157720607cdcb4b32afa4cc6853868458d7258b907112e5a434b",
"address": "bc1pz4kvfpurqc2hwgrq0nwtfve2lfxvdpfcdpzc6ujchyr3ztj6gd9sfr6ayf",
"transaction": "bc4c30829a9564c0d58e6287195622b53ced54a25711d1b86be7cd3a70ef61ed",
"sat_ranges": null,
"inscriptions": [
"6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0"
]
}
```

View File

@@ -0,0 +1,316 @@
Ordinal Inscription Guide
=========================
Individual sats can be inscribed with arbitrary content, creating
Bitcoin-native digital artifacts that can be held in a Bitcoin wallet and
transferred using Bitcoin transactions. Inscriptions are as durable, immutable,
secure, and decentralized as Bitcoin itself.
Working with inscriptions requires a Bitcoin full node, to give you a view of
the current state of the Bitcoin blockchain, and a wallet that can create
inscriptions and perform sat control when constructing transactions to send
inscriptions to another wallet.
Bitcoin Core provides both a Bitcoin full node and wallet. However, the Bitcoin
Core wallet cannot create inscriptions and does not perform sat control.
This requires [`ord`](https://github.com/ordinals/ord), the ordinal utility. `ord`
doesn't implement its own wallet, so `ord wallet` subcommands interact with
Bitcoin Core wallets.
This guide covers:
1. Installing Bitcoin Core
2. Syncing the Bitcoin blockchain
3. Creating a Bitcoin Core wallet
4. Using `ord wallet receive` to receive sats
5. Creating inscriptions with `ord wallet inscribe`
6. Sending inscriptions with `ord wallet send`
7. Receiving inscriptions with `ord wallet receive`
8. Batch inscribing with `ord wallet inscribe --batch`
Getting Help
------------
If you get stuck, try asking for help on the [Ordinals Discord
Server](https://discord.com/invite/87cjuz4FYg), or checking GitHub for relevant
[issues](https://github.com/ordinals/ord/issues) and
[discussions](https://github.com/ordinals/ord/discussions).
Installing Bitcoin Core
-----------------------
Bitcoin Core is available from [bitcoincore.org](https://bitcoincore.org/) on
the [download page](https://bitcoincore.org/en/download/).
Making inscriptions requires Bitcoin Core 24 or newer.
This guide does not cover installing Bitcoin Core in detail. Once Bitcoin Core
is installed, you should be able to run `bitcoind -version` successfully from
the command line. Do *NOT* use `bitcoin-qt`.
Configuring Bitcoin Core
------------------------
`ord` requires Bitcoin Core's transaction index and rest interface.
To configure your Bitcoin Core node to maintain a transaction
index, add the following to your `bitcoin.conf`:
```
txindex=1
```
Or, run `bitcoind` with `-txindex`:
```
bitcoind -txindex
```
Details on creating or modifying your `bitcoin.conf` file can be found
[here](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md).
Syncing the Bitcoin Blockchain
------------------------------
To sync the chain, run:
```
bitcoind -txindex
```
…and leave it running until `getblockcount`:
```
bitcoin-cli getblockcount
```
agrees with the block count on a block explorer like [the mempool.space block
explorer](https://mempool.space/). `ord` interacts with `bitcoind`, so you
should leave `bitcoind` running in the background when you're using `ord`.
The blockchain takes about 600GB of disk space. If you have an external drive
you want to store blocks on, use the configuration option
`blocksdir=<external_drive_path>`. This is much simpler than using the
`datadir` option because the cookie file will still be in the default location
for `bitcoin-cli` and `ord` to find.
Troubleshooting
---------------
Make sure you can access `bitcoind` with `bitcoin-cli -getinfo` and that it is
fully synced.
If `bitcoin-cli -getinfo` returns `Could not connect to the server`, `bitcoind`
is not running.
Make sure `rpcuser`, `rpcpassword`, or `rpcauth` are *NOT* set in your
`bitcoin.conf` file. `ord` requires using cookie authentication. Make sure there
is a file `.cookie` in your bitcoin data directory.
If `bitcoin-cli -getinfo` returns `Could not locate RPC credentials`, then you
must specify the cookie file location.
If you are using a custom data directory (specifying the `datadir` option),
then you must specify the cookie location like
`bitcoin-cli -rpccookiefile=<your_bitcoin_datadir>/.cookie -getinfo`.
When running `ord` you must specify the cookie file location with
`--cookie-file=<your_bitcoin_datadir>/.cookie`.
Make sure you do *NOT* have `disablewallet=1` in your `bitcoin.conf` file. If
`bitcoin-cli listwallets` returns `Method not found` then the wallet is disabled
and you won't be able to use `ord`.
Make sure `txindex=1` is set. Run `bitcoin-cli getindexinfo` and it should
return something like
```json
{
"txindex": {
"synced": true,
"best_block_height": 776546
}
}
```
If it only returns `{}`, `txindex` is not set.
If it returns `"synced": false`, `bitcoind` is still creating the `txindex`.
Wait until `"synced": true` before using `ord`.
If you have `maxuploadtarget` set it can interfere with fetching blocks for
`ord` index. Either remove it or set `whitebind=127.0.0.1:8333`.
Installing `ord`
----------------
The `ord` utility is written in Rust and can be built from
[source](https://github.com/ordinals/ord). Pre-built binaries are available on the
[releases page](https://github.com/ordinals/ord/releases).
You can install the latest pre-built binary from the command line with:
```sh
curl --proto '=https' --tlsv1.2 -fsLS https://ordinals.com/install.sh | bash -s
```
Once `ord` is installed, you should be able to run:
```
ord --version
```
Which prints out `ord`'s version number.
Creating a Bitcoin Core Wallet
------------------------------
`ord` uses Bitcoin Core to manage private keys, sign transactions, and
broadcast transactions to the Bitcoin network.
To create a Bitcoin Core wallet named `ord` for use with `ord`, run:
```
ord wallet create
```
Receiving Sats
--------------
Inscriptions are made on individual sats, using normal Bitcoin transactions
that pay fees in sats, so your wallet will need some sats.
Get a new address from your `ord` wallet by running:
```
ord wallet receive
```
And send it some funds.
You can see pending transactions with:
```
ord wallet transactions
```
Once the transaction confirms, you should be able to see the transactions
outputs with `ord wallet outputs`.
Creating Inscription Content
----------------------------
Sats can be inscribed with any kind of content, but the `ord` wallet only
supports content types that can be displayed by the `ord` block explorer.
Additionally, inscriptions are included in transactions, so the larger the
content, the higher the fee that the inscription transaction must pay.
Inscription content is included in transaction witnesses, which receive the
witness discount. To calculate the approximate fee that an inscribe transaction
will pay, divide the content size by four and multiply by the fee rate.
Inscription transactions must be less than 400,000 weight units, or they will
not be relayed by Bitcoin Core. One byte of inscription content costs one
weight unit. Since an inscription transaction includes not just the inscription
content, limit inscription content to less than 400,000 weight units. 390,000
weight units should be safe.
Creating Inscriptions
---------------------
To create an inscription with the contents of `FILE`, run:
```
ord wallet inscribe --fee-rate FEE_RATE --file FILE
```
Ord will output two transactions IDs, one for the commit transaction, and one
for the reveal transaction, and the inscription ID. Inscription IDs are of the
form `TXIDiN`, where `TXID` is the transaction ID of the reveal transaction,
and `N` is the index of the inscription in the reveal transaction.
The commit transaction commits to a tapscript containing the content of the
inscription, and the reveal transaction spends from that tapscript, revealing
the content on chain and inscribing it on the first sat of the input that
contains the corresponding tapscript.
Wait for the reveal transaction to be mined. You can check the status of the
commit and reveal transactions using [the mempool.space block
explorer](https://mempool.space/).
Once the reveal transaction has been mined, the inscription ID should be
printed when you run:
```
ord wallet inscriptions
```
Parent-Child Inscriptions
-------------------------
Parent-child inscriptions enable what is colloquially known as collections, see
[provenance](../inscriptions/provenance.md) for more information.
To make an inscription a child of another, the parent inscription has to be
inscribed and present in the wallet. To choose a parent run `ord wallet inscriptions`
and copy the inscription id (`<PARENT_INSCRIPTION_ID>`).
Now inscribe the child inscription and specify the parent like so:
```
ord wallet inscribe --fee-rate FEE_RATE --parent <PARENT_INSCRIPTION_ID> --file CHILD_FILE
```
This relationship cannot be added retroactively, the parent has to be
present at inception of the child.
Sending Inscriptions
--------------------
Ask the recipient to generate a new address by running:
```
ord wallet receive
```
Send the inscription by running:
```
ord wallet send --fee-rate <FEE_RATE> <ADDRESS> <INSCRIPTION_ID>
```
See the pending transaction with:
```
ord wallet transactions
```
Once the send transaction confirms, the recipient can confirm receipt by
running:
```
ord wallet inscriptions
```
Receiving Inscriptions
----------------------
Generate a new receive address using:
```
ord wallet receive
```
The sender can transfer the inscription to your address using:
```
ord wallet send ADDRESS INSCRIPTION_ID
```
See the pending transaction with:
```
ord wallet transactions
```
Once the send transaction confirms, you can confirm receipt by running:
```
ord wallet inscriptions
```

View File

@@ -0,0 +1,51 @@
Moderation
==========
`ord` includes a block explorer, which you can run locally with `ord server`.
The block explorer allows viewing inscriptions. Inscriptions are user-generated
content, which may be objectionable or unlawful.
It is the responsibility of each individual who runs an ordinal block explorer
instance to understand their responsibilities with respect to unlawful content,
and decide what moderation policy is appropriate for their instance.
In order to prevent particular inscriptions from being displayed on an `ord`
instance, they can be included in a YAML config file, which is loaded with the
`--config` option.
To hide inscriptions, first create a config file, with the inscription ID you
want to hide:
```yaml
hidden:
- 0000000000000000000000000000000000000000000000000000000000000000i0
```
The suggested name for `ord` config files is `ord.yaml`, but any filename can
be used.
Then pass the file to `--config` when starting the server:
`ord --config ord.yaml server`
Note that the `--config` option comes after `ord` but before the `server`
subcommand.
`ord` must be restarted in to load changes to the config file.
`ordinals.com`
--------------
The `ordinals.com` instances use `systemd` to run the `ord server` service,
which is called `ord`, with a config file located at `/var/lib/ord/ord.yaml`.
To hide an inscription on `ordinals.com`:
1. SSH into the server
2. Add the inscription ID to `/var/lib/ord/ord.yaml`
3. Restart the service with `systemctl restart ord`
4. Monitor the restart with `journalctl -u ord`
Currently, `ord` is slow to restart, so the site will not come back online
immediately.

View File

@@ -0,0 +1,31 @@
Reindexing
==========
Sometimes the `ord` database must be reindexed, which means deleting the
database and restarting the indexing process with either `ord index update` or
`ord server`. Reasons to reindex are:
1. A new major release of ord, which changes the database scheme
2. The database got corrupted somehow
The database `ord` uses is called [redb](https://github.com/cberner/redb),
so we give the index the default file name `index.redb`. By default we store this
file in different locations depending on your operating system.
|Platform | Value | Example |
| ------- | ------------------------------------------------ | -------------------------------------------- |
| Linux | `$XDG_DATA_HOME`/ord or `$HOME`/.local/share/ord | /home/alice/.local/share/ord |
| macOS | `$HOME`/Library/Application Support/ord | /Users/Alice/Library/Application Support/ord |
| Windows | `{FOLDERID_RoamingAppData}`\ord | C:\Users\Alice\AppData\Roaming\ord |
So to delete the database and reindex on MacOS you would have to run the following
commands in the terminal:
```bash
rm ~/Library/Application Support/ord/index.redb
ord index update
```
You can of course also set the location of the data directory yourself with `ord
--data-dir <DIR> index update` or give it a specific filename and path with `ord
--index <FILENAME> index update`.

View File

@@ -0,0 +1,249 @@
Sat Hunting
===========
*This guide is out of date. Since it was written, the `ord` binary was changed
to only build the full satoshi index when the `--index-sats` flag is supplied.
Additionally, `ord` now has a built-in wallet that wraps a Bitcoin Core wallet.
See `ord wallet --help`.*
Ordinal hunting is difficult but rewarding. The feeling of owning a wallet full
of UTXOs, redolent with the scent of rare and exotic sats, is beyond compare.
Ordinals are numbers for satoshis. Every satoshi has an ordinal number and
every ordinal number has a satoshi.
Preparation
-----------
There are a few things you'll need before you start.
1. First, you'll need a synced Bitcoin Core node with a transaction index. To
turn on transaction indexing, pass `-txindex` on the command-line:
```sh
bitcoind -txindex
```
Or put the following in your [Bitcoin configuration
file](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md#configuration-file-path):
```
txindex=1
```
Launch it and wait for it to catch up to the chain tip, at which point the
following command should print out the current block height:
```sh
bitcoin-cli getblockcount
```
2. Second, you'll need a synced `ord` index.
- Get a copy of `ord` from [the repo](https://github.com/ordinals/ord/).
- Run `RUST_LOG=info ord index`. It should connect to your bitcoin core
node and start indexing.
- Wait for it to finish indexing.
3. Third, you'll need a wallet with UTXOs that you want to search.
Searching for Rare Ordinals
---------------------------
### Searching for Rare Ordinals in a Bitcoin Core Wallet
The `ord wallet` command is just a wrapper around Bitcoin Core's RPC API, so
searching for rare ordinals in a Bitcoin Core wallet is Easy. Assuming your
wallet is named `foo`:
1. Load your wallet:
```sh
bitcoin-cli loadwallet foo
```
2. Display any rare ordinals wallet `foo`'s UTXOs:
```sh
ord --wallet foo --index-sats wallet sats
```
### Searching for Rare Ordinals in a Non-Bitcoin Core Wallet
The `ord wallet` command is just a wrapper around Bitcoin Core's RPC API, so to
search for rare ordinals in a non-Bitcoin Core wallet, you'll need to import
your wallet's descriptors into Bitcoin Core.
[Descriptors](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md)
describe the ways that wallets generate private keys and public keys.
You should only import descriptors into Bitcoin Core for your wallet's public
keys, not its private keys.
If your wallet's public key descriptor is compromised, an attacker will be able
to see your wallet's addresses, but your funds will be safe.
If your wallet's private key descriptor is compromised, an attacker can drain
your wallet of funds.
1. Get the wallet descriptor from the wallet whose UTXOs you want to search for
rare ordinals. It will look something like this:
```
wpkh([bf1dd55e/84'/0'/0']xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)#csvefu29
```
2. Create a watch-only wallet named `foo-watch-only`:
```sh
bitcoin-cli createwallet foo-watch-only true true
```
Feel free to give it a better name than `foo-watch-only`!
3. Load the `foo-watch-only` wallet:
```sh
bitcoin-cli loadwallet foo-watch-only
```
4. Import your wallet descriptors into `foo-watch-only`:
```sh
bitcoin-cli importdescriptors \
'[{ "desc": "wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)#tpnxnxax", "timestamp":0 }]'
```
If you know the Unix timestamp when your wallet first started receive
transactions, you may use it for the value of `"timestamp"` instead of `0`.
This will reduce the time it takes for Bitcoin Core to search for your
wallet's UTXOs.
5. Check that everything worked:
```sh
bitcoin-cli getwalletinfo
```
7. Display your wallet's rare ordinals:
```sh
ord wallet sats
```
### Searching for Rare Ordinals in a Wallet that Exports Multi-path Descriptors
Some descriptors describe multiple paths in one descriptor using angle brackets,
e.g., `<0;1>`. Multi-path descriptors are not yet supported by Bitcoin Core, so
you'll first need to convert them into multiple descriptors, and then import
those multiple descriptors into Bitcoin Core.
1. First get the multi-path descriptor from your wallet. It will look something
like this:
```
wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/<0;1>/*)#fw76ulgt
```
2. Create a descriptor for the receive address path:
```
wpkh([bf1dd55e/84'/0'/0']xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)
```
And the change address path:
```
wpkh([bf1dd55e/84'/0'/0']xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/1/*)
```
3. Get and note the checksum for the receive address descriptor, in this case
`tpnxnxax`:
```sh
bitcoin-cli getdescriptorinfo \
'wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)'
```
```json
{
"descriptor": "wpkh([bf1dd55e/84'/0'/0']xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)#csvefu29",
"checksum": "tpnxnxax",
"isrange": true,
"issolvable": true,
"hasprivatekeys": false
}
```
And for the change address descriptor, in this case `64k8wnd7`:
```sh
bitcoin-cli getdescriptorinfo \
'wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/1/*)'
```
```json
{
"descriptor": "wpkh([bf1dd55e/84'/0'/0']xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/1/*)#fyfc5f6a",
"checksum": "64k8wnd7",
"isrange": true,
"issolvable": true,
"hasprivatekeys": false
}
```
4. Load the wallet you want to import the descriptors into:
```sh
bitcoin-cli loadwallet foo-watch-only
```
4. Now import the descriptors, with the correct checksums, into Bitcoin Core.
```sh
bitcoin-cli \
importdescriptors \
'[
{
"desc": "wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/0/*)#tpnxnxax"
"timestamp":0
},
{
"desc": "wpkh([bf1dd55e/84h/0h/0h]xpub6CcJtWcvFQaMo39ANFi1MyXkEXM8T8ZhnxMtSjQAdPmVSTHYnc8Hwoc11VpuP8cb8JUTboZB5A7YYGDonYySij4XTawL6iNZvmZwdnSEEep/1/*)#64k8wnd7",
"timestamp":0
}
]'
```
If you know the Unix timestamp when your wallet first started receive
transactions, you may use it for the value of the `"timestamp"` fields
instead of `0`. This will reduce the time it takes for Bitcoin Core to
search for your wallet's UTXOs.
5. Check that everything worked:
```sh
bitcoin-cli getwalletinfo
```
7. Display your wallet's rare ordinals:
```sh
ord wallet sats
```
### Exporting Descriptors
#### Sparrow Wallet
Navigate to the `Settings` tab, then to `Script Policy`, and press the edit
button to display the descriptor.
### Transferring Ordinals
The `ord` wallet supports transferring specific satoshis. You can also use
`bitcoin-cli` commands `createrawtransaction`, `signrawtransactionwithwallet`,
and `sendrawtransaction`, how to do so is complex and outside the scope of this
guide.

View File

@@ -0,0 +1,50 @@
Teleburning
===========
Teleburn addresses can be used to burn assets on other blockchains, leaving
behind in the smoking rubble a sort of forwarding address pointing to an
inscription on Bitcoin.
Teleburning an asset means something like, "I'm out. Find me on Bitcoin."
Teleburn addresses are derived from inscription IDs. They have no corresponding
private key, so assets sent to a teleburn address are burned. Currently, only
Ethereum teleburn addresses are supported. Pull requests adding teleburn
addresses for other chains are welcome.
Ethereum
--------
Ethereum teleburn addresses are derived by taking the first 20 bytes of the
SHA-256 hash of the inscription ID, serialized as 36 bytes, with the first 32
bytes containing the transaction ID, and the last four bytes containing
big-endian inscription index, and interpreting it as an Ethereum address.
Example
-------
The ENS domain name [rodarmor.eth](https://app.ens.domains/rodarmor.eth), was
teleburned to [inscription
zero](https://ordinals.com/inscription/6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0).
Running the inscription ID of inscription zero is
`6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0`.
Passing `6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0` to
the teleburn command:
```bash
$ ord teleburn 6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0
```
Returns:
```json
{
"ethereum": "0xe43A06530BdF8A4e067581f48Fae3b535559dA9e"
}
```
Indicating that `0xe43A06530BdF8A4e067581f48Fae3b535559dA9e` is the Ethereum
teleburn address for inscription zero, which is, indeed, the current owner, on
Ethereum, of `rodarmor.eth`.

View File

@@ -0,0 +1,85 @@
Testing
=======
Ord can be tested using the following flags to specify the test network. For more
information on running Bitcoin Core for testing, see [Bitcoin's developer documentation](https://developer.bitcoin.org/examples/testing.html).
Most `ord` commands in [inscriptions](inscriptions.md) and [explorer](explorer.md)
can be run with the following network flags:
| Network | Flag |
|---------|------|
| Testnet | `--testnet` or `-t` |
| Signet | `--signet` or `-s` |
| Regtest | `--regtest` or `-r` |
Regtest doesn't require downloading the blockchain or indexing ord.
Example
-------
Run bitcoind in regtest with:
```
bitcoind -regtest -txindex
```
Create a wallet in regtest with:
```
ord -r wallet create
```
Get a regtest receive address with:
```
ord -r wallet receive
```
Mine 101 blocks (to unlock the coinbase) with:
```
bitcoin-cli -regtest generatetoaddress 101 <receive address>
```
Inscribe in regtest with:
```
ord -r wallet inscribe --fee-rate 1 --file <file>
```
Mine the inscription with:
```
bitcoin-cli -regtest generatetoaddress 1 <receive address>
```
View the inscription in the regtest explorer:
```
ord -r server
```
By default, browsers don't support compression over HTTP. To test compressed
content over HTTP, use the `--decompress` flag:
```
ord -r server --decompress
```
Testing Recursion
-----------------
When testing out [recursion](../inscriptions/recursion.md), inscribe the
dependencies first (example with [p5.js](https://p5js.org)):
```
ord -r wallet inscribe --fee-rate 1 --file p5.js
```
This should return a `inscription_id` which you can then reference in your
recursive inscription.
ATTENTION: These ids will be different when inscribing on
mainnet or signet, so be sure to change those in your recursive inscription for
each chain.
Then you can inscribe your recursive inscription with:
```
ord -r wallet inscribe --fee-rate 1 --file recursive-inscription.html
```
Finally you will have to mine some blocks and start the server:
```
bitcoin-cli generatetoaddress 6 <receive address>
ord -r server
```

135
docs/src/inscriptions.md Normal file
View File

@@ -0,0 +1,135 @@
Inscriptions
============
Inscriptions inscribe sats with arbitrary content, creating bitcoin-native
digital artifacts, more commonly known as NFTs. Inscriptions do not require a
sidechain or separate token.
These inscribed sats can then be transferred using bitcoin transactions, sent
to bitcoin addresses, and held in bitcoin UTXOs. These transactions, addresses,
and UTXOs are normal bitcoin transactions, addresses, and UTXOS in all
respects, with the exception that in order to send individual sats,
transactions must control the order and value of inputs and outputs according
to ordinal theory.
The inscription content model is that of the web. An inscription consists of a
content type, also known as a MIME type, and the content itself, which is a
byte string. This allows inscription content to be returned from a web server,
and for creating HTML inscriptions that use and remix the content of other
inscriptions.
Inscription content is entirely on-chain, stored in taproot script-path spend
scripts. Taproot scripts have very few restrictions on their content, and
additionally receive the witness discount, making inscription content storage
relatively economical.
Since taproot script spends can only be made from existing taproot outputs,
inscriptions are made using a two-phase commit/reveal procedure. First, in the
commit transaction, a taproot output committing to a script containing the
inscription content is created. Second, in the reveal transaction, the output
created by the commit transaction is spent, revealing the inscription content
on-chain.
Inscription content is serialized using data pushes within unexecuted
conditionals, called "envelopes". Envelopes consist of an `OP_FALSE OP_IF …
OP_ENDIF` wrapping any number of data pushes. Because envelopes are effectively
no-ops, they do not change the semantics of the script in which they are
included, and can be combined with any other locking script.
A text inscription containing the string "Hello, world!" is serialized as
follows:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 0
OP_PUSH "Hello, world!"
OP_ENDIF
```
First the string `ord` is pushed, to disambiguate inscriptions from other uses
of envelopes.
`OP_PUSH 1` indicates that the next push contains the content type, and
`OP_PUSH 0`indicates that subsequent data pushes contain the content itself.
Multiple data pushes must be used for large inscriptions, as one of taproot's
few restrictions is that individual data pushes may not be larger than 520
bytes.
The inscription content is contained within the input of a reveal transaction,
and the inscription is made on the first sat of its input. This sat can then be
tracked using the familiar rules of ordinal theory, allowing it to be
transferred, bought, sold, lost to fees, and recovered.
Content
-------
The data model of inscriptions is that of a HTTP response, allowing inscription
content to be served by a web server and viewed in a web browser.
Fields
------
Inscriptions may include fields before an optional body. Each field consists of
two data pushes, a tag and a value.
Currently, there are six defined fields:
- `content_type`, with a tag of `1`, whose value is the MIME type of the body.
- `pointer`, with a tag of `2`, see [pointer docs](inscriptions/pointer.md).
- `parent`, with a tag of `3`, see [provenance](inscriptions/provenance.md).
- `metadata`, with a tag of `5`, see [metadata](inscriptions/metadata.md).
- `metaprotocol`, with a tag of `7`, whose value is the metaprotocol identifier.
- `content_encoding`, with a tag of `9`, whose value is the encoding of the body.
- `delegate`, with a tag of `11`, see [delegate](inscriptions/delegate.md).
The beginning of the body and end of fields is indicated with an empty data
push.
Unrecognized tags are interpreted differently depending on whether they are
even or odd, following the "it's okay to be odd" rule used by the Lightning
Network.
Even tags are used for fields which may affect creation, initial assignment, or
transfer of an inscription. Thus, inscriptions with unrecognized even fields
must be displayed as "unbound", that is, without a location.
Odd tags are used for fields which do not affect creation, initial assignment,
or transfer, such as additional metadata, and thus are safe to ignore.
Inscription IDs
---------------
The inscriptions are contained within the inputs of a reveal transaction. In
order to uniquely identify them they are assigned an ID of the form:
`521f8eccffa4c41a3a7728dd012ea5a4a02feed81f41159231251ecf1e5c79dai0`
The part in front of the `i` is the transaction ID (`txid`) of the reveal
transaction. The number after the `i` defines the index (starting at 0) of new inscriptions
being inscribed in the reveal transaction.
Inscriptions can either be located in different inputs, within the same input or
a combination of both. In any case the ordering is clear, since a parser would
go through the inputs consecutively and look for all inscription `envelopes`.
| Input | Inscription Count | Indices |
|:-----:|:-----------------:|:----------:|
| 0 | 2 | i0, i1 |
| 1 | 1 | i2 |
| 2 | 3 | i3, i4, i5 |
| 3 | 0 | |
| 4 | 1 | i6 |
Sandboxing
----------
HTML and SVG inscriptions are sandboxed in order to prevent references to
off-chain content, thus keeping inscriptions immutable and self-contained.
This is accomplished by loading HTML and SVG inscriptions inside `iframes` with
the `sandbox` attribute, as well as serving inscription content with
`Content-Security-Policy` headers.

View File

@@ -0,0 +1,39 @@
Delegate
========
Inscriptions may nominate a delegate inscription. Requests for the content of
an inscription with a delegate will instead return the content and content type
of the delegate. This can be used to cheaply create copies of an inscription.
### Specification
To create an inscription I with delegate inscription D:
- Create an inscription D. Note that inscription D does not have to exist when
making inscription I. It may be inscribed later. Before inscription D is
inscribed, requests for the content of inscription I will return a 404.
- Include tag `11`, i.e. `OP_PUSH 11`, in I, with the value of the serialized
binary inscription ID of D, serialized as the 32-byte `TXID`, followed by the
four-byte little-endian `INDEX`, with trailing zeroes omitted.
_NB_ The bytes of a bitcoin transaction ID are reversed in their text
representation, so the serialized transaction ID will be in the opposite order.
### Example
An example of an inscription which delegates to
`000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fi0`:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 11
OP_PUSH 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
OP_ENDIF
```
Note that the value of tag `11` is binary, not hex.
The delegate field value uses the same encoding as the parent field. See
[provenance](provenance.md) for more examples of inscrpition ID encodings;

View File

@@ -0,0 +1,86 @@
Metadata
========
Inscriptions may include [CBOR](https://cbor.io/) metadata, stored as data
pushes in fields with tag `5`. Since data pushes are limited to 520 bytes,
metadata longer than 520 bytes must be split into multiple tag `5` fields,
which will then be concatenated before decoding.
Metadata is human readable, and all metadata will be displayed to the user with
its inscription. Inscribers are encouraged to consider how metadata will be
displayed, and make metadata concise and attractive.
Metadata is rendered to HTML for display as follows:
- `null`, `true`, `false`, numbers, floats, and strings are rendered as plain
text.
- Byte strings are rendered as uppercase hexadecimal.
- Arrays are rendered as `<ul>` tags, with every element wrapped in `<li>`
tags.
- Maps are rendered as `<dl>` tags, with every key wrapped in `<dt>` tags, and
every value wrapped in `<dd>` tags.
- Tags are rendered as the tag , enclosed in a `<sup>` tag, followed by the
value.
CBOR is a complex spec with many different data types, and multiple ways of
representing the same data. Exotic data types, such as tags, floats, and
bignums, and encoding such as indefinite values, may fail to display correctly
or at all. Contributions to `ord` to remedy this are welcome.
Example
-------
Since CBOR is not human readable, in these examples it is represented as JSON.
Keep in mind that this is *only* for these examples, and JSON metadata will
*not* be displayed correctly.
The metadata `{"foo":"bar","baz":[null,true,false,0]}` would be included in an inscription as:
```
OP_FALSE
OP_IF
...
OP_PUSH 0x05 OP_PUSH '{"foo":"bar","baz":[null,true,false,0]}'
...
OP_ENDIF
```
And rendered as:
```
<dl>
...
<dt>metadata</dt>
<dd>
<dl>
<dt>foo</dt>
<dd>bar</dd>
<dt>baz</dt>
<dd>
<ul>
<li>null</li>
<li>true</li>
<li>false</li>
<li>0</li>
</ul>
</dd>
</dl>
</dd>
...
</dl>
```
Metadata longer than 520 bytes must be split into multiple fields:
```
OP_FALSE
OP_IF
...
OP_PUSH 0x05 OP_PUSH '{"very":"long","metadata":'
OP_PUSH 0x05 OP_PUSH '"is","finally":"done"}'
...
OP_ENDIF
```
Which would then be concatenated into
`{"very":"long","metadata":"is","finally":"done"}`.

View File

@@ -0,0 +1,64 @@
Pointer
=======
In order to make an inscription on a sat other than the first of its input, a
zero-based integer, called the "pointer", can be provided with tag `2`, causing
the inscription to be made on the sat at the given position in the outputs. If
the pointer is equal to or greater than the number of total sats in the outputs
of the inscribe transaction, it is ignored, and the inscription is made as
usual. The value of the pointer field is a little endian integer, with trailing
zeroes ignored.
An even tag is used, so that old versions of `ord` consider the inscription to
be unbound, instead of assigning it, incorrectly, to the first sat.
This can be used to create multiple inscriptions in a single transaction on
different sats, when otherwise they would be made on the same sat.
Examples
--------
An inscription with pointer 255:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 2
OP_PUSH 0xff
OP_PUSH 0
OP_PUSH "Hello, world!"
OP_ENDIF
```
An inscription with pointer 256:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 2
OP_PUSH 0x0001
OP_PUSH 0
OP_PUSH "Hello, world!"
OP_ENDIF
```
An inscription with pointer 256, with trailing zeroes, which are ignored:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 2
OP_PUSH 0x000100
OP_PUSH 0
OP_PUSH "Hello, world!"
OP_ENDIF
```

View File

@@ -0,0 +1,82 @@
Provenance
==========
The owner of an inscription can create child inscriptions, trustlessly
establishing the provenance of those children on-chain as having been created
by the owner of the parent inscription. This can be used for collections, with
the children of a parent inscription being members of the same collection.
Children can themselves have children, allowing for complex hierarchies. For
example, an artist might create an inscription representing themselves, with
sub inscriptions representing collections that they create, with the children
of those sub inscriptions being items in those collections.
### Specification
To create a child inscription C with parent inscription P:
- Create an inscribe transaction T as usual for C.
- Spend the parent P in one of the inputs of T.
- Include tag `3`, i.e. `OP_PUSH 3`, in C, with the value of the serialized
binary inscription ID of P, serialized as the 32-byte `TXID`, followed by the
four-byte little-endian `INDEX`, with trailing zeroes omitted.
_NB_ The bytes of a bitcoin transaction ID are reversed in their text
representation, so the serialized transaction ID will be in the opposite order.
### Example
An example of a child inscription of
`000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fi0`:
```
OP_FALSE
OP_IF
OP_PUSH "ord"
OP_PUSH 1
OP_PUSH "text/plain;charset=utf-8"
OP_PUSH 3
OP_PUSH 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
OP_PUSH 0
OP_PUSH "Hello, world!"
OP_ENDIF
```
Note that the value of tag `3` is binary, not hex, and that for the child
inscription to be recognized as a child,
`000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fi0` must be
spent as one of the inputs of the inscribe transaction.
Example encoding of inscription ID
`000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fi255`:
```
OP_FALSE
OP_IF
OP_PUSH 3
OP_PUSH 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100ff
OP_ENDIF
```
And of inscription ID `000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fi256`:
```
OP_FALSE
OP_IF
OP_PUSH 3
OP_PUSH 0x1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201000001
OP_ENDIF
```
### Notes
The tag `3` is used because it is the first available odd tag. Unrecognized odd
tags do not make an inscription unbound, so child inscriptions would be
recognized and tracked by old versions of `ord`.
A collection can be closed by burning the collection's parent inscription,
which guarantees that no more items in the collection can be issued.

View File

@@ -0,0 +1,110 @@
Recursion
=========
An important exception to [sandboxing](../inscriptions.md#sandboxing) is
recursion: access to `ord`'s `/content` endpoint is permitted, allowing
inscriptions to access the content of other inscriptions by requesting
`/content/<INSCRIPTION_ID>`.
This has a number of interesting use-cases:
- Remixing the content of existing inscriptions.
- Publishing snippets of code, images, audio, or stylesheets as shared public
resources.
- Generative art collections where an algorithm is inscribed as JavaScript,
and instantiated from multiple inscriptions with unique seeds.
- Generative profile picture collections where accessories and attributes are
inscribed as individual images, or in a shared texture atlas, and then
combined, collage-style, in unique combinations in multiple inscriptions.
The recursive endpoints are:
- `/r/blockhash/<HEIGHT>`: block hash at given block height.
- `/r/blockhash`: latest block hash.
- `/r/blockheight`: latest block height.
- `/r/blocktime`: UNIX time stamp of latest block.
- `/r/children/<INSCRIPTION_ID>`: the first 100 child inscription ids.
- `/r/children/<INSCRIPTION_ID>/<PAGE>`: the set of 100 child inscription ids on `<PAGE>`.
- `/r/metadata/<INSCRIPTION_ID>`: JSON string containing the hex-encoded CBOR metadata.
- `/r/sat/<SAT_NUMBER>`: the first 100 inscription ids on a sat.
- `/r/sat/<SAT_NUMBER>/<PAGE>`: the set of 100 inscription ids on `<PAGE>`.
- `/r/sat/<SAT_NUMBER>/at/<INDEX>`: the inscription id at `<INDEX>` of all inscriptions on a sat. `<INDEX>` may be a negative number to index from the back. `0` being the first and `-1` being the most recent for example.
Note: `<SAT_NUMBER>` only allows the actual number of a sat no other sat
notations like degree, percentile or decimal. We may expand to allow those in
the future.
Responses from the above recursive endpoints are JSON. For backwards
compatibility additional endpoints are supported, some of which return
plain-text responses.
- `/blockheight`: latest block height.
- `/blockhash`: latest block hash.
- `/blockhash/<HEIGHT>`: block hash at given block height.
- `/blocktime`: UNIX time stamp of latest block.
Examples
--------
- `/r/blockheight`:
```json
777000
```
- `/r/blockhash/0`:
```json
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
```
- `/r/blocktime`:
```json
1700770905
```
- `/r/metadata/35b66389b44535861c44b2b18ed602997ee11db9a30d384ae89630c9fc6f011fi3`:
```json
"a2657469746c65664d656d6f727966617574686f726e79656c6c6f775f6f72645f626f74"
```
- `/r/sat/1023795949035695`:
```json
{
"ids":[
"17541f6adf6eb160d52bc6eb0a3546c7c1d2adfe607b1a3cddc72cc0619526adi0"
],
"more":false,
"page":0
}
```
- `/r/sat/1023795949035695/at/-1`:
```json
{
"id":"17541f6adf6eb160d52bc6eb0a3546c7c1d2adfe607b1a3cddc72cc0619526adi0"
}
```
- `/r/children/60bcf821240064a9c55225c4f01711b0ebbcab39aa3fafeefe4299ab158536fai0/49`:
```json
{
"ids":[
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4900",
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4901",
...
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4935",
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4936"
],
"more":false,
"page":49
}
```

51
docs/src/introduction.md Normal file
View File

@@ -0,0 +1,51 @@
Introduction
============
This handbook is a guide to ordinal theory. Ordinal theory concerns itself with
satoshis, giving them individual identities and allowing them to be tracked,
transferred, and imbued with meaning.
Satoshis, not bitcoin, are the atomic, native currency of the Bitcoin network.
One bitcoin can be sub-divided into 100,000,000 satoshis, but no further.
Ordinal theory does not require a sidechain or token aside from Bitcoin, and
can be used without any changes to the Bitcoin network. It works right now.
Ordinal theory imbues satoshis with numismatic value, allowing them to be
collected and traded as curios.
Individual satoshis can be inscribed with arbitrary content, creating unique
Bitcoin-native digital artifacts that can be held in Bitcoin wallets and
transferred using Bitcoin transactions. Inscriptions are as durable, immutable,
secure, and decentralized as Bitcoin itself.
Other, more unusual use-cases are possible: off-chain colored-coins, public key
infrastructure with key rotation, a decentralized replacement for the DNS. For
now though, such use-cases are speculative, and exist only in the minds of
fringe ordinal theorists.
For more details on ordinal theory, see the [overview](overview.md).
For more details on inscriptions, see [inscriptions](inscriptions.md).
When you're ready to get your hands dirty, a good place to start is with
[inscriptions](guides/inscriptions.md), a curious species of digital artifact
enabled by ordinal theory.
Links
-----
- [GitHub](https://github.com/ordinals/ord/)
- [BIP](https://github.com/ordinals/ord/blob/master/bip.mediawiki)
- [Discord](https://discord.gg/ordinals)
- [Open Ordinals Institute Website](https://ordinals.org/)
- [Open Ordinals Institute X](https://x.com/ordinalsorg)
- [Mainnet Block Explorer](https://ordinals.com)
- [Signet Block Explorer](https://signet.ordinals.com)
Videos
------
- [Ordinal Theory Explained: Satoshi Serial Numbers and NFTs on Bitcoin](https://www.youtube.com/watch?v=rSS0O2KQpsI)
- [Ordinals Workshop with Rodarmor](https://www.youtube.com/watch?v=MC_haVa6N3I)
- [Ordinal Art: Mint Your own NFTs on Bitcoin w/ @rodarmor](https://www.youtube.com/watch?v=j5V33kV3iqo)

271
docs/src/overview.md Normal file
View File

@@ -0,0 +1,271 @@
Ordinal Theory Overview
=======================
Ordinals are a numbering scheme for satoshis that allows tracking and
transferring individual sats. These numbers are called [ordinal
numbers](https://ordinals.com). Satoshis are numbered in the order in which
they're mined, and transferred from transaction inputs to transaction outputs
first-in-first-out. Both the numbering scheme and the transfer scheme rely on
*order*, the numbering scheme on the *order* in which satoshis are mined, and
the transfer scheme on the *order* of transaction inputs and outputs. Thus the
name, *ordinals*.
Technical details are available in [the
BIP](https://github.com/ordinals/ord/blob/master/bip.mediawiki).
Ordinal theory does not require a separate token, another blockchain, or any
changes to Bitcoin. It works right now.
Ordinal numbers have a few different representations:
- *Integer notation*:
[`2099994106992659`](https://ordinals.com/sat/2099994106992659) The
ordinal number, assigned according to the order in which the satoshi was
mined.
- *Decimal notation*:
[`3891094.16797`](https://ordinals.com/sat/3891094.16797) The first
number is the block height in which the satoshi was mined, the second the
offset of the satoshi within the block.
- *Degree notation*:
[`3°111094214″16797‴`](https://ordinals.com/sat/3%C2%B0111094%E2%80%B2214%E2%80%B316797%E2%80%B4).
We'll get to that in a moment.
- *Percentile notation*:
[`99.99971949060254%`](https://ordinals.com/sat/99.99971949060254%25) .
The satoshi's position in Bitcoin's supply, expressed as a percentage.
- *Name*: [`satoshi`](https://ordinals.com/sat/satoshi). An encoding of the
ordinal number using the characters `a` through `z`.
Arbitrary assets, such as NFTs, security tokens, accounts, or stablecoins can
be attached to satoshis using ordinal numbers as stable identifiers.
Ordinals is an open-source project, developed [on
GitHub](https://github.com/ordinals/ord). The project consists of a BIP describing
the ordinal scheme, an index that communicates with a Bitcoin Core node to
track the location of all satoshis, a wallet that allows making ordinal-aware
transactions, a block explorer for interactive exploration of the blockchain,
functionality for inscribing satoshis with digital artifacts, and this manual.
Rarity
------
Humans are collectors, and since satoshis can now be tracked and transferred,
people will naturally want to collect them. Ordinal theorists can decide for
themselves which sats are rare and desirable, but there are some hints…
Bitcoin has periodic events, some frequent, some more uncommon, and these
naturally lend themselves to a system of rarity. These periodic events are:
- *Blocks*: A new block is mined approximately every 10 minutes, from now until
the end of time.
- *Difficulty adjustments*: Every 2016 blocks, or approximately every two
weeks, the Bitcoin network responds to changes in hashrate by adjusting the
difficulty target which blocks must meet in order to be accepted.
- *Halvings*: Every 210,000 blocks, or roughly every four years, the amount of
new sats created in every block is cut in half.
- *Cycles*: Every six halvings, something magical happens: the halving and the
difficulty adjustment coincide. This is called a conjunction, and the time
period between conjunctions a cycle. A conjunction occurs roughly every 24
years. The first conjunction should happen sometime in 2032.
This gives us the following rarity levels:
- `common`: Any sat that is not the first sat of its block
- `uncommon`: The first sat of each block
- `rare`: The first sat of each difficulty adjustment period
- `epic`: The first sat of each halving epoch
- `legendary`: The first sat of each cycle
- `mythic`: The first sat of the genesis block
Which brings us to degree notation, which unambiguously represents an ordinal
number in a way that makes the rarity of a satoshi easy to see at a glance:
```
A°BC″D‴
│ │ │ ╰─ Index of sat in the block
│ │ ╰─── Index of block in difficulty adjustment period
│ ╰───── Index of block in halving epoch
╰─────── Cycle, numbered starting from 0
```
Ordinal theorists often use the terms "hour", "minute", "second", and "third"
for *A*, *B*, *C*, and *D*, respectively.
Now for some examples. This satoshi is common:
```
1°11″1‴
│ │ │ ╰─ Not first sat in block
│ │ ╰─── Not first block in difficulty adjustment period
│ ╰───── Not first block in halving epoch
╰─────── Second cycle
```
This satoshi is uncommon:
```
1°11″0‴
│ │ │ ╰─ First sat in block
│ │ ╰─── Not first block in difficulty adjustment period
│ ╰───── Not first block in halving epoch
╰─────── Second cycle
```
This satoshi is rare:
```
1°10″0‴
│ │ │ ╰─ First sat in block
│ │ ╰─── First block in difficulty adjustment period
│ ╰───── Not the first block in halving epoch
╰─────── Second cycle
```
This satoshi is epic:
```
1°01″0‴
│ │ │ ╰─ First sat in block
│ │ ╰─── Not first block in difficulty adjustment period
│ ╰───── First block in halving epoch
╰─────── Second cycle
```
This satoshi is legendary:
```
1°00″0‴
│ │ │ ╰─ First sat in block
│ │ ╰─── First block in difficulty adjustment period
│ ╰───── First block in halving epoch
╰─────── Second cycle
```
And this satoshi is mythic:
```
0°00″0‴
│ │ │ ╰─ First sat in block
│ │ ╰─── First block in difficulty adjustment period
│ ╰───── First block in halving epoch
╰─────── First cycle
```
If the block offset is zero, it may be omitted. This is the uncommon satoshi
from above:
```
1°11″
│ │ ╰─ Not first block in difficulty adjustment period
│ ╰─── Not first block in halving epoch
╰───── Second cycle
```
Rare Satoshi Supply
-------------------
### Total Supply
- `common`: 2.1 quadrillion
- `uncommon`: 6,929,999
- `rare`: 3437
- `epic`: 32
- `legendary`: 5
- `mythic`: 1
### Current Supply
- `common`: 1.9 quadrillion
- `uncommon`: 808,262
- `rare`: 369
- `epic`: 3
- `legendary`: 0
- `mythic`: 1
At the moment, even uncommon satoshis are quite rare. As of this writing,
745,855 uncommon satoshis have been mined - one per 25.6 bitcoin in
circulation.
Names
-----
Each satoshi has a name, consisting of the letters *A* through *Z*, that get
shorter the further into the future the satoshi was mined. They could start
short and get longer, but then all the good, short names would be trapped in
the unspendable genesis block.
As an example, 1905530482684727°'s name is "iaiufjszmoba". The name of the last
satoshi to be mined is "a". Every combination of 10 characters or less is out
there, or will be out there, someday.
Exotics
-------
Satoshis may be prized for reasons other than their name or rarity. This might
be due to a quality of the number itself, like having an integer square or cube
root. Or it might be due to a connection to a historical event, such as
satoshis from block 477,120, the block in which SegWit activated, or
2099999997689999°, the last satoshi that will ever be mined.
Such satoshis are termed "exotic". Which satoshis are exotic and what makes
them so is subjective. Ordinal theorists are encouraged to seek out exotics
based on criteria of their own devising.
Inscriptions
------------
Satoshis can be inscribed with arbitrary content, creating Bitcoin-native
digital artifacts. Inscribing is done by sending the satoshi to be inscribed in
a transaction that reveals the inscription content on-chain. This content is
then inextricably linked to that satoshi, turning it into an immutable digital
artifact that can be tracked, transferred, hoarded, bought, sold, lost, and
rediscovered.
Archaeology
-----------
A lively community of archaeologists devoted to cataloging and collecting early
NFTs has sprung up. [Here's a great summary of historical NFTs by
Chainleft.](https://mirror.xyz/chainleft.eth/MzPWRsesC9mQflxlLo-N29oF4iwCgX3lacrvaG9Kjko)
A commonly accepted cut-off for early NFTs is March 19th, 2018, the date the
first ERC-721 contract, [SU SQUARES](https://tenthousandsu.com/), was deployed
on Ethereum.
Whether or not ordinals are of interest to NFT archaeologists is an open
question! In one sense, ordinals were created in early 2022, when the Ordinals
specification was finalized. In this sense, they are not of historical
interest.
In another sense though, ordinals were in fact created by Satoshi Nakamoto in
2009 when he mined the Bitcoin genesis block. In this sense, ordinals, and
especially early ordinals, are certainly of historical interest.
Many ordinal theorists favor the latter view. This is not least because the
ordinals were independently discovered on at least two separate occasions, long
before the era of modern NFTs began.
On August 21st, 2012, Charlie Lee [posted a proposal to add proof-of-stake to
Bitcoin to the Bitcoin Talk
forum](https://bitcointalk.org/index.php?topic=102355.0). This wasn't an asset
scheme, but did use the ordinal algorithm, and was implemented but never
deployed.
On October 8th, 2012, jl2012 [posted a scheme to the same
forum](https://bitcointalk.org/index.php?topic=117224.0) which uses decimal
notation and has all the important properties of ordinals. The scheme was
discussed but never implemented.
These independent inventions of ordinals indicate in some way that ordinals
were discovered, or rediscovered, and not invented. The ordinals are an
inevitability of the mathematics of Bitcoin, stemming not from their modern
documentation, but from their ancient genesis. They are the culmination of a
sequence of events set in motion with the mining of the first block, so many
years ago.

422
docs/theme/index.hbs vendored Normal file
View File

@@ -0,0 +1,422 @@
<!DOCTYPE HTML>
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>{{ title }}</title>
{{#if is_print }}
<meta name="robots" content="noindex">
{{/if}}
{{#if base_url}}
<base href="{{ base_url }}">
{{/if}}
<!-- Custom HTML head -->
{{> head}}
<meta name="description" content="{{ description }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
{{#if favicon_svg}}
<link rel="icon" href="{{ path_to_root }}favicon.svg">
{{/if}}
{{#if favicon_png}}
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
{{/if}}
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
{{#if print_enable}}
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}}
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
<!-- Custom theme stylesheets -->
{{#each additional_css}}
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
{{/each}}
{{#if mathjax_support}}
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('{{ default_theme }}')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
{{#toc}}{{/toc}}
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
{{> header}}
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
</div>
<h1 class="menu-title">{{ book_title }}</h1>
<div class="right-buttons">
<button id="language-toggle" class="icon-button" type="button"
title="Change language" aria-label="Change language"
aria-haspopup="true" aria-expanded="false"
aria-controls="language-list">
<i class="fa fa-globe"></i>
</button>
<ul id="language-list" class="theme-popup" aria-label="Languages" role="menu">
<li role="none"><button role="menuitem" class="theme">
<a id="en">English</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="de">Deutsch</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="fr">Français</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="es">Español</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="pt">Português</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="ru">Русский</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="zh">中文版</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="ja">日本語版</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="ko">한국어</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="fil">Filipino</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="ar">العربية</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="hi">हिंदी</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="it">Italiano</a>
</button></li>
</ul>
<script>
let langToggle = document.getElementById("language-toggle");
let langList = document.getElementById("language-list");
langToggle.addEventListener("click", (event) => {
langList.style.display = langList.style.display == "block" ? "none" : "block";
});
let selectedLang = document.getElementById("{{ language }}");
if (selectedLang) {
selectedLang.parentNode.classList.add("theme-selected");
}
// The path to the root, taking the current
// language into account.
{{#if (eq language "en")}}
let full_path_to_root = "{{ path_to_root }}";
{{else}}
let full_path_to_root = "{{ path_to_root }}../";
{{/if}}
// The page path (mdbook only gives us
// access to the path to the Markdown file).
let path = "{{ path }}".replace(/\.md$/, ".html");
for (let lang of langList.querySelectorAll("a")) {
if (lang.id == "en") {
lang.href = `${full_path_to_root}${path}`;
} else {
lang.href = `${full_path_to_root}${lang.id}/${path}`;
}
}
</script>
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
{{/if}}
{{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
</a>
{{/if}}
{{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{/if}}
</div>
</div>
{{#if search_enabled}}
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
{{/if}}
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
{{{ content }}}
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
</nav>
</div>
{{#if live_reload_endpoint}}
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
{{/if}}
{{#if google_analytics}}
<!-- Google Analytics Tag -->
<script>
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{google_analytics}}', 'auto');
ga('send', 'pageview');
}
</script>
{{/if}}
{{#if playground_line_numbers}}
<script>
window.playground_line_numbers = true;
</script>
{{/if}}
{{#if playground_copyable}}
<script>
window.playground_copyable = true;
</script>
{{/if}}
{{#if playground_js}}
<script src="{{ path_to_root }}ace.js"></script>
<script src="{{ path_to_root }}editor.js"></script>
<script src="{{ path_to_root }}mode-rust.js"></script>
<script src="{{ path_to_root }}theme-dawn.js"></script>
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
{{/if}}
{{#if search_js}}
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
<script src="{{ path_to_root }}mark.min.js"></script>
<script src="{{ path_to_root }}searcher.js"></script>
{{/if}}
<script src="{{ path_to_root }}clipboard.min.js"></script>
<script src="{{ path_to_root }}highlight.js"></script>
<script src="{{ path_to_root }}book.js"></script>
<!-- Custom JS scripts -->
{{#each additional_js}}
<script src="{{ ../path_to_root }}{{this}}"></script>
{{/each}}
{{#if is_print}}
{{#if mathjax_support}}
<script>
window.addEventListener('load', function() {
MathJax.Hub.Register.StartupHook('End', function() {
window.setTimeout(window.print, 100);
});
});
</script>
{{else}}
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
{{/if}}
{{/if}}
</div>
</body>
</html>

1
examples/alert.html Normal file
View File

@@ -0,0 +1 @@
<script>alert('LFG!')</script>

View File

@@ -0,0 +1 @@
<img src=https://rodarmor.com/blaster/images/1407912129089.26abc8c.png>

Some files were not shown because too many files have changed in this diff Show More