Skip to content

Commit

Permalink
feat: CNS-931: vault and operator provider addresses (#1382)
Browse files Browse the repository at this point in the history
* CNS-931: change stake entry address field to "operator"

* CNS-931: support vault definition from stake TXs (normal and bulk)

* CNS-931: let staking define operator instead of vault

* CNS-931: fix staking and enforce modify stake operator restrictions

* CNS-931: unstake proposal uses only operator

* CNS-931: agrregate rewards to vault

* CNS-931: use vault for stake TX

* CNS-931: make unstake work with vault

* CNS-931: aggregate rewards to vault

* CNS-931: small fix

* CNS-931: change rewards queries to support both operator/vault

* cns-931: added vault sig account and fixed unit tests

* CNS-931: conflict unit tests

* CNS-931: cu tracker unit test

* CNS-931: delegator rewards unit test

* CNS-931: relay payment unit test

* CNS-931: stake unit tests

* CNS-931: unstake unit tests

* CNS-931: pairing unit tests

* CNS-931: fix stake unit test

* CNS-931: iprpc unit test

* CNS-931: update readme

* CNS-931: migrator

* CNS-931: lint fix

* CNS-931: sdk fixes

* CNS-931: another sdk fix

* CNS-931: bug fix

* CNS-931: test fix

* CNS-931: small PR changes

* CNS-931: more PR fixes

* CNS-931: return address field in stake entry for backwards complatibility

* Update x/dualstaking/README.md

Co-authored-by: Elad Gildnur <[email protected]>

* Update x/epochstorage/README.md

Co-authored-by: Elad Gildnur <[email protected]>

* CNS-931: some PR fixes

* CNS-931: implemented GetVaultAddr

* CNS-931: PR fix

* CNS-931: change stake funcs arg order

* CNS-931: small PR fixes

* CNS-931: small PR change

* CNS-931: small fix

* CNS-931: lint fix

* change operator to address

* change operator in all files

* CNS-931: revert typescript changes

* Merge branch 'CNS-931-support-provider-using-2-keys' into CNS-949-add-feegrant-module

* CNS-931: revert expected providers change

* CNS-931: fix migrator

* CNS-931: make it impossible to delegate to vault

* CNS-931: comment fixes and code rearrangement

* CNS-931: added small check

* CNS-931: added helper func to clean code

* CNS-931: unit tests

---------

Co-authored-by: Elad Gildnur <[email protected]>
Co-authored-by: Yarom Swisa <[email protected] git config --global user.name Yarom>
Co-authored-by: Yaroms <[email protected]>
  • Loading branch information
4 people authored May 8, 2024
1 parent 4cc0300 commit 0401570
Show file tree
Hide file tree
Showing 64 changed files with 3,614 additions and 434 deletions.
1 change: 1 addition & 0 deletions proto/lavanet/lava/epochstorage/stake_entry.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ message StakeEntry {
uint64 delegate_commission = 11; // delegation commission (precentage 0-100)
uint64 last_change = 12;
BlockReport block_report = 13;
string vault = 14;
}

// BlockReport holds the most up-to-date info regarding blocks of the provider
Expand Down
1 change: 1 addition & 0 deletions proto/lavanet/lava/pairing/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ message MsgStakeProvider {
cosmos.base.v1beta1.Coin delegate_limit = 7 [(gogoproto.nullable) = false];
uint64 delegate_commission = 8; // delegation commission (precentage 0-100)
string validator = 9;
string address = 10;
}

message MsgStakeProviderResponse {
Expand Down
2 changes: 1 addition & 1 deletion testutil/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func StakeAccount(t *testing.T, ctx context.Context, keepers testkeeper.Keepers,
for _, collection := range spec.ApiCollections {
endpoints = append(endpoints, epochstoragetypes.Endpoint{IPPORT: "123", ApiInterfaces: []string{collection.CollectionData.ApiInterface}, Geolocation: 1})
}
_, err := servers.PairingServer.StakeProvider(ctx, &types.MsgStakeProvider{Creator: acc.Addr.String(), ChainID: spec.Index, Amount: sdk.NewCoin(keepers.StakingKeeper.BondDenom(sdk.UnwrapSDKContext(ctx)), sdk.NewInt(stake)), Geolocation: 1, Endpoints: endpoints, Moniker: "prov", DelegateLimit: sdk.NewCoin(keepers.StakingKeeper.BondDenom(sdk.UnwrapSDKContext(ctx)), sdk.ZeroInt()), DelegateCommission: 100, Validator: sdk.ValAddress(validator.Addr).String()})
_, err := servers.PairingServer.StakeProvider(ctx, &types.MsgStakeProvider{Creator: acc.Addr.String(), Address: acc.Addr.String(), ChainID: spec.Index, Amount: sdk.NewCoin(keepers.StakingKeeper.BondDenom(sdk.UnwrapSDKContext(ctx)), sdk.NewInt(stake)), Geolocation: 1, Endpoints: endpoints, Moniker: "prov", DelegateLimit: sdk.NewCoin(keepers.StakingKeeper.BondDenom(sdk.UnwrapSDKContext(ctx)), sdk.ZeroInt()), DelegateCommission: 100, Validator: sdk.ValAddress(validator.Addr).String()})
require.NoError(t, err)
}

Expand Down
40 changes: 25 additions & 15 deletions testutil/common/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ func (ts *Tester) SetupAccounts(numSub, numAdm, numDev int) *Tester {

func (ts *Tester) AddAccount(kind string, idx int, balance int64) (sigs.Account, string) {
name := kind + strconv.Itoa(idx)
ts.accounts[name] = CreateNewAccount(ts.GoCtx, *ts.Keepers, balance)
acc := CreateNewAccount(ts.GoCtx, *ts.Keepers, balance)
if kind == PROVIDER {
vault := CreateNewAccount(ts.GoCtx, *ts.Keepers, balance)
acc.Vault = &vault
}
ts.accounts[name] = acc
return ts.Account(name)
}

Expand Down Expand Up @@ -150,12 +155,13 @@ func (ts *Tester) AccountsMap() map[string]sigs.Account {
return ts.accounts
}

func (ts *Tester) StakeProvider(addr string, spec spectypes.Spec, amount int64) error {
return ts.StakeProviderExtra(addr, spec, amount, nil, 0, "prov")
func (ts *Tester) StakeProvider(vault string, provider string, spec spectypes.Spec, amount int64) error {
return ts.StakeProviderExtra(vault, provider, spec, amount, nil, 0, "prov")
}

func (ts *Tester) StakeProviderExtra(
addr string,
vault string,
provider string,
spec spectypes.Spec,
amount int64,
endpoints []epochstoragetypes.Endpoint,
Expand Down Expand Up @@ -186,7 +192,7 @@ func (ts *Tester) StakeProviderExtra(
}

stake := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(amount))
_, err := ts.TxPairingStakeProvider(addr, spec.Index, stake, endpoints, geoloc, moniker)
_, err := ts.TxPairingStakeProvider(vault, provider, spec.Index, stake, endpoints, geoloc, moniker)

return err
}
Expand Down Expand Up @@ -554,7 +560,8 @@ func (ts *Tester) TxProjectSetPolicy(projectID, subkey string, policy *planstype

// TxPairingStakeProvider: implement 'tx pairing stake-provider'
func (ts *Tester) TxPairingStakeProvider(
addr string,
vault string,
provider string,
chainID string,
amount sdk.Coin,
endpoints []epochstoragetypes.Endpoint,
Expand All @@ -563,7 +570,7 @@ func (ts *Tester) TxPairingStakeProvider(
) (*pairingtypes.MsgStakeProviderResponse, error) {
val, _ := ts.GetAccount(VALIDATOR, 0)
msg := &pairingtypes.MsgStakeProvider{
Creator: addr,
Creator: vault,
Validator: sdk.ValAddress(val.Addr).String(),
ChainID: chainID,
Amount: amount,
Expand All @@ -572,13 +579,15 @@ func (ts *Tester) TxPairingStakeProvider(
Moniker: moniker,
DelegateLimit: sdk.NewCoin(ts.Keepers.StakingKeeper.BondDenom(ts.Ctx), sdk.ZeroInt()),
DelegateCommission: 100,
Address: provider,
}
return ts.Servers.PairingServer.StakeProvider(ts.GoCtx, msg)
}

// TxPairingStakeProvider: implement 'tx pairing stake-provider'
func (ts *Tester) TxPairingStakeProviderFull(
addr string,
vault string,
provider string,
chainID string,
amount sdk.Coin,
endpoints []epochstoragetypes.Endpoint,
Expand Down Expand Up @@ -612,7 +621,7 @@ func (ts *Tester) TxPairingStakeProviderFull(
}

msg := &pairingtypes.MsgStakeProvider{
Creator: addr,
Creator: vault,
Validator: sdk.ValAddress(val.Addr).String(),
ChainID: chainID,
Amount: amount,
Expand All @@ -621,28 +630,29 @@ func (ts *Tester) TxPairingStakeProviderFull(
Moniker: moniker,
DelegateLimit: sdk.NewCoin(ts.Keepers.StakingKeeper.BondDenom(ts.Ctx), sdk.NewIntFromUint64(delegateLimit)),
DelegateCommission: commission,
Address: provider,
}
return ts.Servers.PairingServer.StakeProvider(ts.GoCtx, msg)
}

// TxPairingUnstakeProvider: implement 'tx pairing unstake-provider'
func (ts *Tester) TxPairingUnstakeProvider(
addr string,
vault string,
chainID string,
) (*pairingtypes.MsgUnstakeProviderResponse, error) {
val, _ := ts.GetAccount(VALIDATOR, 0)
msg := &pairingtypes.MsgUnstakeProvider{
Validator: sdk.ValAddress(val.Addr).String(),
Creator: addr,
Creator: vault,
ChainID: chainID,
}
return ts.Servers.PairingServer.UnstakeProvider(ts.GoCtx, msg)
}

// TxPairingRelayPayment: implement 'tx pairing relay-payment'
func (ts *Tester) TxPairingRelayPayment(addr string, rs ...*pairingtypes.RelaySession) (*pairingtypes.MsgRelayPaymentResponse, error) {
func (ts *Tester) TxPairingRelayPayment(provider string, rs ...*pairingtypes.RelaySession) (*pairingtypes.MsgRelayPaymentResponse, error) {
msg := &pairingtypes.MsgRelayPayment{
Creator: addr,
Creator: provider,
Relays: rs,
DescriptionString: "test",
}
Expand Down Expand Up @@ -1129,8 +1139,8 @@ func (ts *Tester) SetupForTests(getToTopMostPath string, specId string, validato
// setup providers
start = len(ts.Accounts(PROVIDER))
for i := 0; i < providers; i++ {
_, addr := ts.AddAccount(PROVIDER, start+i, balance)
err := ts.StakeProviderExtra(addr, spec, spec.MinStakeProvider.Amount.Int64(), nil, 1, "prov"+strconv.Itoa(start+i))
acc, provider := ts.AddAccount(PROVIDER, start+i, balance)
err := ts.StakeProviderExtra(acc.GetVaultAddr(), provider, spec, spec.MinStakeProvider.Amount.Int64(), nil, 1, "prov"+strconv.Itoa(start+i))
if err != nil {
return err
}
Expand Down
9 changes: 9 additions & 0 deletions utils/sigs/sigs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Account struct {
PubKey cryptotypes.PubKey
Addr sdk.AccAddress
ConsKey cryptotypes.PrivKey
Vault *Account // provider vault account (only for provider)
}

type Signable interface {
Expand All @@ -46,6 +47,14 @@ type Signable interface {
HashRounds() int
}

func (acc Account) GetVaultAddr() string {
if acc.Vault != nil {
return acc.Vault.Addr.String()
}

return ""
}

// Sign creates a signature for a struct. The prepareFunc prepares the struct before extracting the data for the signature
func Sign(pkey *btcSecp256k1.PrivateKey, data Signable) ([]byte, error) {
msgData := data.DataToSign()
Expand Down
6 changes: 5 additions & 1 deletion x/conflict/keeper/conflict.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,14 @@ func (k Keeper) ValidateResponseConflict(ctx sdk.Context, conflictData *types.Re
if err != nil {
return nil, fmt.Errorf("AccAddressFromHex %s provider: %w", print_st, err)
}
_, found := k.epochstorageKeeper.GetStakeEntryForProviderEpoch(ctx, chainID, providerAddress.String(), epochStart)
stakeEntry, found := k.epochstorageKeeper.GetStakeEntryForProviderEpoch(ctx, chainID, providerAddress.String(), epochStart)
if !found {
return nil, fmt.Errorf("did not find a stake entry for %s provider %s on epoch %d, chainID %s", print_st, providerAddress, epochStart, chainID)
}

if stakeEntry.IsAddressVaultAndNotProvider(providerAddress.String()) {
return nil, fmt.Errorf("provider vault address should not be used in conflict. vault: %s, provider: %s, chainID: %s, epoch: %d", stakeEntry.Vault, stakeEntry.Address, chainID, epochStart)
}
return providerAddress, nil
}
providerAccAddress0, err := providerAddressFromRelayReplyAndVerifyStakeEntry(conflictData.ConflictRelayData0.Reply, true)
Expand Down
4 changes: 2 additions & 2 deletions x/conflict/keeper/msg_server_detection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func (ts *tester) setupForConflict(providersCount int) *tester {
ts.consumer = consumer

for i := 0; i < providersCount; i++ {
providerAcct, providerAddr := ts.AddAccount(common.PROVIDER, i, balance)
err := ts.StakeProvider(providerAddr, ts.spec, stake)
providerAcct, provider := ts.AddAccount(common.PROVIDER, i, balance)
err := ts.StakeProvider(providerAcct.GetVaultAddr(), provider, ts.spec, stake)
require.Nil(ts.T, err)
ts.providers = append(ts.providers, providerAcct)
}
Expand Down
40 changes: 38 additions & 2 deletions x/conflict/keeper/vote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ func TestNewVoterOldVote(t *testing.T) {

// add a staked provider
balance := int64(10000)
_, notVoterProvider := ts.AddAccount(common.PROVIDER, 10, balance)
err := ts.StakeProvider(notVoterProvider, ts.spec, balance/10)
notVoterAcc, notVoterProvider := ts.AddAccount(common.PROVIDER, 10, balance)
err := ts.StakeProvider(notVoterAcc.GetVaultAddr(), notVoterProvider, ts.spec, balance/10)
require.NoError(t, err)

ts.AdvanceEpoch()
Expand Down Expand Up @@ -497,3 +497,39 @@ func TestNoDecisionVote(t *testing.T) {
LastEvent := events[len(events)-1]
require.Equal(t, utils.EventPrefix+conflicttypes.ConflictVoteUnresolvedEventName, LastEvent.Type)
}

// TestVaultProviderConflictVote tests that conflicts are using the provider addresses
// Scenarios:
// 1. conflict are between provider addresses, voting can be done only by providers, punishment
// is done to provider address. Usage of vault addresses should fail the process
func TestVaultProviderConflictVote(t *testing.T) {
ts := newTester(t)
ts.setupForConflict(2)

tests := []struct {
name string
p1 sigs.Account
p2 sigs.Account
valid bool
}{
{"HappyFlow", ts.providers[0], ts.providers[1], true},
{"Provider0Vault", *ts.providers[0].Vault, ts.providers[1], false},
{"Provider1Vault", ts.providers[0], *ts.providers[1].Vault, false},
{"BothVault", *ts.providers[0].Vault, *ts.providers[1].Vault, false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg, _, _, err := common.CreateMsgDetectionTest(ts.GoCtx, ts.consumer, tt.p1, tt.p2, ts.spec)
require.NoError(t, err)

_, err = ts.txConflictDetection(msg)
if tt.valid {
require.NoError(t, err)
} else {
require.Error(t, err)
}
ts.AdvanceEpoch()
})
}
}
3 changes: 3 additions & 0 deletions x/dualstaking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ The Dualstaking module supports the following queries:
| `provider-delegators` | provider address | shows all the providers delegators |
| `delegator-rewards` | delegator address | shows all the claimable rewards of the delegator |

Note, use the provider's operator address for the `provider-delegators` query, and the provider's vault address for the `delegator-rewards` query.
For more information on the operator and vault addresses see the pairing module's [README.md](../pairing/README.md).

## Transactions

The Dualstaking module supports the following transactions:
Expand Down
28 changes: 10 additions & 18 deletions x/dualstaking/keeper/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/lavanet/lava/utils"
lavaslices "github.com/lavanet/lava/utils/lavaslices"
"github.com/lavanet/lava/x/dualstaking/types"
Expand Down Expand Up @@ -177,23 +176,15 @@ func (k Keeper) decreaseDelegation(ctx sdk.Context, delegator, provider, chainID
// modifyStakeEntryDelegation modifies the (epochstorage) stake-entry of the provider for a chain based on the action (increase or decrease).
func (k Keeper) modifyStakeEntryDelegation(ctx sdk.Context, delegator, provider, chainID string, amount sdk.Coin, increase bool) (err error) {
stakeEntry, exists := k.epochstorageKeeper.GetStakeEntryByAddressCurrent(ctx, chainID, provider)
if !exists {
if !exists || provider != stakeEntry.Address {
if increase {
return epochstoragetypes.ErrProviderNotStaked
}
// For decrease, if the provider doesn't exist, return without error
return nil
}

// Sanity check
if stakeEntry.Address != provider {
return utils.LavaFormatError("critical: delegate/un-delegate with provider address mismatch", sdkerrors.ErrInvalidAddress,
utils.Attribute{Key: "provider", Value: provider},
utils.Attribute{Key: "address", Value: stakeEntry.Address},
)
}

if delegator == provider {
if delegator == stakeEntry.Vault {
if increase {
stakeEntry.Stake = stakeEntry.Stake.Add(amount)
} else {
Expand All @@ -214,15 +205,16 @@ func (k Keeper) modifyStakeEntryDelegation(ctx sdk.Context, delegator, provider,
}

details := map[string]string{
"provider": stakeEntry.Address,
"chain_id": stakeEntry.Chain,
"moniker": stakeEntry.Moniker,
"stake": stakeEntry.Stake.String(),
"effective_stake": stakeEntry.EffectiveStake().String() + stakeEntry.Stake.Denom,
"provider_vault": stakeEntry.Vault,
"provider_provider": stakeEntry.Address,
"chain_id": stakeEntry.Chain,
"moniker": stakeEntry.Moniker,
"stake": stakeEntry.Stake.String(),
"effective_stake": stakeEntry.EffectiveStake().String() + stakeEntry.Stake.Denom,
}

if stakeEntry.Stake.IsLT(k.GetParams(ctx).MinSelfDelegation) {
err = k.epochstorageKeeper.RemoveStakeEntryCurrent(ctx, chainID, stakeEntry.Address)
err = k.epochstorageKeeper.RemoveStakeEntryCurrent(ctx, chainID, stakeEntry.Vault)
if err != nil {
return utils.LavaFormatError("can't remove stake Entry after decreasing provider self delegation", err,
utils.Attribute{Key: "provider", Value: stakeEntry.Address},
Expand All @@ -238,7 +230,7 @@ func (k Keeper) modifyStakeEntryDelegation(ctx sdk.Context, delegator, provider,
details["min_spec_stake"] = k.specKeeper.GetMinStake(ctx, chainID).String()
utils.LogLavaEvent(ctx, k.Logger(ctx), types.FreezeFromUnbond, details, "freezing provider due to stake below min spec stake")
stakeEntry.Freeze()
} else if delegator == provider && stakeEntry.IsFrozen() {
} else if delegator == stakeEntry.Vault && stakeEntry.IsFrozen() {
stakeEntry.UnFreeze(k.epochstorageKeeper.GetCurrentNextEpoch(ctx) + 1)
}

Expand Down
Loading

0 comments on commit 0401570

Please sign in to comment.