Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas metering for filters #450

Merged
merged 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion proto/sedachain/tally/v1/tally.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ option go_package = "github.com/sedaprotocol/seda-chain/x/tally/types";

// Params defines the parameters for the tally module.
message Params {
// max_tally_gas_limit is the maximum gas limit for a tally request.
// MaxTallyGasLimit is the maximum gas limit for a tally request.
uint64 max_tally_gas_limit = 1;
// FilterGasCostNone is the gas cost for a filter type none.
uint64 filter_gas_cost_none = 2;
// FilterGasCostMultiplierMode is the gas cost multiplier for a filter type
// mode.
uint64 filter_gas_cost_multiplier_mode = 3;
// FilterGasCostMultiplierStdDev is the gas cost multiplier for a filter type
// stddev.
uint64 filter_gas_cost_multiplier_stddev = 4;
}
16 changes: 11 additions & 5 deletions x/tally/keeper/endblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
}
paybackAddrHex := hex.EncodeToString(decodedBytes)

var outliers []int
outliers, result.consensus, result.proxyPubKeys, err = ApplyFilter(filter, reveals)
filterResult, err := k.ApplyFilter(ctx, filter, reveals, req.ReplicationFactor)
result.consensus = filterResult.Consensus
result.proxyPubKeys = filterResult.ProxyPubKeys
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrApplyingFilter, req)
}
Expand All @@ -238,7 +239,7 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
return result, k.logErrAndRet(ctx, err, types.ErrDecodingTallyInputs, req)
}

args, err := tallyVMArg(tallyInputs, reveals, outliers)
args, err := tallyVMArg(tallyInputs, reveals, filterResult.Outliers)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrConstructingTallyVMArgs, req)
}
Expand All @@ -247,7 +248,12 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrGettingMaxTallyGasLimit, req)
}
gasLimit := min(req.TallyGasLimit, maxGasLimit)
var gasLimit uint64
if min(req.TallyGasLimit, maxGasLimit) > filterResult.GasUsed {
gasLimit = min(req.TallyGasLimit, maxGasLimit) - filterResult.GasUsed
} else {
gasLimit = 0
}

k.Logger(ctx).Info(
"executing tally VM",
Expand Down Expand Up @@ -277,7 +283,7 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
result.stderr = vmRes.Stderr
result.result = vmRes.Result
result.exitInfo = vmRes.ExitInfo
result.tallyGasUsed = vmRes.GasUsed
result.tallyGasUsed = vmRes.GasUsed + filterResult.GasUsed

return result, nil
}
Expand Down
40 changes: 18 additions & 22 deletions x/tally/keeper/endblock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ import (
"github.com/sedaprotocol/seda-chain/x/tally/types"
)

func TestProcessTallies(t *testing.T) {
f := initFixture(t)

drID := f.commitRevealDataRequest(t, "c2VkYXByb3RvY29s")

err := f.tallyKeeper.ProcessTallies(f.Context(), f.coreContractAddr)
require.NoError(t, err)

// TODO check tally result & exit code

dataResult, err := f.batchingKeeper.GetLatestDataResult(f.Context(), drID)
require.NoError(t, err)

dataResults, err := f.batchingKeeper.GetDataResults(f.Context(), false)
require.NoError(t, err)
require.Equal(t, *dataResult, dataResults[0])
}

// TestTallyVM tests tally VM using a sample tally wasm that performs
// preliminary checks on the given reveal data.
func TestTallyVM(t *testing.T) {
Expand Down Expand Up @@ -240,25 +258,3 @@ func TestTallyVM_EnvVars(t *testing.T) {
})
}
}

func TestProcessTallies(t *testing.T) {
f := initFixture(t)

drID := f.commitRevealDataRequest(t, "c2VkYXByb3RvY29s")

err := f.tallyKeeper.ProcessTallies(f.Context(), f.coreContractAddr)
require.NoError(t, err)

// TODO check tally result & exit code

// TODO check events
// fmt.Println(f.logBuf.String())
// require.Contains(t, f.logBuf.String(), exp)

dataResult, err := f.batchingKeeper.GetLatestDataResult(f.Context(), drID)
require.NoError(t, err)

dataResults, err := f.batchingKeeper.GetDataResults(f.Context(), false)
require.NoError(t, err)
require.Equal(t, *dataResult, dataResults[0])
}
44 changes: 34 additions & 10 deletions x/tally/keeper/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"errors"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/sedaprotocol/seda-chain/x/tally/types"
)

