diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000000..e2964469a0 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,13 @@ +*.redb +.idea/ +.vagrant +docs/build +env +fuzz/artifacts +fuzz/corpus +fuzz/coverage +fuzz/target +*.log +target +test-times.txt +tmp diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000000..32ec71111d --- /dev/null +++ b/.cursorrules @@ -0,0 +1,52 @@ +# CAT-21 Ordinals Best Practices and Development Rules + +Purpose: To standardize development for LockTimeOrdinals on the ord client. +You are an expert Rust developer with deep knowledge of Bitcoin and the Ordinals protocol. + +## General +- Ensure the code follows Rust's best practices and idiomatic style. +- Write meaningful and concise comments for complex logic, especially around CAT-21 logic. +- Avoid hardcoding values; define constants where applicable (e.g., LOCK_TIME_THRESHOLD). +- Extend existing indexer logic to track LockTimeOrdinals without disrupting other ord features. +- Use code-comments like "// CAT-21 😺 - START" and "// CAT-21 😺 - END" to indicate the start and end of the CAT-21 logic. +- The code should be easily mergeable with the main branch of the ord client. Avoid changing existing line of code if possible, for better mergeability. +- Chose to keep LTO logic modular and extensible for potential future protocols. +- Focused on minimizing changes to the core `ord` client to maintain compatibility. +- Don't remove existing code comments, keep them as they are if still relevant, update them if functionality has changed. + +- **nLockTime Handling**: + - Track transactions with nLockTime values between 0 and a predefined upper limit LOCK_TIME_THRESHOLD (e.g., 1000). + - The satoshis / sats are called LockTimeOrdinals ("LTO"). + - A LockTimeOrdinal is always created / minted for the the first satoshi of the first output of the transaction. + - LTOs are tied to the first sat of the first output, ignoring whether it is an OP_RETURN output, + but introduces cases where LTOs are "burned" on creation + +- **Integration with ord Client**: + - Reuse existing SatPoint tracking mechanisms wherever possible to avoid duplicating logic. + - Mimic inscription tracking logic to extend functionality for LockTimeOrdinals. + +# Database and Indexing +Rules for interacting with the database: +- Use `WriteTransaction` for all atomic operations related to indexing. +- Commit transactions only after all operations for a LockTimeOrdinal are complete. +- Ensure database schemas are extendable to accommodate future changes. +- Ensure all LockTimeOrdinal entities are stored in `LOCKTIME_ORDINAL_TABLE` and `LOCK_TIME_TO_NUMBER` and other tables. + + +# Error Handling +Guidelines for handling errors: +- Use `Result` types consistently for error propagation. +- Provide detailed error messages, especially when dealing with database transactions. +- Write unit tests for edge cases, such as transactions with no outputs or invalid lockTime values. + +# Testing +- Write robust tests to validate all features +- Use mock transactions for testing to avoid reliance on live Bitcoin RPC data. + +# Documentation +Maintain clear and concise documentation: +- Document all new structs, enums, and functions related to LockTimeOrdinals. +- Keep in mind the `docs-cat21/developer-howto.md` file for onboarding developers. Update it when relevant. +- Keep in mind the `docs-cat21/development-summary.md` file for for a detailed documentation of this project and notable changes to the ord client. Update it when relevant. +- Include inline comments where complex logic is implemented. +- Further Documentation, see the `docs-cat21` folder. diff --git a/README.md b/README.md index e03e5b2107..3570814345 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ -`ord` +`cat21-ord` ===== +![Orange Banner](https://raw.githubusercontent.com/ordpool-space/cat-21/main/assets/cat21-banner.svg) + +This is a hacked version of [ord](https://github.com/ordinals/ord), that indexes [CAT-21 ordinals](https://github.com/ordpool-space/cat-21). Meow! 😺 + +In general, there is no difference to the normal ord, except that cats are also indexed. +If you want to learn how to set up an ord instance, [refer to this guide](https://gist.github.com/hans-crypto/30d05b9dcb3c05940e9a8db2e365da1e) or any other guide in offical ord Discord. Of course you need the executable from this repo if ord should also index cats. + +If you are a coder (or want to run/build the executable), [read on here](README_developer). + +--- + `ord` is an index, block explorer, and command-line wallet. It is experimental software with no warranty. See [LICENSE](LICENSE) for more details. diff --git a/deploy-ord-dev/bitcoind.service b/deploy-ord-dev/bitcoind.service new file mode 100644 index 0000000000..b399259725 --- /dev/null +++ b/deploy-ord-dev/bitcoind.service @@ -0,0 +1,13 @@ +[Unit] +Description=Bitcoin Daemon +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/home/linuxbrew/.linuxbrew/opt/bitcoin/bin/bitcoind +User=ord-dev +Restart=on-failure +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/deploy-ord-dev/ord.service b/deploy-ord-dev/ord.service new file mode 100644 index 0000000000..151c31c8a3 --- /dev/null +++ b/deploy-ord-dev/ord.service @@ -0,0 +1,19 @@ +[Unit] +Description=Ord Daemon +After=network.target + +[Service] +AmbientCapabilities=CAP_NET_BIND_SERVICE +Environment=RUST_BACKTRACE=1 +Environment=RUST_LOG=info +ExecStart=/home/ord-dev/cat21-ord/target/release/ord \ + --index-sats \ + --index-addresses \ + --no-index-inscriptions \ + server \ + --http +User=ord-dev +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/docs-cat21/0-developer-howto.md b/docs-cat21/0-developer-howto.md new file mode 100644 index 0000000000..65756eb3ee --- /dev/null +++ b/docs-cat21/0-developer-howto.md @@ -0,0 +1,225 @@ +# Developer HowTo for `cat21-ord` + +Super quick howto to jump into coding (or building the executable). + + +## 0. Prerequisites + +VS Code and/or Cursor is recommended for development. +Read more about Rust development with VS Code here: +https://code.visualstudio.com/docs/languages/rust + + +## 1. Clone the repo + +```sh +cd ~ +git clone https://github.com/ordpool-space/cat21-ord.git +cd cat21-ord +``` + + +## 2. Debugging with VS Code (while using a remote Bitcoin RPC) on a Mac + +> You want this option when you want to develop new features. +> On a normal working machine, with normal hardware (less than 64 GB RAM). + +1. Install the VS Code extension rust-analyzer: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer and this if you are on a Mac: https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb + +2. Install rust `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`, follow the instructions to configure your current shell. + +3. `rustc --version` must work! + +4. `cd cat21-ord` into the repo + +5. `cargo build`, once built, the `ord` binary (debug build) can be found at `./target/debug/ord`. + +6. `cargo run` + +7. You'll need to enable the setting `Debug: Allow Breakpoints Everywhere`, which you can find in the Settings editor (`⌘,`) by searching on 'everywhere`. + +8. Generate a new `launch.json` by trying to debug (VS Code should create one for you) + +9. Add a configuration like this to your `launch.json` + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'ord' INDEX-SATS (Localhost RPC Setup)", + "cargo": { + "args": [ + "build", + "--bin=ord", + "--package=ord" + ], + "filter": { + "name": "ord", + "kind": "bin" + } + }, + "args": [ + "--index-sats", + "--index-addresses", + "--no-index-inscriptions", + "--bitcoin-rpc-username=xxx", + "--bitcoin-rpc-password=yyy", + "server" + ], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'ord' INDEX-SATS (Remote RPC Setup)", + "cargo": { + "args": [ + "build", + "--bin=ord", + "--package=ord" + ], + "filter": { + "name": "ord", + "kind": "bin" + } + }, + "args": [ + "--index-sats", + "--index-addresses", + "--no-index-inscriptions", + "--bitcoin-rpc-url=192.0.2.1:8332", + "--bitcoin-rpc-username=xxx", + "--bitcoin-rpc-password=yyy", + "server" + ], + "cwd": "${workspaceFolder}" + } + ] +} +``` + +## 3. Building & Running a `cat21-ord` server (not a normal ord) on Linux + +> You want this option when you want to build the full damn index. +> On a beafy server, 64 GB RAM, 2TB SSD. Do not try lower hardware! + +1. Bitcoin must be up and running, see `1_bitcoin-guide-for-mac.md` or `1_bitcoin-guide-for-linux.md`. + + Feel free to use the files in `deploy-ord-dev` for a fast setup: + + ``` + sudo cp ./deploy-ord-dev/bitcoind.service /etc/systemd/system/ + sudo systemctl daemon-reload + sudo systemctl enable bitcoind + sudo systemctl start bitcoind + sudo systemctl status bitcoind + + tail -F -n 10000 ~/.bitcoin/debug.log + ``` + +2. Install rust via curl (rustup) or homebrew, both should work: + + * Rustup is officially recommended. + `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` to install. + Follow the instructions to configure your current shell. + But then you realise that you still need to take care of the dependencies, such as OpenSSL. + So you might want to execute `rustup self uninstall` to uninstall, and use brew again! 😇 + Check your path via `echo $PATH` to see if it's clean again. + + * `brew install rust` requires no additional steps + +3. `rustc --version` must work! + +4. `cd cat21-ord` into the repo + +5. `cargo build --release`, once built, the `ord` binary (release build) can be found at `./target/release/ord`. + +6. You can now execute the CAT-21 version of `ord`: + + * Running the binary directly: `./target/release/ord --version` + * Running via cargo: `cargo run --release -- --version` (If you’ve made changes to the code, cargo run will automatically rebuild the project before running it, ensuring you're always running the latest version of your code. If you don't make changes to the code, both options are fine.) + +7. Start the indexing: + + ```sh + ./target/release/ord \ + --index-sats \ + --index-addresses \ + --no-index-inscriptions \ + server \ + --http-port 8080 + ``` + +9. Run the ord daemon permanently: + + ```sh + sudo cp /home/ord-dev/cat21-ord/deploy-ord-dev/ord.service /etc/systemd/system/ + sudo systemctl daemon-reload + sudo systemctl enable ord + sudo systemctl start ord + sudo systemctl status ord + ``` + + To view the full logs for your `ord` service managed by `systemd`, you can use `journalctl`, which collects logs for all systemd-managed services. + This will show the complete log history: + + ```sh + sudo journalctl -u ord + ``` + + Or follow the logs in real-time (like `tail -f`): + + ``` + sudo journalctl -u ord -f + ``` + + To delete logs older than a specific time period, such as 1 week: + ``` + sudo journalctl --vacuum-time=1w + ``` + + To confirm the size of logs: + + ```bash + sudo journalctl --disk-usage + ``` + + You can limit the size of `journalctl` logs to prevent them from growing indefinitely. + + To set a Disk Space Limit, edit the `journald.conf` file: + + ```bash + sudo nano /etc/systemd/journald.conf + ``` + + Set or modify the following values: + + ```ini + SystemMaxUse=200M + SystemKeepFree=50M + SystemMaxFileSize=50M + ``` + + - **`SystemMaxUse`**: Limits the total space used by journal logs. + - **`SystemKeepFree`**: Ensures this much disk space remains free. + - **`SystemMaxFileSize`**: Limits the size of individual log files. + + Save and exit the file. Then restart the `systemd-journald` service: + + ```bash + sudo systemctl restart systemd-journald + ``` + + Manually clean up logs to free space immediately: + + ```bash + sudo journalctl --vacuum-size=200M + ``` + + +### Personal Notepad with intersting links + +* [Add option to retain sat index for spent outputs #2999](https://github.com/ordinals/ord/pull/2999) diff --git a/docs-cat21/1_bitcoin-guide-for-linux.md b/docs-cat21/1_bitcoin-guide-for-linux.md new file mode 100644 index 0000000000..8dcf58f362 --- /dev/null +++ b/docs-cat21/1_bitcoin-guide-for-linux.md @@ -0,0 +1,257 @@ +# Bitcoin / Ord Guide for Linux + +Main source for this: [Homebrew appreciation thread for Bitcoin and ord on Mac and Linux by ETS](https://discord.com/channels/987504378242007100/1078719003624747108) – **This whole guide is a blatant copy & paste, I deserve zero credits!** + +This guide assumes a 2TB primary SSD and 64GB of Ram. Even machines with 32GB+ sometimes struggle these days. Go for 64GB! +I did this installation on a "debian 12 (bookworm) - minimal" VPS. + +## 0️⃣ Only if required: Installing Curl on Debian + +First apply patches for your system: + +```sh +sudo apt update && sudo apt upgrade +``` + +Install `curl` (necessary) and other build tools: + +```sh +apt install build-essential procps curl file git + +curl --version +``` + + +## 1️⃣ INSTALL HOMEBREW + +Install Homebrew from http://brew.sh/, the webpage should tell you to execute: + +``` +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +If you get the error: **"Don't run this as root!"** then you are logged in as "root". The issue here is that Homebrew does not recommend or allow installation as the root user for security reasons. Instead, it should be installed under a regular user account with sudo privileges. + +Here's how you can resolve the issue: + +Create a new user (if you don't already have one) and give it sudo privileges. Here's how to do that for a user called `ord-dev` + +``` +adduser ord-dev +``` + +Follow the prompt to set up a password. +Add the user to the sudo group: + +```sh +usermod -aG sudo ord-dev +``` + +Switch to the new user: + +```sh +su - ord-dev +``` + +Now try again: + +``` +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +⚠️ When installation completes, follow the steps listed in "Next steps" to add Homebrew to your path. + +## 2️⃣ INSTALL BITCOIN + +After Homebrew installation and "Next steps", run this from terminal: + +```sh +brew install bitcoin +``` + +## 3️⃣ CONFIGURE BITCOIN.CONF + +Run these commands in Terminal: + +```sh +mkdir ~/.bitcoin/bitcoin.conf + +echo txindex=1 >> ~/.bitcoin/bitcoin.conf +echo server=1 >> ~/.bitcoin/bitcoin.conf +echo mempoolfullrbf=1 >> ~/.bitcoin/bitcoin.conf +``` + +* `txindex=1` If you want to be able to access any transaction with commands like gettransaction , you need to configure Bitcoin Core to build a complete transaction index, which can be achieved with the txindex option. +* `server=1` tells bitcoin to accept JSON-RPC commands, so you can query it + +Confirm all settings with: + +```sh +cat ~/.bitcoin/bitcoin.conf +``` + +## 4️⃣ START/STOP BITCOIN SERVICE + +In terminal: + +```sh +brew services start bitcoin +``` + +and this if you want to stop it again (not yet) + +```sh +brew services stop bitcoin +``` + +If this works you are lucky and can skip section 5. + +## 5️⃣ TROUBLESHOOTING (for Debian) + +How to fix this error: + +```sh +Failed to connect to bus: No medium found +Error: Failure while executing; `/usr/bin/systemctl --user daemon-reload` exited with 1. +``` + +This error occurs because `systemctl` (used by brew services) requires `systemd` to be set up for user sessions, which is not always enabled by default in systems like Debian, especially in minimal or server installations. Switching to `systemctl ` for managing services like `bitcoin` and `ord` is a more sustainable and reliable solution on Debian. Homebrew works well for package management, but it struggles with service management on Linux, particularly in systems like Debian that don’t fully support `systemctl --user` by default. + +```sh +sudo nano /etc/systemd/system/bitcoind.service +``` + +Add the following content: + +```ini +[Unit] +Description=Bitcoin Daemon +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/home/linuxbrew/.linuxbrew/opt/bitcoin/bin/bitcoind +User=ord-dev +Restart=on-failure +Type=simple + +[Install] +WantedBy=multi-user.target +``` + +Then execute the following commands to start the daemon: + +```sh +sudo systemctl daemon-reload +sudo systemctl enable bitcoind +sudo systemctl start bitcoind +sudo systemctl status bitcoind +``` + +While we are here, this is a working the configution for `ord` (that was installed via homebrew): + +```sh +sudo nano /etc/systemd/system/ord.service +``` + +Add the following content: + +```ini +[Unit] +Description=Ord Daemon +After=network.target + +[Service] +AmbientCapabilities=CAP_NET_BIND_SERVICE +Environment=RUST_BACKTRACE=1 +Environment=RUST_LOG=info +ExecStart=/home/linuxbrew/.linuxbrew/bin/ord \ + --index-runes \ + --index-sats \ + --index-addresses \ + server \ + --http +User=ord-dev +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +`AmbientCapabilities=CAP_NET_BIND_SERVICE:` This grants the `ord` service the specific capability to bind to privileged ports (like port 80) without running as root. The default location of the index is: `~/.local/share/ord`. + +Run the ord daemon permanently: + +```sh +sudo systemctl daemon-reload +sudo systemctl enable ord +sudo systemctl start ord +sudo systemctl status ord +``` + +To view the full logs for your `ord` service managed by `systemd`, you can use `journalctl`, which collects logs for all systemd-managed services. +This will show the complete log history: + +```sh +sudo journalctl -u ord +``` + +Or follow the logs in real-time (like `tail -f`): + +``` +sudo journalctl -u ord -f +``` + +To delete logs older than a specific time period, such as 1 week: +``` +sudo journalctl --vacuum-time=1w +``` + +## 6️⃣ CONFIRM SETTINGS + +In the `debug.log` file located in `~/.bitcoin`, look for lines similar to these: + +```log +2024-09-24T19:28:09Z Default data directory /home/ord-dev/.bitcoin +2024-09-24T19:28:09Z Using data directory /home/ord-dev/.bitcoin +2024-09-24T19:28:09Z Config file: /home/ord-dev/.bitcoin/bitcoin.conf +2024-09-24T19:28:09Z Config file arg: server="1" +2024-09-24T19:28:09Z Config file arg: txindex="1" +2024-09-24T19:28:09Z Generated RPC authentication cookie /home/ord-dev/.bitcoin/.cookie +``` + +⚠️ An easy way to monitor the file in Terminal is to use this command: + +```sh +tail -F -n 10000 ~/.bitcoin/debug.log +``` + +These entries in the output confirm that the configuration entries have been recognized and have taken effect. Pay attention to the cookie line. You'll need that later, so make sure you have it! + +## 7️⃣ INITIAL BLOCK DOWNLOAD (aka BLOCKCHAIN SYNC) + +The initial block download will generally take 1+ days, depending on factors like CPU/disk/network speed. As it progresses, you'll see lines like this in the debug.log file: + +```sh +2023-02-22T15:18:23Z UpdateTip: new best=00000000000000000000e2319131e7e41d3b93e8b9086fc427f2ee9383aa2686 height=777656 version=0x20004000 log2_work=94.017345 tx=807469638 date='2023-02-21T14:40:00Z' progress=0.999679 cache=2.5MiB(18835txo) +``` + +The `progress=` entry tells you how far you are along. It will reach 1.000000 at completion of the initial block download and then you can leave bitcoin running as a service and proceed to the ord installation and configuration. + +## 7️⃣ EXTRA: FINAL CONFIRMATION OF READINESS + +Run this in terminal: + +`bitcoin-cli getindexinfo` + +You should see a response like this: + +```json +{ + "txindex": { + "synced": true, + "best_block_height": 839505 +} +``` + +If it shows `synced:false` or the block height is not current, wait longer and run the command again. If it shows `synced:true` and current block height, proceed to ord installation! diff --git a/docs-cat21/1_bitcoin-guide-for-mac.md b/docs-cat21/1_bitcoin-guide-for-mac.md new file mode 100644 index 0000000000..0a30a4a9e7 --- /dev/null +++ b/docs-cat21/1_bitcoin-guide-for-mac.md @@ -0,0 +1,185 @@ +# Bitcoin / Ord Guide for Mac + +Main source for this: [Mac beginner's guide by ETS](https://discord.com/channels/987504378242007100/1078719003624747108) – **This whole guide is a blatant copy & paste, I deserve zero credits!** + +This guide mostly assumes a primary SSD and an external SSD, formatted as APFS. If you want to follow the commands verbatim, name your external SSD volume as **ord-dev**. You'll still need ~100 GB free on your internal disk, as only the bitcoin blocks are moved to the external SSD. This configuration allows for moving the bulk of the storage needs to the external but leaving enough in the default locations to make commands easier. + +⚠️ This guide won't work well if your primary or external storage are on a spinning disk. SSD only! **Like really, you will regret your wasted time otherwise!** Also make sure that you have a lot of memory – otherwise ord will never finish. Even machines with 32 GB+ are struggling sometimes these days. Go for 64 GB! + +## 1️⃣ INSTALL HOMEBREW + +Install Homebrew from http://brew.sh/ +During installation you may be prompted for a password. This is your main Mac password and you won't see any letters as you type it. After typing it, press enter to continue. + +⚠️ When installation completes, follow the steps listed in "Next steps" to add Homebrew to your path. + +## 2️⃣ INSTALL BITCOIN + +After Homebrew installation and "Next steps", run this from terminal: + +```sh +brew install bitcoin +``` + +## 3️⃣ CREATE BLOCKS DIR + +⚠️Skip this step if no external SSD. + +After the bitcoin installation, create your bitcoin blocks directory on your external SSD (most Macs don't have enough storage on their internal SSDs. 1 TB free for comfort.) Name the folder `bitcoin`. If your external SSD volume name has a space, rename that, too. No spaces! In this guide, the external SSD has the name `ord-dev`! + +## 4️⃣ CONFIGURE BITCOIN.CONF + +Run these commands in Terminal: + +```sh +mkdir ~/Library/Application\ support/Bitcoin + +echo blocksdir=/Volumes/ord-dev/Bitcoin > ~/Library/Application\ Support/Bitcoin/bitcoin.conf +echo txindex=1 >> ~/Library/Application\ Support/Bitcoin/bitcoin.conf +echo server=1 >> ~/Library/Application\ Support/Bitcoin/bitcoin.conf +echo mempoolfullrbf=1 >> ~/Library/Application\ Support/Bitcoin/bitcoin.conf + +echo blocksdir=/Volumes/ord-dev/Bitcoin\\ntxindex=1\\nserver=1\\n > ~/Library/Application\ support/Bitcoin/bitcoin.conf +``` + +(where the `/ord-dev/Bitcoin` part is the actual path to the SSD and folder you created above. CAREFUL MODIFYING THIS COMMAND, EVERY SPACE AND SLASH AND ~ IS NEEDED!) + +⚠️ If you don't have an external drive, then skip the `blocksdir` line! + +* `txindex=1` If you want to be able to access any transaction with commands like gettransaction , you need to configure Bitcoin Core to build a complete transaction index, which can be achieved with the txindex option. +* `server=1` tells bitcoin to accept JSON-RPC commands, so you can query it + +Confirm all settings with: + +```sh +cat ~/Library/Application\ Support/Bitcoin/bitcoin.conf +``` + + +## 5️⃣ START/STOP BITCOIN SERVICE + +In terminal: + +```sh +brew services start bitcoin +``` + +and this if you want to stop it again (not yet) + +```sh +brew services stop bitcoin +``` + +## 6️⃣ CONFIRM SETTINGS + +In the `debug.log` file located in `~/Library/Application\ Support/Bitcoin/`, look for lines similar to these: + +```log +2023-11-15T13:16:47Z Default data directory /Users/username/Library/Application Support/Bitcoin +2023-11-15T13:16:47Z Using data directory /Users/username/Library/Application Support/Bitcoin +2023-11-15T13:16:47Z Config file: /Users/username/Library/Application Support/Bitcoin/bitcoin.conf +2023-11-15T13:16:47Z Config file arg: blocksdir="/Volumes/ord-dev/Bitcoin" +2023-11-15T13:16:47Z Config file arg: server="1" +2023-11-15T13:16:47Z Config file arg: txindex="1" +2023-11-15T13:16:47Z Generated RPC authentication cookie /Users/username/Library/Application Support/Bitcoin/.cookie +``` + +⚠️ An easy way to monitor the file in Terminal is to use this command: + +```sh +tail -F -n 10000 ~/Library/Application\ Support/Bitcoin/debug.log +``` + +These entries in the output confirm that the blocks dir and the configuration entries have been recognized and have taken effect. Pay attention to the cookie line. You'll need that later, so make sure you have it! + +## 7️⃣ INITIAL BLOCK DOWNLOAD (aka BLOCKCHAIN SYNC) + +The initial block download will generally take 1+ days, depending on factors like CPU/disk/network speed. As it progresses, you'll see lines like this in the debug.log file: + +```sh +2023-02-22T15:18:23Z UpdateTip: new best=00000000000000000000e2319131e7e41d3b93e8b9086fc427f2ee9383aa2686 height=777656 version=0x20004000 log2_work=94.017345 tx=807469638 date='2023-02-21T14:40:00Z' progress=0.999679 cache=2.5MiB(18835txo) +``` + +The `progress=` entry tells you how far you are along. It will reach 1.000000 at completion of the initial block download and then you can leave bitcoin running as a service and proceed to the ord installation and configuration. + +## 8️⃣ EXTRA: FINAL CONFIRMATION OF READINESS + +Run this in terminal: + +`bitcoin-cli getindexinfo` + +You should see a response like this: + +```json +{ + "txindex": { + "synced": true, + "best_block_height": 839505 +} +``` + +If it shows `synced:false` or the block height is not current, wait longer and run the command again. If it shows `synced:true` and current block height, proceed to ord installation! + +## 9️⃣ EXTRA: Setting Up Bitcoin Core to Accept Remote Procedure Calls (RPC) from an External Host + +Here are some tips for getting the bitcoin core node up and running and accepting RPC connections from external hosts. +This is not an efficient setup in combination with `ord` (it should be much faster when everything runs on the same machine), but useful for other development tasks. + +`~/Library/Application\ Support/Bitcoin/bitcoin.conf` (mac). +`/bitcoin/.bitcoin/bitcoin.conf` (linux) + +```ini +server=1 +rpcbind=0.0.0.0 +rpcallowip= +rpcport=8332 +rpcauth=: +txindex=1 +mempoolfullrbf=1 +``` + +#### server=1 + +This tells tells Bitcoin to accept JSON-RPC commands. + +#### rpcbind=0.0.0.0 + +This will tell Bitcoin to listen an every available network interface. If you would like to only accept connections on a specific network interface, replace `0.0.0.0` with the IP of your desired network interface. + +#### rpcallowip= + +With `rpcallowip` you can either specify an IP range in CIDR format, or specify the rpcallowip option multiple times with distinct IP addresses that you want to allow connections from. + +Note: Wildcards are no longer supported. You need to use subnets now. +To allow everything that would be `0.0.0.0/0` (ipv4) or **`::/0`** (ipv6). +Of course, this is not save to expose to untrusted networks such as the public internet! + +#### rpcport=8332 + +This sets the port that Bitcoin will listen on for RPC connections. Port 8332 is the default. + +#### rpcauth=: + +This configuration sets a username and password (hashed) to be used for authenticating RPC requests. +[Bitcoin Core RPC Auth Config Generator](https://jlopp.github.io/bitcoin-core-rpc-auth-generator/) is a useful tool for generating the hashed passwords. + +#### txindex=1 + +With `txindex=1` Bitcoin Core maintains an index of all transactions that have ever happened, which you can query using the remote procedure call (RPC) method `getrawtransaction ` or the RESTful API call `get-tx `. + +Next, restart the bitcoind service! + +Then, on the machine that you’ll be making RPC requests from, make a test request using `curl`: + +```sh +curl -f --user --data-binary \ + '{"method":"getblockhash","params":[0],"id":1}' \ + -H 'content-type: text/plain;' http://:/ +``` + +In the above request, replace the following values: + +* **``** – Replace this with the value you specified for rpcuser in your bitcoin.conf file. +* **`:`** - Replace **``** with the IP address or hostname of your Bitcoin server and **``** with the value you specified for `rpcport` in your `bitcoin.conf` file Hint: execute `ifconfig` to figure out your IP address. + +If everything is set up correctly, after running the `curl` command, you should be prompted for your RPC password and Bitcoin will respond with a valid result. diff --git a/docs-cat21/2_ord-guide.md b/docs-cat21/2_ord-guide.md new file mode 100644 index 0000000000..09dbc14d9f --- /dev/null +++ b/docs-cat21/2_ord-guide.md @@ -0,0 +1,61 @@ +## 1️⃣ INSTALL ORD + +Run this command in terminal: + +```sh +brew install ord +``` + +Confirm success by typing this in terminal: + +```sh +ord --version +``` + +You should get a response like this (or a higher version): + +```sh +ord 0.20.0 +``` + +## 2️⃣ CREATE ORD INDEX + +⚠️ If you don't have an internal SSD (you have an internal spinning disk) this may not work well. Seek assistance to relocate your index file with the --index switch. Doing so will change all following ord commands in this guide, as well. + +We will create a full-blown ord index with sats and runes on your internal SSD. Run this command: + +```sh +ord --index-runes --index-sats --index ~/ordindex.redb server +``` + +If all's well you'll see an indexing progress bar. It will start fast and slow down considerably. If it completes, great -- often it doesn't if your computer's resources are constrained. If it slows or appears to stop--WAIT LONGER. A trick that seems to work if it completely bombs out at the end or stops for many hours, is to press ctrl-c (only once!) when it's a few blocks from completing, then give it time to exit gracefully, reboot and run it again. Repeat until successful. + +#### --index-sats +Track location of all satoshis. Note: This tracks the current location of the sat, not the history. Spent outputs won't show sat-ranges. + + + +#### --index-addresses + +Track unspent output addresses. This allows you to view the contents of an address within ord. + +## JUST DOWNLOAD AN ORD INDEX + +Tired of waiting? Ok, there are Ordinals Index files pre-built by Greg. +These are the pre-built `index.redb` files, ready for download: + +https://ordstuff.info/ + +Thanks Greg! 🙏 + +Yeah, that's just the .torrent file. You need to plug that into a torrent app to download the multi-gigabyte .redb.gz file. + +Two possible clients for Mac: +* [Transmission](https://transmissionbt.com/) +* [qbittorrent](https://www.qbittorrent.org/) diff --git a/docs-cat21/development-summary.md b/docs-cat21/development-summary.md new file mode 100644 index 0000000000..5a9d6f0d7e --- /dev/null +++ b/docs-cat21/development-summary.md @@ -0,0 +1,85 @@ + +# CAT-21 and LockTimeOrdinal Development Summary + +## 1. What is CAT-21 and LockTimeOrdinal Protocols? + +### CAT-21 Overview +- **CAT-21** is an fun protocol that extends Bitcoin Ordinals by leveraging the `nLockTime` field in Bitcoin transactions. +- It focuses on enabling new digital asset types by associating sats with transactions having specific `nLockTime` values. +- These assets, called **LockTimeOrdinals (LTOs)**, are sats tied to transactions with a defined `nLockTime` range. + +### LockTimeOrdinal Rules +- LTOs are created for transactions with an `nLockTime` between `0` and a hardcoded upper limit (e.g., `10000`). +- Each transaction generates one LTO, tied to the **first sat of the first output**. +- Attributes of an LTO include: + - Transaction ID + - `nLockTime` value + - Incremental number (per `nLockTime` value) + - Block ID, height, and timestamp + - Fee, size, and weight of the transaction + - Value of the first output + - The satoshi and address associated with the first output + +### Purpose and Applications +- The first prototocl that uses the `nLockTime` field to create new asset types is CAT-21 with nLockTime set to 21. +- CAT-21 opens the door for new protocols and asset types. It's a proof of concept that shows how the `nLockTime` field can be used to create new asset types. + +--- + +## 2. Notable changes to the `ord` Client (to be continued) + +### New Entities and Structures +- **`LockTimeOrdinalEntry`**: Represents an indexed LTO +- **Database Table**: Added `LOCKTIME_ORDINAL_TABLE` to store `LockTimeOrdinalEntry` data. +- **Database Table**: Added `LOCK_TIME_TO_NUMBER is used to maintain a mapping between specific nLockTime values and their respective incrementing ordinal numbers. + + +### Description of the `LOCKTIME_ORDINAL_TABLE` table + +Stores metadata for each LockTimeOrdinal (LTO) created, ensuring all relevant data about the LTO is persistently tracked and retrievable. + +```rust +define_table! { LOCKTIME_ORDINAL_TABLE, &[u8; 32], LockTimeOrdinalEntryValue } + + +### Description of the `LOCK_TIME_TO_NUMBER` table + +The `LOCK_TIME_TO_NUMBER` table is used to maintain a mapping between specific +`nLockTime` values and their respective incrementing ordinal numbers for LockTimeOrdinal (LTO) assets. + This table is essential to ensure that each `nLockTime` value is tracked independently and has its own sequence of LTO numbers. + +#### Purpose +- **Track Ordinal Numbers per `nLockTime`:** Assign and increment a unique ordinal number for each `nLockTime` value. +- **Persistence Across Runs:** Ensure that the numbering continues correctly even if the indexing process is restarted. +- **Efficient Lookups:** Quickly fetch the next number for a given `nLockTime` without recalculating or keeping data in memory. + +#### Table Structure +```rust +define_table! { LOCK_TIME_TO_NUMBER, u32, u32 } +``` + +#### Fields +1. **Key (`u32`):** + - Represents the `nLockTime` value. + - Serves as the unique identifier for this mapping. + - Example: `21` for `nLockTime=21`. + +2. **Value (`u32`):** + - Represents the next available ordinal number for the given `nLockTime`. + - Starts from `0` and increments as new LockTimeOrdinals are created. + +#### Usage +1. **Insert or Increment Logic:** + - If a specific `nLockTime` exists in the table, the value is incremented to determine the next ordinal number. + - If the `nLockTime` is not present, it is initialized with a value of `0`. + +2. **Example Workflow:** + - If `LOCK_TIME_TO_NUMBER` contains an entry `(21, 5)`, it means the next ordinal number for `nLockTime=21` is `5`. + - When a new LockTimeOrdinal is created for `nLockTime=21`, the table is updated to `(21, 6)`. + +3. **Querying:** + - The table is queried whenever a transaction with a specific `nLockTime` is processed. + - Ensures that each LockTimeOrdinal is assigned the correct ordinal number. + +4. **Consistency:** + - Since the table operates within a transaction (`WriteTransaction`), it ensures that updates are atomic and consistent. diff --git a/src/index.rs b/src/index.rs index 16122a6ee8..06bb7ad9d4 100644 --- a/src/index.rs +++ b/src/index.rs @@ -47,6 +47,10 @@ mod rtx; mod updater; mod utxo_entry; +// CAT-21 😺 - START +mod cat21; +// CAT-21 😺 - END + #[cfg(test)] pub(crate) mod testing; diff --git a/src/index/cat21.rs b/src/index/cat21.rs new file mode 100644 index 0000000000..d805c43a64 --- /dev/null +++ b/src/index/cat21.rs @@ -0,0 +1,191 @@ +use super::*; +use crate::index::updater::BlockData; + +// CAT-21 😺 - START + +// to track transactions with nLockTime values between 0 and this upper limit +pub(crate) const LOCK_TIME_THRESHOLD: u32 = 1000; + +// *** also ssee /src/index.ts (where the other tables are defined) +define_table! { LOCKTIME_ORDINAL_TABLE, &TxidValue, LockTimeOrdinalEntryValue } +define_table! { LOCK_TIME_TO_NUMBER, u32, u32 } // u32 for lock_time, u32 for incrementing number + +// *** also see /src/entry.rs (where the other entries are defined) +#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] +pub struct LockTimeOrdinalEntry { + pub transaction_id: Txid, // 1. + pub lock_time: u32, // 2. + pub number: u32, // 3. + pub block_height: u32, // 4. + pub block_time: u32, // 5. + pub fee: u64, // 6. + pub size: u64, // 7. + pub weight: u64, // 8. + pub value: u64, // 9. + pub sat: u64, // 10. +} + +pub(crate) type LockTimeOrdinalEntryValue = ( + TxidValue, // 1. transaction id + u32, // 2. lock time + u32, // 3. number + u32, // 4. block height + u32, // 5. block time + u64, // 6. fee + u64, // 7. size + u64, // 8. weight + u64, // 9. value + u64, // 10. sat +); + +impl Entry for LockTimeOrdinalEntry { + type Value = LockTimeOrdinalEntryValue; + + fn load( + ( + transaction_id, // 1. + lock_time, // 2. + number, // 3. + block_height, // 4. + block_time, // 5. + fee, // 6. + size, // 7. + weight, // 8. + value, // 9. + sat, // 10. + ): LockTimeOrdinalEntryValue, + ) -> Self { + Self { + transaction_id: Txid::load(transaction_id), + lock_time, + number, + block_height, + block_time, + fee, + size, + weight, + value, + sat + } + } + + fn store(self) -> Self::Value { + ( + self.transaction_id.store(), + self.lock_time, + self.number, + self.block_height, + self.block_time, + self.fee, + self.size, + self.weight, + self.value, + self.sat, + ) + } +} + +pub(crate) fn get_next_lock_time_number( + wtx: &mut WriteTransaction, + lock_time: u32 +) -> Result { + let mut lock_time_to_number = wtx.open_table(LOCK_TIME_TO_NUMBER)?; + + // Get the current lock_time_number from the table + let current_number = lock_time_to_number + .get(&lock_time)? + .map(|entry| entry.value()) // Get the current value + .unwrap_or(0); // If not present, start with 0 + + // To keep next_number as 0 or increment it otherwise (counting should start 0, which is cooler) + let next_number = if current_number == 0 { 0 } else { current_number + 1 }; + + // Store the incremented number back into the table + lock_time_to_number.insert(&lock_time, &next_number)?; + + Ok(next_number) +} + +// Function to handle CAT-21 minting logic (and other future LTOs) +pub fn process_mint( + tx: &Transaction, + input_sat_ranges: Option>, + input_utxo_entries: &[ParsedUtxoEntry], + block: &BlockData, + height: u32, + index: &Index, +) -> Result<(), Error> { + + let lock_time = tx.lock_time.to_consensus_u32(); + if lock_time > 0 && lock_time <= cat21::LOCK_TIME_THRESHOLD { + + // Get the total input value by iterating through both `tx.input` and `input_utxo_entries` + let total_input_value = tx.input.iter().enumerate().map(|(index, _txin)| { + // Get the corresponding ParsedUtxoEntry based on the input index + let entry = &input_utxo_entries[index]; + + // Return the total value of the UTXO entry + entry.total_value() + }).sum::(); + + let total_output_value = tx.output.iter().map(|txout| txout.value.to_sat()).sum::(); + + // Calculate the fee + let fee = total_input_value - total_output_value; + + // Open a new write transaction + let mut wtx = index.begin_write()?; + + // Get the next number for this lock_time + let next_lock_time_number = cat21::get_next_lock_time_number(&mut wtx, lock_time)?; + + // According to the first-in, first-out (FIFO) rule, + // the first sat of the first output corresponds to the first sat of the first input. + let sat = input_sat_ranges + .as_ref() + .and_then(|ranges| ranges.get(0)) + .and_then(|range| range.get(0..11)) + .map(|chunk| SatRange::load(chunk.try_into().unwrap()).0) + .unwrap_or(0); // Use 0 if no input sat range is found (should never happen) + + let first_output = &tx.output[0]; + let value = first_output.value.to_sat(); + + // TODO: figure out what "size" is used by bitcoin core and/or esplora so that we have the same value for all indexers + // see https://github.com/rust-bitcoin/rust-bitcoin/pull/2076 + // base_size vs total_size + let size = tx.total_size().try_into().unwrap(); + let weight = tx.weight().into(); + let transaction_id = tx.compute_txid(); + + if lock_time == 21 { + println!("Meow! 😺 {} {} {}", next_lock_time_number, transaction_id, sat); + } + + let lock_time_ordinal_entry: LockTimeOrdinalEntry = LockTimeOrdinalEntry { + transaction_id, + lock_time, + number: next_lock_time_number, + block_height: height, + block_time: block.header.time, + fee, + size, + weight, + value, + sat + }; + + // Open the table and insert the new entry + { + let mut lock_time_ordinal_table = wtx.open_table(cat21::LOCKTIME_ORDINAL_TABLE)?; + lock_time_ordinal_table.insert(&lock_time_ordinal_entry.transaction_id.store(), lock_time_ordinal_entry.store())?; + } + + wtx.commit()?; + } + + Ok(()) + +} + +// CAT-21 😺 - END diff --git a/src/index/updater.rs b/src/index/updater.rs index 93a4b9eeae..3fefc31e5e 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -5,7 +5,7 @@ use { tokio::sync::{ broadcast::{self, error::TryRecvError}, mpsc::{self}, - }, + } }; mod inscription_updater; @@ -645,6 +645,20 @@ impl Updater<'_> { )?; } + // CAT-21 😺 - START: index cats (and more) + + // Call the new function + cat21::process_mint( + tx, + input_sat_ranges, + &input_utxo_entries, + &block, + self.height, + self.index, + )?; + + // CAT-21 😺 - END + for (vout, output_utxo_entry) in output_utxo_entries.into_iter().enumerate() { let vout = u32::try_from(vout).unwrap(); utxo_cache.insert(OutPoint { txid: *txid, vout }, output_utxo_entry); diff --git a/src/templates.rs b/src/templates.rs index fa54b01725..6becfb4109 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -77,7 +77,7 @@ where fn superscript(&self) -> String { if self.config.chain == Chain::Mainnet { - "beta".into() + "CAT-21 😺".into() } else { self.config.chain.to_string() } diff --git a/templates/page.html b/templates/page.html index 243a29593a..2e14765f5f 100644 --- a/templates/page.html +++ b/templates/page.html @@ -18,11 +18,14 @@