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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions app/app.go
Expand Up @@ -87,6 +87,7 @@ func NewPocketCoreApp(genState GenesisState, keybase keys.Keybase, tmClient clie
// give pocket keeper to nodes module for easy cache clearing
app.nodesKeeper.PocketKeeper = app.pocketKeeper
app.appsKeeper.PocketKeeper = app.pocketKeeper
app.accountKeeper.POSKeeper = app.nodesKeeper
msmania marked this conversation as resolved.
Show resolved Hide resolved
// setup module manager
app.mm = module.NewManager(
auth.NewAppModule(app.accountKeeper),
Expand Down
28 changes: 20 additions & 8 deletions app/cmd/cli/stake.go
Expand Up @@ -3,13 +3,14 @@ package cli
import (
"encoding/json"
"fmt"
"github.com/pokt-network/pocket-core/app"
"github.com/pokt-network/pocket-core/types"
"github.com/spf13/cobra"
"log"
"regexp"
"strconv"
"strings"

"github.com/pokt-network/pocket-core/app"
"github.com/pokt-network/pocket-core/types"
"github.com/spf13/cobra"
)

func init() {
Expand Down Expand Up @@ -93,11 +94,22 @@ var nonCustodialstakeCmd = &cobra.Command{
Use: "non-custodial <operatorPublicKey> <outputAddress> <amount> <RelayChainIDs> <serviceURI> <networkID> <fee> <isBefore8.0>",
Short: "Stake a node in the network, non-custodial stake allows a different output address for rewards/return of staked funds. The signer may be the operator or the output address. The signer must specify the public key of the operator",
Long: `Stake the node into the network, making it available for service.
Will prompt the user for the signer account passphrase, fund and fees are collected from signer account. If both accounts are present signer priority is first output then operator. If the node is already staked, this transaction acts as an *update* transaction.
A node can updated relayChainIDs, serviceURI, and raise the stake amount with this transaction.
If the node is currently staked at X and you submit an update with new stake Y. Only Y-X will be subtracted from an account
If no changes are desired for the parameter, just enter the current param value just as before.
The signer may be the operator or the output address.`,
Searches the keybase for the operator address, the new output address, and the
current output address in order and chooses the first address available in the
keybase as a signer.

Will prompt the user for the signer account passphrase, fund and fees are
collected from signer account. If the node is already staked, this transaction
acts as an *update* transaction. A node can update relayChainIDs, serviceURI,
and raise the stake amount with this transaction. The output address can be
changed when the output address is empty or a transaction is signed by the
current output address.

If the node is currently staked at X and you submit an update with new stake Y,
only Y-X will be subtracted from an account.

If no changes are desired for the parameter, just enter the current param value
just as before.`,
Args: cobra.ExactArgs(8),
Run: func(cmd *cobra.Command, args []string) {
app.InitConfig(datadir, tmNode, persistentPeers, seeds, remoteCLIURL)
Expand Down
84 changes: 63 additions & 21 deletions app/cmd/cli/txUtil.go
Expand Up @@ -5,21 +5,20 @@ import (
"encoding/json"
"errors"
"fmt"

"github.com/pokt-network/pocket-core/app"
"github.com/pokt-network/pocket-core/app/cmd/rpc"
"github.com/pokt-network/pocket-core/codec"
"github.com/pokt-network/pocket-core/crypto"
"github.com/pokt-network/pocket-core/crypto/keys"
appsType "github.com/pokt-network/pocket-core/x/apps/types"
nodeTypes "github.com/pokt-network/pocket-core/x/nodes/types"
pocketTypes "github.com/pokt-network/pocket-core/x/pocketcore/types"
"github.com/tendermint/tendermint/libs/rand"

//"github.com/pokt-network/pocket-core/crypto/keys/mintkey"
sdk "github.com/pokt-network/pocket-core/types"
appsType "github.com/pokt-network/pocket-core/x/apps/types"
"github.com/pokt-network/pocket-core/x/auth"
authTypes "github.com/pokt-network/pocket-core/x/auth/types"
govTypes "github.com/pokt-network/pocket-core/x/gov/types"
nodeTypes "github.com/pokt-network/pocket-core/x/nodes/types"
pocketTypes "github.com/pokt-network/pocket-core/x/pocketcore/types"
"github.com/tendermint/tendermint/libs/rand"
)

// SendTransaction - Deliver Transaction to node
Expand Down Expand Up @@ -123,11 +122,47 @@ func LegacyStakeNode(chains []string, serviceURL, fromAddr, passphrase, chainID
}, nil
}

// getCurrentOutputAddress returns the current output address for the operator
// provided. Nil is returned if there is no output address.
func getCurrentOutputAddress(operatorAddr sdk.Address) (sdk.Address, error) {
msmania marked this conversation as resolved.
Show resolved Hide resolved
j, err := json.Marshal(rpc.HeightAndAddrParams{
Height: 0,
Address: operatorAddr.String(),
})
if err != nil {
return nil, err
}

res, err := QueryRPC(GetNodePath, j)
if err != nil {
return nil, err
}

var node nodeTypes.Validator
if err := json.Unmarshal([]byte(res), &node); err != nil {
return nil, err
}

return node.OutputAddress, nil
}

// getFirstAddressAvailableInKeybase searches the given keybase for each of the
// given addresses in order, and returns the first address that exists. Nil is
// returned if none of the addresses exists.
func getFirstAddressAvailableInKeybase(
msmania marked this conversation as resolved.
Show resolved Hide resolved
kb keys.Keybase,
addresses []sdk.Address,
) *sdk.Address {
for _, address := range addresses {
if _, err := kb.Get(address); err == nil {
return &address
}
}
return nil
}

// StakeNode - Deliver Stake message to node
func StakeNode(chains []string, serviceURL, operatorPubKey, output, passphrase, chainID string, amount sdk.BigInt, fees int64, isBefore8 bool) (*rpc.SendRawTxParams, error) {
var operatorPublicKey crypto.PublicKey
var operatorAddress sdk.Address
var fromAddress sdk.Address
kb, err := app.GetKeybase()
if err != nil {
return nil, err
Expand All @@ -141,23 +176,30 @@ func StakeNode(chains []string, serviceURL, operatorPubKey, output, passphrase,
if err != nil {
return nil, err
}
operatorPublicKey = pbkey
operatorPublicKey := pbkey

outputAddress, err := sdk.AddressFromHex(output)
if err != nil {
return nil, err
}
kp, err := kb.Get(outputAddress)
if err != nil {
operatorAddress = sdk.Address(operatorPublicKey.Address())
kp, err = kb.Get(operatorAddress)
if err != nil {
return nil, errors.New("Neither the Output Address nor the Operator Address is able to be retrieved from the keybase" + err.Error())
}
fromAddress = kp.GetAddress()
} else {
fromAddress = outputAddress

operatorAddress := sdk.Address(operatorPublicKey.Address())
validSigners := []sdk.Address{
outputAddress,
operatorAddress,
}

if outputCur, err := getCurrentOutputAddress(operatorAddress); err == nil {
validSigners = append(validSigners, outputCur)
}

fromAddress := getFirstAddressAvailableInKeybase(kb, validSigners)
if fromAddress == nil {
return nil, errors.New(
"None of the operator address, the new output address, or the current" +
" output address is able to be retrieved from the keybase")
}

m := make(map[string]struct{})
for _, chain := range chains {
if _, found := m[chain]; found {
Expand Down Expand Up @@ -199,7 +241,7 @@ func StakeNode(chains []string, serviceURL, operatorPubKey, output, passphrase,
if err != nil {
return nil, err
}
txBz, err := newTxBz(app.Codec(), msg, fromAddress, chainID, kb, passphrase, fees, "", false)
txBz, err := newTxBz(app.Codec(), msg, *fromAddress, chainID, kb, passphrase, fees, "", false)
if err != nil {
return nil, err
}
Expand Down
7 changes: 7 additions & 0 deletions codec/codec.go
Expand Up @@ -50,6 +50,7 @@ const (
BlockSizeModifyKey = "BLOCK"
RSCALKey = "RSCAL"
VEDITKey = "VEDIT"
OutputAddressEditKey = "OEDIT"
ClearUnjailedValSessionKey = "CRVAL"
)

Expand Down Expand Up @@ -267,6 +268,12 @@ func (cdc *Codec) IsAfterNonCustodialUpgrade(height int64) bool {
return (UpgradeFeatureMap[NonCustodialUpdateKey] != 0 && height >= UpgradeFeatureMap[NonCustodialUpdateKey]) || TestMode <= -3
}

func (cdc *Codec) IsAfterOutputAddressEditorUpgrade(height int64) bool {
return (UpgradeFeatureMap[OutputAddressEditKey] != 0 &&
height >= UpgradeFeatureMap[OutputAddressEditKey]) ||
TestMode <= -3
}

// IsOnNonCustodialUpgrade Note: includes the actual upgrade height
func (cdc *Codec) IsOnNonCustodialUpgrade(height int64) bool {
return (UpgradeFeatureMap[NonCustodialUpdateKey] != 0 && height == UpgradeFeatureMap[NonCustodialUpdateKey]) || TestMode <= -3
Expand Down
15 changes: 13 additions & 2 deletions x/auth/ante.go
Expand Up @@ -4,14 +4,15 @@ import (
"bytes"
"encoding/hex"
"fmt"
"os"

"github.com/pokt-network/pocket-core/codec"
posCrypto "github.com/pokt-network/pocket-core/crypto"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/pokt-network/pocket-core/x/auth/keeper"
"github.com/pokt-network/pocket-core/x/auth/types"
"github.com/tendermint/tendermint/state/txindex"
tmTypes "github.com/tendermint/tendermint/types"
"os"
)

// NewAnteHandler returns an AnteHandler that checks signatures and deducts fees from the first signer.
Expand Down Expand Up @@ -61,8 +62,18 @@ func ValidateTransaction(ctx sdk.Ctx, k Keeper, stdTx types.StdTx, params Params
if res != nil {
msmania marked this conversation as resolved.
Show resolved Hide resolved
return nil, types.ErrDuplicateTx(ModuleName, hex.EncodeToString(txHash))
}

validSigners := stdTx.GetSigners()
if k.Cdc.IsAfterNonCustodialUpgrade(ctx.BlockHeight()) &&
k.Cdc.IsAfterOutputAddressEditorUpgrade(ctx.BlockHeight()) {
// MsgStake may be signed by the current output address. We need to ask
// the node keeper to retrieve the current output address.
validSigners = append(validSigners,
k.POSKeeper.GetMsgStakeOutputSigner(ctx, stdTx.Msg))
}

var pk posCrypto.PublicKey
for _, signer := range stdTx.GetSigners() {
for _, signer := range validSigners {
// attempt to get the public key from the signature
if stdTx.GetSignature().GetPublicKey() != "" {
var err error
Expand Down
2 changes: 2 additions & 0 deletions x/auth/keeper/keeper.go
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"fmt"

"github.com/pokt-network/pocket-core/codec"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/pokt-network/pocket-core/x/auth/types"
Expand All @@ -11,6 +12,7 @@ import (
// Keeper of the supply store
type Keeper struct {
Cdc *codec.Codec
POSKeeper types.PosKeeper
storeKey sdk.StoreKey
subspace sdk.Subspace
permAddrs map[string]types.PermissionsForAddress
Expand Down
9 changes: 9 additions & 0 deletions x/auth/types/expectedKeepers.go
@@ -0,0 +1,9 @@
package types

import (
sdk "github.com/pokt-network/pocket-core/types"
)

type PosKeeper interface {
GetMsgStakeOutputSigner(sdk.Ctx, sdk.Msg) sdk.Address
}
35 changes: 25 additions & 10 deletions x/nodes/keeper/common_test.go
@@ -1,26 +1,24 @@
package keeper

import (
types2 "github.com/pokt-network/pocket-core/codec/types"
"math/rand"
"testing"

"github.com/pokt-network/pocket-core/codec"
types2 "github.com/pokt-network/pocket-core/codec/types"
"github.com/pokt-network/pocket-core/crypto"
"github.com/pokt-network/pocket-core/store"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/pokt-network/pocket-core/types/module"
"github.com/pokt-network/pocket-core/x/auth"
"github.com/pokt-network/pocket-core/x/gov"
"github.com/pokt-network/pocket-core/x/nodes/exported"
"github.com/pokt-network/pocket-core/x/nodes/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"

abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/pokt-network/pocket-core/codec"
"github.com/pokt-network/pocket-core/store"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/pokt-network/pocket-core/x/auth"
"github.com/pokt-network/pocket-core/x/nodes/types"
dbm "github.com/tendermint/tm-db"
)

var (
Expand Down Expand Up @@ -135,6 +133,23 @@ func sendFromModuleToAccount(t *testing.T, ctx sdk.Ctx, k *Keeper, module string
}
}

// fundAccount mints new tokens in the stake pool and sends them
// to the address provided, for testing purposes.
func fundAccount(
ctx sdk.Context,
k Keeper,
address sdk.Address,
coin sdk.Coin,
) sdk.Error {
coins := sdk.NewCoins(coin)
if err := k.AccountKeeper.MintCoins(
ctx, types.StakedPoolName, coins); err != nil {
return err
}
return k.AccountKeeper.SendCoinsFromModuleToAccount(
ctx, types.StakedPoolName, address, coins)
}

func getRandomPubKey() crypto.Ed25519PublicKey {
var pub crypto.Ed25519PublicKey
_, err := rand.Read(pub[:])
Expand Down
13 changes: 13 additions & 0 deletions x/nodes/keeper/keeper.go
Expand Up @@ -117,3 +117,16 @@ func (k Keeper) ConvertState(ctx sdk.Ctx) {
k.SetValidatorSigningInfos(ctx, signingInfos)
k.Cdc.DisableUpgradeOverride()
}

func (k Keeper) GetMsgStakeOutputSigner(ctx sdk.Ctx, msg sdk.Msg) sdk.Address {
stakeMsg, ok := msg.(*types.MsgStake)
if !ok {
return nil
}
operatorAddr := sdk.Address(stakeMsg.PublicKey.Address())
outputAddr, found := k.GetValidatorOutputAddress(ctx, operatorAddr)
if !found {
return nil
}
return outputAddr
}