Expand All @@ -13,15 +15,25 @@ const (
filterTypeStdDev byte = 0x02
)

type FilterResult struct {
Outliers []int // outlier list
Consensus bool // whether consensus was reached
ProxyPubKeys []string // consensus data proxy public keys
GasUsed uint64 // gas used for filter
}

// ApplyFilter processes filter of the type specified in the first
// byte of consensus filter. It returns an outlier list, which is
// a boolean list where true at index i means that the reveal at
// index i is an outlier, consensus boolean, consensus data proxy
// public keys, and error. It assumes that the reveals and their
// proxy public keys are sorted.
func ApplyFilter(input []byte, reveals []types.RevealBody) ([]int, bool, []string, error) {
func (k Keeper) ApplyFilter(ctx sdk.Context, input []byte, reveals []types.RevealBody, replicationFactor uint16) (FilterResult, error) {
var result FilterResult
result.Outliers = make([]int, len(reveals))

if len(input) == 0 {
return make([]int, len(reveals)), false, nil, types.ErrInvalidFilterType
return result, types.ErrInvalidFilterType
}

// Determine basic consensus on tuple of (exit_code, proxy_pub_keys)
Expand All @@ -39,34 +51,46 @@ func ApplyFilter(input []byte, reveals []types.RevealBody) ([]int, bool, []strin
}
}
if maxFreq*3 < len(reveals)*2 {
return make([]int, len(reveals)), false, nil, types.ErrNoBasicConsensus
return result, types.ErrNoBasicConsensus
}
result.ProxyPubKeys = proxyPubKeys

params, err := k.GetParams(ctx)
if err != nil {
return result, err
}

var filter types.Filter
var err error
switch input[0] {
case filterTypeNone:
filter, err = types.NewFilterNone(input)
result.GasUsed = params.FilterGasCostNone
case filterTypeMode:
filter, err = types.NewFilterMode(input)
result.GasUsed = params.FilterGasCostMultiplierMode * uint64(replicationFactor)
hacheigriega marked this conversation as resolved.
Show resolved Hide resolved
case filterTypeStdDev:
filter, err = types.NewFilterStdDev(input)
result.GasUsed = params.FilterGasCostMultiplierStddev * uint64(replicationFactor)
default:
return make([]int, len(reveals)), false, proxyPubKeys, types.ErrInvalidFilterType
return result, types.ErrInvalidFilterType
}
if err != nil {
return make([]int, len(reveals)), false, proxyPubKeys, err
result.GasUsed = 0
return result, err
}

outliers, err := filter.ApplyFilter(reveals)
switch {
case err == nil:
return outliers, true, proxyPubKeys, nil
result.Outliers = outliers
result.Consensus = true
return result, nil
case errors.Is(err, types.ErrNoConsensus):
return outliers, false, proxyPubKeys, nil
result.Outliers = outliers
return result, nil
case errors.Is(err, types.ErrCorruptReveals):
return make([]int, len(reveals)), false, proxyPubKeys, err
return result, err
default:
return make([]int, len(reveals)), false, proxyPubKeys, err
return result, err
}
}
10 changes: 7 additions & 3 deletions x/tally/keeper/filter_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import (

"github.com/stretchr/testify/require"

"github.com/sedaprotocol/seda-chain/x/tally/keeper"
"github.com/sedaprotocol/seda-chain/x/tally/types"
)

func FuzzStdDevFilter(f *testing.F) {
fixture := initFixture(f)

err := fixture.tallyKeeper.SetParams(fixture.Context(), types.DefaultParams())
require.NoError(f, err)

f.Fuzz(func(t *testing.T, n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 int64) {
source := rand.NewSource(time.Now().UnixNano())
t.Log("random testing with seed", source.Int63())
Expand Down Expand Up @@ -57,8 +61,8 @@ func FuzzStdDevFilter(f *testing.F) {
filter, err := hex.DecodeString(filterHex)
require.NoError(t, err)

outliers, _, _, err := keeper.ApplyFilter(filter, reveals)
require.Equal(t, expOutliers, outliers)
result, err := fixture.tallyKeeper.ApplyFilter(fixture.Context(), filter, reveals, uint16(len(reveals)))
require.Equal(t, expOutliers, result.Outliers)
require.ErrorIs(t, err, nil)
})
}
Loading
Loading