Synopsis

The Bimodal ledger is the core data-structure holding account information. The checksum aggregates all the ledger and makes it straightforward to audit and verify its content.

Motivation

The user accounts need to be structured and aggregated in a deterministic manner for the NOCUST periodic commitment.

Definitions

  • Ti,jX(e)\mathfrak{T}_{i,j}^X(e): Transfer from PiX(e)\mathbb{P}_i^X(e) to PjX(e)\mathbb{P}_j^X(e) of asset token XX during eon ee
  • XiX,Y(e)\mathfrak{X}_i^{X,Y}(e): Swap made by Pi(e)\mathbb{P}_i(e) selling asset token XX from PiX(e)\mathbb{P}_i^X(e) buying asset token YY with PiY(e)\mathbb{P}_i^Y(e) during eon ee
  • AiX(e)\mathfrak{A}_i^X(e): Proof of exclusive balance allotment for PiX(e)\mathbb{P}_i^X(e)
  • λP(Ti,jXPjX)\lambda_P(\mathfrak{T}_{i,j}^X \in\mathbb{P}_j^X) : Proof of transfer delivery for Ti,jX\mathfrak{T}_{i,j}^X in PiX\mathbb{P}_i^X
  • λA(XiX,YPiX)\lambda_A(\mathfrak{X}_i^{X,Y} \in\mathbb{P}_i^X ) : Proof of active swap delivery for XiX,Y\mathfrak{X}_i^{X,Y} in PiX\mathbb{P}_i^X
  • SiK(e)\mathfrak{S}_i^K(e): Represent the latest active state of the account PiX(e)\mathbb{P}_i^X(e)
  • MiK(e)\mathfrak{M}_i^K(e): Represent the latest Balance Marker of the account PiX(e)\mathbb{P}_i^X(e)
  • SIGop(N) SIG_{op}(N) Signature by to operator over an object NN
  • SIGi(N) SIG_{i}(N) Signature by Pi\mathbb{P}_i over an object NN
  • Rip(e)R^p_i(e): The amount received from transfers during the round
  • Ria(e)R^a_i(e): The amount received from swaps during the round
  • Si(e)S_i(e): The amount spent by doing swap or transfer during the round

We will refer to an element/variable of an object with the notation .variableName. For example, SiK(e).eonNumber\mathfrak{S}_i^K(e).\texttt{eonNumber}​ refers to the eonNumber element of the active state

Desired Properties

The bimodal ledger allows for light client verifications of exclusive allotments. Merkle proofs should be compact and grow logarithmically with the number of users and transactions.

Technical Specification

Below is an overview of the complete NOCUST bimodal ledger.

Bimodal Ledger Overview

At the very top is the checkpoint that is periodically committed onto the parent chain. At the top level is the membership tree or token tree. Each leaf consist of 1 token/asset supported by the commit-chain (typically ERC-20 or Ether in Ethereum). The second level from the top is the account tree. Each leaf is a user account for a specific token. This tree has the specificity to be an Interval tree of account balance. Each user account is composed of several fields including the account address (public key) and Active and Passive transaction trees.

Trees

Checkpoint

The checkpoint is the 32 byte root checksum of the membership tree. The checkpoint needs to be periodically submitted to the access restricted function submitCheckpoint. Exactly one checkpoint per eon MUST be submitted by operator. All the data within the bimodal ledger depends on a specific eon number.

interface checkpoint {
    checkpoint: Byte32
}

Membership tree

The membership tree MUST be a standard Merkle tree as described in NSPEC004. The NOCUST commit-chain MAY support compliant asset tokens (ERC-20 on Ethereum). Each asset supported by the commit chain is reflected by a leaf in the membership tree. The leaf at index 0 MUST represents the native asset of the parent-chain (Ether for Ethereum). The operator MAY add support for additional asset token. To add an asset token it MUST call the access restricted contract function registerERC20. Each leaf simply consists of the root checksum of the account tree for the given asset.

