Fix Basecoin CheckTx cache

This commit is contained in:
Jae Kwon 2016-03-29 14:25:17 -07:00
parent 615981c70d
commit 13a0bdd911
6 changed files with 75 additions and 69 deletions

View File

@ -159,10 +159,17 @@ func (app *Basecoin) InitChain(validators []*tmsp.Validator) {
app.govMint.InitChain(validators)
}
// TMSP::BeginBlock
func (app *Basecoin) BeginBlock(height uint64) {
// app.govMint.BeginBlock(height)
// TODO other plugins?
}
// TMSP::EndBlock
func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
app.state.ResetCacheState()
return app.govMint.EndBlock(height)
// TODO other plugins?
}
//----------------------------------------

View File

@ -10,10 +10,20 @@ import (
)
// If the tx is invalid, a TMSP error will be returned.
func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
// TODO: do something with fees
fees := int64(0)
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
}
// Exec tx
switch tx := tx.(type) {
@ -31,8 +41,8 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
}
// Validate inputs and outputs
signBytes := tx.SignBytes(state.GetChainID())
inTotal, res := validateInputs(state, accounts, signBytes, tx.Inputs)
signBytes := tx.SignBytes(chainID)
inTotal, res := validateInputs(accounts, signBytes, tx.Inputs)
if res.IsErr() {
return res.PrependLog("in validateInputs()")
}
@ -49,7 +59,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
// TODO: Fee validation for SendTx
// Good! Adjust accounts
adjustByInputs(state, accounts, tx.Inputs, isCheckTx)
adjustByInputs(state, accounts, tx.Inputs)
adjustByOutputs(state, accounts, tx.Outputs, isCheckTx)
/*
@ -81,8 +91,8 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address))
return res
}
signBytes := tx.SignBytes(state.GetChainID())
res := validateInput(state, inAcc, signBytes, tx.Input)
signBytes := tx.SignBytes(chainID)
res := validateInput(inAcc, signBytes, tx.Input)
if res.IsErr() {
log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, res))
return res.PrependLog("in validateInput()")
@ -103,16 +113,18 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
value := tx.Input.Amount - tx.Fee
inAcc.Sequence += 1
inAcc.Balance -= tx.Input.Amount
state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance)
inAccCopy := inAcc.Copy()
// If this is a CheckTx, stop now.
if isCheckTx {
state.SetAccount(tx.Input.Address, inAcc)
return tmsp.OK
}
// Create inAcc checkpoint
inAccCopy := inAcc.Copy()
// Run the tx.
cache := NewAccountCache(state)
cache := types.NewAccountCache(state)
cache.SetAccount(tx.Input.Address, inAcc)
gas := int64(1) // TODO
ctx := types.NewCallContext(cache, inAcc, value, &gas)
@ -218,14 +230,14 @@ func checkInputPubKey(address []byte, acc *types.Account, in types.TxInput) tmsp
}
// Validate inputs and compute total amount
func validateInputs(state *State, accounts map[string]*types.Account, signBytes []byte, ins []types.TxInput) (total int64, res tmsp.Result) {
func validateInputs(accounts map[string]*types.Account, signBytes []byte, ins []types.TxInput) (total int64, res tmsp.Result) {
for _, in := range ins {
acc := accounts[string(in.Address)]
if acc == nil {
PanicSanity("validateInputs() expects account in accounts")
}
res = validateInput(state, acc, signBytes, in)
res = validateInput(acc, signBytes, in)
if res.IsErr() {
return
}
@ -235,13 +247,13 @@ func validateInputs(state *State, accounts map[string]*types.Account, signBytes
return total, tmsp.OK
}
func validateInput(state *State, acc *types.Account, signBytes []byte, in types.TxInput) (res tmsp.Result) {
func validateInput(acc *types.Account, signBytes []byte, in types.TxInput) (res tmsp.Result) {
// Check TxInput basic
if res := in.ValidateBasic(); res.IsErr() {
return res
}
// Check sequence/balance
seq, balance := state.GetCheckAccount(in.Address, acc.Sequence, acc.Balance)
seq, balance := acc.Sequence, acc.Balance
if seq+1 != in.Sequence {
return tmsp.ErrBaseInvalidSequence.AppendLog(Fmt("Got %v, expected %v. (acc.seq=%v)", in.Sequence, seq+1, acc.Sequence))
}
@ -268,7 +280,7 @@ func validateOutputs(outs []types.TxOutput) (total int64, res tmsp.Result) {
return total, tmsp.OK
}
func adjustByInputs(state *State, accounts map[string]*types.Account, ins []types.TxInput, isCheckTx bool) {
func adjustByInputs(state types.AccountSetter, accounts map[string]*types.Account, ins []types.TxInput) {
for _, in := range ins {
acc := accounts[string(in.Address)]
if acc == nil {
@ -279,15 +291,11 @@ func adjustByInputs(state *State, accounts map[string]*types.Account, ins []type
}
acc.Balance -= in.Amount
acc.Sequence += 1
state.SetCheckAccount(in.Address, acc.Sequence, acc.Balance)
if !isCheckTx {
// NOTE: Must be set in deterministic order
state.SetAccount(in.Address, acc)
}
state.SetAccount(in.Address, acc)
}
}
func adjustByOutputs(state *State, accounts map[string]*types.Account, outs []types.TxOutput, isCheckTx bool) {
func adjustByOutputs(state types.AccountSetter, accounts map[string]*types.Account, outs []types.TxOutput, isCheckTx bool) {
for _, out := range outs {
acc := accounts[string(out.Address)]
if acc == nil {
@ -295,8 +303,6 @@ func adjustByOutputs(state *State, accounts map[string]*types.Account, outs []ty
}
acc.Balance += out.Amount
if !isCheckTx {
state.SetCheckAccount(out.Address, acc.Sequence, acc.Balance)
// NOTE: Must be set in deterministic order
state.SetAccount(out.Address, acc)
}
}

View File

@ -10,7 +10,7 @@ import (
type State struct {
chainID string
eyesCli *eyes.Client
checkCache map[string]checkAccount
checkCache *types.AccountCache
LastBlockHeight uint64
LastBlockHash []byte
@ -19,10 +19,10 @@ type State struct {
func NewState(eyesCli *eyes.Client) *State {
s := &State{
chainID: "",
eyesCli: eyesCli,
checkCache: make(map[string]checkAccount),
chainID: "",
eyesCli: eyesCli,
}
s.checkCache = types.NewAccountCache(s)
return s
}
@ -37,32 +37,6 @@ func (s *State) GetChainID() string {
return s.chainID
}
//----------------------------------------
// CheckTx state
type checkAccount struct {
sequence int
balance int64
}
func (s *State) GetCheckAccount(addr []byte, defaultSequence int, defaultBalance int64) (sequence int, balance int64) {
cAcc, ok := s.checkCache[string(addr)]
if !ok {
return defaultSequence, defaultBalance
}
return cAcc.sequence, cAcc.balance
}
func (s *State) SetCheckAccount(addr []byte, sequence int, balance int64) {
s.checkCache[string(addr)] = checkAccount{sequence, balance}
}
func (s *State) ResetCacheState() {
s.checkCache = make(map[string]checkAccount)
}
//----------------------------------------
func (s *State) GetAccount(addr []byte) *types.Account {
res := s.eyesCli.GetSync(addr)
if res.IsErr() {
@ -86,3 +60,11 @@ func (s *State) SetAccount(address []byte, acc *types.Account) {
panic(Fmt("Error storing account addr %X error: %v", address, res.Error()))
}
}
func (s *State) GetCheckCache() *types.AccountCache {
return s.checkCache
}
func (s *State) ResetCacheState() {
s.checkCache = types.NewAccountCache(s)
}

View File

@ -129,16 +129,18 @@ func testSequence() {
signBytes := tx.SignBytes(chainID)
sig := root.PrivKey.Sign(signBytes)
tx.Inputs[0].Signature = sig
//fmt.Println("tx:", tx)
// fmt.Printf("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
// Write request
txBytes := wire.BinaryBytes(tx)
res := bcApp.CheckTx(txBytes)
res := bcApp.AppendTx(txBytes)
if res.IsErr() {
Exit("AppendTx error: " + res.Error())
}
}
fmt.Println("-------------------- RANDOM SENDS --------------------")
// Now send coins between these accounts
for {
randA := RandInt() % len(privAccounts)
@ -173,7 +175,7 @@ func testSequence() {
signBytes := tx.SignBytes(chainID)
sig := privAccountA.PrivKey.Sign(signBytes)
tx.Inputs[0].Signature = sig
//fmt.Println("tx:", tx)
// fmt.Printf("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
// Write request
txBytes := wire.BinaryBytes(tx)

View File

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

View File

@ -1,23 +1,22 @@
package state
package types
import (
"github.com/tendermint/basecoin/types"
"sort"
)
type AccountCache struct {
state *State
accounts map[string]*types.Account
state AccountGetterSetter
accounts map[string]*Account
}
func NewAccountCache(state *State) *AccountCache {
func NewAccountCache(state AccountGetterSetter) *AccountCache {
return &AccountCache{
state: state,
accounts: make(map[string]*types.Account),
accounts: make(map[string]*Account),
}
}
func (cache *AccountCache) GetAccount(addr []byte) *types.Account {
func (cache *AccountCache) GetAccount(addr []byte) *Account {
acc, ok := cache.accounts[string(addr)]
if !ok {
acc = cache.state.GetAccount(addr)
@ -26,7 +25,7 @@ func (cache *AccountCache) GetAccount(addr []byte) *types.Account {
return acc
}
func (cache *AccountCache) SetAccount(addr []byte, acc *types.Account) {
func (cache *AccountCache) SetAccount(addr []byte, acc *Account) {
cache.accounts[string(addr)] = acc
}
@ -43,4 +42,16 @@ func (cache *AccountCache) Sync() {
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()
}