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

Refactor tally process to updated specs #452

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
69 changes: 40 additions & 29 deletions x/tally/keeper/endblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,44 +209,57 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
sort.Strings(reveals[i].ProxyPubKeys)
}

result.execGasUsed = calculateExecGasUsed(reveals)

filter, err := base64.StdEncoding.DecodeString(req.ConsensusFilter)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrDecodingConsensusFilter, req)
}
// Convert base64-encoded payback address to hex encoding that
// the tally VM expects.
decodedBytes, err := base64.StdEncoding.DecodeString(req.PaybackAddress)
// Phase I: Filtering
filter, err := k.BuildFilter(ctx, req.ConsensusFilter, req.ReplicationFactor)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrDecodingPaybackAddress, req)
return result, err
}
paybackAddrHex := hex.EncodeToString(decodedBytes)

filterResult, err := k.ApplyFilter(ctx, filter, reveals, req.ReplicationFactor)
filterResult, err := ApplyFilter(filter, reveals)
result.consensus = filterResult.Consensus
result.proxyPubKeys = filterResult.ProxyPubKeys
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrApplyingFilter, req)
}

// Phase II: Tally Program Execution
vmRes, err := k.ExecuteTallyProgram(ctx, req, filterResult, reveals)
if err != nil {
return TallyResult{}, err
}
result.stdout = vmRes.Stdout
result.stderr = vmRes.Stderr
result.result = vmRes.Result
result.exitInfo = vmRes.ExitInfo
result.tallyGasUsed = vmRes.GasUsed + filterResult.GasUsed

// Phase III: Calculate Payouts
result.execGasUsed = calculateExecGasUsed(reveals)

return result, nil
}

func (k Keeper) ExecuteTallyProgram(ctx sdk.Context, req types.Request, filterResult FilterResult, reveals []types.RevealBody) (tallyvm.VmResult, error) {
tallyProgram, err := k.wasmStorageKeeper.GetOracleProgram(ctx, req.TallyProgramID)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrFindingTallyProgram, req)
return tallyvm.VmResult{}, k.logErrAndRet(ctx, err, types.ErrFindingTallyProgram, req)
}
tallyInputs, err := base64.StdEncoding.DecodeString(req.TallyInputs)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrDecodingTallyInputs, req)
return tallyvm.VmResult{}, k.logErrAndRet(ctx, err, types.ErrDecodingTallyInputs, req)
}

args, err := tallyVMArg(tallyInputs, reveals, filterResult.Outliers)
// Convert base64-encoded payback address to hex encoding that
// the tally VM expects.
decodedBytes, err := base64.StdEncoding.DecodeString(req.PaybackAddress)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrConstructingTallyVMArgs, req)
return tallyvm.VmResult{}, k.logErrAndRet(ctx, err, types.ErrDecodingPaybackAddress, req)
}
paybackAddrHex := hex.EncodeToString(decodedBytes)

// Adjust gas limit based on the gas used by the filter.
maxGasLimit, err := k.GetMaxTallyGasLimit(ctx)
if err != nil {
return result, k.logErrAndRet(ctx, err, types.ErrGettingMaxTallyGasLimit, req)
return tallyvm.VmResult{}, k.logErrAndRet(ctx, err, types.ErrGettingMaxTallyGasLimit, req)
}
var gasLimit uint64
if min(req.TallyGasLimit, maxGasLimit) > filterResult.GasUsed {
Expand All @@ -255,17 +268,22 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
gasLimit = 0
}

args, err := tallyVMArg(tallyInputs, reveals, filterResult.Outliers)
if err != nil {
return tallyvm.VmResult{}, k.logErrAndRet(ctx, err, types.ErrConstructingTallyVMArgs, req)
}

k.Logger(ctx).Info(
"executing tally VM",
"request_id", req.ID,
"tally_program_id", req.TallyProgramID,
"consensus", result.consensus,
"consensus", filterResult.Consensus,
"arguments", args,
)

vmRes := tallyvm.ExecuteTallyVm(tallyProgram.Bytecode, args, map[string]string{
return tallyvm.ExecuteTallyVm(tallyProgram.Bytecode, args, map[string]string{
"VM_MODE": "tally",
"CONSENSUS": fmt.Sprintf("%v", result.consensus),
"CONSENSUS": fmt.Sprintf("%v", filterResult.Consensus),
"BLOCK_HEIGHT": fmt.Sprintf("%d", ctx.BlockHeight()),
"DR_ID": req.ID,
"DR_REPLICATION_FACTOR": fmt.Sprintf("%v", req.ReplicationFactor),
Expand All @@ -278,14 +296,7 @@ func (k Keeper) FilterAndTally(ctx sdk.Context, req types.Request) (TallyResult,
"DR_GAS_PRICE": req.GasPrice,
"DR_MEMO": req.Memo,
"DR_PAYBACK_ADDRESS": paybackAddrHex,
})
result.stdout = vmRes.Stdout
result.stderr = vmRes.Stderr
result.result = vmRes.Result
result.exitInfo = vmRes.ExitInfo
result.tallyGasUsed = vmRes.GasUsed + filterResult.GasUsed

return result, nil
}), nil
}

// logErrAndRet logs the base error along with the request ID for
Expand Down
64 changes: 35 additions & 29 deletions x/tally/keeper/filter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"encoding/base64"
"errors"
"fmt"

Expand All @@ -22,19 +23,48 @@ type FilterResult struct {
GasUsed uint64 // gas used for filter
}

