# Table
# The Features of Table
Miyabi uses permissioned tables to manage data in the world state like in most database software. Miyabi stores the world state by tables while keeping all the blocks as a record for all transactions. Each table has a unique table name for identification. The fundamental structure of the table is the Key-value pair, where the key is unique for searching values.
Each table maintains a TableDescriptor
which defines various attributes of a Miyabi table.
It contains metadata about the table, for example, name of the table, tracking changes to the table etc.
All tables in Miyabi inherit from the TableDescriptor
base class which contains three members - table name, a Boolean flag indicating whether to support the state proof generation for table data or not, and another Boolean flag which enables/disables the table history tracking.
In Miyabi, various extensions provide out of the box implementations for custom tables which can be used to store different types of data:
- Asset Table stores the balance of each address.
- Binary Table stores arbitrary value for arbitrary keys.
- Entity Table contains the entity and its parent-child relationship with other entity by key.
- NFT Table stores the owner of each non-fungible token.
- Private Data Table stores the evidence of private data shared between nodes over a private channel.
# Permissions Related to Tables
# Permission Models
Each permissioned table in Miyabi follows a PermissionModel
to execute Insert, Update (Delete is part of Update operation) and Read operation.
Miyabi has Table Level and Row Level Permission.
A PermissionModel
is defined for each table as a combination of above two models:
PermissionModel | Behavior |
---|---|
PermissionLess | No check |
CheckRowOnly | Only need to pass row level check |
CheckTableOnly | Only need to pass table level check |
TableOrRow | Either table level check or row level check should pass |
TableAndRow | Both table level check and row level check should pass |
# Table Level Permission
All custom tables are permissioned and have table-level permissions, which is defined through the TableAccessControlList
in the PermissionedTableDescriptor
.
A TableAccessControlList
stores a set of explicitly allowed addresses for each operation.
For a given set of credentials, TableAccessControlList
can evaluate whether permission is granted for a given operation.
# Table Owner
For any PermissionModel
, all table level permissions will be granted to TableOwners
.
TableAccessControlList
maintains non-empty list of addresses for the TableOwner
, who has permission to delete this table and grant permissions to other addresses.
# Row Level Permission
Each permissioned table can define row-level permission to validate reading, updating and deletion of a row. A row-level check function is explicitly required for all permissioned tables.
# Row Owners
Row owner's signature is required to validate the row level permission check in a table.
To be noted, insert permission belongs to the table, hence if a table has PermissionModel
set to CheckRowOnly
, any address can create a new row, but only the row owners, if defined, can update it.
# Custom Table Permission
The CustomTablePermission
includes insert, update, and read permissions.
TableAccessControlList
stores a collection of GrantedAddress
, which stores the CustomTablePermission
granted to various addresses.
For any action if the table-level permission
check is required, then apart from table owners, only the addresses stored in GrantedAddress
corresponding to that action can pass the validation.
CustomTablePermissionModel | AssignedBinaryValue |
---|---|
None | 0000 |
Insert | 0001 |
Update | 0010 |
Read | 0100 |
All | 0111 |
# Read Restricted
A boolean ReadRestricted
is presented to control the read permission check for each row.
If a table is not read restricted, read permission will not be checked no matter which PermissionModel
is selected.
Otherwise, read operation will trigger permission checks based on the PermissionModel
of the table.
# Token Table Permission
Token table (Asset Table and NFT Table) has two additional roles: TokenAdmins
and TrustedEntities
(AKA TrustedAddress
).
Token admin can generate tokens from void address, while TrustedEntities can withdraw any tokens from any user address to its address.
Only table owners can grant these two roles to an address.
# Permissioned Table Descriptor
Descriptor (metadata) of all permissioned tables in Miyabi inherits from a permissioned table descriptor. It defines following properties:
Parameters | Type | description |
---|---|---|
TableACL | TableAccessControlList | Defines the table owner and other addresses' table level permissions |
Name | string | Unique name for the table |
Tracked | bool | Tracks the detailed transaction history of table |
SupportProofs | bool | Allows generating the proof of an entry that can be verified by state proof |
IsReadRestricted | bool | Defines if read permission should be checked or not |
PermissionModel | PermissionModel | Defines how permission should be checked when updating the contents of permissioned table |
# Table Interfaces
Tables are exposed through the following interfaces in smart contracts.
Each table can be exposed as a reader and writer according to the context and requirements.
These interfaces are available in the related module models. (i.e. Miyabi.Asset.Models
)
Table Interfaces |
# Asset Table
# Basic Information
The asset table is the table designed for recording decimal assets like digital coins. In the asset table, the key is the public key of the asset owner, and the value is asset balance in decimal.
The data stored in Asset table is as follows:
Field | Description | Type |
---|---|---|
Key | address of the asset owner | Address |
Value | amount of assets owned by asset owner | Decimal |
An asset table descriptor has the following parameters in addition to general table descriptors.
Parameters | Type | description |
---|---|---|
TokenAdmins | IReadOnlyList<Address> | Addresses that can mint token |
TrustedEntities | IReadOnlyList<Address> | Addresses that can withdraw token from other addresses without permission checks |
Rules | IReadOnlyList<ValidationRule> | Restrictions to the table, which will be examined by an independent verifier Kotowari |
VoidAddress | Address | Address that is kept as a source of tokens. |
Granularity | AssetGranularity | Includes the maximum, minimum as well as the unit of value for each move. By default, they are ( |
# Validation Rule
The validation rule is some policies set for a table. Whenever a transaction tries to access an asset table, Kotowari
will check if the operation to the table violates the validation rules.
If the validation failed, all changes introduced by this transaction will be discarded.
Rule | Description |
---|---|
Sum equal zero (default) | The sum of all values in the table should be zero. E.g. balance sheet |
No Negative (default) | All value should not be negative (except for VoidAddress ). E.g. bank account deposit |
Prevent Admin Trustee (default) | Prevent admins from adjusting the balances of other addresses. |
Prevent Trusted Withdrawals | Prevent trusted entities (listed in TrustedEntities) to withdraw from other addresses. |
Each table will reserve one void address, which can only be touched by TokenAdmins
to mint tokens.
The void address can pump out infinity tokens, and send them to any addresses.
The void address is the only address can have negative value under No Negative
rule.
According to the Sum Equal Zero
rule, Kotowari
only need to checks
The basic operations on the asset table are moving assets from one address to another address.
Usually, move assets from one address require the signature of that address.
TokenAdmin
can move an arbitrary asset from void address to arbitrary address if Prevent Admin Trustee is not applied.
The trusted address can withdraw assets from arbitrary addresses except for the void address.
Also, a smart contract can use its instance Id to create an asset table and only the smart contract can modify its entry.
# Examples
An example of using asset table which includes the following operations is illustrated in Figure 11:
- Create an Asset table
- Generate assets from void address
- Move assets to another address
If No Negative
rule is applied, all addresses other than the void address cannot have a negative value.
Hence, the table owner will generate assets by moving assets from the void address which requires asset admin permission.
For moving assets, either the signature of the source address is provided or the trusted address has signed the transaction.
If Prevent Admin Trusted
is not applied, the admin signature can also withdraw assets from arbitrary addresses.
Figure 11. Example of Asset table usage |
# Permission Model of Asset Table
For an asset table, the row permission check cannot be skipped.
Hence, only CheckRowOnly
and TableAndRow
are allowed as the PermissionModel
.
For TableAndRow
model asset table, two additional operations are allowed called RegisterAccount
and DeleteAccount
.
These two operations are defined in IPermissionedAssetTableWriter
which allows creating a new row for a specific address with 0 balance.
This operation is not needed in CheckRowOnly
model because a row can be inserted or deleted whenever someone transfers tokens to that address or the balance of the address return to 0.
However, in the TableAndRow
model, a row cannot be created or deleted without insert or update permission.
# Binary Table
# Basic Information
A binary table is a simple permissioned data table.
In a binary table, key and value are ByteString.
The value is stored within a PermissionedData
together with a list of addresses who are treated as the owner of this row.
If there is no owner for a row, the row permission check will always return true.
The data stored in binary table is as follows:
Field | Description | Type |
---|---|---|
Key | Arbitrary ByteString key | ByteString |
Value | Permissioned data containing value and owner | PermissionedData<ByteString> |
BinaryTableDescriptor
has the same definition as PermissionedTableDescriptor
Binary tables support a special operation called UpsertRow
, which will insert a row if it does not exist, otherwise update it with the new value.
The permissions to be checked depending on the actual behavior is insert or update.
# Entity Table
# Basic Information
An entity table is an extended binary table optimized for storing the relationships between different entries. Each entry may link to multiple parent entries and multiple children entries to form a graph within the same table or across different entity tables. The storage rule and command for creating tables are the same as the binary table.
Field | Description | Type |
---|---|---|
Key | Arbitrary ByteString key | ByteString |
Value | Permissioned data containing value and owner | PermissionedData<ByteString> |
An entity table supports creating a link between different entity values in different entity tables. These links can be used to split data into pieces for complex management like:
Grant permission arbitrarily small
Like binary tables, each row has row owners. Since an entity table allows links to manage different rows, the user can divide one binary value into several rows and grant them to different owners.
Store notes of a specific row
Users can create rows to store notes of another row and add the row as the parent of the note rows.
Optimize update only a part of a row
According to the frequency of updating data, one user can split data into different tables and use different keys to manage the update process. The integrity of the data is guaranteed by links. One link has two components
Item | Description |
---|---|
Parent | the table name and key of the parent row |
Tag | identifier for a unique parent-child relationship |
When a parent-child relationship is created, Miyabi will automatically add several rows to store the relationship information. In case of a parent entity, all children and corresponding tags information will be stored. Similarly, for child entity, references to the parent entity corresponding to the respective tags will be stored. The entities can form a graph of parent child relationship across different entity tables.
EntityTableDescriptor
has the same definition as PermissionedTableDescriptor
# Example for Entity Table
Figure 12 shows an example of the entity table's structure.
Figure 12. Example of Entity Table's Structure |
# NFT Table
# Basic Information
A non-fungible token (NFT), is a special type of asset, which represents something unique. In Miyabi, we implement a NFT module to support NFT. NFT table stores a token Id as key and its owner as of the value.
The data stored in NFT table is as follows:
Field | Description | Type |
---|---|---|
Key | Token Id of NFT | String |
Value | Address of the token owner. | Address |
An NFT table descriptor has the following in addition to general table descriptors.
Parameters | Type | description |
---|---|---|
TokenAdmins | IReadOnlyList<Address> | Addresses that can mint token and move token arbitrarily |
TrustedEntities | IReadOnlyList<Address> | Addresses that can claim arbitrary token |
NFT table supports ERC-721 (opens new window) and provide a set of address to be trusted address like asset table.
TokenAdmins
can mint NFT and move token arbitrarily.
In addition, TrustedEntities
can change an arbitrary token's owner to itself.
Currently, the balance of an address is the number of tokens it owned. NFT table has implemented an inner table optimization for speeding up checks of the balance of one address.
# Private Data Table
# Basic Information
The Private Data module extension of Miyabi allows a subset of nodes to privately share the data among themselves.
The private data is stored in the PrivateState
while the hash of this private data is stored in WorldState
under the "Private Data table".
The hash stored in WorldState
can be used to audit the existence of the actual private data in PrivateState
.
The Private Data table is a permissioned table which stores the keys and values as raw ByteString. The key-value in this table is the key-value of the actual private data.
The node operators can create their own private data channel to define the members which will participate in the private data sharing.
These members are called "Private Data Owners (PDOs)".
The PDO members must all individually sign transactions before updating the Private State
and these PDO members can be used to retrieve the raw values of the private data.
A Private transaction (different from a normal Miyabi transaction) contains a Miyabi transaction (to update the WorldState
) and a payload (to update the PrivateState
), where Payload
represents the raw private data sent to one of the Private Data Owners.
The raw data stored in the private state of PDO members is as follows:
Field | Description | Type |
---|---|---|
Key | Arbitrary ByteString key | ByteString |
Value | Permissioned data containing value and owner | PermissionedData<ByteString> |
The hash data stored in the world state is as follows:
Field | Description | Type |
---|---|---|
Key | Hash of the ByteString key | ByteString |
Value | Hash of the ByteString value | ByteString |
A Private Data table descriptor has the following parameters in addition to general table descriptors.
Parameters | Type | description |
---|---|---|
PrivateDataOwners | IReadOnlyList<Address> | Addresses that store the evidence of private data |