From a0da0c122b0d45d631f0adec1de5d4fb09ebaac6 Mon Sep 17 00:00:00 2001 From: raph Date: Fri, 20 Dec 2024 20:59:06 +0100 Subject: [PATCH] Return `None` for assets when asset index does not exist (#4141) --- src/api.rs | 12 ++-- src/index.rs | 84 +++++++++++++++++--------- src/subcommand/list.rs | 4 +- src/subcommand/server.rs | 42 ++++++++----- src/subcommand/wallet/addresses.rs | 21 +++---- src/subcommand/wallet/balance.rs | 4 +- src/subcommand/wallet/batch_command.rs | 2 +- src/subcommand/wallet/burn.rs | 2 +- src/subcommand/wallet/cardinals.rs | 2 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/outputs.rs | 21 +++---- src/subcommand/wallet/runics.rs | 64 ++++++++++---------- src/subcommand/wallet/split.rs | 2 + src/templates/address.rs | 12 ++-- src/templates/output.rs | 54 +++++++++-------- src/wallet.rs | 27 +++++---- src/wallet/wallet_constructor.rs | 3 +- templates/address.html | 10 +-- templates/output.html | 8 +-- tests/json_api.rs | 31 ++++++---- tests/list.rs | 4 +- tests/wallet/send.rs | 6 +- 22 files changed, 229 insertions(+), 188 deletions(-) diff --git a/src/api.rs b/src/api.rs index eae5ac144a..5b335c48e0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -155,9 +155,9 @@ pub struct Inscriptions { pub struct Output { pub address: Option>, pub indexed: bool, - pub inscriptions: Vec, + pub inscriptions: Option>, pub outpoint: OutPoint, - pub runes: BTreeMap, + pub runes: Option>, pub sat_ranges: Option>, pub script_pubkey: ScriptBuf, pub spent: bool, @@ -168,11 +168,11 @@ pub struct Output { impl Output { pub fn new( chain: Chain, - inscriptions: Vec, + inscriptions: Option>, outpoint: OutPoint, tx_out: TxOut, indexed: bool, - runes: BTreeMap, + runes: Option>, sat_ranges: Option>, spent: bool, ) -> Self { @@ -229,7 +229,7 @@ pub struct SatInscriptions { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct AddressInfo { pub outputs: Vec, - pub inscriptions: Vec, + pub inscriptions: Option>, pub sat_balance: u64, - pub runes_balances: Vec<(SpacedRune, Decimal, Option)>, + pub runes_balances: Option)>>, } diff --git a/src/index.rs b/src/index.rs index bac14b8725..b5e035b668 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1020,7 +1020,11 @@ impl Index { pub fn get_rune_balances_for_output( &self, outpoint: OutPoint, - ) -> Result> { + ) -> Result>> { + if !self.index_runes { + return Ok(None); + } + let rtx = self.database.begin_read()?; let outpoint_to_balances = rtx.open_table(OUTPOINT_TO_RUNE_BALANCES)?; @@ -1028,7 +1032,7 @@ impl Index { let id_to_rune_entries = rtx.open_table(RUNE_ID_TO_RUNE_ENTRY)?; let Some(balances) = outpoint_to_balances.get(&outpoint.store())? else { - return Ok(BTreeMap::new()); + return Ok(Some(BTreeMap::new())); }; let balances_buffer = balances.value(); @@ -1051,7 +1055,7 @@ impl Index { ); } - Ok(balances) + Ok(Some(balances)) } pub fn get_rune_balance_map(&self) -> Result>> { @@ -1534,7 +1538,11 @@ impl Index { pub fn get_inscriptions_on_output_with_satpoints( &self, outpoint: OutPoint, - ) -> Result> { + ) -> Result>> { + if !self.index_inscriptions { + return Ok(None); + } + let rtx = self.database.begin_read()?; let outpoint_to_utxo_entry = rtx.open_table(OUTPOINT_TO_UTXO_ENTRY)?; let sequence_number_to_inscription_entry = @@ -1547,31 +1555,40 @@ impl Index { ) } - pub fn get_inscriptions_for_output(&self, outpoint: OutPoint) -> Result> { - Ok( - self - .get_inscriptions_on_output_with_satpoints(outpoint)? + pub fn get_inscriptions_for_output( + &self, + outpoint: OutPoint, + ) -> Result>> { + let Some(inscriptions) = self.get_inscriptions_on_output_with_satpoints(outpoint)? else { + return Ok(None); + }; + + Ok(Some( + inscriptions .iter() .map(|(_satpoint, inscription_id)| *inscription_id) .collect(), - ) + )) } pub fn get_inscriptions_for_outputs( &self, outpoints: &Vec, - ) -> Result> { - let mut inscriptions = Vec::new(); + ) -> Result>> { + let mut result = Vec::new(); for outpoint in outpoints { - inscriptions.extend( - self - .get_inscriptions_on_output_with_satpoints(*outpoint)? + let Some(inscriptions) = self.get_inscriptions_on_output_with_satpoints(*outpoint)? else { + return Ok(None); + }; + + result.extend( + inscriptions .iter() .map(|(_satpoint, inscription_id)| *inscription_id), ); } - Ok(inscriptions) + Ok(Some(result)) } pub fn get_transaction(&self, txid: Txid) -> Result> { @@ -2247,13 +2264,13 @@ impl Index { outpoint_to_utxo_entry: &'a impl ReadableTable<&'static OutPointValue, &'static UtxoEntry>, sequence_number_to_inscription_entry: &'a impl ReadableTable, outpoint: OutPoint, - ) -> Result> { + ) -> Result>> { if !self.index_inscriptions { - return Ok(Vec::new()); + return Ok(None); } let Some(utxo_entry) = outpoint_to_utxo_entry.get(&outpoint.store())? else { - return Ok(Vec::new()); + return Ok(Some(Vec::new())); }; let mut inscriptions = utxo_entry.value().parse(self).parse_inscriptions(); @@ -2266,10 +2283,13 @@ impl Index { let entry = sequence_number_to_inscription_entry .get(sequence_number)? .unwrap(); + let satpoint = SatPoint { outpoint, offset }; + Ok((satpoint, InscriptionEntry::load(entry.value()).id)) }) .collect::>() + .map(Some) } pub fn get_address_info(&self, address: &Address) -> Result> { @@ -2289,11 +2309,13 @@ impl Index { pub(crate) fn get_aggregated_rune_balances_for_outputs( &self, outputs: &Vec, - ) -> Result)>> { + ) -> Result)>>> { let mut runes = BTreeMap::new(); for output in outputs { - let rune_balances = self.get_rune_balances_for_output(*output)?; + let Some(rune_balances) = self.get_rune_balances_for_output(*output)? else { + return Ok(None); + }; for (spaced_rune, pile) in rune_balances { runes @@ -2312,12 +2334,12 @@ impl Index { } } - Ok( + Ok(Some( runes .into_iter() .map(|(spaced_rune, (decimal, symbol))| (spaced_rune, decimal, symbol)) .collect(), - ) + )) } pub(crate) fn get_sat_balances_for_outputs(&self, outputs: &Vec) -> Result { @@ -3476,7 +3498,8 @@ mod tests { context .index .get_inscriptions_for_output(OutPoint { txid, vout: 0 }) - .unwrap(), + .unwrap() + .unwrap_or_default(), [] ); @@ -3486,7 +3509,8 @@ mod tests { context .index .get_inscriptions_for_output(OutPoint { txid, vout: 0 }) - .unwrap(), + .unwrap() + .unwrap_or_default(), [inscription_id] ); @@ -3501,7 +3525,8 @@ mod tests { context .index .get_inscriptions_for_output(OutPoint { txid, vout: 0 }) - .unwrap(), + .unwrap() + .unwrap_or_default(), [] ); @@ -3512,7 +3537,8 @@ mod tests { txid: send_id, vout: 0, }) - .unwrap(), + .unwrap() + .unwrap_or_default(), [inscription_id] ); } @@ -3542,7 +3568,8 @@ mod tests { txid: first, vout: 0 }) - .unwrap(), + .unwrap() + .unwrap_or_default(), [inscription_id] ); @@ -4420,6 +4447,7 @@ mod tests { .index .get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 }) .unwrap() + .unwrap_or_default() .iter() .map(|(_satpoint, inscription_id)| *inscription_id) .collect::>() @@ -4484,6 +4512,7 @@ mod tests { .index .get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 }) .unwrap() + .unwrap_or_default() ) } } @@ -4533,6 +4562,7 @@ mod tests { vout: 0 }) .unwrap() + .unwrap_or_default() ) } } diff --git a/src/subcommand/list.rs b/src/subcommand/list.rs index b790a621e8..d5892e36c1 100644 --- a/src/subcommand/list.rs +++ b/src/subcommand/list.rs @@ -10,8 +10,8 @@ pub(crate) struct List { pub struct Output { pub address: Option>, pub indexed: bool, - pub inscriptions: Vec, - pub runes: BTreeMap, + pub inscriptions: Option>, + pub runes: Option>, pub sat_ranges: Option>, pub script_pubkey: String, pub spent: bool, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 38e501f4ed..cdbaf763b2 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -758,13 +758,21 @@ impl Server { OutputType::Cardinal => { index .get_inscriptions_on_output_with_satpoints(output)? + .unwrap_or_default() .is_empty() - && index.get_rune_balances_for_output(output)?.is_empty() + && index + .get_rune_balances_for_output(output)? + .unwrap_or_default() + .is_empty() } OutputType::Inscribed => !index .get_inscriptions_on_output_with_satpoints(output)? + .unwrap_or_default() + .is_empty(), + OutputType::Runic => !index + .get_rune_balances_for_output(output)? + .unwrap_or_default() .is_empty(), - OutputType::Runic => !index.get_rune_balances_for_output(output)?.is_empty(), }; if include { @@ -3728,21 +3736,23 @@ mod tests { transaction: txid, sat_ranges: None, indexed: true, - inscriptions: Vec::new(), + inscriptions: Some(Vec::new()), outpoint: output, - runes: vec![( - SpacedRune { - rune: Rune(RUNE), - spacers: 0 - }, - Pile { - amount: 340282366920938463463374607431768211455, - divisibility: 1, - symbol: None, - } - )] - .into_iter() - .collect(), + runes: Some( + vec![( + SpacedRune { + rune: Rune(RUNE), + spacers: 0 + }, + Pile { + amount: 340282366920938463463374607431768211455, + divisibility: 1, + symbol: None, + } + )] + .into_iter() + .collect() + ), spent: false, } ); diff --git a/src/subcommand/wallet/addresses.rs b/src/subcommand/wallet/addresses.rs index f8db7b3a06..2119bf3133 100644 --- a/src/subcommand/wallet/addresses.rs +++ b/src/subcommand/wallet/addresses.rs @@ -16,16 +16,12 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { for (output, txout) in wallet.utxos() { let address = wallet.chain().address_from_script(&txout.script_pubkey)?; - let inscriptions = if wallet.has_inscription_index() { - Some(wallet.get_inscriptions_in_output(output)) - } else { - None - }; + let inscriptions = wallet.get_inscriptions_in_output(output); - let runes = if wallet.has_rune_index() { - Some( - wallet - .get_runes_balances_in_output(output)? + let runes = wallet + .get_runes_balances_in_output(output)? + .map(|balances| { + balances .iter() .map(|(rune, pile)| { ( @@ -36,11 +32,8 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { }, ) }) - .collect(), - ) - } else { - None - }; + .collect() + }); let output = Output { output: *output, diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index bda1b72cf5..b7a5445532 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -26,7 +26,9 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let mut runic = 0; for (output, txout) in unspent_outputs { - let rune_balances = wallet.get_runes_balances_in_output(output)?; + let rune_balances = wallet + .get_runes_balances_in_output(output)? + .unwrap_or_default(); let is_ordinal = inscription_outputs.contains(output); let is_runic = !rune_balances.is_empty(); diff --git a/src/subcommand/wallet/batch_command.rs b/src/subcommand/wallet/batch_command.rs index 43e4c79133..5d6500cb22 100644 --- a/src/subcommand/wallet/batch_command.rs +++ b/src/subcommand/wallet/batch_command.rs @@ -64,7 +64,7 @@ impl Batch { } .inscribe( &locked_utxos.into_keys().collect(), - wallet.get_runic_outputs()?, + wallet.get_runic_outputs()?.unwrap_or_default(), utxos, &wallet, ) diff --git a/src/subcommand/wallet/burn.rs b/src/subcommand/wallet/burn.rs index bdf4e70bc2..93eb804645 100644 --- a/src/subcommand/wallet/burn.rs +++ b/src/subcommand/wallet/burn.rs @@ -144,7 +144,7 @@ impl Burn { script_pubkey: ScriptBuf, burn_amount: Amount, ) -> Result { - let runic_outputs = wallet.get_runic_outputs()?; + let runic_outputs = wallet.get_runic_outputs()?.unwrap_or_default(); ensure!( !runic_outputs.contains(&satpoint.outpoint), diff --git a/src/subcommand/wallet/cardinals.rs b/src/subcommand/wallet/cardinals.rs index bceeba421c..5f061dc607 100644 --- a/src/subcommand/wallet/cardinals.rs +++ b/src/subcommand/wallet/cardinals.rs @@ -15,7 +15,7 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { .map(|satpoint| satpoint.outpoint) .collect::>(); - let runic_utxos = wallet.get_runic_outputs()?; + let runic_utxos = wallet.get_runic_outputs()?.unwrap_or_default(); let cardinal_utxos = unspent_outputs .iter() diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index c71c652318..13a2c332dd 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -95,7 +95,7 @@ impl Inscribe { } .inscribe( &wallet.locked_utxos().clone().into_keys().collect(), - wallet.get_runic_outputs()?, + wallet.get_runic_outputs()?.unwrap_or_default(), wallet.utxos(), &wallet, ) diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index 1bddcaeff1..1254fc2316 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -30,16 +30,12 @@ impl Outputs { .ok() .map(|address| address.as_unchecked().clone()); - let inscriptions = if wallet.has_inscription_index() { - Some(wallet.get_inscriptions_in_output(output)) - } else { - None - }; + let inscriptions = wallet.get_inscriptions_in_output(output); - let runes = if wallet.has_rune_index() { - Some( - wallet - .get_runes_balances_in_output(output)? + let runes = wallet + .get_runes_balances_in_output(output)? + .map(|balances| { + balances .iter() .map(|(rune, pile)| { ( @@ -50,11 +46,8 @@ impl Outputs { }, ) }) - .collect(), - ) - } else { - None - }; + .collect() + }); let sat_ranges = if wallet.has_sat_index() && self.ranges { Some( diff --git a/src/subcommand/wallet/runics.rs b/src/subcommand/wallet/runics.rs index 12631e0e70..2b1702869b 100644 --- a/src/subcommand/wallet/runics.rs +++ b/src/subcommand/wallet/runics.rs @@ -8,37 +8,39 @@ pub struct RunicUtxo { pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let unspent_outputs = wallet.utxos(); - let runic_utxos = wallet.get_runic_outputs()?; - - let runic_utxos = unspent_outputs - .iter() - .filter_map(|(output, _)| { - if runic_utxos.contains(output) { - let rune_balances = wallet.get_runes_balances_in_output(output).ok()?; - let mut runes = BTreeMap::new(); - - for (spaced_rune, pile) in rune_balances { - runes - .entry(spaced_rune) - .and_modify(|decimal: &mut Decimal| { - assert_eq!(decimal.scale, pile.divisibility); - decimal.value += pile.amount; - }) - .or_insert(Decimal { - value: pile.amount, - scale: pile.divisibility, - }); - } - - Some(RunicUtxo { - output: *output, - runes, - }) - } else { - None + let Some(runic_utxos) = wallet.get_runic_outputs()? else { + bail!("`ord wallet runics` requires index created with `--index-runes`") + }; + + let mut result = Vec::new(); + + for output in unspent_outputs.keys() { + if runic_utxos.contains(output) { + let rune_balances = wallet + .get_runes_balances_in_output(output)? + .unwrap_or_default(); + + let mut runes = BTreeMap::new(); + + for (spaced_rune, pile) in rune_balances { + runes + .entry(spaced_rune) + .and_modify(|decimal: &mut Decimal| { + assert_eq!(decimal.scale, pile.divisibility); + decimal.value += pile.amount; + }) + .or_insert(Decimal { + value: pile.amount, + scale: pile.divisibility, + }); } - }) - .collect::>(); - Ok(Some(Box::new(runic_utxos))) + result.push(RunicUtxo { + output: *output, + runes, + }); + } + } + + Ok(Some(Box::new(result))) } diff --git a/src/subcommand/wallet/split.rs b/src/subcommand/wallet/split.rs index 1342a34fea..3e498b3f80 100644 --- a/src/subcommand/wallet/split.rs +++ b/src/subcommand/wallet/split.rs @@ -113,6 +113,7 @@ impl Split { let balances = wallet .get_runic_outputs()? + .unwrap_or_default() .into_iter() .filter(|output| !inscribed_outputs.contains(output)) .map(|output| { @@ -120,6 +121,7 @@ impl Split { ( output, balance + .unwrap_or_default() .into_iter() .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) .collect(), diff --git a/src/templates/address.rs b/src/templates/address.rs index 6eb8ebe5e0..685835bb7a 100644 --- a/src/templates/address.rs +++ b/src/templates/address.rs @@ -4,9 +4,9 @@ use super::*; pub(crate) struct AddressHtml { pub(crate) address: Address, pub(crate) outputs: Vec, - pub(crate) inscriptions: Vec, + pub(crate) inscriptions: Option>, pub(crate) sat_balance: u64, - pub(crate) runes_balances: Vec<(SpacedRune, Decimal, Option)>, + pub(crate) runes_balances: Option)>>, } impl PageContent for AddressHtml { @@ -26,9 +26,9 @@ mod tests { .require_network(Network::Bitcoin) .unwrap(), outputs: vec![outpoint(1), outpoint(2)], - inscriptions: vec![inscription_id(1)], + inscriptions: Some(vec![inscription_id(1)]), sat_balance: 99, - runes_balances: vec![ + runes_balances: Some(vec![ ( SpacedRune { rune: Rune::from_str("TEEEEEEEEESTRUNE").unwrap(), @@ -51,7 +51,7 @@ mod tests { }, Some('F'), ), - ], + ]), } } @@ -80,7 +80,7 @@ mod tests { #[test] fn test_runes_balances_rendering() { let address_html = setup(); - let expected_pattern = r#".*
runes balances
\n\s*
TEEEEEEEEESTRUNE: 20000R
\n\s*
ANOTHERTEESTRUNE: 10000F
.*"#; + let expected_pattern = r#".*
rune balances
\n\s*
TEEEEEEEEESTRUNE: 20000R
\n\s*
ANOTHERTEESTRUNE: 10000F
.*"#; assert_regex_match!(address_html, expected_pattern); } diff --git a/src/templates/output.rs b/src/templates/output.rs index 235403cff8..23da2b64e9 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -3,10 +3,10 @@ use super::*; #[derive(Boilerplate)] pub(crate) struct OutputHtml { pub(crate) chain: Chain, - pub(crate) inscriptions: Vec, + pub(crate) inscriptions: Option>, pub(crate) outpoint: OutPoint, pub(crate) output: TxOut, - pub(crate) runes: BTreeMap, + pub(crate) runes: Option>, pub(crate) sat_ranges: Option>, pub(crate) spent: bool, } @@ -29,10 +29,10 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: Vec::new(), + inscriptions: Some(Vec::new()), outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(3), script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()), }, - runes: BTreeMap::new(), + runes: Some(BTreeMap::new()), sat_ranges: Some(vec![(0, 1), (1, 3)]), spent: false, }, @@ -60,13 +60,13 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: Vec::new(), + inscriptions: None, outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(1), script_pubkey: script::Builder::new().push_int(0).into_script(), }, - runes: BTreeMap::new(), + runes: None, sat_ranges: None, spent: true, }, @@ -88,10 +88,10 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: Vec::new(), + inscriptions: None, outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(3), script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()), }, - runes: BTreeMap::new(), + runes: None, sat_ranges: Some(vec![(0, 1), (1, 3)]), spent: true, }, @@ -119,10 +119,10 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: Vec::new(), + inscriptions: None, outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(3), script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()), }, - runes: BTreeMap::new(), + runes: None, sat_ranges: None, spent: false, } @@ -146,13 +146,13 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: vec![inscription_id(1)], + inscriptions: Some(vec![inscription_id(1)]), outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(3), script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()), }, - runes: BTreeMap::new(), + runes: None, sat_ranges: None, spent: false, }, @@ -175,25 +175,27 @@ mod tests { assert_regex_match!( OutputHtml { chain: Chain::Mainnet, - inscriptions: Vec::new(), + inscriptions: None, outpoint: outpoint(1), output: TxOut { value: Amount::from_sat(3), script_pubkey: ScriptBuf::new_p2pkh(&PubkeyHash::all_zeros()), }, - runes: vec![( - SpacedRune { - rune: Rune(26), - spacers: 1 - }, - Pile { - amount: 11, - divisibility: 1, - symbol: None, - } - )] - .into_iter() - .collect(), + runes: Some( + vec![( + SpacedRune { + rune: Rune(26), + spacers: 1 + }, + Pile { + amount: 11, + divisibility: 1, + symbol: None, + } + )] + .into_iter() + .collect() + ), sat_ranges: None, spent: false, }, diff --git a/src/wallet.rs b/src/wallet.rs index f45fc9dd6a..6dae9a4228 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -75,7 +75,6 @@ pub(crate) enum Maturity { pub(crate) struct Wallet { bitcoin_client: Client, database: Database, - has_inscription_index: bool, has_rune_index: bool, has_sat_index: bool, rpc_url: Url, @@ -181,7 +180,7 @@ impl Wallet { .utxos() .keys() .filter(|utxo| inscriptions.contains(utxo)) - .chain(self.get_runic_outputs()?.iter()) + .chain(self.get_runic_outputs()?.unwrap_or_default().iter()) .cloned() .filter(|utxo| !locked.contains(utxo)) .collect::>(); @@ -217,7 +216,7 @@ impl Wallet { ) } - pub(crate) fn get_inscriptions_in_output(&self, output: &OutPoint) -> Vec { + pub(crate) fn get_inscriptions_in_output(&self, output: &OutPoint) -> Option> { self.output_info.get(output).unwrap().inscriptions.clone() } @@ -251,21 +250,25 @@ impl Wallet { Ok(parent_info) } - pub(crate) fn get_runic_outputs(&self) -> Result> { + pub(crate) fn get_runic_outputs(&self) -> Result>> { let mut runic_outputs = BTreeSet::new(); - for (output, info) in self.output_info.iter() { - if !info.runes.is_empty() { + for (output, info) in &self.output_info { + let Some(runes) = &info.runes else { + return Ok(None); + }; + + if !runes.is_empty() { runic_outputs.insert(*output); } } - Ok(runic_outputs) + Ok(Some(runic_outputs)) } pub(crate) fn get_runes_balances_in_output( &self, output: &OutPoint, - ) -> Result> { + ) -> Result>> { Ok( self .output_info @@ -311,10 +314,6 @@ impl Wallet { ) } - pub(crate) fn has_inscription_index(&self) -> bool { - self.has_inscription_index - } - pub(crate) fn has_sat_index(&self) -> bool { self.has_sat_index } @@ -879,7 +878,7 @@ impl Wallet { } } - let runic_outputs = self.get_runic_outputs()?; + let runic_outputs = self.get_runic_outputs()?.unwrap_or_default(); ensure!( !runic_outputs.contains(&satpoint.outpoint), @@ -940,6 +939,7 @@ impl Wallet { let balances = self .get_runic_outputs()? + .unwrap_or_default() .into_iter() .filter(|output| !inscribed_outputs.contains(output)) .map(|output| { @@ -947,6 +947,7 @@ impl Wallet { ( output, balance + .unwrap_or_default() .into_iter() .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) .collect(), diff --git a/src/wallet/wallet_constructor.rs b/src/wallet/wallet_constructor.rs index b5e43c84fe..7d6108c565 100644 --- a/src/wallet/wallet_constructor.rs +++ b/src/wallet/wallet_constructor.rs @@ -118,7 +118,7 @@ impl WalletConstructor { let inscriptions = output_info .iter() - .flat_map(|(_output, info)| info.inscriptions.clone()) + .flat_map(|(_output, info)| info.inscriptions.clone().unwrap_or_default()) .collect::>(); let (inscriptions, inscription_info) = self.get_inscriptions(&inscriptions)?; @@ -128,7 +128,6 @@ impl WalletConstructor { Ok(Wallet { bitcoin_client, database, - has_inscription_index: status.inscription_index, has_rune_index: status.rune_index, has_sat_index: status.sat_index, inscription_info, diff --git a/templates/address.html b/templates/address.html index 6b7146ae65..677290a0c2 100644 --- a/templates/address.html +++ b/templates/address.html @@ -2,21 +2,23 @@

Address {{ self.address }}

sat balance
{{ self.sat_balance }}
-%% if !self.inscriptions.is_empty() { +%% if let Some(inscriptions) = self.inscriptions.as_ref().filter(|inscriptions| !inscriptions.is_empty()) {
inscriptions
-%% for inscription in &self.inscriptions { +%% for inscription in inscriptions { {{Iframe::thumbnail(*inscription)}} %% }
%% } -
runes balances
-%% for (rune, decimal, symbol) in self.runes_balances.iter() { +%% if let Some(runes_balances) = self.runes_balances.as_ref().filter(|runes_balances| !runes_balances.is_empty()) { +
rune balances
+%% for (rune, decimal, symbol) in runes_balances { %% if let Some(symbol) = symbol {
{{ rune }}: {{ decimal }}{{ symbol }}
%% } else {
{{ rune }}: {{ decimal }}ยค
%% } +%% } %% }
outputs
diff --git a/templates/output.html b/templates/output.html index 2cd281a162..56a640b312 100644 --- a/templates/output.html +++ b/templates/output.html @@ -1,14 +1,14 @@

Output {{self.outpoint}}

-%% if !self.inscriptions.is_empty() { +%% if let Some(inscriptions) = self.inscriptions.as_ref().filter(|inscriptions| !inscriptions.is_empty()) {
inscriptions
-%% for inscription in &self.inscriptions { +%% for inscription in inscriptions { {{Iframe::thumbnail(*inscription)}} %% }
%% } -%% if !self.runes.is_empty() { +%% if let Some(runes) = self.runes.as_ref().filter(|runes| !runes.is_empty()) {
runes
@@ -16,7 +16,7 @@

Output {{self.outpoint}}

-%% for (rune, balance) in &self.runes { +%% for (rune, balance) in runes { diff --git a/tests/json_api.rs b/tests/json_api.rs index 6ef28b2ae6..5606d7929b 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -406,13 +406,13 @@ fn get_output() { .unwrap() ), outpoint: OutPoint { txid, vout: 0 }, - inscriptions: vec![ + inscriptions: Some(vec![ InscriptionId { txid, index: 0 }, InscriptionId { txid, index: 1 }, InscriptionId { txid, index: 2 }, - ], + ]), indexed: true, - runes: BTreeMap::new(), + runes: None, sat_ranges: Some(vec![ (5000000000, 10000000000,), (10000000000, 15000000000,), @@ -809,13 +809,13 @@ fn outputs_address() { cardinals_json, vec![api::Output { address: Some(address.parse().unwrap()), - inscriptions: vec![], + inscriptions: Some(vec![]), outpoint: OutPoint { txid: cardinal_send.txid, vout: 0 }, indexed: true, - runes: BTreeMap::new(), + runes: Some(BTreeMap::new()), sat_ranges: None, script_pubkey: ScriptBuf::from( address @@ -853,13 +853,13 @@ fn outputs_address() { runes_json, vec![api::Output { address: Some(address.parse().unwrap()), - inscriptions: vec![], + inscriptions: Some(vec![]), outpoint: OutPoint { txid: rune_send.txid, vout: 0 }, indexed: true, - runes: expected_runes, + runes: Some(expected_runes), sat_ranges: None, script_pubkey: ScriptBuf::from( address @@ -884,16 +884,16 @@ fn outputs_address() { inscriptions_json, vec![api::Output { address: Some(address.parse().unwrap()), - inscriptions: vec![InscriptionId { + inscriptions: Some(vec![InscriptionId { txid: reveal, index: 0 - },], + },]), outpoint: OutPoint { txid: inscription_send.txid, vout: 0 }, indexed: true, - runes: BTreeMap::new(), + runes: Some(BTreeMap::new()), sat_ranges: None, script_pubkey: ScriptBuf::from( address @@ -924,11 +924,16 @@ fn outputs_address() { .unwrap(); assert_eq!(any.len(), 3); - assert!(any.iter().any(|output| output.runes.len() == 1)); - assert!(any.iter().any(|output| output.inscriptions.len() == 1)); assert!(any .iter() - .any(|output| output.inscriptions.is_empty() && output.runes.is_empty())); + .any(|output| output.runes.clone().unwrap_or_default().len() == 1)); + assert!(any + .iter() + .any(|output| output.inscriptions.clone().unwrap_or_default().len() == 1)); + assert!(any.iter().any( + |output| output.inscriptions.clone().unwrap_or_default().is_empty() + && output.runes.clone().unwrap_or_default().is_empty() + )); assert_eq!(any, default); } diff --git a/tests/list.rs b/tests/list.rs index 58dde2edee..199c5ceb59 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -17,8 +17,8 @@ fn output_found() { Output { address: None, indexed: true, - inscriptions: vec![], - runes: BTreeMap::new(), + inscriptions: Some(Vec::new()), + runes: None, sat_ranges: Some(vec![Range { end: 50 * COIN_VALUE, name: "nvtdijuwxlp".into(), diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 2d843f65b8..11e2fc9602 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -349,7 +349,7 @@ inscriptions: txid: reveal_txid, vout: 0 }, - inscriptions: vec![ + inscriptions: Some(vec![ InscriptionId { txid: reveal_txid, index: 0 @@ -362,9 +362,9 @@ inscriptions: txid: reveal_txid, index: 2 }, - ], + ]), indexed: true, - runes: BTreeMap::new(), + runes: None, sat_ranges: Some(vec![(5_000_000_000, 5_000_030_000)]), script_pubkey: destination.assume_checked_ref().script_pubkey(), spent: false,
rune balance
{{ rune }} {{ balance }}