diff --git a/app/app.go b/app/app.go index 228a46f0c..c96be39dd 100644 --- a/app/app.go +++ b/app/app.go @@ -1,6 +1,8 @@ package app import ( + "strings" + "github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/types" . "github.com/tendermint/go-common" @@ -22,7 +24,7 @@ type Basecoin struct { func NewBasecoin(eyesCli *eyes.Client) *Basecoin { state_ := state.NewState(eyesCli) govMint := gov.NewGovernmint(eyesCli) - state_.RegisterPlugin([]byte("gov"), govMint) + state_.RegisterPlugin("GOV", govMint) return &Basecoin{ eyesCli: eyesCli, govMint: govMint, @@ -37,25 +39,37 @@ func (app *Basecoin) Info() string { // TMSP::SetOption func (app *Basecoin) SetOption(key string, value string) (log string) { - switch key { - case "chainID": - app.state.SetChainID(value) - return "Success" - case "account": - var err error - var setAccount types.Account - wire.ReadJSONPtr(&setAccount, []byte(value), &err) - if err != nil { - return "Error decoding setAccount message: " + err.Error() + + pluginName, key := splitKey(key) + if pluginName != "BASE" { + // Set option on plugin + plugin := app.state.GetPlugin(pluginName) + if plugin == nil { + return "Invalid plugin name: " + pluginName } - accBytes := wire.BinaryBytes(setAccount) - res := app.eyesCli.SetSync(setAccount.PubKey.Address(), accBytes) - if res.IsErr() { - return "Error saving account: " + res.Error() + return plugin.SetOption(key, value) + } else { + // Set option on basecoin + switch key { + case "chainID": + app.state.SetChainID(value) + return "Success" + case "account": + var err error + var setAccount types.Account + wire.ReadJSONPtr(&setAccount, []byte(value), &err) + if err != nil { + return "Error decoding setAccount message: " + err.Error() + } + accBytes := wire.BinaryBytes(setAccount) + res := app.eyesCli.SetSync(setAccount.PubKey.Address(), accBytes) + if res.IsErr() { + return "Error saving account: " + res.Error() + } + return "Success" } - return "Success" + return "Unrecognized option key " + key } - return "Unrecognized option key " + key } // TMSP::AppendTx @@ -98,18 +112,36 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) { // TMSP::Query func (app *Basecoin) Query(query []byte) (res tmsp.Result) { - res = app.eyesCli.GetSync(query) - if res.IsErr() { - return res.PrependLog("Error querying eyesCli") + pluginName, queryStr := splitKey(string(query)) + if pluginName != "BASE" { + plugin := app.state.GetPlugin(pluginName) + if plugin == nil { + return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName)) + } + return plugin.Query([]byte(queryStr)) + } else { + // TODO turn Basecoin ops into a plugin? + res = app.eyesCli.GetSync([]byte(queryStr)) + if res.IsErr() { + return res.PrependLog("Error querying eyesCli") + } + return res } - return res } // TMSP::Commit func (app *Basecoin) Commit() (res tmsp.Result) { + // First, commit all the plugins + for _, plugin := range app.state.GetPlugins() { + res = plugin.Commit() + if res.IsErr() { + PanicSanity(Fmt("Error committing plugin %v", plugin.Name)) + } + } + // Then, commit eyes. res = app.eyesCli.CommitSync() if res.IsErr() { - panic("Error getting hash: " + res.Error()) + PanicSanity("Error getting hash: " + res.Error()) } return res } @@ -124,3 +156,15 @@ func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator { app.state.ResetCacheState() return app.govMint.EndBlock(height) } + +//---------------------------------------- + +// Splits the string at the first :. +// if there are none, the second string is nil. +func splitKey(key string) (prefix string, sufix string) { + if strings.Contains(key, ":") { + keyParts := strings.SplitN(key, ":", 2) + return keyParts[0], keyParts[1] + } + return key, "" +} diff --git a/state/execution.go b/state/execution.go index 2f0b47e60..2d35f0061 100644 --- a/state/execution.go +++ b/state/execution.go @@ -93,9 +93,9 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp } // Validate call address - plugin := state.GetPlugin(tx.Address) + plugin := state.GetPlugin(string(tx.Address)) if plugin != nil { - return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X", tx.Address)) + return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X (%v)", tx.Address, string(tx.Address))) } // Good! diff --git a/state/state.go b/state/state.go index 7d3309a00..ac17ff0ae 100644 --- a/state/state.go +++ b/state/state.go @@ -8,10 +8,11 @@ import ( ) type State struct { - chainID string - eyesCli *eyes.Client - checkCache map[string]checkAccount - plugins map[string]types.Plugin + chainID string + eyesCli *eyes.Client + checkCache map[string]checkAccount + plugins map[string]types.Plugin + pluginsList []types.NamedPlugin LastBlockHeight uint64 LastBlockHash []byte @@ -23,6 +24,7 @@ func NewState(eyesCli *eyes.Client) *State { chainID: "", eyesCli: eyesCli, checkCache: make(map[string]checkAccount), + plugins: make(map[string]types.Plugin), } return s } @@ -38,12 +40,20 @@ func (s *State) GetChainID() string { return s.chainID } -func (s *State) RegisterPlugin(addr []byte, plugin types.Plugin) { - s.plugins[string(addr)] = plugin +func (s *State) RegisterPlugin(name string, plugin types.Plugin) { + s.plugins[name] = plugin + s.pluginsList = append(s.pluginsList, types.NamedPlugin{ + Name: name, + Plugin: plugin, + }) } -func (s *State) GetPlugin(addr []byte) types.Plugin { - return s.plugins[string(addr)] +func (s *State) GetPlugin(name string) types.Plugin { + return s.plugins[name] +} + +func (s *State) GetPlugins() []types.NamedPlugin { + return s.pluginsList } //---------------------------------------- diff --git a/tests/tmsp/main.go b/tests/tmsp/main.go index 71cdbc856..89a15315f 100644 --- a/tests/tmsp/main.go +++ b/tests/tmsp/main.go @@ -8,17 +8,16 @@ import ( "github.com/tendermint/basecoin/types" . "github.com/tendermint/go-common" "github.com/tendermint/go-wire" + govtypes "github.com/tendermint/governmint/types" eyescli "github.com/tendermint/merkleeyes/client" - _ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types ) -/* - Get the "test" account. - PrivKey: 019F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A0867D3B5EAF0C0BF6B5A602D359DAECC86A7A74053490EC37AE08E71360587C870 - PubKey: 0167D3B5EAF0C0BF6B5A602D359DAECC86A7A74053490EC37AE08E71360587C870 - Address: D9B727742AA29FA638DC63D70813C976014C4CE0 -*/ func main() { + //testSendTx() + testGov() +} + +func testSendTx() { eyesCli := eyescli.NewLocalClient() bcApp := app.NewBasecoin(eyesCli) fmt.Println(bcApp.Info()) @@ -61,4 +60,27 @@ func main() { txBytes := wire.BinaryBytes(tx) res := bcApp.AppendTx(txBytes) fmt.Println(res) + if res.IsErr() { + Exit(Fmt("Failed: %v", res.Error())) + } +} + +func testGov() { + eyesCli := eyescli.NewLocalClient() + bcApp := app.NewBasecoin(eyesCli) + fmt.Println(bcApp.Info()) + + tPriv := tests.PrivAccountFromSecret("test") + + // Seed Basecoin with admin using PrivAccount + tAcc := tPriv.Account + adminEntity := govtypes.Entity{ + ID: "", + PubKey: tAcc.PubKey, + } + log := bcApp.SetOption("GOV:admin", string(wire.JSONBytes(adminEntity))) + if log != "Success" { + Exit(Fmt("Failed to set option: %v", log)) + } + // TODO test proposals or something. } diff --git a/types/plugin.go b/types/plugin.go index 6cc1b8f0a..0a4bd9370 100644 --- a/types/plugin.go +++ b/types/plugin.go @@ -8,7 +8,15 @@ import ( // Gas is a pointer to remainig gas. Decrement as you go, // if any gas is left the user is type Plugin interface { - CallTx(ctx CallContext, txBytes []byte) tmsp.Result + SetOption(key string, value string) (log string) + CallTx(ctx CallContext, txBytes []byte) (res tmsp.Result) + Query(query []byte) (res tmsp.Result) + Commit() (res tmsp.Result) +} + +type NamedPlugin struct { + Name string + Plugin } type CallContext struct {