tally

package
v0.5.0-dev.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 14, 2025 License: GPL-3.0 Imports: 15 Imported by: 0

README

Tally Module

Overview

Primarily, the tally module aggregates the Oracle Program execution results reported by the Overlay Nodes, detects outliers, and calculates payouts.

Tally Flow

flowchart TD
    A["2/3 agree on<br>(exec_success,<br>proxy_pubkeys)?"]
    A -->|"ErrNoBasicConsensus<br>(outliers=nil)"| G1["fallback gas to committers"]
    A --->|Yes| C
    C["BuildFilter<br>(based on<br> requestor-provided input)"]
    C -->|"ErrInvalidFilterInput<br>(outliers=nil)"| R["ReducePayout mode"]
    C --->|OK| D
    D["ApplyFilter<br>(parse reveals and<br>determine <br>consensus & outliers)"]
    D ---->|"ErrNoConsensus<br>(>1/3 outlier)<br>(outliers=nil)"| R
    D --->|"ErrConsensusInError<br>(>2/3 error)"| G2
    D ----->|"Consensus<br>(<=1/3 outlier)"| F["TallyVM Execution"]
    R ---> G2
    F ---> |Error|R
    F -----> |NoError|G2["meter proxy gas"]
    G2 ---> G3["meter executor gas"]
    G1  ----> Z(("Done"))
    G3  ----> Z(("Done"))

At every block, the end blocker of the tally module queries the Core Contract for the list of data requests that are ready to be tallied. The tally process for a given data request involves the following sequence:

  1. Filtering: The goal of filtering is to determine whether there is sufficient consensus among the reveals by the Overlay Nodes. The outcome of filter determines the future flow of tally operation on the data request. First, if there is less than 2/3 agreement on the success-or-fail of the execution and the data proxy public keys used during the execution, the outcome of tally is ErrorNoBasicConsensus. Otherwise, the module builds a filter based on the input provided by the data requestor. If there is an error while building a filter, the outcome of tally is ErrorInvalidFilterInput. Finally, if there is no error up to this point, the module applies the filter, whose outcome is NoError (= consensus), ErrorConsensusInError (more than 2/3 of reveals are failed executions or cannot be parsed), or ErrorNoConsensus (1/3 or more of reveals are deemed “outliers”). Note the definition of “outlier” depends on the type of filter:

    • None filter: No reveal is an outlier, even if it cannot be parsed.
    • Mode filter: A reveal is an outlier if its parsed value is not equal to the most frequent parsed reveal value.
    • Standard deviation filter: A reveal is an outlier if it deviates from the mean by more than calculated_sample_standard_deviation * sigma_multiplier. Note the sigma multiplier is a part of the filter input provided by the data requestor. Also note that the standard deviation computation involves some precision loss for it uses math/big.Int type for simplicity and overflow prevention.
  2. Tally VM execution: If the outcome of the filtering did not result in an error, the module executes the Tally Program specified in the data request.

  3. Gas calculation: Since execution gas includes gas used by data proxies, the module first computes and consumes their gas consumption. If the filtering phase has failed to determine a list of data proxy public keys in consensus, the module skips this step and simply consumes the fallback gas for the committers. Otherwise, the amount of gas consumed by each data proxy j in the list in consensus per execution is

    $$ \text{proxyGasUsed}_j = \frac{\text{proxyFee}}{\text{gasPrice}} $$

    Note the data proxy’s fee is retrieved from the data-proxy module and the gas price from the data request.

    The remaining part of the execution gas is due to the executors’ work. However, since the gas reports from the Overlay Nodes cannot be blindly trusted, the tally module calculates the “canonical” gas consumptions by the executors. Given n gas reports, they are first adjusted by the previously computed data proxy gas consumption and the remaining execution gas. The adjusted gas report R_i for executor i is

    $$ R_i=\min \left( \left\lfloor\frac{\text{RemainingExecGas}}{\text{ReplicationFactor}}\right\rfloor, \text{reportedGas}_i-\Sigma_j \text{proxyGasUsed}_j \right) $$

    The payout calculation based on the adjusted gas reports is split into two scenarios.

    • Gas reports from the Overlay Nodes are uniform: The gas used by executor i is simply the adjusted gas report R_i.

    $$ \text{executorGasUsed}_i = R_0 = R_1 = ... = R_n $$

    • Gas reports from the Overlay Nodes are divergent: The module assigns a different gas consumption to the Overlay Node that has reported the lowest gas.

    $$ \text{lowestReporter} = \left(\frac{\text{Low} \times 2}{\text{TotalShares}} \right)\times\text{TotalUsedGas} $$

    $$ \text{nonLowestReporter} = \left(\frac{\text{Median}}{\text{TotalShares}} \right) \times \text{TotalUsedGas} $$

    where

    $$ \text{Low}=\min(R_0,R_1,...,R_n) $$

    $$ \text{Median}=\text{median}(R_0,R_1,...,R_n) $$

    $$ \text{TotalUsedGas} = \text{Median} \times (\text{RF} - 1) + \min(\text{Low} * 2, \text{Median}) $$

    $$ \text{TotalShares} = \left(\text{Median} \times (\text{RF} - 1)\right) + (\text{Low} \times 2) $$

    The calculated execution gas consumption is returned in the tally result along with the tally gas consumption, which is the gas used by the tally module for filtering and tally VM execution. These gas calculations are converted into payout distribution messages, which are then delivered to the Core Contract for actual executions of funds transfers. Note the tally gas is burned, whereas the data proxy gas and executor gas are paid to the data proxies and executors, respectively. The table below describes the payout depending on the filter/tally outcome.

    ⬇️ Order of error detection ➡️ Order of payment (The burns are combined in one burn execution)

    Outcome Base Fee (burn) Filter Fee (burn) Tally VM Fee (burn) Data Proxies Reward Data Executors Reward
    Timeout without reveal ❌ (not executed) Fallback pay to committers
    ErrNoBasicConsensus ❌ (not executed) Fallback pay to committers
    ErrInvalidFilterInput ❌ (not executed) 80% pay (20% burn)
    ErrNoConsensus ❌ (not executed) 80% pay (20% burn)
    ErrConsensusInError ❌ (not executed) Full pay
    Tally execution error ❌ (not executed) 80% pay (20% burn)
    No error Full pay

