Separation of Tx from Msg; CodeType

This commit is contained in:
Jae Kwon 2018-01-26 06:22:56 -08:00
parent 05036e35d2
commit b95b67d520
14 changed files with 154 additions and 115 deletions

View File

@ -227,7 +227,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
}
return abci.ResponseCheckTx{
Code: result.Code,
Code: uint32(result.Code),
Data: result.Data,
Log: result.Log,
GasWanted: result.GasWanted,
@ -253,7 +253,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
}
// After-handler hooks.
if result.Code == abci.CodeTypeOK {
if result.IsOK() {
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
} else {
// Even though the Code is not OK, there will be some side
@ -263,7 +263,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
// Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{
Code: result.Code,
Code: uint32(result.Code),
Data: result.Data,
Log: result.Log,
GasWanted: result.GasWanted,
@ -285,8 +285,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
}
}()
// Validate the Tx.Msg.
err := tx.ValidateBasic()
// Get the Msg.
var msg = tx.GetMsg()
if msg == nil {
return sdk.ErrInternal("Tx.GetMsg() returned nil").Result()
}
// Validate the Msg.
err := msg.ValidateBasic()
if err != nil {
return err.Result()
}
@ -297,9 +303,6 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
// TODO: override default ante handler w/ custom ante handler.
// Run the ante handler.
if ctx.IsZero() {
panic("why? before")
}
newCtx, result, abort := app.defaultAnteHandler(ctx, tx)
if isCheckTx || abort {
return result
@ -313,9 +316,9 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
ctx = ctx.WithMultiStore(msCache)
// Match and run route.
msgType := tx.Type()
msgType := msg.Type()
handler := app.router.Route(msgType)
result = handler(ctx, tx)
result = handler(ctx, msg)
// If result was successful, write to app.msDeliver or app.msCheck.
if result.IsOK() {

View File

@ -22,10 +22,11 @@ type testUpdatePowerTx struct {
NewPower int64
}
const txType = "testUpdatePowerTx"
const msgType = "testUpdatePowerTx"
func (tx testUpdatePowerTx) Type() string { return txType }
func (tx testUpdatePowerTx) Type() string { return msgType }
func (tx testUpdatePowerTx) Get(key interface{}) (value interface{}) { return nil }
func (tx testUpdatePowerTx) GetMsg() sdk.Msg { return tx }
func (tx testUpdatePowerTx) GetSignBytes() []byte { return nil }
func (tx testUpdatePowerTx) ValidateBasic() sdk.Error { return nil }
func (tx testUpdatePowerTx) GetSigners() []crypto.Address { return nil }
@ -44,7 +45,7 @@ func TestBasic(t *testing.T) {
})
app.SetDefaultAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(txType, func(ctx sdk.Context, tx sdk.Tx) sdk.Result {
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// TODO
return sdk.Result{}
})

View File

@ -63,7 +63,7 @@ func (tapp *TestApp) RunCheckTx(tx sdk.Tx) sdk.Result {
func (tapp *TestApp) RunDeliverTx(tx sdk.Tx) sdk.Result {
tapp.ensureBeginBlock()
return tapp.BaseApp.runTx(true, nil, tx)
return tapp.BaseApp.runTx(false, nil, tx)
}
// NOTE: Skips authentication by wrapping msg in testTx{}.
@ -75,7 +75,7 @@ func (tapp *TestApp) RunCheckMsg(msg sdk.Msg) sdk.Result {
// NOTE: Skips authentication by wrapping msg in testTx{}.
func (tapp *TestApp) RunDeliverMsg(msg sdk.Msg) sdk.Result {
var tx = testTx{msg}
return tapp.RunCheckTx(tx)
return tapp.RunDeliverTx(tx)
}
func (tapp *TestApp) CommitMultiStore() sdk.CommitMultiStore {
@ -97,6 +97,7 @@ type testTx struct {
sdk.Msg
}
func (tx testTx) GetMsg() sdk.Msg { return tx.Msg }
func (tx testTx) GetSigners() []crypto.Address { return nil }
func (tx testTx) GetFeePayer() crypto.Address { return nil }
func (tx testTx) GetSignatures() []sdk.StdSignature { return nil }

View File

@ -30,7 +30,11 @@ func TestSendMsg(t *testing.T) {
},
}
// Run a SendMsg.
// Run a Check on SendMsg.
res := tba.RunCheckMsg(msg)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Run a Deliver on SendMsg.
res = tba.RunDeliverMsg(msg)
assert.Equal(t, sdk.CodeUnrecognizedAddress, res.Code, res.Log)
}

View File

@ -51,10 +51,10 @@ func main() {
}
func DummyHandler(storeKey sdk.StoreKey) sdk.Handler {
return func(ctx sdk.Context, tx sdk.Tx) sdk.Result {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// tx is already unmarshalled
key := tx.Get("key").([]byte)
value := tx.Get("value").([]byte)
key := msg.Get("key").([]byte)
value := msg.Get("value").([]byte)
store := ctx.KVStore(storeKey)
store.Set(key, value)

View File

@ -7,6 +7,7 @@ import (
crypto "github.com/tendermint/go-crypto"
)
// An sdk.Tx which is its own sdk.Msg.
type dummyTx struct {
key []byte
value []byte
@ -30,6 +31,10 @@ func (tx dummyTx) Type() string {
return "dummy"
}
func (tx dummyTx) GetMsg() sdk.Msg {
return tx
}
func (tx dummyTx) GetSignBytes() []byte {
return tx.bytes
}

View File

@ -2,25 +2,36 @@ package types
import (
"fmt"
"github.com/tendermint/go-crypto"
"runtime"
)
type CodeType uint32
func (code CodeType) IsOK() bool {
if code == CodeOK {
return true
} else {
return false
}
}
const (
// ABCI Response Codes
// Base SDK reserves 0 ~ 99.
CodeOK uint32 = 0
CodeInternal = 1
CodeTxParse = 2
CodeBadNonce = 3
CodeUnauthorized = 4
CodeInsufficientFunds = 5
CodeUnknownRequest = 6
CodeUnrecognizedAddress = 7
CodeInvalidSequence = 8
CodeOK CodeType = 0
CodeInternal CodeType = 1
CodeTxParse CodeType = 2
CodeBadNonce CodeType = 3
CodeUnauthorized CodeType = 4
CodeInsufficientFunds CodeType = 5
CodeUnknownRequest CodeType = 6
CodeUnrecognizedAddress CodeType = 7
CodeInvalidSequence CodeType = 8
)
// NOTE: Don't stringer this, we'll put better messages in later.
func CodeToDefaultMsg(code uint32) string {
func CodeToDefaultMsg(code CodeType) string {
switch code {
case CodeInternal:
return "Internal error"
@ -71,8 +82,8 @@ func ErrUnknownRequest(msg string) Error {
return newError(CodeUnknownRequest, msg)
}
func ErrUnrecognizedAddress(msg string) Error {
return newError(CodeUnrecognizedAddress, msg)
func ErrUnrecognizedAddress(addr crypto.Address) Error {
return newError(CodeUnrecognizedAddress, addr.String())
}
func ErrInvalidSequence(msg string) Error {
@ -84,7 +95,7 @@ func ErrInvalidSequence(msg string) Error {
type Error interface {
Error() string
ABCICode() uint32
ABCICode() CodeType
ABCILog() string
Trace(msg string) Error
TraceCause(cause error, msg string) Error
@ -92,7 +103,7 @@ type Error interface {
Result() Result
}
func NewError(code uint32, msg string) Error {
func NewError(code CodeType, msg string) Error {
return newError(code, msg)
}
@ -107,39 +118,39 @@ func (ti traceItem) String() string {
}
type sdkError struct {
code uint32
msg string
cause error
trace []traceItem
code CodeType
msg string
cause error
traces []traceItem
}
func newError(code uint32, msg string) *sdkError {
func newError(code CodeType, msg string) *sdkError {
// TODO capture stacktrace if ENV is set.
if msg == "" {
msg = CodeToDefaultMsg(code)
}
return &sdkError{
code: code,
msg: msg,
cause: nil,
trace: nil,
code: code,
msg: msg,
cause: nil,
traces: nil,
}
}
// Implements ABCIError.
func (err *sdkError) Error() string {
return fmt.Sprintf("Error{%d:%s,%v,%v}", err.code, err.msg, err.cause, len(err.trace))
return fmt.Sprintf("Error{%d:%s,%v,%v}", err.code, err.msg, err.cause, len(err.traces))
}
// Implements ABCIError.
func (err *sdkError) ABCICode() uint32 {
func (err *sdkError) ABCICode() CodeType {
return err.code
}
// Implements ABCIError.
func (err *sdkError) ABCILog() string {
traceLog := ""
for _, ti := range err.trace {
for _, ti := range err.traces {
traceLog += ti.String() + "\n"
}
return fmt.Sprintf("msg: %v\ntrace:\n%v",
@ -150,7 +161,17 @@ func (err *sdkError) ABCILog() string {
// Add tracing information with msg.
func (err *sdkError) Trace(msg string) Error {
_, fn, line, ok := runtime.Caller(1)
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>"
@ -161,7 +182,7 @@ func (err *sdkError) Trace(msg string) Error {
}
// Include file & line number & msg.
// Do not include the whole stack trace.
err.trace = append(err.trace, traceItem{
err.traces = append(err.traces, traceItem{
filename: fn,
lineno: line,
msg: msg,
@ -169,12 +190,6 @@ func (err *sdkError) Trace(msg string) Error {
return err
}
// Add tracing information with cause and msg.
func (err *sdkError) TraceCause(cause error, msg string) Error {
err.cause = cause
return err.Trace(msg)
}
func (err *sdkError) Cause() error {
return err.cause
}

View File

@ -1,6 +1,6 @@
package types
type Handler func(ctx Context, tx Tx) Result
type Handler func(ctx Context, msg Msg) Result
// If newCtx.IsZero(), ctx is used instead.
type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool)

View File

@ -9,7 +9,7 @@ import (
type Result struct {
// Code is the response code, is stored back on the chain.
Code uint32
Code CodeType
// Data is any data returned from the app.
Data []byte
@ -36,5 +36,5 @@ type Result struct {
// TODO: In the future, more codes may be OK.
func (res Result) IsOK() bool {
return res.Code == CodeOK
return res.Code.IsOK()
}

View File

@ -27,7 +27,9 @@ type Msg interface {
}
type Tx interface {
Msg
// Gets the Msg.
GetMsg() Msg
// The address that pays the base fee for this message. The fee is
// deducted before the Msg is processed.
@ -50,6 +52,7 @@ type StdTx struct {
Signatures []StdSignature
}
func (tx StdTx) GetMsg() Msg { return tx.Msg }
func (tx StdTx) GetFeePayer() crypto.Address { return tx.Signatures[0].PubKey.Address() }
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }

View File

@ -18,7 +18,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler {
payerAcc := accountMapper.GetAccount(ctx, payerAddr)
if payerAcc == nil {
return ctx,
sdk.ErrUnrecognizedAddress("").Result(),
sdk.ErrUnrecognizedAddress(payerAddr).Result(),
true
}
// TODO: Charge fee from payerAcc.
@ -29,61 +29,66 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler {
// create a Tx with no payer.
}
// Ensure that signatures are correct.
var signerAddrs = tx.GetSigners()
var signerAccs = make([]sdk.Account, len(signerAddrs))
var signatures = tx.GetSignatures()
var sigs = tx.GetSignatures()
// Assert that there are signers.
if len(signerAddrs) == 0 {
if !bam.IsTestAppTx(tx) {
// Assert that there are signatures.
if !bam.IsTestAppTx(tx) {
if len(sigs) == 0 {
return ctx,
sdk.ErrUnauthorized("no signers").Result(),
true
}
}
// Ensure that sigs are correct.
var msg = tx.GetMsg()
var signerAddrs = msg.GetSigners()
var signerAccs = make([]sdk.Account, len(signerAddrs))
// Assert that number of signatures is correct.
if len(signatures) != len(signerAddrs) {
return ctx,
sdk.ErrUnauthorized("wrong number of signers").Result(),
true
}
if !bam.IsTestAppTx(tx) {
if len(sigs) != len(signerAddrs) {
return ctx,
sdk.ErrUnauthorized("wrong number of signers").Result(),
true
}
// Check each nonce and sig.
for i, sig := range signatures {
// Check each nonce and sig.
// TODO Refactor out.
for i, sig := range sigs {
var signerAcc = accountMapper.GetAccount(ctx, signerAddrs[i])
signerAccs[i] = signerAcc
var signerAcc = accountMapper.GetAccount(ctx, signerAddrs[i])
signerAccs[i] = signerAcc
// If no pubkey, set pubkey.
if signerAcc.GetPubKey() == nil {
err := signerAcc.SetPubKey(sig.PubKey)
if err != nil {
// If no pubkey, set pubkey.
if signerAcc.GetPubKey() == nil {
err := signerAcc.SetPubKey(sig.PubKey)
if err != nil {
return ctx,
sdk.ErrInternal("setting PubKey on signer").Result(),
true
}
}
// Check and increment sequence number.
seq := signerAcc.GetSequence()
if seq != sig.Sequence {
return ctx,
sdk.ErrInternal("setting PubKey on signer").Result(),
sdk.ErrInvalidSequence("").Result(),
true
}
}
signerAcc.SetSequence(seq + 1)
// Check and increment sequence number.
seq := signerAcc.GetSequence()
if seq != sig.Sequence {
return ctx,
sdk.ErrInvalidSequence("").Result(),
true
}
signerAcc.SetSequence(seq + 1)
// Check sig.
if !sig.PubKey.VerifyBytes(msg.GetSignBytes(), sig.Signature) {
return ctx,
sdk.ErrUnauthorized("").Result(),
true
}
// Check sig.
if !sig.PubKey.VerifyBytes(tx.GetSignBytes(), sig.Signature) {
return ctx,
sdk.ErrUnauthorized("").Result(),
true
// Save the account.
accountMapper.SetAccount(ctx, signerAcc)
}
// Save the account.
accountMapper.SetAccount(ctx, signerAcc)
}
ctx = WithSigners(ctx, signerAccs)

View File

@ -5,19 +5,21 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
type CodeType = sdk.CodeType
const (
// Coin errors reserve 100 ~ 199.
CodeInvalidInput uint32 = 101
CodeInvalidOutput uint32 = 102
CodeInvalidAddress uint32 = 103
CodeUnknownAddress uint32 = 104
CodeInsufficientCoins uint32 = 105
CodeInvalidCoins uint32 = 106
CodeUnknownRequest uint32 = sdk.CodeUnknownRequest
CodeInvalidInput CodeType = 101
CodeInvalidOutput CodeType = 102
CodeInvalidAddress CodeType = 103
CodeUnknownAddress CodeType = 104
CodeInsufficientCoins CodeType = 105
CodeInvalidCoins CodeType = 106
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
)
// NOTE: Don't stringer this, we'll put better messages in later.
func codeToDefaultMsg(code uint32) string {
func codeToDefaultMsg(code CodeType) string {
switch code {
case CodeInvalidInput:
return "Invalid input coins"
@ -83,7 +85,7 @@ func ErrUnknownRequest(msg string) sdk.Error {
//----------------------------------------
func msgOrDefaultMsg(msg string, code uint32) string {
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
} else {
@ -91,7 +93,7 @@ func msgOrDefaultMsg(msg string, code uint32) string {
}
}
func newError(code uint32, msg string) sdk.Error {
func newError(code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
}

View File

@ -1,23 +1,23 @@
package bank
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Handle all "bank" type messages.
func NewHandler(am sdk.AccountMapper) sdk.Handler {
return func(ctx sdk.Context, tx sdk.Tx) sdk.Result {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
cm := CoinMapper{am}
msg := tx.(sdk.Msg)
switch msg := msg.(type) {
case SendMsg:
return handleSendMsg(ctx, cm, msg)
case IssueMsg:
return handleIssueMsg(ctx, cm, msg)
default:
errMsg := "Unrecognized bank Tx type: " + reflect.TypeOf(tx).Name()
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
@ -31,14 +31,14 @@ func handleSendMsg(ctx sdk.Context, cm CoinMapper, msg SendMsg) sdk.Result {
for _, in := range msg.Inputs {
_, err := cm.SubtractCoins(ctx, in.Address, in.Coins)
if err != nil {
return ErrInvalidInput("").TraceCause(err, "").Result()
return err.Result()
}
}
for _, out := range msg.Outputs {
_, err := cm.AddCoins(ctx, out.Address, out.Coins)
if err != nil {
return ErrInvalidOutput("").TraceCause(err, "").Result()
return err.Result()
}
}

View File

@ -13,10 +13,10 @@ type CoinMapper struct {
}
// SubtractCoins subtracts amt from the coins at the addr.
func (cm CoinMapper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt sdk.Coins) (sdk.Coins, error) {
func (cm CoinMapper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) {
acc := cm.am.GetAccount(ctx, addr)
if acc == nil {
return amt, fmt.Errorf("Sending account (%s) does not exist", addr)
return amt, sdk.ErrUnrecognizedAddress(addr)
}
coins := acc.GetCoins()
@ -31,7 +31,7 @@ func (cm CoinMapper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt sdk
}
// AddCoins adds amt to the coins at the addr.
func (cm CoinMapper) AddCoins(ctx sdk.Context, addr crypto.Address, amt sdk.Coins) (sdk.Coins, error) {
func (cm CoinMapper) AddCoins(ctx sdk.Context, addr crypto.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) {
acc := cm.am.GetAccount(ctx, addr)
if acc == nil {
acc = cm.am.NewAccountWithAddress(ctx, addr)