use chia_sdk_types::MerkleTree;
use chia_sha2::Sha256;
use serde::{Deserialize, Serialize};
use crate::constants::EMPTY_ROOT;
use crate::primitives::Bytes32;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReceiptStatus {
Success = 0,
InsufficientBalance = 1,
InvalidNonce = 2,
InvalidSignature = 3,
AccountNotFound = 4,
Failed = 255,
}
impl ReceiptStatus {
#[inline]
#[must_use]
pub const fn as_u8(self) -> u8 {
self as u8
}
#[must_use]
pub fn from_u8(byte: u8) -> Self {
match byte {
0 => Self::Success,
1 => Self::InsufficientBalance,
2 => Self::InvalidNonce,
3 => Self::InvalidSignature,
4 => Self::AccountNotFound,
255 => Self::Failed,
_ => Self::Failed,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Receipt {
pub tx_id: Bytes32,
pub block_height: u64,
pub tx_index: u32,
pub status: ReceiptStatus,
pub fee_charged: u64,
pub post_state_root: Bytes32,
pub cumulative_fees: u64,
}
impl Receipt {
pub fn new(
tx_id: Bytes32,
block_height: u64,
tx_index: u32,
status: ReceiptStatus,
fee_charged: u64,
post_state_root: Bytes32,
cumulative_fees: u64,
) -> Self {
Self {
tx_id,
block_height,
tx_index,
status,
fee_charged,
post_state_root,
cumulative_fees,
}
}
}
#[must_use]
pub fn compute_receipts_root(receipts: &[Receipt]) -> Bytes32 {
if receipts.is_empty() {
return EMPTY_ROOT;
}
let hashes: Vec<Bytes32> = receipts
.iter()
.map(|r| {
let bytes =
bincode::serialize(r).expect("Receipt bincode serialization should not fail");
let mut hasher = Sha256::new();
hasher.update(&bytes);
Bytes32::new(hasher.finalize())
})
.collect();
MerkleTree::new(&hashes).root()
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReceiptList {
pub receipts: Vec<Receipt>,
pub root: Bytes32,
}
impl Default for ReceiptList {
fn default() -> Self {
Self::new()
}
}
impl ReceiptList {
#[must_use]
pub fn new() -> Self {
Self {
receipts: Vec::new(),
root: EMPTY_ROOT,
}
}
#[must_use]
pub fn from_receipts(receipts: Vec<Receipt>) -> Self {
let root = compute_receipts_root(&receipts);
Self { receipts, root }
}
pub fn push(&mut self, receipt: Receipt) {
self.receipts.push(receipt);
}
pub fn finalize(&mut self) {
self.root = compute_receipts_root(&self.receipts);
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&Receipt> {
self.receipts.get(index)
}
#[must_use]
pub fn get_by_tx_id(&self, tx_id: Bytes32) -> Option<&Receipt> {
self.receipts.iter().find(|r| r.tx_id == tx_id)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.receipts.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.receipts.is_empty()
}
#[must_use]
pub fn success_count(&self) -> usize {
self.receipts
.iter()
.filter(|r| matches!(r.status, ReceiptStatus::Success))
.count()
}
#[must_use]
pub fn failure_count(&self) -> usize {
self.receipts
.iter()
.filter(|r| !matches!(r.status, ReceiptStatus::Success))
.count()
}
#[must_use]
pub fn total_fees(&self) -> u64 {
self.receipts.iter().map(|r| r.fee_charged).sum()
}
}