From 13a0bdd911314300518ee6caee8ed39e676ee22d Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 29 Mar 2016 14:25:17 -0700 Subject: [PATCH] Fix Basecoin CheckTx cache --- app/app.go | 7 +++++ state/execution.go | 50 +++++++++++++++++-------------- state/state.go | 42 ++++++++------------------ tests/tmsp/main.go | 8 +++-- types/account.go | 10 +++---- {state => types}/account_cache.go | 27 ++++++++++++----- 6 files changed, 75 insertions(+), 69 deletions(-) rename {state => types}/account_cache.go (53%) diff --git a/app/app.go b/app/app.go index 1aea8a333..95c9ba10f 100644 --- a/app/app.go +++ b/app/app.go @@ -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? } //---------------------------------------- diff --git a/state/execution.go b/state/execution.go index 318921c15..529d4b63a 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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) } } diff --git a/state/state.go b/state/state.go index 79a8cf265..4a9c1167f 100644 --- a/state/state.go +++ b/state/state.go @@ -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) +} diff --git a/tests/tmsp/main.go b/tests/tmsp/main.go index bd2971ea4..7e38768ef 100644 --- a/tests/tmsp/main.go +++ b/tests/tmsp/main.go @@ -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) diff --git a/types/account.go b/types/account.go index 67d601605..3591407a5 100644 --- a/types/account.go +++ b/types/account.go @@ -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() -} diff --git a/state/account_cache.go b/types/account_cache.go similarity index 53% rename from state/account_cache.go rename to types/account_cache.go index 47253c838..69ae10603 100644 --- a/state/account_cache.go +++ b/types/account_cache.go @@ -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() }