SHOWING: First 60
let sat = if unbound {
None
} else {
Self::calculate_sat(input_sat_ranges, flotsam.offset)
};
let mut charms = 0;
if cursed {
Charm::Cursed.set(&mut charms);
}
if reinscription {
Charm::Reinscription.set(&mut charms);
}
if let Some(sat) = sat {
if sat.nineball() {
Charm::Nineball.set(&mut charms);
}
match sat.rarity() {
Rarity::Common | Rarity::Mythic => {}
Rarity::Uncommon => Charm::Uncommon.set(&mut charms),
Rarity::Rare => Charm::Rare.set(&mut charms),
Rarity::Epic => Charm::Epic.set(&mut charms),
Rarity::Legendary => Charm::Legendary.set(&mut charms),
}
}
if new_satpoint.outpoint == OutPoint::null() {
Charm::Lost.set(&mut charms);
}
if unbound {
Charm::Unbound.set(&mut charms);
}
if let Some(Sat(n)) = sat {
self.sat_to_sequence_number.insert(&n, &sequence_number)?;
#44,984,785text )],
..Default::default()
});
let inscription = InscriptionId { txid, index: 0 };
bitcoin_rpc_server.mine_blocks(1);
let server = TestServer::new_with_bitcoin_rpc_server_and_config(
bitcoin_rpc_server,
format!("\"hidden\":\n - {inscription}"),
);
server.assert_response(
format!("/preview/{inscription}"),
StatusCode::OK,
&fs::read_to_string("templates/preview-unknown.html").unwrap(),
);
server.assert_response(
format!("/content/{inscription}"),
StatusCode::OK,
&fs::read_to_string("templates/preview-unknown.html").unwrap(),
);
}
#[test]
fn inscription_links_to_parent() {
let server = TestServer::new_with_regtest_with_json_api();
server.mine_blocks(1);
let parent_txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())],
..Default::default()
});
server.mine_blocks(1);
let parent_inscription_id = InscriptionId {
#44,983,931text TapSighashType::Default,
)
.expect("signature hash should compute");
let sig = secp256k1.sign_schnorr(
&secp256k1::Message::from_slice(sighash.as_ref())
.expect("should be cryptographically secure hash"),
&key_pair,
);
let witness = sighash_cache
.witness_mut(commit_input)
.expect("getting mutable witness reference should work");
witness.push(
Signature {
sig,
hash_ty: TapSighashType::Default,
}
.to_vec(),
);
witness.push(reveal_script);
witness.push(&control_block.serialize());
let recovery_key_pair = key_pair.tap_tweak(&secp256k1, taproot_spend_info.merkle_root());
let (x_only_pub_key, _parity) = recovery_key_pair.to_inner().x_only_public_key();
assert_eq!(
Address::p2tr_tweaked(
TweakedPublicKey::dangerous_assume_tweaked(x_only_pub_key),
chain.network(),
),
commit_tx_address
);
let reveal_weight = reveal_tx.weight();
if !self.no_limit && reveal_weight > bitcoin::Weight::from_wu(MAX_STANDARD_TX_WEIGHT.into()) {
#44,982,865text
context.mine_blocks(1);
let id = RuneId {
height: 2,
index: 1,
};
context.assert_runes(
[(
id,
RuneEntry {
etching: txid,
rune: Rune(RUNE),
supply: u128::max_value(),
timestamp: 2,
..Default::default()
},
)],
[(OutPoint { txid, vout: 0 }, vec![(id, u128::max_value())])],
);
}
#[test]
fn edicts_which_transfer_no_runes_to_output_create_no_balance_entry() {
let context = Context::builder()
.arg("--index-runes-pre-alpha-i-agree-to-get-rekt")
.build();
context.mine_blocks(1);
let txid = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, Witness::new())],
outputs: 2,
op_return: Some(
Runestone {
edicts: vec![
Edict {
id: 0,
amount: u128::max_value(),
output: 0,
},
Edict {
id: 0,
amount: 0,
output: 1,
#44,982,847text
let txid0 = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, Witness::new())],
op_return: Some(
Runestone {
edicts: vec![Edict {
id: 0,
amount: u128::max_value(),
output: 0,
}],
etching: Some(Etching {
rune: Rune(RUNE),
..Default::default()
}),
..Default::default()
}
.encipher(),
),
..Default::default()
});
context.mine_blocks(1);
let id0 = RuneId {
height: 2,
index: 1,
};
context.assert_runes(
[(
id0,
RuneEntry {
etching: txid0,
rune: Rune(RUNE),
supply: u128::max_value(),
timestamp: 2,
..Default::default()
},
)],
[(
OutPoint {
txid: txid0,
vout: 0,
},
vec![(id0, u128::max_value())],
)],
);
let txid1 = context.rpc_server.broadcast_tx(TransactionTemplate {
#44,982,840text
if benefit > deficit {
tprintln!("added {value} sat input to cover {deficit} sat deficit");
deficit = Amount::ZERO;
} else {
tprintln!("added {value} sat input to reduce {deficit} sat deficit by {benefit} sat");
deficit -= benefit;
}
}
}
Ok(self)
}
fn strip_value(mut self) -> Self {
let sat_offset = self.calculate_sat_offset();
let total_output_amount = self
.outputs
.iter()
.map(|(_address, amount)| *amount)
.sum::<Amount>();
self
.outputs
.iter()
.find(|(address, _amount)| address == &self.recipient)
.expect("couldn't find output that contains the index");
let value = total_output_amount - Amount::from_sat(sat_offset);
if let Some(excess) = value.checked_sub(self.fee_rate.fee(self.estimate_vbytes())) {
let (max, target) = match self.target {
Target::ExactPostage(postage) => (postage, postage),
Target::Postage => (Self::MAX_POSTAGE, Self::TARGET_POSTAGE),
#44,982,835text outpoint: outpoint(1),
list: None,
chain: Chain::Mainnet,
output: TxOut {
value: 3,
script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()),
},
runes: Vec::new(),
},
"
<h1>Output <span class=monospace>1{64}:1</span></h1>
<dl>
<dt>inscriptions</dt>
<dd class=thumbnails>
<a href=/inscription/1{64}i1><iframe .* src=/preview/1{64}i1></iframe></a>
</dd>
.*
</dl>
"
.unindent()
);
}
#[test]
fn with_runes() {
assert_regex_match!(
OutputHtml {
inscriptions: Vec::new(),
outpoint: outpoint(1),
list: None,
chain: Chain::Mainnet,
output: TxOut {
value: 3,
script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()),
},
runes: vec![(
Rune(0),
Pile {
amount: 11,
divisibility: 1,
symbol: None,
}
#44,982,751text "0°0′336″0‴"
);
assert_eq!(
Sat(50 * COIN_VALUE * u64::from(SUBSIDY_HALVING_INTERVAL) + 1)
.degree()
.to_string(),
"0°0′336″1‴"
);
assert_eq!(
Sat(2067187500000000 - 1).degree().to_string(),
"0°209999′2015″156249999‴"
);
assert_eq!(Sat(2067187500000000).degree().to_string(), "1°0′0″0‴");
assert_eq!(Sat(2067187500000000 + 1).degree().to_string(), "1°0′0″1‴");
}
#[test]
fn invalid_degree_bugfix() {
// Break glass in case of emergency:
// for height in 0..(2 * CYCLE_EPOCHS * Epoch::BLOCKS) {
// // 1054200000000000
// let expected = Height(height).starting_sat();
// // 0°1680′0″0‴
// let degree = expected.degree();
// // 2034637500000000
// let actual = degree.to_string().parse::<Sat>().unwrap();
// assert_eq!(
// actual, expected,
// "Sat at height {height} did not round-trip from degree {degree} successfully"
// );
// }
assert_eq!(Sat(1054200000000000).degree().to_string(), "0°1680′0″0‴");
#44,982,749text block: Block,
height: Height,
best_height: Height,
inscriptions: Vec<InscriptionId>,
) -> Self {
Self {
hash: block.header.block_hash(),
target: target_as_block_hash(block.header.target()),
height: height.0,
best_height: best_height.0,
inscriptions,
}
}
}
impl PageContent for BlockHtml {
fn title(&self) -> String {
format!("Block {}", self.height)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn html() {
assert_regex_match!(
BlockHtml::new(
Chain::Mainnet.genesis_block(),
Height(0),
Height(0),
0,
Vec::new()
),
"
<h1>Block 0</h1>
<dl>
<dt>hash</dt><dd class=monospace>[[:xdigit:]]{64}</dd>
<dt>target</dt><dd class=monospace>[[:xdigit:]]{64}</dd>
<dt>timestamp</dt><dd><time>2009-01-03 18:15:05 UTC</time></dd>
<dt>size</dt><dd>285</dd>
<dt>weight</dt><dd>1140</dd>
</dl>
.*
prev
next
#44,982,732text <a href="/sat/1°0′1008″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(98.18181818181817)"><title>9th Halving</title></line></a>
<a href="/sat/1°0′1344″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(109.0909090909091)"><title>10th Halving</title></line></a>
<a href="/sat/1°0′1680″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(120.0)"><title>11th Halving</title></line></a>
<a href="/sat/2°0′0″0‴"><line class="legendary" y1="-16" y2="-15" stroke-width="0.5" transform="rotate(130.9090909090909)"><title>2nd Conjunction</title></line></a>
<a href="/sat/2°0′336″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(141.8181818181818)"><title>13th Halving</title></line></a>
<a href="/sat/2°0′672″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(152.72727272727272)"><title>14th Halving</title></line></a>
<a href="/sat/2°0′1008″0‴"><line class="epic" y1="-16" y2="-15" stroke-width="0.3" transform="rotate(163.63636363636363)"><title>15th Halving</title></line></a>
#44,982,714text 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));
}
super::wallet::Wallet::Create(super::wallet::create::Create {
passphrase: "".into(),
})
.run(options.clone())?;
let rpc_client = options.bitcoin_rpc_client_for_wallet_command(false)?;
let address = rpc_client
.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?
.require_network(Network::Regtest)?;
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 {
#44,982,713text fn powers_of_two_round_trip_successfully() {
for i in 0..128 {
let n = 1 << i;
let encoded = encode(n);
let (decoded, length) = decode(&encoded).unwrap();
assert_eq!(decoded, n);
assert_eq!(length, encoded.len());
}
}
#[test]
fn alternating_bit_strings_round_trip_successfully() {
let mut n = 0;
for i in 0..129 {
n = n << 1 | (i % 2);
let encoded = encode(n);
let (decoded, length) = decode(&encoded).unwrap();
assert_eq!(decoded, n);
assert_eq!(length, encoded.len());
}
}
#[test]
fn decoding_integer_over_max_is_an_error() {
assert_eq!(
decode(&[
130, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255,
0,
]),
Err(Error::Varint)
);
}
#[test]
fn taproot_annex_format_bip_test_vectors_round_trip_successfully() {
const TEST_VECTORS: &[(u128, &[u8])] = &[
(0, &[0x00]),
(1, &[0x01]),
(127, &[0x7F]),
(128, &[0x80, 0x00]),
#44,982,708text inputs: &[(2, 1, 0, Default::default())],
fee: 50 * COIN_VALUE,
..Default::default()
});
server.mine_blocks_with_subsidy(1, 0);
server.assert_response_regex(
format!("/inscription/{id}"),
StatusCode::OK,
format!(
".*<h1>Inscription 0</h1>.*
<dl>
<dt>id</dt>
<dd class=monospace>{id}</dd>
<dt>charms</dt>
<dd>
<span title=lost>🤔</span>
</dd>
.*
</dl>
.*
"
),
);
}
#[test]
fn sat_recursive_endpoints() {
let server = TestServer::new_with_regtest_with_index_sats();
assert_eq!(
server.get_json::<SatInscriptionsJson>("/r/sat/5000000000"),
SatInscriptionsJson {
ids: vec![],
page: 0,
more: false
}
);
assert_eq!(
server.get_json::<SatInscriptionJson>("/r/sat/5000000000/at/0"),
SatInscriptionJson { id: None }
);
server.mine_blocks(1);
let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("text/plain", "foo").to_witness())],
#44,981,948text7888887878887888787888887888887878887888787888887.bixel#22,160,534text