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

Allow the output address owner to change the output address via MsgStake #1534

Merged
merged 3 commits into from Apr 6, 2023
Merged

Allow the output address owner to change the output address via MsgStake #1534

merged 3 commits into from Apr 6, 2023

Conversation

msmania
Copy link
Contributor

@msmania msmania commented Mar 13, 2023

This patch allows the owner of the output address of a staked node to change the output address to another via the existing stake transaction. The same idea was proposed as #1499.

Rationale:

This feature gives users more flexibility. One of the possible use cases is that a node runner can convert custodial stake nodes into non-custodial stake nodes without unstaking. Or a current non-custodial stake user may want to change the output address some time after they stake nodes.

Technical details:

Below are the key functions in the transaction processing flow. This patch makes a new MsgStake transaction to change the output address pass through all Validate* functions and lets the final function EditStakeValidator accept the change in the world state if the new feature key “OEDIT” is enabled.

  1. BaseApp.runTx (baseapp.go)
    1. auth.ValidateTransaction (ante.go)
    2. BaseApp.runMsg (baseapp.go)
      1. nodes.handleStake (handler.go)
        1. keeper.ValidateValidatorStaking (valStateChanges.go)
          1. keeper.ValidateValidatorMsgSigner (for the new validator)
          2. keeper.ValidateValidatorMsgSigner (for the current validator)
          3. keeper.ValidateEditStake
        2. keeper.StakeValidator
          1. keeper.EditStakeValidator

The challenge was in ValidateTransaction, which expects a transaction message to be signed by a key which appears in the transaction itself. For example, MsgStake must be signed by MsgStake.PublicKey or MsgStake.Output. Since we intend to allow only the current output address owner to change the output address, however, a new MsgStake transaction must be signed by the current output address, and it doesn’t appear in the transaction because a new output address is there. This patch needs to change that signer restriction rule.

Each message type implements GetSigners that returns a slice of Address. When a transaction arrives at ValidateTransaction, it calls GetSigners and checks if a transaction is signed by any of the addresses returned from GetSigners. If not, the transaction is rejected with CodeUnauthorized. The challenge here is that ValidateTransaction is in the auth package that doesn’t know about MsgStake. One possible solution is to make MsgStake.GetSigners return three addresses, MsgStake.PublicKey, MsgStake.Output, and the current output address. However, implementing it is not straightforward because MsgStake is in the types package which doesn’t have direct access to the keeper package that is required to get the current output address.

The proposed solution in this patch is to introduce a new method GetMsgStakeOutputSigner in the keeper package and ValidateTransaction calls it to append the current output address to the slice returned from GetSigners.

The changes in valStateChanges.go are straightforward. ValidateValidatorMsgSigner simply checks if a signer is either the node’s operator or output address. We need to skip the call to ValidateValidatorStaking with the new validator because the signer does not appear in the new validator. The final validate function ValidateEditStake ensures that MsgStake to change the output address must be signed by the current output address. Otherwise it returns a new error ErrDisallowedOutputAddressChange.

After all validate functions, EditStakeValidator accepts a new output address.

Testing:

  1. Prepare a local network with NCUST enabled.
  2. Set the following environment variables accordingly.
    export POKT=<path to pocket executable>
    export CHAINS=<chain IDs to host>
    export URL=<service url>
    export STAKE=<upokt to stake>
    export NETWORK=<network name>
    export NODE=<node address>
    export PUBKEY=<node public key>
    export PRIVKEY=<node private key>
    export OUTPUT1=<output address>
    export OUTPUT1_PRIVKEY=<OUTPUT1’s private key>
    export OUTPUT2=<another output address>
    export OUTPUT2_PRIVKEY=<OUTPUT2’s private key>
    export DAO=<dao address>
    
  3. Make sure all addresses are stored in the keybase and have enough tokens.
  4. Submit a non-custodial stake tx to stake a new node.
    $POKT accounts delete $OUTPUT1
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT1 $STAKE $CHAINS $URL $NETWORK 10000 false
    
  5. Submit three stake transactions to change the output address signed by the new output address, the operator address, and the current output address respectively. Verify all transactions are rejected with CodeUnauthorizedSigner (pos:125), CodeUnequalOutputAddr (pos:124), and CodeUnauthorized (sdk:4).
    $POKT accounts import-raw $OUTPUT1_PRIVKEY
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    $POKT accounts delete $OUTPUT2
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    $POKT accounts delete $NODE
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    
  6. Enable OEDIT. Choose the upgrade height accordingly.
    $POKT gov enable $DAO <upgrade height> OEDIT $NETWORK 10000
    
  7. Submit three stake transactions to change the output address signed by the new output address, the operator address, and the current output address respectively. Verify the first two transactions are rejected with CodeUnauthorizedSigner (pos:125) and CodeDisallowedOutputAddressEdit (pos:127) respectively, and the last transaction is accepted.
    $POKT accounts import-raw $PRIVKEY
    $POKT accounts import-raw $OUTPUT2_PRIVKEY
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    $POKT accounts delete $OUTPUT2
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    $POKT accounts delete $NODE
    $POKT nodes stake non-custodial $PUBKEY $OUTPUT2 $STAKE $CHAINS $URL $NETWORK 10000 false
    
  8. Verify the node’s output address is changed to $OUTPUT2.

