382 lines
9.8 KiB
Go
382 lines
9.8 KiB
Go
package coin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
crypto "github.com/tendermint/go-crypto"
|
|
"github.com/tendermint/tmlibs/log"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk"
|
|
"github.com/cosmos/cosmos-sdk/errors"
|
|
"github.com/cosmos/cosmos-sdk/modules/auth"
|
|
"github.com/cosmos/cosmos-sdk/stack"
|
|
"github.com/cosmos/cosmos-sdk/state"
|
|
)
|
|
|
|
// this makes sure that txs are rejected with invalid data or permissions
|
|
func TestHandlerValidation(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
// these are all valid, except for minusCoins
|
|
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
|
|
addr2 := sdk.Actor{App: "role", Address: []byte{7, 8}}
|
|
someCoins := Coins{{"atom", 123}}
|
|
doubleCoins := Coins{{"atom", 246}}
|
|
minusCoins := Coins{{"eth", -34}}
|
|
|
|
cases := []struct {
|
|
valid bool
|
|
tx sdk.Tx
|
|
perms []sdk.Actor
|
|
}{
|
|
// auth works with different apps
|
|
{true,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
|
[]sdk.Actor{addr1}},
|
|
{true,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr2, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr1, someCoins)}),
|
|
[]sdk.Actor{addr1, addr2}},
|
|
// check multi-input with both sigs
|
|
{true,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins), NewTxInput(addr2, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr1, doubleCoins)}),
|
|
[]sdk.Actor{addr1, addr2}},
|
|
// wrong permissions fail
|
|
{false,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
|
[]sdk.Actor{}},
|
|
{false,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
|
[]sdk.Actor{addr2}},
|
|
{false,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins), NewTxInput(addr2, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr1, doubleCoins)}),
|
|
[]sdk.Actor{addr1}},
|
|
// invalid input fails
|
|
{false,
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, minusCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, minusCoins)}),
|
|
[]sdk.Actor{addr2}},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
|
|
err := checkTx(ctx, tc.tx.Unwrap().(SendTx))
|
|
if tc.valid {
|
|
assert.Nil(err, "%d: %+v", i, err)
|
|
} else {
|
|
assert.NotNil(err, "%d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCheckDeliverSendTx(t *testing.T) {
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
// some sample settings
|
|
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
|
|
addr2 := sdk.Actor{App: "role", Address: []byte{7, 8}}
|
|
addr3 := sdk.Actor{App: "coin", Address: []byte{6, 5, 4, 3}}
|
|
|
|
someCoins := Coins{{"atom", 123}}
|
|
moreCoins := Coins{{"atom", 6487}}
|
|
diffCoins := moreCoins.Minus(someCoins)
|
|
otherCoins := Coins{{"eth", 11}}
|
|
mixedCoins := someCoins.Plus(otherCoins)
|
|
|
|
type money struct {
|
|
addr sdk.Actor
|
|
coins Coins
|
|
}
|
|
|
|
cases := []struct {
|
|
init []money
|
|
tx sdk.Tx
|
|
perms []sdk.Actor
|
|
final []money // nil for error
|
|
cost uint64 // gas allocated (if not error)
|
|
}{
|
|
{
|
|
[]money{{addr1, moreCoins}},
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
|
[]sdk.Actor{addr1},
|
|
[]money{{addr1, diffCoins}, {addr2, someCoins}},
|
|
20,
|
|
},
|
|
// simple multi-sig 2 accounts to 1
|
|
{
|
|
[]money{{addr1, mixedCoins}, {addr2, moreCoins}},
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, otherCoins), NewTxInput(addr2, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr3, mixedCoins)}),
|
|
[]sdk.Actor{addr1, addr2},
|
|
[]money{{addr1, someCoins}, {addr2, diffCoins}, {addr3, mixedCoins}},
|
|
30,
|
|
},
|
|
// multi-sig with one account sending many times
|
|
{
|
|
[]money{{addr1, moreCoins.Plus(otherCoins)}},
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr1, otherCoins), NewTxInput(addr1, someCoins)},
|
|
[]TxOutput{NewTxOutput(addr2, mixedCoins)}),
|
|
[]sdk.Actor{addr1},
|
|
[]money{{addr1, diffCoins}, {addr2, mixedCoins}},
|
|
30,
|
|
},
|
|
// invalid send (not enough money )
|
|
{
|
|
[]money{{addr1, moreCoins}, {addr2, someCoins}},
|
|
NewSendTx(
|
|
[]TxInput{NewTxInput(addr2, moreCoins)},
|
|
[]TxOutput{NewTxOutput(addr1, moreCoins)}),
|
|
[]sdk.Actor{addr1, addr2},
|
|
nil,
|
|
0,
|
|
},
|
|
}
|
|
|
|
h := NewHandler()
|
|
for i, tc := range cases {
|
|
// setup the cases....
|
|
store := state.NewMemKVStore()
|
|
for _, m := range tc.init {
|
|
acct := Account{Coins: m.coins}
|
|
err := storeAccount(store, m.addr.Bytes(), acct)
|
|
require.Nil(err, "%d: %+v", i, err)
|
|
}
|
|
|
|
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
|
|
|
|
// throw-away state for checktx
|
|
cache := store.Checkpoint()
|
|
cres, err := h.CheckTx(ctx, cache, tc.tx, nil)
|
|
// real store for delivertx
|
|
_, err2 := h.DeliverTx(ctx, store, tc.tx, nil)
|
|
|
|
if len(tc.final) > 0 { // valid
|
|
assert.Nil(err, "%d: %+v", i, err)
|
|
assert.Nil(err2, "%d: %+v", i, err2)
|
|
// make sure proper gas is set
|
|
assert.Equal(uint64(0), cres.GasPayment, "%d", i)
|
|
assert.Equal(tc.cost, cres.GasAllocated, "%d", i)
|
|
// make sure the final balances are correct
|
|
for _, f := range tc.final {
|
|
acct, err := loadAccount(store, f.addr.Bytes())
|
|
assert.Nil(err, "%d: %+v", i, err)
|
|
assert.Equal(f.coins, acct.Coins)
|
|
}
|
|
} else {
|
|
// both check and deliver should fail
|
|
assert.NotNil(err, "%d", i)
|
|
assert.NotNil(err2, "%d", i)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestInitState(t *testing.T) {
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
// some sample settings
|
|
pk := crypto.GenPrivKeySecp256k1().Wrap()
|
|
addr := pk.PubKey().Address()
|
|
actor := auth.SigPerm(addr)
|
|
|
|
someCoins := Coins{{"atom", 123}}
|
|
otherCoins := Coins{{"eth", 11}}
|
|
mixedCoins := someCoins.Plus(otherCoins)
|
|
|
|
type money struct {
|
|
addr sdk.Actor
|
|
coins Coins
|
|
}
|
|
|
|
cases := []struct {
|
|
init []GenesisAccount
|
|
expected []money
|
|
}{
|
|
{
|
|
[]GenesisAccount{{Address: addr, Balance: mixedCoins}},
|
|
[]money{{actor, mixedCoins}},
|
|
},
|
|
}
|
|
|
|
h := NewHandler()
|
|
l := log.NewNopLogger()
|
|
for i, tc := range cases {
|
|
store := state.NewMemKVStore()
|
|
key := "account"
|
|
|
|
// set the options
|
|
for j, gen := range tc.init {
|
|
value, err := json.Marshal(gen)
|
|
require.Nil(err, "%d,%d: %+v", i, j, err)
|
|
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
|
|
require.Nil(err)
|
|
}
|
|
|
|
// check state is proper
|
|
for _, f := range tc.expected {
|
|
acct, err := loadAccount(store, f.addr.Bytes())
|
|
assert.Nil(err, "%d: %+v", i, err)
|
|
assert.Equal(f.coins, acct.Coins)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSetIssuer(t *testing.T) {
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
cases := []struct {
|
|
issuer sdk.Actor
|
|
}{
|
|
{sdk.Actor{App: "sig", Address: []byte("gwkfgk")}},
|
|
// and set back to empty (nil is valid, but assert.Equals doesn't match)
|
|
{sdk.Actor{}},
|
|
{sdk.Actor{ChainID: "other", App: "role", Address: []byte("vote")}},
|
|
}
|
|
|
|
h := NewHandler()
|
|
l := log.NewNopLogger()
|
|
for i, tc := range cases {
|
|
store := state.NewMemKVStore()
|
|
key := "issuer"
|
|
|
|
value, err := json.Marshal(tc.issuer)
|
|
require.Nil(err, "%d,%d: %+v", i, err)
|
|
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
|
|
require.Nil(err, "%+v", err)
|
|
|
|
// check state is proper
|
|
info, err := loadHandlerInfo(store)
|
|
assert.Nil(err, "%d: %+v", i, err)
|
|
assert.Equal(tc.issuer, info.Issuer)
|
|
}
|
|
}
|
|
|
|
func TestDeliverCreditTx(t *testing.T) {
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
// sample coins
|
|
someCoins := Coins{{"atom", 6570}}
|
|
minusCoins := Coins{{"atom", -1234}}
|
|
lessCoins := someCoins.Plus(minusCoins)
|
|
otherCoins := Coins{{"eth", 11}}
|
|
mixedCoins := someCoins.Plus(otherCoins)
|
|
|
|
// some sample addresses
|
|
owner := sdk.Actor{App: "foo", Address: []byte("rocks")}
|
|
addr1 := sdk.Actor{App: "coin", Address: []byte{1, 2}}
|
|
key := NewAccountWithKey(someCoins)
|
|
addr2 := key.Actor()
|
|
addr3 := sdk.Actor{ChainID: "other", App: "sigs", Address: []byte{3, 9}}
|
|
|
|
h := NewHandler()
|
|
store := state.NewMemKVStore()
|
|
ctx := stack.MockContext("secret", 77)
|
|
|
|
// set the owner who can issue credit
|
|
js, err := json.Marshal(owner)
|
|
require.Nil(err, "%+v", err)
|
|
_, err = h.InitState(log.NewNopLogger(), store, "coin", "issuer", string(js), nil)
|
|
require.Nil(err, "%+v", err)
|
|
|
|
// give addr2 some coins to start
|
|
_, err = h.InitState(log.NewNopLogger(), store, "coin", "account", key.MakeOption(), nil)
|
|
require.Nil(err, "%+v", err)
|
|
|
|
cases := []struct {
|
|
tx sdk.Tx
|
|
perm sdk.Actor
|
|
check errors.CheckErr
|
|
addr sdk.Actor
|
|
expected Account
|
|
}{
|
|
// require permission
|
|
{
|
|
tx: NewCreditTx(addr1, someCoins),
|
|
check: errors.IsUnauthorizedErr,
|
|
},
|
|
// add credit
|
|
{
|
|
tx: NewCreditTx(addr1, someCoins),
|
|
perm: owner,
|
|
check: errors.NoErr,
|
|
addr: addr1,
|
|
expected: Account{Coins: someCoins, Credit: someCoins},
|
|
},
|
|
// remove some
|
|
{
|
|
tx: NewCreditTx(addr1, minusCoins),
|
|
perm: owner,
|
|
check: errors.NoErr,
|
|
addr: addr1,
|
|
expected: Account{Coins: lessCoins, Credit: lessCoins},
|
|
},
|
|
// can't remove more cash than there is
|
|
{
|
|
tx: NewCreditTx(addr1, otherCoins.Negative()),
|
|
perm: owner,
|
|
check: IsInsufficientFundsErr,
|
|
},
|
|
// cumulative with initial state
|
|
{
|
|
tx: NewCreditTx(addr2, otherCoins),
|
|
perm: owner,
|
|
check: errors.NoErr,
|
|
addr: addr2,
|
|
expected: Account{Coins: mixedCoins, Credit: otherCoins},
|
|
},
|
|
// Even if there is cash, credit can't go negative
|
|
{
|
|
tx: NewCreditTx(addr2, minusCoins),
|
|
perm: owner,
|
|
check: IsInsufficientCreditErr,
|
|
},
|
|
// make sure it works for other chains
|
|
{
|
|
tx: NewCreditTx(addr3, mixedCoins),
|
|
perm: owner,
|
|
check: errors.NoErr,
|
|
addr: ChainAddr(addr3),
|
|
expected: Account{Coins: mixedCoins, Credit: mixedCoins},
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
myStore := store.Checkpoint()
|
|
|
|
myCtx := ctx.WithPermissions(tc.perm)
|
|
_, err = h.DeliverTx(myCtx, myStore, tc.tx, nil)
|
|
assert.True(tc.check(err), "%d: %+v", i, err)
|
|
|
|
if err == nil {
|
|
store.Commit(myStore)
|
|
acct, err := GetAccount(store, tc.addr)
|
|
require.Nil(err, "%+v", err)
|
|
assert.Equal(tc.expected, acct, "%d", i)
|
|
}
|
|
}
|
|
}
|