Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add ord rune publish #637

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ html-escaper = "0.2.0"
http = "0.2.6"
lazy_static = "1.4.0"
log = "0.4.14"
mime = "0.3.16"
mime_guess = "2.0.4"
rayon = "1.5.1"
redb = "0.7.0"
regex = "1.6.0"
reqwest = { version = "0.11.10", features = ["blocking"] }
rust-embed = "6.4.0"
rustls = "0.20.6"
rustls-acme = { version = "0.5.0", features = ["axum"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.81"
serde_json = { version = "1.0.81", features = ["arbitrary_precision"] }
sys-info = "0.9.1"
tokio = { version = "1.17.0", features = ["rt-multi-thread"] }
tokio-stream = "0.1.9"
Expand All @@ -49,7 +51,6 @@ tower-http = { version = "0.3.3", features = ["cors"] }
executable-path = "1.0.0"
nix = "0.25.0"
pretty_assertions = "1.2.1"
reqwest = { version = "0.11.10", features = ["blocking"] }
tempfile = "3.2.0"
test-bitcoincore-rpc = { path = "test-bitcoincore-rpc" }
unindent = "0.1.7"
Expand Down
6 changes: 3 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
watch +args='test':
cargo watch --clear --exec '{{args}}'

ci: clippy forbid
cargo fmt -- --check
cargo test --all
Expand All @@ -14,9 +17,6 @@ clippy:
bench:
cargo criterion

watch +args='test':
cargo watch --clear --exec '{{args}}'

install-dev-deps:
cargo install cargo-criterion

Expand Down
32 changes: 29 additions & 3 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ use {
super::*,
bitcoin::consensus::encode::deserialize,
bitcoin::BlockHeader,
bitcoincore_rpc::{json::GetBlockHeaderResult, Auth, Client, RpcApi},
bitcoincore_rpc::{json::GetBlockHeaderResult, Auth, Client},
rayon::iter::{IntoParallelRefIterator, ParallelIterator},
redb::WriteStrategy,
std::sync::atomic::{AtomicBool, Ordering},
};

mod rtx;

const HASH_TO_RUNE: TableDefinition<[u8; 32], str> = TableDefinition::new("HASH_TO_RUNE");
const HEIGHT_TO_HASH: TableDefinition<u64, [u8; 32]> = TableDefinition::new("HEIGHT_TO_HASH");
const ORDINAL_TO_SATPOINT: TableDefinition<u64, [u8; 44]> =
TableDefinition::new("ORDINAL_TO_SATPOINT");
const OUTPOINT_TO_ORDINAL_RANGES: TableDefinition<[u8; 36], [u8]> =
TableDefinition::new("OUTPOINT_TO_ORDINAL_RANGES");
const STATISTICS: TableDefinition<u64, u64> = TableDefinition::new("STATISTICS");
const ORDINAL_TO_SATPOINT: TableDefinition<u64, [u8; 44]> =
TableDefinition::new("ORDINAL_TO_SATPOINT");

fn encode_outpoint(outpoint: OutPoint) -> [u8; 36] {
let mut array = [0; 36];
Expand Down Expand Up @@ -124,6 +125,7 @@ impl Index {
tx
};

tx.open_table(HASH_TO_RUNE)?;
tx.open_table(HEIGHT_TO_HASH)?;
tx.open_table(ORDINAL_TO_SATPOINT)?;
tx.open_table(OUTPOINT_TO_ORDINAL_RANGES)?;
Expand Down Expand Up @@ -534,6 +536,30 @@ impl Index {
)
}

pub(crate) fn rune(&self, hash: sha256::Hash) -> Result<Option<Rune>> {
Ok(
self
.database
.begin_read()?
.open_table(HASH_TO_RUNE)?
.get(hash.as_inner())?
.map(serde_json::from_str)
.transpose()?,
)
}

pub(crate) fn insert_rune(&self, rune: &Rune) -> Result<(bool, sha256::Hash)> {
let json = serde_json::to_string(rune)?;
let hash = sha256::Hash::hash(json.as_ref());
let wtx = self.database.begin_write()?;
let created = wtx
.open_table(HASH_TO_RUNE)?
.insert(hash.as_inner(), &json)?
.is_none();
wtx.commit()?;
Ok((created, hash))
}

pub(crate) fn find(&self, ordinal: u64) -> Result<Option<SatPoint>> {
if self.height()? < Ordinal(ordinal).height() {
return Ok(None);
Expand Down
9 changes: 5 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ use {
options::Options,
ordinal::Ordinal,
rarity::Rarity,
rune::Rune,
sat_point::SatPoint,
subcommand::Subcommand,
tally::Tally,
},
anyhow::{anyhow, bail, Context, Error},
axum::{extract, http::StatusCode, response::Html, response::IntoResponse, routing::get, Router},
axum_server::Handle,
bitcoin::{
blockdata::constants::COIN_VALUE,
consensus::{Decodable, Encodable},
hash_types::BlockHash,
hashes::Hash,
hashes::{sha256, Hash},
Address, Block, Network, OutPoint, Transaction, TxOut, Txid,
},
bitcoincore_rpc::RpcApi,
chrono::{DateTime, NaiveDateTime, Utc},
clap::Parser,
derive_more::{Display, FromStr},
Expand Down Expand Up @@ -72,6 +72,7 @@ mod index;
mod options;
mod ordinal;
mod rarity;
mod rune;
mod sat_point;
mod subcommand;
mod tally;
Expand All @@ -84,7 +85,7 @@ const SUBSIDY_HALVING_INTERVAL: u64 =
const CYCLE_EPOCHS: u64 = 6;

static INTERRUPTS: AtomicU64 = AtomicU64::new(0);
static LISTENERS: Mutex<Vec<Handle>> = Mutex::new(Vec::new());
static LISTENERS: Mutex<Vec<axum_server::Handle>> = Mutex::new(Vec::new());

fn main() {
env_logger::init();
Expand Down
8 changes: 8 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ impl Options {
Client::new(&rpc_url, Auth::CookieFile(cookie_file))
.context("Failed to connect to Bitcoin Core RPC at {rpc_url}")
}

pub(crate) fn bitcoin_rpc_client_mainnet_forbidden(&self, command: &str) -> Result<Client> {
let client = self.bitcoin_rpc_client()?;
if self.chain.network() == Network::Bitcoin || client.get_blockchain_info()?.chain == "main" {
bail!("`{command}` is unstable and not yet supported on mainnet.");
}
Ok(client)
}
}

#[cfg(test)]
Expand Down
8 changes: 8 additions & 0 deletions src/rune.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use super::*;

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct Rune {
pub(crate) name: String,
pub(crate) network: Network,
pub(crate) ordinal: Ordinal,
}
6 changes: 5 additions & 1 deletion src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod info;
mod list;
mod parse;
mod range;
mod rune;
mod server;
mod supply;
mod traits;
Expand All @@ -21,6 +22,8 @@ pub(crate) enum Subcommand {
List(list::List),
Parse(parse::Parse),
Range(range::Range),
#[clap(subcommand)]
Rune(rune::Rune),
Server(server::Server),
Supply,
Traits(traits::Traits),
Expand All @@ -38,9 +41,10 @@ impl Subcommand {
Self::List(list) => list.run(options),
Self::Parse(parse) => parse.run(),
Self::Range(range) => range.run(),
Self::Rune(rune) => rune.run(options),
Self::Server(server) => {
let index = Arc::new(Index::open(&options)?);
let handle = Handle::new();
let handle = axum_server::Handle::new();
LISTENERS.lock().unwrap().push(handle.clone());
server.run(options, index, handle)
}
Expand Down
16 changes: 16 additions & 0 deletions src/subcommand/rune.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::*;

mod publish;

#[derive(Debug, Parser)]
pub(crate) enum Rune {
Publish(publish::Publish),
}

impl Rune {
pub(crate) fn run(self, options: Options) -> Result<()> {
match self {
Self::Publish(publish) => publish.run(options),
}
}
}
51 changes: 51 additions & 0 deletions src/subcommand/rune/publish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use {super::*, reqwest::Url};

#[derive(Debug, Parser)]
pub(crate) struct Publish {
#[clap(long, help = "Give rune <NAME>.")]
name: String,
#[clap(long, help = "Inscribe rune on <ORDINAL>.")]
ordinal: Ordinal,
#[clap(
long,
default_value = "https://ordinals.com/",
help = "Publish rune to <PUBLISH_URL>."
)]
publish_url: Url,
}

impl Publish {
pub(crate) fn run(self, options: Options) -> Result {
options.bitcoin_rpc_client_mainnet_forbidden("ord rune publish")?;

let rune = crate::Rune {
network: options.chain.network(),
name: self.name,
ordinal: self.ordinal,
};

let json = serde_json::to_string(&rune)?;

let url = self.publish_url.join("rune")?;

let response = reqwest::blocking::Client::new()
.put(url.clone())
.header(
reqwest::header::CONTENT_TYPE,
mime::APPLICATION_JSON.as_ref(),
)
.body(json)
.send()?;

let status = response.status();

if !status.is_success() {
bail!("Failed to post rune to `{}`:\n{}", url, response.text()?)
}

eprintln!("Rune published: {}", response.status());
println!("{}", response.text()?);

Ok(())
}
}
Loading