codec/codec.go Outdated Show resolved Hide resolved
codec/codec.go Outdated Show resolved Hide resolved
app/app.go Show resolved Hide resolved
x/auth/ante.go Outdated Show resolved Hide resolved
x/auth/ante.go Show resolved Hide resolved
x/nodes/keeper/valStateChanges_test.go Outdated Show resolved Hide resolved
x/nodes/keeper/valStateChanges_test.go Outdated Show resolved Hide resolved
x/nodes/keeper/valStateChanges_test.go Outdated Show resolved Hide resolved
x/nodes/keeper/valStateChanges_test.go Show resolved Hide resolved
x/nodes/keeper/valStateChanges_test.go Show resolved Hide resolved
This patch allows the owner of the output address of a staked node to change
the output address to another via MsgStake after the new feature key "OEDIT"
is enabled.

In addition to the server side change, this patch updates the behavior of
the `pocket nodes stake non-custodial` command to get an MsgStake to be signed
by the current output address if neither the operator nor the new output
address is not available in the key base.
@msmania msmania requested a review from Olshansk April 1, 2023 00:30
@Olshansk
Copy link
Member

Olshansk commented Apr 5, 2023

@msmania

Here's a mermaid diagram ChatGPT gave me using your github description.

Screenshot 2023-04-04 at 7 44 37 PM

Screenshot 2023-04-04 at 7 44 02 PM

flowchart TD
    A(BaseApp.runTx) --> B(auth.ValidateTransaction)
    B --> C(BaseApp.runMsg)
    C --> D(nodes.handleStake)
    D --> E(keeper.ValidateValidatorStaking)
    E --> F(keeper.ValidateValidatorMsgSigner)
    F --> G(keeper.ValidateValidatorMsgSigner)
    G --> H(keeper.ValidateEditStake)
    H --> I(keeper.StakeValidator)
    I --> J(keeper.EditStakeValidator)

    subgraph Before
        B -->|Validate using signed keys|L(MsgStake.GetSigners)
        H -->|Ensure signed by current output address| M(Error)
    end

    subgraph After
        B --> K(GetMsgStakeOutputSigner)
        K -->|Append current output address|L(MsgStake.GetSigners)
        H -->|Ensure signed by current output address| M(ValidateEditStake)
    end

@reviewpad reviewpad bot added large Pull request is large waiting-for-review labels Apr 5, 2023
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.

A couple of minor NITS/comments, but otherwise this PR LGTM from a code perspective. Amazing test and amazingly clear code!

We need to submit a proposal to the DAO to approve this (cc @jessicadaugherty), but I don't expect any opposition from the community.

app/cmd/cli/txUtil.go Outdated Show resolved Hide resolved
app/cmd/cli/txUtil.go Show resolved Hide resolved
app/cmd/cli/txUtil.go Outdated Show resolved Hide resolved
app/cmd/cli/txUtil.go Outdated Show resolved Hide resolved
app/cmd/cli/txUtil.go Show resolved Hide resolved
app/cmd/cli/stake.go Outdated Show resolved Hide resolved
x/nodes/keeper/valStateChanges.go Show resolved Hide resolved
@reviewpad reviewpad bot requested a review from luyzdeleon April 5, 2023 03:49
@reviewpad
Copy link

