diff --git a/app/app.go b/app/app.go index ef6cb37760..a2a824814f 100644 --- a/app/app.go +++ b/app/app.go @@ -41,8 +41,8 @@ func (app *Basecoin) Info() abci.ResponseInfo { return abci.ResponseInfo{Data: Fmt("Basecoin v%v", version)} } -func (app *Basecoin) RegisterPlugin(name string, plugin types.Plugin) { - app.plugins.RegisterPlugin(name, plugin) +func (app *Basecoin) RegisterPlugin(plugin types.Plugin) { + app.plugins.RegisterPlugin(plugin) } // TMSP::SetOption @@ -144,21 +144,21 @@ func (app *Basecoin) Commit() (res abci.Result) { // TMSP::InitChain func (app *Basecoin) InitChain(validators []*abci.Validator) { for _, plugin := range app.plugins.GetList() { - plugin.Plugin.InitChain(app.state, validators) + plugin.InitChain(app.state, validators) } } // TMSP::BeginBlock func (app *Basecoin) BeginBlock(height uint64) { for _, plugin := range app.plugins.GetList() { - plugin.Plugin.BeginBlock(app.state, height) + plugin.BeginBlock(app.state, height) } } // TMSP::EndBlock func (app *Basecoin) EndBlock(height uint64) (diffs []*abci.Validator) { for _, plugin := range app.plugins.GetList() { - moreDiffs := plugin.Plugin.EndBlock(app.state, height) + moreDiffs := plugin.EndBlock(app.state, height) diffs = append(diffs, moreDiffs...) } return diff --git a/app/tmsp_test.go b/app/tmsp_test.go index a96ec24d2b..8f4b3a2596 100644 --- a/app/tmsp_test.go +++ b/app/tmsp_test.go @@ -3,9 +3,9 @@ package app import ( "testing" - cmn "github.com/tendermint/basecoin/common" + "github.com/tendermint/basecoin/testutils" "github.com/tendermint/basecoin/types" - . "github.com/tendermint/go-common" + cmn "github.com/tendermint/go-common" "github.com/tendermint/go-wire" eyescli "github.com/tendermint/merkleeyes/client" ) @@ -17,8 +17,8 @@ func TestSendTx(t *testing.T) { bcApp.SetOption("base/chainID", chainID) t.Log(bcApp.Info()) - test1PrivAcc := cmn.PrivAccountFromSecret("test1") - test2PrivAcc := cmn.PrivAccountFromSecret("test2") + test1PrivAcc := testutils.PrivAccountFromSecret("test1") + test2PrivAcc := testutils.PrivAccountFromSecret("test2") // Seed Basecoin with account test1Acc := test1PrivAcc.Account @@ -27,15 +27,15 @@ func TestSendTx(t *testing.T) { res := bcApp.Commit() if res.IsErr() { - Exit(Fmt("Failed Commit: %v", res.Error())) + cmn.Exit(cmn.Fmt("Failed Commit: %v", res.Error())) } // Construct a SendTx signature tx := &types.SendTx{ - Fee: 0, Gas: 0, + Fee: types.Coin{"", 0}, Inputs: []types.TxInput{ - cmn.MakeInput(test1PrivAcc.Account.PubKey, types.Coins{{"", 1}}, 1), + types.NewTxInput(test1PrivAcc.Account.PubKey, types.Coins{{"", 1}}, 1), }, Outputs: []types.TxOutput{ types.TxOutput{ @@ -57,7 +57,7 @@ func TestSendTx(t *testing.T) { res = bcApp.DeliverTx(txBytes) t.Log(res) if res.IsErr() { - t.Errorf(Fmt("Failed: %v", res.Error())) + t.Errorf("Failed: %v", res.Error()) } } @@ -69,19 +69,19 @@ func TestSequence(t *testing.T) { t.Log(bcApp.Info()) // Get the test account - test1PrivAcc := cmn.PrivAccountFromSecret("test1") + test1PrivAcc := testutils.PrivAccountFromSecret("test1") test1Acc := test1PrivAcc.Account test1Acc.Balance = types.Coins{{"", 1 << 53}} t.Log(bcApp.SetOption("base/account", string(wire.JSONBytes(test1Acc)))) res := bcApp.Commit() if res.IsErr() { - t.Errorf(Fmt("Failed Commit: %v", res.Error())) + t.Errorf("Failed Commit: %v", res.Error()) } sequence := int(1) // Make a bunch of PrivAccounts - privAccounts := cmn.RandAccounts(1000, 1000000, 0) + privAccounts := testutils.RandAccounts(1000, 1000000, 0) privAccountSequences := make(map[string]int) // Send coins to each account @@ -89,10 +89,10 @@ func TestSequence(t *testing.T) { privAccount := privAccounts[i] tx := &types.SendTx{ - Fee: 2, Gas: 2, + Fee: types.Coin{"", 2}, Inputs: []types.TxInput{ - cmn.MakeInput(test1Acc.PubKey, types.Coins{{"", 1000002}}, sequence), + types.NewTxInput(test1Acc.PubKey, types.Coins{{"", 1000002}}, sequence), }, Outputs: []types.TxOutput{ types.TxOutput{ @@ -122,13 +122,13 @@ func TestSequence(t *testing.T) { res = bcApp.Commit() if res.IsErr() { - t.Errorf(Fmt("Failed Commit: %v", res.Error())) + t.Errorf("Failed Commit: %v", res.Error()) } // Now send coins between these accounts for i := 0; i < 10000; i++ { - randA := RandInt() % len(privAccounts) - randB := RandInt() % len(privAccounts) + randA := cmn.RandInt() % len(privAccounts) + randB := cmn.RandInt() % len(privAccounts) if randA == randB { continue } @@ -139,10 +139,10 @@ func TestSequence(t *testing.T) { privAccountB := privAccounts[randB] tx := &types.SendTx{ - Fee: 2, Gas: 2, + Fee: types.Coin{"", 2}, Inputs: []types.TxInput{ - cmn.MakeInput(privAccountA.Account.PubKey, types.Coins{{"", 3}}, privAccountASequence+1), + types.NewTxInput(privAccountA.Account.PubKey, types.Coins{{"", 3}}, privAccountASequence+1), }, Outputs: []types.TxOutput{ types.TxOutput{ diff --git a/glide.lock b/glide.lock index 8be4a107d7..948a5da3ba 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 3869944d14a8df914ffcad02c2ef3548173daba51c5ea697767f8af77c07b348 -updated: 2017-01-14T20:46:16.999965963-08:00 +updated: 2017-01-15T14:45:40.368426139-08:00 imports: - name: github.com/btcsuite/btcd version: afec1bd1245a4a19e6dfe1306974b733e7cbb9b8 @@ -41,7 +41,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 6526ab2137fadd0f4d2e25002bbfc1784b4f3c27 + version: 05096de3687ac582bec63860b3dd384acd9149aa subpackages: - client - server @@ -68,9 +68,9 @@ imports: - name: github.com/tendermint/go-logger version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2 - name: github.com/tendermint/go-merkle - version: 1bc9a24706d715cd45403593c6d0a870d70011bb + version: 2979c7eb8aa020fa1cf203654907dbb889703888 - name: github.com/tendermint/go-p2p - version: eab2baa363de01b052b88c559e803776cd2c7dd6 + version: 67c9086b7458eb45b1970483decd01cd744c477a subpackages: - upnp - name: github.com/tendermint/go-rpc @@ -85,12 +85,12 @@ imports: subpackages: - term - name: github.com/tendermint/merkleeyes - version: db66769b34a950bb588919c94925724f21583925 + version: 2cf87e5f049ab6131aa4ea188c1b5b629d9b3bf9 subpackages: - app - client - name: github.com/tendermint/tendermint - version: 0aecfe2dae3269a9450b5d8b3bac8721a8dde7c7 + version: cf0cb9558aaecbf3ddb071eb863df77e55d828ed subpackages: - rpc/core/types - types @@ -131,4 +131,16 @@ imports: - stats - tap - transport -testImports: [] +testImports: +- name: github.com/davecgh/go-spew + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert diff --git a/plugins/counter/counter.go b/plugins/counter/counter.go new file mode 100644 index 0000000000..61945f20c1 --- /dev/null +++ b/plugins/counter/counter.go @@ -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 +} diff --git a/plugins/counter/counter_test.go b/plugins/counter/counter_test.go new file mode 100644 index 0000000000..b9aa889464 --- /dev/null +++ b/plugins/counter/counter_test.go @@ -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) { +} diff --git a/plugins/vote/vote.go b/plugins/vote/vote.go deleted file mode 100644 index b4eb612571..0000000000 --- a/plugins/vote/vote.go +++ /dev/null @@ -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 -} diff --git a/plugins/vote/vote_test.go b/plugins/vote/vote_test.go deleted file mode 100644 index 49548a8c0e..0000000000 --- a/plugins/vote/vote_test.go +++ /dev/null @@ -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())) - } -} diff --git a/state/execution.go b/state/execution.go index 13f79008b4..6e722bdc9c 100644 --- a/state/execution.go +++ b/state/execution.go @@ -10,8 +10,6 @@ import ( // If the tx is invalid, a TMSP error will be returned. func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) abci.Result { - // TODO: do something with fees - fees := types.Coins{} chainID := state.GetChainID() // Exec tx @@ -46,10 +44,9 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e return res.PrependLog("in validateInputsAdvanced()") } outTotal := sumOutputs(tx.Outputs) - if !inTotal.IsEqual(outTotal.Plus(types.Coins{{"", tx.Fee}})) { + if !inTotal.IsEqual(outTotal.Plus(types.Coins{tx.Fee})) { return abci.ErrBaseInvalidOutput.AppendLog("Input total != output total + fees") } - fees = fees.Plus(types.Coins{{"", tx.Fee}}) // TODO: Fee validation for SendTx @@ -96,7 +93,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e log.Info(Fmt("validateInputAdvanced failed on %X: %v", tx.Input.Address, res)) return res.PrependLog("in validateInputAdvanced()") } - if !tx.Input.Coins.IsGTE(types.Coins{{"", tx.Fee}}) { + if !tx.Input.Coins.IsGTE(types.Coins{tx.Fee}) { log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address)) return abci.ErrBaseInsufficientFunds } @@ -109,7 +106,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e } // Good! - coins := tx.Input.Coins.Minus(types.Coins{{"", tx.Fee}}) + coins := tx.Input.Coins.Minus(types.Coins{tx.Fee}) inAcc.Sequence += 1 inAcc.Balance = inAcc.Balance.Minus(tx.Input.Coins) @@ -120,13 +117,12 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e } // Create inAcc checkpoint - inAccDeducted := inAcc.Copy() + inAccCopy := inAcc.Copy() // Run the tx. - // XXX cache := types.NewStateCache(state) cache := state.CacheWrap() cache.SetAccount(tx.Input.Address, inAcc) - ctx := types.NewCallContext(tx.Input.Address, coins) + ctx := types.NewCallContext(tx.Input.Address, inAcc, coins) res = plugin.RunTx(cache, ctx, tx.Data) if res.IsOK() { cache.CacheSync() @@ -145,10 +141,10 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e } else { log.Info("AppTx failed", "error", res) // Just return the coins and return. - inAccDeducted.Balance = inAccDeducted.Balance.Plus(coins) + inAccCopy.Balance = inAccCopy.Balance.Plus(coins) // But take the gas // TODO - state.SetAccount(tx.Input.Address, inAccDeducted) + state.SetAccount(tx.Input.Address, inAccCopy) } return res diff --git a/tests/tendermint/main.go b/tests/tendermint/main.go index 91ed52e7b9..1776952b3b 100644 --- a/tests/tendermint/main.go +++ b/tests/tendermint/main.go @@ -5,9 +5,9 @@ import ( "time" "github.com/gorilla/websocket" - cmn "github.com/tendermint/basecoin/common" + "github.com/tendermint/basecoin/testutils" "github.com/tendermint/basecoin/types" - . "github.com/tendermint/go-common" + cmn "github.com/tendermint/go-common" "github.com/tendermint/go-rpc/client" "github.com/tendermint/go-rpc/types" "github.com/tendermint/go-wire" @@ -21,7 +21,7 @@ func main() { _, err := ws.Start() if err != nil { - Exit(err.Error()) + cmn.Exit(err.Error()) } var counter = 0 @@ -32,15 +32,15 @@ func main() { if !ok { break } - fmt.Println(counter, "res:", Blue(string(res))) + fmt.Println(counter, "res:", cmn.Blue(string(res))) } }() // Get the root account - root := cmn.PrivAccountFromSecret("test") + root := testutils.PrivAccountFromSecret("test") sequence := int(0) // Make a bunch of PrivAccounts - privAccounts := cmn.RandAccounts(1000, 1000000, 0) + privAccounts := testutils.RandAccounts(1000, 1000000, 0) privAccountSequences := make(map[string]int) // Send coins to each account @@ -72,12 +72,12 @@ func main() { // Write request txBytes := wire.BinaryBytes(struct{ types.Tx }{tx}) - request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", Arr(txBytes)) + request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", cmn.Arr(txBytes)) reqBytes := wire.JSONBytes(request) //fmt.Print(".") err := ws.WriteMessage(websocket.TextMessage, reqBytes) if err != nil { - Exit("writing websocket request: " + err.Error()) + cmn.Exit("writing websocket request: " + err.Error()) } } @@ -86,8 +86,8 @@ func main() { counter += 1 time.Sleep(time.Millisecond * 10) - randA := RandInt() % len(privAccounts) - randB := RandInt() % len(privAccounts) + randA := cmn.RandInt() % len(privAccounts) + randB := cmn.RandInt() % len(privAccounts) if randA == randB { continue } @@ -122,12 +122,12 @@ func main() { // Write request txBytes := wire.BinaryBytes(struct{ types.Tx }{tx}) - request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", Arr(txBytes)) + request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", cmn.Arr(txBytes)) reqBytes := wire.JSONBytes(request) //fmt.Print(".") err := ws.WriteMessage(websocket.TextMessage, reqBytes) if err != nil { - Exit("writing websocket request: " + err.Error()) + cmn.Exit("writing websocket request: " + err.Error()) } } diff --git a/common/testing.go b/testutils/testing.go similarity index 73% rename from common/testing.go rename to testutils/testing.go index 918494e60c..f91394a0e1 100644 --- a/common/testing.go +++ b/testutils/testing.go @@ -1,5 +1,5 @@ -//functions used in testing throughout -package common +// Functions used in testing throughout +package testutils import ( "github.com/tendermint/basecoin/types" @@ -45,17 +45,3 @@ func RandAccounts(num int, minAmount int64, maxAmount int64) []types.PrivAccount return privAccs } - -//make input term for the AppTx or SendTx Types -func MakeInput(pubKey crypto.PubKey, coins types.Coins, sequence int) types.TxInput { - input := types.TxInput{ - Address: pubKey.Address(), - PubKey: pubKey, - Coins: coins, - Sequence: sequence, - } - if sequence > 1 { - input.PubKey = nil - } - return input -} diff --git a/types/coin.go b/types/coin.go index bc2bd806e1..94e38b7011 100644 --- a/types/coin.go +++ b/types/coin.go @@ -128,3 +128,15 @@ func (coins Coins) IsPositive() bool { } return true } + +func (coins Coins) IsNonnegative() bool { + if len(coins) == 0 { + return true + } + for _, coinAmount := range coins { + if coinAmount.Amount < 0 { + return false + } + } + return true +} diff --git a/types/plugin.go b/types/plugin.go index e8b71d33c1..09d68d4de0 100644 --- a/types/plugin.go +++ b/types/plugin.go @@ -1,10 +1,12 @@ package types import ( + "fmt" abci "github.com/tendermint/abci/types" ) type Plugin interface { + Name() string SetOption(store KVStore, key string, value string) (log string) RunTx(store KVStore, ctx CallContext, txBytes []byte) (res abci.Result) InitChain(store KVStore, vals []*abci.Validator) @@ -12,22 +14,21 @@ type Plugin interface { EndBlock(store KVStore, height uint64) []*abci.Validator } -type NamedPlugin struct { - Name string - Plugin -} - //---------------------------------------- +// CallContext.Caller's coins have been deducted by CallContext.Coins +// Caller's Sequence has been incremented. type CallContext struct { - Caller []byte - Coins Coins + CallerAddress []byte + CallerAccount *Account + Coins Coins } -func NewCallContext(caller []byte, coins Coins) CallContext { +func NewCallContext(callerAddress []byte, callerAccount *Account, coins Coins) CallContext { return CallContext{ - Caller: caller, - Coins: coins, + CallerAddress: callerAddress, + CallerAccount: callerAccount, + Coins: coins, } } @@ -35,7 +36,7 @@ func NewCallContext(caller []byte, coins Coins) CallContext { type Plugins struct { byName map[string]Plugin - plist []NamedPlugin + plist []Plugin } func NewPlugins() *Plugins { @@ -44,18 +45,22 @@ func NewPlugins() *Plugins { } } -func (pgz *Plugins) RegisterPlugin(name string, plugin Plugin) { +func (pgz *Plugins) RegisterPlugin(plugin Plugin) { + name := plugin.Name() + if name == "" { + panic("Plugin name cannot be blank") + } + if _, exists := pgz.byName[name]; exists { + panic(fmt.Sprintf("Plugin already exists by the name of %v", name)) + } pgz.byName[name] = plugin - pgz.plist = append(pgz.plist, NamedPlugin{ - Name: name, - Plugin: plugin, - }) + pgz.plist = append(pgz.plist, plugin) } func (pgz *Plugins) GetByName(name string) Plugin { return pgz.byName[name] } -func (pgz *Plugins) GetList() []NamedPlugin { +func (pgz *Plugins) GetList() []Plugin { return pgz.plist } diff --git a/types/tx.go b/types/tx.go index 5ac48edf1a..b8757e44aa 100644 --- a/types/tx.go +++ b/types/tx.go @@ -75,6 +75,19 @@ func (txIn TxInput) String() string { return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Coins, txIn.Sequence, txIn.Signature, txIn.PubKey) } +func NewTxInput(pubKey crypto.PubKey, coins Coins, sequence int) TxInput { + input := TxInput{ + Address: pubKey.Address(), + PubKey: pubKey, + Coins: coins, + Sequence: sequence, + } + if sequence > 1 { + input.PubKey = nil + } + return input +} + //----------------------------------------------------------------------------- type TxOutput struct { @@ -102,8 +115,8 @@ func (txOut TxOutput) String() string { //----------------------------------------------------------------------------- type SendTx struct { - Fee int64 `json:"fee"` // Fee Gas int64 `json:"gas"` // Gas + Fee Coin `json:"fee"` // Fee Inputs []TxInput `json:"inputs"` Outputs []TxOutput `json:"outputs"` } @@ -133,14 +146,14 @@ func (tx *SendTx) SetSignature(addr []byte, sig crypto.Signature) bool { } func (tx *SendTx) String() string { - return Fmt("SendTx{%v/%v %v->%v}", tx.Fee, tx.Gas, tx.Inputs, tx.Outputs) + return Fmt("SendTx{%v/%v %v->%v}", tx.Gas, tx.Fee, tx.Inputs, tx.Outputs) } //----------------------------------------------------------------------------- type AppTx struct { - Fee int64 `json:"fee"` // Fee Gas int64 `json:"gas"` // Gas + Fee Coin `json:"fee"` // Fee Name string `json:"type"` // Which plugin Input TxInput `json:"input"` // Hmmm do we want coins? Data []byte `json:"data"` @@ -161,7 +174,7 @@ func (tx *AppTx) SetSignature(sig crypto.Signature) bool { } func (tx *AppTx) String() string { - return Fmt("AppTx{%v/%v %v %v %X}", tx.Fee, tx.Gas, tx.Name, tx.Input, tx.Data) + return Fmt("AppTx{%v/%v %v %v %X}", tx.Gas, tx.Fee, tx.Name, tx.Input, tx.Data) } //----------------------------------------------------------------------------- diff --git a/types/tx_test.go b/types/tx_test.go index 3a35b7ec7e..43bcc032c6 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -10,8 +10,8 @@ var chainID string = "test_chain" func TestSendTxSignable(t *testing.T) { sendTx := &SendTx{ - Fee: 111, Gas: 222, + Fee: Coin{"", 111}, Inputs: []TxInput{ TxInput{ Address: []byte("input1"), @@ -37,7 +37,7 @@ func TestSendTxSignable(t *testing.T) { } signBytes := sendTx.SignBytes(chainID) signBytesHex := Fmt("%X", signBytes) - expected := "010A746573745F636861696E01000000000000006F00000000000000DE01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC" + expected := "010A746573745F636861696E0100000000000000DE00000000000000006F01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC" if signBytesHex != expected { t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex) } @@ -45,8 +45,8 @@ func TestSendTxSignable(t *testing.T) { func TestAppTxSignable(t *testing.T) { callTx := &AppTx{ - Fee: 111, Gas: 222, + Fee: Coin{"", 111}, Name: "X", Input: TxInput{ Address: []byte("input1"), @@ -57,7 +57,7 @@ func TestAppTxSignable(t *testing.T) { } signBytes := callTx.SignBytes(chainID) signBytesHex := Fmt("%X", signBytes) - expected := "010A746573745F636861696E01000000000000006F00000000000000DE0101580106696E70757431010100000000000000303903010932000001056461746131" + expected := "010A746573745F636861696E0100000000000000DE00000000000000006F0101580106696E70757431010100000000000000303903010932000001056461746131" if signBytesHex != expected { t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex) }