From db29dbc3f22cca1fa6b632893b82c05204a9d431 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 1 Mar 2023 19:25:11 -0800 Subject: [PATCH 01/34] enable collections through parent inscriptions method #783 --- src/index.rs | 4 +- src/index/updater.rs | 3 + src/index/updater/inscription_updater.rs | 40 +++-- src/inscription.rs | 61 +++++++- src/subcommand/preview.rs | 1 + src/subcommand/server.rs | 8 +- src/subcommand/wallet/inscribe.rs | 187 +++++++++++++++++------ src/test.rs | 2 +- tests/lib.rs | 1 + tests/wallet/inscribe.rs | 43 +++++- 10 files changed, 281 insertions(+), 69 deletions(-) diff --git a/src/index.rs b/src/index.rs index e4c02d853c..9b93b2db36 100644 --- a/src/index.rs +++ b/src/index.rs @@ -23,7 +23,7 @@ mod fetcher; mod rtx; mod updater; -const SCHEMA_VERSION: u64 = 3; +const SCHEMA_VERSION: u64 = 4; macro_rules! define_table { ($name:ident, $key:ty, $value:ty) => { @@ -33,6 +33,7 @@ macro_rules! define_table { define_table! { HEIGHT_TO_BLOCK_HASH, u64, &BlockHashValue } define_table! { INSCRIPTION_ID_TO_INSCRIPTION_ENTRY, &InscriptionIdValue, InscriptionEntryValue } +define_table! { INSCRIPTION_ID_TO_PARENT_ID, &InscriptionIdValue, &InscriptionIdValue } define_table! { INSCRIPTION_ID_TO_SATPOINT, &InscriptionIdValue, &SatPointValue } define_table! { INSCRIPTION_NUMBER_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } define_table! { OUTPOINT_TO_SAT_RANGES, &OutPointValue, &[u8] } @@ -204,6 +205,7 @@ impl Index { tx.open_table(HEIGHT_TO_BLOCK_HASH)?; tx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; + tx.open_table(INSCRIPTION_ID_TO_PARENT_ID)?; tx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; tx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; tx.open_table(OUTPOINT_TO_VALUE)?; diff --git a/src/index/updater.rs b/src/index/updater.rs index d6c1223326..6e578088b5 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -404,6 +404,7 @@ impl Updater { let mut inscription_id_to_inscription_entry = wtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; + let mut inscription_id_to_parent_id = wtx.open_table(INSCRIPTION_ID_TO_PARENT_ID)?; let mut inscription_id_to_satpoint = wtx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; let mut inscription_number_to_inscription_id = wtx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; @@ -421,6 +422,7 @@ impl Updater { &mut inscription_id_to_satpoint, value_receiver, &mut inscription_id_to_inscription_entry, + &mut inscription_id_to_parent_id, lost_sats, &mut inscription_number_to_inscription_id, &mut outpoint_to_value, @@ -519,6 +521,7 @@ impl Updater { outpoint_to_sat_ranges.insert(&OutPoint::null().store(), lost_sat_ranges.as_slice())?; } } else { + // move coinbase to end for (tx, txid) in block.txdata.iter().skip(1).chain(block.txdata.first()) { lost_sats += inscription_updater.index_transaction_inscriptions(tx, *txid, None)?; } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 575ccf7ca7..14c93a8ffb 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -4,6 +4,7 @@ pub(super) struct Flotsam { inscription_id: InscriptionId, offset: u64, origin: Origin, + parent: Option, } enum Origin { @@ -17,6 +18,7 @@ pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, value_receiver: &'a mut Receiver, id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, + id_to_parent_id: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static InscriptionIdValue>, lost_sats: u64, next_number: u64, number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, @@ -34,6 +36,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, value_receiver: &'a mut Receiver, id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, + id_to_parent_id: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static InscriptionIdValue>, lost_sats: u64, number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, @@ -55,6 +58,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { id_to_satpoint, value_receiver, id_to_entry, + id_to_parent_id, lost_sats, next_number, number_to_id, @@ -70,13 +74,15 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { pub(super) fn index_transaction_inscriptions( &mut self, tx: &Transaction, - txid: Txid, + txid: Txid, // we can calulcate this from tx. Is this expensive? input_sat_ranges: Option<&VecDeque<(u64, u64)>>, ) -> Result { let mut inscriptions = Vec::new(); + // go through all flotsam and ensure parent is there let mut input_value = 0; for tx_in in &tx.input { + // if coinbase if tx_in.previous_output.is_null() { input_value += Height(self.height).subsidy(); } else { @@ -87,6 +93,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { offset: input_value + old_satpoint.offset, inscription_id, origin: Origin::Old(old_satpoint), + parent: None, }); } @@ -108,14 +115,27 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - if inscriptions.iter().all(|flotsam| flotsam.offset != 0) - && Inscription::from_transaction(tx).is_some() - { - inscriptions.push(Flotsam { - inscription_id: txid.into(), - offset: 0, - origin: Origin::New(input_value - tx.output.iter().map(|txout| txout.value).sum::()), - }); + // make sure no re-inscriptions + if inscriptions.iter().all(|flotsam| flotsam.offset != 0) { + if let Some(inscription) = Inscription::from_transaction(tx) { + + let parent = if let Some(parent_id) = inscription.get_parent_id() { + if inscriptions.iter().any(|flotsam| flotsam.inscription_id == parent_id) { + Some(parent_id) + } else { + None + } + } else { + None + }; + + inscriptions.push(Flotsam { + inscription_id: txid.into(), + offset: 0, + origin: Origin::New(input_value - tx.output.iter().map(|txout| txout.value).sum::()), + parent, + }); + } }; let is_coinbase = tx @@ -218,6 +238,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } + + self.id_to_entry.insert( &inscription_id, &InscriptionEntry { diff --git a/src/inscription.rs b/src/inscription.rs index d0fba77016..cd6ae1d88e 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -15,24 +15,42 @@ const PROTOCOL_ID: &[u8] = b"ord"; const BODY_TAG: &[u8] = &[]; const CONTENT_TYPE_TAG: &[u8] = &[1]; +const PARENT_TAG: &[u8] = &[3]; #[derive(Debug, PartialEq, Clone)] pub(crate) struct Inscription { - body: Option>, + parent: Option>, content_type: Option>, + body: Option>, } impl Inscription { #[cfg(test)] - pub(crate) fn new(content_type: Option>, body: Option>) -> Self { - Self { content_type, body } + pub(crate) fn new( + parent: Option>, + content_type: Option>, + body: Option>, + ) -> Self { + Self { + parent, + content_type, + body, + } } pub(crate) fn from_transaction(tx: &Transaction) -> Option { + // let mut inscriptions = Vec::new(); + // for input in tx.input { + // InscriptionParser::parse(input.witness).ok() + // } InscriptionParser::parse(&tx.input.get(0)?.witness).ok() } - pub(crate) fn from_file(chain: Chain, path: impl AsRef) -> Result { + pub(crate) fn from_file( + chain: Chain, + path: impl AsRef, + parent: Option, + ) -> Result { let path = path.as_ref(); let body = fs::read(path).with_context(|| format!("io error reading {}", path.display()))?; @@ -46,7 +64,14 @@ impl Inscription { let content_type = Media::content_type_for_path(path)?; + let parent = if let Some(inscription_id) = parent { + Some(inscription_id.to_string().into_bytes()) + } else { + None + }; + Ok(Self { + parent, body: Some(body), content_type: Some(content_type.into()), }) @@ -58,6 +83,10 @@ impl Inscription { .push_opcode(opcodes::all::OP_IF) .push_slice(PROTOCOL_ID); + if let Some(parent) = &self.parent { + builder = builder.push_slice(PARENT_TAG).push_slice(parent); + } + if let Some(content_type) = &self.content_type { builder = builder .push_slice(CONTENT_TYPE_TAG) @@ -106,6 +135,14 @@ impl Inscription { str::from_utf8(self.content_type.as_ref()?).ok() } + pub(crate) fn get_parent_id(&self) -> Option { + if let Some(vec) = &self.parent { + InscriptionId::from_str(str::from_utf8(&vec).unwrap()).ok() + } else { + None + } + } + #[cfg(test)] pub(crate) fn to_witness(&self) -> Witness { let builder = script::Builder::new(); @@ -222,6 +259,7 @@ impl<'a> InscriptionParser<'a> { let body = fields.remove(BODY_TAG); let content_type = fields.remove(CONTENT_TYPE_TAG); + let parent = fields.remove(PARENT_TAG); for tag in fields.keys() { if let Some(lsb) = tag.first() { @@ -231,7 +269,11 @@ impl<'a> InscriptionParser<'a> { } } - return Ok(Some(Inscription { body, content_type })); + return Ok(Some(Inscription { + body, + content_type, + parent, + })); } Ok(None) @@ -358,7 +400,7 @@ mod tests { b"ord", &[1], b"text/plain;charset=utf-8", - &[3], + &[5], b"bar", &[], b"ord", @@ -372,6 +414,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&envelope(&[b"ord", &[1], b"text/plain;charset=utf-8"])), Ok(Inscription { + parent: None, content_type: Some(b"text/plain;charset=utf-8".to_vec()), body: None, }), @@ -383,6 +426,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&envelope(&[b"ord", &[], b"foo"])), Ok(Inscription { + parent: None, content_type: None, body: Some(b"foo".to_vec()), }), @@ -705,6 +749,7 @@ mod tests { witness.push( &Inscription { + parent: None, content_type: None, body: None, } @@ -716,6 +761,7 @@ mod tests { assert_eq!( InscriptionParser::parse(&witness).unwrap(), Inscription { + parent: None, content_type: None, body: None, } @@ -725,8 +771,9 @@ mod tests { #[test] fn unknown_odd_fields_are_ignored() { assert_eq!( - InscriptionParser::parse(&envelope(&[b"ord", &[3], &[0]])), + InscriptionParser::parse(&envelope(&[b"ord", &[5], &[0]])), Ok(Inscription { + parent: None, content_type: None, body: None, }), diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index d9f402e47e..13ba8d1730 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -86,6 +86,7 @@ impl Preview { dry_run: false, no_limit: false, destination: None, + parent: None, }, )), } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 63adafb300..c16fbf3a31 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1971,6 +1971,7 @@ mod tests { fn content_response_no_content() { assert_eq!( Server::content_response(Inscription::new( + None, Some("text/plain".as_bytes().to_vec()), None )), @@ -1981,6 +1982,7 @@ mod tests { #[test] fn content_response_with_content() { let (headers, body) = Server::content_response(Inscription::new( + None, Some("text/plain".as_bytes().to_vec()), Some(vec![1, 2, 3]), )) @@ -1993,7 +1995,7 @@ mod tests { #[test] fn content_response_no_content_type() { let (headers, body) = - Server::content_response(Inscription::new(None, Some(Vec::new()))).unwrap(); + Server::content_response(Inscription::new(None, None, Some(Vec::new()))).unwrap(); assert_eq!(headers["content-type"], "application/octet-stream"); assert!(body.is_empty()); @@ -2291,7 +2293,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: Inscription::new(Some("foo/bar".as_bytes().to_vec()), None).to_witness(), + witness: Inscription::new(None, Some("foo/bar".as_bytes().to_vec()), None).to_witness(), ..Default::default() }); @@ -2313,7 +2315,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: Inscription::new(Some("image/png".as_bytes().to_vec()), None).to_witness(), + witness: Inscription::new(None, Some("image/png".as_bytes().to_vec()), None).to_witness(), ..Default::default() }); diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 915613e8fe..d072382ea8 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -22,6 +22,7 @@ use { struct Output { commit: Txid, inscription: InscriptionId, + parent: Option, reveal: Txid, fees: u64, } @@ -54,12 +55,12 @@ pub(crate) struct Inscribe { pub(crate) dry_run: bool, #[clap(long, help = "Send inscription to .")] pub(crate) destination: Option
, + #[clap(long, help = "Establish parent relationship with .")] + pub(crate) parent: Option, } impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - let inscription = Inscription::from_file(options.chain(), &self.file)?; - let index = Index::open(&options)?; index.update()?; @@ -69,6 +70,34 @@ impl Inscribe { let inscriptions = index.get_inscriptions(None)?; + let (parent, commit_input_offset) = if let Some(parent_id) = self.parent { + if let Some(satpoint) = index.get_inscription_satpoint_by_id(parent_id)? { + if !utxos.contains_key(&satpoint.outpoint) { + return Err(anyhow!(format!( + "unrelated parent {parent_id} not accepting mailman's child" // for the germans: "Kuckuckskind" + ))); + } + + let output = index + .get_transaction(satpoint.outpoint.txid)? + .expect("not found") + .output + .into_iter() + .nth(satpoint.outpoint.vout.try_into().unwrap()) + .expect("current transaction output"); + + (Some((satpoint, output)), 1) + } else { + return Err(anyhow!(format!( + "specified parent {parent_id} does not exist" + ))); + } + } else { + (None, 0) + }; + + let inscription = Inscription::from_file(options.chain(), &self.file, self.parent)?; + let commit_tx_change = [get_change_address(&client)?, get_change_address(&client)?]; let reveal_tx_destination = self @@ -76,9 +105,10 @@ impl Inscribe { .map(Ok) .unwrap_or_else(|| get_change_address(&client))?; - let (unsigned_commit_tx, reveal_tx, recovery_key_pair) = + let (unsigned_commit_tx, partially_signed_reveal_tx, recovery_key_pair) = Inscribe::create_inscription_transactions( self.satpoint, + parent, inscription, inscriptions, options.chain().network(), @@ -91,18 +121,19 @@ impl Inscribe { )?; utxos.insert( - reveal_tx.input[0].previous_output, - Amount::from_sat(unsigned_commit_tx.output[0].value), + partially_signed_reveal_tx.input[commit_input_offset].previous_output, + Amount::from_sat(unsigned_commit_tx.output[commit_input_offset].value), ); - let fees = - Self::calculate_fee(&unsigned_commit_tx, &utxos) + Self::calculate_fee(&reveal_tx, &utxos); + let fees = Self::calculate_fee(&unsigned_commit_tx, &utxos) + + Self::calculate_fee(&partially_signed_reveal_tx, &utxos); if self.dry_run { print_json(Output { commit: unsigned_commit_tx.txid(), - reveal: reveal_tx.txid(), - inscription: reveal_tx.txid().into(), + reveal: partially_signed_reveal_tx.txid(), + inscription: partially_signed_reveal_tx.txid().into(), + parent: self.parent, fees, })?; } else { @@ -118,18 +149,33 @@ impl Inscribe { .send_raw_transaction(&signed_raw_commit_tx) .context("Failed to send commit transaction")?; - let reveal = client - .send_raw_transaction(&reveal_tx) - .context("Failed to send reveal transaction")?; + let reveal = if self.parent.is_some() { + let fully_signed_raw_reveal_tx = client + .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? + .hex; + + client + .send_raw_transaction(&fully_signed_raw_reveal_tx) + .context("Failed to send reveal transaction")? + } else { + client + .send_raw_transaction(&partially_signed_reveal_tx) + .context("Failed to send reveal transaction")? + }; print_json(Output { commit, reveal, inscription: reveal.into(), + parent: self.parent, fees, })?; }; + // if self.parent.is_some() { + // println!("{}", partially_signed_reveal_tx.raw_hex()); + // } + Ok(()) } @@ -144,6 +190,7 @@ impl Inscribe { fn create_inscription_transactions( satpoint: Option, + parent: Option<(SatPoint, TxOut)>, inscription: Inscription, inscriptions: BTreeMap, network: Network, @@ -207,17 +254,41 @@ impl Inscribe { let commit_tx_address = Address::p2tr_tweaked(taproot_spend_info.output_key(), network); + let (mut inputs, mut outputs, commit_input_offset) = if let Some((satpoint, output)) = parent { + ( + vec![satpoint.outpoint, OutPoint::null()], + vec![ + TxOut { + script_pubkey: output.script_pubkey, + value: output.value, + }, + TxOut { + script_pubkey: destination.script_pubkey(), + value: 0, + }, + ], + 1, + ) + } else { + ( + vec![OutPoint::null()], + vec![TxOut { + script_pubkey: destination.script_pubkey(), + value: 0, + }], + 0, + ) + }; + let (_, reveal_fee) = Self::build_reveal_transaction( &control_block, reveal_fee_rate, - OutPoint::null(), - TxOut { - script_pubkey: destination.script_pubkey(), - value: 0, - }, + inputs.clone(), + outputs.clone(), &reveal_script, ); + // watch out that parent and inscription preserved let unsigned_commit_tx = TransactionBuilder::build_transaction_with_value( satpoint, inscriptions, @@ -235,26 +306,35 @@ impl Inscribe { .find(|(_vout, output)| output.script_pubkey == commit_tx_address.script_pubkey()) .expect("should find sat commit/inscription output"); + inputs[commit_input_offset] = OutPoint { + txid: unsigned_commit_tx.txid(), + vout: vout.try_into().unwrap(), + }; + + outputs[commit_input_offset] = TxOut { + script_pubkey: destination.script_pubkey(), + value: output.value, + }; + let (mut reveal_tx, fee) = Self::build_reveal_transaction( &control_block, reveal_fee_rate, - OutPoint { - txid: unsigned_commit_tx.txid(), - vout: vout.try_into().unwrap(), - }, - TxOut { - script_pubkey: destination.script_pubkey(), - value: output.value, - }, + inputs, + outputs, &reveal_script, ); - reveal_tx.output[0].value = reveal_tx.output[0] + reveal_tx.output[commit_input_offset].value = reveal_tx.output[commit_input_offset] .value .checked_sub(fee.to_sat()) .context("commit transaction output value insufficient to pay transaction fee")?; - if reveal_tx.output[0].value < reveal_tx.output[0].script_pubkey.dust_value().to_sat() { + if reveal_tx.output[commit_input_offset].value + < reveal_tx.output[commit_input_offset] + .script_pubkey + .dust_value() + .to_sat() + { bail!("commit transaction output would be dust"); } @@ -262,10 +342,10 @@ impl Inscribe { let signature_hash = sighash_cache .taproot_script_spend_signature_hash( - 0, - &Prevouts::All(&[output]), + commit_input_offset, + &Prevouts::One(commit_input_offset, output), TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), - SchnorrSighashType::Default, + SchnorrSighashType::AllPlusAnyoneCanPay, ) .expect("signature hash should compute"); @@ -276,7 +356,7 @@ impl Inscribe { ); let witness = sighash_cache - .witness_mut(0) + .witness_mut(commit_input_offset) .expect("getting mutable witness reference should work"); witness.push(signature.as_ref()); witness.push(reveal_script); @@ -335,18 +415,21 @@ impl Inscribe { fn build_reveal_transaction( control_block: &ControlBlock, fee_rate: FeeRate, - input: OutPoint, - output: TxOut, + inputs: Vec, + outputs: Vec, script: &Script, ) -> (Transaction, Amount) { let reveal_tx = Transaction { - input: vec![TxIn { - previous_output: input, - script_sig: script::Builder::new().into_script(), - witness: Witness::new(), - sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, - }], - output: vec![output], + input: inputs + .iter() + .map(|outpoint| TxIn { + previous_output: *outpoint, + script_sig: script::Builder::new().into_script(), + witness: Witness::new(), + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + }) + .collect(), + output: outputs, lock_time: PackedLockTime::ZERO, version: 1, }; @@ -354,13 +437,15 @@ impl Inscribe { let fee = { let mut reveal_tx = reveal_tx.clone(); - reveal_tx.input[0].witness.push( - Signature::from_slice(&[0; SCHNORR_SIGNATURE_SIZE]) - .unwrap() - .as_ref(), - ); - reveal_tx.input[0].witness.push(script); - reveal_tx.input[0].witness.push(&control_block.serialize()); + for txin in &mut reveal_tx.input { + txin.witness.push( + Signature::from_slice(&[0; SCHNORR_SIGNATURE_SIZE]) + .unwrap() + .as_ref(), + ); + txin.witness.push(script); + txin.witness.push(&control_block.serialize()); + } fee_rate.fee(reveal_tx.vsize()) }; @@ -382,6 +467,7 @@ mod tests { let (commit_tx, reveal_tx, _private_key) = Inscribe::create_inscription_transactions( Some(satpoint(1, 0)), + None, inscription, BTreeMap::new(), Network::Bitcoin, @@ -413,6 +499,7 @@ mod tests { let (commit_tx, reveal_tx, _) = Inscribe::create_inscription_transactions( Some(satpoint(1, 0)), + None, inscription, BTreeMap::new(), Network::Bitcoin, @@ -448,6 +535,7 @@ mod tests { let error = Inscribe::create_inscription_transactions( satpoint, + None, inscription, inscriptions, Network::Bitcoin, @@ -490,6 +578,7 @@ mod tests { assert!(Inscribe::create_inscription_transactions( satpoint, + None, inscription, inscriptions, Network::Bitcoin, @@ -526,6 +615,7 @@ mod tests { let (commit_tx, reveal_tx, _private_key) = Inscribe::create_inscription_transactions( satpoint, + None, inscription, inscriptions, bitcoin::Network::Signet, @@ -588,6 +678,7 @@ mod tests { let (commit_tx, reveal_tx, _private_key) = Inscribe::create_inscription_transactions( satpoint, + None, inscription, inscriptions, bitcoin::Network::Signet, @@ -637,6 +728,7 @@ mod tests { let error = Inscribe::create_inscription_transactions( satpoint, + None, inscription, BTreeMap::new(), Network::Bitcoin, @@ -668,6 +760,7 @@ mod tests { let (_commit_tx, reveal_tx, _private_key) = Inscribe::create_inscription_transactions( satpoint, + None, inscription, BTreeMap::new(), Network::Bitcoin, diff --git a/src/test.rs b/src/test.rs index 27a8d45f83..a374d1fad4 100644 --- a/src/test.rs +++ b/src/test.rs @@ -101,7 +101,7 @@ pub(crate) fn tx_out(value: u64, address: Address) -> TxOut { } pub(crate) fn inscription(content_type: &str, body: impl AsRef<[u8]>) -> Inscription { - Inscription::new(Some(content_type.into()), Some(body.as_ref().into())) + Inscription::new(None, Some(content_type.into()), Some(body.as_ref().into())) } pub(crate) fn inscription_id(n: u32) -> InscriptionId { diff --git a/tests/lib.rs b/tests/lib.rs index 710e6b2a88..b871a03588 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -43,6 +43,7 @@ struct Inscribe { inscription: String, reveal: Txid, fees: u64, + parent: Option, } fn inscribe(rpc_server: &test_bitcoincore_rpc::Handle) -> Inscribe { diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 16b486037e..eda850f150 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -335,7 +335,7 @@ fn inscribe_with_dry_run_flag() { } #[test] -fn inscribe_with_dry_run_flag_fees_inscrease() { +fn inscribe_with_dry_run_flag_fees_increase() { let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); rpc_server.mine_blocks(1); @@ -394,3 +394,44 @@ fn inscribe_with_no_limit() { .write("degenerate.png", four_megger) .rpc_server(&rpc_server); } + +#[test] +fn inscribe_with_parent_inscription() { + let rpc_server = test_bitcoincore_rpc::spawn(); + create_wallet(&rpc_server); + rpc_server.mine_blocks(1); + + let parent_id = CommandBuilder::new("wallet inscribe parent.png") + .write("parent.png", [1; 520]) + .rpc_server(&rpc_server) + .output::() + .inscription; + + rpc_server.mine_blocks(1); + + assert_eq!( + parent_id, + CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) + .write("child.png", [1; 520]) + .rpc_server(&rpc_server) + .output::() + .parent + .unwrap() + ); +} + +#[test] +fn inscribe_with_non_existent_parent_inscription() { + let rpc_server = test_bitcoincore_rpc::spawn(); + create_wallet(&rpc_server); + rpc_server.mine_blocks(1); + + let parent_id = "3ac40a8f3c0d295386e1e597467a1ee0578df780834be885cd62337c2ed738a5i0"; + + CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) + .write("child.png", [1; 520]) + .rpc_server(&rpc_server) + .expected_stderr(format!("error: specified parent {parent_id} does not exist\n")) + .expected_exit_code(1) + .run(); +} From d1080430745320b6f2f1800f39ff782f6788e9d3 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 2 Mar 2023 23:12:44 +0100 Subject: [PATCH 02/34] Update src/inscription.rs --- src/inscription.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inscription.rs b/src/inscription.rs index cd6ae1d88e..26039bd4dd 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -65,7 +65,7 @@ impl Inscription { let content_type = Media::content_type_for_path(path)?; let parent = if let Some(inscription_id) = parent { - Some(inscription_id.to_string().into_bytes()) + Some(inscription_id.store()) } else { None }; From 12533658158518bd1120a5e681b6262f828f1a4e Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 2 Mar 2023 23:13:51 +0100 Subject: [PATCH 03/34] Update src/inscription.rs --- src/inscription.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inscription.rs b/src/inscription.rs index 26039bd4dd..d080ae332b 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -137,7 +137,7 @@ impl Inscription { pub(crate) fn get_parent_id(&self) -> Option { if let Some(vec) = &self.parent { - InscriptionId::from_str(str::from_utf8(&vec).unwrap()).ok() + InscriptionId::load(todo!()) } else { None } From 33cf4b51225ecb715bfed5ae0e28d702c9ad7706 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 2 Mar 2023 14:24:59 -0800 Subject: [PATCH 04/34] review changes --- src/index/entry.rs | 115 +++++++++++++++++++++++ src/index/updater/inscription_updater.rs | 2 + 2 files changed, 117 insertions(+) diff --git a/src/index/entry.rs b/src/index/entry.rs index 15ff3d8ecb..86403cbcf7 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -26,10 +26,12 @@ pub(crate) struct InscriptionEntry { pub(crate) fee: u64, pub(crate) height: u64, pub(crate) number: u64, + // pub(crate) parent: Option, pub(crate) sat: Option, pub(crate) timestamp: u32, } +// pub(crate) type InscriptionEntryValue = (u64, u64, u64, (u128, u128), u64, u32); pub(crate) type InscriptionEntryValue = (u64, u64, u64, u64, u32); impl Entry for InscriptionEntry { @@ -143,3 +145,116 @@ impl Entry for SatRange { n.to_le_bytes()[0..11].try_into().unwrap() } } + +pub(super) type TxidValue = [u8; 32]; + +impl Entry for Txid { + type Value = TxidValue; + + fn load(value: Self::Value) -> Self { + Txid::from_inner(value) + } + + fn store(self) -> Self::Value { + Txid::into_inner(self) + } +} + +impl Entry for Option { + type Value = (u128, u128, u32); + + fn load(value: Self::Value) -> Self { + if (0, 0, u32::MAX) == value { + None + } else { + let (head, tail, index) = value; + debug_assert_eq!(index, 0); + let head_array = head.to_le_bytes(); + let tail_array = tail.to_le_bytes(); + let array = [ + head_array[0], + head_array[1], + head_array[2], + head_array[3], + head_array[4], + head_array[5], + head_array[6], + head_array[7], + head_array[8], + head_array[9], + head_array[10], + head_array[11], + head_array[12], + head_array[13], + head_array[14], + head_array[15], + tail_array[0], + tail_array[1], + tail_array[2], + tail_array[3], + tail_array[4], + tail_array[5], + tail_array[6], + tail_array[7], + tail_array[8], + tail_array[9], + tail_array[10], + tail_array[11], + tail_array[12], + tail_array[13], + tail_array[14], + tail_array[15], + ]; + let txid = Txid::load(array); + // TODO: do we want to handle inscriptions not at index 0 + Some(InscriptionId::from(txid)) + } + } + + fn store(self) -> Self::Value { + if let Some(inscription_id) = self { + let txid_entry = inscription_id.txid.store(); + let head = u128::from_le_bytes([ + txid_entry[0], + txid_entry[1], + txid_entry[2], + txid_entry[3], + txid_entry[4], + txid_entry[5], + txid_entry[6], + txid_entry[7], + txid_entry[8], + txid_entry[9], + txid_entry[10], + txid_entry[11], + txid_entry[12], + txid_entry[13], + txid_entry[14], + txid_entry[15], + ]); + + let tail = u128::from_le_bytes([ + txid_entry[16 + 0], + txid_entry[16 + 1], + txid_entry[16 + 2], + txid_entry[16 + 3], + txid_entry[16 + 4], + txid_entry[16 + 5], + txid_entry[16 + 6], + txid_entry[16 + 7], + txid_entry[16 + 8], + txid_entry[16 + 9], + txid_entry[16 + 10], + txid_entry[16 + 11], + txid_entry[16 + 12], + txid_entry[16 + 13], + txid_entry[16 + 14], + txid_entry[16 + 15], + ]); + + (head, tail, inscription_id.index) + } else { + (0, 0, u32::MAX) + } + } +} diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 14c93a8ffb..a94e86ce75 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -7,7 +7,9 @@ pub(super) struct Flotsam { parent: Option, } +// change name to Jetsam or more poetic german word enum Origin { + // put Some(parent_id) in Origin::New() New(u64), Old(SatPoint), } From a43c376e54c5bbaadf289d81a6c496e2b14f1af7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 3 Mar 2023 13:21:47 -0800 Subject: [PATCH 05/34] stash --- src/index.rs | 2 +- src/index/entry.rs | 2 +- src/inscription.rs | 13 +++++-------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/index.rs b/src/index.rs index 9b93b2db36..366f27a741 100644 --- a/src/index.rs +++ b/src/index.rs @@ -18,7 +18,7 @@ use { std::sync::atomic::{self, AtomicBool}, }; -mod entry; +pub(crate) mod entry; mod fetcher; mod rtx; mod updater; diff --git a/src/index/entry.rs b/src/index/entry.rs index 86403cbcf7..5492fa1af6 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -1,6 +1,6 @@ use super::*; -pub(super) trait Entry: Sized { +pub(crate) trait Entry: Sized { type Value; fn load(value: Self::Value) -> Self; diff --git a/src/inscription.rs b/src/inscription.rs index d080ae332b..7b7d8dde7e 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -1,5 +1,6 @@ use { super::*, + crate::index::entry::Entry, bitcoin::{ blockdata::{ opcodes, @@ -64,14 +65,8 @@ impl Inscription { let content_type = Media::content_type_for_path(path)?; - let parent = if let Some(inscription_id) = parent { - Some(inscription_id.store()) - } else { - None - }; - Ok(Self { - parent, + parent: parent.store(), body: Some(body), content_type: Some(content_type.into()), }) @@ -137,7 +132,9 @@ impl Inscription { pub(crate) fn get_parent_id(&self) -> Option { if let Some(vec) = &self.parent { - InscriptionId::load(todo!()) + Some(InscriptionId::load( + vec.clone().try_into().expect("expected a [u8; 36]"), + )) } else { None } From c781e555c976affb2e57c163951afebeffe98b36 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 6 Mar 2023 18:06:57 -0800 Subject: [PATCH 06/34] stash --- src/index/entry.rs | 146 ++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/src/index/entry.rs b/src/index/entry.rs index 5492fa1af6..a8541a1f69 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -87,79 +87,6 @@ impl Entry for InscriptionId { } } -pub(super) type OutPointValue = [u8; 36]; - -impl Entry for OutPoint { - type Value = OutPointValue; - - fn load(value: Self::Value) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() - } - - fn store(self) -> Self::Value { - let mut value = [0; 36]; - self.consensus_encode(&mut value.as_mut_slice()).unwrap(); - value - } -} - -pub(super) type SatPointValue = [u8; 44]; - -impl Entry for SatPoint { - type Value = SatPointValue; - - fn load(value: Self::Value) -> Self { - Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() - } - - fn store(self) -> Self::Value { - let mut value = [0; 44]; - self.consensus_encode(&mut value.as_mut_slice()).unwrap(); - value - } -} - -pub(super) type SatRange = (u64, u64); - -impl Entry for SatRange { - type Value = [u8; 11]; - - fn load([b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10]: Self::Value) -> Self { - let raw_base = u64::from_le_bytes([b0, b1, b2, b3, b4, b5, b6, 0]); - - // 51 bit base - let base = raw_base & ((1 << 51) - 1); - - let raw_delta = u64::from_le_bytes([b6, b7, b8, b9, b10, 0, 0, 0]); - - // 33 bit delta - let delta = raw_delta >> 3; - - (base, base + delta) - } - - fn store(self) -> Self::Value { - let base = self.0; - let delta = self.1 - self.0; - let n = u128::from(base) | u128::from(delta) << 51; - n.to_le_bytes()[0..11].try_into().unwrap() - } -} - -pub(super) type TxidValue = [u8; 32]; - -impl Entry for Txid { - type Value = TxidValue; - - fn load(value: Self::Value) -> Self { - Txid::from_inner(value) - } - - fn store(self) -> Self::Value { - Txid::into_inner(self) - } -} - impl Entry for Option { type Value = (u128, u128, u32); @@ -258,3 +185,76 @@ impl Entry for Option { } } } + +pub(super) type OutPointValue = [u8; 36]; + +impl Entry for OutPoint { + type Value = OutPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 36]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value + } +} + +pub(super) type SatPointValue = [u8; 44]; + +impl Entry for SatPoint { + type Value = SatPointValue; + + fn load(value: Self::Value) -> Self { + Decodable::consensus_decode(&mut io::Cursor::new(value)).unwrap() + } + + fn store(self) -> Self::Value { + let mut value = [0; 44]; + self.consensus_encode(&mut value.as_mut_slice()).unwrap(); + value + } +} + +pub(super) type SatRange = (u64, u64); + +impl Entry for SatRange { + type Value = [u8; 11]; + + fn load([b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10]: Self::Value) -> Self { + let raw_base = u64::from_le_bytes([b0, b1, b2, b3, b4, b5, b6, 0]); + + // 51 bit base + let base = raw_base & ((1 << 51) - 1); + + let raw_delta = u64::from_le_bytes([b6, b7, b8, b9, b10, 0, 0, 0]); + + // 33 bit delta + let delta = raw_delta >> 3; + + (base, base + delta) + } + + fn store(self) -> Self::Value { + let base = self.0; + let delta = self.1 - self.0; + let n = u128::from(base) | u128::from(delta) << 51; + n.to_le_bytes()[0..11].try_into().unwrap() + } +} + +pub(super) type TxidValue = [u8; 32]; + +impl Entry for Txid { + type Value = TxidValue; + + fn load(value: Self::Value) -> Self { + Txid::from_inner(value) + } + + fn store(self) -> Self::Value { + Txid::into_inner(self) + } +} From d2eee5586df57125a9e05d00b85215cb726482e5 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 20:20:02 +0100 Subject: [PATCH 07/34] Update src/inscription.rs Co-authored-by: ericatallah --- src/inscription.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index 7b7d8dde7e..c2ae2ad694 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -132,9 +132,11 @@ impl Inscription { pub(crate) fn get_parent_id(&self) -> Option { if let Some(vec) = &self.parent { - Some(InscriptionId::load( - vec.clone().try_into().expect("expected a [u8; 36]"), - )) + if let Some(vec2) = vec.clone().try_into().ok() { + Some(InscriptionId::load(vec2)) + } else { + None + } } else { None } From 1bd6333381ef4c7f13e26b45919e51700ce61bbd Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 11:27:10 -0800 Subject: [PATCH 08/34] small clean up --- src/inscription.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index 7ec7fd6928..54710d2632 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -140,11 +140,11 @@ impl Inscription { pub(crate) fn get_parent_id(&self) -> Option { if let Some(vec) = &self.parent { - if let Some(vec2) = vec.clone().try_into().ok() { - Some(InscriptionId::load(vec2)) - } else { - None - } + vec + .clone() + .try_into() + .ok() + .map(|vec| InscriptionId::load(vec)) } else { None } From af9344ee3c537606508cbd1f72f46bd24e330359 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 11:54:04 -0800 Subject: [PATCH 09/34] stashing --- src/subcommand/server.rs | 1 + src/templates/inscription.rs | 4 ++++ templates/inscription.html | 4 ++++ tests/wallet/inscribe.rs | 31 ++++++++++++++++++++++--------- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index c16fbf3a31..bc6217653a 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -859,6 +859,7 @@ impl Server { next, number: entry.number, output, + parent: entry.parent, previous, sat: entry.sat, satpoint, diff --git a/src/templates/inscription.rs b/src/templates/inscription.rs index 0f396903fe..c6c40dcfa9 100644 --- a/src/templates/inscription.rs +++ b/src/templates/inscription.rs @@ -10,6 +10,7 @@ pub(crate) struct InscriptionHtml { pub(crate) next: Option, pub(crate) number: u64, pub(crate) output: TxOut, + pub(crate) parent: Option, pub(crate) previous: Option, pub(crate) sat: Option, pub(crate) satpoint: SatPoint, @@ -42,6 +43,7 @@ mod tests { next: None, number: 1, output: tx_out(1, address()), + parent: None, previous: None, sat: None, satpoint: satpoint(1, 0), @@ -102,6 +104,7 @@ mod tests { number: 1, output: tx_out(1, address()), previous: None, + parent: None, sat: Some(Sat(1)), satpoint: satpoint(1, 0), timestamp: timestamp(0), @@ -133,6 +136,7 @@ mod tests { next: Some(inscription_id(3)), number: 1, output: tx_out(1, address()), + parent: None, previous: Some(inscription_id(1)), sat: None, satpoint: satpoint(1, 0), diff --git a/templates/inscription.html b/templates/inscription.html index 37573f3706..fd1144a6ca 100644 --- a/templates/inscription.html +++ b/templates/inscription.html @@ -15,6 +15,10 @@

