Error codespacing (#809)

* Initial codespacing layout (ref #766)
* Add codespace to Router (ref #766)
* Implement Codespacer and update modules
* Default codespaces, testcases
* Update error formatting, codespacer tests
* Add RegisterOrPanic testcase
* Update CHANGELOG
This commit is contained in:
Christopher Goes 2018-04-18 04:16:21 +02:00 committed by Jae Kwon
parent ffd428d199
commit f8e44b5b00
41 changed files with 438 additions and 305 deletions

View File

@ -13,6 +13,7 @@ BREAKING CHANGES
* Remove go-wire, use go-amino
* [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface
* [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores
* All module keepers now require a codespace, see basecoin or democoin for usage
BUG FIXES

9
Gopkg.lock generated
View File

@ -342,7 +342,7 @@
"types/priv_validator",
"version"
]
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
revision = "a2930cd7233f04f5a651020669289296545e70dc"
version = "v0.19.0"
[[projects]]
@ -384,6 +384,7 @@
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
@ -391,13 +392,13 @@
"lex/httplex",
"trace"
]
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
revision = "8d16fa6dc9a85c1cd3ed24ad08ff21cf94f10888"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
revision = "b126b21c05a91c856b027c16779c12e3bf236954"
[[projects]]
name = "golang.org/x/text"
@ -424,7 +425,7 @@
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[projects]]
name = "google.golang.org/grpc"

View File

@ -24,11 +24,12 @@ var dbHeaderKey = []byte("header")
// The ABCI application
type BaseApp struct {
// initialized on creation
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
codespacer *sdk.Codespacer // handle module codespacing
// must be set
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
@ -56,13 +57,18 @@ var _ abci.Application = (*BaseApp)(nil)
// Create and name new BaseApp
// NOTE: The db is used to store the version number for now.
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp {
return &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
app := &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
codespacer: sdk.NewCodespacer(),
}
// Register the undefined & root codespaces, which should not be used by any modules
app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined)
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
return app
}
// BaseApp Name
@ -70,6 +76,11 @@ func (app *BaseApp) Name() string {
return app.name
}
// Register the next available codespace through the baseapp's codespacer, starting from a default
func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType {
return app.codespacer.RegisterNext(codespace)
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys {
@ -355,6 +366,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
// Validate the Msg.
err := msg.ValidateBasic()
if err != nil {
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
return err.Result()
}

View File

@ -80,7 +80,7 @@ func TestKeys(t *testing.T) {
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
res, body = request(t, port, "POST", "/keys", jsonStr)
assert.Equal(t, http.StatusOK, res.StatusCode, body)
require.Equal(t, http.StatusOK, res.StatusCode, body)
addr := body
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
@ -311,7 +311,11 @@ func TestTxs(t *testing.T) {
// strt TM and the LCD in process, listening on their respective sockets
func startTMAndLCD() (*nm.Node, net.Listener, error) {
viper.Set(cli.HomeFlag, os.TempDir())
dir, err := ioutil.TempDir("", "lcd_test")
if err != nil {
return nil, nil, err
}
viper.Set(cli.HomeFlag, dir)
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
if err != nil {
return nil, nil, err

View File

@ -65,8 +65,8 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Add handlers.
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
@ -123,7 +123,7 @@ func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
// are registered by MakeTxCodec in bank.RegisterAmino.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
return nil, sdk.ErrTxDecode("").Trace(err.Error())
}
return tx, nil
}

View File

@ -166,7 +166,7 @@ func TestSortGenesis(t *testing.T) {
// Unsorted coins means invalid
err := sendMsg5.ValidateBasic()
require.Equal(t, sdk.CodeInvalidCoins, err.ABCICode(), err.ABCILog())
require.Equal(t, sdk.CodeInvalidCoins, err.Code(), err.ABCILog())
// Sort coins, should be valid
sendMsg5.Inputs[0].Coins.Sort()
@ -243,7 +243,7 @@ func TestSendMsgWithAccounts(t *testing.T) {
tx.Signatures[0].Sequence = 1
res := bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the bumped sequence should work
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
@ -437,18 +437,18 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64,
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()

View File

@ -70,10 +70,10 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Add handlers.
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("cool", cool.NewHandler(coolKeeper)).
@ -136,7 +136,7 @@ func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
// are registered by MakeTxCodec in bank.RegisterWire.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
return nil, sdk.ErrTxDecode("").Trace(err.Error())
}
return tx, nil
}

View File

@ -202,12 +202,12 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
@ -218,19 +218,19 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), sdk.ABCICodeType(res.Code), res.Log)
// bumping the txnonce number without resigning should be an auth error
tx.Signatures[0].Sequence = 1
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), sdk.ABCICodeType(res.Code), res.Log)
// resigning the tx with the bumped sequence should work
sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
tx.Signatures[0].Signature = sig
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
}
func TestMineMsg(t *testing.T) {
@ -403,18 +403,18 @@ func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, e
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()

View File

@ -7,11 +7,13 @@ import (
)
const (
DefaultCodespace sdk.CodespaceType = 6
// Cool module reserves error 400-499 lawl
CodeIncorrectCoolAnswer sdk.CodeType = 400
)
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
func ErrIncorrectCoolAnswer(answer string) sdk.Error {
return sdk.NewError(CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
return sdk.NewError(codespace, CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
}

View File

@ -44,7 +44,7 @@ func handleQuizMsg(ctx sdk.Context, k Keeper, msg QuizMsg) sdk.Result {
correct := k.CheckTrend(ctx, msg.CoolAnswer)
if !correct {
return ErrIncorrectCoolAnswer(msg.CoolAnswer).Result()
return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
}
if ctx.IsCheckTx() {

View File

@ -10,11 +10,13 @@ type Keeper struct {
ck bank.CoinKeeper
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
codespace sdk.CodespaceType
}
// NewKeeper - Returns the Keeper
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper) Keeper {
return Keeper{bankKeeper, key}
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper, codespace sdk.CodespaceType) Keeper {
return Keeper{bankKeeper, key, codespace}
}
// Key to knowing the trend on the streets!

View File

@ -7,6 +7,8 @@ import (
type CodeType = sdk.CodeType
const (
DefaultCodespace sdk.CodespaceType = 5
CodeInvalidDifficulty CodeType = 201
CodeNonexistentDifficulty CodeType = 202
CodeNonexistentReward CodeType = 203
@ -40,32 +42,32 @@ func codeToDefaultMsg(code CodeType) string {
}
}
func ErrInvalidDifficulty(msg string) sdk.Error {
return newError(CodeInvalidDifficulty, msg)
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidDifficulty, msg)
}
func ErrNonexistentDifficulty() sdk.Error {
return newError(CodeNonexistentDifficulty, "")
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentDifficulty, "")
}
func ErrNonexistentReward() sdk.Error {
return newError(CodeNonexistentReward, "")
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentReward, "")
}
func ErrNonexistentCount() sdk.Error {
return newError(CodeNonexistentCount, "")
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentCount, "")
}
func ErrInvalidProof(msg string) sdk.Error {
return newError(CodeInvalidProof, msg)
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidProof, msg)
}
func ErrNotBelowTarget(msg string) sdk.Error {
return newError(CodeNotBelowTarget, msg)
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeNotBelowTarget, msg)
}
func ErrInvalidCount(msg string) sdk.Error {
return newError(CodeInvalidCount, msg)
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidCount, msg)
}
func msgOrDefaultMsg(msg string, code CodeType) string {
@ -76,7 +78,7 @@ func msgOrDefaultMsg(msg string, code CodeType) string {
}
}
func newError(code CodeType, msg string) sdk.Error {
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
return sdk.NewError(codespace, code, msg)
}