Account tree

The membership tree is an Interval tree as described in NSPEC004. Each leaf in the tree represent a user account Pi\mathbb{P}_i that includes all of its transactions and additional meta-data. The leafChecksum from NSPEC004 MUST be set to the checksum of the Account aggregate. The allotment AiA_i of each account is set to the account balance. The left and right values from NSPEC004 MUST be set according to the interval equation.

Active tree

The active tree of PiX\mathbb{P}_i^X MUST contain all outgoing transactions Ti,jX(e)\mathfrak{T}_{i,j}^X(e) made during the eon, and all swaps XiX,Y(e)\mathfrak{X}_i^{X,Y}(e) buy or sell made by the user account during the eon of the checkpoint. The active tree MUST be a standard Merkle tree as described in NSPEC004. The leafs are the checksums of the transactions. The sum of the amount of all the outgoing transfer and swap in the active tree is equal to Si(e)S_i(e). The sum of the amount of all the swaps is equal to Ria(e)R_i^a(e)

Passive tree

The passive tree MUST contain all of the incoming transactions made during the eon of the checkpoint. The passive tree MUST not contain any swaps. The passive tree MUST be an interval tree as described in NSPEC004. The allotment of each transaction element is set to the amount of the transaction. The right and left values are set according to the interval formula from NSPEC004. The leafChecksum is set to the checksum of the transaction model. The sum of the amounts of all transfers in the passive tree is equal to Rip(e)R_i^p(e)

Models

Account aggregate

They MUST be exactly one Account aggregate per PiX\mathbb{P}_i^X. The checksum of this data structure is to be used as a leaf element of the Account tree.

interface AccountAggregate extends Checksumable {
    nocustContractAddressChecksum: Byte32 // checksum(nocustContractAddress)
    tokenContractAddressChecksum: Byte32  // checksum(tokenContractAddress)
    userAddressChecksum: Byte32           // checksum(userAddress)
    passiveAggregateChecksum: Byte32      // checksum(passiveAggregate)
    activeTreeRoot: Byte32
}
  • nocustContractAddressChecksum is the checksum of the NOCUST smart contract address:

    interface NocustSmartContractAddress extends Checksumable {
      nocustSmartContractAddress: Address
    }
    
  • tokenContractAddressChecksum is the checksum of the asset token smart-contract address.

    interface TokenContractAddressChecksum extends Checksumable {
      tokenContractAddressChecksum: Address
    }
    
  • userAddressChecksum is the checksum of the address of the user account.

    interface UserAddressChecksum extends Checksumable {
      userAddressChecksum: Address
    }
    
  • passiveAggregateChecksum is the checksum of the PassiveAggregate model:

    interface PassiveAggregate extends Checksumable {
      passiveTreeRoot: Byte32
      passiveAmountReceived: Uint256
      lastOugoingTransferpassiveMarker: Uint256
    }
    
    • passiveTreeRoot is the Merkle root of the passive tree.
    • passiveAmountReceived is Rip(e)R^p_i(e) the total sum of the funds received by transfer for the particular asset or the sum of the transactions in the passive tree.
    • lastOugoingTransferpassiveMarker Is the Passive Marker of the last outgoing transfer.
    • activeTreeRoot is the Merkle root of the active tree.

Transaction Model

NOCUST supports 2 types of transaction. Transfers, sending money from Alice to Bob, and token swaps between 2 asset tokens. The commit-chain MAY support only payment but no swaps. However, the commit-chain MUST always support transfer.

Transaction model for payment

Ti,jX(e)\mathfrak{T}_{i,j}^X(e), transfer from PiX(e)\mathbb{P}_i^X(e) to PjX(e)\mathbb{P}_j^X(e) of asset token XX during eon ee

