---
# System prepended metadata

title: Designing Core Components for API

---

# Designing Core Components for API

## Data structures

three primary data structures that form the foundational components: `Round`, `SnapShot` and `SnapShotData`.
 At this stage, we have not factored in considerations related to Authentication and Security.

### Round

Each round has several snapshots that are considered with block finalization of various chains.

```Rust!
struct Round {
    round: usize,
    snapshots: Vec<SnapShot>,                     // candidate snapshots
    committed_snapshots: Option<Vec<SnapshotId>>, // this may replaced by index of snapshot
    
    // Metadata
    roundstatus: RoundStatus,
}

type SnapshotId = String;

enum RoundStatus {
    NotStarted,  // Round hasn't begun yet
    InProgress,  // At least one snapshot started commit period
    Complete     // All snapshot commit time period end.
}

```

Having multiple candidates could be useful for proof generation even when some chains have not been finalized. For instance, if there are two blocks at the same height, we can proceed by generating two separate snapshots that each contain one of the candidate blocks.

I anticipate that in most scenarios, we can expect the same snapshot in both the `snapshot` and `committed_snapshot` fields.

Please remember that if we have a limit on the number of assets we can handle in a single snapshot, we might need to commit several snapshots to `SummaSC` to cover all types of assets.

### Snapshot

I believe the system will be managing Summa on a snapshot basis.
After consolidating the `chain_hashes`, we could generate a unique `id` for each snapshot

```Rust!

struct SnapShot {
    id: String,
    round_num: usize,
    status: SnapShotStatus,
    timestamp: Instant,     // can be called snapshot time `t`
    chain_hashes: Vec<ChainHash>, // the order of ChainHash might important if we use this for metatdata of the balances on the snapshot. 
    
    // data for committing to the `SummaSC`
    round_info: Option<RoundInfo>,
}

enum SnapShotStatus {
    Initializing, // newly created and waiting for calculations, commit hash and others. 
    Initialized,  // Ready to commit, most of evaluataion be done, except generating proofs.
    Submitted,    // For commit, sent Tx to the `SummaSC` wait for confirmation.
    CommitFailed, // The commit transaction reverted.

    Committed, // Should be shared Signed Account Data to each user, may can start generating proofs internally.
    FinalizeFailed, // There's a possibility that proof generation could fail after a commitment has been made.
    Finalized, // commit period closed and ready to share proofs
}


struct ChainHash {
    name: String,
    blockhash: String,
    timestamp: usize,  // block time
    finalized: bool,   // This flag may differently calculated by policy of CEX
}

struct RoundInfo {
    // possible to change `[u8; 32]` instead `String`
    commit_hash: String,
    eth_block_number: u64,
    signing_pubkey: String,
    commitment_time_period: u64,
    // usdc_block_hash: String,
    // more generalize data type for this.
    asset_hashes: Vec<String>,
}

impl SnapShot {
    pub fn new(round_id: usize, snapshot_id: String, chainhashes: Vec<ChainHash>) -> Self {
        // Self
        // starting with `Initializing` status.
    }
    
    pub fn generate_roundinfo(&self, commit_hash: String, eth_block_number: u64, signing_pubkey: String, commitperiod: usize) -> Result<(), Err> {
        // This function is responsible for updating the round information with `mst commitment` in V1.
        //
        // The existence of a `SnapShotData` instance signifies that all necessary computations for `mst_commitment` are complete, and it's ready to be used here.
        //
        // Initialize `SnapShotData`
        
        let mut asset_hashes = vec![];
        for asset in self.chain_hashes {
            asset_hash.push(asset.blockhash)
        }
        self.round_info = RoundInfo {
            // ...
            asset_hashes
        }
    }
    
    pub fn get_roundinfo(&self) -> RoundInfo {
        // for generating `startRound` transaction
        return self.RoundInfo
    }
    
    // For inputs, should fetch data from `SnapShotData`
    pub fn get_public_input(&self, assets: &Vec<Asset>, signatures: &Vec<String>, account_data_hashes: &Vec<String>) {
        // Verify that the names in `ChainHashes` match the names in the `assets`.
        // Other verificaion steps are in here. i.e verifying signatures with account_data_hashes
        // return data for `verifyProofOfSolvency` tx
        
        // struct PublicInput {
        //    bytes32[] cexBTCPubKey; 
        //    address[] cexETHPubKey;
        //    uint[] cexBTCBalances;
        //    uint[] cexETHBalances;
        //    bytes[] signatures;
        //    bytes32 mstRoot;
        //    bytes32[] accountDataHash;
        //    bytes32 btcBlockHash;
        //    uint ethBlockNumber;
        //    uint timestamp;
        //    uint round;
        // }
        
        // the PublicInput not specified yet
        let public_input = PublicInput {
            // Asset data from assets
            // Asset1_block_hash
            // Asset2_block_hash
            // Asset1_PubKey
            // Asset2_PubKey
            // Asset1_Balances
            // Asset2_Balances
            signatures,
            account_data_hashes,
            mst_root: self.round_info.commit_hash,
            eth_block_number: self.round_info.eth_block_number,
            timestamp: self.timestamp,
            round: self.round_num,
        }
    }
}
```

