Counter uses dispatcher to deduct fees from account
This commit is contained in:
parent
8003034bbb
commit
670e7b48d1
|
@ -32,6 +32,7 @@ type Context interface {
|
|||
log.Logger
|
||||
WithPermissions(perms ...Actor) Context
|
||||
HasPermission(perm Actor) bool
|
||||
GetPermissions(chain, app string) []Actor
|
||||
IsParent(ctx Context) bool
|
||||
Reset() Context
|
||||
ChainID() string
|
||||
|
|
|
@ -74,6 +74,6 @@ func readCounterTxFlags() (tx basecoin.Tx, err error) {
|
|||
return tx, err
|
||||
}
|
||||
|
||||
tx = counter.NewCounterTx(viper.GetBool(FlagValid), feeCoins)
|
||||
tx = counter.NewCounterTx(viper.GetBool(FlagValid), feeCoins, viper.GetInt(FlagSequence))
|
||||
return tx, nil
|
||||
}
|
||||
|
|
|
@ -30,14 +30,16 @@ func init() {
|
|||
}
|
||||
|
||||
type CounterTx struct {
|
||||
Valid bool `json:"valid"`
|
||||
Fee types.Coins `json:"fee"`
|
||||
Valid bool `json:"valid"`
|
||||
Fee types.Coins `json:"fee"`
|
||||
Sequence int `json:"sequence"`
|
||||
}
|
||||
|
||||
func NewCounterTx(valid bool, fee types.Coins) basecoin.Tx {
|
||||
func NewCounterTx(valid bool, fee types.Coins, sequence int) basecoin.Tx {
|
||||
return CounterTx{
|
||||
Valid: valid,
|
||||
Fee: fee,
|
||||
Valid: valid,
|
||||
Fee: fee,
|
||||
Sequence: sequence,
|
||||
}.Wrap()
|
||||
}
|
||||
|
||||
|
@ -85,29 +87,31 @@ func NewCounterHandler() basecoin.Handler {
|
|||
counter := CounterHandler{}
|
||||
dispatcher := stack.NewDispatcher(
|
||||
stack.WrapHandler(coin),
|
||||
stack.WrapHandler(counter),
|
||||
counter,
|
||||
)
|
||||
return stack.NewDefault().Use(dispatcher)
|
||||
}
|
||||
|
||||
type CounterHandler struct {
|
||||
basecoin.NopOption
|
||||
stack.NopOption
|
||||
}
|
||||
|
||||
var _ basecoin.Handler = CounterHandler{}
|
||||
var _ stack.Dispatchable = CounterHandler{}
|
||||
|
||||
func (_ CounterHandler) Name() string {
|
||||
return NameCounter
|
||||
}
|
||||
|
||||
func (_ CounterHandler) AssertDispatcher() {}
|
||||
|
||||
// CheckTx checks if the tx is properly structured
|
||||
func (h CounterHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||
func (h CounterHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, _ basecoin.Checker) (res basecoin.Result, err error) {
|
||||
_, err = checkTx(ctx, tx)
|
||||
return
|
||||
}
|
||||
|
||||
// DeliverTx executes the tx if valid
|
||||
func (h CounterHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||
func (h CounterHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, dispatch basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
ctr, err := checkTx(ctx, tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
@ -118,8 +122,22 @@ func (h CounterHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx
|
|||
return res, ErrInvalidCounter()
|
||||
}
|
||||
|
||||
// TODO: handle coin movement.... ugh, need sequence to do this, right?
|
||||
// like, actually decrement the other account
|
||||
// handle coin movement.... like, actually decrement the other account
|
||||
if !ctr.Fee.IsZero() {
|
||||
// take the coins and put them in out account!
|
||||
senders := ctx.GetPermissions("", stack.NameSigs)
|
||||
if len(senders) == 0 {
|
||||
return res, errors.ErrMissingSignature()
|
||||
}
|
||||
in := []coin.TxInput{{Address: senders[0], Coins: ctr.Fee, Sequence: ctr.Sequence}}
|
||||
out := []coin.TxOutput{{Address: CounterAcct(), Coins: ctr.Fee}}
|
||||
send := coin.NewSendTx(in, out)
|
||||
// if the deduction fails (too high), abort the command
|
||||
_, err = dispatch.DeliverTx(ctx, store, send)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// update the counter
|
||||
state, err := LoadState(store)
|
||||
|
@ -148,6 +166,10 @@ func checkTx(ctx basecoin.Context, tx basecoin.Tx) (ctr CounterTx, err error) {
|
|||
// CounterStore
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
func CounterAcct() basecoin.Actor {
|
||||
return basecoin.Actor{App: NameCounter, Address: []byte{0x04, 0x20}}
|
||||
}
|
||||
|
||||
type CounterState struct {
|
||||
Counter int `json:"counter"`
|
||||
TotalFees types.Coins `json:"total_fees"`
|
||||
|
|
|
@ -2,6 +2,7 @@ package counter
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -21,10 +22,14 @@ func TestCounterPlugin(t *testing.T) {
|
|||
// Basecoin initialization
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
chainID := "test_chain_id"
|
||||
|
||||
// l := log.TestingLogger().With("module", "app"),
|
||||
l := log.NewTMLogger(os.Stdout).With("module", "app")
|
||||
// l = log.NewTracingLogger(l)
|
||||
bcApp := app.NewBasecoin(
|
||||
NewCounterHandler(),
|
||||
eyesCli,
|
||||
log.TestingLogger().With("module", "app"),
|
||||
l,
|
||||
)
|
||||
bcApp.SetOption("base/chain_id", chainID)
|
||||
// t.Log(bcApp.Info())
|
||||
|
@ -42,7 +47,7 @@ func TestCounterPlugin(t *testing.T) {
|
|||
|
||||
// Deliver a CounterTx
|
||||
DeliverCounterTx := func(valid bool, counterFee types.Coins, inputSequence int) abci.Result {
|
||||
tx := NewCounterTx(valid, counterFee)
|
||||
tx := NewCounterTx(valid, counterFee, inputSequence)
|
||||
tx = txs.NewChain(chainID, tx)
|
||||
stx := txs.NewSig(tx)
|
||||
txs.Sign(stx, test1PrivAcc.PrivKey)
|
||||
|
@ -50,19 +55,19 @@ func TestCounterPlugin(t *testing.T) {
|
|||
return bcApp.DeliverTx(txBytes)
|
||||
}
|
||||
|
||||
// Test a basic send, no fee
|
||||
res := DeliverCounterTx(true, types.Coins{}, 1)
|
||||
// Test a basic send, no fee (doesn't update sequence as no money spent)
|
||||
res := DeliverCounterTx(true, nil, 1)
|
||||
assert.True(res.IsOK(), res.String())
|
||||
|
||||
// Test an invalid send, no fee
|
||||
res = DeliverCounterTx(false, types.Coins{}, 1)
|
||||
res = DeliverCounterTx(false, nil, 1)
|
||||
assert.True(res.IsErr(), res.String())
|
||||
|
||||
// Test the fee
|
||||
res = DeliverCounterTx(true, types.Coins{{"gold", 100}}, 2)
|
||||
// Test the fee (increments sequence)
|
||||
res = DeliverCounterTx(true, types.Coins{{"gold", 100}}, 1)
|
||||
assert.True(res.IsOK(), res.String())
|
||||
|
||||
// TODO: Test unsupported fee
|
||||
// res = DeliverCounterTx(true, types.Coins{{"silver", 100}}, 3)
|
||||
// assert.True(res.IsErr(), res.String())
|
||||
// Test unsupported fee
|
||||
res = DeliverCounterTx(true, types.Coins{{"silver", 100}}, 2)
|
||||
assert.True(res.IsErr(), res.String())
|
||||
}
|
||||
|
|
|
@ -66,6 +66,17 @@ func (c secureContext) HasPermission(perm basecoin.Actor) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (c secureContext) GetPermissions(chain, app string) (res []basecoin.Actor) {
|
||||
for _, p := range c.perms {
|
||||
if chain == p.ChainID {
|
||||
if app == "" || app == p.App {
|
||||
res = append(res, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// IsParent ensures that this is derived from the given secureClient
|
||||
func (c secureContext) IsParent(other basecoin.Context) bool {
|
||||
so, ok := other.(secureContext)
|
||||
|
|
|
@ -56,8 +56,9 @@ func (d *Dispatcher) CheckTx(ctx basecoin.Context, store types.KVStore, tx basec
|
|||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.CheckTx(ctx, store, tx, nil)
|
||||
// TODO: check on callback
|
||||
cb := d
|
||||
return r.CheckTx(ctx, store, tx, cb)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||
|
@ -65,8 +66,9 @@ func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas
|
|||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.DeliverTx(ctx, store, tx, nil)
|
||||
// TODO: check on callback
|
||||
cb := d
|
||||
return r.DeliverTx(ctx, store, tx, cb)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) SetOption(l log.Logger, store types.KVStore, module, key, value string) (string, error) {
|
||||
|
@ -74,8 +76,9 @@ func (d *Dispatcher) SetOption(l log.Logger, store types.KVStore, module, key, v
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.SetOption(l, store, module, key, value, nil)
|
||||
// TODO: check on callback
|
||||
cb := d
|
||||
return r.SetOption(l, store, module, key, value, cb)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) lookupTx(tx basecoin.Tx) (Dispatchable, error) {
|
||||
|
|
|
@ -67,6 +67,12 @@ func (_ PassOption) SetOption(l log.Logger, store types.KVStore, module, key, va
|
|||
return next.SetOption(l, store, module, key, value)
|
||||
}
|
||||
|
||||
type NopOption struct{}
|
||||
|
||||
func (_ NopOption) SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Dispatchable is like middleware, except the meaning of "next" is different.
|
||||
// Whereas in the middleware, it is the next handler that we should pass the same tx into,
|
||||
// for dispatchers, it is a dispatcher, which it can use to
|
||||
|
|
|
@ -44,6 +44,17 @@ func (c mockContext) HasPermission(perm basecoin.Actor) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (c mockContext) GetPermissions(chain, app string) (res []basecoin.Actor) {
|
||||
for _, p := range c.perms {
|
||||
if chain == p.ChainID {
|
||||
if app == "" || app == p.App {
|
||||
res = append(res, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// IsParent ensures that this is derived from the given secureClient
|
||||
func (c mockContext) IsParent(other basecoin.Context) bool {
|
||||
_, ok := other.(mockContext)
|
||||
|
|
|
@ -70,8 +70,12 @@ test03AddCount() {
|
|||
HASH=$(echo $TX | jq .hash | tr -d \")
|
||||
TX_HEIGHT=$(echo $TX | jq .height)
|
||||
|
||||
# make sure the counter was updated
|
||||
checkCounter "1" "10"
|
||||
|
||||
# make sure the account was debited
|
||||
checkAccount $SENDER "2" "9007199254739990"
|
||||
|
||||
# make sure tx is indexed
|
||||
TX=$(${CLIENT_EXE} query tx $HASH --trace)
|
||||
if assertTrue "found tx" $?; then
|
||||
|
|
Loading…
Reference in New Issue