diff --git a/integration/integration.go b/integration/integration.go index cd55aaff..a582aaab 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -48,6 +48,7 @@ func NewIntegrationApp( logger log.Logger, keys map[string]*storetypes.KVStoreKey, appCodec codec.Codec, + router *baseapp.MsgServiceRouter, modules map[string]appmodule.AppModule, ) *IntegationApp { db := dbm.NewMemDB() @@ -78,8 +79,12 @@ func NewIntegrationApp( return moduleManager.EndBlock(sdkCtx) }) - router := baseapp.NewMsgServiceRouter() router.SetInterfaceRegistry(interfaceRegistry) + configurator := module.NewConfigurator(appCodec, router, bApp.GRPCQueryRouter()) + err := moduleManager.RegisterServices(configurator) + if err != nil { + panic(err) + } bApp.SetMsgServiceRouter(router) if keys[consensusparamtypes.StoreKey] != nil { @@ -104,7 +109,7 @@ func NewIntegrationApp( } } - _, err := bApp.Commit() + _, err = bApp.Commit() if err != nil { panic(err) } diff --git a/proto/sedachain/tally/v1/tally.proto b/proto/sedachain/tally/v1/tally.proto index 91c4d68f..6f8b9f4c 100644 --- a/proto/sedachain/tally/v1/tally.proto +++ b/proto/sedachain/tally/v1/tally.proto @@ -15,4 +15,7 @@ message Params { // FilterGasCostMultiplierStdDev is the gas cost multiplier for a filter type // stddev. uint64 filter_gas_cost_multiplier_stddev = 4; + // GasCostCommitment is the gas cost for a commitment corresponding to an + // expired data request. + uint64 gas_cost_commitment = 5; } diff --git a/x/batching/keeper/integration_test.go b/x/batching/keeper/integration_test.go index cad665e1..19e6fa61 100644 --- a/x/batching/keeper/integration_test.go +++ b/x/batching/keeper/integration_test.go @@ -18,6 +18,7 @@ import ( "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/runtime" @@ -168,6 +169,7 @@ func initFixture(tb testing.TB) *fixture { ) // x/wasm + router := baseapp.NewMsgServiceRouter() wasmKeeper := wasmkeeper.NewKeeper( cdc, runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), @@ -175,7 +177,7 @@ func initFixture(tb testing.TB) *fixture { bankKeeper, stakingKeeper, nil, nil, nil, nil, - nil, nil, nil, nil, + nil, nil, router, nil, tempDir, wasmtypes.DefaultWasmConfig(), wasmCapabilities, @@ -238,7 +240,7 @@ func initFixture(tb testing.TB) *fixture { pubKeyModule := pubkey.NewAppModule(cdc, pubKeyKeeper) batchingModule := batching.NewAppModule(cdc, batchingKeeper) - integrationApp := integration.NewIntegrationApp(ctx, logger, keys, cdc, map[string]appmodule.AppModule{ + integrationApp := integration.NewIntegrationApp(ctx, logger, keys, cdc, router, map[string]appmodule.AppModule{ authtypes.ModuleName: authModule, banktypes.ModuleName: bankModule, sdkstakingtypes.ModuleName: stakingModule, diff --git a/x/pubkey/keeper/endblock_test.go b/x/pubkey/keeper/endblock_test.go index 48f76859..d796d1eb 100644 --- a/x/pubkey/keeper/endblock_test.go +++ b/x/pubkey/keeper/endblock_test.go @@ -18,6 +18,7 @@ import ( "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -48,7 +49,6 @@ import ( "github.com/sedaprotocol/seda-chain/x/pubkey/types" "github.com/sedaprotocol/seda-chain/x/staking" stakingkeeper "github.com/sedaprotocol/seda-chain/x/staking/keeper" - stakingtypes "github.com/sedaprotocol/seda-chain/x/staking/types" "github.com/sedaprotocol/seda-chain/x/vesting" ) @@ -157,19 +157,16 @@ func initFixture(tb testing.TB) *fixture { stakingModule := staking.NewAppModule(cdc, stakingKeeper, accountKeeper, bankKeeper, pubKeyKeeper) pubkeyModule := pubkey.NewAppModule(cdc, pubKeyKeeper) - integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{ - authtypes.ModuleName: authModule, - banktypes.ModuleName: bankModule, - sdkstakingtypes.ModuleName: stakingModule, - types.ModuleName: pubkeyModule, - }) - - types.RegisterMsgServer(integrationApp.MsgServiceRouter(), keeper.NewMsgServerImpl(pubKeyKeeper)) - - sdkStakingMsgServer := sdkstakingkeeper.NewMsgServerImpl(sdkStakingKeeper) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(sdkStakingMsgServer, stakingKeeper) - sdkstakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), stakingMsgServer) - stakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), stakingMsgServer) + integrationApp := integration.NewIntegrationApp( + newCtx, logger, keys, cdc, + baseapp.NewMsgServiceRouter(), + map[string]appmodule.AppModule{ + authtypes.ModuleName: authModule, + banktypes.ModuleName: bankModule, + sdkstakingtypes.ModuleName: stakingModule, + types.ModuleName: pubkeyModule, + }, + ) return &fixture{ IntegationApp: integrationApp, diff --git a/x/tally/keeper/endblock.go b/x/tally/keeper/endblock.go index e8052acb..ab0fc81e 100644 --- a/x/tally/keeper/endblock.go +++ b/x/tally/keeper/endblock.go @@ -74,8 +74,8 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err // Loop through the list to apply filter, execute tally, and post // execution result. + processedReqs := make(map[string]types.DistributionMessages) tallyResults := make([]TallyResult, len(tallyList)) - sudoMsgs := make([]types.SudoRemoveDataRequest, len(tallyList)) dataResults := make([]batchingtypes.DataResult, len(tallyList)) for i, req := range tallyList { dataResults[i] = batchingtypes.DataResult{ @@ -90,15 +90,27 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err SedaPayload: req.SedaPayload, } + var distMsgs types.DistributionMessages + var err error switch { case len(req.Commits) == 0 || len(req.Commits) < int(req.ReplicationFactor): dataResults[i].Result = []byte(fmt.Sprintf("need %d commits; received %d", req.ReplicationFactor, len(req.Commits))) dataResults[i].ExitCode = TallyExitCodeNotEnoughCommits k.Logger(ctx).Info("data request's number of commits did not meet replication factor", "request_id", req.ID) + + distMsgs, err = k.CalculateCommitterPayouts(ctx, req) + if err != nil { + return err + } case len(req.Reveals) == 0 || len(req.Reveals) < int(req.ReplicationFactor): dataResults[i].Result = []byte(fmt.Sprintf("need %d reveals; received %d", req.ReplicationFactor, len(req.Reveals))) dataResults[i].ExitCode = TallyExitCodeNotEnoughReveals k.Logger(ctx).Info("data request's number of reveals did not meet replication factor", "request_id", req.ID) + + distMsgs, err = k.CalculateCommitterPayouts(ctx, req) + if err != nil { + return err + } default: tallyResults[i] = k.FilterAndTally(ctx, req) dataResults[i].Result = tallyResults[i].result @@ -109,31 +121,27 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err k.Logger(ctx).Info("completed tally", "request_id", req.ID) k.Logger(ctx).Debug("tally result", "request_id", req.ID, "tally_result", tallyResults[i]) + + // TODO + distMsgs = types.DistributionMessages{ + Messages: []types.DistributionMessage{}, + RefundType: types.DistributionTypeNoConsensus, + } } + processedReqs[req.ID] = distMsgs dataResults[i].Id, err = dataResults[i].TryHash() if err != nil { return err } - sudoMsgs[i] = types.SudoRemoveDataRequest{ID: req.ID} } // Notify the Core Contract of tally completion. - msg, err := json.Marshal(struct { - SudoRemoveDataRequests struct { - Requests []types.SudoRemoveDataRequest `json:"requests"` - } `json:"remove_data_requests"` - }{ - SudoRemoveDataRequests: struct { - Requests []types.SudoRemoveDataRequest `json:"requests"` - }{ - Requests: sudoMsgs, - }, - }) + msg, err := types.MarshalSudoRemoveDataRequests(processedReqs) if err != nil { return err } - postRes, err := k.wasmKeeper.Sudo(ctx, coreContract, msg) + _, err = k.wasmKeeper.Sudo(ctx, coreContract, msg) if err != nil { return err } @@ -144,14 +152,8 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err if err != nil { return err } - } - for i := range sudoMsgs { - k.Logger(ctx).Info( - "tally flow completed", - "request_id", dataResults[i].DrId, - "post_result", postRes, - ) + k.Logger(ctx).Info("tally flow completed", "request_id", dataResults[i].DrId) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeTallyCompletion, @@ -167,6 +169,7 @@ func (k Keeper) ProcessTallies(ctx sdk.Context, coreContract sdk.AccAddress) err ), ) } + return nil } @@ -243,12 +246,3 @@ func (k Keeper) logErrAndRet(ctx sdk.Context, baseErr, registeredErr error, req k.Logger(ctx).Debug(baseErr.Error(), "request_id", req.ID, "error", registeredErr) return registeredErr } - -// TODO: This will become more complex when we introduce incentives. -func calculateExecGasUsed(reveals []types.RevealBody) uint64 { - var execGasUsed uint64 - for _, reveal := range reveals { - execGasUsed += reveal.GasUsed - } - return execGasUsed -} diff --git a/x/tally/keeper/endblock_test.go b/x/tally/keeper/endblock_test.go index 6c835907..bb5a985d 100644 --- a/x/tally/keeper/endblock_test.go +++ b/x/tally/keeper/endblock_test.go @@ -10,25 +10,103 @@ import ( "github.com/sedaprotocol/seda-wasm-vm/tallyvm/v2" + "github.com/sedaprotocol/seda-chain/x/tally/keeper" "github.com/sedaprotocol/seda-chain/x/tally/keeper/testdata" "github.com/sedaprotocol/seda-chain/x/tally/types" ) -func TestProcessTallies(t *testing.T) { +func TestEndBlock(t *testing.T) { f := initFixture(t) - drID := f.commitRevealDataRequest(t, "c2VkYXByb3RvY29s") + tests := []struct { + name string + memo string + replicationFactor int + numCommits int + numReveals int + timeout bool + expExitCode uint32 + }{ + { + name: "full single commit-reveal", + memo: "YzJWamRYSmxaR0YwWVE9PQ==", + replicationFactor: 1, + numCommits: 1, + numReveals: 1, + timeout: false, + expExitCode: 0, + }, + { + name: "full 5 commit-reveals", + memo: "ZnVsbCA1IGNvbW1pdC1yZXZlYWxz", + replicationFactor: 5, + numCommits: 5, + numReveals: 5, + timeout: false, + expExitCode: 0, + }, + { + name: "commit timeout", + memo: "Y29tbWl0IHRpbWVvdXQ=", + replicationFactor: 2, + numCommits: 0, + numReveals: 0, + timeout: true, + expExitCode: keeper.TallyExitCodeNotEnoughCommits, + }, + { + name: "commit timeout with 1 commit", + memo: "Y29tbWl0IHRpbWVvdXQgd2l0aCAxIGNvbW1pdA==", + replicationFactor: 2, + numCommits: 1, + numReveals: 0, + timeout: true, + expExitCode: keeper.TallyExitCodeNotEnoughCommits, + }, + { + name: "commit timeout with 2 commits", + memo: "Y29tbWl0IHRpbWVvdXQgd2l0aCAyIGNvbW1pdHM=", + replicationFactor: 2, + numCommits: 1, + numReveals: 0, + timeout: true, + expExitCode: keeper.TallyExitCodeNotEnoughCommits, + }, + { + name: "reveal timeout", + memo: "cmV2ZWFsIHRpbWVvdXQ=", + replicationFactor: 2, + numCommits: 2, + numReveals: 0, + timeout: true, + expExitCode: keeper.TallyExitCodeNotEnoughReveals, + }, + { + name: "reveal timeout with 2 reveals", + memo: "cmV2ZWFsIHRpbWVvdXQgd2l0aCAyIHJldmVhbHM=", + replicationFactor: 3, + numCommits: 3, + numReveals: 2, + timeout: true, + expExitCode: keeper.TallyExitCodeNotEnoughReveals, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + drID := f.commitRevealDataRequest(t, tt.memo, tt.replicationFactor, tt.numCommits, tt.numReveals, tt.timeout) - err := f.tallyKeeper.ProcessTallies(f.Context(), f.coreContractAddr) - require.NoError(t, err) + err := f.tallyKeeper.EndBlock(f.Context()) + require.NoError(t, err) - dataResult, err := f.batchingKeeper.GetLatestDataResult(f.Context(), drID) - require.NoError(t, err) - require.Equal(t, uint32(0), dataResult.ExitCode) + dataResult, err := f.batchingKeeper.GetLatestDataResult(f.Context(), drID) + require.NoError(t, err) + require.Equal(t, tt.expExitCode, dataResult.ExitCode) - dataResults, err := f.batchingKeeper.GetDataResults(f.Context(), false) - require.NoError(t, err) - require.Equal(t, *dataResult, dataResults[0]) + dataResults, err := f.batchingKeeper.GetDataResults(f.Context(), false) + require.NoError(t, err) + require.Contains(t, dataResults, *dataResult) + }) + } } // TestTallyVM tests tally VM using a sample tally wasm that performs diff --git a/x/tally/keeper/integration_helpers_test.go b/x/tally/keeper/integration_helpers_test.go index 6f36a363..2cff81d8 100644 --- a/x/tally/keeper/integration_helpers_test.go +++ b/x/tally/keeper/integration_helpers_test.go @@ -24,63 +24,32 @@ import ( ) const ( - salt = "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501" - reveal = "Ghkvq84TmIuEmU1ClubNxBjVXi8df5QhiNQEC5T8V6w=" + salt = "9c0257114eb9399a2985f8e75dad7600c5d89fe3824ffa99ec1c3eb8bf3b0501" + reveal = "Ghkvq84TmIuEmU1ClubNxBjVXi8df5QhiNQEC5T8V6w=" + defaultRevealTimeoutBlocks = 10 ) -// commitRevealDataRequest performs the following steps to prepare a -// tally-ready data request. -// 1. Generate staker key and add to allowlist. -// 2. Upload data request and tally oracle programs. -// 3. Create an account and stake. -// 4. Post a data request. -// 5. The staker commits and reveals. -// It returns the data request ID. -func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) string { - // 1. Generate staker key and add to allowlist. - privKey := secp256k1.GenPrivKey() - stakerKey := privKey.Bytes() - stakerPubKey := hex.EncodeToString(privKey.PubKey().Bytes()) - staker := privKey.PubKey().Address().Bytes() - - _, err := f.contractKeeper.Execute( - f.Context(), - f.coreContractAddr, - f.deployer, - addToAllowListMsg(stakerPubKey), - sdk.NewCoins(), - ) - require.NoError(t, err) +// commitRevealDataRequest simulates stakers committing and revealing +// for a data request. It returns the data request ID. +func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string, replicationFactor, numCommits, numReveals int, timeout bool) string { + stakers := f.addStakers(t, 5) - // 2. Upload data request and tally oracle programs. + // Upload data request and tally oracle programs. execProgram := wasmstoragetypes.NewOracleProgram(testdata.SampleTallyWasm(), f.Context().BlockTime(), f.Context().BlockHeight(), 1000) - err = f.wasmStorageKeeper.OracleProgram.Set(f.Context(), execProgram.Hash, execProgram) + err := f.wasmStorageKeeper.OracleProgram.Set(f.Context(), execProgram.Hash, execProgram) require.NoError(t, err) tallyProgram := wasmstoragetypes.NewOracleProgram(testdata.SampleTallyWasm2(), f.Context().BlockTime(), f.Context().BlockHeight(), 1000) err = f.wasmStorageKeeper.OracleProgram.Set(f.Context(), tallyProgram.Hash, tallyProgram) require.NoError(t, err) - // 3. Create an account and stake. - f.initAccountWithCoins(t, staker, sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1e18)))) - - proof := f.generateStakeProof(t, stakerKey) - _, err = f.contractKeeper.Execute( - f.Context(), - f.coreContractAddr, - staker, - stakeMsg(stakerPubKey, proof), - sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), - ) - require.NoError(t, err) - - // 4. Post a data request. + // Post a data request. resJSON, err := f.contractKeeper.Execute( f.Context(), f.coreContractAddr, - f.deployer, - postDataRequestMsg(execProgram.Hash, tallyProgram.Hash, requestMemo), - sdk.NewCoins(), + stakers[0].address, + postDataRequestMsg(execProgram.Hash, tallyProgram.Hash, requestMemo, replicationFactor), + sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(3000000000000100))), ) require.NoError(t, err) @@ -93,7 +62,7 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri require.NoError(t, err) drID := res.DrID - // 5. The staker commits and reveals. + // The stakers commit and reveal. revealBody := types.RevealBody{ ID: drID, Salt: []byte(salt), @@ -105,29 +74,81 @@ func (f *fixture) commitRevealDataRequest(t *testing.T, requestMemo string) stri commitment, err := revealBody.TryHash() require.NoError(t, err) - proof = f.generateCommitProof(t, stakerKey, drID, commitment, res.Height) - _, err = f.contractKeeper.Execute( - f.Context(), - f.coreContractAddr, - staker, - commitMsg(drID, commitment, stakerPubKey, proof), - sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), - ) - require.NoError(t, err) + for i := 0; i < numCommits; i++ { + proof := f.generateCommitProof(t, stakers[i].key, drID, commitment, res.Height) + _, err = f.contractKeeper.Execute( + f.Context(), + f.coreContractAddr, + stakers[i].address, + commitMsg(drID, commitment, stakers[i].pubKey, proof), + sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), + ) + require.NoError(t, err) + } - proof = f.generateRevealProof(t, stakerKey, drID, commitment, res.Height) - _, err = f.contractKeeper.Execute( - f.Context(), - f.coreContractAddr, - staker, - revealMsg(drID, stakerPubKey, proof), - sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), - ) - require.NoError(t, err) + for i := 0; i < numReveals; i++ { + proof := f.generateRevealProof(t, stakers[i].key, drID, commitment, res.Height) + _, err = f.contractKeeper.Execute( + f.Context(), + f.coreContractAddr, + stakers[i].address, + revealMsg(drID, stakers[i].pubKey, proof), + sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), + ) + require.NoError(t, err) + } + + if timeout { + for i := 0; i < defaultRevealTimeoutBlocks; i++ { + f.AddBlock() + } + } return res.DrID } +type staker struct { + key []byte + pubKey string + address []byte +} + +// addStakers generates stakers and adds them to the allowlist. The +// stakers subsequently send their stakes to the core contract. +func (f *fixture) addStakers(t *testing.T, num int) []staker { + stakers := make([]staker, num) + for i := 0; i < num; i++ { + privKey := secp256k1.GenPrivKey() + stakers[i] = staker{ + key: privKey.Bytes(), + pubKey: hex.EncodeToString(privKey.PubKey().Bytes()), + address: privKey.PubKey().Address().Bytes(), + } + + _, err := f.contractKeeper.Execute( + f.Context(), + f.coreContractAddr, + f.deployer, + addToAllowListMsg(stakers[i].pubKey), + sdk.NewCoins(), + ) + require.NoError(t, err) + + f.initAccountWithCoins(t, stakers[i].address, sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1e18)))) + + proof := f.generateStakeProof(t, stakers[i].key) + _, err = f.contractKeeper.Execute( + f.Context(), + f.coreContractAddr, + stakers[i].address, + stakeMsg(stakers[i].pubKey, proof), + sdk.NewCoins(sdk.NewCoin(bondDenom, math.NewIntFromUint64(1))), + ) + require.NoError(t, err) + } + return stakers +} + func addToAllowListMsg(stakerPubKey string) []byte { var addToAllowListMsg = `{ "add_to_allowlist": { @@ -148,7 +169,7 @@ func stakeMsg(stakerPubKey, proof string) []byte { return []byte(fmt.Sprintf(stakeMsg, stakerPubKey, proof)) } -func postDataRequestMsg(execProgHash, tallyProgHash []byte, requestMemo string) []byte { +func postDataRequestMsg(execProgHash, tallyProgHash []byte, requestMemo string, replicationFactor int) []byte { var postDataRequestMsg = `{ "post_data_request": { "posted_dr": { @@ -159,7 +180,7 @@ func postDataRequestMsg(execProgHash, tallyProgHash []byte, requestMemo string) "tally_program_id": "%s", "tally_inputs": "dGFsbHlfaW5wdXRz", "tally_gas_limit": 300000000000000, - "replication_factor": 1, + "replication_factor": %d, "consensus_filter": "AA==", "gas_price": "10", "memo": "%s" @@ -168,7 +189,7 @@ func postDataRequestMsg(execProgHash, tallyProgHash []byte, requestMemo string) "payback_address": "AQID" } }` - return []byte(fmt.Sprintf(postDataRequestMsg, hex.EncodeToString(execProgHash), hex.EncodeToString(tallyProgHash), requestMemo)) + return []byte(fmt.Sprintf(postDataRequestMsg, hex.EncodeToString(execProgHash), hex.EncodeToString(tallyProgHash), replicationFactor, requestMemo)) } func commitMsg(drID, commitment, stakerPubKey, proof string) []byte { diff --git a/x/tally/keeper/integration_test.go b/x/tally/keeper/integration_test.go index c07d8402..b5d2de79 100644 --- a/x/tally/keeper/integration_test.go +++ b/x/tally/keeper/integration_test.go @@ -19,14 +19,19 @@ import ( "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/codec/testutil" "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/std" sdkintegration "github.com/cosmos/cosmos-sdk/testutil/integration" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" "github.com/cosmos/cosmos-sdk/x/auth" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" @@ -100,7 +105,26 @@ func initFixture(t testing.TB) *fixture { authtypes.StoreKey, banktypes.StoreKey, sdkstakingtypes.StoreKey, wasmstoragetypes.StoreKey, wasmtypes.StoreKey, pubkeytypes.StoreKey, batchingtypes.StoreKey, types.StoreKey, ) - cdc := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{}, wasmstorage.AppModuleBasic{}).Codec + + mb := module.NewBasicManager(auth.AppModuleBasic{}, bank.AppModuleBasic{}, wasmstorage.AppModuleBasic{}) + + interfaceRegistry := testutil.CodecOptions{ + AccAddressPrefix: params.Bech32PrefixAccAddr, + ValAddressPrefix: params.Bech32PrefixValAddr, + }.NewInterfaceRegistry() + protoCodec := codec.NewProtoCodec(interfaceRegistry) + aminoCodec := codec.NewLegacyAmino() + encCfg := moduletestutil.TestEncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: protoCodec, + TxConfig: tx.NewTxConfig(protoCodec, tx.DefaultSignModes), + Amino: aminoCodec, + } + cdc := encCfg.Codec + std.RegisterLegacyAminoCodec(encCfg.Amino) + std.RegisterInterfaces(encCfg.InterfaceRegistry) + mb.RegisterLegacyAminoCodec(encCfg.Amino) + mb.RegisterInterfaces(encCfg.InterfaceRegistry) buf := &bytes.Buffer{} logger := log.NewLogger(buf, log.LevelOption(zerolog.DebugLevel)) @@ -149,6 +173,7 @@ func initFixture(t testing.TB) *fixture { require.NoError(t, err) // x/wasm + router := baseapp.NewMsgServiceRouter() wasmKeeper := wasmkeeper.NewKeeper( cdc, runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), @@ -156,7 +181,7 @@ func initFixture(t testing.TB) *fixture { bankKeeper, stakingKeeper, nil, nil, nil, nil, - nil, nil, nil, nil, + nil, nil, router, nil, tempDir, wasmtypes.DefaultWasmConfig(), wasmCapabilities, @@ -221,7 +246,7 @@ func initFixture(t testing.TB) *fixture { wasmStorageModule := wasmstorage.NewAppModule(cdc, *wasmStorageKeeper) tallyModule := tally.NewAppModule(cdc, tallyKeeper) - integrationApp := integration.NewIntegrationApp(ctx, logger, keys, cdc, map[string]appmodule.AppModule{ + integrationApp := integration.NewIntegrationApp(ctx, logger, keys, cdc, router, map[string]appmodule.AppModule{ authtypes.ModuleName: authModule, banktypes.ModuleName: bankModule, sdkstakingtypes.ModuleName: stakingModule, diff --git a/x/tally/keeper/keeper.go b/x/tally/keeper/keeper.go index 12e83b7d..d7cbb89a 100644 --- a/x/tally/keeper/keeper.go +++ b/x/tally/keeper/keeper.go @@ -61,6 +61,14 @@ func (k Keeper) GetMaxTallyGasLimit(ctx sdk.Context) (uint64, error) { return params.MaxTallyGasLimit, nil } +func (k Keeper) GetGasCostCommitment(ctx sdk.Context) (uint64, error) { + params, err := k.params.Get(ctx) + if err != nil { + return 0, err + } + return params.GasCostCommitment, nil +} + func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } diff --git a/x/tally/keeper/payout.go b/x/tally/keeper/payout.go new file mode 100644 index 00000000..ac0ea01a --- /dev/null +++ b/x/tally/keeper/payout.go @@ -0,0 +1,55 @@ +package keeper + +import ( + "sort" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/sedaprotocol/seda-chain/x/tally/types" +) + +// CalculateCommitterPayouts constructs distribution messages that +// pay the fixed gas cost for each commiter of a given data request. +func (k Keeper) CalculateCommitterPayouts(ctx sdk.Context, req types.Request) (types.DistributionMessages, error) { + gasCost, err := k.GetGasCostCommitment(ctx) + if err != nil { + return types.DistributionMessages{}, err + } + + i := 0 + committers := make([]string, len(req.Commits)) + for k := range req.Commits { + committers[i] = k + i++ + } + sort.Strings(committers) + + distMsgs := make([]types.DistributionMessage, len(committers)) + for i, committer := range committers { + distMsgs[i] = types.DistributionMessage{ + Kind: types.DistributionKind{ + ExecutorReward: &types.DistributionExecutorReward{ + Identity: committer, + Amount: math.NewIntFromUint64(gasCost), + }, + }, + Type: types.DistributionTypeTimedOut, + } + } + + return types.DistributionMessages{ + Messages: distMsgs, + RefundType: types.DistributionTypeTimedOut, + }, nil +} + +// TODO: This will become more complex when we introduce incentives. +func calculateExecGasUsed(reveals []types.RevealBody) uint64 { + var execGasUsed uint64 + for _, reveal := range reveals { + execGasUsed += reveal.GasUsed + } + return execGasUsed +} diff --git a/x/tally/keeper/testdata/core_contract.wasm b/x/tally/keeper/testdata/core_contract.wasm index 7138ad5f..356fdab1 100644 Binary files a/x/tally/keeper/testdata/core_contract.wasm and b/x/tally/keeper/testdata/core_contract.wasm differ diff --git a/x/tally/types/abci_types.go b/x/tally/types/abci_types.go index 1db768e5..aec3a753 100644 --- a/x/tally/types/abci_types.go +++ b/x/tally/types/abci_types.go @@ -7,6 +7,8 @@ import ( "encoding/json" "golang.org/x/crypto/sha3" + + "cosmossdk.io/math" ) type Request struct { @@ -103,8 +105,56 @@ func (u *RevealBody) TryHash() (string, error) { return hex.EncodeToString(hasher.Sum(nil)), nil } -// SudoRemoveDataRequest is the message type used to remove a given -// data request from the Core Contract. -type SudoRemoveDataRequest struct { - ID string `json:"dr_id"` +type DistributionMessages struct { + Messages []DistributionMessage `json:"messages"` + RefundType DistributionType `json:"refund_type"` +} + +type DistributionMessage struct { + Kind DistributionKind `json:"kind"` + Type DistributionType `json:"type"` +} + +type DistributionKind struct { + Burn *DistributionBurn `json:"burn,omitempty"` + ExecutorReward *DistributionExecutorReward `json:"executor_reward,omitempty"` + Send *DistributionSend `json:"send,omitempty"` +} + +type DistributionBurn struct { + Amount math.Int `json:"amount"` +} + +type DistributionSend struct { + To []byte `json:"to"` + Amount math.Int `json:"amount"` +} + +type DistributionExecutorReward struct { + Amount math.Int `json:"amount"` + Identity string `json:"identity"` +} + +type DistributionType string + +const ( + DistributionTypeTallyReward DistributionType = "tally_reward" + DistributionTypeExecutorReward DistributionType = "executor_reward" + DistributionTypeTimedOut DistributionType = "timed_out" + DistributionTypeNoConsensus DistributionType = "no_consensus" + DistributionTypeRemainderRefund DistributionType = "remainder_refund" +) + +func MarshalSudoRemoveDataRequests(processedReqs map[string]DistributionMessages) ([]byte, error) { + return json.Marshal(struct { + SudoRemoveDataRequests struct { + Requests map[string]DistributionMessages `json:"requests"` + } `json:"remove_data_requests"` + }{ + SudoRemoveDataRequests: struct { + Requests map[string]DistributionMessages `json:"requests"` + }{ + Requests: processedReqs, + }, + }) } diff --git a/x/tally/types/params.go b/x/tally/types/params.go index 7219ae67..0bfce809 100644 --- a/x/tally/types/params.go +++ b/x/tally/types/params.go @@ -9,6 +9,7 @@ const ( DefaultFilterGasCostNone = 100_000 DefaultFilterGasCostMultiplierMode = 100_000 DefaultFilterGasCostMultiplierStddev = 100_000 + DefaultGasCostCommitment = 100_000_000 ) // DefaultParams returns default tally module parameters. @@ -18,6 +19,7 @@ func DefaultParams() Params { FilterGasCostNone: DefaultFilterGasCostNone, FilterGasCostMultiplierMode: DefaultFilterGasCostMultiplierMode, FilterGasCostMultiplierStddev: DefaultFilterGasCostMultiplierStddev, + GasCostCommitment: DefaultGasCostCommitment, } } @@ -26,6 +28,17 @@ 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) } - + if p.FilterGasCostNone <= 0 { + return sdkerrors.ErrInvalidRequest.Wrapf("filter gas cost (none) must be greater than 0: %d", p.FilterGasCostNone) + } + if p.FilterGasCostMultiplierMode <= 0 { + return sdkerrors.ErrInvalidRequest.Wrapf("filter gas cost (mode) must be greater than 0: %d", p.FilterGasCostMultiplierMode) + } + if p.FilterGasCostMultiplierStddev <= 0 { + return sdkerrors.ErrInvalidRequest.Wrapf("filter gas cost (std dev) must be greater than 0: %d", p.FilterGasCostMultiplierStddev) + } + if p.GasCostCommitment <= 0 { + return sdkerrors.ErrInvalidRequest.Wrapf("gas cost for a commitment must be greater than 0: %d", p.GasCostCommitment) + } return nil } diff --git a/x/tally/types/tally.pb.go b/x/tally/types/tally.pb.go index 07443880..676f45fd 100644 --- a/x/tally/types/tally.pb.go +++ b/x/tally/types/tally.pb.go @@ -34,6 +34,9 @@ type Params struct { // 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"` + // GasCostCommitment is the gas cost for a commitment corresponding to an + // expired data request. + GasCostCommitment uint64 `protobuf:"varint,5,opt,name=gas_cost_commitment,json=gasCostCommitment,proto3" json:"gas_cost_commitment,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -97,6 +100,13 @@ func (m *Params) GetFilterGasCostMultiplierStddev() uint64 { return 0 } +func (m *Params) GetGasCostCommitment() uint64 { + if m != nil { + return m.GasCostCommitment + } + return 0 +} + func init() { proto.RegisterType((*Params)(nil), "sedachain.tally.v1.Params") } @@ -104,25 +114,26 @@ func init() { func init() { proto.RegisterFile("sedachain/tally/v1/tally.proto", fileDescriptor_2917df8a6808d5e2) } var fileDescriptor_2917df8a6808d5e2 = []byte{ - // 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, 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, + // 297 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xcd, 0x4a, 0xf3, 0x40, + 0x14, 0x86, 0x93, 0x7e, 0xfd, 0xba, 0x98, 0x95, 0x4e, 0x5d, 0x04, 0xc4, 0xf1, 0x67, 0xe5, 0xa6, + 0x89, 0xc5, 0x3b, 0xb0, 0x42, 0x45, 0xac, 0x88, 0xba, 0x72, 0x13, 0xa6, 0xc9, 0x98, 0x0e, 0xcc, + 0xe4, 0x84, 0xcc, 0x69, 0x48, 0xef, 0xc2, 0xcb, 0xf0, 0x52, 0x5c, 0x76, 0xe9, 0x52, 0x92, 0x1b, + 0x91, 0xcc, 0x48, 0x40, 0x41, 0x77, 0xc9, 0x79, 0x9e, 0xf7, 0x65, 0x38, 0x87, 0x30, 0x23, 0x52, + 0x9e, 0xac, 0xb8, 0xcc, 0x23, 0xe4, 0x4a, 0x6d, 0xa2, 0x6a, 0xea, 0x3e, 0xc2, 0xa2, 0x04, 0x04, + 0x4a, 0x7b, 0x1e, 0xba, 0x71, 0x35, 0x3d, 0x79, 0x1d, 0x90, 0xd1, 0x1d, 0x2f, 0xb9, 0x36, 0x74, + 0x42, 0xc6, 0x9a, 0xd7, 0xb1, 0x45, 0x71, 0xc6, 0x4d, 0xac, 0xa4, 0x96, 0x18, 0xf8, 0x47, 0xfe, + 0xe9, 0xf0, 0x7e, 0x47, 0xf3, 0xfa, 0xb1, 0x23, 0x73, 0x6e, 0x6e, 0xba, 0x39, 0x8d, 0xc8, 0xde, + 0xb3, 0x54, 0x28, 0x4a, 0xeb, 0x26, 0x60, 0x30, 0xce, 0x21, 0x17, 0xc1, 0xc0, 0xfa, 0xbb, 0x8e, + 0xcd, 0xb9, 0x99, 0x81, 0xc1, 0x5b, 0xc8, 0x05, 0xbd, 0x24, 0x87, 0x3f, 0x03, 0x7a, 0xad, 0x50, + 0x16, 0x4a, 0x8a, 0x32, 0xd6, 0x90, 0x8a, 0xe0, 0x9f, 0xcd, 0xee, 0x7f, 0xcb, 0x2e, 0x7a, 0x67, + 0x01, 0xa9, 0xa0, 0x57, 0xe4, 0xf8, 0x8f, 0x16, 0x83, 0x69, 0x2a, 0xaa, 0x60, 0x68, 0x7b, 0x0e, + 0x7e, 0xe9, 0x79, 0xb0, 0x12, 0x0d, 0xc9, 0xb8, 0xaf, 0x48, 0x40, 0x6b, 0x89, 0x5a, 0xe4, 0x18, + 0xfc, 0x77, 0xef, 0xcf, 0x5c, 0x6a, 0xd6, 0x83, 0x8b, 0xeb, 0xb7, 0x86, 0xf9, 0xdb, 0x86, 0xf9, + 0x1f, 0x0d, 0xf3, 0x5f, 0x5a, 0xe6, 0x6d, 0x5b, 0xe6, 0xbd, 0xb7, 0xcc, 0x7b, 0x3a, 0xcb, 0x24, + 0xae, 0xd6, 0xcb, 0x30, 0x01, 0x1d, 0x75, 0x3b, 0xb6, 0xeb, 0x4e, 0x40, 0xd9, 0x9f, 0x89, 0xbb, + 0x48, 0xfd, 0x75, 0x13, 0xdc, 0x14, 0xc2, 0x2c, 0x47, 0x56, 0x39, 0xff, 0x0c, 0x00, 0x00, 0xff, + 0xff, 0x26, 0x2a, 0xb2, 0xa6, 0xb3, 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -145,6 +156,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.GasCostCommitment != 0 { + i = encodeVarintTally(dAtA, i, uint64(m.GasCostCommitment)) + i-- + dAtA[i] = 0x28 + } if m.FilterGasCostMultiplierStddev != 0 { i = encodeVarintTally(dAtA, i, uint64(m.FilterGasCostMultiplierStddev)) i-- @@ -197,6 +213,9 @@ func (m *Params) Size() (n int) { if m.FilterGasCostMultiplierStddev != 0 { n += 1 + sovTally(uint64(m.FilterGasCostMultiplierStddev)) } + if m.GasCostCommitment != 0 { + n += 1 + sovTally(uint64(m.GasCostCommitment)) + } return n } @@ -311,6 +330,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasCostCommitment", wireType) + } + m.GasCostCommitment = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTally + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasCostCommitment |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTally(dAtA[iNdEx:]) diff --git a/x/vesting/keeper/integration_test.go b/x/vesting/keeper/integration_test.go index 8e00ab8a..25aeafdb 100644 --- a/x/vesting/keeper/integration_test.go +++ b/x/vesting/keeper/integration_test.go @@ -13,6 +13,7 @@ import ( "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -41,9 +42,7 @@ import ( pubkeytypes "github.com/sedaprotocol/seda-chain/x/pubkey/types" "github.com/sedaprotocol/seda-chain/x/staking" stakingkeeper "github.com/sedaprotocol/seda-chain/x/staking/keeper" - stakingtypes "github.com/sedaprotocol/seda-chain/x/staking/types" "github.com/sedaprotocol/seda-chain/x/vesting" - "github.com/sedaprotocol/seda-chain/x/vesting/keeper" "github.com/sedaprotocol/seda-chain/x/vesting/types" ) @@ -151,19 +150,16 @@ func initFixture(tb testing.TB) *fixture { stakingModule := staking.NewAppModule(cdc, stakingKeeper, accountKeeper, bankKeeper, pubKeyKeeper) vestingModule := vesting.NewAppModule(accountKeeper, bankKeeper, stakingKeeper) - integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{ - authtypes.ModuleName: authModule, - banktypes.ModuleName: bankModule, - sdkstakingtypes.ModuleName: stakingModule, - types.ModuleName: vestingModule, - }) - - types.RegisterMsgServer(integrationApp.MsgServiceRouter(), keeper.NewMsgServerImpl(accountKeeper, bankKeeper, stakingKeeper)) - - sdkStakingMsgServer := sdkstakingkeeper.NewMsgServerImpl(sdkStakingKeeper) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(sdkStakingMsgServer, stakingKeeper) - sdkstakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), stakingMsgServer) - stakingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), stakingMsgServer) + integrationApp := integration.NewIntegrationApp( + newCtx, logger, keys, cdc, + baseapp.NewMsgServiceRouter(), + map[string]appmodule.AppModule{ + authtypes.ModuleName: authModule, + banktypes.ModuleName: bankModule, + sdkstakingtypes.ModuleName: stakingModule, + types.ModuleName: vestingModule, + }, + ) return &fixture{ IntegationApp: integrationApp,