In the `roundinfo`, It should be included root of mst for V1. And includes block hashes.

```sequence
Title: CEX operator flow for SummaSC
participant SnapShotData
participant SnapShot
participant Signer
participant SummaSC

SnapShot->SnapShotData: generate_roundinfo()
SnapShotData->SnapShot: assets, commit_hash, entries
SnapShot->Signer: get_round_info()
Note over Signer: generate `startRound` tx
Signer->SummaSC: startRound
SnapShot->SnapShotData: generate_proofs()
Note over SnapShotData: Proof generation time \nmay increase with more users.
SnapShot->SnapShotData: get_all_proofs()
SnapShotData->SnapShot: proofs
SnapShot->Signer: get_public_info()
Signer->SummaSC: verifyProofOfSolvency
```

Note that this workflow may change if proof of inclusion is involved.

The signer can be internal or external of the `SnapShot` instance.

### SnapShotData

The actual data can be managed directly within `SnapShotData`. This component will continue running even after the `SnapShot` cycle is complete. However, for the early version of the Backend, it might be a good option to place this inside the `SnapShot`.

This component role can be like these
- Aggregating User Data
- Signing Data
- Generating Proofs

For simplicity, the `entries` and `proofs` can be stored for the user as well as for the contract.

```Rust!
struct SnapShotData<const N_ASSETS: usize> {
    snapshot_id: String,
    commit_hash: String, // i.e root of MST at V1
    entries: HashMap<Name, Entry<N_ASSETS>>,
    assets: Vec<Asset>
    // the Name in `proofs` can be use for user and contracts.
    // the proofs may not be as Option, can be initalized 
    proofs: Option<HashMap<Name, Proof>>, 
    // can be define additional data for optimization
}

type Name = String;

// this structure may change in future
struct Asset {
    name: String,
    pubkeys: Vec<String>,
    balances: Vec<BigUint>,
}

// Consider storing this data in a database for improved efficiency and faster data retrieval.
struct SignedAccountData<const N_ASSETS: usize> {
    // for signature inputs
    hash: String,
    username: String,
    balances: [BigUint; N_ASSETS],
    // metadata are same for all users
    meta_data: Vec<u8>, // i.e contract address, round_id etc..
}

struct Proof {
    // depends on the circuit
    // The type T can be about a User or a Contract  
}

impl SnapShotData<const N_ASSETS: usize> {
    fn new(commit_hash: &str, snapshot_id: u64, entries: Vec<Entry>, assets: Vec<Asset>) -> SnapShotData {
        // this can be consider MstInclusion & Solvency Circuit
        // For the Solvency Circuit, we have to store panu
    }
    // for SnapShot operator
    fn generate_proofs(&self) {
        // running backend for generating proofs for each users
        // may consider KGZ
        for e in entries {
            // the proof for each user and also generate for contract with specified name
            self.proofs.insert(e.username, proof)
        }
        
    }
    
    // for serving `SAD` to user
    fn get_sad(&self, username: &str) -> SignedAccountData<N_ASSETS> {
        // ...
        let balances = self.entries[username].balances;
        let message = create_message_from_entry(self.entries[username];
        let hash = ethers.sign_message(message);
        
        SignedAccountData {
            hash,
            username: username.to_string(),
            balances,
            meta_data: vec![], // placeholder,
        }
    }
    
    // for serving proof by name
    fn get_proof(&self, name: &str) -> Proof {}
}
```

