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 preview subcommand #1089

Merged
merged 19 commits into from
Dec 30, 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.

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