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
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, ""
}

View File

@ -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!

View File

@ -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
}
//----------------------------------------

View File

@ -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.
}

View File

@ -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 {