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

feat(x/ecocredit): Put basket server method #758

Merged
merged 28 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f2ee929
feat: put server method
technicallyty Feb 10, 2022
df7beb5
feat: test
technicallyty Feb 10, 2022
411bc14
chore: move balance keys/data prefixes
technicallyty Feb 11, 2022
e2e7712
wip
technicallyty Feb 11, 2022
68b9ca8
fix: decimal math
technicallyty Feb 11, 2022
97cc155
chore: remove dead code
technicallyty Feb 11, 2022
7ed4262
chore: comment cleanups
technicallyty Feb 11, 2022
563a27c
chore: address review
technicallyty Feb 11, 2022
201a00f
Merge branch 'release/v2.2.x' into ty/732-msg_server_put
aaronc Feb 11, 2022
0b6536a
Update x/ecocredit/server/basket/put.go
technicallyty Feb 11, 2022
52d37ff
chore: review comments
technicallyty Feb 11, 2022
6498166
Merge branch 'ty/732-msg_server_put' of https://github.com/regen-netw…
technicallyty Feb 11, 2022
5b28eb2
feat: assert basket has balance in test
technicallyty Feb 11, 2022
a2c57a6
chore: add balance checks to test
technicallyty Feb 11, 2022
43570f9
Update types/math/dec.go
technicallyty Feb 11, 2022
67be2de
chore: add addr check
technicallyty Feb 11, 2022
670b740
fix: invariant checks for supplies
technicallyty Feb 11, 2022
98f752f
Merge branch 'ty/732-msg_server_put' of https://github.com/regen-netw…
technicallyty Feb 11, 2022
f45f638
chore: add event emission
technicallyty Feb 11, 2022
69c3cb9
Update types/math/dec.go
technicallyty Feb 11, 2022
c210657
Merge branch 'release/v2.2.x' into ty/732-msg_server_put
technicallyty Feb 11, 2022
ed06ec2
fix: fix tests
technicallyty Feb 11, 2022
d327c24
Merge branch 'release/v2.2.x' into ty/732-msg_server_put
technicallyty Feb 11, 2022
0db3736
chore: godocs, fix tests, credit type
technicallyty Feb 11, 2022
2572114
chore: class id
technicallyty Feb 11, 2022
221d9b5
chore: comment, sub module coins
technicallyty Feb 11, 2022
ad2665e
chore: error shortening
technicallyty Feb 11, 2022
61fa847
chore: BasketModuleName -> BasketSubModuleName
technicallyty Feb 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions types/math/dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ func NewDecFromInt64(x int64) Dec {
return res
}

// NewDecFinite returns a decimal with a value of (10 * coeff)^exp.
technicallyty marked this conversation as resolved.
Show resolved Hide resolved
func NewDecFinite(coeff int64, exp int32) Dec {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
var res Dec
res.dec.SetFinite(coeff, exp)
return res
}

