Vote->Counter; Fee is types.Coin; Context has Account; Cleanup

This commit is contained in:
Jae Kwon
2017-01-15 15:10:51 -08:00
parent 5ba8250efd
commit 16a6680e24
14 changed files with 319 additions and 286 deletions
+102
View File
@@ -0,0 +1,102 @@
package counter
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/basecoin/types"
"github.com/tendermint/go-wire"
)
type CounterPluginState struct {
Counter int
TotalCost types.Coins
}
type CounterTx struct {
Valid bool
Cost types.Coins
}
//--------------------------------------------------------------------------------
type CounterPlugin struct {
name string
}
func (cp *CounterPlugin) Name() string {
return cp.name
}
func (cp *CounterPlugin) StateKey() []byte {
return []byte(fmt.Sprintf("CounterPlugin{name=%v}.State", cp.name))
}
func NewCounterPlugin(name string) *CounterPlugin {
return &CounterPlugin{
name: name,
}
}
func (cp *CounterPlugin) SetOption(store types.KVStore, key string, value string) (log string) {
return ""
}
func (cp *CounterPlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
// Decode tx
var tx CounterTx
err := wire.ReadBinaryBytes(txBytes, &tx)
if err != nil {
return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
}
// Validate tx
if !tx.Valid {
return abci.ErrInternalError.AppendLog("CounterTx.Valid must be true")
}
if !tx.Cost.IsValid() {
return abci.ErrInternalError.AppendLog("CounterTx.Cost is not sorted or has zero amounts")
}
if !tx.Cost.IsNonnegative() {
return abci.ErrInternalError.AppendLog("CounterTx.Cost must be nonnegative")
}
// Did the caller provide enough coins?
if !ctx.Coins.IsGTE(tx.Cost) {
return abci.ErrInsufficientFunds.AppendLog("CounterTx.Cost was not provided")
}
// TODO If there are any funds left over, return funds.
// e.g. !ctx.Coins.Minus(tx.Cost).IsZero()
// ctx.CallerAccount is synced w/ store, so just modify that and store it.
// Load CounterPluginState
var cpState CounterPluginState
cpStateBytes := store.Get(cp.StateKey())
if len(cpStateBytes) > 0 {
err = wire.ReadBinaryBytes(cpStateBytes, &cpState)
if err != nil {
return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error())
}
}
// Update CounterPluginState
cpState.Counter += 1
cpState.TotalCost = cpState.TotalCost.Plus(tx.Cost)
// Save CounterPluginState
store.Set(cp.StateKey(), wire.BinaryBytes(cpState))
return abci.OK
}
func (cp *CounterPlugin) InitChain(store types.KVStore, vals []*abci.Validator) {
}
func (cp *CounterPlugin) BeginBlock(store types.KVStore, height uint64) {
}
func (cp *CounterPlugin) EndBlock(store types.KVStore, height uint64) []*abci.Validator {
return nil
}
+99
View File
@@ -0,0 +1,99 @@
package counter
import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/basecoin/app"
"github.com/tendermint/basecoin/testutils"
"github.com/tendermint/basecoin/types"
"github.com/tendermint/go-wire"
eyescli "github.com/tendermint/merkleeyes/client"
)
func TestCounterPlugin(t *testing.T) {
// Basecoin initialization
eyesCli := eyescli.NewLocalClient()
chainID := "test_chain_id"
bcApp := app.NewBasecoin(eyesCli)
bcApp.SetOption("base/chainID", chainID)
t.Log(bcApp.Info())
// Add Counter plugin
counterPluginName := "testcounter"
counterPlugin := NewCounterPlugin(counterPluginName)
bcApp.RegisterPlugin(counterPlugin)
// Account initialization
test1PrivAcc := testutils.PrivAccountFromSecret("test1")
// Seed Basecoin with account
test1Acc := test1PrivAcc.Account
test1Acc.Balance = types.Coins{{"", 1000}, {"gold", 1000}}
bcApp.SetOption("base/account", string(wire.JSONBytes(test1Acc)))
// Deliver a CounterTx
DeliverCounterTx := func(gas int64, fee types.Coin, inputCoins types.Coins, inputSequence int, cost types.Coins) abci.Result {
// Construct an AppTx signature
tx := &types.AppTx{
Gas: gas,
Fee: fee,
Name: counterPluginName,
Input: types.NewTxInput(test1Acc.PubKey, inputCoins, inputSequence),
Data: wire.BinaryBytes(CounterTx{Valid: true, Cost: cost}),
}
// Sign request
signBytes := tx.SignBytes(chainID)
t.Logf("Sign bytes: %X\n", signBytes)
sig := test1PrivAcc.PrivKey.Sign(signBytes)
tx.Input.Signature = sig
t.Logf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
// Write request
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
return bcApp.DeliverTx(txBytes)
}
// REF: DeliverCounterTx(gas, fee, inputCoins, inputSequence, cost) {
// Test a basic send, no fee
res := DeliverCounterTx(0, types.Coin{}, types.Coins{{"", 1}}, 1, types.Coins{})
assert.True(t, res.IsOK(), res.String())
// Test fee prevented transaction
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 1}}, 2, types.Coins{})
assert.True(t, res.IsErr(), res.String())
// Test input equals fee
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 2}}, 2, types.Coins{})
assert.True(t, res.IsOK(), res.String())
// Test more input than fee
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 3}}, 3, types.Coins{})
assert.True(t, res.IsOK(), res.String())
// Test input equals fee+cost
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 4, types.Coins{{"", 2}, {"gold", 1}})
assert.True(t, res.IsOK(), res.String())
// Test fee+cost prevented transaction, not enough ""
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 2}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 1}})
assert.True(t, res.IsErr(), res.String())
// Test fee+cost prevented transaction, not enough "gold"
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 2}})
assert.True(t, res.IsErr(), res.String())
// Test more input than fee, more ""
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 4}, {"gold", 1}}, 6, types.Coins{{"", 2}, {"gold", 1}})
assert.True(t, res.IsOK(), res.String())
// Test more input than fee, more "gold"
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 2}}, 7, types.Coins{{"", 2}, {"gold", 1}})
assert.True(t, res.IsOK(), res.String())
// REF: DeliverCounterTx(gas, fee, inputCoins, inputSequence, cost) {
}
-92
View File
@@ -1,92 +0,0 @@
package vote
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/basecoin/types"
"github.com/tendermint/go-wire"
)
type Vote struct {
bb *ballotBox
}
type ballotBox struct {
issue string
votesYes int
votesNo int
}
type Tx struct {
voteYes bool
}
func NewVoteInstance(issue string) Vote {
return Vote{
&ballotBox{
issue: issue,
votesYes: 0,
votesNo: 0,
},
}
}
func (app Vote) SetOption(store types.KVStore, key string, value string) (log string) {
return ""
}
//because no coins are being exchanged ctx is unused
func (app Vote) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
// Decode tx
var tx Tx
err := wire.ReadBinaryBytes(txBytes, &tx)
if err != nil {
return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
}
//Read the ballotBox from the store
kvBytes := store.Get([]byte(app.bb.issue))
var tempBB ballotBox
//does the issue already exist?
if kvBytes != nil {
err := wire.ReadBinaryBytes(kvBytes, &tempBB)
if err != nil {
return abci.ErrBaseEncodingError.AppendLog("Error decoding BallotBox: " + err.Error())
}
} else {
//TODO add extra fee for opening new issue
tempBB = ballotBox{
issue: app.bb.issue,
votesYes: 0,
votesNo: 0,
}
issueBytes := wire.BinaryBytes(struct{ ballotBox }{tempBB})
store.Set([]byte(app.bb.issue), issueBytes)
}
//Write the updated ballotBox to the store
if tx.voteYes {
tempBB.votesYes += 1
} else {
tempBB.votesNo += 1
}
issueBytes := wire.BinaryBytes(struct{ ballotBox }{tempBB})
store.Set([]byte(app.bb.issue), issueBytes)
return abci.OK
}
//unused
func (app Vote) InitChain(store types.KVStore, vals []*abci.Validator) {
}
func (app Vote) BeginBlock(store types.KVStore, height uint64) {
}
func (app Vote) EndBlock(store types.KVStore, height uint64) []*abci.Validator {
var diffs []*abci.Validator
return diffs
}
-100
View File
@@ -1,100 +0,0 @@
package vote
import (
"fmt"
"testing"
"github.com/tendermint/basecoin/app"
cmn "github.com/tendermint/basecoin/common"
"github.com/tendermint/basecoin/types"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
eyescli "github.com/tendermint/merkleeyes/client"
)
const PluginNameVote = "vote"
func TestVote(t *testing.T) {
//base initialization
eyesCli := eyescli.NewLocalClient()
chainID := "test_chain_id"
bcApp := app.NewBasecoin(eyesCli)
bcApp.SetOption("base/chainID", chainID)
fmt.Println(bcApp.Info())
//account initialization
test1PrivAcc := cmn.PrivAccountFromSecret("test1")
// Seed Basecoin with account
test1Acc := test1PrivAcc.Account
test1Acc.Balance = types.Coins{{"", 1000}}
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(test1Acc))))
//vote initialization
votePlugin := NewVoteInstance("humanRights")
bcApp.RegisterPlugin(
PluginNameVote,
votePlugin,
)
//commit
res := bcApp.Commit()
if res.IsErr() {
Exit(Fmt("Failed Commit: %v", res.Error()))
}
//transaction sequence number
seqNum := 1
//Construct, Sign, Write function variable
CSW := func(fees, sendCoins int64) {
// Construct an AppTx signature
tx := &types.AppTx{
Fee: fees,
Gas: 0,
Name: PluginNameVote,
Input: cmn.MakeInput(test1Acc.PubKey, types.Coins{{"", sendCoins}}, seqNum),
Data: wire.BinaryBytes(struct{ Tx }{Tx{voteYes: true}}), //a vote for human rights
}
// Sign request
signBytes := tx.SignBytes(chainID)
fmt.Printf("Sign bytes: %X\n", signBytes)
sig := test1PrivAcc.PrivKey.Sign(signBytes)
tx.Input.Signature = sig
fmt.Printf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
// Write request
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
res = bcApp.DeliverTx(txBytes)
fmt.Println(res)
if res.IsOK() {
seqNum += 1
}
}
//Test a basic send, no fees
CSW(0, 1)
if res.IsErr() {
Exit(Fmt("Failed: %v", res.Error()))
}
//Test fee prevented transaction
CSW(2, 1)
if res.IsOK() {
Exit(Fmt("expected bad transaction"))
}
//Test equal fees
CSW(2, 2)
if res.IsErr() {
Exit(Fmt("Failed: %v", res.Error()))
}
//Test more send coins than fees
CSW(2, 3)
if res.IsErr() {
Exit(Fmt("Failed: %v", res.Error()))
}
}