interface Transfer extends Checksumable {
    counterpartyAddressChecksum: Bytes32
    amount: Uint256
    recipientLeafIndex: Uint64
    transferTag: Uint256
}
  • counterpartyAddressChecksum is the checksum of the recipient or sender address ii:

    interface CounterpartyAddress extends Checksumable {
        counterpartyAddress: Address
    }
    
  • amount is the amount sent.

  • recipientLeafIndex is the index of the leaf in the account tree of the recipient Pj\mathbb{P}_j.

  • transferTag is a unique number resulting from the Uint256 interpretation of the checksum of the Tag model

    interface TransferTag extends Checksumable {
        passiveMarker: Uint256
        nonce: Uint256
    }
    
    • passiveMarker If the transfer is finalized. We set its value to the Passive Marker (left value of the transfer in the passive interval tree of the recipient). If the transfer is not finalized, we set its value to 225612^{256} - 1.
    • nonce is a unique random number to be chosen by the sender.

We call a transfer finalized, if it is not the last element in the active set.

A valid Ti,jX(e)\mathfrak{T}_{i,j}^X(e) MUST be included in the passive tree of PjX(e)\mathbb{P}^X_j(e) and in the active tree of PiX(e)\mathbb{P}_i^X(e).

Transaction model for swap

For a new swap XiX,Y(e)\mathfrak{X}_i^{X,Y}(e). We call PiX\mathbb{P}_i^X debited account and PiY\mathbb{P}_i^Y the credited account

interface Swap extends Checksumable {
    sellTokenChecksum: Bytes32
    buyTokenChecksum: Bytes32
    index: Uint64
    sellAmount: Uint256
    buyAmount: Uint256
    startinBalance: Uint256
    nonce: Uint256
}
  • sellToken is the checksum of the token address to sell:

    interface SellToken extends Checksumable {
        sellToken: Address
    }
    
  • buyToken is the checksum of the token address to buy:

    interface BuyToken extends Checksumable {
        buyToken: Address
    }
    
  • index is the leaf index of the credited account.

  • sellAmount amount of the sellToken to sell.

  • buyAmount amount of the buyToken to buy.

  • startingBalance is the balance balance(Pi)balance(\mathbb{P}_i) at the start of the swap.

  • nonce is a unique random number to be chosen by the sender.

A valid XiX,Y(e)\mathfrak{X}_i^{X,Y}(e) MUST be included in the active tree of PiX(e)\mathbb{P}_i^X(e) and PiY(e)\mathbb{P}_i^Y(e).

Proof of exclusive balance allotment

AiX(e)\mathfrak{A}_i^X(e) A proof of exclusive balance allotment is specific to an account PiX(e)\mathbb{P}_i^X(e). It allows to fully verify AiX(e)A_i^X(e) from the checkpoint on the parent-chain.

interface ProofOfExclusiveBalanceAllotement {
    membershipProof: MerkleProof
    accountProof: ProofOfExclusiveAllotement
    accountAggregate: AccountAggregate
}

A proof of exclusive balance allotment is considered valid if the root of the membership tree reconstructed match the checkpoint.

Proof of passive delivery

λP(Ti,jXPjX)\lambda_P(\mathfrak{T}_{i,j}^{X} \in\mathbb{P}_j^X ) is the Proof of transfer delivery for Ti,jX\mathfrak{T}_{i,j}^X in PjX\mathbb{P}_j^X. It allows to verify the delivery of transfer in the recipient account.

interface ProofOfPassiveDelivery {
    deliveryProof: ProofOfExclusiveAllotement
    transfer: Transfer
}

A proof of passive delivery is considered valid if the if the root of the membership tree reconstructed match the checkpoint.

Proof of active delivery

λA(Ti,jXPiX)\lambda_A(\mathfrak{T}_{i,j}^{X} \in\mathbb{P}_i^X )​ is the Proof of active delivery of transfer Ti,jX\mathfrak{T}_{i,j}^X​ in PiX\mathbb{P}_i^X​. It allows proving the inclusion of a transfer in the sender account.

