transfer100.pizza#71,336,182brc-20dmt-mint.redactedtest#70,591,912tapInscription #47121017#47,121,017png 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,948textInscription #42591877#42,591,877pngInscription #42591874#42,591,874pngInscription #42591873#42,591,873pngInscription #42591871#42,591,871pngInscription #42591870#42,591,870pngInscription #42591858#42,591,858pngInscription #42591857#42,591,857pngInscription #42591856#42,591,856pngInscription #42591851#42,591,851pngInscription #42591850#42,591,850pngInscription #36877218#36,877,218html7888887878887888787888887888887878887888787888887.bixel#22,160,534textInscription #11385284#11,385,284webptransfer1,000.oegg#11,079,113brc-20transfer5,000.oxbt#9,235,292brc-20