use crate::chaindef::OutPointHash;
use crate::chaindef::TokenID;
use crate::store::Row;
use crate::store::{db_decode, db_encode};
use bitcoincash::hashes::Hash;
use super::DBRow;
const TOKENOUTPUTINDEX_CF: &str = "tokenoutput";
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
pub struct TokenOutputIndexKey {
pub parent_token_id: [u8; 32],
pub subgroup_blob: Vec<u8>,
outpoint_hash: [u8; 32],
}
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
pub struct TokenOutputIndexRow {
key: TokenOutputIndexKey,
}
#[cfg(nexa)]
fn get_parent_id(token_id: &TokenID) -> [u8; 32] {
token_id.parent_id()
}
#[cfg(bch)]
fn get_parent_id(token_id: &TokenID) -> [u8; 32] {
token_id.into_inner()
}
impl TokenOutputIndexRow {
#[cfg(nexa)]
pub fn new(token_id: TokenID, outpoint_hash: &OutPointHash) -> Self {
let (parent, sub) = token_id.into_parent_and_subgroup();
Self {
key: TokenOutputIndexKey {
parent_token_id: parent,
subgroup_blob: sub,
outpoint_hash: outpoint_hash.into_inner(),
},
}
}
#[cfg(bch)]
pub fn new(token_id: TokenID, commitment: Vec<u8>, outpoint_hash: &OutPointHash) -> Self {
Self {
key: TokenOutputIndexKey {
parent_token_id: token_id.into_inner(),
subgroup_blob: commitment,
outpoint_hash: outpoint_hash.into_inner(),
},
}
}
#[cfg(nexa)]
pub fn filter_by_parent_and_subgroup(token_id: &TokenID) -> Vec<u8> {
use crate::store::db_encode;
let (parent, subgroup) = &token_id.clone().into_parent_and_subgroup();
db_encode(&parent)
.into_iter()
.chain(db_encode(subgroup))
.flatten()
.collect()
}
#[cfg(bch)]
pub fn filter_by_parent_and_subgroup(token_id: &TokenID, commitment: &[u8]) -> Vec<u8> {
let parent: [u8; 32] = get_parent_id(token_id);
db_encode(&parent)
.into_iter()
.chain(db_encode(commitment))
.flatten()
.collect()
}
pub fn filter_by_parent_including_subroup(parent: &TokenID) -> Vec<u8> {
let parent: [u8; 32] = get_parent_id(parent);
db_encode(&parent).into_iter().flatten().collect()
}
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(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)
}
#[inline]
pub fn is_subtoken(&self) -> bool {
!self.key.subgroup_blob.is_empty()
}
pub fn into_parent_and_subgroup(self) -> ([u8; 32], Vec<u8>) {
(self.key.parent_token_id, self.key.subgroup_blob)
}
}
impl DBRow for TokenOutputIndexRow {
const CF: &'static str = TOKENOUTPUTINDEX_CF;
fn to_row(&self) -> Row {
Row {
key: db_encode(&self.key).unwrap().into_boxed_slice(),
value: vec![].into_boxed_slice(),
}
}
fn from_row(row: &Row) -> Self {
Self {
key: db_decode(&row.key).expect("failed to parse TokenOutputIndexKey"),
}
}
}
#[cfg(test)]
mod tests {
#[cfg(nexa)]
use super::*;
#[cfg(nexa)]
use crate::nexa::token::PARENT_GROUP_ID_LENGTH;
#[cfg(nexa)]
#[test]
fn test_parent_to_from_row() {
let row =
TokenOutputIndexRow::new(TokenID::from_inner([0xaa; 32]), &OutPointHash::hash(&[11]));
let decoded_row = TokenOutputIndexRow::from_row(&row.to_row());
assert_eq!(row, decoded_row);
}
#[cfg(nexa)]
#[test]
fn test_subgroup_to_from_row() {
let row = TokenOutputIndexRow::new(
TokenID::from_vec(vec![0xbb; 256]).unwrap(),
&OutPointHash::hash(&[11]),
);
let decoded_row = TokenOutputIndexRow::from_row(&row.to_row());
assert_eq!(row, decoded_row);
}
#[cfg(nexa)]
#[test]
fn test_filter_by_tokenid() {
let parent = [0xaa; PARENT_GROUP_ID_LENGTH];
let sub = vec![0xbb; 42];
let token_id_parent = TokenID::from_inner(parent);
let token_id_subgroup = TokenID::from_parent_and_subgroup(parent, sub);
let parent_row =
TokenOutputIndexRow::new(token_id_parent.clone(), &OutPointHash::hash(&[11])).to_row();
let sub_row =
TokenOutputIndexRow::new(token_id_subgroup.clone(), &OutPointHash::hash(&[22]))
.to_row();
let filter_parent: Vec<u8> =
TokenOutputIndexRow::filter_by_parent_and_subgroup(&token_id_parent);
let filter_parent_prefix: Vec<u8> =
TokenOutputIndexRow::filter_by_parent_including_subroup(&token_id_parent);
let filter_parent_and_subgroup: Vec<u8> =
TokenOutputIndexRow::filter_by_parent_and_subgroup(&token_id_subgroup);
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(&parent_row, &filter_parent));
assert!(starts_with(&parent_row, &filter_parent_prefix));
assert!(!starts_with(&sub_row, &filter_parent));
assert!(starts_with(&sub_row, &filter_parent_prefix));
assert!(starts_with(&sub_row, &filter_parent_and_subgroup))
}
}