Plugin interface methods take store
This commit is contained in:
parent
fefcbbf3b0
commit
324e72f36d
62
app/app.go
62
app/app.go
|
@ -3,7 +3,7 @@ package app
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/basecoin/state"
|
||||
sm "github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
|
@ -26,22 +26,24 @@ const (
|
|||
)
|
||||
|
||||
type Basecoin struct {
|
||||
eyesCli *eyes.Client
|
||||
govMint *gov.Governmint
|
||||
state *state.State
|
||||
plugins *types.Plugins
|
||||
eyesCli *eyes.Client
|
||||
govMint *gov.Governmint
|
||||
state *sm.State
|
||||
cacheState *sm.State
|
||||
plugins *types.Plugins
|
||||
}
|
||||
|
||||
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||
govMint := gov.NewGovernmint(eyesCli)
|
||||
state_ := state.NewState(eyesCli)
|
||||
govMint := gov.NewGovernmint()
|
||||
state := sm.NewState(eyesCli)
|
||||
plugins := types.NewPlugins()
|
||||
plugins.RegisterPlugin(PluginTypeByteGov, PluginNameGov, govMint)
|
||||
return &Basecoin{
|
||||
eyesCli: eyesCli,
|
||||
govMint: govMint,
|
||||
state: state_,
|
||||
plugins: plugins,
|
||||
eyesCli: eyesCli,
|
||||
govMint: govMint,
|
||||
state: state,
|
||||
cacheState: nil,
|
||||
plugins: plugins,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +61,7 @@ func (app *Basecoin) SetOption(key string, value string) (log string) {
|
|||
if plugin == nil {
|
||||
return "Invalid plugin name: " + PluginName
|
||||
}
|
||||
return plugin.SetOption(key, value)
|
||||
return plugin.SetOption(app.state, key, value)
|
||||
} else {
|
||||
// Set option on basecoin
|
||||
switch key {
|
||||
|
@ -92,7 +94,7 @@ func (app *Basecoin) AppendTx(txBytes []byte) (res tmsp.Result) {
|
|||
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
||||
}
|
||||
// Validate and exec tx
|
||||
res = state.ExecTx(app.state, app.plugins, tx, false, nil)
|
||||
res = sm.ExecTx(app.state, app.plugins, tx, false, nil)
|
||||
if res.IsErr() {
|
||||
return res.PrependLog("Error in AppendTx")
|
||||
}
|
||||
|
@ -111,7 +113,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
|
|||
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
||||
}
|
||||
// Validate tx
|
||||
res = state.ExecTx(app.state, app.plugins, tx, true, nil)
|
||||
res = sm.ExecTx(app.cacheState, app.plugins, tx, true, nil)
|
||||
if res.IsErr() {
|
||||
return res.PrependLog("Error in CheckTx")
|
||||
}
|
||||
|
@ -130,8 +132,6 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
|||
return tmsp.OK.SetLog("This type of query not yet supported")
|
||||
case PluginTypeByteEyes:
|
||||
return app.eyesCli.QuerySync(query)
|
||||
case PluginTypeByteGov:
|
||||
return app.govMint.Query(query)
|
||||
}
|
||||
return tmsp.ErrBaseUnknownPlugin.SetLog(
|
||||
Fmt("Unknown plugin with type byte %X", typeByte))
|
||||
|
@ -139,14 +139,7 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
|||
|
||||
// TMSP::Commit
|
||||
func (app *Basecoin) Commit() (res tmsp.Result) {
|
||||
// First, commit all the plugins
|
||||
for _, plugin := range app.plugins.GetList() {
|
||||
res = plugin.Commit()
|
||||
if res.IsErr() {
|
||||
PanicSanity(Fmt("Error committing plugin %v", plugin.Name))
|
||||
}
|
||||
}
|
||||
// Then, commit eyes.
|
||||
// Commit eyes.
|
||||
res = app.eyesCli.CommitSync()
|
||||
if res.IsErr() {
|
||||
PanicSanity("Error getting hash: " + res.Error())
|
||||
|
@ -157,32 +150,23 @@ func (app *Basecoin) Commit() (res tmsp.Result) {
|
|||
// TMSP::InitChain
|
||||
func (app *Basecoin) InitChain(validators []*tmsp.Validator) {
|
||||
for _, plugin := range app.plugins.GetList() {
|
||||
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||
plugin.Plugin.(tmsp.BlockchainAware).InitChain(validators)
|
||||
}
|
||||
plugin.Plugin.InitChain(app.state, validators)
|
||||
}
|
||||
}
|
||||
|
||||
// TMSP::BeginBlock
|
||||
func (app *Basecoin) BeginBlock(height uint64) {
|
||||
app.state.ResetCacheState()
|
||||
for _, plugin := range app.plugins.GetList() {
|
||||
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||
plugin.Plugin.(tmsp.BlockchainAware).BeginBlock(height)
|
||||
}
|
||||
plugin.Plugin.BeginBlock(app.state, height)
|
||||
}
|
||||
app.cacheState = app.state.CacheWrap()
|
||||
}
|
||||
|
||||
// TMSP::EndBlock
|
||||
func (app *Basecoin) EndBlock(height uint64) (vals []*tmsp.Validator) {
|
||||
func (app *Basecoin) EndBlock(height uint64) (diffs []*tmsp.Validator) {
|
||||
for _, plugin := range app.plugins.GetList() {
|
||||
if plugin.Plugin == app.govMint {
|
||||
vals = plugin.Plugin.(tmsp.BlockchainAware).EndBlock(height)
|
||||
} else {
|
||||
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||
plugin.Plugin.(tmsp.BlockchainAware).EndBlock(height)
|
||||
}
|
||||
}
|
||||
moreDiffs := plugin.Plugin.EndBlock(app.state, height)
|
||||
diffs = append(diffs, moreDiffs...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -8,20 +8,11 @@ import (
|
|||
)
|
||||
|
||||
// If the tx is invalid, a TMSP error will be returned.
|
||||
func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
|
||||
func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
|
||||
|
||||
// TODO: do something with fees
|
||||
fees := types.Coins{}
|
||||
chainID := s.GetChainID()
|
||||
|
||||
// Get the state. If isCheckTx, then we use a cache.
|
||||
// The idea is to throw away this cache after every EndBlock().
|
||||
var state types.AccountGetterSetter
|
||||
if isCheckTx {
|
||||
state = s.GetCheckCache()
|
||||
} else {
|
||||
state = s
|
||||
}
|
||||
chainID := state.GetChainID()
|
||||
|
||||
// Exec tx
|
||||
switch tx := tx.(type) {
|
||||
|
@ -129,15 +120,16 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
|
|||
}
|
||||
|
||||
// Create inAcc checkpoint
|
||||
inAccCopy := inAcc.Copy()
|
||||
inAccDeducted := inAcc.Copy()
|
||||
|
||||
// Run the tx.
|
||||
cache := types.NewAccountCache(state)
|
||||
// XXX cache := types.NewStateCache(state)
|
||||
cache := state.CacheWrap()
|
||||
cache.SetAccount(tx.Input.Address, inAcc)
|
||||
ctx := types.NewCallContext(cache, inAcc, coins)
|
||||
res = plugin.RunTx(ctx, tx.Data)
|
||||
ctx := types.NewCallContext(tx.Input.Address, coins)
|
||||
res = plugin.RunTx(cache, ctx, tx.Data)
|
||||
if res.IsOK() {
|
||||
cache.Sync()
|
||||
cache.CacheSync()
|
||||
log.Info("Successful execution")
|
||||
// Fire events
|
||||
/*
|
||||
|
@ -153,10 +145,10 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
|
|||
} else {
|
||||
log.Info("AppTx failed", "error", res)
|
||||
// Just return the coins and return.
|
||||
inAccCopy.Balance = inAccCopy.Balance.Plus(coins)
|
||||
inAccDeducted.Balance = inAccDeducted.Balance.Plus(coins)
|
||||
// But take the gas
|
||||
// TODO
|
||||
state.SetAccount(tx.Input.Address, inAccCopy)
|
||||
state.SetAccount(tx.Input.Address, inAccDeducted)
|
||||
}
|
||||
return res
|
||||
|
||||
|
|
|
@ -4,26 +4,21 @@ import (
|
|||
"github.com/tendermint/basecoin/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
)
|
||||
|
||||
// CONTRACT: State should be quick to copy.
|
||||
// See CacheWrap().
|
||||
type State struct {
|
||||
chainID string
|
||||
eyesCli *eyes.Client
|
||||
checkCache *types.AccountCache
|
||||
|
||||
LastBlockHeight uint64
|
||||
LastBlockHash []byte
|
||||
GasLimit int64
|
||||
chainID string
|
||||
store types.KVStore
|
||||
cache *types.KVCache // optional
|
||||
}
|
||||
|
||||
func NewState(eyesCli *eyes.Client) *State {
|
||||
s := &State{
|
||||
func NewState(store types.KVStore) *State {
|
||||
return &State{
|
||||
chainID: "",
|
||||
eyesCli: eyesCli,
|
||||
store: store,
|
||||
}
|
||||
s.checkCache = types.NewAccountCache(s)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *State) SetChainID(chainID string) {
|
||||
|
@ -37,36 +32,34 @@ func (s *State) GetChainID() string {
|
|||
return s.chainID
|
||||
}
|
||||
|
||||
func (s *State) Get(key []byte) (value []byte) {
|
||||
return s.store.Get(key)
|
||||
}
|
||||
|
||||
func (s *State) Set(key []byte, value []byte) {
|
||||
s.store.Set(key, value)
|
||||
}
|
||||
|
||||
func (s *State) GetAccount(addr []byte) *types.Account {
|
||||
res := s.eyesCli.GetSync(AccountKey(addr))
|
||||
if res.IsErr() {
|
||||
panic(Fmt("Error loading account addr %X error: %v", addr, res.Error()))
|
||||
}
|
||||
if len(res.Data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var acc *types.Account
|
||||
err := wire.ReadBinaryBytes(res.Data, &acc)
|
||||
if err != nil {
|
||||
panic(Fmt("Error reading account %X error: %v", res.Data, err.Error()))
|
||||
}
|
||||
return acc
|
||||
return GetAccount(s.store, addr)
|
||||
}
|
||||
|
||||
func (s *State) SetAccount(addr []byte, acc *types.Account) {
|
||||
accBytes := wire.BinaryBytes(acc)
|
||||
res := s.eyesCli.SetSync(AccountKey(addr), accBytes)
|
||||
if res.IsErr() {
|
||||
panic(Fmt("Error storing account addr %X error: %v", addr, res.Error()))
|
||||
SetAccount(s.store, addr, acc)
|
||||
}
|
||||
|
||||
func (s *State) CacheWrap() *State {
|
||||
cache := types.NewKVCache(s.store)
|
||||
return &State{
|
||||
chainID: s.chainID,
|
||||
store: cache,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) GetCheckCache() *types.AccountCache {
|
||||
return s.checkCache
|
||||
}
|
||||
|
||||
func (s *State) ResetCacheState() {
|
||||
s.checkCache = types.NewAccountCache(s)
|
||||
// NOTE: errors if s is not from CacheWrap()
|
||||
func (s *State) CacheSync() {
|
||||
s.cache.Sync()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -74,3 +67,22 @@ func (s *State) ResetCacheState() {
|
|||
func AccountKey(addr []byte) []byte {
|
||||
return append([]byte("base/a/"), addr...)
|
||||
}
|
||||
|
||||
func GetAccount(store types.KVStore, addr []byte) *types.Account {
|
||||
data := store.Get(AccountKey(addr))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var acc *types.Account
|
||||
err := wire.ReadBinaryBytes(data, &acc)
|
||||
if err != nil {
|
||||
panic(Fmt("Error reading account %X error: %v",
|
||||
data, err.Error()))
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func SetAccount(store types.KVStore, addr []byte, acc *types.Account) {
|
||||
accBytes := wire.BinaryBytes(acc)
|
||||
store.Set(AccountKey(addr), accBytes)
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ func testGov() {
|
|||
}
|
||||
fmt.Println(res)
|
||||
|
||||
// XXX Difficult to debug without a stacktrace...
|
||||
// TODO more tests...
|
||||
}
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type AccountCache struct {
|
||||
state AccountGetterSetter
|
||||
accounts map[string]*Account
|
||||
}
|
||||
|
||||
func NewAccountCache(state AccountGetterSetter) *AccountCache {
|
||||
return &AccountCache{
|
||||
state: state,
|
||||
accounts: make(map[string]*Account),
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *AccountCache) GetAccount(addr []byte) *Account {
|
||||
acc, ok := cache.accounts[string(addr)]
|
||||
if !ok {
|
||||
acc = cache.state.GetAccount(addr)
|
||||
cache.accounts[string(addr)] = acc
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
func (cache *AccountCache) SetAccount(addr []byte, acc *Account) {
|
||||
cache.accounts[string(addr)] = acc
|
||||
}
|
||||
|
||||
func (cache *AccountCache) Sync() {
|
||||
// MUST BE DETERMINISTIC
|
||||
// First, order the addrs.
|
||||
addrs := []string{}
|
||||
for addr := range cache.accounts {
|
||||
addrs = append(addrs, string(addr))
|
||||
}
|
||||
sort.Strings(addrs)
|
||||
|
||||
// Set the accounts in order.
|
||||
for _, addr := range addrs {
|
||||
cache.state.SetAccount([]byte(addr), cache.accounts[addr])
|
||||
}
|
||||
|
||||
// Reset accounts
|
||||
cache.accounts = make(map[string]*Account)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// NOT USED
|
||||
type AccountCacher interface {
|
||||
GetAccount(addr []byte) *Account
|
||||
SetAccount(addr []byte, acc *Account)
|
||||
Sync()
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
type KVStore interface {
|
||||
Set(key, value []byte)
|
||||
Get(key []byte) (value []byte)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
type MemKVStore struct {
|
||||
m map[string][]byte
|
||||
}
|
||||
|
||||
func NewMemKVStore() *MemKVStore {
|
||||
return &MemKVStore{
|
||||
m: make(map[string][]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (mkv *MemKVStore) Set(key []byte, value []byte) {
|
||||
mkv.m[string(key)] = value
|
||||
}
|
||||
|
||||
func (mkv *MemKVStore) Get(key []byte) (value []byte) {
|
||||
return mkv.m[string(key)]
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// A Cache that enforces deterministic sync order.
|
||||
type KVCache struct {
|
||||
store KVStore
|
||||
cache map[string]kvCacheValue
|
||||
keys *list.List
|
||||
}
|
||||
|
||||
type kvCacheValue struct {
|
||||
v []byte // The value of some key
|
||||
e *list.Element // The KVCache.keys element
|
||||
}
|
||||
|
||||
func NewKVCache(store KVStore) *KVCache {
|
||||
return (&KVCache{
|
||||
store: store,
|
||||
}).Reset()
|
||||
}
|
||||
|
||||
func (kvc *KVCache) Reset() *KVCache {
|
||||
kvc.cache = make(map[string]kvCacheValue)
|
||||
kvc.keys = list.New()
|
||||
return kvc
|
||||
}
|
||||
|
||||
func (kvc *KVCache) Set(key []byte, value []byte) {
|
||||
cacheValue, ok := kvc.cache[string(key)]
|
||||
if ok {
|
||||
kvc.keys.MoveToBack(cacheValue.e)
|
||||
} else {
|
||||
cacheValue.e = kvc.keys.PushBack(key)
|
||||
}
|
||||
cacheValue.v = value
|
||||
kvc.cache[string(key)] = cacheValue
|
||||
}
|
||||
|
||||
func (kvc *KVCache) Get(key []byte) (value []byte) {
|
||||
cacheValue := kvc.cache[string(key)]
|
||||
return cacheValue.v
|
||||
}
|
||||
|
||||
func (kvc *KVCache) Sync() {
|
||||
for e := kvc.keys.Front(); e != nil; e = e.Next() {
|
||||
key := e.Value.([]byte)
|
||||
value := kvc.cache[string(key)]
|
||||
kvc.store.Set(key, value.v)
|
||||
}
|
||||
kvc.Reset()
|
||||
}
|
|
@ -4,12 +4,12 @@ import (
|
|||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
||||
// Value is any floating value. It must be given to someone.
|
||||
type Plugin interface {
|
||||
SetOption(key string, value string) (log string)
|
||||
RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
|
||||
Query(query []byte) (res tmsp.Result)
|
||||
Commit() (res tmsp.Result)
|
||||
SetOption(store KVStore, key string, value string) (log string)
|
||||
RunTx(store KVStore, ctx CallContext, txBytes []byte) (res tmsp.Result)
|
||||
InitChain(store KVStore, vals []*tmsp.Validator)
|
||||
BeginBlock(store KVStore, height uint64)
|
||||
EndBlock(store KVStore, height uint64) []*tmsp.Validator
|
||||
}
|
||||
|
||||
type NamedPlugin struct {
|
||||
|
@ -21,14 +21,12 @@ type NamedPlugin struct {
|
|||
//----------------------------------------
|
||||
|
||||
type CallContext struct {
|
||||
Cache AccountCacher
|
||||
Caller *Account
|
||||
Caller []byte
|
||||
Coins Coins
|
||||
}
|
||||
|
||||
func NewCallContext(cache AccountCacher, caller *Account, coins Coins) CallContext {
|
||||
func NewCallContext(caller []byte, coins Coins) CallContext {
|
||||
return CallContext{
|
||||
Cache: cache,
|
||||
Caller: caller,
|
||||
Coins: coins,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue