Skip to content

Commit

Permalink
fix and clean fees and x/auth
Browse files Browse the repository at this point in the history
  • Loading branch information
ebuchman committed Mar 17, 2018
1 parent 6dc4606 commit 3babf8c
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 118 deletions.
2 changes: 1 addition & 1 deletion client/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func SignAndBuild(msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
}}

// marshal bytes
tx := sdk.NewStdTx(signMsg.Msg, sigs)
tx := sdk.NewStdTx(signMsg.Msg, signMsg.Fee, sigs)

return cdc.MarshalBinary(tx)
}
Expand Down
21 changes: 14 additions & 7 deletions examples/basecoin/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ var (
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
}

sendMsg = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Expand Down Expand Up @@ -82,8 +86,8 @@ func TestMsgs(t *testing.T) {

sequences := []int64{0}
for i, m := range msgs {
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, m.msg))
tx := sdk.NewStdTx(m.msg, []sdk.StdSignature{{
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, m.msg))
tx := sdk.NewStdTx(m.msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
Expand Down Expand Up @@ -180,8 +184,8 @@ func TestSendMsgWithAccounts(t *testing.T) {

// Sign the tx
sequences := []int64{0}
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, sendMsg))
tx := sdk.NewStdTx(sendMsg, []sdk.StdSignature{{
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, sendMsg))
tx := sdk.NewStdTx(sendMsg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
Expand Down Expand Up @@ -213,7 +217,7 @@ func TestSendMsgWithAccounts(t *testing.T) {

// resigning the tx with the bumped sequence should work
sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, tx.Msg))
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)
Expand Down Expand Up @@ -269,10 +273,13 @@ func TestQuizMsg(t *testing.T) {

func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, expPass bool) {

// TODO:
var fee sdk.StdFee

// Sign the tx
tx := sdk.NewStdTx(msg, []sdk.StdSignature{{
tx := sdk.NewStdTx(msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, msg)),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, fee, msg)),
Sequence: seq,
}})

Expand Down
4 changes: 2 additions & 2 deletions examples/basecoin/x/cool/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (msg SetTrendMsg) String() string {
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg SetTrendMsg) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnrecognizedAddress(msg.Sender).Trace("")
return sdk.ErrUnrecognizedAddress(msg.Sender.String()).Trace("")
}
if strings.Contains(msg.Cool, "hot") {
return sdk.ErrUnauthorized("").Trace("hot is not cool")
Expand Down Expand Up @@ -88,7 +88,7 @@ func (msg QuizMsg) String() string {
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg QuizMsg) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnrecognizedAddress(msg.Sender).Trace("")
return sdk.ErrUnrecognizedAddress(msg.Sender.String()).Trace("")
}
return nil
}
Expand Down
12 changes: 6 additions & 6 deletions types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
CodeInsufficientFunds CodeType = 5
CodeUnknownRequest CodeType = 6
CodeUnrecognizedAddress CodeType = 7
CodeMissingPubKey CodeType = 8
CodeInvalidPubKey CodeType = 8

