Skip to content

Commit

Permalink
Add ord preview (#1089)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Dec 30, 2022
1 parent 8895f62 commit 5416093
Show file tree
Hide file tree
Showing 25 changed files with 249 additions and 61 deletions.
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.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ rustls-acme = { version = "0.5.0", features = ["axum"] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = { version = "1.0.81" }
sys-info = "0.9.1"
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.3.3", features = ["set-header"] }

[dev-dependencies]
executable-path = "1.0.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
1 change: 1 addition & 0 deletions examples/external-resources-are-blocked.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<img src=https://rodarmor.com/blaster/images/1407912129089.26abc8c.png>
3 changes: 3 additions & 0 deletions examples/navigation-to-external-pages-is-blocked.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script>
location.href = "https://google.com";
</script>
3 changes: 3 additions & 0 deletions examples/top-navigation-is-blocked.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script>
top.location = "https://google.com";
</script>
3 changes: 3 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,6 @@ build-docs:

update-changelog:
git log --pretty='format:- %s' >> CHANGELOG.md

preview-examples:
cargo run preview examples/*
3 changes: 2 additions & 1 deletion src/chain.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use {super::*, clap::ValueEnum};

#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Default, ValueEnum, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Chain {
#[default]
#[clap(alias("main"))]
Mainnet,
#[clap(alias("test"))]
Expand Down
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ use {
cmp::Ordering,
collections::{BTreeMap, HashSet, VecDeque},
env,
ffi::OsString,
fmt::{self, Display, Formatter},
fs, io,
net::ToSocketAddrs,
net::{TcpListener, ToSocketAddrs},
ops::{Add, AddAssign, Sub},
path::{Path, PathBuf},
process,
process::{self, Command},
str::FromStr,
sync::{
atomic::{self, AtomicU64},
Expand All @@ -64,6 +65,7 @@ use {
thread,
time::{Duration, Instant, SystemTime},
},
tempfile::TempDir,
tokio::{runtime::Runtime, task},
};

Expand Down
39 changes: 22 additions & 17 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,45 @@ use {
bitcoincore_rpc::{Auth, Client},
};

#[derive(Debug, Parser)]
#[derive(Clone, Default, Debug, Parser)]
#[clap(group(
ArgGroup::new("chains")
.required(false)
.args(&["chain", "signet", "regtest", "testnet"]),
.args(&["chain-argument", "signet", "regtest", "testnet"]),
))]
pub(crate) struct Options {
#[clap(long, help = "Load Bitcoin Core data dir from <BITCOIN_DATA_DIR>.")]
bitcoin_data_dir: Option<PathBuf>,
#[clap(long, arg_enum, default_value = "mainnet", help = "Use <CHAIN>.")]
chain: Chain,
pub(crate) bitcoin_data_dir: Option<PathBuf>,
#[clap(
long = "chain",
arg_enum,
default_value = "mainnet",
help = "Use <CHAIN>."
)]
pub(crate) chain_argument: Chain,
#[clap(long, help = "Load Bitcoin Core RPC cookie file from <COOKIE_FILE>.")]
cookie_file: Option<PathBuf>,
pub(crate) cookie_file: Option<PathBuf>,
#[clap(long, help = "Store index in <DATA_DIR>.")]
data_dir: Option<PathBuf>,
pub(crate) data_dir: Option<PathBuf>,
#[clap(
long,
help = "Don't look for inscriptions below <FIRST_INSCRIPTION_HEIGHT>."
)]
first_inscription_height: Option<u64>,
pub(crate) first_inscription_height: Option<u64>,
#[clap(long, help = "Limit index to <HEIGHT_LIMIT> blocks.")]
pub(crate) height_limit: Option<u64>,
#[clap(long, help = "Use index at <INDEX>.")]
pub(crate) index: Option<PathBuf>,
#[clap(long, help = "Index current location of all satoshis.")]
#[clap(long, help = "Track location of all satoshis.")]
pub(crate) index_sats: bool,
#[clap(long, short, help = "Use regtest.")]
regtest: bool,
#[clap(long, short, help = "Use regtest. Equivalent to `--chain regtest`.")]
pub(crate) regtest: bool,
#[clap(long, help = "Connect to Bitcoin Core RPC at <RPC_URL>.")]
rpc_url: Option<String>,
#[clap(long, short, help = "Use signet.")]
signet: bool,
#[clap(long, short, help = "Use testnet.")]
testnet: bool,
pub(crate) rpc_url: Option<String>,
#[clap(long, short, help = "Use signet. Equivalent to `--chain signet`.")]
pub(crate) signet: bool,
#[clap(long, short, help = "Use testnet. Equivalent to `--chain testnet`.")]
pub(crate) testnet: bool,
}

impl Options {
Expand All @@ -48,7 +53,7 @@ impl Options {
} else if self.testnet {
Chain::Testnet
} else {
self.chain
self.chain_argument
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod index;
mod info;
mod list;
mod parse;
mod preview;
mod server;
mod subsidy;
mod supply;
Expand All @@ -16,6 +17,8 @@ mod wallet;
pub(crate) enum Subcommand {
#[clap(about = "List the first satoshis of each reward epoch")]
Epochs,
#[clap(about = "Run an explorer server populated with inscriptions")]
Preview(preview::Preview),
#[clap(about = "Find a satoshi's current location")]
Find(find::Find),
#[clap(about = "Update the index")]
Expand All @@ -42,6 +45,7 @@ impl Subcommand {
pub(crate) fn run(self, options: Options) -> Result {
match self {
Self::Epochs => epochs::run(),
Self::Preview(preview) => preview.run(),
Self::Find(find) => find.run(options),
Self::Index => index::run(options),
Self::Info(info) => info.run(options),
Expand Down
89 changes: 89 additions & 0 deletions src/subcommand/preview.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use super::*;

#[derive(Debug, Parser)]
pub(crate) struct Preview {
#[clap(flatten)]
server: super::server::Server,
inscriptions: Vec<PathBuf>,
}

impl Preview {
pub(crate) fn run(self) -> Result {
let tmpdir = TempDir::new()?;

let rpc_port = TcpListener::bind("127.0.0.1:0")?.local_addr()?.port();

let bitcoin_data_dir = tmpdir.path().join("bitcoin");

fs::create_dir(&bitcoin_data_dir)?;

let mut bitcoind = Command::new("bitcoind")
.arg({
let mut arg = OsString::from("-datadir=");
arg.push(&bitcoin_data_dir);
arg
})
.arg("-regtest")
.arg("-txindex=1")
.arg("-listen=0")
.arg(format!("-rpcport={rpc_port}"))
.spawn()?;

let options = Options {
chain_argument: Chain::Regtest,
bitcoin_data_dir: Some(bitcoin_data_dir),
data_dir: Some(tmpdir.path().into()),
rpc_url: Some(format!("127.0.0.1:{rpc_port}")),
index_sats: true,
..Options::default()
};

for attempt in 0.. {
if options.bitcoin_rpc_client().is_ok() {
break;
}

if attempt == 100 {
panic!("Bitcoin Core RPC did not respond");
}

thread::sleep(Duration::from_millis(50));
}

let rpc_client = options.bitcoin_rpc_client()?;

super::wallet::create::run(options.clone())?;

let address = rpc_client.get_new_address(None, None)?;

rpc_client.generate_to_address(101, &address)?;

for file in self.inscriptions {
Arguments {
options: options.clone(),
subcommand: Subcommand::Wallet(super::wallet::Wallet::Inscribe(
super::wallet::inscribe::Inscribe {
file,
no_backup: true,
satpoint: None,
},
)),
}
.run()?;

rpc_client.generate_to_address(1, &address)?;
}

rpc_client.generate_to_address(1, &address)?;

Arguments {
options,
subcommand: Subcommand::Server(self.server),
}
.run()?;

bitcoind.kill()?;

Ok(())
}
}
13 changes: 9 additions & 4 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use {
axum::{
body,
extract::{Extension, Path, Query},
http::{header, StatusCode},
http::{header, HeaderValue, StatusCode},
response::{IntoResponse, Redirect, Response},
routing::get,
Router,
Expand All @@ -25,6 +25,7 @@ use {
serde::{de, Deserializer},
std::{cmp::Ordering, str},
tokio_stream::StreamExt,
tower_http::set_header::SetResponseHeaderLayer,
};

mod deserialize_from_str;
Expand Down Expand Up @@ -168,7 +169,11 @@ impl Server {
.route("/status", get(Self::status))
.route("/tx/:txid", get(Self::transaction))
.layer(Extension(index))
.layer(Extension(options.chain()));
.layer(Extension(options.chain()))
.layer(SetResponseHeaderLayer::if_not_present(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src 'self'"),
));

match (self.http_port(), self.https_port()) {
(Some(http_port), None) => self.spawn(router, handle, http_port, None)?.await??,
Expand Down Expand Up @@ -674,7 +679,7 @@ impl Server {
(header::CONTENT_TYPE, content_type),
(
header::CONTENT_SECURITY_POLICY,
"default-src 'none' 'unsafe-eval' 'unsafe-inline'".to_string(),
"default-src 'unsafe-eval' 'unsafe-inline'".to_string(),
),
],
content,
Expand Down Expand Up @@ -748,7 +753,7 @@ impl Server {

#[cfg(test)]
mod tests {
use {super::*, reqwest::Url, std::net::TcpListener, tempfile::TempDir};
use {super::*, reqwest::Url, std::net::TcpListener};

struct TestServer {
bitcoin_rpc_server: test_bitcoincore_rpc::Handle,
Expand Down
12 changes: 6 additions & 6 deletions src/subcommand/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use {super::*, transaction_builder::TransactionBuilder};

mod balance;
mod create;
mod inscribe;
pub(crate) mod create;
pub(crate) mod inscribe;
mod inscriptions;
mod receive;
mod sats;
Expand All @@ -16,13 +16,13 @@ pub(crate) enum Wallet {
#[clap(about = "Get wallet balance")]
Balance,
#[clap(about = "Create a new wallet")]
Create(create::Create),
Create,
#[clap(about = "Create an inscription")]
Inscribe(inscribe::Inscribe),
#[clap(about = "List wallet inscriptions")]
Inscriptions(inscriptions::Inscriptions),
#[clap(about = "Generate a receive address")]
Receive(receive::Receive),
Receive,
#[clap(about = "List wallet satoshis")]
Sats(sats::Sats),
#[clap(about = "Send a satoshi or inscription")]
Expand All @@ -37,10 +37,10 @@ impl Wallet {
pub(crate) fn run(self, options: Options) -> Result {
match self {
Self::Balance => balance::run(options),
Self::Create(create) => create.run(options),
Self::Create => create::run(options),
Self::Inscribe(inscribe) => inscribe.run(options),
Self::Inscriptions(inscriptions) => inscriptions.run(options),
Self::Receive(receive) => receive.run(options),
Self::Receive => receive::run(options),
Self::Sats(sats) => sats.run(options),
Self::Send(send) => send.run(options),
Self::Transactions(transactions) => transactions.run(options),
Expand Down
15 changes: 5 additions & 10 deletions src/subcommand/wallet/create.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
use super::*;

#[derive(Debug, Parser)]
pub(crate) struct Create {}

impl Create {
pub(crate) fn run(self, options: Options) -> Result {
options
.bitcoin_rpc_client_mainnet_forbidden("ord wallet create")?
.create_wallet("ord", None, None, None, None)?;
Ok(())
}
pub(crate) fn run(options: Options) -> Result {
options
.bitcoin_rpc_client_mainnet_forbidden("ord wallet create")?
.create_wallet("ord", None, None, None, None)?;
Ok(())
}
10 changes: 7 additions & 3 deletions src/subcommand/wallet/inscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ use {
#[derive(Debug, Parser)]
pub(crate) struct Inscribe {
#[clap(long, help = "Inscribe <SATPOINT>")]
satpoint: Option<SatPoint>,
pub(crate) satpoint: Option<SatPoint>,
#[clap(help = "Inscribe sat with contents of <FILE>")]
file: PathBuf,
pub(crate) file: PathBuf,
#[clap(long, help = "Do not back up recovery key.")]
pub(crate) no_backup: bool,
}

impl Inscribe {
Expand Down Expand Up @@ -52,7 +54,9 @@ impl Inscribe {
reveal_tx_destination,
)?;

Inscribe::backup_recovery_key(&client, recovery_key_pair, options.chain().network())?;
if !self.no_backup {
Inscribe::backup_recovery_key(&client, recovery_key_pair, options.chain().network())?;
}

let signed_raw_commit_tx = client
.sign_raw_transaction_with_wallet(&unsigned_commit_tx, None, None)?
Expand Down
Loading

0 comments on commit 5416093

Please sign in to comment.