A `SnapshotData` instance can be created once the `Snapshot` is initialized.


#### Prepare entries - initializing snapshot


There are two approaches for checking identity inclusion.

- Merkle sum tree
- KGZ commitment

Certainly, we might need a wrapper to handle the two types of inclusion-checking schemes: Merkle Sum Trees and KGZ. This wrapper would maintain all entries in memory (or a database), waiting until data is flushed to the proof generator.

If the private key for signing is not important, the `SignedEntry` may not need in `PrepareEntries`. It is because I expect that more than half of Signed message does not use in the commit phase at least. There is more efficient in generating a signed message when calling `get_entry`.


```Rust!

struct PrepareEntries<const N_ASSETS: usize> {
    snapshotid: String,
    entries: Vec<SignedEntry<N_ASSETS>>,
    #[cfg(mode = "mst")]
    depth: Option<usize>
}

enum CsvForm {
    Path(String),
    Bytes(Vec<u8>)
}

impl PrepareEntries {
    fn new(snapshotid: String) -> Result<(), ()> {
        //  depth will be calculated in here
        
    }
    
    // update entries with csv file path
    pub fn from_csv(&self, snapshotid: String, source: CsvForm) -> Result<(), ()> {
        match CsvForm {
            ...
        }
    }

    pub fn flush() -> Self {
        // depends on which inclusion return proper type
    }
}
```

this method could be bound `Snapshot` or `SnapshotData`

## Interaction

I presume that users can retrieve their data(i.e SAD, proofs and others) from the CEX through its web platform.

In this section, I'm outlining middleware modules for the development of a higher-level API in the future.

### Verification

There are two verification steps that take place on the user side, both of which occur off-chain.

- Verification for Signed Account Data(SAD)   
    - Verify `hash` with public key
- Verification for Proofs
    - Verify `leaf_hash` with balances.
    - Verify `timestamp` in `RoundInfo`.

Firstly, if the user believes their `SAD` is accurate, they can opt to commit it to the `SummaSC`, and they should retain the `SAD` for later verification of the `leaf_hash`.

Secondly, when the user receives the proof for their round, they need to verify the `leaf_hash` and `timestamp` in the `RoundInfo`. To verify the `leaf_hash`, the user requires the balances from the `SAD` of the round that matches the one in the `RoundInfo`. Additionally, they need to ensure that the `timestamp` in the `RoundInfo` corresponds to the nearest block for that timestamp. For this, the user need API key(s) that query on chain data.

#### SAD & Commit

The user is required to store their entry, which is the Signed Account Data generated by the CEX. They must also manage their entries and proofs for verifying it accuracy and committing it to on-chain.

```Rust!

struct CommittedEntry {
    txhash: Option<String>;
    contract_addres: String, // This field might not be necessary once the message specification is defined
    round_id: u64,
    signed_entry_hash: String,
    signed_entry: SignedEntry
    
}

struct MyEntries {
    // Entries
    unverified_entries: Vec<SignedEntry>,
    verified_entries: Vec<SignedEntry>,
    pending_entries: Vec<CommittedEntry>,     // send tx for committing, but not included in finalized block.
    committed_entries: Vec<CommittedEntry>,
}

impl MyEntries {
    pub fn verify_entry(unverified_entry: SignedEntry) {
        // verify signature, if valid then move it to `verified_entries`
    }
    
    pub fn generate_commit(entry: SignedEntry) ->  {
        // if possible, we can generate raw transaction for committing to target round
        // or return data for generating commit transaction to High level API(i.e metamask)
    }
}

```

#### Proofs 

After committing phase, the CEX should be finished generating proofs for serving data to users.

```Rust!
// Serving proof data from CEX to users
struct ProofData {
    snapshot_id: String,
    contract_address: String,
    username: String,  // this may different text inside the proofdata.
    proofdata: Vec<u8>,
    // these fields below, would be possible to get parsing proofdata, it depends on proofdata specified
    
}
```

User can store all proofs on their device.

```Rust!
struct ProofData {
    round_info: 
    proof_data: Vec<u8>,  // Includ public inputs
}

struct MyProofs {
    // TBD
}
```