// BuildFilter builds a filter based on the requestor-provided input.
func (k Keeper) BuildFilter(ctx sdk.Context, filterInput string, replicationFactor uint16) (types.Filter, error) {
input, err := base64.StdEncoding.DecodeString(filterInput)
if err != nil {
return nil, err
}
if len(input) == 0 {
return nil, types.ErrInvalidFilterType
}

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

var filter types.Filter
switch input[0] {
case filterTypeNone:
filter = types.NewFilterNone(params.FilterGasCostNone)
case filterTypeMode:
filter, err = types.NewFilterMode(input, params.FilterGasCostMultiplierMode, replicationFactor)
case filterTypeStdDev:
filter, err = types.NewFilterStdDev(input, params.FilterGasCostMultiplierStddev, replicationFactor)
default:
return nil, types.ErrInvalidFilterType
}
if err != nil {
return nil, err
}
return filter, nil
}

// 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 (k Keeper) ApplyFilter(ctx sdk.Context, input []byte, reveals []types.RevealBody, replicationFactor uint16) (FilterResult, error) {
func ApplyFilter(filter types.Filter, reveals []types.RevealBody) (FilterResult, error) {
var result FilterResult
result.Outliers = make([]int, len(reveals))

if len(input) == 0 {
return result, types.ErrInvalidFilterType
}
result.GasUsed = filter.GasCost()

// Determine basic consensus on tuple of (exit_code, proxy_pub_keys)
var maxFreq int
Expand All @@ -55,30 +85,6 @@ func (k Keeper) ApplyFilter(ctx sdk.Context, input []byte, reveals []types.Revea
}
result.ProxyPubKeys = proxyPubKeys

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

var filter types.Filter
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)
case filterTypeStdDev:
filter, err = types.NewFilterStdDev(input)
result.GasUsed = params.FilterGasCostMultiplierStddev * uint64(replicationFactor)
default:
return result, types.ErrInvalidFilterType
}
if err != nil {
result.GasUsed = 0
return result, err
}

outliers, err := filter.ApplyFilter(reveals)
switch {
case err == nil:
Expand Down
7 changes: 5 additions & 2 deletions x/tally/keeper/filter_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/stretchr/testify/require"

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

Expand Down Expand Up @@ -58,10 +59,12 @@ func FuzzStdDevFilter(f *testing.F) {
bz := make([]byte, 8)
binary.BigEndian.PutUint64(bz, uint64(neighborDist*1e6))
filterHex := fmt.Sprintf("02%s01000000000000000b726573756C742E74657874", hex.EncodeToString(bz)) // max_sigma = neighborDist, number_type = int64, json_path = result.text
filter, err := hex.DecodeString(filterHex)
filterInput, err := hex.DecodeString(filterHex)
require.NoError(t, err)

result, err := fixture.tallyKeeper.ApplyFilter(fixture.Context(), filter, reveals, uint16(len(reveals)))
filter, err := fixture.tallyKeeper.BuildFilter(fixture.Context(), base64.StdEncoding.EncodeToString(filterInput), uint16(len(reveals)))
require.NoError(t, err)
result, err := keeper.ApplyFilter(filter, reveals)
require.Equal(t, expOutliers, result.Outliers)
require.ErrorIs(t, err, nil)
})
Expand Down
14 changes: 9 additions & 5 deletions x/tally/keeper/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/stretchr/testify/require"

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

Expand Down Expand Up @@ -122,7 +123,7 @@ func TestFilter(t *testing.T) {
},
consensus: false,
consPubKeys: nil,
gasUsed: 0,
gasUsed: defaultParams.FilterGasCostMultiplierMode * 6,
wantErr: types.ErrNoBasicConsensus,
},
{
Expand Down Expand Up @@ -355,7 +356,7 @@ func TestFilter(t *testing.T) {
},
consensus: false,
consPubKeys: nil,
gasUsed: 0,
gasUsed: defaultParams.FilterGasCostMultiplierMode * 6,
wantErr: types.ErrNoBasicConsensus,
},
{
Expand Down Expand Up @@ -430,7 +431,7 @@ func TestFilter(t *testing.T) {
},
consensus: false,
consPubKeys: nil,
gasUsed: 0,
gasUsed: defaultParams.FilterGasCostMultiplierMode * 4,
wantErr: types.ErrNoBasicConsensus,
},
{
Expand Down Expand Up @@ -630,7 +631,7 @@ func TestFilter(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter, err := hex.DecodeString(tt.tallyInputAsHex)
filterInput, err := hex.DecodeString(tt.tallyInputAsHex)
require.NoError(t, err)

// For illustration
Expand All @@ -643,7 +644,10 @@ func TestFilter(t *testing.T) {
sort.Strings(tt.reveals[i].ProxyPubKeys)
}

result, err := f.tallyKeeper.ApplyFilter(f.Context(), filter, tt.reveals, uint16(len(tt.reveals)))
filter, err := f.tallyKeeper.BuildFilter(f.Context(), base64.StdEncoding.EncodeToString(filterInput), uint16(len(tt.reveals)))
require.NoError(t, err)

result, err := keeper.ApplyFilter(filter, tt.reveals)
require.ErrorIs(t, err, tt.wantErr)
if tt.consPubKeys == nil {
require.Nil(t, nil, result.ProxyPubKeys)
Expand Down
2 changes: 1 addition & 1 deletion x/tally/types/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestDecodeFilterInput(t *testing.T) {
b, err := hex.DecodeString(tt.hexStr)
require.NoError(t, err)

filter, err := NewFilterMode(b)
filter, err := NewFilterMode(b, 1, 1)
if tt.wantErr != nil {
require.ErrorIs(t, err, tt.wantErr)
return
Expand Down
Loading
Loading