Add fee handler and signer handler to show how layering works

This commit is contained in:
Ethan Frey 2017-06-01 20:26:43 +02:00
parent f4393511ee
commit ab72ca881e
5 changed files with 138 additions and 27 deletions

View File

@ -11,8 +11,10 @@ const (
msgUnauthorized = "Unauthorized"
msgInvalidAddress = "Invalid Address"
msgInvalidCoins = "Invalid Coins"
msgInvalidFormat = "Invalid Format"
msgInvalidSequence = "Invalid Sequence"
msgInvalidSignature = "Invalid Signature"
msgInsufficientFees = "Insufficient Fees"
msgNoInputs = "No Input Coins"
msgNoOutputs = "No Output Coins"
msgTooLarge = "Input size too large"
@ -48,10 +50,18 @@ func InvalidCoins() TMError {
return New(msgInvalidCoins, abci.CodeType_BaseInvalidInput)
}
func InvalidFormat() TMError {
return New(msgInvalidFormat, abci.CodeType_BaseInvalidInput)
}
func InvalidSequence() TMError {
return New(msgInvalidSequence, abci.CodeType_BaseInvalidInput)
}
func InsufficientFees() TMError {
return New(msgInsufficientFees, abci.CodeType_BaseInvalidInput)
}
func NoInputs() TMError {
return New(msgNoInputs, abci.CodeType_BaseInvalidInput)
}

View File

@ -1,6 +1,8 @@
package basecoin
import (
"bytes"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire/data"
@ -38,6 +40,24 @@ func (c Context) GetSigners() []crypto.PubKey {
return c.sigs
}
func (c Context) IsSignerAddr(addr []byte) bool {
for _, pk := range c.sigs {
if bytes.Equal(addr, pk.Address()) {
return true
}
}
return false
}
func (c Context) IsSignerKey(key crypto.PubKey) bool {
for _, pk := range c.sigs {
if key.Equals(pk) {
return true
}
}
return false
}
// Result captures any non-error abci result
// to make sure people use error for error cases
type Result struct {

View File

@ -1,61 +1,77 @@
package handlers
import (
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/txs"
"github.com/tendermint/basecoin/types"
)
type SignedHandler struct {
AllowMultiSig bool
Inner basecoin.Handler
type AccountChecker interface {
// Get amount checks the current amount
GetAmount(store types.KVStore, addr []byte) (types.Coins, error)
// ChangeAmount modifies the balance by the given amount and returns the new balance
// always returns an error if leading to negative balance
ChangeAmount(store types.KVStore, addr []byte, coins types.Coins) (types.Coins, error)
}
func (h SignedHandler) Next() basecoin.Handler {
type SimpleFeeHandler struct {
AccountChecker
MinFee types.Coins
Inner basecoin.Handler
}
func (h SimpleFeeHandler) Next() basecoin.Handler {
return h.Inner
}
var _ basecoin.Handler = SignedHandler{}
var _ basecoin.Handler = SimpleFeeHandler{}
type Signed interface {
basecoin.TxLayer
Signers() ([]crypto.PubKey, error)
}
// Yes, I know refactor a bit... really too late already
func (h SignedHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
var sigs []crypto.PubKey
stx, ok := tx.Unwrap().(Signed)
func (h SimpleFeeHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
feeTx, ok := tx.Unwrap().(*txs.Fee)
if !ok {
return res, errors.InvalidFormat()
}
fees := types.Coins{feeTx.Fee}
if !fees.IsGTE(h.MinFee) {
return res, errors.InsufficientFees()
}
if !ctx.IsSignerAddr(feeTx.Payer) {
return res, errors.Unauthorized()
}
sigs, err = stx.Signers()
_, err = h.ChangeAmount(store, feeTx.Payer, fees.Negative())
if err != nil {
return res, err
}
// add the signers to the context and continue
ctx2 := ctx.AddSigners(sigs...)
return h.Next().CheckTx(ctx2, store, stx.Next())
return basecoin.Result{Log: "Valid tx"}, nil
}
func (h SignedHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
var sigs []crypto.PubKey
stx, ok := tx.Unwrap().(Signed)
func (h SimpleFeeHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
feeTx, ok := tx.Unwrap().(*txs.Fee)
if !ok {
return res, errors.InvalidFormat()
}
fees := types.Coins{feeTx.Fee}
if !fees.IsGTE(h.MinFee) {
return res, errors.InsufficientFees()
}
if !ctx.IsSignerAddr(feeTx.Payer) {
return res, errors.Unauthorized()
}
sigs, err = stx.Signers()
_, err = h.ChangeAmount(store, feeTx.Payer, fees.Negative())
if err != nil {
return res, err
}
// add the signers to the context and continue
ctx2 := ctx.AddSigners(sigs...)
return h.Next().DeliverTx(ctx2, store, stx.Next())
return h.Next().DeliverTx(ctx, store, feeTx.Next())
}

61
handlers/sigs.go Normal file
View File

@ -0,0 +1,61 @@
package handlers
import (
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/types"
)
type SignedHandler struct {
AllowMultiSig bool
Inner basecoin.Handler
}
func (h SignedHandler) Next() basecoin.Handler {
return h.Inner
}
var _ basecoin.Handler = SignedHandler{}
type Signed interface {
basecoin.TxLayer
Signers() ([]crypto.PubKey, error)
}
func (h SignedHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
var sigs []crypto.PubKey
stx, ok := tx.Unwrap().(Signed)
if !ok {
return res, errors.Unauthorized()
}
sigs, err = stx.Signers()
if err != nil {
return res, err
}
// add the signers to the context and continue
ctx2 := ctx.AddSigners(sigs...)
return h.Next().CheckTx(ctx2, store, stx.Next())
}
func (h SignedHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
var sigs []crypto.PubKey
stx, ok := tx.Unwrap().(Signed)
if !ok {
return res, errors.Unauthorized()
}
sigs, err = stx.Signers()
if err != nil {
return res, err
}
// add the signers to the context and continue
ctx2 := ctx.AddSigners(sigs...)
return h.Next().DeliverTx(ctx2, store, stx.Next())
}

View File

@ -86,6 +86,10 @@ func (f *Fee) Wrap() basecoin.Tx {
return basecoin.Tx{f}
}
func (f *Fee) Next() basecoin.Tx {
return f.Tx
}
/**** MultiTx ******/
type MultiTx struct {
Txs []basecoin.Tx `json:"txs"`