From 03cb3c4eda992e50e624019c8df75c0928beb44c Mon Sep 17 00:00:00 2001 From: hacheigriega Date: Tue, 17 Dec 2024 15:40:26 +0900 Subject: [PATCH 1/4] feat(tally): filter gas metering --- proto/sedachain/tally/v1/tally.proto | 10 +- x/tally/keeper/endblock.go | 11 ++- x/tally/keeper/filter.go | 42 ++++++-- x/tally/keeper/filter_fuzz_test.go | 10 +- x/tally/keeper/filter_test.go | 46 +++++++-- x/tally/keeper/integration_test.go | 2 +- x/tally/types/params.go | 11 ++- x/tally/types/tally.pb.go | 139 ++++++++++++++++++++++++--- 8 files changed, 232 insertions(+), 39 deletions(-) diff --git a/proto/sedachain/tally/v1/tally.proto b/proto/sedachain/tally/v1/tally.proto index 7b169bb6..91c4d68f 100644 --- a/proto/sedachain/tally/v1/tally.proto +++ b/proto/sedachain/tally/v1/tally.proto @@ -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; } diff --git a/x/tally/keeper/endblock.go b/x/tally/keeper/endblock.go index a2d80a97..cea05aec 100644 --- a/x/tally/keeper/endblock.go +++ b/x/tally/keeper/endblock.go @@ -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) } @@ -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) } @@ -247,7 +248,7 @@ 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) + gasLimit := min(req.TallyGasLimit, maxGasLimit) - filterResult.GasUsed k.Logger(ctx).Info( "executing tally VM", @@ -277,7 +278,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 } diff --git a/x/tally/keeper/filter.go b/x/tally/keeper/filter.go index 22bd6271..2f708d35 100644 --- a/x/tally/keeper/filter.go +++ b/x/tally/keeper/filter.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/sedaprotocol/seda-chain/x/tally/types" ) @@ -13,15 +14,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 int64) (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) @@ -39,34 +50,45 @@ 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) 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 + 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 } } diff --git a/x/tally/keeper/filter_fuzz_test.go b/x/tally/keeper/filter_fuzz_test.go index 2acc3de4..129a154d 100644 --- a/x/tally/keeper/filter_fuzz_test.go +++ b/x/tally/keeper/filter_fuzz_test.go @@ -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()) @@ -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, int64(len(reveals))) + require.Equal(t, expOutliers, result.Outliers) require.ErrorIs(t, err, nil) }) } diff --git a/x/tally/keeper/filter_test.go b/x/tally/keeper/filter_test.go index 30847418..3da72f1a 100644 --- a/x/tally/keeper/filter_test.go +++ b/x/tally/keeper/filter_test.go @@ -8,11 +8,16 @@ import ( "github.com/stretchr/testify/require" - "github.com/sedaprotocol/seda-chain/x/tally/keeper" "github.com/sedaprotocol/seda-chain/x/tally/types" ) func TestFilter(t *testing.T) { + f := initFixture(t) + + defaultParams := types.DefaultParams() + err := f.tallyKeeper.SetParams(f.Context(), defaultParams) + require.NoError(t, err) + tests := []struct { name string tallyInputAsHex string @@ -20,6 +25,7 @@ func TestFilter(t *testing.T) { reveals []types.RevealBody consensus bool consPubKeys []string // expected proxy public keys in basic consensus + gasUsed uint64 wantErr error }{ { @@ -35,6 +41,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostNone, wantErr: nil, }, { @@ -52,6 +59,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 7, wantErr: nil, }, { @@ -65,6 +73,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 3, wantErr: nil, }, { @@ -82,6 +91,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 7, wantErr: nil, }, { @@ -95,6 +105,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 3, wantErr: nil, }, { @@ -111,6 +122,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: 0, wantErr: types.ErrNoBasicConsensus, }, { @@ -127,6 +139,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 6, wantErr: types.ErrCorruptReveals, }, { @@ -202,6 +215,7 @@ func TestFilter(t *testing.T) { "02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4c3", "034c0f86f0cb61f9ddb47c4ba0b2ca0470962b5a1c50bee3a563184979672195f4", }, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 6, wantErr: nil, }, { @@ -276,6 +290,7 @@ func TestFilter(t *testing.T) { "02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4c3", "034c0f86f0cb61f9ddb47c4ba0b2ca0470962b5a1c50bee3a563184979672195f4", }, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 6, wantErr: types.ErrCorruptReveals, }, { @@ -340,6 +355,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: 0, wantErr: types.ErrNoBasicConsensus, }, { @@ -354,6 +370,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: []string{"02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4g3"}, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 4, wantErr: nil, }, { @@ -368,6 +385,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: []string{"02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4g3"}, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 4, wantErr: nil, }, { @@ -382,6 +400,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: []string{"02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4g3"}, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 4, wantErr: nil, }, { @@ -396,6 +415,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: []string{"02100efce2a783cc7a3fbf9c5d15d4cc6e263337651312f21a35d30c16cb38f4g3"}, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 4, wantErr: nil, }, { @@ -410,6 +430,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: 0, wantErr: types.ErrNoBasicConsensus, }, { @@ -430,6 +451,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 7, wantErr: nil, }, { @@ -446,6 +468,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 6, wantErr: nil, }, { @@ -462,6 +485,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierMode * 6, wantErr: nil, }, { @@ -478,6 +502,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -494,6 +519,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -503,6 +529,7 @@ func TestFilter(t *testing.T) { reveals: []types.RevealBody{}, consensus: false, consPubKeys: nil, + gasUsed: 0, wantErr: types.ErrEmptyReveals, }, { @@ -514,6 +541,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev, wantErr: nil, }, { @@ -530,6 +558,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -546,6 +575,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -562,6 +592,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -578,6 +609,7 @@ func TestFilter(t *testing.T) { }, consensus: true, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 6, wantErr: nil, }, { @@ -592,6 +624,7 @@ func TestFilter(t *testing.T) { }, consensus: false, consPubKeys: nil, + gasUsed: defaultParams.FilterGasCostMultiplierStddev * 4, wantErr: nil, }, } @@ -610,18 +643,19 @@ func TestFilter(t *testing.T) { sort.Strings(tt.reveals[i].ProxyPubKeys) } - outliers, cons, pks, err := keeper.ApplyFilter(filter, tt.reveals) + result, err := f.tallyKeeper.ApplyFilter(f.Context(), filter, tt.reveals, int64(len(tt.reveals))) require.ErrorIs(t, err, tt.wantErr) if tt.consPubKeys == nil { - require.Nil(t, nil, pks) + require.Nil(t, nil, result.ProxyPubKeys) } else { for _, pk := range tt.consPubKeys { - require.Contains(t, pks, pk) + require.Contains(t, result.ProxyPubKeys, pk) } } - require.Equal(t, tt.outliers, outliers) - require.Equal(t, tt.consensus, cons) + require.Equal(t, tt.outliers, result.Outliers) + require.Equal(t, tt.consensus, result.Consensus) + require.Equal(t, tt.gasUsed, result.GasUsed) }) } } diff --git a/x/tally/keeper/integration_test.go b/x/tally/keeper/integration_test.go index 2f93c3cd..c07d8402 100644 --- a/x/tally/keeper/integration_test.go +++ b/x/tally/keeper/integration_test.go @@ -89,7 +89,7 @@ type fixture struct { logBuf *bytes.Buffer } -func initFixture(t *testing.T) *fixture { +func initFixture(t testing.TB) *fixture { t.Helper() tempDir := t.TempDir() diff --git a/x/tally/types/params.go b/x/tally/types/params.go index d10cf530..7219ae67 100644 --- a/x/tally/types/params.go +++ b/x/tally/types/params.go @@ -5,13 +5,19 @@ import ( ) const ( - DefaultMaxTallyGasLimit = 300_000_000_000_000 + DefaultMaxTallyGasLimit = 300_000_000_000_000 + DefaultFilterGasCostNone = 100_000 + DefaultFilterGasCostMultiplierMode = 100_000 + DefaultFilterGasCostMultiplierStddev = 100_000 ) // DefaultParams returns default tally module parameters. func DefaultParams() Params { return Params{ - MaxTallyGasLimit: DefaultMaxTallyGasLimit, + MaxTallyGasLimit: DefaultMaxTallyGasLimit, + FilterGasCostNone: DefaultFilterGasCostNone, + FilterGasCostMultiplierMode: DefaultFilterGasCostMultiplierMode, + FilterGasCostMultiplierStddev: DefaultFilterGasCostMultiplierStddev, } } @@ -20,5 +26,6 @@ func (p *Params) Validate() error { if p.MaxTallyGasLimit <= 0 { return sdkerrors.ErrInvalidRequest.Wrapf("max tally gas limit must be greater than 0: %d", p.MaxTallyGasLimit) } + return nil } diff --git a/x/tally/types/tally.pb.go b/x/tally/types/tally.pb.go index 99c06c3d..07443880 100644 --- a/x/tally/types/tally.pb.go +++ b/x/tally/types/tally.pb.go @@ -24,8 +24,16 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Params defines the parameters for the tally module. type Params struct { - // max_tally_gas_limit is the maximum gas limit for a tally request. + // MaxTallyGasLimit is the maximum gas limit for a tally request. MaxTallyGasLimit uint64 `protobuf:"varint,1,opt,name=max_tally_gas_limit,json=maxTallyGasLimit,proto3" json:"max_tally_gas_limit,omitempty"` + // FilterGasCostNone is the gas cost for a filter type none. + FilterGasCostNone uint64 `protobuf:"varint,2,opt,name=filter_gas_cost_none,json=filterGasCostNone,proto3" json:"filter_gas_cost_none,omitempty"` + // FilterGasCostMultiplierMode is the gas cost multiplier for a filter type + // mode. + FilterGasCostMultiplierMode uint64 `protobuf:"varint,3,opt,name=filter_gas_cost_multiplier_mode,json=filterGasCostMultiplierMode,proto3" json:"filter_gas_cost_multiplier_mode,omitempty"` + // FilterGasCostMultiplierStdDev is the gas cost multiplier for a filter type + // stddev. + FilterGasCostMultiplierStddev uint64 `protobuf:"varint,4,opt,name=filter_gas_cost_multiplier_stddev,json=filterGasCostMultiplierStddev,proto3" json:"filter_gas_cost_multiplier_stddev,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -68,6 +76,27 @@ func (m *Params) GetMaxTallyGasLimit() uint64 { return 0 } +func (m *Params) GetFilterGasCostNone() uint64 { + if m != nil { + return m.FilterGasCostNone + } + return 0 +} + +func (m *Params) GetFilterGasCostMultiplierMode() uint64 { + if m != nil { + return m.FilterGasCostMultiplierMode + } + return 0 +} + +func (m *Params) GetFilterGasCostMultiplierStddev() uint64 { + if m != nil { + return m.FilterGasCostMultiplierStddev + } + return 0 +} + func init() { proto.RegisterType((*Params)(nil), "sedachain.tally.v1.Params") } @@ -75,18 +104,25 @@ func init() { func init() { proto.RegisterFile("sedachain/tally/v1/tally.proto", fileDescriptor_2917df8a6808d5e2) } var fileDescriptor_2917df8a6808d5e2 = []byte{ - // 176 bytes of a gzipped FileDescriptorProto + // 275 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2b, 0x4e, 0x4d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x2f, 0x49, 0xcc, 0xc9, 0xa9, 0xd4, 0x2f, 0x33, 0x84, 0x30, - 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x84, 0xe0, 0xf2, 0x7a, 0x10, 0xe1, 0x32, 0x43, 0x25, - 0x73, 0x2e, 0xb6, 0x80, 0xc4, 0xa2, 0xc4, 0xdc, 0x62, 0x21, 0x5d, 0x2e, 0xe1, 0xdc, 0xc4, 0x8a, - 0x78, 0xb0, 0x4c, 0x7c, 0x7a, 0x62, 0x71, 0x7c, 0x4e, 0x66, 0x6e, 0x66, 0x89, 0x04, 0xa3, 0x02, - 0xa3, 0x06, 0x4b, 0x90, 0x40, 0x6e, 0x62, 0x45, 0x08, 0x48, 0xc6, 0x3d, 0xb1, 0xd8, 0x07, 0x24, - 0xee, 0xe4, 0x75, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, - 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x06, 0xe9, 0x99, - 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x20, 0x1b, 0xc1, 0x96, 0x27, 0xe7, 0xe7, - 0x80, 0x39, 0xba, 0x10, 0xf7, 0x55, 0x40, 0x5d, 0x58, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, 0x06, - 0x56, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x11, 0xa0, 0xcf, 0x24, 0xc1, 0x00, 0x00, 0x00, + 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x84, 0xe0, 0xf2, 0x7a, 0x10, 0xe1, 0x32, 0x43, 0xa5, + 0x1f, 0x8c, 0x5c, 0x6c, 0x01, 0x89, 0x45, 0x89, 0xb9, 0xc5, 0x42, 0xba, 0x5c, 0xc2, 0xb9, 0x89, + 0x15, 0xf1, 0x60, 0xa9, 0xf8, 0xf4, 0xc4, 0xe2, 0xf8, 0x9c, 0xcc, 0xdc, 0xcc, 0x12, 0x09, 0x46, + 0x05, 0x46, 0x0d, 0x96, 0x20, 0x81, 0xdc, 0xc4, 0x8a, 0x10, 0x90, 0x8c, 0x7b, 0x62, 0xb1, 0x0f, + 0x48, 0x5c, 0x48, 0x9f, 0x4b, 0x24, 0x2d, 0x33, 0xa7, 0x24, 0xb5, 0x08, 0xac, 0x36, 0x39, 0xbf, + 0xb8, 0x24, 0x3e, 0x2f, 0x3f, 0x2f, 0x55, 0x82, 0x09, 0xac, 0x5e, 0x10, 0x22, 0xe7, 0x9e, 0x58, + 0xec, 0x9c, 0x5f, 0x5c, 0xe2, 0x97, 0x9f, 0x97, 0x2a, 0xe4, 0xc2, 0x25, 0x8f, 0xae, 0x21, 0xb7, + 0x34, 0xa7, 0x24, 0xb3, 0x20, 0x27, 0x33, 0xb5, 0x28, 0x3e, 0x37, 0x3f, 0x25, 0x55, 0x82, 0x19, + 0xac, 0x57, 0x1a, 0x45, 0xaf, 0x2f, 0x5c, 0x8d, 0x6f, 0x7e, 0x4a, 0xaa, 0x90, 0x07, 0x97, 0x22, + 0x1e, 0x53, 0x8a, 0x4b, 0x52, 0x52, 0x52, 0xcb, 0x24, 0x58, 0xc0, 0xe6, 0xc8, 0xe2, 0x30, 0x27, + 0x18, 0xac, 0xc8, 0xc9, 0xeb, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, + 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x0c, + 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x41, 0x61, 0x06, 0x0e, 0xbe, + 0xe4, 0xfc, 0x1c, 0x30, 0x47, 0x17, 0x12, 0xc2, 0x15, 0xd0, 0x30, 0x2e, 0xa9, 0x2c, 0x48, 0x2d, + 0x4e, 0x62, 0x03, 0x2b, 0x31, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x5a, 0xc5, 0x20, 0x83, + 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -109,6 +145,21 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.FilterGasCostMultiplierStddev != 0 { + i = encodeVarintTally(dAtA, i, uint64(m.FilterGasCostMultiplierStddev)) + i-- + dAtA[i] = 0x20 + } + if m.FilterGasCostMultiplierMode != 0 { + i = encodeVarintTally(dAtA, i, uint64(m.FilterGasCostMultiplierMode)) + i-- + dAtA[i] = 0x18 + } + if m.FilterGasCostNone != 0 { + i = encodeVarintTally(dAtA, i, uint64(m.FilterGasCostNone)) + i-- + dAtA[i] = 0x10 + } if m.MaxTallyGasLimit != 0 { i = encodeVarintTally(dAtA, i, uint64(m.MaxTallyGasLimit)) i-- @@ -137,6 +188,15 @@ func (m *Params) Size() (n int) { if m.MaxTallyGasLimit != 0 { n += 1 + sovTally(uint64(m.MaxTallyGasLimit)) } + if m.FilterGasCostNone != 0 { + n += 1 + sovTally(uint64(m.FilterGasCostNone)) + } + if m.FilterGasCostMultiplierMode != 0 { + n += 1 + sovTally(uint64(m.FilterGasCostMultiplierMode)) + } + if m.FilterGasCostMultiplierStddev != 0 { + n += 1 + sovTally(uint64(m.FilterGasCostMultiplierStddev)) + } return n } @@ -194,6 +254,63 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FilterGasCostNone", wireType) + } + m.FilterGasCostNone = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTally + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FilterGasCostNone |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FilterGasCostMultiplierMode", wireType) + } + m.FilterGasCostMultiplierMode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTally + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FilterGasCostMultiplierMode |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FilterGasCostMultiplierStddev", wireType) + } + m.FilterGasCostMultiplierStddev = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTally + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FilterGasCostMultiplierStddev |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTally(dAtA[iNdEx:]) From f363304d3d87e56dd565b83159f8980e6bede29b Mon Sep 17 00:00:00 2001 From: hacheigriega Date: Tue, 17 Dec 2024 15:59:32 +0900 Subject: [PATCH 2/4] fix(tally): return zero for filter gas used if filter has not been applied --- x/tally/keeper/filter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/tally/keeper/filter.go b/x/tally/keeper/filter.go index 2f708d35..05faaff0 100644 --- a/x/tally/keeper/filter.go +++ b/x/tally/keeper/filter.go @@ -74,6 +74,7 @@ func (k Keeper) ApplyFilter(ctx sdk.Context, input []byte, reveals []types.Revea return result, types.ErrInvalidFilterType } if err != nil { + result.GasUsed = 0 return result, err } From 53cbd3dc3627beaac4fec034631641b7d5e58fc6 Mon Sep 17 00:00:00 2001 From: hacheigriega Date: Tue, 17 Dec 2024 22:16:44 +0900 Subject: [PATCH 3/4] chore(tally): address review comments --- x/tally/keeper/endblock.go | 7 ++++++- x/tally/keeper/filter.go | 3 ++- x/tally/keeper/filter_fuzz_test.go | 2 +- x/tally/keeper/filter_test.go | 2 +- x/tally/types/abci_types.go | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/x/tally/keeper/endblock.go b/x/tally/keeper/endblock.go index cea05aec..19140501 100644 --- a/x/tally/keeper/endblock.go +++ b/x/tally/keeper/endblock.go @@ -248,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) - filterResult.GasUsed + 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", diff --git a/x/tally/keeper/filter.go b/x/tally/keeper/filter.go index 05faaff0..faee9165 100644 --- a/x/tally/keeper/filter.go +++ b/x/tally/keeper/filter.go @@ -5,6 +5,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/sedaprotocol/seda-chain/x/tally/types" ) @@ -27,7 +28,7 @@ type FilterResult struct { // 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 int64) (FilterResult, 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)) diff --git a/x/tally/keeper/filter_fuzz_test.go b/x/tally/keeper/filter_fuzz_test.go index 129a154d..7bdcc193 100644 --- a/x/tally/keeper/filter_fuzz_test.go +++ b/x/tally/keeper/filter_fuzz_test.go @@ -61,7 +61,7 @@ func FuzzStdDevFilter(f *testing.F) { filter, err := hex.DecodeString(filterHex) require.NoError(t, err) - result, err := fixture.tallyKeeper.ApplyFilter(fixture.Context(), filter, reveals, int64(len(reveals))) + result, err := fixture.tallyKeeper.ApplyFilter(fixture.Context(), filter, reveals, uint16(len(reveals))) require.Equal(t, expOutliers, result.Outliers) require.ErrorIs(t, err, nil) }) diff --git a/x/tally/keeper/filter_test.go b/x/tally/keeper/filter_test.go index 3da72f1a..79925ccb 100644 --- a/x/tally/keeper/filter_test.go +++ b/x/tally/keeper/filter_test.go @@ -643,7 +643,7 @@ func TestFilter(t *testing.T) { sort.Strings(tt.reveals[i].ProxyPubKeys) } - result, err := f.tallyKeeper.ApplyFilter(f.Context(), filter, tt.reveals, int64(len(tt.reveals))) + result, err := f.tallyKeeper.ApplyFilter(f.Context(), filter, tt.reveals, uint16(len(tt.reveals))) require.ErrorIs(t, err, tt.wantErr) if tt.consPubKeys == nil { require.Nil(t, nil, result.ProxyPubKeys) diff --git a/x/tally/types/abci_types.go b/x/tally/types/abci_types.go index f8b6b55c..1db768e5 100644 --- a/x/tally/types/abci_types.go +++ b/x/tally/types/abci_types.go @@ -21,7 +21,7 @@ type Request struct { GasPrice string `json:"gas_price"` Memo string `json:"memo"` PaybackAddress string `json:"payback_address"` - ReplicationFactor int64 `json:"replication_factor"` + ReplicationFactor uint16 `json:"replication_factor"` ConsensusFilter string `json:"consensus_filter"` Commits map[string][]byte `json:"commits"` Reveals map[string]RevealBody `json:"reveals"` From 050dea4b4eadbabcd3d059a2fc9b6640ed27c0fc Mon Sep 17 00:00:00 2001 From: hacheigriega Date: Wed, 18 Dec 2024 10:11:25 +0900 Subject: [PATCH 4/4] test(tally): clean up integration test msg construction --- x/tally/keeper/endblock_test.go | 40 +++--- x/tally/keeper/integration_helpers_test.go | 154 ++++++++++++--------- 2 files changed, 105 insertions(+), 89 deletions(-) diff --git a/x/tally/keeper/endblock_test.go b/x/tally/keeper/endblock_test.go index 68caf19b..53d54604 100644 --- a/x/tally/keeper/endblock_test.go +++ b/x/tally/keeper/endblock_test.go @@ -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) { @@ -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]) -} diff --git a/x/tally/keeper/integration_helpers_test.go b/x/tally/keeper/integration_helpers_test.go index 97a89a62..ef969f6f 100644 --- a/x/tally/keeper/integration_helpers_test.go +++ b/x/tally/keeper/integration_helpers_test.go @@ -23,6 +23,11 @@ import ( wasmstoragetypes "github.com/sedaprotocol/seda-chain/x/wasm-storage/types" ) +const ( + salt = "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501" + reveal = "Ghkvq84TmIuEmU1ClubNxBjVXi8df5QhiNQEC5T8V6w=" +) + // commitRevealDataRequest performs the following steps to prepare a // tally-ready data request. // 1. Generate staker key and add to allowlist. @@ -42,7 +47,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri f.Context(), f.coreContractAddr, f.deployer, - []byte(fmt.Sprintf(addToAllowListMsg, stakerPubKey)), + addToAllowListMsg(stakerPubKey), sdk.NewCoins(), ) require.NoError(t, err) @@ -64,7 +69,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri f.Context(), f.coreContractAddr, staker, - []byte(fmt.Sprintf(stakeMsg, stakerPubKey, proof)), + stakeMsg(stakerPubKey, proof), sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), ) require.NoError(t, err) @@ -74,7 +79,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri f.Context(), f.coreContractAddr, f.deployer, - []byte(fmt.Sprintf(postDataRequestMsg, hex.EncodeToString(execProgram.Hash), hex.EncodeToString(tallyProgram.Hash), requestMemo)), + postDataRequestMsg(execProgram.Hash, tallyProgram.Hash, requestMemo), sdk.NewCoins(), ) require.NoError(t, err) @@ -91,8 +96,8 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri // 5. The staker commits and reveals. revealBody := types.RevealBody{ ID: drID, - Salt: []byte("9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501"), - Reveal: "Ghkvq84TmIuEmU1ClubNxBjVXi8df5QhiNQEC5T8V6w=", + Salt: []byte(salt), + Reveal: reveal, GasUsed: 0, ExitCode: 0, ProxyPubKeys: []string{}, @@ -105,7 +110,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri f.Context(), f.coreContractAddr, staker, - []byte(fmt.Sprintf(commitMsg, drID, commitment, stakerPubKey, proof)), + commitMsg(drID, commitment, stakerPubKey, proof), sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), ) require.NoError(t, err) @@ -115,7 +120,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri f.Context(), f.coreContractAddr, staker, - []byte(fmt.Sprintf(revealMsg, drID, drID, stakerPubKey, proof)), + revealMsg(drID, stakerPubKey, proof), sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), ) require.NoError(t, err) @@ -123,66 +128,81 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri return res.DrID } -var addToAllowListMsg = `{ - "add_to_allowlist": { - "public_key": "%s" - } -}` - -var stakeMsg = `{ - "stake": { - "public_key": "%s", - "proof": "%s", - "memo": "YWRkcmVzcw==" - } -}` - -var postDataRequestMsg = `{ - "post_data_request": { - "posted_dr": { - "version": "0.0.1", - "exec_program_id": "%s", - "exec_inputs": "ZXhlY19pbnB1dHM=", - "exec_gas_limit": 10, - "tally_program_id": "%s", - "tally_inputs": "dGFsbHlfaW5wdXRz", - "tally_gas_limit": 10, - "replication_factor": 1, - "consensus_filter": "AA==", - "gas_price": "10", - "memo": "%s" - }, - "seda_payload": "", - "payback_address": "AQID" - } -}` - -var commitMsg = `{ - "commit_data_result": { - "dr_id": "%s", - "commitment": "%s", - "public_key": "%s", - "proof": "%s" - } -}` - -var revealMsg = `{ - "reveal_data_result": { - "dr_id": "%s", - "reveal_body": { - "id": "%s", - "salt": "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501", - "exit_code": 0, - "gas_used": 0, - "reveal": "Ghkvq84TmIuEmU1ClubNxBjVXi8df5QhiNQEC5T8V6w=", - "proxy_public_keys": [] - }, - "public_key": "%s", - "proof": "%s", - "stderr": [], - "stdout": [] - } -}` +func addToAllowListMsg(stakerPubKey string) []byte { + var addToAllowListMsg = `{ + "add_to_allowlist": { + "public_key": "%s" + } + }` + return []byte(fmt.Sprintf(addToAllowListMsg, stakerPubKey)) +} + +func stakeMsg(stakerPubKey, proof string) []byte { + var stakeMsg = `{ + "stake": { + "public_key": "%s", + "proof": "%s", + "memo": "YWRkcmVzcw==" + } + }` + return []byte(fmt.Sprintf(stakeMsg, stakerPubKey, proof)) +} + +func postDataRequestMsg(execProgHash, tallyProgHash []byte, requestMemo string) []byte { + var postDataRequestMsg = `{ + "post_data_request": { + "posted_dr": { + "version": "0.0.1", + "exec_program_id": "%s", + "exec_inputs": "ZXhlY19pbnB1dHM=", + "exec_gas_limit": 10, + "tally_program_id": "%s", + "tally_inputs": "dGFsbHlfaW5wdXRz", + "tally_gas_limit": 10, + "replication_factor": 1, + "consensus_filter": "AA==", + "gas_price": "10", + "memo": "%s" + }, + "seda_payload": "", + "payback_address": "AQID" + } + }` + return []byte(fmt.Sprintf(postDataRequestMsg, hex.EncodeToString(execProgHash), hex.EncodeToString(tallyProgHash), requestMemo)) +} + +func commitMsg(drID, commitment, stakerPubKey, proof string) []byte { + var commitMsg = `{ + "commit_data_result": { + "dr_id": "%s", + "commitment": "%s", + "public_key": "%s", + "proof": "%s" + } + }` + return []byte(fmt.Sprintf(commitMsg, drID, commitment, stakerPubKey, proof)) +} + +func revealMsg(drID, stakerPubKey, proof string) []byte { + var revealMsg = `{ + "reveal_data_result": { + "dr_id": "%s", + "reveal_body": { + "id": "%s", + "salt": "%s", + "exit_code": 0, + "gas_used": 0, + "reveal": "%s", + "proxy_public_keys": [] + }, + "public_key": "%s", + "proof": "%s", + "stderr": [], + "stdout": [] + } + }` + return []byte(fmt.Sprintf(revealMsg, drID, drID, salt, reveal, stakerPubKey, proof)) +} // generateStakeProof generates a proof for a stake message given a // base64-encoded memo.