use crate::chaindef::OutPointHash;
use crate::chaindef::TokenID;
use crate::store::db_decode;
use crate::store::db_encode;
use crate::store::Row;
use bitcoincash::hashes::Hash;
use super::DBRow;
const OUTPUTTOKENINDEX_CF: &str = "outputtoken";
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
pub struct OutputTokenIndexKey {
pub outpoint_hash: [u8; 32],
pub parent_token_id: [u8; 32],
pub subgroup_blob: Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
pub struct OutputTokenIndexRow {
key: OutputTokenIndexKey,
token_amount: u64,
}
impl OutputTokenIndexRow {
#[cfg(nexa)]
pub fn new(outpoint_hash: &OutPointHash, token_id: TokenID, token_amount: i64) -> Self {
let (parent, sub) = token_id.into_parent_and_subgroup();
Self {
key: OutputTokenIndexKey {
outpoint_hash: outpoint_hash.into_inner(),
parent_token_id: parent,
subgroup_blob: sub,
},
token_amount: token_amount as u64,
}
}
#[cfg(bch)]
pub fn new(
outpoint_hash: &OutPointHash,
token_id: TokenID,
commitment: Vec<u8>,
token_amount: i64,
) -> Self {
Self {
key: OutputTokenIndexKey {
outpoint_hash: outpoint_hash.into_inner(),
parent_token_id: token_id.into_inner(),
subgroup_blob: commitment,
},
token_amount: token_amount as u64,
}
}
pub fn filter_by_outpointhash(hash: &OutPointHash) -> Vec<u8> {
hash.to_vec()
}
#[cfg(nexa)]
pub fn filter_by_outpointhash_and_token(hash: &OutPointHash, token_id: TokenID) -> Vec<u8> {
let (parent, sub) = token_id.into_parent_and_subgroup();
db_encode(&OutputTokenIndexKey {
outpoint_hash: hash.into_inner(),
parent_token_id: parent,
subgroup_blob: sub,
})
.unwrap()
}
pub fn outpoint_hash(&self) -> OutPointHash {
OutPointHash::from_inner(self.key.outpoint_hash)
}
#[cfg(nexa)]
pub fn token_id(&self) -> TokenID {
TokenID::from_parent_and_subgroup(self.key.parent_token_id, self.key.subgroup_blob.clone())
}
#[cfg(bch)]
pub fn token_id(&self) -> TokenID {
TokenID::from_inner(self.key.parent_token_id)
}
#[cfg(nexa)]
pub fn into_token_id(self) -> TokenID {
TokenID::from_parent_and_subgroup(self.key.parent_token_id, self.key.subgroup_blob)
}
#[cfg(bch)]
pub fn into_token_id(self) -> TokenID {
TokenID::from_inner(self.key.parent_token_id)
}
#[cfg(bch)]
pub fn into_token_id_and_commitment(self) -> (TokenID, Vec<u8>) {
(
TokenID::from_inner(self.key.parent_token_id),
self.key.subgroup_blob,
)
}
pub fn token_amount(&self) -> i64 {
self.token_amount as i64
}
pub fn into_parent_and_subgroup(self) -> ([u8; 32], Vec<u8>) {
(self.key.parent_token_id, self.key.subgroup_blob)
}
}
impl DBRow for OutputTokenIndexRow {
const CF: &'static str = OUTPUTTOKENINDEX_CF;
fn to_row(&self) -> Row {
Row {
key: db_encode(&self.key).unwrap().into_boxed_slice(),
value: db_encode(&self.token_amount).unwrap().into_boxed_slice(),
}
}
fn from_row(row: &Row) -> Self {
Self {
key: db_decode(&row.key).unwrap(),
token_amount: db_decode(&row.value).unwrap(),
}
}
}
#[cfg(test)]
#[cfg(nexa)]
mod tests {
use bitcoin_hashes::hex::ToHex;
use super::*;
#[test]
fn test_to_from_row() {
let row = OutputTokenIndexRow::new(
&OutPointHash::hash(&[42]),
TokenID::from_inner([0xAA; 32]),
42,
);
let decoded_row = OutputTokenIndexRow::from_row(&row.to_row());
assert_eq!(row, decoded_row);
}
#[test]
fn test_filter_by_outpointhash_and_token() {
let token_a = TokenID::from_inner([0xAA; 32]);
let token_b = TokenID::from_inner([0xBB; 32]);
let outpointhash = OutPointHash::hash(&[42]);
let row_with_token_a =
OutputTokenIndexRow::new(&outpointhash, token_a.clone(), 42).to_row();
let row_with_token_b =
OutputTokenIndexRow::new(&outpointhash, token_b.clone(), 24).to_row();
let filter_token_a =
OutputTokenIndexRow::filter_by_outpointhash_and_token(&outpointhash, token_a);
let all = OutputTokenIndexRow::filter_by_outpointhash(&outpointhash);
let starts_with = |a: &Row, b: &Vec<u8>| -> bool {
a.key.to_vec().iter().zip(b.iter()).all(|(x, y)| x == y)
};
assert!(starts_with(&row_with_token_a, &filter_token_a));
assert!(!starts_with(&row_with_token_b, &filter_token_a));
assert!(starts_with(&row_with_token_a, &all));
assert!(starts_with(&row_with_token_b, &all));
}
#[test]
fn test_filter_matches_key() {
let token = TokenID::from_inner([0xAA; 32]);
let outpointhash = OutPointHash::hash(&[42]);
let row = OutputTokenIndexRow::new(&outpointhash, token.clone(), 42).to_row();
let filter = OutputTokenIndexRow::filter_by_outpointhash_and_token(&outpointhash, token);
assert_eq!(row.key.to_vec().to_hex(), filter.to_hex());
}
}