reviewpad bot commented Apr 6, 2023

AI-Generated Summary: This pull request includes changes in various files related to the handling of outputs addresses, transaction signing, and validator staking. Key updates include refactoring and improvements of functions in x/nodes/keeper/valStateChanges.go, adding test functions and helper functions for handling stakes, introducing a new GetMsgStakeOutputSigner function to the Keeper struct, and modifying the ValidateTransaction function in x/auth/ante.go. Additionally, changes to the nodes module store and improved connections between account keeping and node modules have been made.

New functions such as fundAccount, IsAfterOutputAddressEditorUpgrade, and methods related to the PosKeeper interface have also been introduced. Error handling has been updated with the addition of CodeDisallowedOutputAddressEdit and its corresponding error function. Finally, various code quality improvements have been made, such as rearranging import statements, providing extended descriptions for certain fields, and adding new utility functions in the app/cmd/cli/txUtil.go file. Overall, these changes mainly affect output address handling, transaction signing, and improving code readability and robustness.

Olshansk
Olshansk previously approved these changes Apr 6, 2023
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.

LGTM!! :shipit:

@msmania
Copy link
Contributor Author

msmania commented Apr 6, 2023

@msmania

Here's a mermaid diagram ChatGPT gave me using your github description.

Interesting. Fortunately or unfortunately, AI made a mistake. That flow is not correct. I think it's hard to overlap before and after in the same chart. Here are two charts before and after. This matches the test scenario in the first description.

Sending MsgStake without this patch

flowchart TD
  A(runTx) --> B(ValidateTransaction)
  B -- If signed by the current output address --> K([sdk:4])
  A --> C(runMsg)
  C --> D(handleStake)
  D --> E(ValidateValidatorStaking)
  D -.-> F(StakeValidator)
  E --> G(ValidateValidatorMsgSigner\nwith the current state)
  G -- If signed by the new output address --> L([pos:125])
  E --> H(ValidateValidatorMsgSigner\nwith the new state)
  E --> I(ValidateEditStake)
  I -- If signed by the operator --> M([pos:124])
  F -.-> J(EditStakeValidator)

Sending MsgStake with this patch

flowchart TD
  A(runTx) --> B(ValidateTransaction)
  B --> N(GetMsgStakeOutputSigner)
  N -- If signed by the current output address --> K([ok])
  A --> C(runMsg)
  C --> D(handleStake)
  D --> E(ValidateValidatorStaking)
  D --> F(StakeValidator)
  E --> O{Valid output address edit?}
  O -. No .-> G(ValidateValidatorMsgSigner\nwith the current state)
  G -.-> L([pos:125])
  E --> H(ValidateValidatorMsgSigner\nwith the new state)
  E --> I(ValidateEditStake)
  I -- If signed by the operator --> M([pos:127])
  F --> J(EditStakeValidator)
  J --> P([Happy ending!])

@msmania msmania removed the request for review from luyzdeleon April 6, 2023 21:39
@reviewpad
Copy link

reviewpad bot commented Apr 6, 2023

AI-Generated Summary: This pull request introduces updates across several files, making significant changes to the staking and validation logic. Key changes include adding new functions and conditions to accommodate various upgrades, enhanced validation, and better handling of different block heights. Some refactoring has been done for improved code maintainability and robustness, particularly in the StakeNode function. Additionally, a new interface PosKeeper, a corresponding implementation in the Keeper struct, and an app/app.go update to set the POSKeeper property have been introduced. Lastly, import statements have been reorganized throughout various files, and new test functions and utilities have been added for more comprehensive testing.

@msmania msmania requested a review from Olshansk April 6, 2023 22:33
@msmania msmania merged commit 0aa12e7 into pokt-network:staging Apr 6, 2023
2 checks passed
@Olshansk
Copy link
Member

Olshansk commented Apr 6, 2023

@msmania Wanted to encourage you to submit another PR updating the documentation so these awesome diagrams don't go to waste!

@msmania msmania deleted the feature-output-address-change branch April 7, 2023 01:33
@msmania
Copy link
Contributor Author

msmania commented Apr 7, 2023

@msmania Wanted to encourage you to submit another PR updating the documentation so these awesome diagrams don't go to waste!

Created #1544

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

2 participants