Disclaimer: These are planning documents. The functionalities described here may be unimplemented, partially implemented, or implemented differently than the original design.

Libsawtooth Receipt Store

Summary

This document discusses the current purpose and design of the receipt store in libsawtooth as well as proposes a new ReceiptStore trait.

Existing Receipt Store

The purpose of the receipt store in libsawtooth is to store transaction receipts. Transaction receipts contain information related to the execution of a transaction. The receipt store allows for information related to the execution of a transaction that should not be stored in state to be saved to an underlying database.

The TransactionReceiptStore is a struct that is a wrapper around an OrderedStore. The OrderedStore is a key value store indexed by a type. The receipt store uses transaction ID as the key, transaction receipt as the value and is indexed by u64s. The OrderedStore trait is implemented for various backends including LMDB, redis and btree. The OrderedStore maintains the order of transactions by storing receipts in order of execution. Receipt store methods include: fetching and deleting receipts by ID or index, adding new receipts, counting the total number of receipts, iterating over all receipts and iterating over all receipts added after a specified receipt.

Proposed ReceiptStore trait

A new ReceiptStore trait should have methods for retrieving and deleting receipts by both index and ID, adding receipts, retrieving the total number of receipts and listing all receipts since a specified receipt. Separate methods for listing all receipts and listing receipts since a specified receipt are not needed because the list_receipts_since method will handle both cases. To include methods for retrieving and deleting receipts by index as well as to maintain order, the database tables in implementations of this trait will be required to include an attribute in addition to transaction_id and transaction_result to represent the index. The proposed trait will be implemented with LMDB and be able to read from an existing LMDB receipt store to maintain backwards compatability. The trait will also be implemented with SQLite and PostgreSQL backends. This design follows the pattern of stores in Splinter.

Guide-level explanation

The new receipt store trait is a trait that covers all the capabilities of the existing receipt store and allows for multiple back-end implementations. Planned implementations include:

  • LMDB
  • SQLite
  • PostgreSQL

Reference-level explanation

Proposed ReceiptStore Trait

/// Interface for performing CRUD operations on transaction receipts
pub trait ReceiptStore {
    /// Retrieves the receipt with the given ID from underlying storage
    ///
    /// # Arguments
    ///
    /// * `id` - The ID of the transaction receipt to be retrieved
    fn get_txn_receipt_by_id(
        &self,
        id: String,
    ) -> Result<Option<TransactionReceipt>, TransactionReceiptStoreError>;

    /// Retrieves the receipt at the given index from underlying storage
    ///
    /// # Arguments
    ///
    /// * `index` - The index of the transaction receipt to be retrieved
    fn get_txn_receipt_by_index(
        &self,
        index: u64,
    ) -> Result<Option<TransactionReceipt>, TransactionReceiptStoreError>;

    /// Adds transaction receipts to the underlying storage
    ///
    /// # Arguments
    ///
    /// * `receipts` - A vector of the transaction receipts to be added
    fn add_txn_receipts(
        &self,
        receipts: Vec<TransactionReceipt>,
    ) -> Result<(), TransactionReceiptStoreError>;

    /// Removes the transaction receipt with the given ID from underlying storage
    ///
    /// # Arguments
    ///
    /// * `id` - The ID of the transaction receipt to be removed
    fn remove_txn_receipt_by_id(
        &self,
        id: String,
    ) -> Result<(), TransactionReceiptStoreError>;

    /// Removes the transaction receipt at the given index from underlying storage
    ///
    /// # Arguments
    ///
    ///  * `index` - The index of the transaction receipt to be removed
    fn remove_txn_receipt_by_index(
        &self,
        index: u64,
    ) -> Result<(), TransactionReceiptStoreError>;

    /// Gets the total number of transaction receipts
    fn count_txn_receipts(&self) -> Result<u32, TransactionReceiptStoreError>;

    /// List transaction receipts that have been added to the store since the
    /// provided ID
    ///
    /// # Arguments
    ///
    /// * `id` - The transaction ID of the receipt preceding the receipts to be
    ///          listed
    fn list_receipts_since(
        &self,
        id: String,
    ) ->  Result<Box<dyn ExactSizeIterator<Item = TransactionReceipt>>, TransactionReceiptStoreError>;
}

Prior art

The design for this trait and future implementation is based on stores in Splinter.