View File

@ -22,7 +22,7 @@ func TestPowHandler(t *testing.T) {
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
config := NewPowConfig("pow", int64(1))
ck := bank.NewCoinKeeper(am)
keeper := NewKeeper(capKey, config, ck)
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
handler := keeper.Handler

View File

@ -21,17 +21,18 @@ type PowGenesis struct {
}
type Keeper struct {
key sdk.StoreKey
config PowConfig
ck bank.CoinKeeper
key sdk.StoreKey
config PowConfig
ck bank.CoinKeeper
codespace sdk.CodespaceType
}
func NewPowConfig(denomination string, reward int64) PowConfig {
return PowConfig{denomination, reward}
}
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
return Keeper{key, config, ck}
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper, codespace sdk.CodespaceType) Keeper {
return Keeper{key, config, ck, codespace}
}
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
@ -78,24 +79,24 @@ func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (u
lastDifficulty, err := pk.GetLastDifficulty(ctx)
if err != nil {
return 0, 0, ErrNonexistentDifficulty()
return 0, 0, ErrNonexistentDifficulty(pk.codespace)
}
newDifficulty := lastDifficulty + 1
lastCount, err := pk.GetLastCount(ctx)
if err != nil {
return 0, 0, ErrNonexistentCount()
return 0, 0, ErrNonexistentCount(pk.codespace)
}
newCount := lastCount + 1
if count != newCount {
return 0, 0, ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
return 0, 0, ErrInvalidCount(pk.codespace, fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
}
if difficulty != newDifficulty {
return 0, 0, ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
return 0, 0, ErrInvalidDifficulty(pk.codespace, fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
}
return newDifficulty, newCount, nil

View File

@ -35,7 +35,7 @@ func TestPowKeeperGetSet(t *testing.T) {
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
config := NewPowConfig("pow", int64(1))
ck := bank.NewCoinKeeper(am)
keeper := NewKeeper(capKey, config, ck)
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
assert.Nil(t, err)

View File

@ -52,7 +52,7 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
hex.Encode(hashHex, hash)
hashHex = hashHex[:16]
if !bytes.Equal(hashHex, msg.Proof) {
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
}
// check proof below difficulty
@ -60,10 +60,10 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
target := math.MaxUint64 / msg.Difficulty
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
if err != nil {
return ErrInvalidProof(fmt.Sprintf("proof: %s", msg.Proof))
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
}
if hashUint >= target {
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
}
return nil

View File

@ -128,34 +128,34 @@ func TestMultiStoreQuery(t *testing.T) {
// Test bad path.
query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
qres := multi.Query(query)
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
query.Path = "h897fy32890rf63296r92"
qres = multi.Query(query)
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
// Test invalid store name.
query.Path = "/garbage/key"
qres = multi.Query(query)
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
// Test valid query with data.
query.Path = "/store1/key"
qres = multi.Query(query)
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
assert.Equal(t, v, qres.Value)
// Test valid but empty query.
query.Path = "/store2/key"
query.Prove = true
qres = multi.Query(query)
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
assert.Nil(t, qres.Value)
// Test store2 data.
query.Data = k2
qres = multi.Query(query)
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
assert.Equal(t, v2, qres.Value)
}

35
types/codespacer.go Normal file
View File

@ -0,0 +1,35 @@
package types
// Codespacer is a simple struct to track reserved codespaces
type Codespacer struct {
reserved map[CodespaceType]bool
}
// NewCodespacer generates a new Codespacer with the starting codespace
func NewCodespacer() *Codespacer {
return &Codespacer{
reserved: make(map[CodespaceType]bool),
}
}
// RegisterNext reserves and returns the next available codespace, starting from a default, and panics if the maximum codespace is reached
func (c *Codespacer) RegisterNext(codespace CodespaceType) CodespaceType {
for {
if !c.reserved[codespace] {
c.reserved[codespace] = true
return codespace
}
codespace++
if codespace == MaximumCodespace {
panic("Maximum codespace reached!")
}
}
}
// RegisterOrPanic reserved a codespace or panics if it is unavailable
func (c *Codespacer) RegisterOrPanic(codespace CodespaceType) {
if c.reserved[codespace] {
panic("Cannot register codespace, already reserved")
}
c.reserved[codespace] = true
}

47
types/codespacer_test.go Normal file
View File

@ -0,0 +1,47 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestRegisterNext(t *testing.T) {
codespacer := NewCodespacer()
// unregistered, allow
code1 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code1, CodespaceType(2))
// registered, pick next
code2 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code2, CodespaceType(3))
// pick next
code3 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code3, CodespaceType(4))
// skip 1
code4 := codespacer.RegisterNext(CodespaceType(6))
require.Equal(t, code4, CodespaceType(6))
code5 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code5, CodespaceType(5))
code6 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code6, CodespaceType(7))
// panic on maximum
defer func() {
r := recover()
require.NotNil(t, r, "Did not panic on maximum codespace")
}()
codespacer.RegisterNext(MaximumCodespace - 1)
codespacer.RegisterNext(MaximumCodespace - 1)
}
func TestRegisterOrPanic(t *testing.T) {
codespacer := NewCodespacer()
// unregistered, allow
code1 := codespacer.RegisterNext(CodespaceType(2))
require.Equal(t, code1, CodespaceType(2))
// panic on duplicate
defer func() {
r := recover()
require.NotNil(t, r, "Did not panic on duplicate codespace")
}()
codespacer.RegisterOrPanic(CodespaceType(2))
}

