Skip to content

Commit

Permalink
feat: take rate, multi-currency (#61)
Browse files Browse the repository at this point in the history
* Allow leases to be priced in arbitrary denomination.
* Add gov-configurable default & denomination-specific "take rate".
* Add gov-configurable denomination-specific deposit amounts.
  • Loading branch information
boz authored Jun 9, 2023
1 parent d879c24 commit 9b4993c
Show file tree
Hide file tree
Showing 23 changed files with 1,266 additions and 89 deletions.
10 changes: 1 addition & 9 deletions go/node/deployment/v1beta1/deployment_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
types "github.com/akash-network/akash-api/go/node/deployment/v1beta1"
akashtypes "github.com/akash-network/akash-api/go/node/types/v1beta1"
tutil "github.com/akash-network/akash-api/go/testutil"
"github.com/akash-network/akash-api/go/testutil/v1beta1"
testutil "github.com/akash-network/akash-api/go/testutil/v1beta1"
)

func TestZeroValueGroupSpec(t *testing.T) {
Expand Down Expand Up @@ -183,11 +183,3 @@ func TestGroupWithNegativePrice(t *testing.T) {
require.Error(t, err)
require.Regexp(t, "^.*invalid price object.*$", err)
}

func TestGroupWithInvalidDenom(t *testing.T) {
group := validSimpleGroupSpec()
group.Resources[0].Price.Denom = "goldenTicket"
err := group.ValidateBasic()
require.Error(t, err)
require.Regexp(t, "^.*denomination must be.*$", err)
}
12 changes: 8 additions & 4 deletions go/node/deployment/v1beta1/group_pricing_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,27 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"

"github.com/akash-network/akash-api/go/node/types/constants"
)

