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

[Applicaton] Provable past delegations & delayed undelegations #497

Closed
wants to merge 20 commits into from

Conversation

red-0ne
Copy link
Contributor

@red-0ne red-0ne commented Apr 25, 2024

Summary

Add EndBlockers keeper methods to:

  • Process delayed undelegations to become effective at the next session
  • Archive of old delegations to be used for past relay requests signature verification
  • Prune archived delegations past a certain number of blocks

Update the RingClient and RingCache to use archived delegations when needed

Remove stale code implementing PubKeyClient

2255 added lines of code are auto-generated pulsar files

A followup PR for e2e testing the feature involving sending relays to gateways instead of self-signing AppGateServers will come after. E2E tests for:

  • Delayed undelegation
  • Delegation archival and pruning
  • Undelegation overriding (undelegate A -> redelegate A before undelegation is effective)

Issue

Type of change

Select one or more:

  • New feature, functionality or library
  • Bug fix
  • Code health or cleanup
  • Documentation
  • Other (specify)

Testing

Documentation changes (only if making doc changes)

  • make docusaurus_start; only needed if you make doc changes

Local Testing (only if making code changes)

  • Unit Tests: make go_develop_and_test
  • LocalNet E2E Tests: make test_e2e
  • See quickstart guide for instructions

PR Testing (only if making code changes)

  • DevNet E2E Tests: Add the devnet-test-e2e label to the PR.
    • THIS IS VERY EXPENSIVE, so 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

Sanity Checklist

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

@red-0ne red-0ne added application Changes related to the Application actor off-chain Off-chain business logic on-chain On-chain business logic labels Apr 25, 2024
@red-0ne red-0ne added this to the Shannon Public TestNet milestone Apr 25, 2024
@red-0ne red-0ne self-assigned this Apr 25, 2024
@red-0ne red-0ne marked this pull request as ready for review April 25, 2024 20:57
@Olshansk
Copy link
Member

@red-0ne Aiming to take a look at this tomorrow.

@Olshansk
Copy link
Member

Haven't started looking at the code yet but great PR summary @red-0ne!

Copy link
Member

@Olshansk Olshansk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@red-0ne Did a partial review (about 50% done) and it's a good start but I think this will require either iteration or discussion. Lmk what you prefer after seeing some of the comments.

  1. I don’t fully understand the logic in pkg/crypto/rings/client.go. We either need to discuss or follow up async.
  2. We added a lot of on-chain logic but there are 0 integration / unit tests. At least a few are necessary.
  3. There’s a lot of indexing / storage code that I don’t believe is needed. See my comment below.

When we discussed it on a call, I thought it would be as simple ass:

  • Add map[height]-> list[gateway_addr] to the Application proto.
  • Ensure height is always a session_end_height inside the undelegate
  • Prune / update directly from the map
  • New delegations mid session: active immediately
  • Undelegations mid session: active until session ends

@@ -0,0 +1,61 @@
Feature: Delegate Namespaces

Background:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL! Going to start using this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up going through [1] to learn more but not going to change anything now.

[1] https://cucumber.io/docs/gherkin/reference/#rule

const (
// serviceId to stake for by applications.
serviceId = "anvil"
// defaultStakeAmount is the default amount to stake for applications and gateways.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Can we decouple app & gateway stakes? It'll be more "scalable" from a dec perspective.

}

