Changeset View
Changeset View
Standalone View
Standalone View
chronik/chronik-db/src/groups/script.rs
// Copyright (c) 2023 The Bitcoin developers | // Copyright (c) 2023 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
use bitcoinsuite_core::script::{compress_script_variant, Script}; | use bitcoinsuite_core::{ | ||||
script::{compress_script_variant, Script}, | |||||
tx::Tx, | |||||
}; | |||||
use bytes::Bytes; | use bytes::Bytes; | ||||
use crate::{ | use crate::{ | ||||
db::{CF_SCRIPT_HISTORY, CF_SCRIPT_HISTORY_NUM_TXS, CF_SCRIPT_UTXO}, | db::{CF_SCRIPT_HISTORY, CF_SCRIPT_HISTORY_NUM_TXS, CF_SCRIPT_UTXO}, | ||||
group::{Group, GroupQuery, MemberItem, UtxoDataValue}, | group::{Group, GroupQuery, MemberItem, UtxoDataValue}, | ||||
io::{ | io::{ | ||||
GroupHistoryConf, GroupHistoryReader, GroupHistoryWriter, | GroupHistoryConf, GroupHistoryReader, GroupHistoryWriter, | ||||
GroupUtxoConf, GroupUtxoReader, GroupUtxoWriter, | GroupUtxoConf, GroupUtxoReader, GroupUtxoWriter, | ||||
Show All 13 Lines | |||||
pub type ScriptUtxoWriter<'a> = GroupUtxoWriter<'a, ScriptGroup>; | pub type ScriptUtxoWriter<'a> = GroupUtxoWriter<'a, ScriptGroup>; | ||||
/// Read the UTXOs of scripts in the DB | /// Read the UTXOs of scripts in the DB | ||||
pub type ScriptUtxoReader<'a> = GroupUtxoReader<'a, ScriptGroup>; | pub type ScriptUtxoReader<'a> = GroupUtxoReader<'a, ScriptGroup>; | ||||
/// Group txs by input/output scripts. | /// Group txs by input/output scripts. | ||||
#[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||
pub struct ScriptGroup; | pub struct ScriptGroup; | ||||
/// Iterator over the scripts of a tx | |||||
#[derive(Debug)] | |||||
pub struct ScriptGroupIter<'a> { | |||||
is_coinbase: bool, | |||||
tx: &'a Tx, | |||||
idx: usize, | |||||
is_outputs: bool, | |||||
} | |||||
impl<'a> Iterator for ScriptGroupIter<'a> { | |||||
type Item = MemberItem<&'a Script>; | |||||
fn next(&mut self) -> Option<Self::Item> { | |||||
if self.is_coinbase && !self.is_outputs { | |||||
return None; | |||||
} | |||||
let idx = self.idx; | |||||
self.idx += 1; | |||||
Some(MemberItem { | |||||
idx, | |||||
member: if self.is_outputs { | |||||
&self.tx.outputs.get(idx)?.script | |||||
} else { | |||||
&self.tx.inputs.get(idx)?.coin.as_ref()?.output.script | |||||
}, | |||||
}) | |||||
} | |||||
} | |||||
impl Group for ScriptGroup { | impl Group for ScriptGroup { | ||||
type Aux = (); | type Aux = (); | ||||
type Iter<'a> = Vec<MemberItem<&'a Script>>; | type Iter<'a> = ScriptGroupIter<'a>; | ||||
type Member<'a> = &'a Script; | type Member<'a> = &'a Script; | ||||
type MemberSer<'a> = Bytes; | type MemberSer<'a> = Bytes; | ||||
type UtxoData = UtxoDataValue; | type UtxoData = UtxoDataValue; | ||||
fn input_members<'a>( | fn input_members<'a>( | ||||
&self, | &self, | ||||
query: GroupQuery<'a>, | query: GroupQuery<'a>, | ||||
_aux: &(), | _aux: &(), | ||||
) -> Self::Iter<'a> { | ) -> Self::Iter<'a> { | ||||
if query.is_coinbase { | ScriptGroupIter { | ||||
return vec![]; | is_coinbase: query.is_coinbase, | ||||
} | tx: query.tx, | ||||
let mut input_scripts = Vec::with_capacity(query.tx.inputs.len()); | idx: 0, | ||||
for (idx, input) in query.tx.inputs.iter().enumerate() { | is_outputs: false, | ||||
if let Some(coin) = &input.coin { | |||||
input_scripts.push(MemberItem { | |||||
idx, | |||||
member: &coin.output.script, | |||||
}); | |||||
} | } | ||||
} | } | ||||
input_scripts | |||||
} | |||||
fn output_members<'a>( | fn output_members<'a>( | ||||
&self, | &self, | ||||
query: GroupQuery<'a>, | query: GroupQuery<'a>, | ||||
_aux: &(), | _aux: &(), | ||||
) -> Self::Iter<'a> { | ) -> Self::Iter<'a> { | ||||
let mut output_scripts = Vec::with_capacity(query.tx.outputs.len()); | ScriptGroupIter { | ||||
for (idx, output) in query.tx.outputs.iter().enumerate() { | is_coinbase: query.is_coinbase, | ||||
if !output.script.is_opreturn() { | tx: query.tx, | ||||
output_scripts.push(MemberItem { | idx: 0, | ||||
idx, | is_outputs: true, | ||||
member: &output.script, | |||||
}); | |||||
} | |||||
} | } | ||||
output_scripts | |||||
} | } | ||||
fn ser_member<'a>(&self, member: &Self::Member<'a>) -> Self::MemberSer<'a> { | fn ser_member<'a>(&self, member: &Self::Member<'a>) -> Self::MemberSer<'a> { | ||||
compress_script_variant(&member.variant()) | compress_script_variant(&member.variant()) | ||||
} | } | ||||
fn tx_history_conf() -> GroupHistoryConf { | fn tx_history_conf() -> GroupHistoryConf { | ||||
GroupHistoryConf { | GroupHistoryConf { | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | fn test_script_group() { | ||||
vec![ | vec![ | ||||
&make_script(vec![0x51]), | &make_script(vec![0x51]), | ||||
&make_script(vec![0x52]), | &make_script(vec![0x52]), | ||||
&make_script(vec![0x53]), | &make_script(vec![0x53]), | ||||
&make_script(vec![0x51]), | &make_script(vec![0x51]), | ||||
], | ], | ||||
); | ); | ||||
assert_eq!( | assert_eq!( | ||||
script_group.input_members(query, &()), | script_group.input_members(query, &()).collect::<Vec<_>>(), | ||||
vec![ | vec![ | ||||
make_member_item(0, &make_script(vec![0x51])), | make_member_item(0, &make_script(vec![0x51])), | ||||
make_member_item(1, &make_script(vec![0x52])), | make_member_item(1, &make_script(vec![0x52])), | ||||
], | ], | ||||
); | ); | ||||
assert_eq!( | assert_eq!( | ||||
script_group.output_members(query, &()), | script_group.output_members(query, &()).collect::<Vec<_>>(), | ||||
vec![ | vec![ | ||||
make_member_item(0, &make_script(vec![0x53])), | make_member_item(0, &make_script(vec![0x53])), | ||||
make_member_item(1, &make_script(vec![0x51])), | make_member_item(1, &make_script(vec![0x51])), | ||||
], | ], | ||||
); | ); | ||||
let query = GroupQuery { | let query = GroupQuery { | ||||
is_coinbase: true, | is_coinbase: true, | ||||
tx: &tx, | tx: &tx, | ||||
}; | }; | ||||
assert_eq!( | assert_eq!( | ||||
tx_members_for_group(&script_group, query, &()).collect::<Vec<_>>(), | tx_members_for_group(&script_group, query, &()).collect::<Vec<_>>(), | ||||
vec![ | vec![ | ||||
&Script::new(vec![0x53].into()), | &Script::new(vec![0x53].into()), | ||||
&Script::new(vec![0x51].into()), | &Script::new(vec![0x51].into()), | ||||
], | ], | ||||
); | ); | ||||
assert_eq!(script_group.input_members(query, &()), vec![]); | |||||
assert_eq!( | assert_eq!( | ||||
script_group.output_members(query, &()), | script_group.input_members(query, &()).collect::<Vec<_>>(), | ||||
vec![], | |||||
); | |||||
assert_eq!( | |||||
script_group.output_members(query, &()).collect::<Vec<_>>(), | |||||
vec![ | vec![ | ||||
make_member_item(0, &make_script(vec![0x53])), | make_member_item(0, &make_script(vec![0x53])), | ||||
make_member_item(1, &make_script(vec![0x51])), | make_member_item(1, &make_script(vec![0x51])), | ||||
], | ], | ||||
); | ); | ||||
assert_eq!( | assert_eq!( | ||||
script_group.ser_member(&&make_script(vec![0x53])), | script_group.ser_member(&&make_script(vec![0x53])), | ||||
[[0x07].as_ref(), &[0x53]].concat(), | [[0x07].as_ref(), &[0x53]].concat(), | ||||
); | ); | ||||
} | } | ||||
} | } |