Inscription {{ self.number }}

id
{{ self.inscription_id }}
+%% if let Some(parent) = self.parent { +
parent
+
{{parent}}
+%% } %% if let Ok(address) = self.chain.address_from_script(&self.output.script_pubkey ) {
address
{{ address }}
diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index eda850f150..9bdcada61b 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -409,14 +409,25 @@ fn inscribe_with_parent_inscription() { rpc_server.mine_blocks(1); - assert_eq!( - parent_id, - CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) - .write("child.png", [1; 520]) - .rpc_server(&rpc_server) - .output::() - .parent - .unwrap() + TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + format!("/inscription/{parent_id}"), + format!(".*"), + ); + + let child_output = CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) + .write("child.png", [1; 520]) + .rpc_server(&rpc_server) + .output::(); + + rpc_server.mine_blocks(1); + + assert_eq!(parent_id, child_output.parent.unwrap()); + + println!("{}", child_output.inscription); + + TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + format!("/inscription/{}", child_output.inscription), + format!(".*parent.*{}", parent_id), ); } @@ -431,7 +442,9 @@ fn inscribe_with_non_existent_parent_inscription() { CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) .write("child.png", [1; 520]) .rpc_server(&rpc_server) - .expected_stderr(format!("error: specified parent {parent_id} does not exist\n")) + .expected_stderr(format!( + "error: specified parent {parent_id} does not exist\n" + )) .expected_exit_code(1) .run(); } From 96a08c29b477c7a789f138957bb04b9bf8d54586 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 13:11:08 -0800 Subject: [PATCH 10/34] stash --- src/index.rs | 52 ++++++++++++++++++++++++ src/index/entry.rs | 1 + src/index/updater.rs | 1 + src/index/updater/inscription_updater.rs | 12 +++--- src/subcommand/server.rs | 2 +- src/test.rs | 6 +++ tests/test_server.rs | 1 + tests/wallet/inscribe.rs | 6 +-- 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/index.rs b/src/index.rs index a6948ac182..c2e3d9bb47 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2190,4 +2190,56 @@ mod tests { ); } } + + #[test] + fn test_inscription_with_parent() { + // for context in Context::configurations() { + let context = Context::builder().build(); + + context.mine_blocks(1); + + let parent_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0)], + witness: inscription("text/plain", "parent").to_witness(), + ..Default::default() + }); + + let parent_id = InscriptionId::from(parent_txid); + + context.mine_blocks(1); + + assert_eq!( + context.index.get_inscription_entry(parent_id).unwrap(), + Some(InscriptionEntry { + fee: 0, + height: 2, + number: 0, + parent: None, + sat: None, + timestamp: 2 + }) + ); + + let child_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0)], + witness: inscription_with_parent("text/plain", "child", parent_id).to_witness(), + ..Default::default() + }); + + let child_id = InscriptionId::from(child_txid); + + context.mine_blocks(1); + + assert_eq!( + context.index.get_inscription_entry(child_id).unwrap(), + Some(InscriptionEntry { + fee: 0, + height: 2, + number: 0, + parent: Some(parent_id), + sat: None, + timestamp: 2 + }) + ); + } } diff --git a/src/index/entry.rs b/src/index/entry.rs index d670ad5bf9..f910112db6 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -22,6 +22,7 @@ impl Entry for BlockHash { } } +#[derive(Debug, PartialEq)] pub(crate) struct InscriptionEntry { pub(crate) fee: u64, pub(crate) height: u64, diff --git a/src/index/updater.rs b/src/index/updater.rs index 69ad34b249..9ce9292b95 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -65,6 +65,7 @@ impl Updater { range_cache: HashMap::new(), height, index_sats: index.has_sat_index()?, + sat_ranges_since_flush: 0, outputs_cached: 0, outputs_inserted_since_flush: 0, diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index c9613092f1..248b91ffae 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -4,13 +4,13 @@ pub(super) struct Flotsam { inscription_id: InscriptionId, offset: u64, origin: Origin, - parent: Option, + // parent: Option, } // change name to Jetsam or more poetic german word enum Origin { // put Some(parent_id) in Origin::New() - New(u64), + New((u64, Option)), Old(SatPoint), } @@ -92,7 +92,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { offset: input_value + old_satpoint.offset, inscription_id, origin: Origin::Old(old_satpoint), - parent: None, }); } @@ -133,8 +132,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { inscriptions.push(Flotsam { inscription_id: txid.into(), offset: 0, - origin: Origin::New(input_value - tx.output.iter().map(|txout| txout.value).sum::()), - parent, + origin: Origin::New((input_value - tx.output.iter().map(|txout| txout.value).sum::(), parent)), }); } }; @@ -219,7 +217,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Origin::Old(old_satpoint) => { self.satpoint_to_id.remove(&old_satpoint.store())?; } - Origin::New(fee) => { + Origin::New((fee, parent)) => { self .number_to_id .insert(&self.next_number, &inscription_id)?; @@ -245,7 +243,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { fee, height: self.height, number: self.next_number, - parent: None, + parent, sat, timestamp: self.timestamp, } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index bc6217653a..905aa69484 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -859,7 +859,7 @@ impl Server { next, number: entry.number, output, - parent: entry.parent, + parent: dbg!(entry.parent), previous, sat: entry.sat, satpoint, diff --git a/src/test.rs b/src/test.rs index a374d1fad4..a22c65d5f5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -104,6 +104,12 @@ pub(crate) fn inscription(content_type: &str, body: impl AsRef<[u8]>) -> Inscrip Inscription::new(None, Some(content_type.into()), Some(body.as_ref().into())) } +pub(crate) fn inscription_with_parent(content_type: &str, body: impl AsRef<[u8]>, parent: InscriptionId) -> Inscription { + let mut vec = parent.txid.to_vec(); + vec.push(parent.index.try_into().unwrap()); + Inscription::new(Some(vec), Some(content_type.into()), Some(body.as_ref().into())) +} + pub(crate) fn inscription_id(n: u32) -> InscriptionId { let hex = format!("{n:x}"); diff --git a/tests/test_server.rs b/tests/test_server.rs index d99fb3ff93..ac8e5d98c0 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -74,6 +74,7 @@ impl TestServer { thread::sleep(Duration::from_millis(25)); } + dbg!(path.as_ref()); let response = reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap(); assert_eq!(response.status(), StatusCode::OK); assert_regex_match!(response.text().unwrap(), regex.as_ref()); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 9bdcada61b..c9cf34c6bc 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -419,14 +419,12 @@ fn inscribe_with_parent_inscription() { .rpc_server(&rpc_server) .output::(); - rpc_server.mine_blocks(1); - assert_eq!(parent_id, child_output.parent.unwrap()); - println!("{}", child_output.inscription); + rpc_server.mine_blocks(1); TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - format!("/inscription/{}", child_output.inscription), + format!("/inscription/{}", dbg!(child_output.inscription)), format!(".*parent.*{}", parent_id), ); } From 01bc48eff41e87e2e221809b2c3881a01cd67ea7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 13:48:57 -0800 Subject: [PATCH 11/34] quick fix --- tests/wallet/inscribe.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index c9cf34c6bc..55ddc4846c 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -409,10 +409,10 @@ fn inscribe_with_parent_inscription() { rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - format!("/inscription/{parent_id}"), - format!(".*"), - ); + // TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + // format!("/inscription/{parent_id}"), + // format!(".*"), + // ); let child_output = CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) .write("child.png", [1; 520]) From a5029a9cbf33e6899e06afb290aa5ed201ee850a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 7 Mar 2023 15:19:10 -0800 Subject: [PATCH 12/34] fixed database stuff --- src/index.rs | 102 ++++++++-------- src/index/entry.rs | 142 ++++++++++++++++------- src/index/updater/inscription_updater.rs | 13 ++- src/inscription.rs | 26 +---- src/subcommand/wallet/inscribe.rs | 5 +- src/test.rs | 6 - tests/wallet/inscribe.rs | 4 +- 7 files changed, 167 insertions(+), 131 deletions(-) diff --git a/src/index.rs b/src/index.rs index c2e3d9bb47..0be4dc31de 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2191,55 +2191,55 @@ mod tests { } } - #[test] - fn test_inscription_with_parent() { - // for context in Context::configurations() { - let context = Context::builder().build(); - - context.mine_blocks(1); - - let parent_txid = context.rpc_server.broadcast_tx(TransactionTemplate { - inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "parent").to_witness(), - ..Default::default() - }); - - let parent_id = InscriptionId::from(parent_txid); - - context.mine_blocks(1); - - assert_eq!( - context.index.get_inscription_entry(parent_id).unwrap(), - Some(InscriptionEntry { - fee: 0, - height: 2, - number: 0, - parent: None, - sat: None, - timestamp: 2 - }) - ); - - let child_txid = context.rpc_server.broadcast_tx(TransactionTemplate { - inputs: &[(2, 1, 0)], - witness: inscription_with_parent("text/plain", "child", parent_id).to_witness(), - ..Default::default() - }); - - let child_id = InscriptionId::from(child_txid); - - context.mine_blocks(1); - - assert_eq!( - context.index.get_inscription_entry(child_id).unwrap(), - Some(InscriptionEntry { - fee: 0, - height: 2, - number: 0, - parent: Some(parent_id), - sat: None, - timestamp: 2 - }) - ); - } + // #[test] + // fn test_inscription_with_parent() { + // // for context in Context::configurations() { + // let context = Context::builder().build(); + // + // context.mine_blocks(1); + // + // let parent_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + // inputs: &[(1, 0, 0)], + // witness: inscription("text/plain", "parent").to_witness(), + // ..Default::default() + // }); + // + // let parent_id = InscriptionId::from(parent_txid); + // + // context.mine_blocks(1); + // + // assert_eq!( + // context.index.get_inscription_entry(parent_id).unwrap(), + // Some(InscriptionEntry { + // fee: 0, + // height: 2, + // number: 0, + // parent: None, + // sat: None, + // timestamp: 2 + // }) + // ); + // + // let child_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + // inputs: &[(2, 1, 0)], + // witness: inscription_with_parent("text/plain", "child", parent_id).to_witness(), + // ..Default::default() + // }); + // + // let child_id = InscriptionId::from(child_txid); + // + // context.mine_blocks(1); + // + // assert_eq!( + // context.index.get_inscription_entry(child_id).unwrap(), + // Some(InscriptionEntry { + // fee: 0, + // height: 2, + // number: 0, + // parent: Some(parent_id), + // sat: None, + // timestamp: 2 + // }) + // ); + // } } diff --git a/src/index/entry.rs b/src/index/entry.rs index f910112db6..5a971c1af7 100644 --- a/src/index/entry.rs +++ b/src/index/entry.rs @@ -89,17 +89,19 @@ impl Entry for InscriptionId { } } +type ParentValue = (u128, u128, u32); + impl Entry for Option { - type Value = (u128, u128, u32); + type Value = ParentValue; fn load(value: Self::Value) -> Self { if (0, 0, u32::MAX) == value { None } else { let (head, tail, index) = value; - debug_assert_eq!(index, 0); let head_array = head.to_le_bytes(); let tail_array = tail.to_le_bytes(); + let index_array = index.to_be_bytes(); let array = [ head_array[0], head_array[1], @@ -133,55 +135,22 @@ impl Entry for Option { tail_array[13], tail_array[14], tail_array[15], + index_array[0], + index_array[1], + index_array[2], + index_array[3], ]; - let txid = Txid::load(array); - // TODO: do we want to handle inscriptions not at index 0 - Some(InscriptionId::from(txid)) + + Some(InscriptionId::load(array)) } } - + // TODO: test head and tail byte order fn store(self) -> Self::Value { if let Some(inscription_id) = self { let txid_entry = inscription_id.txid.store(); - let head = u128::from_le_bytes([ - txid_entry[0], - txid_entry[1], - txid_entry[2], - txid_entry[3], - txid_entry[4], - txid_entry[5], - txid_entry[6], - txid_entry[7], - txid_entry[8], - txid_entry[9], - txid_entry[10], - txid_entry[11], - txid_entry[12], - txid_entry[13], - txid_entry[14], - txid_entry[15], - ]); - - let tail = u128::from_le_bytes([ - txid_entry[16 + 0], - txid_entry[16 + 1], - txid_entry[16 + 2], - txid_entry[16 + 3], - txid_entry[16 + 4], - txid_entry[16 + 5], - txid_entry[16 + 6], - txid_entry[16 + 7], - txid_entry[16 + 8], - txid_entry[16 + 9], - txid_entry[16 + 10], - txid_entry[16 + 11], - txid_entry[16 + 12], - txid_entry[16 + 13], - txid_entry[16 + 14], - txid_entry[16 + 15], - ]); - - (head, tail, inscription_id.index) + let little_end = u128::from_le_bytes(txid_entry[..16].try_into().unwrap()); + let big_end = u128::from_le_bytes(txid_entry[16..].try_into().unwrap()); + (little_end, big_end, inscription_id.index) } else { (0, 0, u32::MAX) } @@ -260,3 +229,86 @@ impl Entry for Txid { Txid::into_inner(self) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parent_entry() { + let inscription_id: Option = None; + + assert_eq!(inscription_id.store(), (0, 0, u32::MAX)); + assert_eq!( + as Entry>::load((0, 0, u32::MAX)), + inscription_id + ); + + let inscription_id = Some( + "0000000000000000000000000000000000000000000000000000000000000000i0" + .parse::() + .unwrap(), + ); + + assert_eq!(inscription_id.store(), (0, 0, 0)); + assert_eq!( + as Entry>::load((0, 0, 0)), + inscription_id + ); + + let inscription_id = Some( + "ffffffffffffffffffffffffffffffff00000000000000000000000000000000i0" + .parse::() + .unwrap(), + ); + + assert_eq!(inscription_id.store(), (0, u128::MAX, 0)); + assert_eq!( + as Entry>::load((0, u128::MAX, 0)), + inscription_id + ); + } + + #[test] + fn parent_entry_individual_byte_order() { + let inscription_id = Some( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefi0" + .parse::() + .unwrap(), + ); + + assert_eq!( + inscription_id.store(), + ( + 0x0123456789abcdef0123456789abcdef, + 0x0123456789abcdef0123456789abcdef, + 0 + ) + ); + + assert_eq!( + as Entry>::load(( + 0x0123456789abcdef0123456789abcdef, + 0x0123456789abcdef0123456789abcdef, + 0 + )), + inscription_id + ); + } + + #[test] + fn parent_entry_index() { + let inscription_id = Some( + "0000000000000000000000000000000000000000000000000000000000000000i1" + .parse::() + .unwrap(), + ); + + assert_eq!(inscription_id.store(), (0, 0, 1)); + + assert_eq!( + as Entry>::load((0, 0, 1)), + inscription_id + ); + } +} diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 248b91ffae..a41a28204e 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -73,15 +73,13 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { pub(super) fn index_transaction_inscriptions( &mut self, tx: &Transaction, - txid: Txid, // we can calulcate this from tx. Is this expensive? + txid: Txid, input_sat_ranges: Option<&VecDeque<(u64, u64)>>, ) -> Result { let mut inscriptions = Vec::new(); - // go through all flotsam and ensure parent is there let mut input_value = 0; for tx_in in &tx.input { - // if coinbase if tx_in.previous_output.is_null() { input_value += Height(self.height).subsidy(); } else { @@ -113,7 +111,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - // make sure no re-inscriptions + // TODO: find a different way to + // TODO: handle re-inscriptions properly if inscriptions.iter().all(|flotsam| flotsam.offset != 0) { if let Some(inscription) = Inscription::from_transaction(tx) { let parent = if let Some(parent_id) = inscription.get_parent_id() { @@ -132,7 +131,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { inscriptions.push(Flotsam { inscription_id: txid.into(), offset: 0, - origin: Origin::New((input_value - tx.output.iter().map(|txout| txout.value).sum::(), parent)), + origin: Origin::New(( + input_value - tx.output.iter().map(|txout| txout.value).sum::(), + parent, + )), }); } }; @@ -169,6 +171,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { self.update_inscription_location( input_sat_ranges, + // TODO: something with two inscriptions in the input inscriptions.next().unwrap(), new_satpoint, )?; diff --git a/src/inscription.rs b/src/inscription.rs index 54710d2632..2a810e969b 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -20,7 +20,7 @@ const PARENT_TAG: &[u8] = &[3]; #[derive(Debug, PartialEq, Clone)] pub(crate) struct Inscription { - parent: Option>, + parent: Option, content_type: Option>, body: Option>, } @@ -28,7 +28,7 @@ pub(crate) struct Inscription { impl Inscription { #[cfg(test)] pub(crate) fn new( - parent: Option>, + parent: Option, content_type: Option>, body: Option>, ) -> Self { @@ -65,14 +65,6 @@ impl Inscription { let content_type = Media::content_type_for_path(path)?; - let parent = if let Some(inscription_id) = parent { - let mut vec = inscription_id.txid.to_vec(); - vec.push(inscription_id.index.try_into().unwrap()); - Some(vec) - } else { - None - }; - Ok(Self { parent, body: Some(body), @@ -87,7 +79,7 @@ impl Inscription { .push_slice(PROTOCOL_ID); if let Some(parent) = &self.parent { - builder = builder.push_slice(PARENT_TAG).push_slice(parent); + builder = builder.push_slice(PARENT_TAG).push_slice(&parent.store()); } if let Some(content_type) = &self.content_type { @@ -139,15 +131,7 @@ impl Inscription { } pub(crate) fn get_parent_id(&self) -> Option { - if let Some(vec) = &self.parent { - vec - .clone() - .try_into() - .ok() - .map(|vec| InscriptionId::load(vec)) - } else { - None - } + self.parent } #[cfg(test)] @@ -279,7 +263,7 @@ impl<'a> InscriptionParser<'a> { return Ok(Some(Inscription { body, content_type, - parent, + parent: parent.map(|parent| InscriptionId::load(parent.as_slice().try_into().unwrap())), })); } diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 54c3da4f96..584521dffb 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -123,7 +123,10 @@ impl Inscribe { utxos.insert( partially_signed_reveal_tx.input[commit_input_offset].previous_output, Amount::from_sat( - unsigned_commit_tx.output[partially_signed_reveal_tx.input[commit_input_offset].previous_output.vout as usize].value, + unsigned_commit_tx.output[partially_signed_reveal_tx.input[commit_input_offset] + .previous_output + .vout as usize] + .value, ), ); diff --git a/src/test.rs b/src/test.rs index a22c65d5f5..a374d1fad4 100644 --- a/src/test.rs +++ b/src/test.rs @@ -104,12 +104,6 @@ pub(crate) fn inscription(content_type: &str, body: impl AsRef<[u8]>) -> Inscrip Inscription::new(None, Some(content_type.into()), Some(body.as_ref().into())) } -pub(crate) fn inscription_with_parent(content_type: &str, body: impl AsRef<[u8]>, parent: InscriptionId) -> Inscription { - let mut vec = parent.txid.to_vec(); - vec.push(parent.index.try_into().unwrap()); - Inscription::new(Some(vec), Some(content_type.into()), Some(body.as_ref().into())) -} - pub(crate) fn inscription_id(n: u32) -> InscriptionId { let hex = format!("{n:x}"); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 55ddc4846c..0b40f21ab1 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -410,8 +410,8 @@ fn inscribe_with_parent_inscription() { rpc_server.mine_blocks(1); // TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - // format!("/inscription/{parent_id}"), - // format!(".*"), + // format!("/inscription/{parent_id}"), + // format!(".*"), // ); let child_output = CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) From ff6c809917a3996dd1e2216d02c1bb509ba96f21 Mon Sep 17 00:00:00 2001 From: Ordinally Date: Wed, 8 Mar 2023 20:25:28 +0100 Subject: [PATCH 13/34] Fix sighashflag issue ``` error: Failed to send reveal transaction because: JSON-RPC error: RPC error response: RpcError { code: -26, message: "non-mandatory-script-verify-flag (Invalid Schnorr signature)", data: None } ``` --- src/subcommand/wallet/inscribe.rs | 53 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 584521dffb..07f8631a86 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -259,31 +259,32 @@ impl Inscribe { let commit_tx_address = Address::p2tr_tweaked(taproot_spend_info.output_key(), network); - let (mut inputs, mut outputs, commit_input_offset) = if let Some((satpoint, output)) = parent { - ( - vec![satpoint.outpoint, OutPoint::null()], - vec![ - TxOut { - script_pubkey: output.script_pubkey, - value: output.value, - }, - TxOut { + let (mut inputs, mut outputs, commit_input_offset) = + if let Some((satpoint, output)) = parent.clone() { + ( + vec![satpoint.outpoint, OutPoint::null()], + vec![ + TxOut { + script_pubkey: output.script_pubkey, + value: output.value, + }, + TxOut { + script_pubkey: destination.script_pubkey(), + value: 0, + }, + ], + 1, + ) + } else { + ( + vec![OutPoint::null()], + vec![TxOut { script_pubkey: destination.script_pubkey(), value: 0, - }, - ], - 1, - ) - } else { - ( - vec![OutPoint::null()], - vec![TxOut { - script_pubkey: destination.script_pubkey(), - value: 0, - }], - 0, - ) - }; + }], + 0, + ) + }; let (_, reveal_fee) = Self::build_reveal_transaction( &control_block, @@ -350,7 +351,11 @@ impl Inscribe { commit_input_offset, &Prevouts::One(commit_input_offset, output), TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), - SchnorrSighashType::AllPlusAnyoneCanPay, + if let Some(_parent) = parent { + SchnorrSighashType::AllPlusAnyoneCanPay + } else { + SchnorrSighashType::Default + }, ) .expect("signature hash should compute"); From 340fdef80da259acca77b6aed8449514488ccbf6 Mon Sep 17 00:00:00 2001 From: Ordinally Date: Wed, 8 Mar 2023 20:42:08 +0100 Subject: [PATCH 14/34] Fix signature error ``` thread 'main' panicked at 'signature hash should compute: PrevoutKind', src/subcommand/wallet/inscribe.rs:360:8 ``` --- src/subcommand/wallet/inscribe.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 07f8631a86..4c6f6e5e90 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -346,18 +346,22 @@ impl Inscribe { let mut sighash_cache = SighashCache::new(&mut reveal_tx); - let signature_hash = sighash_cache - .taproot_script_spend_signature_hash( + let signature_hash = if let Some(_parent) = parent { + sighash_cache.taproot_script_spend_signature_hash( commit_input_offset, &Prevouts::One(commit_input_offset, output), TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), - if let Some(_parent) = parent { - SchnorrSighashType::AllPlusAnyoneCanPay - } else { - SchnorrSighashType::Default - }, + SchnorrSighashType::AllPlusAnyoneCanPay, ) - .expect("signature hash should compute"); + } else { + sighash_cache.taproot_script_spend_signature_hash( + commit_input_offset, + &Prevouts::All(&[output]), + TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), + SchnorrSighashType::Default, + ) + } + .expect("signature hash should compute"); let signature = secp256k1.sign_schnorr( &secp256k1::Message::from_slice(signature_hash.as_inner()) From ca16f448ba35599c118080029a568426c4689d35 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 8 Mar 2023 13:35:51 -0800 Subject: [PATCH 15/34] refector re-inscription handling --- src/index/updater/inscription_updater.rs | 145 ++++++++++++++--------- src/inscription.rs | 8 +- 2 files changed, 92 insertions(+), 61 deletions(-) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index a41a28204e..71d5901eeb 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -1,15 +1,15 @@ -use super::*; +use {super::*, std::collections::BTreeSet}; +#[derive(Clone, Copy)] pub(super) struct Flotsam { inscription_id: InscriptionId, offset: u64, origin: Origin, - // parent: Option, } // change name to Jetsam or more poetic german word +#[derive(Clone, Copy)] enum Origin { - // put Some(parent_id) in Origin::New() New((u64, Option)), Old(SatPoint), } @@ -76,68 +76,99 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { txid: Txid, input_sat_ranges: Option<&VecDeque<(u64, u64)>>, ) -> Result { - let mut inscriptions = Vec::new(); - + let mut floating_inscriptions = Vec::new(); + let mut inscribed_offsets = BTreeSet::new(); let mut input_value = 0; for tx_in in &tx.input { + // skip subsidy since no inscriptions possible if tx_in.previous_output.is_null() { input_value += Height(self.height).subsidy(); - } else { - for (old_satpoint, inscription_id) in - Index::inscriptions_on_output(self.satpoint_to_id, tx_in.previous_output)? - { - inscriptions.push(Flotsam { - offset: input_value + old_satpoint.offset, - inscription_id, - origin: Origin::Old(old_satpoint), + continue; + } + + // find existing inscriptions on input aka transfers + for (old_satpoint, inscription_id) in + Index::inscriptions_on_output(self.satpoint_to_id, tx_in.previous_output)? + { + floating_inscriptions.push(Flotsam { + offset: input_value + old_satpoint.offset, + inscription_id, + origin: Origin::Old(old_satpoint), + }); + + inscribed_offsets.insert(input_value + old_satpoint.offset); + } + + // find new inscriptions + if let Some(inscription) = Inscription::from_tx_input(tx_in) { + // ignore new inscriptions on already inscribed offset (sats) + if !inscribed_offsets.contains(&input_value) { + let parent = if let Some(parent_id) = inscription.get_parent_id() { + // parent has to be in an input before child + // think about specifying a more general approach in a protocol doc/BIP + if floating_inscriptions + .iter() + .any(|flotsam| flotsam.inscription_id == parent_id) + { + Some(parent_id) + } else { + None + } + } else { + None + }; + + floating_inscriptions.push(Flotsam { + inscription_id: InscriptionId { + txid, + index: input_value as u32, // TODO: is index a sat offset or and number of inscriptions offset + }, + offset: input_value, + origin: Origin::New((0, parent)), }); } + } - input_value += if let Some(value) = self.value_cache.remove(&tx_in.previous_output) { - value - } else if let Some(value) = self - .outpoint_to_value - .remove(&tx_in.previous_output.store())? - { - value.value() - } else { - self.value_receiver.blocking_recv().ok_or_else(|| { - anyhow!( - "failed to get transaction for {}", - tx_in.previous_output.txid - ) - })? - } + // different ways to get the utxo set (input amount) + input_value += if let Some(value) = self.value_cache.remove(&tx_in.previous_output) { + value + } else if let Some(value) = self + .outpoint_to_value + .remove(&tx_in.previous_output.store())? + { + value.value() + } else { + self.value_receiver.blocking_recv().ok_or_else(|| { + anyhow!( + "failed to get transaction for {}", + tx_in.previous_output.txid + ) + })? } } - // TODO: find a different way to - // TODO: handle re-inscriptions properly - if inscriptions.iter().all(|flotsam| flotsam.offset != 0) { - if let Some(inscription) = Inscription::from_transaction(tx) { - let parent = if let Some(parent_id) = inscription.get_parent_id() { - if inscriptions - .iter() - .any(|flotsam| flotsam.inscription_id == parent_id) - { - Some(parent_id) - } else { - None + let mut floating_inscriptions = floating_inscriptions + .into_iter() + .map(|flotsam| { + if let Flotsam { + inscription_id, + offset, + origin: Origin::New((_, parent)), + } = flotsam + { + Flotsam { + inscription_id, + offset, + origin: Origin::New(( + input_value - tx.output.iter().map(|txout| txout.value).sum::(), + parent, + )), } } else { - None - }; - - inscriptions.push(Flotsam { - inscription_id: txid.into(), - offset: 0, - origin: Origin::New(( - input_value - tx.output.iter().map(|txout| txout.value).sum::(), - parent, - )), - }); - } - }; + flotsam + } + }) + .collect::>(); let is_coinbase = tx .input @@ -146,11 +177,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .unwrap_or_default(); if is_coinbase { - inscriptions.append(&mut self.flotsam); + floating_inscriptions.append(&mut self.flotsam); } - inscriptions.sort_by_key(|flotsam| flotsam.offset); - let mut inscriptions = inscriptions.into_iter().peekable(); + floating_inscriptions.sort_by_key(|flotsam| flotsam.offset); + let mut inscriptions = floating_inscriptions.into_iter().peekable(); let mut output_value = 0; for (vout, tx_out) in tx.output.iter().enumerate() { @@ -171,7 +202,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { self.update_inscription_location( input_sat_ranges, - // TODO: something with two inscriptions in the input + // TODO: do something with two inscriptions in the input inscriptions.next().unwrap(), new_satpoint, )?; diff --git a/src/inscription.rs b/src/inscription.rs index 2a810e969b..1d53c85cdd 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -40,13 +40,13 @@ impl Inscription { } pub(crate) fn from_transaction(tx: &Transaction) -> Option { - // let mut inscriptions = Vec::new(); - // for input in tx.input { - // InscriptionParser::parse(input.witness).ok() - // } InscriptionParser::parse(&tx.input.get(0)?.witness).ok() } + pub(crate) fn from_tx_input(tx_in: &TxIn) -> Option { + InscriptionParser::parse(&tx_in.witness).ok() + } + pub(crate) fn from_file( chain: Chain, path: impl AsRef, From 791e21cc6bedc33af746ef13cbc27dd03c3b673a Mon Sep 17 00:00:00 2001 From: ordinally Date: Wed, 8 Mar 2023 22:37:43 +0100 Subject: [PATCH 16/34] More robust parsing --- src/inscription.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/inscription.rs b/src/inscription.rs index 2a810e969b..279380bda2 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -263,7 +263,15 @@ impl<'a> InscriptionParser<'a> { return Ok(Some(Inscription { body, content_type, - parent: parent.map(|parent| InscriptionId::load(parent.as_slice().try_into().unwrap())), + parent: match parent { + None => None, + Some(bytes) => { + if bytes.len() != 36 { + return Err(InscriptionError::InvalidInscription) + } + Some(InscriptionId::load(bytes.as_slice().try_into().unwrap())) + } + }, })); } From 4e4cf9f96dbb30cd1f439ff0703832ca51bfc7b0 Mon Sep 17 00:00:00 2001 From: ordinally Date: Fri, 10 Mar 2023 13:33:34 +0100 Subject: [PATCH 17/34] Add temporary debug logging and TODO regarding bug in building the reveal TX for inscriptions containing a parent. --- src/subcommand/wallet/inscribe.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 4c6f6e5e90..65db2d389d 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,3 +1,5 @@ +use bitcoin::consensus::serialize; + use { super::*, crate::wallet::Wallet, @@ -154,10 +156,20 @@ impl Inscribe { .send_raw_transaction(&signed_raw_commit_tx) .context("Failed to send commit transaction")?; + log::debug!( + "partially signed reveal tx: {}", + hex::encode(serialize(&partially_signed_reveal_tx)) + ); let reveal = if self.parent.is_some() { let fully_signed_raw_reveal_tx = client .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? .hex; + // TODO: there is a bug here, the fully signed reveal TX no longer contains + // the inscription data + log::debug!( + "fully signed reveal tx: {}", + hex::encode(serialize(&fully_signed_raw_reveal_tx)) + ); client .send_raw_transaction(&fully_signed_raw_reveal_tx) From 24f5bdbdf619bbbdac5a29f6bea954eb3b7c71c8 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 10 Mar 2023 10:07:51 -0800 Subject: [PATCH 18/34] inscription index now correct --- src/index.rs | 162 +++++++++++++---------- src/index/updater/inscription_updater.rs | 8 +- src/subcommand/server.rs | 40 +++--- src/test.rs | 4 + test-bitcoincore-rpc/src/lib.rs | 4 +- test-bitcoincore-rpc/src/state.rs | 6 +- 6 files changed, 123 insertions(+), 101 deletions(-) diff --git a/src/index.rs b/src/index.rs index 0be4dc31de..f9b3927601 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1032,7 +1032,7 @@ mod tests { let inscription = inscription("text/plain;charset=utf-8", "hello"); let template = TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription.to_witness(), + witnesses: vec![inscription.to_witness()], ..Default::default() }; @@ -1378,7 +1378,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1403,7 +1403,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1447,7 +1447,7 @@ mod tests { let first_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); @@ -1455,7 +1455,7 @@ mod tests { let second_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0)], - witness: inscription("text/png", [1; 100]).to_witness(), + witnesses: vec![inscription("text/png", [1; 100]).to_witness()], ..Default::default() }); let second_inscription_id = InscriptionId::from(second_txid); @@ -1502,7 +1502,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1551,7 +1551,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1595,7 +1595,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1632,7 +1632,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1661,7 +1661,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1687,7 +1687,7 @@ mod tests { let first_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let first_inscription_id = InscriptionId::from(first_txid); @@ -1698,7 +1698,7 @@ mod tests { let second_txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(3, 0, 0)], fee: 50 * COIN_VALUE, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let second_inscription_id = InscriptionId::from(second_txid); @@ -1812,7 +1812,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0)], outputs: 2, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1844,7 +1844,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], outputs: 2, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], output_values: &[0, 50 * COIN_VALUE], ..Default::default() }); @@ -1870,7 +1870,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], fee: 50 * COIN_VALUE, - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -1979,7 +1979,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); @@ -2038,7 +2038,7 @@ mod tests { let first = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); @@ -2071,7 +2071,7 @@ mod tests { let second = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 1, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); @@ -2110,7 +2110,7 @@ mod tests { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -2137,7 +2137,7 @@ mod tests { for i in 0..103 { let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(i + 1, 0, 0)], - witness: inscription("text/plain", "hello").to_witness(), + witnesses: vec![inscription("text/plain", "hello").to_witness()], ..Default::default() }); ids.push(InscriptionId::from(txid)); @@ -2191,55 +2191,75 @@ mod tests { } } - // #[test] - // fn test_inscription_with_parent() { - // // for context in Context::configurations() { - // let context = Context::builder().build(); - // - // context.mine_blocks(1); - // - // let parent_txid = context.rpc_server.broadcast_tx(TransactionTemplate { - // inputs: &[(1, 0, 0)], - // witness: inscription("text/plain", "parent").to_witness(), - // ..Default::default() - // }); - // - // let parent_id = InscriptionId::from(parent_txid); - // - // context.mine_blocks(1); - // - // assert_eq!( - // context.index.get_inscription_entry(parent_id).unwrap(), - // Some(InscriptionEntry { - // fee: 0, - // height: 2, - // number: 0, - // parent: None, - // sat: None, - // timestamp: 2 - // }) - // ); - // - // let child_txid = context.rpc_server.broadcast_tx(TransactionTemplate { - // inputs: &[(2, 1, 0)], - // witness: inscription_with_parent("text/plain", "child", parent_id).to_witness(), - // ..Default::default() - // }); - // - // let child_id = InscriptionId::from(child_txid); - // - // context.mine_blocks(1); - // - // assert_eq!( - // context.index.get_inscription_entry(child_id).unwrap(), - // Some(InscriptionEntry { - // fee: 0, - // height: 2, - // number: 0, - // parent: Some(parent_id), - // sat: None, - // timestamp: 2 - // }) - // ); - // } + #[test] + fn test_inscription_with_parent() { + // for context in Context::configurations() { + let context = Context::builder().build(); + + context.mine_blocks(1); + + let parent_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0)], + witnesses: vec![inscription("text/plain", "parent").to_witness()], + ..Default::default() + }); + + let parent_id = InscriptionId::from(parent_txid); + + context.mine_blocks(1); + + assert_eq!( + context.index.get_inscription_entry(parent_id).unwrap(), + Some(InscriptionEntry { + fee: 0, + height: 2, + number: 0, + parent: None, + sat: None, + timestamp: 2 + }) + ); + + let child_txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 1, 0), (2, 0, 0)], + witnesses: vec![ + Witness::new(), + inscription_with_parent("text/plain", "child", parent_id).to_witness(), + ], + ..Default::default() + }); + + let child_id = InscriptionId { + txid: child_txid, + index: 0, + }; + + context.mine_blocks(1); + + // parent is transferred successfully + context.index.assert_inscription_location( + parent_id, + SatPoint { + outpoint: OutPoint { + txid: child_txid, + vout: 0, + }, + offset: 0, + }, + 50 * COIN_VALUE, + ); + + // child inscription successfully added to database + assert_eq!( + context.index.get_inscription_entry(child_id).unwrap(), + Some(InscriptionEntry { + fee: 0, + height: 3, + number: 1, + parent: Some(parent_id), + sat: None, + timestamp: 3 + }) + ); + } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 71d5901eeb..5a8c393ed6 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -1,6 +1,6 @@ use {super::*, std::collections::BTreeSet}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub(super) struct Flotsam { inscription_id: InscriptionId, offset: u64, @@ -8,7 +8,7 @@ pub(super) struct Flotsam { } // change name to Jetsam or more poetic german word -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum Origin { New((u64, Option)), Old(SatPoint), @@ -121,7 +121,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { floating_inscriptions.push(Flotsam { inscription_id: InscriptionId { txid, - index: input_value as u32, // TODO: is index a sat offset or and number of inscriptions offset + index: 0, }, offset: input_value, origin: Origin::New((0, parent)), @@ -147,6 +147,8 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } + // TODO: inefficient + // calulate genesis fee for new inscriptions let mut floating_inscriptions = floating_inscriptions .into_iter() .map(|flotsam| { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 905aa69484..01f202aeeb 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2009,7 +2009,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), + witnesses: vec![inscription("text/plain;charset=utf-8", "hello").to_witness()], ..Default::default() }); @@ -2030,7 +2030,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain;charset=utf-8", b"\xc3\x28").to_witness(), + witnesses: vec![inscription("text/plain;charset=utf-8", b"\xc3\x28").to_witness()], ..Default::default() }); @@ -2050,11 +2050,11 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription( + witnesses: vec![inscription( "text/plain;charset=utf-8", "", ) - .to_witness(), + .to_witness()], ..Default::default() }); @@ -2075,7 +2075,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("audio/flac", "hello").to_witness(), + witnesses: vec![inscription("audio/flac", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -2096,7 +2096,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("application/pdf", "hello").to_witness(), + witnesses: vec![inscription("application/pdf", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -2117,7 +2117,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("image/png", "hello").to_witness(), + witnesses: vec![inscription("image/png", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -2139,7 +2139,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/html;charset=utf-8", "hello").to_witness(), + witnesses: vec![inscription("text/html;charset=utf-8", "hello").to_witness()], ..Default::default() }); @@ -2160,7 +2160,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2181,7 +2181,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("video/webm", "hello").to_witness(), + witnesses: vec![inscription("video/webm", "hello").to_witness()], ..Default::default() }); let inscription_id = InscriptionId::from(txid); @@ -2202,7 +2202,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2222,7 +2222,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2242,7 +2242,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2274,7 +2274,7 @@ mod tests { server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2294,7 +2294,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: Inscription::new(None, Some("foo/bar".as_bytes().to_vec()), None).to_witness(), + witnesses: vec![Inscription::new(None, Some("foo/bar".as_bytes().to_vec()), None).to_witness()], ..Default::default() }); @@ -2316,7 +2316,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: Inscription::new(None, Some("image/png".as_bytes().to_vec()), None).to_witness(), + witnesses: vec![Inscription::new(None, Some("image/png".as_bytes().to_vec()), None).to_witness()], ..Default::default() }); @@ -2338,7 +2338,7 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); @@ -2370,7 +2370,7 @@ mod tests { server.mine_blocks(1); server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(i + 1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); } @@ -2392,7 +2392,7 @@ mod tests { server.mine_blocks(1); server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(i + 1, 0, 0)], - witness: inscription("text/foo", "hello").to_witness(), + witnesses: vec![inscription("text/foo", "hello").to_witness()], ..Default::default() }); } @@ -2456,7 +2456,7 @@ mod tests { bitcoin_rpc_server.mine_blocks(1); let txid = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witness: inscription("text/plain;charset=utf-8", "hello").to_witness(), + witnesses: vec![inscription("text/plain;charset=utf-8", "hello").to_witness()], ..Default::default() }); let inscription = InscriptionId::from(txid); diff --git a/src/test.rs b/src/test.rs index a374d1fad4..9ff0648be3 100644 --- a/src/test.rs +++ b/src/test.rs @@ -104,6 +104,10 @@ pub(crate) fn inscription(content_type: &str, body: impl AsRef<[u8]>) -> Inscrip Inscription::new(None, Some(content_type.into()), Some(body.as_ref().into())) } +pub(crate) fn inscription_with_parent(content_type: &str, body: impl AsRef<[u8]>, parent: InscriptionId) -> Inscription { + Inscription::new(Some(parent), Some(content_type.into()), Some(body.as_ref().into())) +} + pub(crate) fn inscription_id(n: u32) -> InscriptionId { let hex = format!("{n:x}"); diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 7051d283d6..d7db02d5a0 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -118,7 +118,7 @@ pub struct TransactionTemplate<'a> { pub inputs: &'a [(usize, usize, usize)], pub output_values: &'a [u64], pub outputs: usize, - pub witness: Witness, + pub witnesses: Vec, } #[derive(Clone, Debug, PartialEq)] @@ -150,7 +150,7 @@ impl<'a> Default for TransactionTemplate<'a> { inputs: &[], output_values: &[], outputs: 1, - witness: Witness::default(), + witnesses: vec![], } } } diff --git a/test-bitcoincore-rpc/src/state.rs b/test-bitcoincore-rpc/src/state.rs index 80f887c003..26f049cc2b 100644 --- a/test-bitcoincore-rpc/src/state.rs +++ b/test-bitcoincore-rpc/src/state.rs @@ -138,11 +138,7 @@ impl State { previous_output: OutPoint::new(tx.txid(), *vout as u32), script_sig: Script::new(), sequence: Sequence::MAX, - witness: if i == 0 { - template.witness.clone() - } else { - Witness::new() - }, + witness: template.witnesses.get(i).map_or(Witness::new(), |i| i.clone()), }); } From 1f33ff782dd20cee8c5f10826e126eb6974ad35d Mon Sep 17 00:00:00 2001 From: ordinally Date: Fri, 10 Mar 2023 20:33:59 +0100 Subject: [PATCH 19/34] Encode schnorr signature with proper sighashtype when inscribing with parent Thanks @ericatallah ! https://github.com/raphjaph/ord/pull/2/commits/c46d003e993f8a41c5aa1ea9e227f9c2a2cd4dcf --- src/subcommand/wallet/inscribe.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 65db2d389d..62f126e650 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,4 +1,4 @@ -use bitcoin::consensus::serialize; +use bitcoin::{consensus::serialize, SchnorrSig}; use { super::*, @@ -165,7 +165,7 @@ impl Inscribe { .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? .hex; // TODO: there is a bug here, the fully signed reveal TX no longer contains - // the inscription data + // the inscription data when backup key is in bitcoin core wallet log::debug!( "fully signed reveal tx: {}", hex::encode(serialize(&fully_signed_raw_reveal_tx)) @@ -189,10 +189,6 @@ impl Inscribe { })?; }; - // if self.parent.is_some() { - // println!("{}", partially_signed_reveal_tx.raw_hex()); - // } - Ok(()) } @@ -358,7 +354,7 @@ impl Inscribe { let mut sighash_cache = SighashCache::new(&mut reveal_tx); - let signature_hash = if let Some(_parent) = parent { + let signature_hash = if parent.is_some() { sighash_cache.taproot_script_spend_signature_hash( commit_input_offset, &Prevouts::One(commit_input_offset, output), @@ -384,7 +380,15 @@ impl Inscribe { let witness = sighash_cache .witness_mut(commit_input_offset) .expect("getting mutable witness reference should work"); - witness.push(signature.as_ref()); + if parent.is_some() { + let encoded_sig = SchnorrSig { + sig: signature, + hash_ty: SchnorrSighashType::AllPlusAnyoneCanPay, + }; + witness.push(encoded_sig.to_vec()); + } else { + witness.push(signature.as_ref()); + } witness.push(reveal_script); witness.push(&control_block.serialize()); From 5f82a48bfd8f657d045738dc2f7cf6d904743753 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 10 Mar 2023 12:54:02 -0800 Subject: [PATCH 20/34] stash --- src/subcommand/wallet/inscribe.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 65db2d389d..909732693e 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -160,6 +160,9 @@ impl Inscribe { "partially signed reveal tx: {}", hex::encode(serialize(&partially_signed_reveal_tx)) ); + + // TODO: get Bitcoin Core to attach reveal witness + // after signing replace witness with correct one let reveal = if self.parent.is_some() { let fully_signed_raw_reveal_tx = client .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? From edd0a76ca941ed5a85b80a52274c79584081cef9 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 10 Mar 2023 14:54:34 -0800 Subject: [PATCH 21/34] refactoring --- src/subcommand/wallet/inscribe.rs | 42 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index f321fcd7d6..5880685f75 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -355,27 +355,31 @@ impl Inscribe { bail!("commit transaction output would be dust"); } - let mut sighash_cache = SighashCache::new(&mut reveal_tx); + // NB. This binding is to avoid borrow-checker problems + let prevouts_all_inputs = &[output]; - let signature_hash = if parent.is_some() { - sighash_cache.taproot_script_spend_signature_hash( - commit_input_offset, - &Prevouts::One(commit_input_offset, output), - TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), + let (prevouts, hash_type) = if parent.is_some() { + ( + Prevouts::One(commit_input_offset, output), SchnorrSighashType::AllPlusAnyoneCanPay, ) } else { - sighash_cache.taproot_script_spend_signature_hash( + (Prevouts::All(prevouts_all_inputs), SchnorrSighashType::Default) + }; + + let mut sighash_cache = SighashCache::new(&mut reveal_tx); + + let message = sighash_cache + .taproot_script_spend_signature_hash( commit_input_offset, - &Prevouts::All(&[output]), + &prevouts, TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), - SchnorrSighashType::Default, + hash_type, ) - } - .expect("signature hash should compute"); + .expect("signature hash should compute"); - let signature = secp256k1.sign_schnorr( - &secp256k1::Message::from_slice(signature_hash.as_inner()) + let sig = secp256k1.sign_schnorr( + &secp256k1::Message::from_slice(message.as_inner()) .expect("should be cryptographically secure hash"), &key_pair, ); @@ -383,15 +387,9 @@ impl Inscribe { let witness = sighash_cache .witness_mut(commit_input_offset) .expect("getting mutable witness reference should work"); - if parent.is_some() { - let encoded_sig = SchnorrSig { - sig: signature, - hash_ty: SchnorrSighashType::AllPlusAnyoneCanPay, - }; - witness.push(encoded_sig.to_vec()); - } else { - witness.push(signature.as_ref()); - } + + witness.push(SchnorrSig { sig, hash_ty: hash_type }.to_vec()); + witness.push(reveal_script); witness.push(&control_block.serialize()); From 044d6dec14e90d4a40d16bf3f7355d2bfc799848 Mon Sep 17 00:00:00 2001 From: ordinally <11798624+veryordinally@users.noreply.github.com> Date: Fri, 10 Mar 2023 23:25:52 -0800 Subject: [PATCH 22/34] Update src/inscription.rs Ignore malformed inscriptions. Co-authored-by: Clarke Benedict --- src/inscription.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/inscription.rs b/src/inscription.rs index 2bf23a69a0..f5a08b986d 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -263,15 +263,9 @@ impl<'a> InscriptionParser<'a> { return Ok(Some(Inscription { body, content_type, - parent: match parent { - None => None, - Some(bytes) => { - if bytes.len() != 36 { - return Err(InscriptionError::InvalidInscription) - } - Some(InscriptionId::load(bytes.as_slice().try_into().unwrap())) - } - }, + parent: parent.and_then(|parent| { + Some(InscriptionId::load(parent.as_slice().try_into().ok()?)) + }), })); } From aaaffb6aa124de766ac2370e8b843e82c46c9649 Mon Sep 17 00:00:00 2001 From: ordinally Date: Sat, 11 Mar 2023 13:03:05 +0100 Subject: [PATCH 23/34] Fixed formatting --- src/index/updater/inscription_updater.rs | 5 +---- src/inscription.rs | 5 ++--- src/subcommand/server.rs | 8 ++++++-- src/subcommand/wallet/c3.json | 7 +++++++ src/subcommand/wallet/inscribe.rs | 13 +++++++++++-- src/test.rs | 12 ++++++++++-- test-bitcoincore-rpc/src/state.rs | 5 ++++- 7 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 src/subcommand/wallet/c3.json diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 5a8c393ed6..d92cc080e1 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -119,10 +119,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { }; floating_inscriptions.push(Flotsam { - inscription_id: InscriptionId { - txid, - index: 0, - }, + inscription_id: InscriptionId { txid, index: 0 }, offset: input_value, origin: Origin::New((0, parent)), }); diff --git a/src/inscription.rs b/src/inscription.rs index f5a08b986d..5169194db8 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -263,9 +263,8 @@ impl<'a> InscriptionParser<'a> { return Ok(Some(Inscription { body, content_type, - parent: parent.and_then(|parent| { - Some(InscriptionId::load(parent.as_slice().try_into().ok()?)) - }), + parent: parent + .and_then(|parent| Some(InscriptionId::load(parent.as_slice().try_into().ok()?))), })); } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 01f202aeeb..4990451952 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2294,7 +2294,9 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witnesses: vec![Inscription::new(None, Some("foo/bar".as_bytes().to_vec()), None).to_witness()], + witnesses: vec![ + Inscription::new(None, Some("foo/bar".as_bytes().to_vec()), None).to_witness(), + ], ..Default::default() }); @@ -2316,7 +2318,9 @@ mod tests { let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(1, 0, 0)], - witnesses: vec![Inscription::new(None, Some("image/png".as_bytes().to_vec()), None).to_witness()], + witnesses: vec![ + Inscription::new(None, Some("image/png".as_bytes().to_vec()), None).to_witness(), + ], ..Default::default() }); diff --git a/src/subcommand/wallet/c3.json b/src/subcommand/wallet/c3.json new file mode 100644 index 0000000000..127130b9a9 --- /dev/null +++ b/src/subcommand/wallet/c3.json @@ -0,0 +1,7 @@ +{ + "commit": "9664939b7b134207e6caf9c9afa050cf570feb9f31b488cb4e20aa599613e783", + "inscription": "e6f437e2090e3a0fa79490dccce7f15e308920b38240d8d276a220e76c0f57c2i0", + "parent": "6fbec3a2ee5bb8adc56522bea8d4efd7adc64a46da5491ca4eb1e59da0d555efi0", + "reveal": "e6f437e2090e3a0fa79490dccce7f15e308920b38240d8d276a220e76c0f57c2", + "fees": 497 +} \ No newline at end of file diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 5880685f75..19c950d7f9 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -364,7 +364,10 @@ impl Inscribe { SchnorrSighashType::AllPlusAnyoneCanPay, ) } else { - (Prevouts::All(prevouts_all_inputs), SchnorrSighashType::Default) + ( + Prevouts::All(prevouts_all_inputs), + SchnorrSighashType::Default, + ) }; let mut sighash_cache = SighashCache::new(&mut reveal_tx); @@ -388,7 +391,13 @@ impl Inscribe { .witness_mut(commit_input_offset) .expect("getting mutable witness reference should work"); - witness.push(SchnorrSig { sig, hash_ty: hash_type }.to_vec()); + witness.push( + SchnorrSig { + sig, + hash_ty: hash_type, + } + .to_vec(), + ); witness.push(reveal_script); witness.push(&control_block.serialize()); diff --git a/src/test.rs b/src/test.rs index 9ff0648be3..59df4a06fb 100644 --- a/src/test.rs +++ b/src/test.rs @@ -104,8 +104,16 @@ pub(crate) fn inscription(content_type: &str, body: impl AsRef<[u8]>) -> Inscrip Inscription::new(None, Some(content_type.into()), Some(body.as_ref().into())) } -pub(crate) fn inscription_with_parent(content_type: &str, body: impl AsRef<[u8]>, parent: InscriptionId) -> Inscription { - Inscription::new(Some(parent), Some(content_type.into()), Some(body.as_ref().into())) +pub(crate) fn inscription_with_parent( + content_type: &str, + body: impl AsRef<[u8]>, + parent: InscriptionId, +) -> Inscription { + Inscription::new( + Some(parent), + Some(content_type.into()), + Some(body.as_ref().into()), + ) } pub(crate) fn inscription_id(n: u32) -> InscriptionId { diff --git a/test-bitcoincore-rpc/src/state.rs b/test-bitcoincore-rpc/src/state.rs index 26f049cc2b..08ea2a09c4 100644 --- a/test-bitcoincore-rpc/src/state.rs +++ b/test-bitcoincore-rpc/src/state.rs @@ -138,7 +138,10 @@ impl State { previous_output: OutPoint::new(tx.txid(), *vout as u32), script_sig: Script::new(), sequence: Sequence::MAX, - witness: template.witnesses.get(i).map_or(Witness::new(), |i| i.clone()), + witness: template + .witnesses + .get(i) + .map_or(Witness::new(), |i| i.clone()), }); } From 3ef11d016408e005e6e9d592fced93784634d3c4 Mon Sep 17 00:00:00 2001 From: ordinally Date: Sat, 11 Mar 2023 14:45:01 +0100 Subject: [PATCH 24/34] Handle child inscription index correctly. Indirect pull from https://github.com/raphjaph/ord/pull/3/commits Props to @ericatallah for contributing this --- src/index.rs | 2 +- src/index/updater/inscription_updater.rs | 19 ++++++++++--------- src/subcommand/wallet/inscribe.rs | 7 ++++++- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/index.rs b/src/index.rs index f9b3927601..bfbb0f95af 100644 --- a/src/index.rs +++ b/src/index.rs @@ -552,7 +552,7 @@ impl Index { Ok( self .get_transaction(inscription_id.txid)? - .and_then(|tx| Inscription::from_transaction(&tx)), + .and_then(|tx| Inscription::from_tx_input(tx.input.get(inscription_id.index as usize)?)), ) } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index d92cc080e1..efe2891668 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -103,23 +103,26 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { if let Some(inscription) = Inscription::from_tx_input(tx_in) { // ignore new inscriptions on already inscribed offset (sats) if !inscribed_offsets.contains(&input_value) { - let parent = if let Some(parent_id) = inscription.get_parent_id() { + let (parent, input_index) = if let Some(parent_id) = inscription.get_parent_id() { // parent has to be in an input before child // think about specifying a more general approach in a protocol doc/BIP if floating_inscriptions .iter() .any(|flotsam| flotsam.inscription_id == parent_id) { - Some(parent_id) + (Some(parent_id), 1) } else { - None + (None, 0) } } else { - None + (None, 0) }; floating_inscriptions.push(Flotsam { - inscription_id: InscriptionId { txid, index: 0 }, + inscription_id: InscriptionId { + txid, + index: input_index, + }, offset: input_value, origin: Origin::New((0, parent)), }); @@ -146,6 +149,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { // TODO: inefficient // calulate genesis fee for new inscriptions + let total_output_value = tx.output.iter().map(|txout| txout.value).sum::(); let mut floating_inscriptions = floating_inscriptions .into_iter() .map(|flotsam| { @@ -158,10 +162,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Flotsam { inscription_id, offset, - origin: Origin::New(( - input_value - tx.output.iter().map(|txout| txout.value).sum::(), - parent, - )), + origin: Origin::New((input_value - total_output_value, parent)), } } else { flotsam diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 19c950d7f9..5abb0ea104 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -183,10 +183,15 @@ impl Inscribe { .context("Failed to send reveal transaction")? }; + let inscription = InscriptionId { + txid: reveal, + index: commit_input_offset as u32, + }; + print_json(Output { commit, reveal, - inscription: reveal.into(), + inscription, parent: self.parent, fees, })?; From cb7573bda7cf4c0549271c6d5b7d7c7e99f68d3d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 12 Mar 2023 17:41:07 -0700 Subject: [PATCH 25/34] rename --- src/subcommand/wallet/inscribe.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 5880685f75..3fb8e0bb39 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -358,7 +358,7 @@ impl Inscribe { // NB. This binding is to avoid borrow-checker problems let prevouts_all_inputs = &[output]; - let (prevouts, hash_type) = if parent.is_some() { + let (prevouts, hash_ty) = if parent.is_some() { ( Prevouts::One(commit_input_offset, output), SchnorrSighashType::AllPlusAnyoneCanPay, @@ -374,7 +374,7 @@ impl Inscribe { commit_input_offset, &prevouts, TapLeafHash::from_script(&reveal_script, LeafVersion::TapScript), - hash_type, + hash_ty, ) .expect("signature hash should compute"); @@ -388,7 +388,7 @@ impl Inscribe { .witness_mut(commit_input_offset) .expect("getting mutable witness reference should work"); - witness.push(SchnorrSig { sig, hash_ty: hash_type }.to_vec()); + witness.push(SchnorrSig { sig, hash_ty }.to_vec()); witness.push(reveal_script); witness.push(&control_block.serialize()); From b05c15f1606e240f453df8d7c87cca5a0f01ab38 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 12 Mar 2023 22:56:58 -0700 Subject: [PATCH 26/34] fix inscriptionId index and remove unused file --- src/index.rs | 3 ++- src/index/updater/inscription_updater.rs | 10 +++++----- src/subcommand/wallet/c3.json | 7 ------- 3 files changed, 7 insertions(+), 13 deletions(-) delete mode 100644 src/subcommand/wallet/c3.json diff --git a/src/index.rs b/src/index.rs index bfbb0f95af..a948056997 100644 --- a/src/index.rs +++ b/src/index.rs @@ -552,7 +552,8 @@ impl Index { Ok( self .get_transaction(inscription_id.txid)? - .and_then(|tx| Inscription::from_tx_input(tx.input.get(inscription_id.index as usize)?)), + // .and_then(|tx| Inscription::from_tx_input(tx.input.get(inscription_id.index as usize)?)), + .and_then(|tx| Inscription::from_transaction(&tx)), ) } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index efe2891668..6b232d8b88 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -103,25 +103,25 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { if let Some(inscription) = Inscription::from_tx_input(tx_in) { // ignore new inscriptions on already inscribed offset (sats) if !inscribed_offsets.contains(&input_value) { - let (parent, input_index) = if let Some(parent_id) = inscription.get_parent_id() { + let parent = if let Some(parent_id) = inscription.get_parent_id() { // parent has to be in an input before child // think about specifying a more general approach in a protocol doc/BIP if floating_inscriptions .iter() .any(|flotsam| flotsam.inscription_id == parent_id) { - (Some(parent_id), 1) + Some(parent_id) } else { - (None, 0) + None } } else { - (None, 0) + None }; floating_inscriptions.push(Flotsam { inscription_id: InscriptionId { txid, - index: input_index, + index: 0, }, offset: input_value, origin: Origin::New((0, parent)), diff --git a/src/subcommand/wallet/c3.json b/src/subcommand/wallet/c3.json deleted file mode 100644 index 127130b9a9..0000000000 --- a/src/subcommand/wallet/c3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "commit": "9664939b7b134207e6caf9c9afa050cf570feb9f31b488cb4e20aa599613e783", - "inscription": "e6f437e2090e3a0fa79490dccce7f15e308920b38240d8d276a220e76c0f57c2i0", - "parent": "6fbec3a2ee5bb8adc56522bea8d4efd7adc64a46da5491ca4eb1e59da0d555efi0", - "reveal": "e6f437e2090e3a0fa79490dccce7f15e308920b38240d8d276a220e76c0f57c2", - "fees": 497 -} \ No newline at end of file From fbc4cdec56393136cd9995b71972a70537b529b7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 22 Mar 2023 13:41:11 -0700 Subject: [PATCH 27/34] finally --- src/index.rs | 3 +- src/index/updater/inscription_updater.rs | 12 +- src/inscription.rs | 8 +- src/subcommand/server.rs | 5 +- src/subcommand/wallet/inscribe.rs | 79 ++++---- tests/core.rs | 222 ++++++++++++++++++++++- tests/wallet/inscribe.rs | 3 +- 7 files changed, 276 insertions(+), 56 deletions(-) diff --git a/src/index.rs b/src/index.rs index a948056997..e2eed7772c 100644 --- a/src/index.rs +++ b/src/index.rs @@ -548,11 +548,10 @@ impl Index { { return Ok(None); } - + Ok( self .get_transaction(inscription_id.txid)? - // .and_then(|tx| Inscription::from_tx_input(tx.input.get(inscription_id.index as usize)?)), .and_then(|tx| Inscription::from_transaction(&tx)), ) } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 6b232d8b88..9d458277ee 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -117,11 +117,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } else { None }; - + floating_inscriptions.push(Flotsam { inscription_id: InscriptionId { txid, - index: 0, + index: 0, // will have to be updated for multi inscriptions }, offset: input_value, origin: Origin::New((0, parent)), @@ -203,7 +203,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { self.update_inscription_location( input_sat_ranges, // TODO: do something with two inscriptions in the input - inscriptions.next().unwrap(), + dbg!(inscriptions.next().unwrap()), new_satpoint, )?; } @@ -271,9 +271,11 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } + dbg!(&parent); + self.id_to_entry.insert( &inscription_id, - &InscriptionEntry { + &dbg!(InscriptionEntry { fee, height: self.height, number: self.next_number, @@ -281,7 +283,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { sat, timestamp: self.timestamp, } - .store(), + .store()), )?; self.next_number += 1; diff --git a/src/inscription.rs b/src/inscription.rs index 5169194db8..28fc7377be 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -40,7 +40,13 @@ impl Inscription { } pub(crate) fn from_transaction(tx: &Transaction) -> Option { - InscriptionParser::parse(&tx.input.get(0)?.witness).ok() + for input in &tx.input { + if let Some(inscription) = Inscription::from_tx_input(input) { + return Some(inscription); + } + } + + None } pub(crate) fn from_tx_input(tx_in: &TxIn) -> Option { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 4990451952..51d05e8295 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -821,6 +821,9 @@ impl Server { .get_inscription_entry(inscription_id)? .ok_or_not_found(|| format!("inscription {inscription_id}"))?; + dbg!(&inscription_id); + dbg!(&entry); + let inscription = index .get_inscription_by_id(inscription_id)? .ok_or_not_found(|| format!("inscription {inscription_id}"))?; @@ -859,7 +862,7 @@ impl Server { next, number: entry.number, output, - parent: dbg!(entry.parent), + parent: entry.parent, previous, sat: entry.sat, satpoint, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 8dafcbba1b..40518c5f10 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -143,60 +143,49 @@ impl Inscribe { parent: self.parent, fees, })?; - } else { - 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)? - .hex; + return Ok(()); + } - let commit = client - .send_raw_transaction(&signed_raw_commit_tx) - .context("Failed to send commit transaction")?; + // if !self.no_backup { + // Inscribe::backup_recovery_key(&client, recovery_key_pair, options.chain().network())?; + // } - log::debug!( - "partially signed reveal tx: {}", - hex::encode(serialize(&partially_signed_reveal_tx)) - ); + let signed_raw_commit_tx = client + .sign_raw_transaction_with_wallet(&unsigned_commit_tx, None, None)? + .hex; - // TODO: get Bitcoin Core to attach reveal witness - // after signing replace witness with correct one - let reveal = if self.parent.is_some() { - let fully_signed_raw_reveal_tx = client - .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? - .hex; - // TODO: there is a bug here, the fully signed reveal TX no longer contains - // the inscription data when backup key is in bitcoin core wallet - log::debug!( - "fully signed reveal tx: {}", - hex::encode(serialize(&fully_signed_raw_reveal_tx)) - ); + let commit = client + .send_raw_transaction(&signed_raw_commit_tx) + .context("Failed to send commit transaction")?; - client - .send_raw_transaction(&fully_signed_raw_reveal_tx) - .context("Failed to send reveal transaction")? - } else { - client - .send_raw_transaction(&partially_signed_reveal_tx) - .context("Failed to send reveal transaction")? - }; + let reveal = if self.parent.is_some() { + let fully_signed_raw_reveal_tx = client + .sign_raw_transaction_with_wallet(&partially_signed_reveal_tx, None, None)? + .hex; - let inscription = InscriptionId { - txid: reveal, - index: commit_input_offset as u32, - }; + client + .send_raw_transaction(&fully_signed_raw_reveal_tx) + .context("Failed to send reveal transaction")? + } else { + client + .send_raw_transaction(&partially_signed_reveal_tx) + .context("Failed to send reveal transaction")? + }; - print_json(Output { - commit, - reveal, - inscription, - parent: self.parent, - fees, - })?; + let inscription = InscriptionId { + txid: reveal, + index: 0, }; + print_json(Output { + commit, + reveal, + inscription, + parent: self.parent, + fees, + })?; + Ok(()) } diff --git a/tests/core.rs b/tests/core.rs index 8e4c951d38..effc8dd310 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -1,4 +1,8 @@ -use super::*; +use { + super::*, + bitcoincore_rpc::{Client, RpcApi}, + std::ffi::OsString, +}; struct KillOnDrop(std::process::Child); @@ -70,3 +74,219 @@ fn preview() { format!(".*( u16 { + TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap() + .port() +} + +fn ord( + cookiefile: &std::path::PathBuf, + ord_data_dir: &std::path::PathBuf, + rpc_port: u16, + args: &[&str], +) -> Result { + let mut ord = Command::new(executable_path("ord")); + + ord + .env("ORD_INTEGRATION_TEST", "1") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .current_dir(&ord_data_dir) + .arg("--regtest") + .arg("--data-dir") + .arg(ord_data_dir.as_path()) + .arg("--rpc-url") + .arg(&format!("127.0.0.1:{}", rpc_port)) + .arg("--cookie-file") + .arg(cookiefile.to_str().unwrap()) + .args(args); + + let output = ord.output().unwrap(); + + if output.status.success() { + Ok(String::from(str::from_utf8(&output.stdout).unwrap())) + } else { + Err(String::from(str::from_utf8(&output.stderr).unwrap())) + } +} + +#[test] +#[ignore] +fn inscribe_child() { + let rpc_port = get_free_port(); + + let tmp_dir_1 = TempDir::new().unwrap(); + let bitcoin_data_dir = tmp_dir_1.path().join("bitcoin"); + fs::create_dir(&bitcoin_data_dir).unwrap(); + + let tmp_dir_2 = TempDir::new().unwrap(); + let ord_data_dir = tmp_dir_2.path().join("ord"); + fs::create_dir(&ord_data_dir).unwrap(); + + let _bitcoind = KillOnDrop( + Command::new("bitcoind") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .arg({ + let mut arg = OsString::from("-datadir="); + arg.push(&bitcoin_data_dir); + arg + }) + .arg("-regtest") + .arg("-txindex") + .arg("-listen=0") + .arg("-minrelaytxfee=0") + .arg(format!("-rpcport={rpc_port}")) + .spawn() + .expect("failed to spawn `bitcoind`"), + ); + + let cookiefile = bitcoin_data_dir.as_path().join("regtest/.cookie"); + + for attempt in 0.. { + match Client::new( + &format!("127.0.0.1:{rpc_port}"), + bitcoincore_rpc::Auth::CookieFile(cookiefile.clone()), + ) { + Ok(_) => break, + _ => (), + } + + if attempt == 500 { + panic!("Bitcoin Core RPC did not respond"); + } + + thread::sleep(Duration::from_millis(50)); + } + + let _ = ord(&cookiefile, &ord_data_dir, rpc_port, &["wallet", "create"]); + + // get funds in wallet + // inscribe parent + // mine block + // inscribe child with parent + + let rpc_client = Client::new( + &format!("127.0.0.1:{rpc_port}/wallet/ord"), + bitcoincore_rpc::Auth::CookieFile(cookiefile.clone()), + ) + .unwrap(); + + let address = rpc_client + .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m)) + .unwrap(); + + rpc_client.generate_to_address(101, &address).unwrap(); + + fs::write(ord_data_dir.as_path().join("parent.txt"), "Pater").unwrap(); + + #[derive(Deserialize, Debug)] + struct Output { + commit: String, + inscription: String, + parent: Option, + reveal: String, + fees: u64, + } + + let output: Output = match ord( + &cookiefile, + &ord_data_dir, + rpc_port, + &["wallet", "inscribe", "parent.txt"], + ) { + Ok(s) => serde_json::from_str(&s) + .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), + Err(e) => panic!("error inscribing parent: {}", e), + }; + let parent_id = output.inscription; + + rpc_client.generate_to_address(1, &address).unwrap(); + + fs::write(ord_data_dir.as_path().join("child.txt"), "Filius").unwrap(); + let output: Output = match ord( + &cookiefile, + &ord_data_dir, + rpc_port, + &[ + "wallet", + "inscribe", + "--parent", + &parent_id, + "child.txt", + ], + ) { + Ok(s) => serde_json::from_str(&s) + .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), + Err(e) => panic!("error inscribing child with parent: {}", e), + }; + + let child_id = output.inscription; + let ord_port = 8080; + + rpc_client.generate_to_address(1, &address).unwrap(); + + let _ord_server = KillOnDrop( + Command::new(executable_path("ord")) + .env("ORD_INTEGRATION_TEST", "1") + .stdin(Stdio::null()) + // .stdout(Stdio::piped()) + // .stderr(Stdio::piped()) + .current_dir(&ord_data_dir) + .arg("--regtest") + .arg("--data-dir") + .arg(ord_data_dir.as_path()) + .arg("--rpc-url") + .arg(&format!("127.0.0.1:{}", rpc_port)) + .arg("--cookie-file") + .arg(cookiefile.to_str().unwrap()) + .arg("server") + .arg("--http-port") + .arg(&format!("{ord_port}")) + .spawn() + .expect("failed to spawn `ord server`"), + ); + + let client = reqwest::blocking::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap(); + + for i in 0.. { + match client + .get(format!("http://127.0.0.1:{ord_port}/status")) + .send() + { + Ok(_) => break, + Err(err) => { + if i == 400 { + panic!("server failed to start: {err}"); + } + } + } + + thread::sleep(Duration::from_millis(25)); + } + + let response = client + .get(format!("http://127.0.0.1:{ord_port}/inscription/{parent_id}")) + .send() + .unwrap(); + + assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); + + thread::sleep(Duration::from_secs(10)); + + let response = client + .get(format!("http://127.0.0.1:{ord_port}/inscription/{child_id}")) + .send() + .unwrap(); + + assert_regex_match!(response.text().unwrap(), &format!(".*parent.*{}.*", parent_id)); +} diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 0b40f21ab1..78d518d29b 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -11,7 +11,8 @@ fn inscribe_creates_inscriptions() { let Inscribe { inscription, .. } = inscribe(&rpc_server); - assert_eq!(rpc_server.descriptors().len(), 3); + // no backup + assert_eq!(rpc_server.descriptors().len(), 2); let request = TestServer::spawn_with_args(&rpc_server, &[]).request(format!("/content/{inscription}")); From 0d362e4083cad30f9a17ce3aad88cccd0f4c871f Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 22 Mar 2023 14:12:02 -0700 Subject: [PATCH 28/34] cleanup and works --- fuzz/Cargo.lock | 197 +++++++++++++++++-- src/index.rs | 1 - src/index/updater/inscription_updater.rs | 28 +-- src/inscription.rs | 1 + src/subcommand/server.rs | 7 +- src/subcommand/wallet/inscribe.rs | 6 +- src/subcommand/wallet/transaction_builder.rs | 1 - tests/core.rs | 56 +++--- tests/test_server.rs | 1 - tests/wallet/inscribe.rs | 10 +- 10 files changed, 233 insertions(+), 75 deletions(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index f30058f2a7..87e3510802 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -80,6 +80,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -163,6 +178,20 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-dup" version = "1.2.2" @@ -380,6 +409,7 @@ dependencies = [ "bitflags", "bytes", "futures-util", + "headers", "http", "http-body", "hyper", @@ -569,12 +599,39 @@ dependencies = [ "syn", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.3.0" @@ -741,6 +798,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -1082,6 +1148,16 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1316,6 +1392,31 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64 0.13.1", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1 0.10.5", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "heck" version = "0.4.0" @@ -1442,9 +1543,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -1731,6 +1832,20 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mp4" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509348cba250e7b852a875100a2ddce7a36ee3abf881a681c756670c1774264d" +dependencies = [ + "byteorder", + "bytes", + "num-rational", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "never" version = "0.1.0" @@ -1790,6 +1905,19 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1847,11 +1975,12 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "ord" -version = "0.4.0" +version = "0.5.1" dependencies = [ "anyhow", "axum", "axum-server", + "base64 0.13.1", "bech32", "bip39", "bitcoin", @@ -1866,12 +1995,14 @@ dependencies = [ "hex", "html-escaper", "http", + "hyper", "indicatif", "lazy_static", "log", "mime", "mime_guess", "miniscript", + "mp4", "ord-bitcoincore-rpc", "pulldown-cmark", "redb", @@ -1882,6 +2013,7 @@ dependencies = [ "rustls-acme", "serde", "serde_json", + "serde_yaml", "sys-info", "tempfile", "tokio", @@ -1892,8 +2024,9 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc" -version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#1bc2638f5a3049495a7aa953c4c6dac44fd1524b" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca77a54d5a14a2731dd85150e24be89c849bdcb5704d23097188d763381d98b8" dependencies = [ "jsonrpc", "log", @@ -1904,8 +2037,9 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc-json" -version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#1bc2638f5a3049495a7aa953c4c6dac44fd1524b" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b81ced8ed99f50d3f221a7de8297cbdf9bdf20ca2908a12f6128d38b2284fb" dependencies = [ "bitcoin", "serde", @@ -2061,9 +2195,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.17.3" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28fcd1e73f06ec85bf3280c48c67e731d8290ad3d730f8be9dc07946923005c8" +checksum = "75439f995d07ddfad42b192dfcf3bc66a7ecfd8b4a1f5f6f046aa5c2c5d7677d" dependencies = [ "once_cell", "target-lexicon", @@ -2179,8 +2313,9 @@ dependencies = [ [[package]] name = "redb" -version = "0.11.0" -source = "git+https://github.com/casey/redb.git?branch=ord#826fd633b2e26bb649cf7004406cb469fbb28837" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f210bb101d3a0ddba42f67b12a1d7186e584733ad028f119c8d217d867f03d" dependencies = [ "libc", "pyo3-build-config", @@ -2535,6 +2670,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.6.1" @@ -2544,6 +2692,17 @@ dependencies = [ "sha1_smol", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha1_smol" version = "1.0.0" @@ -2707,7 +2866,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha1", + "sha1 0.6.1", "syn", ] @@ -2987,6 +3146,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ + "async-compression", "bitflags", "bytes", "futures-core", @@ -2995,6 +3155,8 @@ dependencies = [ "http-body", "http-range-header", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -3097,6 +3259,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" + [[package]] name = "untrusted" version = "0.7.1" @@ -3404,3 +3572,8 @@ checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ "time 0.3.17", ] + +[[patch.unused]] +name = "redb" +version = "0.11.0" +source = "git+https://github.com/casey/redb.git?branch=ord#826fd633b2e26bb649cf7004406cb469fbb28837" diff --git a/src/index.rs b/src/index.rs index e2eed7772c..be0306983d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -568,7 +568,6 @@ impl Index { .open_table(SATPOINT_TO_INSCRIPTION_ID)?, outpoint, )? - .into_iter() .map(|(_satpoint, inscription_id)| inscription_id) .collect(), ) diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 9d458277ee..9d8a6216cc 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -103,21 +103,14 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { if let Some(inscription) = Inscription::from_tx_input(tx_in) { // ignore new inscriptions on already inscribed offset (sats) if !inscribed_offsets.contains(&input_value) { - let parent = if let Some(parent_id) = inscription.get_parent_id() { - // parent has to be in an input before child - // think about specifying a more general approach in a protocol doc/BIP - if floating_inscriptions + // parent has to be in an input before child + // think about specifying a more general approach in a protocol doc/BIP + let parent = inscription.get_parent_id().filter(|&parent_id| { + floating_inscriptions .iter() .any(|flotsam| flotsam.inscription_id == parent_id) - { - Some(parent_id) - } else { - None - } - } else { - None - }; - + }); + floating_inscriptions.push(Flotsam { inscription_id: InscriptionId { txid, @@ -202,8 +195,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { self.update_inscription_location( input_sat_ranges, - // TODO: do something with two inscriptions in the input - dbg!(inscriptions.next().unwrap()), + inscriptions.next().unwrap(), // TODO: do something with two inscriptions in the input new_satpoint, )?; } @@ -271,11 +263,9 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { } } - dbg!(&parent); - self.id_to_entry.insert( &inscription_id, - &dbg!(InscriptionEntry { + &InscriptionEntry { fee, height: self.height, number: self.next_number, @@ -283,7 +273,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { sat, timestamp: self.timestamp, } - .store()), + .store(), )?; self.next_number += 1; diff --git a/src/inscription.rs b/src/inscription.rs index 28fc7377be..037cc24dca 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -618,6 +618,7 @@ mod tests { } #[test] + #[ignore] // we need to do this now for parent-child relationships fn do_not_extract_from_second_input() { let tx = Transaction { version: 0, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 51d05e8295..684862afc5 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -768,7 +768,7 @@ impl Server { .get_inscription_by_id(inscription_id)? .ok_or_not_found(|| format!("inscription {inscription_id}"))?; - return match inscription.media() { + match inscription.media() { Media::Audio => Ok(PreviewAudioHtml { inscription_id }.into_response()), Media::Iframe => Ok( Self::content_response(inscription) @@ -809,7 +809,7 @@ impl Server { } Media::Unknown => Ok(PreviewUnknownHtml.into_response()), Media::Video => Ok(PreviewVideoHtml { inscription_id }.into_response()), - }; + } } async fn inscription( @@ -821,9 +821,6 @@ impl Server { .get_inscription_entry(inscription_id)? .ok_or_not_found(|| format!("inscription {inscription_id}"))?; - dbg!(&inscription_id); - dbg!(&entry); - let inscription = index .get_inscription_by_id(inscription_id)? .ok_or_not_found(|| format!("inscription {inscription_id}"))?; diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 40518c5f10..14c1c8011e 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,4 +1,4 @@ -use bitcoin::{consensus::serialize, SchnorrSig}; +use bitcoin::{SchnorrSig}; use { super::*, @@ -107,7 +107,7 @@ impl Inscribe { .map(Ok) .unwrap_or_else(|| get_change_address(&client))?; - let (unsigned_commit_tx, partially_signed_reveal_tx, recovery_key_pair) = + let (unsigned_commit_tx, partially_signed_reveal_tx, _recovery_key_pair) = Inscribe::create_inscription_transactions( self.satpoint, parent, @@ -412,7 +412,7 @@ impl Inscribe { Ok((unsigned_commit_tx, reveal_tx, recovery_key_pair)) } - fn backup_recovery_key( + fn _backup_recovery_key( client: &Client, recovery_key_pair: TweakedKeyPair, network: Network, diff --git a/src/subcommand/wallet/transaction_builder.rs b/src/subcommand/wallet/transaction_builder.rs index aed340be39..11f73d4f65 100644 --- a/src/subcommand/wallet/transaction_builder.rs +++ b/src/subcommand/wallet/transaction_builder.rs @@ -427,7 +427,6 @@ impl TransactionBuilder { version: 1, lock_time: PackedLockTime::ZERO, input: (0..inputs) - .into_iter() .map(|_| TxIn { previous_output: OutPoint::null(), script_sig: Script::new(), diff --git a/tests/core.rs b/tests/core.rs index effc8dd310..5c99cf82ab 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -84,8 +84,8 @@ fn get_free_port() -> u16 { } fn ord( - cookiefile: &std::path::PathBuf, - ord_data_dir: &std::path::PathBuf, + cookiefile: &std::path::Path, + ord_data_dir: &std::path::Path, rpc_port: u16, args: &[&str], ) -> Result { @@ -96,16 +96,16 @@ fn ord( .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .current_dir(&ord_data_dir) + .current_dir(ord_data_dir) .arg("--regtest") .arg("--data-dir") - .arg(ord_data_dir.as_path()) + .arg(ord_data_dir) .arg("--rpc-url") .arg(&format!("127.0.0.1:{}", rpc_port)) .arg("--cookie-file") .arg(cookiefile.to_str().unwrap()) .args(args); - + let output = ord.output().unwrap(); if output.status.success() { @@ -150,12 +150,13 @@ fn inscribe_child() { let cookiefile = bitcoin_data_dir.as_path().join("regtest/.cookie"); for attempt in 0.. { - match Client::new( + if Client::new( &format!("127.0.0.1:{rpc_port}"), bitcoincore_rpc::Auth::CookieFile(cookiefile.clone()), - ) { - Ok(_) => break, - _ => (), + ) + .is_ok() + { + break; } if attempt == 500 { @@ -188,11 +189,11 @@ fn inscribe_child() { #[derive(Deserialize, Debug)] struct Output { - commit: String, + _commit: String, inscription: String, - parent: Option, - reveal: String, - fees: u64, + _parent: Option, + _reveal: String, + _fees: u64, } let output: Output = match ord( @@ -214,13 +215,7 @@ fn inscribe_child() { &cookiefile, &ord_data_dir, rpc_port, - &[ - "wallet", - "inscribe", - "--parent", - &parent_id, - "child.txt", - ], + &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], ) { Ok(s) => serde_json::from_str(&s) .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), @@ -229,7 +224,7 @@ fn inscribe_child() { let child_id = output.inscription; let ord_port = 8080; - + rpc_client.generate_to_address(1, &address).unwrap(); let _ord_server = KillOnDrop( @@ -238,7 +233,7 @@ fn inscribe_child() { .stdin(Stdio::null()) // .stdout(Stdio::piped()) // .stderr(Stdio::piped()) - .current_dir(&ord_data_dir) + .current_dir(ord_data_dir.clone()) .arg("--regtest") .arg("--data-dir") .arg(ord_data_dir.as_path()) @@ -275,18 +270,25 @@ fn inscribe_child() { } let response = client - .get(format!("http://127.0.0.1:{ord_port}/inscription/{parent_id}")) + .get(format!( + "http://127.0.0.1:{ord_port}/inscription/{parent_id}" + )) .send() .unwrap(); assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); - + thread::sleep(Duration::from_secs(10)); - + let response = client - .get(format!("http://127.0.0.1:{ord_port}/inscription/{child_id}")) + .get(format!( + "http://127.0.0.1:{ord_port}/inscription/{child_id}" + )) .send() .unwrap(); - assert_regex_match!(response.text().unwrap(), &format!(".*parent.*{}.*", parent_id)); + assert_regex_match!( + response.text().unwrap(), + &format!(".*parent.*{}.*", parent_id) + ); } diff --git a/tests/test_server.rs b/tests/test_server.rs index ac8e5d98c0..d99fb3ff93 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -74,7 +74,6 @@ impl TestServer { thread::sleep(Duration::from_millis(25)); } - dbg!(path.as_ref()); let response = reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap(); assert_eq!(response.status(), StatusCode::OK); assert_regex_match!(response.text().unwrap(), regex.as_ref()); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 78d518d29b..fdd45a8150 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -410,10 +410,8 @@ fn inscribe_with_parent_inscription() { rpc_server.mine_blocks(1); - // TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - // format!("/inscription/{parent_id}"), - // format!(".*"), - // ); + TestServer::spawn_with_args(&rpc_server, &[]) + .assert_response_regex(format!("/inscription/{parent_id}"), ".*"); let child_output = CommandBuilder::new(format!("wallet inscribe --parent {parent_id} child.png")) .write("child.png", [1; 520]) @@ -425,8 +423,8 @@ fn inscribe_with_parent_inscription() { rpc_server.mine_blocks(1); TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( - format!("/inscription/{}", dbg!(child_output.inscription)), - format!(".*parent.*{}", parent_id), + format!("/inscription/{}", child_output.inscription), + format!(".*parent.*{}.*", parent_id), ); } From 6dd7f7b6c7644d6f150612e97ddc17d6cfeb9588 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 22 Mar 2023 14:25:27 -0700 Subject: [PATCH 29/34] fmt --- src/index.rs | 2 +- src/subcommand/wallet/inscribe.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.rs b/src/index.rs index be0306983d..a113672b00 100644 --- a/src/index.rs +++ b/src/index.rs @@ -548,7 +548,7 @@ impl Index { { return Ok(None); } - + Ok( self .get_transaction(inscription_id.txid)? diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 14c1c8011e..8d7fd67208 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,4 +1,4 @@ -use bitcoin::{SchnorrSig}; +use bitcoin::SchnorrSig; use { super::*, From 4cd3b943f01081789e33e11b20d5957bb0ed4a05 Mon Sep 17 00:00:00 2001 From: ericatallah Date: Wed, 22 Mar 2023 15:49:50 -0700 Subject: [PATCH 30/34] wip debug failed test when backup_recovery_key is enabled --- src/options.rs | 8 +++- src/subcommand/preview.rs | 2 +- src/subcommand/wallet.rs | 2 +- src/subcommand/wallet/inscribe.rs | 19 ++++++--- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 2 +- src/wallet.rs | 2 +- tests/core.rs | 61 +++++++++++++++------------ 9 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/options.rs b/src/options.rs index 6b0823f2a2..53f6c90834 100644 --- a/src/options.rs +++ b/src/options.rs @@ -173,7 +173,12 @@ impl Options { Ok(client) } - pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool) -> Result { + pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool, has_parent: bool) -> Result { + if has_parent { + println!("in bitcoin_rpc_client_for_wallet_command fn"); + println!("self.wallet: {}", self.wallet); + } + let client = self.bitcoin_rpc_client()?; const MIN_VERSION: usize = 240000; @@ -189,6 +194,7 @@ impl Options { if !create { if !client.list_wallets()?.contains(&self.wallet) { + println!("loading wallet..."); client.load_wallet(&self.wallet)?; } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 13ba8d1730..544fe995a3 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -66,7 +66,7 @@ impl Preview { }) .run(options.clone())?; - let rpc_client = options.bitcoin_rpc_client_for_wallet_command(false)?; + let rpc_client = options.bitcoin_rpc_client_for_wallet_command(false, false)?; let address = rpc_client.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 0404f2ad24..c5c7dd6d0c 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -74,7 +74,7 @@ fn get_change_address(client: &Client) -> Result
{ } pub(crate) fn initialize_wallet(options: &Options, seed: [u8; 64]) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command(true)?; + let client = options.bitcoin_rpc_client_for_wallet_command(true, false)?; let network = options.chain().network(); client.create_wallet(&options.wallet, None, Some(true), None, None)?; diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 8d7fd67208..5e53ae6ef0 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -63,10 +63,17 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { + println!("inscribing..."); + println!("parent is some: {}", self.parent.is_some()); + let index = Index::open(&options)?; index.update()?; - let client = options.bitcoin_rpc_client_for_wallet_command(false)?; + let client = options.bitcoin_rpc_client_for_wallet_command(false, self.parent.is_some())?; + + if self.parent.is_some() { + println!("after bitcoin_rpc_client_for_wallet_command call..."); + } let mut utxos = index.get_unspent_outputs(Wallet::load(&options)?)?; @@ -107,7 +114,7 @@ impl Inscribe { .map(Ok) .unwrap_or_else(|| get_change_address(&client))?; - let (unsigned_commit_tx, partially_signed_reveal_tx, _recovery_key_pair) = + let (unsigned_commit_tx, partially_signed_reveal_tx, recovery_key_pair) = Inscribe::create_inscription_transactions( self.satpoint, parent, @@ -147,9 +154,9 @@ impl Inscribe { return Ok(()); } - // if !self.no_backup { - // 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)? @@ -412,7 +419,7 @@ impl Inscribe { Ok((unsigned_commit_tx, reveal_tx, recovery_key_pair)) } - fn _backup_recovery_key( + fn backup_recovery_key( client: &Client, recovery_key_pair: TweakedKeyPair, network: Network, diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 78726b8156..5d0c568572 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -7,7 +7,7 @@ pub struct Output { pub(crate) fn run(options: Options) -> Result { let address = options - .bitcoin_rpc_client_for_wallet_command(false)? + .bitcoin_rpc_client_for_wallet_command(false, false)? .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; print_json(Output { address })?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 85a22f3c09..c8da89bcca 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -26,7 +26,7 @@ impl Send { let index = Index::open(&options)?; index.update()?; - let client = options.bitcoin_rpc_client_for_wallet_command(false)?; + let client = options.bitcoin_rpc_client_for_wallet_command(false, false)?; let unspent_outputs = index.get_unspent_outputs(Wallet::load(&options)?)?; diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 9e5961f911..5bc6d9c271 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -16,7 +16,7 @@ impl Transactions { pub(crate) fn run(self, options: Options) -> Result { let mut output = Vec::new(); for tx in options - .bitcoin_rpc_client_for_wallet_command(false)? + .bitcoin_rpc_client_for_wallet_command(false, false)? .list_transactions( None, Some(self.limit.unwrap_or(u16::MAX).into()), diff --git a/src/wallet.rs b/src/wallet.rs index d97435a83f..5ca27a557b 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -6,7 +6,7 @@ pub(crate) struct Wallet { impl Wallet { pub(crate) fn load(options: &Options) -> Result { - options.bitcoin_rpc_client_for_wallet_command(false)?; + options.bitcoin_rpc_client_for_wallet_command(false, false)?; Ok(Self { _private: () }) } diff --git a/tests/core.rs b/tests/core.rs index 5c99cf82ab..2b02bb5599 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -166,6 +166,8 @@ fn inscribe_child() { thread::sleep(Duration::from_millis(50)); } + thread::sleep(Duration::from_secs(1)); + let _ = ord(&cookiefile, &ord_data_dir, rpc_port, &["wallet", "create"]); // get funds in wallet @@ -179,6 +181,8 @@ fn inscribe_child() { ) .unwrap(); + thread::sleep(Duration::from_secs(1)); + let address = rpc_client .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m)) .unwrap(); @@ -189,24 +193,26 @@ fn inscribe_child() { #[derive(Deserialize, Debug)] struct Output { - _commit: String, + commit: String, inscription: String, - _parent: Option, - _reveal: String, - _fees: u64, + parent: Option, + reveal: String, + fees: u64, } - let output: Output = match ord( + // let output: Output = match ord( + match ord( &cookiefile, &ord_data_dir, rpc_port, &["wallet", "inscribe", "parent.txt"], ) { - Ok(s) => serde_json::from_str(&s) - .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), + Ok(s) => &s, + // Ok(s) => serde_json::from_str(&s) + // .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), Err(e) => panic!("error inscribing parent: {}", e), }; - let parent_id = output.inscription; + // let parent_id = output.inscription; rpc_client.generate_to_address(1, &address).unwrap(); @@ -215,7 +221,8 @@ fn inscribe_child() { &cookiefile, &ord_data_dir, rpc_port, - &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], + // &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], + &["wallet", "inscribe", "--parent", "856036cb73bd2414c6fb65047b26e303ce3fc7ac451ee44982e924fa7777e844i0", "child.txt"], ) { Ok(s) => serde_json::from_str(&s) .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), @@ -269,26 +276,26 @@ fn inscribe_child() { thread::sleep(Duration::from_millis(25)); } - let response = client - .get(format!( - "http://127.0.0.1:{ord_port}/inscription/{parent_id}" - )) - .send() - .unwrap(); + // let response = client + // .get(format!( + // "http://127.0.0.1:{ord_port}/inscription/{parent_id}" + // )) + // .send() + // .unwrap(); - assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); + // assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); - thread::sleep(Duration::from_secs(10)); + // thread::sleep(Duration::from_secs(10)); - let response = client - .get(format!( - "http://127.0.0.1:{ord_port}/inscription/{child_id}" - )) - .send() - .unwrap(); + // let response = client + // .get(format!( + // "http://127.0.0.1:{ord_port}/inscription/{child_id}" + // )) + // .send() + // .unwrap(); - assert_regex_match!( - response.text().unwrap(), - &format!(".*parent.*{}.*", parent_id) - ); + // assert_regex_match!( + // response.text().unwrap(), + // &format!(".*parent.*{}.*", parent_id) + // ); } From 8ef6abf64cf012f152362c36164835185b2ac2a7 Mon Sep 17 00:00:00 2001 From: ericatallah Date: Wed, 22 Mar 2023 16:00:33 -0700 Subject: [PATCH 31/34] put test back to normal --- src/subcommand/wallet/inscribe.rs | 7 +++-- tests/core.rs | 52 +++++++++++++++---------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 5e53ae6ef0..dd4233d334 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -63,8 +63,11 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - println!("inscribing..."); - println!("parent is some: {}", self.parent.is_some()); + // println!("inscribing..."); + // println!("parent is some: {}", self.parent.is_some()); + if self.parent.is_some() { + println!("ahhhhhh..."); + } let index = Index::open(&options)?; index.update()?; diff --git a/tests/core.rs b/tests/core.rs index 2b02bb5599..ebe5c548f0 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -200,19 +200,19 @@ fn inscribe_child() { fees: u64, } - // let output: Output = match ord( - match ord( + let output: Output = match ord( + // match ord( &cookiefile, &ord_data_dir, rpc_port, &["wallet", "inscribe", "parent.txt"], ) { - Ok(s) => &s, - // Ok(s) => serde_json::from_str(&s) - // .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), + // Ok(s) => &s, + Ok(s) => serde_json::from_str(&s) + .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), Err(e) => panic!("error inscribing parent: {}", e), }; - // let parent_id = output.inscription; + let parent_id = output.inscription; rpc_client.generate_to_address(1, &address).unwrap(); @@ -221,8 +221,8 @@ fn inscribe_child() { &cookiefile, &ord_data_dir, rpc_port, - // &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], - &["wallet", "inscribe", "--parent", "856036cb73bd2414c6fb65047b26e303ce3fc7ac451ee44982e924fa7777e844i0", "child.txt"], + &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], + // &["wallet", "inscribe", "--parent", "856036cb73bd2414c6fb65047b26e303ce3fc7ac451ee44982e924fa7777e844i0", "child.txt"], ) { Ok(s) => serde_json::from_str(&s) .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), @@ -276,26 +276,26 @@ fn inscribe_child() { thread::sleep(Duration::from_millis(25)); } - // let response = client - // .get(format!( - // "http://127.0.0.1:{ord_port}/inscription/{parent_id}" - // )) - // .send() - // .unwrap(); + let response = client + .get(format!( + "http://127.0.0.1:{ord_port}/inscription/{parent_id}" + )) + .send() + .unwrap(); - // assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); + assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); - // thread::sleep(Duration::from_secs(10)); + thread::sleep(Duration::from_secs(10)); - // let response = client - // .get(format!( - // "http://127.0.0.1:{ord_port}/inscription/{child_id}" - // )) - // .send() - // .unwrap(); + let response = client + .get(format!( + "http://127.0.0.1:{ord_port}/inscription/{child_id}" + )) + .send() + .unwrap(); - // assert_regex_match!( - // response.text().unwrap(), - // &format!(".*parent.*{}.*", parent_id) - // ); + assert_regex_match!( + response.text().unwrap(), + &format!(".*parent.*{}.*", parent_id) + ); } From 00be03876fe548d7454fc7eb73f4faa8856a6189 Mon Sep 17 00:00:00 2001 From: Felipe Lincoln Date: Thu, 23 Mar 2023 15:29:29 -0300 Subject: [PATCH 32/34] add 100 block confirmations --- tests/core.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/core.rs b/tests/core.rs index ebe5c548f0..cae456f1d7 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -1,5 +1,6 @@ use { super::*, + bitcoin::Address, bitcoincore_rpc::{Client, RpcApi}, std::ffi::OsString, }; @@ -187,7 +188,9 @@ fn inscribe_child() { .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m)) .unwrap(); - rpc_client.generate_to_address(101, &address).unwrap(); + rpc_client.generate_to_address(1, &address).unwrap(); + let random_address = Address::from_str("bcrt1p998q5wmfjdwxkxpjcrnsfytdgu24qazas9gcjdfzl2asq9s38qtsgl8men").unwrap(); + rpc_client.generate_to_address(100, &random_address).unwrap(); fs::write(ord_data_dir.as_path().join("parent.txt"), "Pater").unwrap(); From 26e67c41498d03fe80350ddfc9423f601e803912 Mon Sep 17 00:00:00 2001 From: ericatallah Date: Thu, 23 Mar 2023 11:37:16 -0700 Subject: [PATCH 33/34] cleanup --- src/options.rs | 8 +------- src/subcommand/preview.rs | 2 +- src/subcommand/wallet.rs | 2 +- src/subcommand/wallet/inscribe.rs | 12 +----------- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 2 +- src/wallet.rs | 2 +- tests/core.rs | 10 +++++----- 9 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/options.rs b/src/options.rs index 53f6c90834..6b0823f2a2 100644 --- a/src/options.rs +++ b/src/options.rs @@ -173,12 +173,7 @@ impl Options { Ok(client) } - pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool, has_parent: bool) -> Result { - if has_parent { - println!("in bitcoin_rpc_client_for_wallet_command fn"); - println!("self.wallet: {}", self.wallet); - } - + pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool) -> Result { let client = self.bitcoin_rpc_client()?; const MIN_VERSION: usize = 240000; @@ -194,7 +189,6 @@ impl Options { if !create { if !client.list_wallets()?.contains(&self.wallet) { - println!("loading wallet..."); client.load_wallet(&self.wallet)?; } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 544fe995a3..13ba8d1730 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -66,7 +66,7 @@ impl Preview { }) .run(options.clone())?; - let rpc_client = options.bitcoin_rpc_client_for_wallet_command(false, false)?; + 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))?; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index c5c7dd6d0c..0404f2ad24 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -74,7 +74,7 @@ fn get_change_address(client: &Client) -> Result
{ } pub(crate) fn initialize_wallet(options: &Options, seed: [u8; 64]) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command(true, false)?; + let client = options.bitcoin_rpc_client_for_wallet_command(true)?; let network = options.chain().network(); client.create_wallet(&options.wallet, None, Some(true), None, None)?; diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index dd4233d334..ca74c36cf0 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -63,20 +63,10 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - // println!("inscribing..."); - // println!("parent is some: {}", self.parent.is_some()); - if self.parent.is_some() { - println!("ahhhhhh..."); - } - let index = Index::open(&options)?; index.update()?; - let client = options.bitcoin_rpc_client_for_wallet_command(false, self.parent.is_some())?; - - if self.parent.is_some() { - println!("after bitcoin_rpc_client_for_wallet_command call..."); - } + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; let mut utxos = index.get_unspent_outputs(Wallet::load(&options)?)?; diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 5d0c568572..78726b8156 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -7,7 +7,7 @@ pub struct Output { pub(crate) fn run(options: Options) -> Result { let address = options - .bitcoin_rpc_client_for_wallet_command(false, false)? + .bitcoin_rpc_client_for_wallet_command(false)? .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; print_json(Output { address })?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index c8da89bcca..85a22f3c09 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -26,7 +26,7 @@ impl Send { let index = Index::open(&options)?; index.update()?; - let client = options.bitcoin_rpc_client_for_wallet_command(false, false)?; + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; let unspent_outputs = index.get_unspent_outputs(Wallet::load(&options)?)?; diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 5bc6d9c271..9e5961f911 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -16,7 +16,7 @@ impl Transactions { pub(crate) fn run(self, options: Options) -> Result { let mut output = Vec::new(); for tx in options - .bitcoin_rpc_client_for_wallet_command(false, false)? + .bitcoin_rpc_client_for_wallet_command(false)? .list_transactions( None, Some(self.limit.unwrap_or(u16::MAX).into()), diff --git a/src/wallet.rs b/src/wallet.rs index 5ca27a557b..d97435a83f 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -6,7 +6,7 @@ pub(crate) struct Wallet { impl Wallet { pub(crate) fn load(options: &Options) -> Result { - options.bitcoin_rpc_client_for_wallet_command(false, false)?; + options.bitcoin_rpc_client_for_wallet_command(false)?; Ok(Self { _private: () }) } diff --git a/tests/core.rs b/tests/core.rs index cae456f1d7..6495c5a12c 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -204,28 +204,28 @@ fn inscribe_child() { } let output: Output = match ord( - // match ord( &cookiefile, &ord_data_dir, rpc_port, &["wallet", "inscribe", "parent.txt"], ) { - // Ok(s) => &s, Ok(s) => serde_json::from_str(&s) .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), Err(e) => panic!("error inscribing parent: {}", e), }; + let parent_id = output.inscription; rpc_client.generate_to_address(1, &address).unwrap(); + thread::sleep(Duration::from_secs(1)); + fs::write(ord_data_dir.as_path().join("child.txt"), "Filius").unwrap(); let output: Output = match ord( &cookiefile, &ord_data_dir, rpc_port, &["wallet", "inscribe", "--parent", &parent_id, "child.txt"], - // &["wallet", "inscribe", "--parent", "856036cb73bd2414c6fb65047b26e303ce3fc7ac451ee44982e924fa7777e844i0", "child.txt"], ) { Ok(s) => serde_json::from_str(&s) .unwrap_or_else(|err| panic!("Failed to deserialize JSON: {err}\n{s}")), @@ -237,6 +237,8 @@ fn inscribe_child() { rpc_client.generate_to_address(1, &address).unwrap(); + thread::sleep(Duration::from_secs(1)); + let _ord_server = KillOnDrop( Command::new(executable_path("ord")) .env("ORD_INTEGRATION_TEST", "1") @@ -288,8 +290,6 @@ fn inscribe_child() { assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); - thread::sleep(Duration::from_secs(10)); - let response = client .get(format!( "http://127.0.0.1:{ord_port}/inscription/{child_id}" From a55fc13d5412100ac18333828f868ceccf7da416 Mon Sep 17 00:00:00 2001 From: ericatallah Date: Thu, 23 Mar 2023 13:15:58 -0700 Subject: [PATCH 34/34] add sleep 10 back in --- tests/core.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core.rs b/tests/core.rs index 6495c5a12c..4154574b82 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -237,8 +237,6 @@ fn inscribe_child() { rpc_client.generate_to_address(1, &address).unwrap(); - thread::sleep(Duration::from_secs(1)); - let _ord_server = KillOnDrop( Command::new(executable_path("ord")) .env("ORD_INTEGRATION_TEST", "1") @@ -290,6 +288,8 @@ fn inscribe_child() { assert_regex_match!(response.text().unwrap(), &format!(".*id.*{}.*", parent_id)); + thread::sleep(Duration::from_secs(10)); + let response = client .get(format!( "http://127.0.0.1:{ord_port}/inscription/{child_id}"