func validateGroupPricing(gspec GroupSpec) error {
var price sdk.Coin

mem := sdk.NewInt(0)
denom := ""

for idx, resource := range gspec.Resources {

if err := validateUnitPricing(resource); err != nil {
return fmt.Errorf("group %v: %w", gspec.GetName(), err)
}

if resource.FullPrice().Denom != constants.AkashDenom {
return fmt.Errorf("%w: denomination must be %q", ErrInvalidDeployment, constants.AkashDenom)
// all must be same denomination
if denom == "" {
denom = resource.FullPrice().Denom
} else {
if resource.FullPrice().Denom != denom {
return fmt.Errorf("%w: denomination must be %q", ErrInvalidDeployment, denom)
}
}

if idx == 0 {
Expand Down
12 changes: 8 additions & 4 deletions go/node/deployment/v1beta2/group_pricing_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@ import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/akash-network/akash-api/go/node/types/constants"
)

func validateGroupPricing(gspec GroupSpec) error {
var price sdk.DecCoin

mem := sdk.NewInt(0)
denom := ""

for idx, resource := range gspec.Resources {

if err := validateUnitPricing(resource); err != nil {
return fmt.Errorf("group %v: %w", gspec.GetName(), err)
}

if resource.FullPrice().Denom != constants.AkashDenom {
return fmt.Errorf("%w: denomination must be %q", ErrInvalidDeployment, constants.AkashDenom)
// all must be same denomination
if denom == "" {
denom = resource.FullPrice().Denom
} else {
if resource.FullPrice().Denom != denom {
return fmt.Errorf("%w: denomination must be %q", ErrInvalidDeployment, denom)
}
}

if idx == 0 {
Expand Down
10 changes: 1 addition & 9 deletions go/node/deployment/v1beta3/deployment_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
types "github.com/akash-network/akash-api/go/node/deployment/v1beta3"
akashtypes "github.com/akash-network/akash-api/go/node/types/v1beta3"
tutil "github.com/akash-network/akash-api/go/testutil"
"github.com/akash-network/akash-api/go/testutil/v1beta3"
testutil "github.com/akash-network/akash-api/go/testutil/v1beta3"
)

func TestZeroValueGroupSpec(t *testing.T) {
Expand Down Expand Up @@ -187,11 +187,3 @@ func TestGroupWithNegativePrice(t *testing.T) {
require.Error(t, err)
require.Regexp(t, "^.*invalid price object.*$", err)
}

func TestGroupWithInvalidDenom(t *testing.T) {
group := validSimpleGroupSpec()
group.Resources[0].Price.Denom = "goldenTicket"
err := group.ValidateBasic()
require.Error(t, err)
require.Regexp(t, "^.*denomination must be.*$", err)
}
7 changes: 1 addition & 6 deletions go/node/deployment/v1beta3/group_pricing_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/akash-network/akash-api/go/node/types/constants"
)

func validateGroupPricing(gspec GroupSpec) error {
Expand All @@ -19,10 +17,7 @@ func validateGroupPricing(gspec GroupSpec) error {
return fmt.Errorf("group %v: %w", gspec.GetName(), err)
}

if resource.FullPrice().Denom != constants.AkashDenom {
return fmt.Errorf("%w: denomination must be %q", ErrInvalidDeployment, constants.AkashDenom)
}

// all must be same denomination
if idx == 0 {
price = resource.FullPrice()
} else {
Expand Down
11 changes: 10 additions & 1 deletion go/node/deployment/v1beta3/group_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ func ValidateDeploymentGroups(gspecs []GroupSpec) error {
}

names := make(map[string]int, len(gspecs)) // Used as set
for _, group := range gspecs {
denom := ""
for idx, group := range gspecs {

// all must be same denomination
if idx == 0 {
denom = group.Price().Denom
} else if group.Price().Denom != denom {
return fmt.Errorf("inconsistent denomination: %v != %v", denom, group.Price().Denom)
}

if err := group.ValidateBasic(); err != nil {
return err
}
Expand Down
10 changes: 10 additions & 0 deletions go/node/deployment/v1beta3/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ func (msg MsgCreateDeployment) ValidateBasic() error {
if err := msg.ID.Validate(); err != nil {
return err
}
if err := msg.Deposit.Validate(); err != nil {
return err
}
if len(msg.Groups) == 0 {
return ErrInvalidGroups
}
Expand All @@ -75,6 +78,13 @@ func (msg MsgCreateDeployment) ValidateBasic() error {
if err != nil {
return err
}

// deposit must be same denom as price
if !msg.Deposit.IsZero() {
if gdenom := gs.Price().Denom; gdenom != msg.Deposit.Denom {
return sdkerrors.Wrapf(ErrInvalidDeposit, "Mismatched denominations (%v != %v)", msg.Deposit.Denom, gdenom)
}
}
}

_, err := sdk.AccAddressFromBech32(msg.Depositor)
Expand Down
6 changes: 5 additions & 1 deletion go/node/deployment/v1beta3/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"github.com/stretchr/testify/require"

types "github.com/akash-network/akash-api/go/node/deployment/v1beta3"
"github.com/akash-network/akash-api/go/testutil/v1beta3"
sdktestutil "github.com/akash-network/akash-api/go/testutil"
testutil "github.com/akash-network/akash-api/go/testutil/v1beta3"
)

type testMsg struct {
Expand All @@ -25,6 +26,7 @@ func TestVersionValidation(t *testing.T) {
testutil.GroupSpec(t),
},
Depositor: testutil.AccAddress(t).String(),
Deposit: sdktestutil.AkashCoin(t, 0),
},
err: nil,
},
Expand All @@ -36,6 +38,7 @@ func TestVersionValidation(t *testing.T) {
testutil.GroupSpec(t),
},
Depositor: testutil.AccAddress(t).String(),
Deposit: sdktestutil.AkashCoin(t, 0),
},
err: types.ErrEmptyVersion,
},
Expand All @@ -47,6 +50,7 @@ func TestVersionValidation(t *testing.T) {
testutil.GroupSpec(t),
},
Depositor: testutil.AccAddress(t).String(),
Deposit: sdktestutil.AkashCoin(t, 0),
},
err: types.ErrInvalidVersion,
},
Expand Down
55 changes: 43 additions & 12 deletions go/node/deployment/v1beta3/params.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package v1beta3

import (
math "math"

sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/pkg/errors"
)

var _ paramtypes.ParamSet = (*Params)(nil)

var (
DefaultDeploymentMinDeposit = sdk.NewCoin("uakt", sdk.NewInt(5000000))
)

const (
keyDeploymentMinDeposit = "DeploymentMinDeposit"
keyMinDeposits = "MinDeposits"
)

func ParamKeyTable() paramtypes.KeyTable {
Expand All @@ -22,28 +20,61 @@ func ParamKeyTable() paramtypes.KeyTable {

func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
paramtypes.NewParamSetPair([]byte(keyDeploymentMinDeposit), &p.DeploymentMinDeposit, validateCoin),
paramtypes.NewParamSetPair([]byte(keyMinDeposits), &p.MinDeposits, validateMinDeposits),
}
}

func DefaultParams() Params {
return Params{
DeploymentMinDeposit: DefaultDeploymentMinDeposit,
MinDeposits: map[string]uint32{
"uakt": 5000000,
},
}
}

func (p Params) Validate() error {
if err := validateCoin(p.DeploymentMinDeposit); err != nil {
if err := validateMinDeposits(p.MinDeposits); err != nil {
return err
}

return nil
}

func validateCoin(i interface{}) error {
_, ok := i.(sdk.Coin)
func (p Params) ValidateDeposit(amt sdk.Coin) error {
min, err := p.MinDepositFor(amt.Denom)

if err != nil {
return err
}

if amt.IsGTE(min) {
return nil
}

return errors.Wrapf(ErrInvalidDeposit, "Deposit too low - %v < %v", amt.Amount, min)
}

func (p Params) MinDepositFor(denom string) (sdk.Coin, error) {
val, ok := p.MinDeposits[denom]
if !ok {
return sdk.NewInt64Coin(denom, math.MaxInt64), errors.Wrapf(ErrInvalidDeposit, "Invalid deposit denomination %v", denom)
}
return sdk.NewCoin(denom, sdk.NewInt(int64(val))), nil
}

func validateMinDeposits(i interface{}) error {
vals, ok := i.(map[string]uint32)
if !ok {
return errors.Wrapf(ErrInvalidParam, "%T", i)
return errors.Wrapf(ErrInvalidParam, "Min Deposits - invalid type: %T", i)
}

if _, ok := vals["uakt"]; !ok {
return errors.Wrapf(ErrInvalidParam, "Min Deposits - uakt not given: %#v", vals)
}

for denom, val := range vals {
if val >= math.MaxInt32 {
return errors.Wrapf(ErrInvalidParam, "Min Deposit (%v) - too large: %v", denom, val)
}
}

return nil
Expand Down
Loading

0 comments on commit 9b4993c

Please sign in to comment.