// Add returns a new Dec with value `x+y` without mutating any argument and error if
// there is an overflow.
func (x Dec) Add(y Dec) (Dec, error) {
Expand All @@ -114,6 +121,19 @@ func (x Dec) Quo(y Dec) (Dec, error) {
return z, errors.Wrap(err, "decimal quotient error")
}

// MulExact returns a new dec with value x * y. The product must not round or an error will be returned.
func (x Dec) MulExact(y Dec) (Dec, error) {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
var z Dec
condition, err := dec128Context.Mul(&z.dec, &x.dec, &y.dec)
if err != nil {
return z, err
}
if condition.Rounded() {
return z, errors.Wrap(err, "exact decimal product error")
}
return z, nil
}

// QuoInteger returns a new integral Dec with value `x/y` (formatted as decimal128, with 34 digit precision)
// without mutating any argument and error if there is an overflow.
func (x Dec) QuoInteger(y Dec) (Dec, error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package server
package ecocredit

import (
"testing"
Expand All @@ -11,7 +11,7 @@ import (
var addr = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())

func TestKeys(t *testing.T) {
batchDenom := batchDenomT("testing-denom")
batchDenom := BatchDenomT("testing-denom")

// tradable-balance-key
key := TradableBalanceKey(addr, batchDenom)
Expand Down
151 changes: 151 additions & 0 deletions x/ecocredit/keys.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,159 @@
package ecocredit

import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/regen-network/regen-ledger/types/math"
)

const (
// ModuleName is the module name constant used in many places
ModuleName = "ecocredit"

DefaultParamspace = ModuleName

TradableBalancePrefix byte = 0x0
TradableSupplyPrefix byte = 0x1
RetiredBalancePrefix byte = 0x2
RetiredSupplyPrefix byte = 0x3
CreditTypeSeqTablePrefix byte = 0x4
ClassInfoTablePrefix byte = 0x5
BatchInfoTablePrefix byte = 0x6
ORMPrefix byte = 0x7
)

// BatchDenomT is used to prevent errors when forming keys as accounts and denoms are
// both represented as strings
type BatchDenomT string

// - 0x0 <accAddrLen (1 Byte)><accAddr_Bytes><denom_Bytes>: TradableBalance
// - 0x1 <denom_Bytes>: TradableSupply
// - 0x2 <accAddrLen (1 Byte)><accAddr_Bytes><denom_Bytes>: RetiredBalance
// - 0x3 <denom_Bytes>: RetiredSupply

// TradableBalanceKey creates the index key for recipient address and batch-denom
func TradableBalanceKey(acc sdk.AccAddress, denom BatchDenomT) []byte {
key := []byte{TradableBalancePrefix}
key = append(key, address.MustLengthPrefix(acc)...)
return append(key, denom...)
}

// ParseBalanceKey parses the recipient address and batch-denom from tradable or retired balance key.
func ParseBalanceKey(key []byte) (sdk.AccAddress, BatchDenomT) {
addrLen := key[1]
Copy link
Collaborator

Choose a reason for hiding this comment

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

please add a comment that key[0] is a storage prefix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added comment explaining format

addr := sdk.AccAddress(key[2 : 2+addrLen])
return addr, BatchDenomT(key[2+addrLen:])
}

// TradableSupplyKey creates the tradable supply key for a given batch-denom
func TradableSupplyKey(batchDenom BatchDenomT) []byte {
key := []byte{TradableSupplyPrefix}
return append(key, batchDenom...)
}

// ParseSupplyKey parses the batch-denom from tradable or retired supply key
func ParseSupplyKey(key []byte) BatchDenomT {
return BatchDenomT(key[1:])
}

// RetiredBalanceKey creates the index key for recipient address and batch-denom
func RetiredBalanceKey(acc sdk.AccAddress, batchDenom BatchDenomT) []byte {
key := []byte{RetiredBalancePrefix}
key = append(key, address.MustLengthPrefix(acc)...)
return append(key, batchDenom...)
}

// RetiredSupplyKey creates the retired supply key for a given batch-denom
func RetiredSupplyKey(batchDenom BatchDenomT) []byte {
key := []byte{RetiredSupplyPrefix}
return append(key, batchDenom...)
}

func GetDecimal(store sdk.KVStore, key []byte) (math.Dec, error) {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
bz := store.Get(key)
if bz == nil {
return math.NewDecFromInt64(0), nil
}

value, err := math.NewDecFromString(string(bz))
if err != nil {
return math.Dec{}, sdkerrors.Wrap(err, fmt.Sprintf("can't unmarshal %s as decimal", bz))
}

return value, nil
}

func SetDecimal(store sdk.KVStore, key []byte, value math.Dec) {
// always remove all trailing zeros for canonical representation
value, _ = value.Reduce()

if value.IsZero() {
store.Delete(key)
} else {
// use floating notation here always for canonical representation
store.Set(key, []byte(value.String()))
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Get Set, Add ... Decimal functions are general - how about moving it outside of ecocrerdits? At least, let's move them to different package.

Copy link
Member

@aaronc aaronc Feb 11, 2022

Choose a reason for hiding this comment

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

this is mostly code that was already there and will be going way soon in #723 so I don't think we to spend that much effort on cleanup. agreed a different package makes sense generally...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

perhaps we can just do that when/if the times comes when another package needs it? this move was just a fix for import cycles

Copy link
Member

Choose a reason for hiding this comment

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

yeah i'd say keep as is because it's all getting deleted right after the 2.2 release


func AddAndSetDecimal(store sdk.KVStore, key []byte, x math.Dec) error {
value, err := GetDecimal(store, key)
if err != nil {
return err
}

value, err = value.Add(x)
if err != nil {
return err
}

SetDecimal(store, key, value)
return nil
}

func SubAndSetDecimal(store sdk.KVStore, key []byte, x math.Dec) error {
value, err := GetDecimal(store, key)
if err != nil {
return err
}

if value.Cmp(x) == -1 {
return ErrInsufficientFunds
}

value, err = math.SafeSubBalance(value, x)
if err != nil {
return err
}

SetDecimal(store, key, value)
return nil
}

func IterateSupplies(store sdk.KVStore, storeKey byte, cb func(denom, supply string) (bool, error)) error {
iter := sdk.KVStorePrefixIterator(store, []byte{storeKey})
defer iter.Close()
for ; iter.Valid(); iter.Next() {
stop, err := cb(string(ParseSupplyKey(iter.Key())), string(iter.Value()))
if err != nil {
return err
}
if stop {
break
}
}

return nil
}

func IterateBalances(store sdk.KVStore, storeKey byte, cb func(address, denom, balance string) bool) {
iter := sdk.KVStorePrefixIterator(store, []byte{storeKey})
defer iter.Close()
for ; iter.Valid(); iter.Next() {
addr, denom := ParseBalanceKey(iter.Key())
if cb(addr.String(), string(denom), string(iter.Value())) {
break
}
}
}
5 changes: 3 additions & 2 deletions x/ecocredit/server/basket/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ type Keeper struct {
stateStore basketv1.StateStore
bankKeeper BankKeeper
ecocreditKeeper EcocreditKeeper
storeKey sdk.StoreKey
}

var _ baskettypes.MsgServer = Keeper{}
var _ baskettypes.QueryServer = Keeper{}

// NewKeeper returns a new keeper instance.
func NewKeeper(db ormdb.ModuleDB, ecocreditKeeper EcocreditKeeper, bankKeeper BankKeeper) Keeper {
func NewKeeper(db ormdb.ModuleDB, ecocreditKeeper EcocreditKeeper, bankKeeper BankKeeper, storeKey sdk.StoreKey) Keeper {
basketStore, err := basketv1.NewStateStore(db)
if err != nil {
panic(err)
}
return Keeper{bankKeeper: bankKeeper, ecocreditKeeper: ecocreditKeeper, stateStore: basketStore}
return Keeper{bankKeeper: bankKeeper, ecocreditKeeper: ecocreditKeeper, stateStore: basketStore, storeKey: storeKey}
}

// EcocreditKeeper abstracts over methods that the main eco-credit keeper
Expand Down
4 changes: 3 additions & 1 deletion x/ecocredit/server/basket/keeper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package basket_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"testing"

"github.com/cosmos/cosmos-sdk/orm/model/ormdb"
Expand All @@ -19,6 +20,7 @@ func TestKeeperExample(t *testing.T) {

bankKeeper := mocks.NewMockBankKeeper(ctrl)
ecocreditKeeper := mocks.NewMockEcocreditKeeper(ctrl)
k := basket.NewKeeper(db, ecocreditKeeper, bankKeeper)
sk := sdk.NewKVStoreKey("test")
k := basket.NewKeeper(db, ecocreditKeeper, bankKeeper, sk)
require.NotNil(t, k)
}
Loading