CodeGenesisParse CodeType = 0xdead // TODO: remove ?
)
Expand All @@ -51,7 +51,7 @@ func CodeToDefaultMsg(code CodeType) string {
return "Unknown request"
case CodeUnrecognizedAddress:
return "Unrecognized address"
case CodeMissingPubKey:
case CodeInvalidPubKey:
return "Missing pubkey"
default:
return fmt.Sprintf("Unknown code %d", code)
Expand Down Expand Up @@ -84,11 +84,11 @@ func ErrInsufficientFunds(msg string) Error {
func ErrUnknownRequest(msg string) Error {
return newError(CodeUnknownRequest, msg)
}
func ErrUnrecognizedAddress(addr Address) Error {
return newError(CodeUnrecognizedAddress, addr.String())
func ErrUnrecognizedAddress(msg string) Error {
return newError(CodeUnrecognizedAddress, msg)
}
func ErrMissingPubKey(addr Address) Error {
return newError(CodeMissingPubKey, addr.String())
func ErrInvalidPubKey(msg string) Error {
return newError(CodeInvalidPubKey, msg)
}

//----------------------------------------
Expand Down
61 changes: 37 additions & 24 deletions types/tx_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,22 @@ type Tx interface {

var _ Tx = (*StdTx)(nil)

// StdTx is a standard way to wrap a Msg with Signatures.
// StdTx is a standard way to wrap a Msg with Fee and Signatures.
// NOTE: the first signature is the FeePayer (Signatures must not be nil).
type StdTx struct {
Msg `json:"msg"`
Fee StdFee `json:"fee"`
Signatures []StdSignature `json:"signatures"`
}

func NewStdTx(msg Msg, sigs []StdSignature) StdTx {
func NewStdTx(msg Msg, fee StdFee, sigs []StdSignature) StdTx {
return StdTx{
Msg: msg,
Fee: fee,
Signatures: sigs,
}
}

// SetFee sets the StdFee on the transaction.
func (tx StdTx) SetFee(fee StdFee) StdTx {
tx.Fee = fee
return tx
}

//nolint
func (tx StdTx) GetMsg() Msg { return tx.Msg }
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }
Expand All @@ -77,6 +72,8 @@ func FeePayer(tx Tx) Address {
return tx.GetMsg().GetSigners()[0]
}

//__________________________________________________________

// StdFee includes the amount of coins paid in fees and the maximum
// gas to be used by the transaction. The ratio yields an effective "gasprice",
// which must be above some miminum to be accepted into the mempool.
Expand All @@ -92,6 +89,16 @@ func NewStdFee(gas int64, amount ...Coin) StdFee {
}
}

func (fee StdFee) Bytes() []byte {
bz, err := json.Marshal(fee) // TODO
if err != nil {
panic(err)
}
return bz
}

//__________________________________________________________

// StdSignDoc is replay-prevention structure.
// It includes the result of msg.GetSignBytes(),
// as well as the ChainID (prevent cross chain replay)
Expand All @@ -100,27 +107,18 @@ func NewStdFee(gas int64, amount ...Coin) StdFee {
type StdSignDoc struct {
ChainID string `json:"chain_id"`
Sequences []int64 `json:"sequences"`
FeeBytes []byte `json:"fee_bytes"`
MsgBytes []byte `json:"msg_bytes"`
AltBytes []byte `json:"alt_bytes"` // TODO: do we really want this ?
AltBytes []byte `json:"alt_bytes"`
}

// StdSignMsg is a convenience structure for passing along
// a Msg with the other requirements for a StdSignDoc before
// it is signed. For use in the CLI
type StdSignMsg struct {
ChainID string
Sequences []int64
Msg Msg
}

func (msg StdSignMsg) Bytes() []byte {
return StdSignBytes(msg.ChainID, msg.Sequences, msg.Msg)
}

func StdSignBytes(chainID string, sequences []int64, msg Msg) []byte {
// StdSignBytes returns the bytes to sign for a transaction.
// TODO: change the API to just take a chainID and StdTx ?
func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg Msg) []byte {
bz, err := json.Marshal(StdSignDoc{
ChainID: chainID,
Sequences: sequences,
FeeBytes: fee.Bytes(),
MsgBytes: msg.GetSignBytes(),
})
if err != nil {
Expand All @@ -129,7 +127,22 @@ func StdSignBytes(chainID string, sequences []int64, msg Msg) []byte {
return bz
}

//-------------------------------------
// StdSignMsg is a convenience structure for passing along
// a Msg with the other requirements for a StdSignDoc before
// it is signed. For use in the CLI.
type StdSignMsg struct {
ChainID string
Sequences []int64
Fee StdFee
Msg Msg
// XXX: Alt
}

func (msg StdSignMsg) Bytes() []byte {
return StdSignBytes(msg.ChainID, msg.Sequences, msg.Fee, msg.Msg)
}

//__________________________________________________________

// Application function variable used to unmarshal transaction bytes
type TxDecoder func(txBytes []byte) (Tx, Error)
85 changes: 49 additions & 36 deletions x/auth/ante.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package auth

import (
"bytes"
"fmt"
"reflect"

sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand Down Expand Up @@ -40,44 +40,61 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler {
true
}

// Get the sign bytes (requires all sequence numbers)
// Get the sign bytes (requires all sequence numbers and the fee)
sequences := make([]int64, len(signerAddrs))
for i := 0; i < len(signerAddrs); i++ {
sequences[i] = sigs[i].Sequence
}
signBytes := sdk.StdSignBytes(ctx.ChainID(), sequences, msg)
fee := stdTx.Fee
signBytes := sdk.StdSignBytes(ctx.ChainID(), sequences, fee, msg)

// Check sig and nonce and collect signer accounts.
var signerAccs = make([]sdk.Account, len(signerAddrs))
for i := 0; i < len(sigs); i++ {
isFeePayer := i == 0 // first sig pays the fees

signerAddr, sig := signerAddrs[i], sigs[i]
signerAcc, res := processSig(ctx, accountMapper, signerAddr, sig,
signBytes, isFeePayer, stdTx.Fee.Amount)

// check signature, return account with incremented nonce
signerAcc, res := processSig(
ctx, accountMapper,
signerAddr, sig, signBytes,
)
if !res.IsOK() {
return ctx, res, true
}

// first sig pays the fees
if i == 0 {
signerAcc, res = deductFees(signerAcc, fee)
if !res.IsOK() {
return ctx, res, true
}
}

// Save the account.
accountMapper.SetAccount(ctx, signerAcc)
signerAccs[i] = signerAcc
}

// cache the signer accounts in the context
ctx = WithSigners(ctx, signerAccs)

// TODO: tx tags (?)

return ctx, sdk.Result{}, false // continue...
}
}

// verify the signature and increment the sequence.
// if the account doesn't have a pubkey, set it.
// deduct fee from fee payer.
func processSig(ctx sdk.Context, am sdk.AccountMapper,
addr sdk.Address, sig sdk.StdSignature, signBytes []byte,
isFeePayer bool, feeAmount sdk.Coins) (acc sdk.Account, res sdk.Result) {
func processSig(
ctx sdk.Context, am sdk.AccountMapper,
addr sdk.Address, sig sdk.StdSignature, signBytes []byte) (
acc sdk.Account, res sdk.Result) {

// Get the account
// Get the account.
acc = am.GetAccount(ctx, addr)
if acc == nil {
return nil, sdk.ErrUnrecognizedAddress(addr).Result()
return nil, sdk.ErrUnrecognizedAddress(addr.String()).Result()
}

// Check and increment sequence number.
Expand All @@ -89,43 +106,39 @@ func processSig(ctx sdk.Context, am sdk.AccountMapper,
acc.SetSequence(seq + 1)

// If pubkey is not known for account,
// set it from the StdSignature
// set it from the StdSignature.
pubKey := acc.GetPubKey()
if pubKey.Empty() {
if sig.PubKey.Empty() {
return nil, sdk.ErrInternal("public Key not found").Result()
}
if !reflect.DeepEqual(sig.PubKey.Address(), addr) {
return nil, sdk.ErrInternal(
fmt.Sprintf("invalid PubKey for address %v", addr)).Result()
}
pubKey = sig.PubKey
if pubKey.Empty() {
return nil, sdk.ErrMissingPubKey(addr).Result()
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
}
if !bytes.Equal(pubKey.Address(), addr) {
return nil, sdk.ErrInvalidPubKey(
fmt.Sprintf("PubKey does not match Signer address %v", addr)).Result()
}
err := acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer").Result()
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
}
}
// Check sig.
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}

// If this is the fee payer, deduct the fee.
if isFeePayer {
coins := acc.GetCoins()
newCoins := coins.Minus(feeAmount)
if !newCoins.IsNotNegative() {
errMsg := fmt.Sprintf("%s < %s", coins, feeAmount)
return nil, sdk.ErrInsufficientFunds(errMsg).Result()
}
return
}

acc.SetCoins(newCoins)
// deduct the fee from the account
func deductFees(acc sdk.Account, fee sdk.StdFee) (sdk.Account, sdk.Result) {
coins := acc.GetCoins()
feeAmount := fee.Amount
newCoins := coins.Minus(feeAmount)
if !newCoins.IsNotNegative() {
errMsg := fmt.Sprintf("%s < %s", coins, feeAmount)
return nil, sdk.ErrInsufficientFunds(errMsg).Result()
}

// Save the account.
am.SetAccount(ctx, acc)
return
acc.SetCoins(newCoins)
return acc, sdk.Result{}
}
Loading

0 comments on commit 3babf8c

Please sign in to comment.