λA(XiX,YPiX)\lambda_A(\mathfrak{X}_i^{X,Y} \in\mathbb{P}_i^X ) is the proof of active delivery of swap XiX,Y\mathfrak{X}_i^{X,Y} in PiX\mathbb{P}_i^X. It allows proving the inclusion of the swap in sell account.

λA(XiX,YPiY)\lambda_A(\mathfrak{X}_i^{X,Y} \in\mathbb{P}_i^Y ) is the proof of active delivery of swap XiX,Y\mathfrak{X}_i^{X,Y} in PiY\mathbb{P}_i^Y. It allows proving the inclusion of swap in buy account.

interface ProofOfActiveDelivery {
    deliveryProof: MerkleProof
    transaction: Transfer | Swap
}

A proof of active delivery is considered valid if the root of the membership tree reconstructed match the checkpoint.

Active state

The active state is essentially a subset of the account aggregate. The missing fields are the passiveAggregateChecksum and the passiveAggregate. While its checksum is not directly part of the bimodal ledger, the active state is central in the NOCUST architecture. It is the data-structure that is used to authorize off-chain transactions. We denote SiK(e)\mathfrak{S}_i^K(e) the latest active-state of account PiK(e)\mathbb{P}_i^K(e).

The active state is checksumed and signed by the user and the operator to authorize a transaction. Users make transactions by creating a new updated active state. We denote SIGi(SiK) SIG_{i}(\mathfrak{S}_i^K) the signature of the active state SiK \mathfrak{S}_i^K by the user Pi\mathbb{P}_i and SIGop(SiK) SIG_{op}(\mathfrak{S}_i^K) the signature of the active state SiK \mathfrak{S}_i^K by the operator.

For an active state SiK \mathfrak{S}_i^K to be considered valid, it MUST be signed by the operator and the account owner.

interface ActiveState extends Checksumable {
  nocustContractAddressChecksum: Byte32 // checksum(nocustContractAddress)
  tokenContractAddressChecksum: Byte32  // checksum(tokenContractAddress)
  userAddressChecksum: Byte32           // checksum(userAddress)
  leafIndex: Uint64
  eonNumber: Uint256
  activeTreeRoot: Byte32
  spent: Uint256
  gained: Uint256
}
  • nocustContractAddressChecksum is the checksum of the NOCUST smart contract address:

    interface NocustSmartContractAddress extends Checksumable {
      nocustSmartContractAddress: Address
    }
    
  • tokenContractAddressChecksum is the checksum of the asset token smart-contract address.

    interface TokenContractAddressChecksum extends Checksumable {
      tokenContractAddressChecksum: Address
    }
    
  • userAddressChecksum is the checksum of the address of the user account.

    interface UserAddressChecksum extends Checksumable {
      userAddressChecksum: Address
    }
    
  • leafIndex of the account in the Account tree. The account index is provided by the operator after account registration and can't be changed

  • eonNumber MUST equals ee.

  • activeTreeRoot root of the Active Tree. The active tree MUST include all the the outgoing transactions and swaps made during the eon eonNumber or all the outgoing transaction and swaps made in the current ongoing eon.

  • spent the total amount spent. We add to the spent value after making an outgoing transfer or swap. Withdrawals request do not count toward spent.

  • gained the total amount received by swap during the eon. We add to the gained value only after finalizing a swap. Deposits do not count toward gained

Balance Marker

The balance marker is used to communicate user account balance to the operator. It is not directly part of the bimodal ledger. We denote the Balance Marker MiK(e)\mathfrak{M}_i^K(e) of account PiK\mathbb{P}_i^K.

interface BalanceMarker extends Checksumable {
  nocustContractAddressChecksum: Byte32 
  tokenContractAddressChecksum: Byte32  
  userAddressChecksum: Byte32
  eonNumber: Uint256
  balance: Uint256
}

The first 3 fields are the same as in the active state of the account. eonNumber is set to ee. balance is set to a balance value.

Example Implementation

Other Implementations

History

All content herein is licensed under GPL License.

results matching ""

    No results matching ""