View File

@ -2,25 +2,42 @@ package types
import (
"fmt"
"runtime"
cmn "github.com/tendermint/tmlibs/common"
abci "github.com/tendermint/abci/types"
)
// ABCI Response Code
type CodeType uint32
// ABCICodeType - combined codetype / codespace
type ABCICodeType uint32
// is everything okay?
func (code CodeType) IsOK() bool {
if code == CodeOK {
// CodeType - code identifier within codespace
type CodeType uint16
// CodespaceType - codespace identifier
type CodespaceType uint16
// IsOK - is everything okay?
func (code ABCICodeType) IsOK() bool {
if code == ABCICodeOK {
return true
}
return false
}
// ABCI Response Codes
// Base SDK reserves 0 - 99.
func ToABCICode(space CodespaceType, code CodeType) ABCICodeType {
// TODO: Make Tendermint more aware of codespaces.
if space == CodespaceRoot && code == CodeOK {
return ABCICodeOK
}
return ABCICodeType((uint32(space) << 16) | uint32(code))
}
const (
// ABCI error codes
ABCICodeOK ABCICodeType = 0
// Base error codes
CodeOK CodeType = 0
CodeInternal CodeType = 1
CodeTxDecode CodeType = 2
@ -34,7 +51,14 @@ const (
CodeInsufficientCoins CodeType = 10
CodeInvalidCoins CodeType = 11
CodeGenesisParse CodeType = 0xdead // TODO: remove ? // why remove?
// CodespaceRoot is a codespace for error codes in this file only.
// Notice that 0 is an "unset" codespace, which can be overridden with
// Error.WithDefaultCodespace().
CodespaceUndefined CodespaceType = 0
CodespaceRoot CodespaceType = 1
// Maximum reservable codespace (2^16 - 1)
MaximumCodespace CodespaceType = 65535
)
// NOTE: Don't stringer this, we'll put better messages in later.
@ -44,8 +68,6 @@ func CodeToDefaultMsg(code CodeType) string {
return "Internal error"
case CodeTxDecode:
return "Tx parse error"
case CodeGenesisParse:
return "Genesis parse error"
case CodeInvalidSequence:
return "Invalid sequence"
case CodeUnauthorized:
@ -75,40 +97,37 @@ func CodeToDefaultMsg(code CodeType) string {
// nolint
func ErrInternal(msg string) Error {
return newError(CodeInternal, msg)
return newErrorWithRootCodespace(CodeInternal, msg)
}
func ErrTxDecode(msg string) Error {
return newError(CodeTxDecode, msg)
}
func ErrGenesisParse(msg string) Error {
return newError(CodeGenesisParse, msg)
return newErrorWithRootCodespace(CodeTxDecode, msg)
}
func ErrInvalidSequence(msg string) Error {
return newError(CodeInvalidSequence, msg)
return newErrorWithRootCodespace(CodeInvalidSequence, msg)
}
func ErrUnauthorized(msg string) Error {
return newError(CodeUnauthorized, msg)
return newErrorWithRootCodespace(CodeUnauthorized, msg)
}
func ErrInsufficientFunds(msg string) Error {
return newError(CodeInsufficientFunds, msg)
return newErrorWithRootCodespace(CodeInsufficientFunds, msg)
}
func ErrUnknownRequest(msg string) Error {
return newError(CodeUnknownRequest, msg)
return newErrorWithRootCodespace(CodeUnknownRequest, msg)
}
func ErrInvalidAddress(msg string) Error {
return newError(CodeInvalidAddress, msg)
return newErrorWithRootCodespace(CodeInvalidAddress, msg)
}
func ErrUnknownAddress(msg string) Error {
return newError(CodeUnknownAddress, msg)
return newErrorWithRootCodespace(CodeUnknownAddress, msg)
}
func ErrInvalidPubKey(msg string) Error {
return newError(CodeInvalidPubKey, msg)
return newErrorWithRootCodespace(CodeInvalidPubKey, msg)
}
func ErrInsufficientCoins(msg string) Error {
return newError(CodeInsufficientCoins, msg)
return newErrorWithRootCodespace(CodeInsufficientCoins, msg)
}
func ErrInvalidCoins(msg string) Error {
return newError(CodeInvalidCoins, msg)
return newErrorWithRootCodespace(CodeInvalidCoins, msg)
}
//----------------------------------------
@ -117,104 +136,98 @@ func ErrInvalidCoins(msg string) Error {
// sdk Error type
type Error interface {
Error() string
ABCICode() CodeType
Code() CodeType
Codespace() CodespaceType
ABCILog() string
ABCICode() ABCICodeType
WithDefaultCodespace(codespace CodespaceType) Error
Trace(msg string) Error
TraceCause(cause error, msg string) Error
Cause() error
T() interface{}
Result() Result
QueryResult() abci.ResponseQuery
}
func NewError(code CodeType, msg string) Error {
return newError(code, msg)
// NewError - create an error
func NewError(codespace CodespaceType, code CodeType, msg string) Error {
return newError(codespace, code, msg)
}
type traceItem struct {
msg string
filename string
lineno int
func newErrorWithRootCodespace(code CodeType, msg string) *sdkError {
return newError(CodespaceRoot, code, msg)
}
func (ti traceItem) String() string {
return fmt.Sprintf("%v:%v %v", ti.filename, ti.lineno, ti.msg)
}
type sdkError struct {
code CodeType
msg string
cause error
traces []traceItem
}
func newError(code CodeType, msg string) *sdkError {
// TODO capture stacktrace if ENV is set.
func newError(codespace CodespaceType, code CodeType, msg string) *sdkError {
if msg == "" {
msg = CodeToDefaultMsg(code)
}
return &sdkError{
code: code,
msg: msg,
cause: nil,
traces: nil,
codespace: codespace,
code: code,
err: cmn.NewErrorWithT(code, msg),
}
}
type sdkError struct {
codespace CodespaceType
code CodeType
err cmn.Error
}
// Implements ABCIError.
func (err *sdkError) Error() string {
return fmt.Sprintf("Error{%d:%s,%v,%v}", err.code, err.msg, err.cause, len(err.traces))
return fmt.Sprintf("Error{%d:%d,%#v}", err.codespace, err.code, err.err)
}
// Implements ABCIError.
func (err *sdkError) ABCICode() CodeType {
func (err *sdkError) ABCICode() ABCICodeType {
return ToABCICode(err.codespace, err.code)
}
// Implements Error.
func (err *sdkError) Codespace() CodespaceType {
return err.codespace
}
// Implements Error.
func (err *sdkError) Code() CodeType {
return err.code
}
// Implements ABCIError.
func (err *sdkError) ABCILog() string {
traceLog := ""
for _, ti := range err.traces {
traceLog += ti.String() + "\n"
}
return fmt.Sprintf("msg: %v\ntrace:\n%v",
err.msg,
traceLog,
)
return fmt.Sprintf(`=== ABCI Log ===
Codespace: %v
Code: %v
ABCICode: %v
Error: %#v
=== /ABCI Log ===
`, err.codespace, err.code, err.ABCICode(), err.err)
}
// Add tracing information with msg.
func (err *sdkError) Trace(msg string) Error {
return err.doTrace(msg, 2)
}
// Add tracing information with cause and msg.
func (err *sdkError) TraceCause(cause error, msg string) Error {
err.cause = cause
return err.doTrace(msg, 2)
}
func (err *sdkError) doTrace(msg string, n int) Error {
_, fn, line, ok := runtime.Caller(n)
if !ok {
if fn == "" {
fn = "<unknown>"
}
if line <= 0 {
line = -1
}
return &sdkError{
codespace: err.codespace,
code: err.code,
err: err.err.Trace(msg),
}
// Include file & line number & msg.
// Do not include the whole stack trace.
err.traces = append(err.traces, traceItem{
filename: fn,
lineno: line,
msg: msg,
})
return err
}
func (err *sdkError) Cause() error {
return err.cause
// Implements Error.
func (err *sdkError) WithDefaultCodespace(cs CodespaceType) Error {
codespace := err.codespace
if codespace == CodespaceUndefined {
codespace = cs
}
return &sdkError{
codespace: codespace,
code: err.code,
err: err.err,
}
}
func (err *sdkError) T() interface{} {
return err.err.T()
}
func (err *sdkError) Result() Result {

View File

@ -16,7 +16,6 @@ var codeTypes = []CodeType{
CodeUnknownRequest,
CodeUnknownAddress,
CodeInvalidPubKey,
CodeGenesisParse,
}
type errFn func(msg string) Error
@ -30,14 +29,12 @@ var errFns = []errFn{
ErrUnknownRequest,
ErrUnknownAddress,
ErrInvalidPubKey,
ErrGenesisParse,
}
func TestCodeType(t *testing.T) {
assert.True(t, CodeOK.IsOK())
assert.True(t, ABCICodeOK.IsOK())
for _, c := range codeTypes {
assert.False(t, c.IsOK())
msg := CodeToDefaultMsg(c)
assert.False(t, strings.HasPrefix(msg, "Unknown code"))
}
@ -47,7 +44,7 @@ func TestErrFn(t *testing.T) {
for i, errFn := range errFns {
err := errFn("")
codeType := codeTypes[i]
assert.Equal(t, err.ABCICode(), codeType)
assert.Equal(t, err.Result().Code, codeType)
assert.Equal(t, err.Code(), codeType)
assert.Equal(t, err.Result().Code, ToABCICode(CodespaceRoot, codeType))
}
}

View File

@ -97,23 +97,23 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
switch len(str) {
case 1:
if len(str[0]) == 0 {
return f, NewError(CodeUnknownRequest, "not a decimal string")
return f, ErrUnknownRequest("not a decimal string")
}
numStr = str[0]
case 2:
if len(str[0]) == 0 || len(str[1]) == 0 {
return f, NewError(CodeUnknownRequest, "not a decimal string")
return f, ErrUnknownRequest("not a decimal string")
}
numStr = str[0] + str[1]
len := int64(len(str[1]))
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
default:
return f, NewError(CodeUnknownRequest, "not a decimal string")
return f, ErrUnknownRequest("not a decimal string")
}
num, errConv := strconv.Atoi(numStr)
if errConv != nil {
return f, NewError(CodeUnknownRequest, errConv.Error())
return f, ErrUnknownRequest(errConv.Error())
}
if neg {

View File

@ -9,7 +9,7 @@ import (
type Result struct {
// Code is the response code, is stored back on the chain.
Code CodeType
Code ABCICodeType
// Data is any data returned from the app.
Data []byte

View File

@ -40,7 +40,7 @@ func privAndAddr() (crypto.PrivKey, sdk.Address) {
func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx) {
_, result, abort := anteHandler(ctx, tx)
assert.False(t, abort)
assert.Equal(t, sdk.CodeOK, result.Code)
assert.Equal(t, sdk.ABCICodeOK, result.Code)
assert.True(t, result.IsOK())
}
@ -48,7 +48,7 @@ func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx
func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, code sdk.CodeType) {
_, result, abort := anteHandler(ctx, tx)
assert.True(t, abort)
assert.Equal(t, code, result.Code)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code)
}
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee sdk.StdFee) sdk.Tx {

View File

@ -7,6 +7,8 @@ import (
// Coin errors reserve 100 ~ 199.
const (
DefaultCodespace sdk.CodespaceType = 2
CodeInvalidInput sdk.CodeType = 101
CodeInvalidOutput sdk.CodeType = 102
)
@ -26,20 +28,20 @@ func codeToDefaultMsg(code sdk.CodeType) string {
//----------------------------------------
// Error constructors
func ErrInvalidInput(msg string) sdk.Error {
return newError(CodeInvalidInput, msg)
func ErrInvalidInput(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidInput, msg)
}
func ErrNoInputs() sdk.Error {
return newError(CodeInvalidInput, "")
func ErrNoInputs(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidInput, "")
}
func ErrInvalidOutput(msg string) sdk.Error {
return newError(CodeInvalidOutput, msg)
func ErrInvalidOutput(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidOutput, msg)
}
func ErrNoOutputs() sdk.Error {
return newError(CodeInvalidOutput, "")
func ErrNoOutputs(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidOutput, "")
}
//----------------------------------------
@ -51,7 +53,7 @@ func msgOrDefaultMsg(msg string, code sdk.CodeType) string {
return codeToDefaultMsg(code)
}
func newError(code sdk.CodeType, msg string) sdk.Error {
func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
return sdk.NewError(codespace, code, msg)
}

View File

@ -28,10 +28,10 @@ func (msg SendMsg) ValidateBasic() sdk.Error {
// this just makes sure all the inputs and outputs are properly formatted,
// not that they actually have the money inside
if len(msg.Inputs) == 0 {
return ErrNoInputs().Trace("")
return ErrNoInputs(DefaultCodespace).Trace("")
}
if len(msg.Outputs) == 0 {
return ErrNoOutputs().Trace("")
return ErrNoOutputs(DefaultCodespace).Trace("")
}
// make sure all inputs and outputs are individually valid
var totalIn, totalOut sdk.Coins
@ -102,7 +102,7 @@ func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/issue"
func (msg IssueMsg) ValidateBasic() sdk.Error {
// XXX
if len(msg.Outputs) == 0 {
return ErrNoOutputs().Trace("")
return ErrNoOutputs(DefaultCodespace).Trace("")
}
for _, out := range msg.Outputs {
if err := out.ValidateBasic(); err != nil {

View File

@ -5,6 +5,8 @@ import (
)
const (
DefaultCodespace sdk.CodespaceType = 3
// IBC errors reserve 200 - 299.
CodeInvalidSequence sdk.CodeType = 200
CodeIdenticalChains sdk.CodeType = 201
@ -22,20 +24,20 @@ func codeToDefaultMsg(code sdk.CodeType) string {
}
}
func ErrInvalidSequence() sdk.Error {
return newError(CodeInvalidSequence, "")
func ErrInvalidSequence(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidSequence, "")
}
func ErrIdenticalChains() sdk.Error {
return newError(CodeIdenticalChains, "")
func ErrIdenticalChains(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeIdenticalChains, "")
}
// -------------------------
// Helpers
func newError(code sdk.CodeType, msg string) sdk.Error {
func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
return sdk.NewError(codespace, code, msg)
}
func msgOrDefaultMsg(msg string, code sdk.CodeType) string {

View File

@ -44,7 +44,7 @@ func handleIBCReceiveMsg(ctx sdk.Context, ibcm IBCMapper, ck bank.CoinKeeper, ms
seq := ibcm.GetIngressSequence(ctx, packet.SrcChain)
if msg.Sequence != seq {
return ErrInvalidSequence().Result()
return ErrInvalidSequence(ibcm.codespace).Result()
}
_, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins)

View File

@ -73,7 +73,7 @@ func TestIBC(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, mycoins, coins)
ibcm := NewIBCMapper(cdc, key)
ibcm := NewIBCMapper(cdc, key, DefaultCodespace)
h := NewHandler(ibcm, ck)
packet := IBCPacket{
SrcAddr: src,

View File

@ -8,17 +8,19 @@ import (
)
type IBCMapper struct {
key sdk.StoreKey
cdc *wire.Codec
key sdk.StoreKey
cdc *wire.Codec
codespace sdk.CodespaceType
}
// XXX: The IBCMapper should not take a CoinKeeper. Rather have the CoinKeeper
// take an IBCMapper.
func NewIBCMapper(cdc *wire.Codec, key sdk.StoreKey) IBCMapper {
func NewIBCMapper(cdc *wire.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) IBCMapper {
// XXX: How are these codecs supposed to work?
return IBCMapper{
key: key,
cdc: cdc,
key: key,
cdc: cdc,
codespace: codespace,
}
}

View File

@ -33,7 +33,7 @@ func NewIBCPacket(srcAddr sdk.Address, destAddr sdk.Address, coins sdk.Coins,
func (ibcp IBCPacket) ValidateBasic() sdk.Error {
if ibcp.SrcChain == ibcp.DestChain {
return ErrIdenticalChains().Trace("")
return ErrIdenticalChains(DefaultCodespace).Trace("")
}
if !ibcp.Coins.IsValid() {
return sdk.ErrInvalidCoins("")

View File

@ -5,6 +5,8 @@ import (
)
const (
DefaultCodespace sdk.CodespaceType = 4
// simplestake errors reserve 300 - 399.
CodeEmptyValidator sdk.CodeType = 300
CodeInvalidUnbond sdk.CodeType = 301
@ -12,25 +14,25 @@ const (
CodeIncorrectStakingToken sdk.CodeType = 303
)
func ErrIncorrectStakingToken() sdk.Error {
return newError(CodeIncorrectStakingToken, "")
func ErrIncorrectStakingToken(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeIncorrectStakingToken, "")
}
func ErrEmptyValidator() sdk.Error {
return newError(CodeEmptyValidator, "")
func ErrEmptyValidator(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeEmptyValidator, "")
}
func ErrInvalidUnbond() sdk.Error {
return newError(CodeInvalidUnbond, "")
func ErrInvalidUnbond(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidUnbond, "")
}
func ErrEmptyStake() sdk.Error {
return newError(CodeEmptyStake, "")
func ErrEmptyStake(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeEmptyStake, "")
}
// -----------------------------
// Helpers
func newError(code sdk.CodeType, msg string) sdk.Error {
return sdk.NewError(code, msg)
func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error {
return sdk.NewError(codespace, code, msg)
}

View File

@ -32,7 +32,7 @@ func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
}
return sdk.Result{
Code: sdk.CodeOK,
Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet},
}
}
@ -49,7 +49,7 @@ func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
}
return sdk.Result{
Code: sdk.CodeOK,
Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet},
}
}

View File

@ -15,17 +15,19 @@ const moduleName = "simplestake"
type Keeper struct {
ck bank.CoinKeeper
key sdk.StoreKey
cdc *wire.Codec
key sdk.StoreKey
cdc *wire.Codec
codespace sdk.CodespaceType
}
func NewKeeper(key sdk.StoreKey, coinKeeper bank.CoinKeeper) Keeper {
func NewKeeper(key sdk.StoreKey, coinKeeper bank.CoinKeeper, codespace sdk.CodespaceType) Keeper {
cdc := wire.NewCodec()
wire.RegisterCrypto(cdc)
return Keeper{
key: key,
cdc: cdc,
ck: coinKeeper,
key: key,
cdc: cdc,
ck: coinKeeper,
codespace: codespace,
}
}
@ -59,7 +61,7 @@ func (k Keeper) deleteBondInfo(ctx sdk.Context, addr sdk.Address) {
func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken()
return 0, ErrIncorrectStakingToken(k.codespace)
}
_, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake})
@ -84,7 +86,7 @@ func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, st
func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond()
return nil, 0, ErrInvalidUnbond(k.codespace)
}
k.deleteBondInfo(ctx, addr)
@ -102,7 +104,7 @@ func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64,
func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken()
return 0, ErrIncorrectStakingToken(k.codespace)
}
bi := k.getBondInfo(ctx, addr)
@ -122,7 +124,7 @@ func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypt
func (k Keeper) unbondWithoutCoins(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond()
return nil, 0, ErrInvalidUnbond(k.codespace)
}
k.deleteBondInfo(ctx, addr)

View File

@ -33,7 +33,7 @@ func TestKeeperGetSet(t *testing.T) {
ms, _, capKey := setupMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
stakeKeeper := NewKeeper(capKey, bank.NewCoinKeeper(nil))
stakeKeeper := NewKeeper(capKey, bank.NewCoinKeeper(nil), DefaultCodespace)
addr := sdk.Address([]byte("some-address"))
bi := stakeKeeper.getBondInfo(ctx, addr)
@ -63,13 +63,13 @@ func TestBonding(t *testing.T) {
accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
coinKeeper := bank.NewCoinKeeper(accountMapper)
stakeKeeper := NewKeeper(capKey, coinKeeper)
stakeKeeper := NewKeeper(capKey, coinKeeper, DefaultCodespace)
addr := sdk.Address([]byte("some-address"))
privKey := crypto.GenPrivKeyEd25519()
pubKey := privKey.PubKey()
_, _, err := stakeKeeper.unbondWithoutCoins(ctx, addr)
assert.Equal(t, err, ErrInvalidUnbond())
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
_, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.Coin{"steak", 10})
assert.Nil(t, err)
@ -82,5 +82,5 @@ func TestBonding(t *testing.T) {
assert.Equal(t, pubKey, pk)
_, _, err = stakeKeeper.unbondWithoutCoins(ctx, addr)
assert.Equal(t, err, ErrInvalidUnbond())
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
}

View File

@ -31,7 +31,7 @@ func (msg BondMsg) Type() string {
func (msg BondMsg) ValidateBasic() sdk.Error {
if msg.Stake.IsZero() {
return ErrEmptyStake()
return ErrEmptyStake(DefaultCodespace)
}
if msg.PubKey == nil {

View File

@ -10,6 +10,8 @@ import (
type CodeType = sdk.CodeType
const (
DefaultCodespace sdk.CodespaceType = 4
// Gaia errors reserve 200 ~ 299.
CodeInvalidValidator CodeType = 201
CodeInvalidCandidate CodeType = 202
@ -45,62 +47,62 @@ func codeToDefaultMsg(code CodeType) string {
//----------------------------------------
// Error constructors
func ErrNotEnoughBondShares(shares string) sdk.Error {
return newError(CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
func ErrNotEnoughBondShares(codespace sdk.CodespaceType, shares string) sdk.Error {
return newError(codespace, CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
}
func ErrCandidateEmpty() sdk.Error {
return newError(CodeInvalidValidator, "Cannot bond to an empty candidate")
func ErrCandidateEmpty(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Cannot bond to an empty candidate")
}
func ErrBadBondingDenom() sdk.Error {
return newError(CodeInvalidBond, "Invalid coin denomination")
func ErrBadBondingDenom(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidBond, "Invalid coin denomination")
}
func ErrBadBondingAmount() sdk.Error {
return newError(CodeInvalidBond, "Amount must be > 0")
func ErrBadBondingAmount(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidBond, "Amount must be > 0")
}
func ErrNoBondingAcct() sdk.Error {
return newError(CodeInvalidValidator, "No bond account for this (address, validator) pair")
func ErrNoBondingAcct(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "No bond account for this (address, validator) pair")
}
func ErrCommissionNegative() sdk.Error {
return newError(CodeInvalidValidator, "Commission must be positive")
func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Commission must be positive")
}
func ErrCommissionHuge() sdk.Error {
return newError(CodeInvalidValidator, "Commission cannot be more than 100%")
func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Commission cannot be more than 100%")
}
func ErrBadValidatorAddr() sdk.Error {
return newError(CodeInvalidValidator, "Validator does not exist for that address")
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Validator does not exist for that address")
}
func ErrBadCandidateAddr() sdk.Error {
return newError(CodeInvalidValidator, "Candidate does not exist for that address")
func ErrBadCandidateAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Candidate does not exist for that address")
}
func ErrBadDelegatorAddr() sdk.Error {
return newError(CodeInvalidValidator, "Delegator does not exist for that address")
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Delegator does not exist for that address")
}
func ErrCandidateExistsAddr() sdk.Error {
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
func ErrCandidateExistsAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
}
func ErrCandidateRevoked() sdk.Error {
return newError(CodeInvalidValidator, "Candidacy for this address is currently revoked")
func ErrCandidateRevoked(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Candidacy for this address is currently revoked")
}
func ErrMissingSignature() sdk.Error {
return newError(CodeInvalidValidator, "Missing signature")
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Missing signature")
}
func ErrBondNotNominated() sdk.Error {
return newError(CodeInvalidValidator, "Cannot bond to non-nominated account")
func ErrBondNotNominated(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Cannot bond to non-nominated account")
}
func ErrNoCandidateForAddress() sdk.Error {
return newError(CodeInvalidValidator, "Validator does not exist for that address")
func ErrNoCandidateForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Validator does not exist for that address")
}
func ErrNoDelegatorForAddress() sdk.Error {
return newError(CodeInvalidValidator, "Delegator does not contain validator bond")
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Delegator does not contain validator bond")
}
func ErrInsufficientFunds() sdk.Error {
return newError(CodeInvalidInput, "Insufficient bond shares")
func ErrInsufficientFunds(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidInput, "Insufficient bond shares")
}
func ErrBadShares() sdk.Error {
return newError(CodeInvalidInput, "bad shares provided as input, must be MAX or decimal")
func ErrBadShares(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidInput, "bad shares provided as input, must be MAX or decimal")
}
func ErrBadRemoveValidator() sdk.Error {
return newError(CodeInvalidValidator, "Error removing validator")
func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Error removing validator")
}
//----------------------------------------
@ -114,7 +116,7 @@ func msgOrDefaultMsg(msg string, code CodeType) string {
return codeToDefaultMsg(code)
}
func newError(code CodeType, msg string) sdk.Error {
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
return sdk.NewError(codespace, code, msg)
}

View File

@ -57,10 +57,10 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
// check to see if the pubkey or sender has been registered before
_, found := k.GetCandidate(ctx, msg.CandidateAddr)
if found {
return ErrCandidateExistsAddr().Result()
return ErrCandidateExistsAddr(k.codespace).Result()
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom().Result()
return ErrBadBondingDenom(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{
@ -85,7 +85,7 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
// candidate must already be registered
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
if !found {
return ErrBadCandidateAddr().Result()
return ErrBadCandidateAddr(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{
@ -93,7 +93,7 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
}
}
if candidate.Status == Unbonded { //candidate has been withdrawn
return ErrBondNotNominated().Result()
return ErrBondNotNominated(k.codespace).Result()
}
// XXX move to types
@ -111,13 +111,13 @@ func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
if !found {
return ErrBadCandidateAddr().Result()
return ErrBadCandidateAddr(k.codespace).Result()
}
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
return ErrBadBondingDenom().Result()
return ErrBadBondingDenom(k.codespace).Result()
}
if candidate.Status == Revoked {
return ErrCandidateRevoked().Result()
return ErrCandidateRevoked(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{
@ -165,10 +165,10 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
// check if bond has any shares in it unbond
bond, found := k.getDelegatorBond(ctx, msg.DelegatorAddr, msg.CandidateAddr)
if !found {
return ErrNoDelegatorForAddress().Result()
return ErrNoDelegatorForAddress(k.codespace).Result()
}
if !bond.Shares.GT(sdk.ZeroRat) { // bond shares < msg shares
return ErrInsufficientFunds().Result()
return ErrInsufficientFunds(k.codespace).Result()
}
// test getting rational number from decimal provided
@ -180,18 +180,18 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
// test that there are enough shares to unbond
if msg.Shares == "MAX" {
if !bond.Shares.GT(sdk.ZeroRat) {
return ErrNotEnoughBondShares(msg.Shares).Result()
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
}
} else {
if bond.Shares.LT(shares) {
return ErrNotEnoughBondShares(msg.Shares).Result()
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
}
}
// get candidate
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
if !found {
return ErrNoCandidateForAddress().Result()
return ErrNoCandidateForAddress(k.codespace).Result()
}
if ctx.IsCheckTx() {

View File

@ -19,13 +19,17 @@ type Keeper struct {
// caches
gs Pool
params Params
// codespace
codespace sdk.CodespaceType
}
func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper) Keeper {
func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinKeeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
coinKeeper: ck,
codespace: codespace,
}
return keeper
}

View File

@ -60,18 +60,18 @@ func (msg MsgDeclareCandidacy) GetSignBytes() []byte {
// quick validity check
func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error {
if msg.CandidateAddr == nil {
return ErrCandidateEmpty()
return ErrCandidateEmpty(DefaultCodespace)
}
if msg.Bond.Denom != StakingToken {
return ErrBadBondingDenom()
return ErrBadBondingDenom(DefaultCodespace)
}
if msg.Bond.Amount <= 0 {
return ErrBadBondingAmount()
return ErrBadBondingAmount(DefaultCodespace)
// return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String())
}
empty := Description{}
if msg.Description == empty {
return newError(CodeInvalidInput, "description must be included")
return newError(DefaultCodespace, CodeInvalidInput, "description must be included")
}
return nil
}
@ -111,11 +111,11 @@ func (msg MsgEditCandidacy) GetSignBytes() []byte {
// quick validity check
func (msg MsgEditCandidacy) ValidateBasic() sdk.Error {
if msg.CandidateAddr == nil {
return ErrCandidateEmpty()
return ErrCandidateEmpty(DefaultCodespace)
}
empty := Description{}
if msg.Description == empty {
return newError(CodeInvalidInput, "Transaction must include some information to modify")
return newError(DefaultCodespace, CodeInvalidInput, "Transaction must include some information to modify")
}
return nil
}
@ -157,16 +157,16 @@ func (msg MsgDelegate) GetSignBytes() []byte {
// quick validity check
func (msg MsgDelegate) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrBadDelegatorAddr()
return ErrBadDelegatorAddr(DefaultCodespace)
}
if msg.CandidateAddr == nil {
return ErrBadCandidateAddr()
return ErrBadCandidateAddr(DefaultCodespace)
}
if msg.Bond.Denom != StakingToken {
return ErrBadBondingDenom()
return ErrBadBondingDenom(DefaultCodespace)
}
if msg.Bond.Amount <= 0 {
return ErrBadBondingAmount()
return ErrBadBondingAmount(DefaultCodespace)
// return sdk.ErrInvalidCoins(sdk.Coins{msg.Bond}.String())
}
return nil
@ -209,18 +209,18 @@ func (msg MsgUnbond) GetSignBytes() []byte {
// quick validity check
func (msg MsgUnbond) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrBadDelegatorAddr()
return ErrBadDelegatorAddr(DefaultCodespace)
}
if msg.CandidateAddr == nil {
return ErrBadCandidateAddr()
return ErrBadCandidateAddr(DefaultCodespace)
}
if msg.Shares != "MAX" {
rat, err := sdk.NewRatFromDecimal(msg.Shares)
if err != nil {
return ErrBadShares()
return ErrBadShares(DefaultCodespace)
}
if rat.IsZero() || rat.LT(sdk.ZeroRat) {
return ErrBadShares()
return ErrBadShares(DefaultCodespace)
}
}
return nil

View File

@ -132,7 +132,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
&auth.BaseAccount{}, // prototype
).Seal()
ck := bank.NewCoinKeeper(accountMapper)
keeper := NewKeeper(ctx, cdc, keyStake, ck)
keeper := NewKeeper(ctx, cdc, keyStake, ck, DefaultCodespace)
keeper.setPool(ctx, initialPool())
keeper.setParams(ctx, defaultParams())