Skip to content

Commit

Permalink
[Application] chore: implement app transfer period (#789)
Browse files Browse the repository at this point in the history
## Summary

- Reconcile the need for a transfer period with #743.
- Ensure the app transfer message handler returns grpc status errors
consistently.
- Improve some shared E2E tests.
- Implement `ApplicationIntegrationSuite` test suite.
- Port app transfer E2E tests to integration tests.

## Depends on

- #827 
- #788 
- #743

## Issue

- #657

## Type of change

Select one or more:

- [x] New feature, functionality or library
- [ ] Bug fix
- [ ] Code health or cleanup
- [ ] Documentation
- [ ] Other (specify)

## Testing

<!-- READ & DELETE:
- Documentation changes: only keep this if you're making documentation
changes
- Unit Testing: Remove this if you didn't make code changes
- E2E Testing: Remove this if you didn't make code changes
- See the quickstart guide for instructions:
https://dev.poktroll.com/developer_guide/quickstart
- DevNet E2E Testing: Remove this if you didn't make code changes
- THIS IS VERY EXPENSIVE: only do it after all the reviews are complete.
- Optionally run `make trigger_ci` if you want to re-trigger tests
without any code changes
- If tests fail, try re-running failed tests only using the GitHub UI as
shown
[here](https://github.com/pokt-network/poktroll/assets/1892194/607984e9-0615-4569-9452-4c730190c1d2)
-->

- [ ] **Documentation**: `make docusaurus_start`; only needed if you
make doc changes
- [ ] **Unit Tests**: `make go_develop_and_test`
- [x] **LocalNet E2E Tests**: `make test_e2e`
- [x] **DevNet E2E Tests**: Add the `devnet-test-e2e` label to the PR.

## Sanity Checklist

- [x] I have tested my changes using the available tooling
- [x] I have commented my code
- [x] I have performed a self-review of my own code; both comments &
source code
- [ ] I create and reference any new tickets, if applicable
- [x] I have left TODOs throughout the codebase, if applicable

---------

Co-authored-by: Daniel Olshansky <[email protected]>
Co-authored-by: Redouane Lakrache <[email protected]>
Co-authored-by: red-0ne <[email protected]>
  • Loading branch information
4 people authored Oct 4, 2024
1 parent 1223502 commit 600ba59
Show file tree
Hide file tree
Showing 50 changed files with 6,419 additions and 765 deletions.
2,079 changes: 2,049 additions & 30 deletions api/poktroll/application/event.pulsar.go

Large diffs are not rendered by default.

434 changes: 310 additions & 124 deletions api/poktroll/application/tx.pulsar.go

Large diffs are not rendered by default.

716 changes: 673 additions & 43 deletions api/poktroll/application/types.pulsar.go

Large diffs are not rendered by default.

288 changes: 192 additions & 96 deletions api/poktroll/gateway/tx.pulsar.go

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions docusaurus/docs/operate/user_guide/transfer-application.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: Transfer Application
sidebar_position: 6
---

# Transferring an Application <!-- omit in toc -->

// TODO_DOCUMENT(@bryanchriswhite):

... why would you want to do this?

... what does it mean?

... include diagrams from #789 comments ...

... how do you do it?

... gotchas & troubleshooting
86 changes: 74 additions & 12 deletions e2e/tests/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import (

const (
numQueryRetries = uint8(3)
unbondingPeriod = "unbonding"
transferPeriod = "transfer"
)

var (
Expand Down Expand Up @@ -174,7 +176,7 @@ func (s *suite) TheUserRunsTheCommand(cmd string) {
}

func (s *suite) TheUserShouldBeAbleToSeeStandardOutputContaining(arg1 string) {
require.Contains(s, s.pocketd.result.Stdout, arg1)
require.Containsf(s, s.pocketd.result.Stdout, arg1, s.pocketd.result.Stderr)
}

func (s *suite) TheUserSendsUpoktFromAccountToAccount(amount int64, accName1, accName2 string) {
Expand Down Expand Up @@ -317,13 +319,13 @@ func (s *suite) getConfigFileContent(
) string {
var configContent string
switch actorType {
case "application":
case apptypes.ModuleName:
configContent = fmt.Sprintf(`
stake_amount: %dupokt
service_ids:
- %s`,
amount, serviceId)
case "supplier":
case suppliertypes.ModuleName:
configContent = fmt.Sprintf(`
owner_address: %s
operator_address: %s
Expand All @@ -344,7 +346,7 @@ func (s *suite) TheUserUnstakesAFromTheAccount(actorType string, accName string)
var args []string

switch actorType {
case "supplier":
case suppliertypes.ModuleName:
args = []string{
"tx",
actorType,
Expand Down Expand Up @@ -372,6 +374,13 @@ func (s *suite) TheUserUnstakesAFromTheAccount(actorType string, accName string)
res, err := s.pocketd.RunCommandOnHost("", args...)
require.NoError(s, err, "error unstaking %s", actorType)

// Get current balance
balanceKey := accBalanceKey(accName)
currBalance := s.getAccBalance(accName)
s.scenarioState[balanceKey] = currBalance // save the balance for later

// NB: s.pocketd.result MUST be set AFTER the balance is queried because the
// balance query sets the result first while getting the account balance.
s.pocketd.result = res
}

Expand Down Expand Up @@ -409,7 +418,7 @@ func (s *suite) TheUserVerifiesTheForAccountIsNotStaked(actorType, accName strin
func (s *suite) TheForAccountIsStakedWithUpokt(actorType, accName string, amount int64) {
stakeAmount, ok := s.getStakedAmount(actorType, accName)
require.Truef(s, ok, "account %s of type %s SHOULD be staked", accName, actorType)
require.Equalf(s, int64(stakeAmount), amount, "account %s stake amount is not %d", accName, amount)
require.Equalf(s, amount, int64(stakeAmount), "account %s stake amount is not %d", accName, amount)
s.scenarioState[accStakeKey(actorType, accName)] = stakeAmount // save the stakeAmount for later
}

Expand Down Expand Up @@ -511,22 +520,53 @@ func (s *suite) TheUserWaitsForTheSupplierForAccountUnbondingPeriodToFinish(accN
s.waitForBlockHeight(unbondingHeight + 1) // Add 1 to ensure the unbonding block has been committed
}

func (s *suite) TheApplicationForAccountIsUnbonding(appName string) {
func (s *suite) TheApplicationForAccountIsInThePeriod(appName, periodName string) {
_, ok := accNameToAppMap[appName]
require.True(s, ok, "application %s not found", appName)

s.waitForTxResultEvent(newEventMsgTypeMatchFn("application", "UnstakeApplication"))
var (
msgType string
isAppInState func(*apptypes.Application) bool
)
switch periodName {
case unbondingPeriod:
msgType = "UnstakeApplication"
isAppInState = func(app *apptypes.Application) bool {
return app.IsUnbonding()
}
case transferPeriod:
msgType = "TransferApplication"
isAppInState = func(application *apptypes.Application) bool {
return application.HasPendingTransfer()
}
default:
s.Fatalf("unsupported period type: %q", periodName)
}

s.waitForTxResultEvent(newEventMsgTypeMatchFn("application", msgType))

supplier := s.getApplicationInfo(appName)
require.True(s, supplier.IsUnbonding())
application := s.getApplicationInfo(appName)
require.True(s, isAppInState(application))
}

func (s *suite) TheUserWaitsForTheApplicationForAccountUnbondingPeriodToFinish(accName string) {
func (s *suite) TheUserWaitsForTheApplicationForAccountPeriodToFinish(accName, periodType string) {
_, ok := accNameToAppMap[accName]
require.True(s, ok, "application %s not found", accName)

unbondingHeight := s.getApplicationUnbondingHeight(accName)
s.waitForBlockHeight(unbondingHeight + 1) // Add 1 to ensure the unbonding block has been committed
// TODO_IMPROVE: Add an event to listen for instead. This will require
// refactoring and/or splitting of this method for each event type.

switch periodType {
case unbondingPeriod:
unbondingHeight := s.getApplicationUnbondingHeight(accName)
s.waitForBlockHeight(unbondingHeight + 1) // Add 1 to ensure the unbonding block has been committed
case transferPeriod:
transferEndHeight := s.getApplicationTransferEndHeight(accName)
s.waitForBlockHeight(transferEndHeight + 1) // Add 1 to ensure the transfer end block has been committed
}

// Rebuild app map after the relevant period has elapsed.
s.buildAppMap()
}

func (s *suite) getStakedAmount(actorType, accName string) (int, bool) {
Expand Down Expand Up @@ -786,6 +826,28 @@ func (s *suite) getApplicationUnbondingHeight(accName string) int64 {
return unbondingHeight
}

// getApplicationTransferEndHeight returns the height at which the application will be transferred to the destination.
func (s *suite) getApplicationTransferEndHeight(accName string) int64 {
application := s.getApplicationInfo(accName)
require.NotNil(s, application.GetPendingTransfer())

args := []string{
"query",
"shared",
"params",
"--output=json",
}

res, err := s.pocketd.RunCommandOnHostWithRetry("", numQueryRetries, args...)
require.NoError(s, err, "error getting shared module params")

var resp sharedtypes.QueryParamsResponse
responseBz := []byte(strings.TrimSpace(res.Stdout))
s.cdc.MustUnmarshalJSON(responseBz, &resp)

return apptypes.GetApplicationTransferHeight(&resp.Params, application)
}

// getServiceComputeUnitsPerRelay returns the compute units per relay for a given service ID
func (s *suite) getServiceComputeUnitsPerRelay(serviceId string) uint64 {
args := []string{
Expand Down
31 changes: 30 additions & 1 deletion e2e/tests/session_steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import (
"github.com/pokt-network/poktroll/pkg/client/block"
"github.com/pokt-network/poktroll/pkg/observable/channel"
testutilevents "github.com/pokt-network/poktroll/testutil/events"
apptypes "github.com/pokt-network/poktroll/x/application/types"
prooftypes "github.com/pokt-network/poktroll/x/proof/types"
suppliertypes "github.com/pokt-network/poktroll/x/supplier/types"
tokenomicstypes "github.com/pokt-network/poktroll/x/tokenomics/types"
)

Expand Down Expand Up @@ -49,7 +51,8 @@ func (s *suite) TheUserShouldWaitForTheModuleMessageToBeSubmitted(module, msgTyp
// If the message type is "SubmitProof", save the supplier balance
// so that next steps that assert on supplier rewards can do it without having
// the proof submission fee skewing the results.
if msgType == "SubmitProof" {
switch msgType {
case "SubmitProof":
supplierOperatorAddress := getMsgSubmitProofSenderAddress(event)
require.NotEmpty(s, supplierOperatorAddress)

Expand All @@ -59,11 +62,33 @@ func (s *suite) TheUserShouldWaitForTheModuleMessageToBeSubmitted(module, msgTyp
balanceKey := accBalanceKey(supplierAccName)
currBalance := s.getAccBalance(supplierAccName)
s.scenarioState[balanceKey] = currBalance // save the balance for later
default:
s.Log("no test suite state to update for message type %s", msgType)
}

// Rebuild actor maps after the relevant messages have been committed.
switch module {
case apptypes.ModuleName:
s.buildAppMap()
case suppliertypes.ModuleName:
s.buildSupplierMap()
default:
s.Log("no test suite state to update for module %s", module)
}
}

func (s *suite) TheUserShouldWaitForTheModuleTxEventToBeBroadcast(module, eventType string) {
s.waitForTxResultEvent(newEventTypeMatchFn(module, eventType))

// Rebuild actor maps after the relevant messages have been committed.
switch module {
case apptypes.ModuleName:
s.buildAppMap()
case suppliertypes.ModuleName:
s.buildSupplierMap()
default:
s.Log("no test suite state to update for module %s", module)
}
}

func (s *suite) TheUserShouldWaitForTheClaimsettledEventWithProofRequirementToBeBroadcast(proofRequirement string) {
Expand All @@ -74,6 +99,10 @@ func (s *suite) TheUserShouldWaitForTheClaimsettledEventWithProofRequirementToBe
newEventAttributeMatchFn("proof_requirement", fmt.Sprintf("%q", proofRequirement)),
),
)

// Update the actor maps after end block events have been emitted.
s.buildAppMap()
s.buildSupplierMap()
}

// TODO_FLAKY: See how 'TheClaimCreatedBySupplierForServiceForApplicationShouldBeSuccessfullySettled'
Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/stake_app.feature
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Feature: Stake App Namespaces
Then the user should be able to see standard output containing "txhash:"
And the user should be able to see standard output containing "code: 0"
And the pocketd binary should exit without error
And the application for account "app2" is unbonding
When the user waits for the application for account "app2" unbonding period to finish
And the application for account "app2" is in the "unbonding" period
When the user waits for the application for account "app2" "unbonding" period to finish
And the user verifies the "application" for account "app2" is not staked
And the account balance of "app2" should be "1000070" uPOKT "more" than before
21 changes: 0 additions & 21 deletions e2e/tests/stake_app_transfer.feature

This file was deleted.

51 changes: 0 additions & 51 deletions e2e/tests/stake_app_transfer_steps_test.go

This file was deleted.

8 changes: 8 additions & 0 deletions makefiles/params.mk
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ params_update_shared_proof_window_open_offset_blocks: ## Update the shared modul
params_update_shared_proof_window_close_offset_blocks: ## Update the shared module proof_window_close_offset_blocks param
poktrolld tx authz exec ./tools/scripts/params/shared_proof_window_close_offset_blocks.json $(PARAM_FLAGS)

.PHONY: params_update_shared_supplier_unbonding_period_sessions
params_update_shared_supplier_unbonding_period_sessions: ## Update the shared module supplier_unbonding_period_sessions param
poktrolld tx authz exec ./tools/scripts/params/shared_supplier_unbonding_period_sessions.json $(PARAM_FLAGS)

.PHONY: params_update_shared_application_unbonding_period_sessions
params_update_shared_application_unbonding_period_sessions: ## Update the shared module application_unbonding_period_sessions param
poktrolld tx authz exec ./tools/scripts/params/shared_application_unbonding_period_sessions.json $(PARAM_FLAGS)

.PHONY: params_update_shared_compute_units_to_tokens_multiplier
params_update_shared_compute_units_to_tokens_multiplier: ## Update the shared module compute_units_to_tokens_multiplier param
poktrolld tx authz exec ./tools/scripts/params/shared_compute_units_to_tokens_multiplier.json $(PARAM_FLAGS)
Expand Down
32 changes: 32 additions & 0 deletions proto/poktroll/application/event.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,42 @@ option (gogoproto.stable_marshaler_all) = true;
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";

import "poktroll/application/types.proto";

// EventRedelegation is an event emitted whenever an application changes its
// delegatee gateways on chain. This is in response to both a DelegateToGateway
// and UndelegateFromGateway message.
message EventRedelegation {
string app_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the application.
string gateway_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the gateway the application has changed their delegation of.
}

// EventTransferBegin is emitted whenever an application begins a transfer. It
// includes the source application state immediately after the transfer began.
message EventTransferBegin {
string source_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string destination_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
poktroll.application.Application source_application = 3;
}

// EventTransferEnd is emitted whenever an application transfer is completed. It
// includes the destination application state at the time the transfer completed.
// Either EventTransferEnd or EventTransferError will be emitted corresponding to
// any given EventTransferBegin event.
message EventTransferEnd {
string source_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string destination_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
poktroll.application.Application destination_application = 3;
}

// EventTransferError is emitted whenever an application transfer fails. It
// includes the source application state at the time the transfer failed and
// the error message.
// Either EventTransferEnd or EventTransferError will be emitted corresponding to
// any given EventTransferBegin event.
message EventTransferError {
string source_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string destination_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
poktroll.application.Application source_application = 3;
string error = 4;
}
Loading

0 comments on commit 600ba59

Please sign in to comment.