use alloc::vec::Vec;
use core::{
mem,
ops::Index,
};
use fuel_asm::PanicReason;
use fuel_merkle::binary::root_calculator::MerkleRootCalculator as MerkleTree;
use fuel_tx::Receipt;
use fuel_types::{
Bytes32,
canonical::Serialize,
};
use crate::{
error::SimpleResult,
prelude::{
Bug,
BugVariant,
},
};
#[derive(Debug, Default, Clone)]
pub struct ReceiptsCtx {
receipts: Vec<Receipt>,
receipts_tree: MerkleTree,
}
impl ReceiptsCtx {
pub const MAX_RECEIPTS: usize = u16::MAX as usize;
pub fn push(&mut self, receipt: Receipt) -> SimpleResult<()> {
if self.receipts.len() == Self::MAX_RECEIPTS {
return Err(Bug::new(BugVariant::ReceiptsCtxFull).into())
}
if (self.receipts.len() == Self::MAX_RECEIPTS - 1
&& !matches!(receipt, Receipt::ScriptResult { .. }))
|| (self.receipts.len() == Self::MAX_RECEIPTS - 2
&& !matches!(
receipt,
Receipt::ScriptResult { .. } | Receipt::Panic { .. }
))
{
return Err(PanicReason::TooManyReceipts.into())
}
self.receipts_tree.push(receipt.to_bytes().as_slice());
self.receipts.push(receipt);
Ok(())
}
pub fn clear(&mut self) {
self.receipts_tree = MerkleTree::new();
self.receipts.clear();
}
pub fn len(&self) -> usize {
self.receipts.len()
}
pub fn is_empty(&self) -> bool {
self.receipts.len() == 0
}
pub fn root(&self) -> Bytes32 {
self.receipts_tree.clone().root().into()
}
pub fn lock(&mut self) -> ReceiptsCtxMut<'_> {
ReceiptsCtxMut::new(self)
}
fn recalculate_root(&mut self) {
self.receipts_tree = MerkleTree::new();
for receipt in &self.receipts {
self.receipts_tree.push(receipt.to_bytes().as_slice())
}
}
}
impl Index<usize> for ReceiptsCtx {
type Output = Receipt;
fn index(&self, index: usize) -> &Self::Output {
&self.as_ref()[index]
}
}
impl AsRef<Vec<Receipt>> for ReceiptsCtx {
fn as_ref(&self) -> &Vec<Receipt> {
&self.receipts
}
}
impl PartialEq for ReceiptsCtx {
fn eq(&self, other: &Self) -> bool {
self.root() == other.root()
}
}
impl Eq for ReceiptsCtx {}
#[cfg(any(test, feature = "test-helpers"))]
impl From<Vec<Receipt>> for ReceiptsCtx {
fn from(receipts: Vec<Receipt>) -> Self {
let mut ctx = Self::default();
for receipt in receipts {
ctx.push(receipt).expect("Too many receipts");
}
ctx
}
}
impl From<ReceiptsCtx> for Vec<Receipt> {
fn from(mut ctx: ReceiptsCtx) -> Self {
mem::take(&mut ctx.receipts)
}
}
pub struct ReceiptsCtxMut<'a> {
receipts_ctx: &'a mut ReceiptsCtx,
}
impl<'a> ReceiptsCtxMut<'a> {
pub fn new(receipts_ctx: &'a mut ReceiptsCtx) -> Self {
Self { receipts_ctx }
}
pub fn receipts_mut(&mut self) -> &mut Vec<Receipt> {
&mut self.receipts_ctx.receipts
}
}
impl Drop for ReceiptsCtxMut<'_> {
fn drop(&mut self) {
self.receipts_ctx.recalculate_root()
}
}
#[cfg(test)]
mod tests {
use crate::{
crypto::ephemeral_merkle_root,
interpreter::receipts::ReceiptsCtx,
};
use core::iter;
use fuel_tx::Receipt;
use fuel_types::canonical::Serialize;
use alloc::vec::Vec;
fn create_receipt() -> Receipt {
Receipt::call(
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
)
}
#[test]
fn root_returns_merkle_root_of_pushed_receipts() {
let mut ctx = ReceiptsCtx::default();
let receipts = iter::repeat_n(create_receipt(), 5);
for receipt in receipts.clone() {
ctx.push(receipt).expect("context not full");
}
let root = ctx.root();
let leaves = receipts
.map(|receipt| receipt.to_bytes())
.collect::<Vec<_>>()
.into_iter();
let expected_root = ephemeral_merkle_root(leaves);
assert_eq!(root, expected_root)
}
#[test]
fn root_returns_merkle_root_of_directly_modified_receipts() {
let mut ctx = ReceiptsCtx::default();
let receipts = iter::repeat_n(create_receipt(), 5);
{
let mut ctx_mut = ctx.lock();
*ctx_mut.receipts_mut() = receipts.clone().collect();
}
let root = ctx.root();
let leaves = receipts
.map(|receipt| receipt.to_bytes())
.collect::<Vec<_>>()
.into_iter();
let expected_root = ephemeral_merkle_root(leaves);
assert_eq!(root, expected_root)
}
}