Plugins and AccountCaches

This commit is contained in:
Jae Kwon 2016-03-24 12:17:26 -07:00
parent 83e7c9dab1
commit 964a4cfd50
7 changed files with 115 additions and 22 deletions

View File

@ -20,11 +20,13 @@ type Basecoin struct {
} }
func NewBasecoin(eyesCli *eyes.Client) *Basecoin { func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
state_ := state.NewState(eyesCli)
govMint := gov.NewGovernmint(eyesCli) govMint := gov.NewGovernmint(eyesCli)
state_.RegisterPlugin([]byte("gov"), govMint)
return &Basecoin{ return &Basecoin{
eyesCli: eyesCli, eyesCli: eyesCli,
govMint: govMint, govMint: govMint,
state: state.NewState(eyesCli), state: state_,
} }
} }

46
state/account_cache.go Normal file
View File

@ -0,0 +1,46 @@
package state
import (
"github.com/tendermint/basecoin/types"
"sort"
)
type AccountCache struct {
state *State
accounts map[string]*types.Account
}
func NewAccountCache(state *State) *AccountCache {
return &AccountCache{
state: state,
accounts: make(map[string]*types.Account),
}
}
func (cache *AccountCache) GetAccount(addr []byte) *types.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 *types.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])
}
}

View File

@ -2,7 +2,6 @@ package state
import ( import (
"bytes" "bytes"
"strings"
"github.com/tendermint/basecoin/types" "github.com/tendermint/basecoin/types"
. "github.com/tendermint/go-common" . "github.com/tendermint/go-common"
@ -94,9 +93,8 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
} }
// Validate call address // Validate call address
if strings.HasPrefix(string(tx.Address), "gov/") { plugin := state.GetPlugin(tx.Address)
// This is a gov call. if plugin != nil {
} else {
return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X", tx.Address)) return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X", tx.Address))
} }
@ -105,16 +103,21 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
inAcc.Sequence += 1 inAcc.Sequence += 1
inAcc.Balance -= tx.Input.Amount inAcc.Balance -= tx.Input.Amount
state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance) state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance)
inAccCopy := inAcc.Copy()
// If this is AppendTx, actually save accounts // If this is AppendTx, actually save accounts
if !isCheckTx { if isCheckTx {
state.SetAccount(tx.Input.Address, inAcc) return tmsp.OK
// NOTE: value is dangling. }
// XXX: don't just give it back
inAcc.Balance += value // Run the tx.
// TODO: logic. cache := NewAccountCache(state)
// TODO: persist cache.SetAccount(tx.Input.Address, inAcc)
// state.SetAccount(tx.Input.Address, inAcc) gas := int64(1) // TODO
ctx := types.NewCallContext(cache, inAcc, value, &gas)
res = plugin.CallTx(ctx, tx.Data)
if res.IsOK() {
cache.Sync()
log.Info("Successful execution") log.Info("Successful execution")
// Fire events // Fire events
/* /*
@ -127,9 +130,14 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
evc.FireEvent(types.EventStringAccOutput(tx.Address), types.EventDataTx{tx, ret, exception}) evc.FireEvent(types.EventStringAccOutput(tx.Address), types.EventDataTx{tx, ret, exception})
} }
*/ */
} else {
log.Info("CallTx failed", "error", res)
// Just return the value and return.
// TODO: return gas?
inAccCopy.Balance += value
state.SetAccount(tx.Input.Address, inAccCopy)
} }
return res
return tmsp.OK
default: default:
return tmsp.ErrBaseEncodingError.SetLog("Unknown tx type") return tmsp.ErrBaseEncodingError.SetLog("Unknown tx type")

View File

@ -11,6 +11,7 @@ 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
LastBlockHeight uint64 LastBlockHeight uint64
LastBlockHash []byte LastBlockHash []byte
@ -37,6 +38,14 @@ func (s *State) GetChainID() string {
return s.chainID return s.chainID
} }
func (s *State) RegisterPlugin(addr []byte, plugin types.Plugin) {
s.plugins[string(addr)] = plugin
}
func (s *State) GetPlugin(addr []byte) types.Plugin {
return s.plugins[string(addr)]
}
//---------------------------------------- //----------------------------------------
// CheckTx state // CheckTx state

View File

@ -40,5 +40,11 @@ type AccountGetter interface {
type AccountGetterSetter interface { type AccountGetterSetter interface {
GetAccount(addr []byte) *Account GetAccount(addr []byte) *Account
SetAccount(acc *Account) SetAccount(addr []byte, acc *Account)
}
type AccountCacher interface {
GetAccount(addr []byte) *Account
SetAccount(addr []byte, acc *Account)
Sync()
} }

View File

@ -1,6 +0,0 @@
package types
type Plugin func(ags AccountGetterSetter,
caller *Account,
input []byte,
gas *int64) (result []byte, err error)

28
types/plugin.go Normal file
View File

@ -0,0 +1,28 @@
package types
import (
tmsp "github.com/tendermint/tmsp/types"
)
// Value is any floating value. It must be given to someone.
// 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
}
type CallContext struct {
Cache AccountCacher
Caller *Account
Value int64
Gas *int64
}
func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int64) CallContext {
return CallContext{
Cache: cache,
Caller: caller,
Value: value,
Gas: gas,
}
}