Once the filtering - tally VM execution - gas calculation sequence is completed, and the results are reported back to the Core Contract, data result entries are stored in the batching module under batch-ready status.

Note the tally module’s end blocker is structured so that most errors are caught and logged without causing the chain to halt. Only the most critical operations such as data result ID calculation or state writes can return an error.

State

0x00 -> parameters

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AppModule

type AppModule struct {
	AppModuleBasic
	// contains filtered or unexported fields
}

AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement

func NewAppModule

func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule

func (AppModule) BeginBlock

func (am AppModule) BeginBlock(_ context.Context) error

BeginBlock contains the logic that is automatically triggered at the beginning of each block

func (AppModule) ConsensusVersion

func (AppModule) ConsensusVersion() uint64

ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1

func (AppModule) EndBlock

func (am AppModule) EndBlock(ctx context.Context) error

EndBlock returns the end block logic for the tally module.

func (AppModule) ExportGenesis

func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage

ExportGenesis returns the module's exported genesis state as raw JSON bytes.

func (AppModule) InitGenesis

func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage)

InitGenesis performs the module's genesis initialization. It returns no validator updates.

func (AppModule) IsAppModule

func (am AppModule) IsAppModule()

IsAppModule implements the appmodule.AppModule interface.

func (AppModule) IsOnePerModuleType

func (am AppModule) IsOnePerModuleType()

IsOnePerModuleType implements the depinject.OnePerModuleType interface.

func (AppModule) RegisterInvariants

func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)

RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted)

func (AppModule) RegisterServices

func (am AppModule) RegisterServices(cfg module.Configurator)

RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries

type AppModuleBasic

type AppModuleBasic struct {
	// contains filtered or unexported fields
}

AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement.

func NewAppModuleBasic

func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic

func (AppModuleBasic) DefaultGenesis

func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage

DefaultGenesis returns a default GenesisState for the module, marshaled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing

func (AppModuleBasic) GetQueryCmd

func (AppModuleBasic) GetQueryCmd() *cobra.Command

GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module

func (AppModuleBasic) GetTxCmd

func (a AppModuleBasic) GetTxCmd() *cobra.Command

GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module

func (AppModuleBasic) Name

func (AppModuleBasic) Name() string

Name returns the name of the module as a string

func (AppModuleBasic) RegisterGRPCGatewayRoutes

func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)

RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module

func (AppModuleBasic) RegisterInterfaces

func (a AppModuleBasic) RegisterInterfaces(cdc cdctypes.InterfaceRegistry)

RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message

func (AppModuleBasic) RegisterLegacyAminoCodec

func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)

RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore

func (AppModuleBasic) ValidateGenesis

ValidateGenesis performs genesis state validation for the tally module.

Directories

Path Synopsis
client
cli
Package types is a reverse proxy.
Package types is a reverse proxy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL