fuel-core 0.18.3

Fuel client library is aggregation of all fuels service. It contains the all business logic of the fuel protocol.
Documentation
use std::ops::Deref;

use fuel_core_types::{
    blockchain::header::{
        ApplicationHeader,
        ConsensusHeader,
        PartialBlockHeader,
    },
    entities::message::MerkleProof,
    fuel_tx::{
        Script,
        Transaction,
    },
    fuel_types::{
        BlockHeight,
        *,
    },
    tai64::Tai64,
};

use super::*;

const fn txn_id(i: u8) -> Bytes32 {
    Bytes32::new([i; 32])
}

fn receipt(i: Option<u8>) -> Receipt {
    match i {
        Some(i) => {
            let sender = Address::new([i; 32]);
            let recipient = Address::new([i; 32]);
            let amount = 0;
            let nonce = Nonce::new([i; 32]);
            let data = Vec::new();
            Receipt::MessageOut {
                len: 0,
                digest: Bytes32::new([0; 32]),
                sender,
                recipient,
                amount,
                nonce,
                data,
            }
        }
        None => Receipt::Call {
            id: ContractId::new([0; 32]),
            to: ContractId::new([0; 32]),
            amount: 0,
            asset_id: AssetId::new([0; 32]),
            gas: 0,
            param1: 0,
            param2: 0,
            pc: 0,
            is: 0,
        },
    }
}

mockall::mock! {
    pub ProofDataStorage {}
    impl SimpleBlockData for ProofDataStorage {
        fn block(&self, block_id: &BlockId) -> StorageResult<CompressedBlock>;
    }

    impl DatabaseMessageProof for ProofDataStorage {
        fn block_history_proof(
            &self,
            message_block_height: &BlockHeight,
            commit_block_height: &BlockHeight,
        ) -> StorageResult<MerkleProof>;
    }

    impl SimpleTransactionData for ProofDataStorage {
        fn transaction(&self, transaction_id: &TxId) -> StorageResult<Transaction>;
        fn receipts(&self, transaction_id: &TxId) -> StorageResult<Vec<Receipt>>;
    }

    impl MessageProofData for ProofDataStorage {
        fn transaction_status(&self, transaction_id: &TxId) -> StorageResult<TransactionStatus>;
    }
}

#[tokio::test]
async fn can_build_message_proof() {
    use mockall::predicate::*;
    let commit_block_height = BlockHeight::from(2u32);
    let message_block_height = BlockHeight::from(1u32);
    let expected_receipt = receipt(Some(11));
    let message_id = expected_receipt.message_id().unwrap();
    let receipts: [Receipt; 4] = [
        receipt(Some(10)),
        receipt(None),
        receipt(Some(3)),
        expected_receipt,
    ];
    static TXNS: [Bytes32; 4] = [txn_id(20), txn_id(24), txn_id(1), txn_id(33)];
    let transaction_id = TXNS[3];
    let other_receipts: [Receipt; 3] =
        [receipt(Some(4)), receipt(Some(5)), receipt(Some(6))];

    let message_ids: Vec<MessageId> = other_receipts
        .iter()
        .chain(receipts.iter())
        .filter_map(|r| r.message_id())
        .collect();

    let mut data = MockProofDataStorage::new();
    let mut count = 0;

    data.expect_receipts().returning(move |txn_id| {
        if *txn_id == transaction_id {
            Ok(receipts.to_vec())
        } else {
            let r = other_receipts[count..=count].to_vec();
            count += 1;
            Ok(r)
        }
    });

    data.expect_transaction().returning(move |txn_id| {
        let tx = TXNS
            .iter()
            .find(|t| *t == txn_id)
            .map(|_| Script::default().into())
            .ok_or(not_found!("Transaction in `TXNS`"))?;

        Ok(tx)
    });

    let commit_block_header = PartialBlockHeader {
        application: ApplicationHeader {
            da_height: 0u64.into(),
            generated: Default::default(),
        },
        consensus: ConsensusHeader {
            prev_root: Bytes32::zeroed(),
            height: commit_block_height,
            time: Tai64::UNIX_EPOCH,
            generated: Default::default(),
        },
    }
    .generate(&[], &[]);
    let commit_block = CompressedBlock::test(commit_block_header, vec![]);
    let message_block_header = PartialBlockHeader {
        application: ApplicationHeader {
            da_height: 0u64.into(),
            generated: Default::default(),
        },
        consensus: ConsensusHeader {
            prev_root: Bytes32::zeroed(),
            height: message_block_height,
            time: Tai64::UNIX_EPOCH,
            generated: Default::default(),
        },
    }
    .generate(&[], &message_ids);
    let message_block = CompressedBlock::test(message_block_header, TXNS.to_vec());

    let block_proof = MerkleProof {
        proof_set: vec![message_block.id().into(), commit_block.id().into()],
        proof_index: 2,
    };
    data.expect_block_history_proof()
        .once()
        .with(
            eq(message_block_height),
            eq(commit_block_height - 1u32.into()),
        )
        .returning({
            let block_proof = block_proof.clone();
            move |_, _| Ok(block_proof.clone())
        });

    let message_block_id = message_block.id();
    data.expect_transaction_status()
        .with(eq(transaction_id))
        .returning(move |_| {
            Ok(TransactionStatus::Success {
                block_id: message_block_id,
                time: Tai64::UNIX_EPOCH,
                result: None,
            })
        });

    data.expect_block().times(2).returning({
        let commit_block = commit_block.clone();
        let message_block = message_block.clone();
        move |block_id| {
            let block = if &commit_block.id() == block_id {
                commit_block.clone()
            } else if &message_block.id() == block_id {
                message_block.clone()
            } else {
                panic!("Should request any other block")
            };
            Ok(block)
        }
    });

    let data: Box<dyn MessageProofData> = Box::new(data);

    let proof =
        message_proof(data.deref(), transaction_id, message_id, commit_block.id())
            .unwrap()
            .unwrap();
    assert_eq!(proof.message_id(), message_id);
    assert_eq!(
        proof.message_block_header.message_receipt_root,
        message_block.header().message_receipt_root
    );
    assert_eq!(
        proof.message_block_header.height(),
        message_block.header().height()
    );
    assert_eq!(
        proof.commit_block_header.height(),
        commit_block.header().height()
    );
    assert_eq!(proof.block_proof, block_proof);
}