├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── .prettierignore ├── CHANGELOG.md ├── CONTRIBUTING ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── Vagrantfile ├── benchmark ├── checkout └── run ├── bin ├── benchmark ├── flamegraph ├── forbid ├── graph ├── install-bitcoin-core-linux ├── package └── update-ord-dev ├── bip.mediawiki ├── contrib ├── initialize-opendime └── raw │ └── justfile ├── deploy ├── bitcoin.conf ├── bitcoind.service ├── checkout ├── ord-dev.service ├── ord.service └── setup ├── docs ├── book.toml └── src │ ├── SUMMARY.md │ ├── bounties.md │ ├── bounty │ ├── 0.md │ ├── 1.md │ ├── 2.md │ ├── 3.md │ └── frequency.tsv │ ├── contributing.md │ ├── digital-artifacts.md │ ├── donate.md │ ├── faq.md │ ├── guides.md │ ├── guides │ ├── collecting.md │ ├── collecting │ │ ├── images │ │ │ ├── sending_01.png │ │ │ ├── sending_02.png │ │ │ ├── sending_03.png │ │ │ ├── sending_04.png │ │ │ ├── sending_05.png │ │ │ ├── sending_06.png │ │ │ ├── troubleshooting_01.png │ │ │ ├── troubleshooting_02.png │ │ │ ├── validating_viewing_01.png │ │ │ ├── validating_viewing_02.png │ │ │ ├── wallet_setup_01.png │ │ │ ├── wallet_setup_02.png │ │ │ ├── wallet_setup_03.png │ │ │ ├── wallet_setup_04.png │ │ │ ├── wallet_setup_05.png │ │ │ ├── wallet_setup_06.png │ │ │ ├── wallet_setup_07.png │ │ │ └── wallet_setup_08.png │ │ └── sparrow-wallet.md │ ├── explorer.md │ ├── inscriptions.md │ ├── moderation.md │ └── sat-hunting.md │ ├── inscriptions.md │ ├── introduction.md │ └── overview.md ├── examples ├── alert.html ├── external-resources-are-blocked.html ├── h264.mp4 ├── navigation-to-external-pages-is-blocked.html └── top-navigation-is-blocked.html ├── fuzz ├── Cargo.lock ├── Cargo.toml └── fuzz_targets │ └── transaction_builder.rs ├── install.sh ├── justfile ├── logo-bw.svg ├── logo-wb.svg ├── ord.yaml ├── quickstart └── macos ├── rustfmt.toml ├── shell.nix ├── src ├── arguments.rs ├── bin │ └── main.rs ├── blocktime.rs ├── chain.rs ├── config.rs ├── decimal.rs ├── degree.rs ├── deserialize_from_str.rs ├── epoch.rs ├── fee_rate.rs ├── height.rs ├── index.rs ├── index │ ├── entry.rs │ ├── fetcher.rs │ ├── rtx.rs │ ├── updater.rs │ └── updater │ │ └── inscription_updater.rs ├── inscription.rs ├── inscription_id.rs ├── lib.rs ├── media.rs ├── object.rs ├── options.rs ├── outgoing.rs ├── page_config.rs ├── rarity.rs ├── representation.rs ├── sat.rs ├── sat_point.rs ├── subcommand.rs ├── subcommand │ ├── epochs.rs │ ├── find.rs │ ├── index.rs │ ├── info.rs │ ├── list.rs │ ├── parse.rs │ ├── preview.rs │ ├── server.rs │ ├── server │ │ └── error.rs │ ├── subsidy.rs │ ├── supply.rs │ ├── traits.rs │ ├── wallet.rs │ └── wallet │ │ ├── balance.rs │ │ ├── cardinals.rs │ │ ├── create.rs │ │ ├── inscribe.rs │ │ ├── inscriptions.rs │ │ ├── outputs.rs │ │ ├── receive.rs │ │ ├── restore.rs │ │ ├── sats.rs │ │ ├── send.rs │ │ ├── transaction_builder.rs │ │ └── transactions.rs ├── tally.rs ├── templates.rs ├── templates │ ├── block.rs │ ├── clock.rs │ ├── home.rs │ ├── iframe.rs │ ├── input.rs │ ├── inscription.rs │ ├── inscriptions.rs │ ├── output.rs │ ├── preview.rs │ ├── range.rs │ ├── rare.rs │ ├── sat.rs │ └── transaction.rs ├── test.rs └── wallet.rs ├── static ├── favicon.png ├── favicon.svg ├── index.css ├── index.js ├── modern-normalize.css ├── preview-audio.css ├── preview-pdf.css ├── preview-pdf.js ├── preview-text.css ├── preview-text.js └── preview-video.css ├── templates ├── block.html ├── clock.svg ├── home.html ├── input.html ├── inscription.html ├── inscriptions.html ├── output.html ├── page.html ├── preview-audio.html ├── preview-image.html ├── preview-pdf.html ├── preview-text.html ├── preview-unknown.html ├── preview-video.html ├── range.html ├── rare.txt ├── sat.html └── transaction.html ├── test-bitcoincore-rpc ├── Cargo.toml └── src │ ├── api.rs │ ├── lib.rs │ ├── server.rs │ └── state.rs └── tests ├── command_builder.rs ├── core.rs ├── epochs.rs ├── expected.rs ├── find.rs ├── index.rs ├── info.rs ├── lib.rs ├── list.rs ├── parse.rs ├── server.rs ├── subsidy.rs ├── supply.rs ├── test_server.rs ├── traits.rs ├── version.rs ├── wallet.rs └── wallet ├── balance.rs ├── cardinals.rs ├── create.rs ├── inscribe.rs ├── inscriptions.rs ├── outputs.rs ├── receive.rs ├── restore.rs ├── sats.rs ├── send.rs └── transactions.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | env: 16 | RUSTFLAGS: --deny warnings 17 | 18 | jobs: 19 | lint: 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Install rustfmt 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | components: rustfmt 29 | override: true 30 | toolchain: stable 31 | 32 | - name: Install clippy nightly 33 | uses: actions-rs/toolchain@v1 34 | with: 35 | components: clippy 36 | toolchain: nightly 37 | 38 | - uses: Swatinem/rust-cache@v2 39 | with: 40 | key: "clippy-nightly" 41 | 42 | - name: Clippy 43 | run: cargo +nightly clippy --all --all-targets 44 | 45 | - name: Format 46 | run: cargo fmt --all -- --check 47 | 48 | - name: Check for Forbidden Words 49 | run: | 50 | sudo apt-get install ripgrep 51 | ./bin/forbid 52 | 53 | core: 54 | runs-on: ubuntu-latest 55 | 56 | steps: 57 | - uses: actions/checkout@v2 58 | 59 | - name: Install Rust Toolchain Components 60 | uses: actions-rs/toolchain@v1 61 | with: 62 | profile: minimal 63 | toolchain: stable 64 | 65 | - uses: Swatinem/rust-cache@v2 66 | 67 | - name: Install Bitcoin Core 68 | run: ./bin/install-bitcoin-core-linux 69 | 70 | - name: Test 71 | run: cargo test --all -- --ignored 72 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | defaults: 9 | run: 10 | shell: bash 11 | 12 | jobs: 13 | release: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | target: 18 | - aarch64-apple-darwin 19 | - x86_64-apple-darwin 20 | - x86_64-pc-windows-msvc 21 | - x86_64-unknown-linux-gnu 22 | include: 23 | - target: aarch64-apple-darwin 24 | os: macos-latest 25 | target_rustflags: '' 26 | - target: x86_64-apple-darwin 27 | os: macos-latest 28 | target_rustflags: '' 29 | - target: x86_64-pc-windows-msvc 30 | os: windows-latest 31 | target_rustflags: '' 32 | - target: x86_64-unknown-linux-gnu 33 | os: ubuntu-latest 34 | target_rustflags: '' 35 | 36 | runs-on: ${{matrix.os}} 37 | 38 | steps: 39 | - uses: actions/checkout@v2 40 | 41 | - name: Install Rust Toolchain Components 42 | uses: actions-rs/toolchain@v1 43 | with: 44 | override: true 45 | target: ${{ matrix.target }} 46 | toolchain: stable 47 | 48 | - name: Install Linux Dependencies 49 | if: ${{ matrix.os == 'ubuntu-latest' }} 50 | run: | 51 | sudo apt-get update 52 | sudo apt-get install musl-tools libssl-dev pkg-config 53 | 54 | - name: Release Type 55 | id: release-type 56 | run: | 57 | if [[ ${{ github.ref }} =~ ^refs/tags/[0-9]+[.][0-9]+[.][0-9]+$ ]]; then 58 | echo ::set-output name=value::release 59 | else 60 | echo ::set-output name=value::prerelease 61 | fi 62 | 63 | - name: Package 64 | id: package 65 | env: 66 | TARGET: ${{ matrix.target }} 67 | REF: ${{ github.ref }} 68 | OS: ${{ matrix.os }} 69 | TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }} 70 | run: ./bin/package 71 | shell: bash 72 | 73 | - name: Publish Archive 74 | uses: softprops/action-gh-release@v0.1.15 75 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 76 | with: 77 | draft: false 78 | files: ${{ steps.package.outputs.archive }} 79 | prerelease: ${{ steps.release-type.outputs.value == 'prerelease' }} 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.vagrant 3 | /docs/build 4 | /fuzz/artifacts 5 | /fuzz/corpus 6 | /fuzz/coverage 7 | /fuzz/target 8 | /index.redb 9 | /ord.log 10 | /target 11 | /test-times.txt 12 | /tmp 13 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ignore everything 2 | /* 3 | # except docs 4 | !/docs 5 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Unless you explicitly state otherwise, any contribution intentionally 5 | submitted for inclusion in the work by you shall be licensed as in 6 | LICENSE, without any additional terms or conditions. 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ord" 3 | description = "◉ Ordinal wallet and block explorer" 4 | version = "0.5.2" 5 | license = "CC0-1.0" 6 | edition = "2021" 7 | autotests = false 8 | homepage = "https://github.com/casey/ord" 9 | repository = "https://github.com/casey/ord" 10 | autobins = false 11 | rust-version = "1.67" 12 | 13 | [workspace] 14 | members = [".", "test-bitcoincore-rpc"] 15 | 16 | [dependencies] 17 | anyhow = { version = "1.0.56", features = ["backtrace"] } 18 | axum = { version = "0.6.1", features = ["headers"] } 19 | axum-server = "0.4.0" 20 | base64 = "0.21.0" 21 | bech32 = "0.9.1" 22 | bip39 = "2.0.0" 23 | bitcoin = { version = "0.29.1", features = ["rand"] } 24 | boilerplate = { version = "0.2.3", features = ["axum"] } 25 | chrono = "0.4.19" 26 | clap = { version = "3.1.0", features = ["derive"] } 27 | ctrlc = "3.2.1" 28 | derive_more = "0.99.17" 29 | dirs = "5.0.0" 30 | env_logger = "0.10.0" 31 | futures = "0.3.21" 32 | hex = "0.4.3" 33 | html-escaper = "0.2.0" 34 | http = "0.2.6" 35 | hyper = { version = "0.14.24", features = ["http1", "client"] } 36 | indicatif = "0.17.1" 37 | lazy_static = "1.4.0" 38 | log = "0.4.14" 39 | mime = "0.3.16" 40 | mime_guess = "2.0.4" 41 | miniscript = "9.0.1" 42 | mp4 = "0.13.0" 43 | ord-bitcoincore-rpc = "0.16.5" 44 | redb = "0.13.0" 45 | regex = "1.6.0" 46 | rss = "2.0.1" 47 | rust-embed = "6.4.0" 48 | rustls = "0.20.6" 49 | rustls-acme = { version = "0.5.0", features = ["axum"] } 50 | serde = { version = "1.0.137", features = ["derive"] } 51 | serde_json = { version = "1.0.81" } 52 | serde_yaml = "0.9.17" 53 | sys-info = "0.9.1" 54 | tempfile = "3.2.0" 55 | tokio = { version = "1.17.0", features = ["rt-multi-thread"] } 56 | tokio-stream = "0.1.9" 57 | tokio-util = {version = "0.7.3", features = ["compat"] } 58 | tower-http = { version = "0.3.3", features = ["compression-br", "compression-gzip", "cors", "set-header"] } 59 | 60 | [dev-dependencies] 61 | executable-path = "1.0.0" 62 | pretty_assertions = "1.2.1" 63 | reqwest = { version = "0.11.10", features = ["blocking"] } 64 | test-bitcoincore-rpc = { path = "test-bitcoincore-rpc" } 65 | unindent = "0.2.1" 66 | 67 | [[bin]] 68 | name = "ord" 69 | path = "src/bin/main.rs" 70 | 71 | [lib] 72 | name = "ord" 73 | path = "src/lib.rs" 74 | 75 | [[test]] 76 | name = "integration" 77 | path = "tests/lib.rs" 78 | 79 | [build-dependencies] 80 | pulldown-cmark = "0.9.2" 81 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "debian/bullseye64" 3 | 4 | config.vm.provider "virtualbox" do |v| 5 | v.memory = 1024 * 4 6 | end 7 | 8 | config.vm.network "private_network", ip: "192.168.56.4" 9 | 10 | config.vm.provision "shell" do |s| 11 | s.inline = "" 12 | Dir.glob("#{Dir.home}/.ssh/*.pub").each do |path| 13 | key = File.read(path).strip 14 | s.inline << "echo '#{key}' >> /root/.ssh/authorized_keys\n" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /benchmark/checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | REV=$1 6 | 7 | if [[ ! -d ord ]]; then 8 | git clone https://github.com/casey/ord.git 9 | fi 10 | 11 | cd ord 12 | 13 | git fetch --all --prune 14 | git checkout master 15 | git reset --hard origin/master 16 | git checkout `git rev-parse origin/$REV` 17 | ./benchmark/run 18 | -------------------------------------------------------------------------------- /benchmark/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | systemctl stop ord-dev 6 | 7 | rm -rf /var/lib/ord-dev 8 | 9 | journalctl --unit ord-dev --rotate 10 | 11 | journalctl --unit ord-dev --vacuum-time 1s 12 | 13 | ./bin/update-dev-server 14 | -------------------------------------------------------------------------------- /bin/benchmark: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | rm -rf tmp/benchmark 6 | mkdir -p tmp/benchmark 7 | 8 | INDEX_SNAPSHOT=$1 9 | HEIGHT_LIMIT=$2 10 | 11 | cp $INDEX_SNAPSHOT tmp/benchmark/index.redb 12 | 13 | cargo build --release 14 | 15 | time ./target/release/ord --data-dir tmp/benchmark --height-limit $HEIGHT_LIMIT index 16 | -------------------------------------------------------------------------------- /bin/flamegraph: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | mkdir $1 6 | cd $1 7 | 8 | sudo \ 9 | CARGO_PROFILE_RELEASE_DEBUG=true \ 10 | RUST_LOG=info \ 11 | cargo flamegraph \ 12 | --deterministic \ 13 | --bin ord \ 14 | -- \ 15 | --chain signet \ 16 | --data-dir . \ 17 | --height-limit 0 \ 18 | index 19 | 20 | rm -f flamegraph.svg 21 | 22 | /usr/bin/time -o time sudo \ 23 | CARGO_PROFILE_RELEASE_DEBUG=true \ 24 | RUST_LOG=info \ 25 | cargo flamegraph \ 26 | --deterministic \ 27 | --bin ord \ 28 | -- \ 29 | --chain signet \ 30 | --data-dir . \ 31 | --height-limit 5000 \ 32 | index 33 | 34 | sudo chown -n $UID flamegraph.svg 35 | sudo chown -n $UID index.redb 36 | -------------------------------------------------------------------------------- /bin/forbid: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | which rg > /dev/null 6 | 7 | ! rg \ 8 | --glob '!bin/forbid' \ 9 | --glob '!docs/src/bounty/frequency.tsv' \ 10 | --ignore-case \ 11 | 'dbg!|fixme|todo|xxx' \ 12 | . 13 | -------------------------------------------------------------------------------- /bin/graph: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re, sys 4 | from matplotlib.pyplot import * 5 | from dataclasses import dataclass 6 | 7 | @dataclass 8 | class Block: 9 | height: int 10 | ranges: int 11 | time: int 12 | transactions: int 13 | 14 | pat = re.compile( 15 | '''Block (?P[0-9]+) at.*with (?P[0-9]+) transactions.* 16 | .*Wrote (?P[0-9]+) sat ranges from .* outputs in (?P