Plugin support for SetOption, Query, and Commit

This commit is contained in:
Jae Kwon 2016-03-24 14:10:17 -07:00
parent 964a4cfd50
commit 601a654b7d
5 changed files with 124 additions and 40 deletions

View File

@ -1,6 +1,8 @@
package app package app
import ( import (
"strings"
"github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/state"
"github.com/tendermint/basecoin/types" "github.com/tendermint/basecoin/types"
. "github.com/tendermint/go-common" . "github.com/tendermint/go-common"
@ -22,7 +24,7 @@ type Basecoin struct {
func NewBasecoin(eyesCli *eyes.Client) *Basecoin { func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
state_ := state.NewState(eyesCli) state_ := state.NewState(eyesCli)
govMint := gov.NewGovernmint(eyesCli) govMint := gov.NewGovernmint(eyesCli)
state_.RegisterPlugin([]byte("gov"), govMint) state_.RegisterPlugin("GOV", govMint)
return &Basecoin{ return &Basecoin{
eyesCli: eyesCli, eyesCli: eyesCli,
govMint: govMint, govMint: govMint,
@ -37,25 +39,37 @@ func (app *Basecoin) Info() string {
// TMSP::SetOption // TMSP::SetOption
func (app *Basecoin) SetOption(key string, value string) (log string) { func (app *Basecoin) SetOption(key string, value string) (log string) {
switch key {
case "chainID": pluginName, key := splitKey(key)
app.state.SetChainID(value) if pluginName != "BASE" {
return "Success" // Set option on plugin
case "account": plugin := app.state.GetPlugin(pluginName)
var err error if plugin == nil {
var setAccount types.Account return "Invalid plugin name: " + pluginName
wire.ReadJSONPtr(&setAccount, []byte(value), &err)
if err != nil {
return "Error decoding setAccount message: " + err.Error()
} }
accBytes := wire.BinaryBytes(setAccount) return plugin.SetOption(key, value)
res := app.eyesCli.SetSync(setAccount.PubKey.Address(), accBytes) } else {
if res.IsErr() { // Set option on basecoin
return "Error saving account: " + res.Error() 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 // TMSP::AppendTx
@ -98,18 +112,36 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
// TMSP::Query // TMSP::Query
func (app *Basecoin) Query(query []byte) (res tmsp.Result) { func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
res = app.eyesCli.GetSync(query) pluginName, queryStr := splitKey(string(query))
if res.IsErr() { if pluginName != "BASE" {
return res.PrependLog("Error querying eyesCli") 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 // TMSP::Commit
func (app *Basecoin) Commit() (res tmsp.Result) { 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() res = app.eyesCli.CommitSync()
if res.IsErr() { if res.IsErr() {
panic("Error getting hash: " + res.Error()) PanicSanity("Error getting hash: " + res.Error())
} }
return res return res
} }
@ -124,3 +156,15 @@ func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
app.state.ResetCacheState() app.state.ResetCacheState()
return app.govMint.EndBlock(height) 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, ""
}

View File

@ -93,9 +93,9 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
} }
// Validate call address // Validate call address
plugin := state.GetPlugin(tx.Address) plugin := state.GetPlugin(string(tx.Address))
if plugin != nil { 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! // Good!

View File

@ -8,10 +8,11 @@ import (
) )
type State struct { type State struct {
chainID string chainID string
eyesCli *eyes.Client eyesCli *eyes.Client
checkCache map[string]checkAccount checkCache map[string]checkAccount
plugins map[string]types.Plugin plugins map[string]types.Plugin
pluginsList []types.NamedPlugin
LastBlockHeight uint64 LastBlockHeight uint64
LastBlockHash []byte LastBlockHash []byte
@ -23,6 +24,7 @@ func NewState(eyesCli *eyes.Client) *State {
chainID: "", chainID: "",
eyesCli: eyesCli, eyesCli: eyesCli,
checkCache: make(map[string]checkAccount), checkCache: make(map[string]checkAccount),
plugins: make(map[string]types.Plugin),
} }
return s return s
} }
@ -38,12 +40,20 @@ func (s *State) GetChainID() string {
return s.chainID return s.chainID
} }
func (s *State) RegisterPlugin(addr []byte, plugin types.Plugin) { func (s *State) RegisterPlugin(name string, plugin types.Plugin) {
s.plugins[string(addr)] = plugin s.plugins[name] = plugin
s.pluginsList = append(s.pluginsList, types.NamedPlugin{
Name: name,
Plugin: plugin,
})
} }
func (s *State) GetPlugin(addr []byte) types.Plugin { func (s *State) GetPlugin(name string) types.Plugin {
return s.plugins[string(addr)] return s.plugins[name]
}
func (s *State) GetPlugins() []types.NamedPlugin {
return s.pluginsList
} }
//---------------------------------------- //----------------------------------------

View File

@ -8,17 +8,16 @@ import (
"github.com/tendermint/basecoin/types" "github.com/tendermint/basecoin/types"
. "github.com/tendermint/go-common" . "github.com/tendermint/go-common"
"github.com/tendermint/go-wire" "github.com/tendermint/go-wire"
govtypes "github.com/tendermint/governmint/types"
eyescli "github.com/tendermint/merkleeyes/client" 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() { func main() {
//testSendTx()
testGov()
}
func testSendTx() {
eyesCli := eyescli.NewLocalClient() eyesCli := eyescli.NewLocalClient()
bcApp := app.NewBasecoin(eyesCli) bcApp := app.NewBasecoin(eyesCli)
fmt.Println(bcApp.Info()) fmt.Println(bcApp.Info())
@ -61,4 +60,27 @@ func main() {
txBytes := wire.BinaryBytes(tx) txBytes := wire.BinaryBytes(tx)
res := bcApp.AppendTx(txBytes) res := bcApp.AppendTx(txBytes)
fmt.Println(res) 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.
} }

View File

@ -8,7 +8,15 @@ import (
// Gas is a pointer to remainig gas. Decrement as you go, // Gas is a pointer to remainig gas. Decrement as you go,
// if any gas is left the user is // if any gas is left the user is
type Plugin interface { 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 { type CallContext struct {