SHOWING: First 60
}),
..Default::default()
}
.encipher(),
),
..Default::default()
});
server.mine_blocks(1);
let id = RuneId {
height: 2,
index: 1,
};
assert_eq!(
server.index.runes().unwrap(),
[(
id,
RuneEntry {
etching: txid,
rune,
supply: u128::max_value(),
symbol: Some('$'),
timestamp: 2,
..Default::default()
}
)]
);
assert_eq!(
server.index.get_rune_balances(),
[(OutPoint { txid, vout: 0 }, vec![(id, u128::max_value())])]
);
server.assert_response_regex(
format!("/rune/{rune}"),
StatusCode::OK,
format!(
r".*<title>Rune AAAAAAAAAAAAA</title>.*
<h1>AAAAAAAAAAAAA</h1>
<iframe .* src=/preview/{txid}i0></iframe>
<dl>
<dt>id</dt>
<dd>2/1</dd>
<dt>number</dt>
<dd>0</dd>
<dt>timestamp</dt>
<dd><time>1970-01-01 00:00:02 UTC</time></dd>
<dt>etching block height</dt>
<dd><a href=/block/2>2</a></dd>
#45,161,862text Timestamp, WalletTxInfo,
},
jsonrpc_core::{IoHandler, Value},
jsonrpc_http_server::{CloseHandle, ServerBuilder},
serde::{Deserialize, Serialize},
server::Server,
state::State,
std::{
collections::{BTreeMap, BTreeSet, HashMap},
sync::{Arc, Mutex, MutexGuard},
thread,
time::Duration,
},
};
mod api;
mod server;
mod state;
pub fn builder() -> Builder {
Builder {
fail_lock_unspent: false,
network: Network::Bitcoin,
version: 240000,
}
}
pub struct Builder {
fail_lock_unspent: bool,
network: Network,
version: usize,
}
impl Builder {
pub fn fail_lock_unspent(self, fail_lock_unspent: bool) -> Self {
Self {
fail_lock_unspent,
..self
}
}
pub fn network(self, network: Network) -> Self {
Self { network, ..self }
}
pub fn version(self, version: usize) -> Self {
Self { version, ..self }
}
pub fn build(self) -> Handle {
let state = Arc::new(Mutex::new(State::new(
self.network,
self.version,
self.fail_lock_unspent,#45,161,110text
server.assert_response_regex(
format!("/preview/{inscription_id}"),
StatusCode::OK,
format!(r".*<audio .*>\s*<source src=/content/{inscription_id}>.*"),
);
}
#[test]
fn font_preview() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);
let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("font/ttf", "hello").to_witness())],
..Default::default()
});
let inscription_id = InscriptionId { txid, index: 0 };
server.mine_blocks(1);
server.assert_response_regex(
format!("/preview/{inscription_id}"),
StatusCode::OK,
format!(r".*src: url\(/content/{inscription_id}\).*"),
);
}
#[test]
fn pdf_preview() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);
let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(
1,
0,
0,
inscription("application/pdf", "hello").to_witness(),#45,161,080textuse super::*;
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewAudioHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewCodeHtml {
pub(crate) inscription_id: InscriptionId,
pub(crate) language: media::Language,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewFontHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewImageHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewMarkdownHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewModelHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewPdfHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
pub(crate) struct PreviewTextHtml {
pub(crate) inscription_id: InscriptionId,
}
#[derive(boilerplate::Boilerplate)]
#45,161,068text Timestamp, WalletTxInfo,
},
jsonrpc_core::{IoHandler, Value},
jsonrpc_http_server::{CloseHandle, ServerBuilder},
serde::{Deserialize, Serialize},
server::Server,
state::State,
std::{
collections::{BTreeMap, BTreeSet, HashMap},
sync::{Arc, Mutex, MutexGuard},
thread,
time::Duration,
},
};
mod api;
mod server;
mod state;
pub fn builder() -> Builder {
Builder {
fail_lock_unspent: false,
network: Network::Bitcoin,
version: 240000,
}
}
pub struct Builder {
fail_lock_unspent: bool,
network: Network,
version: usize,
}
impl Builder {
pub fn fail_lock_unspent(self, fail_lock_unspent: bool) -> Self {
Self {
fail_lock_unspent,
..self
}
}
pub fn network(self, network: Network) -> Self {
Self { network, ..self }
}
pub fn version(self, version: usize) -> Self {
Self { version, ..self }
}
pub fn build(self) -> Handle {
let state = Arc::new(Mutex::new(State::new(
self.network,
self.version,
self.fail_lock_unspent,#45,159,679text
#[test]
fn unknown_preview() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);
let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("text/foo", "hello").to_witness())],
..Default::default()
});
server.mine_blocks(1);
server.assert_response_csp(
format!("/preview/{}", InscriptionId { txid, index: 0 }),
StatusCode::OK,
"default-src 'self'",
fs::read_to_string("templates/preview-unknown.html").unwrap(),
);
}
#[test]
fn video_preview() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);
let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("video/webm", "hello").to_witness())],
..Default::default()
});
let inscription_id = InscriptionId { txid, index: 0 };
server.mine_blocks(1);
server.assert_response_regex(
format!("/preview/{inscription_id}"),
StatusCode::OK,#45,022,930textsmall sub-features, and implement them one at a time.
Figuring out how to break down a larger PR into smaller PRs where each can be
merged is an art form well-worth practicing. The hard part is that each PR must
itself be an improvement.
I strive to follow this advice myself, and am always better off when I do.
Small changes are fast to write, review, and merge, which is much more fun than
laboring over a single giant PR that takes forever to write, review, and merge.
Small changes don't take much time, so if you need to stop working on a small
change, you won't have wasted much time as compared to a larger change that
represents many hours of work. Getting a PR in quickly improves the project a
little bit immediately, instead of having to wait a long time for larger
improvement. Small changes are less likely to accumulate merge conflict. As the
Athenians said: *The fast commit what they will, the slow merge what they
must.*
Get help
--------
If you're stuck for more than 15 minutes, ask for help, like a Rust Discord,
#45,021,734text <dd><a class=monospace href=/output/1{64}:1>1{64}:1</a></dd>
<dt>offset</dt>
<dd>0</dd>
<dt>ethereum teleburn address</dt>
<dd>0xa1DfBd1C519B9323FD7Fd8e498Ac16c2E502F059</dd>
</dl>
"
.unindent()
);
}
#[test]
fn with_rune() {
assert_regex_match!(
InscriptionHtml {
genesis_fee: 1,
inscription: inscription("text/plain;charset=utf-8", "HELLOWORLD"),
inscription_id: inscription_id(1),
inscription_number: 1,
satpoint: satpoint(1, 0),
rune: Some(Rune(0)),
..Default::default()
},
"
<h1>Inscription 1</h1>
.*
<dl>
.*
<dt>rune</dt>
<dd><a href=/rune/A>A</a></dd>
</dl>
"
.unindent()
);
}
#[test]
fn with_content_encoding() {
assert_regex_match!(
InscriptionHtml {
genesis_fee: 1,
inscription: Inscription {
content_encoding: Some("br".into()),
..inscription("text/plain;charset=utf-8", "HELLOWORLD")
#44,992,588text
You should add a label for the recipient address, a label like `alice address for inscription #123` would be ideal.
Once you have checked the transaction is a safe transaction using the checks above, and you are confident to send it you can click `Create Transaction`.

Here again you can double check that your transaction looks safe, and once you are confident you can click `Finalize Transaction for Signing`.

Here you can triple check everything before hitting `Sign`.

And then actually you get very very last chance to check everything before hitting `Broadcast Transaction`. Once you broadcast the transaction it is sent to the bitcoin network, and starts being propagated into the mempool.

If you want to track the status of your transaction you can copy the `Transaction Id (Txid)` and paste that into [mempool.space](https://mempool.space)
Once the transaction has confirmed you can check the inscription page on [ordinals.com](https://ordinals.com) to validate that it has moved to the new output location and address.
#44,991,095text
#[test]
fn first() {
assert_regex_match!(
SatHtml {
sat: Sat(0),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscriptions: Vec::new(),
},
"
<h1>Sat 0</h1>
<dl>
<dt>decimal</dt><dd>0.0</dd>
<dt>degree</dt><dd>0°0′0″0‴</dd>
<dt>percentile</dt><dd>0%</dd>
<dt>name</dt><dd>nvtdijuwxlp</dd>
<dt>cycle</dt><dd>0</dd>
<dt>epoch</dt><dd>0</dd>
<dt>period</dt><dd>0</dd>
<dt>block</dt><dd><a href=/block/0>0</a></dd>
<dt>offset</dt><dd>0</dd>
<dt>rarity</dt><dd><span class=mythic>mythic</span></dd>
<dt>timestamp</dt><dd><time>1970-01-01 00:00:00 UTC</time></dd>
</dl>
.*
prev
<a class=next href=/sat/1>next</a>
.*
"
.unindent()
);
}
#[test]
fn last() {
assert_regex_match!(
SatHtml {
sat: Sat(2099999997689999),
satpoint: None,
blocktime: Blocktime::confirmed(0),
#44,990,517text self.outputs[0].1
);
}
}
}
Ok(self)
}
fn add_value(mut self) -> Result<Self> {
let estimated_fee = self.estimate_fee();
let min_value = match self.target {
Target::Postage => self.outputs.last().unwrap().0.script_pubkey().dust_value(),
Target::Value(value) | Target::ExactPostage(value) => value,
};
let total = min_value
.checked_add(estimated_fee)
.ok_or(Error::ValueOverflow)?;
if let Some(mut deficit) = total.checked_sub(self.outputs.last().unwrap().1) {
while deficit > Amount::ZERO {
let additional_fee = self.fee_rate.fee(Self::ADDITIONAL_INPUT_VBYTES);
let needed = deficit
.checked_add(additional_fee)
.ok_or(Error::ValueOverflow)?;
let (utxo, value) = self.select_cardinal_utxo(needed, false)?;
let benefit = value
.checked_sub(additional_fee)
.ok_or(Error::NotEnoughCardinalUtxos)?;
self.inputs.push(utxo);
self.outputs.last_mut().unwrap().1 += value;
#44,986,666text .outputs
.last_mut()
.expect("No output to deduct fee from");
assert!(
total_output_amount.checked_sub(fee).unwrap() > Amount::from_sat(sat_offset),
"invariant: deducting fee does not consume sat",
);
assert!(
*last_output_amount >= fee,
"invariant: last output can pay fee: {} {}",
*last_output_amount,
fee,
);
*last_output_amount -= fee;
self
}
/// Estimate the size in virtual bytes of the transaction under construction.
/// We initialize wallets with taproot descriptors only, so we know that all
/// inputs are taproot key path spends, which allows us to know that witnesses
/// will all consist of single Schnorr signatures.
fn estimate_vbytes(&self) -> usize {
Self::estimate_vbytes_with(
self.inputs.len(),
self
.outputs
.iter()
.map(|(address, _amount)| address)
.cloned()
.collect(),
)
}
fn estimate_vbytes_with(inputs: usize, outputs: Vec<Address>) -> usize {
#44,985,412text <link rel=stylesheet href=/static/index.css>
<link rel=stylesheet href=/static/modern-normalize.css>
<script src=/static/index.js defer></script>
</head>
<body>
<header>
<nav>
<a href=/ title=home>Ordinals<sup>alpha</sup></a>
.*
<a href=/clock title=clock>.*</a>
<a href=/rare.txt title=rare>.*</a>
.*
<form action=/search method=get>
<input type=text .*>
<input class=icon type=image .*>
</form>
</nav>
</header>
<main>
<h1>Foo</h1>
</main>
</body>
</html>
"
);
}
#[test]
fn page_mainnet() {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Mainnet,
csp_origin: None,
domain: None,
index_sats: true,
})),
r".*<nav>\s*<a href=/ title=home>Ordinals<sup>alpha</sup></a>.*"
);
}
#[test]
fn page_no_sat_index() {
assert_regex_match!(
Foo.page(Arc::new(PageConfig {
chain: Chain::Mainnet,
csp_origin: None,
domain: None,
#44,985,407text