// Fund the actor with enough upokt to stake for the service.
s.TheUserSendsUpoktFromAccountToAccount(int64(stakedAmount+1), "pnf", accName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Can you move pnf into a local fundingAccount variable?
  2. Can you try using faucet instead of pnf?

// Stake for the service with the
s.TheUserStakesAWithUpoktForServiceFromTheAccount(
accType,
int64(stakedAmount+1),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Move stakedAmount+1 stakeAmount := stakedAmount + 1 on line 41
  2. Reuse it in both places
  3. #PUC why we're doing +1


Scenario: User can delegate Application to Gateway
Given the user has the pocketd binary installed
And the actor type "application" with account "app1" is staked with enough uPOKT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at the implementation of is staked with enough uPOKT and I think it's actually is restaked with one additional uPOKT.

I don't fully understand the intention - yet - but there's an opportunity to make it clearer.

@@ -92,6 +92,9 @@ func TestMsgServer_UndelegateFromGateway_SuccessfullyUndelegate(t *testing.T) {
_, err = srv.UndelegateFromGateway(ctx, undelegateMsg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coul we increase the unit and/or integration test coverage? We added A LOT of on-chain logic but zero new (non end to end tests) were added.

// Undelegation is a message type used to schedule undelegating an application
// from a gateway.
message Undelegation {
string app_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this (gogoproto.nullable) = false]?


// Undelegation is a message type used to schedule undelegating an application
// from a gateway.
message Undelegation {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the name. Undelegation is VERY general purpose in crypto.

UndelegationFromAppToGatewayEvent for example is better.

gatewayIdx := slices.Index(foundApp.DelegateeGatewayAddresses, msg.GatewayAddress)
if gatewayIdx == -1 {
return nil, types.ErrAppNotDelegated.Wrapf(
"application not delegated to gateway with address %q",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"application not delegated to gateway with address %q",
"application cannot undelegated from gateway with address %q that it is not delegated to",

logger.Info(fmt.Sprintf("Application not found with address [%s]", msg.AppAddress))
return nil, types.ErrAppNotFound.Wrapf("application not found with address: %s", msg.AppAddress)
return nil, types.ErrAppNotFound.Wrapf(
"application not found with address %q",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"application not found with address %q",
"application trying to undelegate with address %q not found",

Copy link
Member

@Olshansk Olshansk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@red-0ne Did a partial review (about 50% done) and it's a good start but I think this will require either iteration or discussion. Lmk what you prefer after seeing some of the comments.

  1. I don’t fully understand the logic in pkg/crypto/rings/client.go. We either need to discuss or follow up async.
  2. We added a lot of on-chain logic but there are 0 integration / unit tests. At least a few are necessary.
  3. There’s a lot of indexing / storage code that I don’t believe is needed. See my comment below.

When we discussed it on a call, I thought it would be as simple ass:

  • Add map[height]-> list[gateway_addr] to the Application proto.
  • Ensure height is always a session_end_height inside the undelegate
  • Prune / update directly from the map
  • New delegations mid session: active immediately
  • Undelegations mid session: active until session ends

// undelegations involve archiving old delegations records which, depending on
// the pruning parameters may result in the deletion of all archived records.

if err := k.EndBlockerProcessPendingUndelegations(ctx); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err := k.EndBlockerProcessPendingUndelegations(ctx); err != nil {
// Make sure to add comment here and below
if err := k.EndBlockerProcessPendingUndelegations(ctx); err != nil {

Copy link
Member

@Olshansk Olshansk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes from offline discussion.

// Submit unelegation during sessionNUmber = 3
application.delegations = removeFromList(application.delegations, gatewayAddress) // remove from active
application.pendingUndelegation[sessionNumber=3] = addToList(application.pendingUndelegation[sessionNumber], gatewayAddress)

Rename the map: `mapOfSessionNumberToGatewayAddressThatWillBeUndelegatedFromInTheFutureuButMayStillBeUsedToValidateActiveSession`

// Verifying signature
// Assume verifiying signature at sessionNUmber = 5
// 1. Get ACTIVE gateway address
activeAddress = application.delegations
// 2. Get PENDING gateway address
// Assume session 3 is from block 10 to block 15
// If undelegation at block 11 -> session Number = 3 // filtered at session=3 -> returns addr
// If undelegation at block 12 -> session Number = 3 // filtered -> returns addr
// If undelegation at block 13 -> session Number = 3 // filtered -> returns addr
// If undelegation at block 14 -> session Number = 3 // filtered -> returns addr
// If undelegation at block 15 -> session Number = 3 // filtered -> returns addr
// If undelegation at block 16 -> session Number = 4 // filtered at session = 3 -> not return
filteredAddress = filtered(application.pendingUndelegation[sessionNumber=3], currentSessionNumber=5)
// filteredFunction: if sessionNumber < currentSessionNUmber: return; else don't return
// 3. Get all session addresses
all = active + filtered
// 4. Do normal business logic


@Olshansk
Copy link
Member

Olshansk commented May 3, 2024

Action Plan:

  1. Refer to this comment: [Rings] Wait for redelegation grace periods to elapse before updating rings #476 (comment)
  2. Leave this PR as is: make it a draft and close later (or pick up) depending on how alterative solution evolves
  3. Refer to pseudo-code below for attempt number
  4. Feel free to copy-paste code from here (e.g. tests) in new PR
ClaimValidation() {
	addressesToBuildRing = getActiveGatewayAddr(app, currentSessionNumber)
}


func getActiveGatewayAddr(app, currSessNumber) {
	activeGateways = app.activeGateways
	for sessionNumber, pendingUndelegations := range app.pendingUndelegations {
		if sessionNumber < currSessNumber {
			activeGateways = append(activeGateways, pendingUndelegations...)
		}
	}
	return activeGateways
}

func EndBlocker() {
	cleanUpPendingUndelegations(currSessionNumber)
}

func cleanUpPendingUndelegations() {
	sessionNumbersToRemoveUndelegationsFor = someFunctionUsingGovParam(currSess, numSessionsToGoBack)
	for app := allApplications {
		for sess, undelegations := app.undelegations {
			if sess <sessionNumbersToRemoveUndelegationsFor {
				delete(app.undelegations, sess)
			}
		}
	}
}

@red-0ne red-0ne marked this pull request as draft May 6, 2024 12:35
@red-0ne
Copy link
Contributor Author

red-0ne commented May 8, 2024

Closing in favor of #518

@red-0ne red-0ne closed this May 8, 2024
@Olshansk
Copy link
Member

Olshansk commented May 8, 2024

Closing in favor of #518

Love two things about this:

  1. We're biasing to a simpler solution straight away
  2. We're keeping this as a reference in history for when we need it

💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
application Changes related to the Application actor off-chain Off-chain business logic on-chain On-chain business logic
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

None yet

2 participants