From c7bd05f0b8107880312e756a1647d6f32c97f742 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 10 Apr 2018 00:10:58 +0200 Subject: [PATCH] hard reset --- x/bank/keeper.go | 211 +++++++++++++++++++++++++++++----------- x/bank/keeper_test.go | 217 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+), 58 deletions(-) create mode 100644 x/bank/keeper_test.go diff --git a/x/bank/keeper.go b/x/bank/keeper.go index b52b480f6..4c20f962d 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -8,6 +8,85 @@ import ( const moduleName = "bank" +func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { + acc := am.GetAccount(ctx, addr) + if acc == nil { + return sdk.Coins{} + } + return acc.GetCoins() +} + +func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { + acc := am.GetAccount(ctx, addr) + if acc == nil { + acc = am.NewAccountWithAddress(ctx, addr) + } + acc.SetCoins(amt) + am.SetAccount(ctx, acc) + return nil +} + +// HasCoins returns whether or not an account has at least amt coins. +func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool { + return getCoins(ctx, am, addr).IsGTE(amt) +} + +// SubtractCoins subtracts amt from the coins at the addr. +func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { + oldCoins := getCoins(ctx, am, addr) + newCoins := oldCoins.Minus(amt) + if !newCoins.IsNotNegative() { + return amt, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) + } + err := setCoins(ctx, am, addr, newCoins) + return newCoins, err +} + +// AddCoins adds amt to the coins at the addr. +func addCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { + oldCoins := getCoins(ctx, am, addr) + newCoins := oldCoins.Plus(amt) + if !newCoins.IsNotNegative() { + return amt, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) + } + err := setCoins(ctx, am, addr, newCoins) + return newCoins, err +} + +// SendCoins moves coins from one account to another +func sendCoins(ctx sdk.Context, am sdk.AccountMapper, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { + _, err := subtractCoins(ctx, am, fromAddr, amt) + if err != nil { + return err + } + + _, err = addCoins(ctx, am, toAddr, amt) + if err != nil { + return err + } + + return nil +} + +// InputOutputCoins handles a list of inputs and outputs +func inputOutputCoins(ctx sdk.Context, am sdk.AccountMapper, inputs []Input, outputs []Output) sdk.Error { + for _, in := range inputs { + _, err := subtractCoins(ctx, am, in.Address, in.Coins) + if err != nil { + return err + } + } + + for _, out := range outputs { + _, err := addCoins(ctx, am, out.Address, out.Coins) + if err != nil { + return err + } + } + + return nil +} + // CoinKeeper manages transfers between accounts type CoinKeeper struct { am sdk.AccountMapper @@ -19,74 +98,90 @@ func NewCoinKeeper(am sdk.AccountMapper) CoinKeeper { } // GetCoins returns the coins at the addr. -func (ck CoinKeeper) GetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) sdk.Coins { - acc := ck.am.GetAccount(ctx, addr) - return acc.GetCoins() +func (keeper CoinKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { + return getCoins(ctx, keeper.am, addr) +} + +// SetCoins sets the coins at the addr. +func (keeper CoinKeeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) sdk.Error { + return setCoins(ctx, keeper.am, addr, amt) +} + +// HasCoins returns whether or not an account has at least amt coins. +func (keeper CoinKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { + return hasCoins(ctx, keeper.am, addr, amt) } // SubtractCoins subtracts amt from the coins at the addr. -func (ck CoinKeeper) SubtractCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { - acc := ck.am.GetAccount(ctx, addr) - if acc == nil { - return amt, sdk.ErrUnknownAddress(addr.String()) - } - - coins := acc.GetCoins() - newCoins := coins.Minus(amt) - if !newCoins.IsNotNegative() { - return amt, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", coins, amt)) - } - - acc.SetCoins(newCoins) - ck.am.SetAccount(ctx, acc) - return newCoins, nil +func (keeper CoinKeeper) SubtractCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { + return subtractCoins(ctx, keeper.am, addr, amt) } // AddCoins adds amt to the coins at the addr. -func (ck CoinKeeper) AddCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { - acc := ck.am.GetAccount(ctx, addr) - if acc == nil { - acc = ck.am.NewAccountWithAddress(ctx, addr) - } - - coins := acc.GetCoins() - newCoins := coins.Plus(amt) - - acc.SetCoins(newCoins) - ck.am.SetAccount(ctx, acc) - return newCoins, nil +func (keeper CoinKeeper) AddCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { + return addCoins(ctx, keeper.am, addr, amt) } // SendCoins moves coins from one account to another -func (ck CoinKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { - _, err := ck.SubtractCoins(ctx, fromAddr, amt) - if err != nil { - return err - } - - _, err = ck.AddCoins(ctx, toAddr, amt) - if err != nil { - return err - } - - return nil +func (keeper CoinKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { + return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) } // InputOutputCoins handles a list of inputs and outputs -func (ck CoinKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) sdk.Error { - for _, in := range inputs { - _, err := ck.SubtractCoins(ctx, in.Address, in.Coins) - if err != nil { - return err - } - } - - for _, out := range outputs { - _, err := ck.AddCoins(ctx, out.Address, out.Coins) - if err != nil { - return err - } - } - - return nil +func (keeper CoinKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) sdk.Error { + return inputOutputCoins(ctx, keeper.am, inputs, outputs) +} + +// -------------------------------------------------- + +// SendKeeper only allows transfers between accounts, without the possibility of creating coins +type SendKeeper struct { + am sdk.AccountMapper +} + +// NewSendKeeper returns a new CoinKeeper +func NewSendKeeper(am sdk.AccountMapper) SendKeeper { + return SendKeeper{am: am} +} + +// GetCoins returns the coins at the addr. +func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { + return getCoins(ctx, keeper.am, addr) +} + +// HasCoins returns whether or not an account has at least amt coins. +func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { + return hasCoins(ctx, keeper.am, addr, amt) +} + +// SendCoins moves coins from one account to another +func (keeper SendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { + return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) +} + +// InputOutputCoins handles a list of inputs and outputs +func (keeper SendKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) sdk.Error { + return inputOutputCoins(ctx, keeper.am, inputs, outputs) +} + +// -------------------------------------------------- + +// ViewKeeper only allows reading of balances +type ViewKeeper struct { + am sdk.AccountMapper +} + +// NewViewKeeper returns a new CoinKeeper +func NewViewKeeper(am sdk.AccountMapper) ViewKeeper { + return ViewKeeper{am: am} +} + +// GetCoins returns the coins at the addr. +func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { + return getCoins(ctx, keeper.am, addr) +} + +// HasCoins returns whether or not an account has at least amt coins. +func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { + return hasCoins(ctx, keeper.am, addr, amt) } diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go new file mode 100644 index 000000000..0f5ce128f --- /dev/null +++ b/x/bank/keeper_test.go @@ -0,0 +1,217 @@ +package bank + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + abci "github.com/tendermint/abci/types" + dbm "github.com/tendermint/tmlibs/db" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + oldwire "github.com/tendermint/go-wire" + + "github.com/cosmos/cosmos-sdk/x/auth" +) + +func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { + db := dbm.NewMemDB() + authKey := sdk.NewKVStoreKey("authkey") + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db) + ms.LoadLatestVersion() + return ms, authKey +} + +func TestCoinKeeper(t *testing.T) { + ms, authKey := setupMultiStore() + + // wire registration while we're at it ... TODO + var _ = oldwire.RegisterInterface( + struct{ sdk.Account }{}, + oldwire.ConcreteType{&auth.BaseAccount{}, 0x1}, + ) + + ctx := sdk.NewContext(ms, abci.Header{}, false, nil) + accountMapper := auth.NewAccountMapper(authKey, &auth.BaseAccount{}) + coinKeeper := NewCoinKeeper(accountMapper) + + addr := sdk.Address([]byte("addr1")) + addr2 := sdk.Address([]byte("addr2")) + addr3 := sdk.Address([]byte("addr3")) + acc := accountMapper.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + accountMapper.SetAccount(ctx, acc) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + + // Test HasCoins + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + + // Test AddCoins + coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}})) + + coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) + + // Test SubtractCoins + coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + + _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) + assert.Implements(t, (*sdk.Error)(nil), err) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + + coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}})) + + // Test SendCoins + coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + + err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) + assert.Implements(t, (*sdk.Error)(nil), err2) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + + coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) + coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + + // Test InputOutputCoins + input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) + output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) + coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + + inputs := []Input{ + NewInput(addr, sdk.Coins{{"foocoin", 3}}), + NewInput(addr2, sdk.Coins{{"barcoin", 3}, {"foocoin", 2}}), + } + + outputs := []Output{ + NewOutput(addr, sdk.Coins{{"barcoin", 1}}), + NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), + } + coinKeeper.InputOutputCoins(ctx, inputs, outputs) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + +} + +func TestSendKeeper(t *testing.T) { + ms, authKey := setupMultiStore() + + // wire registration while we're at it ... TODO + var _ = oldwire.RegisterInterface( + struct{ sdk.Account }{}, + oldwire.ConcreteType{&auth.BaseAccount{}, 0x1}, + ) + + ctx := sdk.NewContext(ms, abci.Header{}, false, nil) + accountMapper := auth.NewAccountMapper(authKey, &auth.BaseAccount{}) + coinKeeper := NewCoinKeeper(accountMapper) + sendKeeper := NewSendKeeper(accountMapper) + + addr := sdk.Address([]byte("addr1")) + addr2 := sdk.Address([]byte("addr2")) + addr3 := sdk.Address([]byte("addr3")) + acc := accountMapper.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + accountMapper.SetAccount(ctx, acc) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + + // Test HasCoins + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + + // Test SendCoins + sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + + err2 := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) + assert.Implements(t, (*sdk.Error)(nil), err2) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + + coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) + sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + + // Test InputOutputCoins + input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) + output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) + sendKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + + inputs := []Input{ + NewInput(addr, sdk.Coins{{"foocoin", 3}}), + NewInput(addr2, sdk.Coins{{"barcoin", 3}, {"foocoin", 2}}), + } + + outputs := []Output{ + NewOutput(addr, sdk.Coins{{"barcoin", 1}}), + NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), + } + sendKeeper.InputOutputCoins(ctx, inputs, outputs) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + +} + +func TestViewKeeper(t *testing.T) { + ms, authKey := setupMultiStore() + + // wire registration while we're at it ... TODO + var _ = oldwire.RegisterInterface( + struct{ sdk.Account }{}, + oldwire.ConcreteType{&auth.BaseAccount{}, 0x1}, + ) + + ctx := sdk.NewContext(ms, abci.Header{}, false, nil) + accountMapper := auth.NewAccountMapper(authKey, &auth.BaseAccount{}) + coinKeeper := NewCoinKeeper(accountMapper) + viewKeeper := NewViewKeeper(accountMapper) + + addr := sdk.Address([]byte("addr1")) + acc := accountMapper.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + accountMapper.SetAccount(ctx, acc) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + + // Test HasCoins + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) +}