diff --git a/blocks/block.go b/blocks/block.go index bcb5d6d6a..77d636d89 100644 --- a/blocks/block.go +++ b/blocks/block.go @@ -47,7 +47,7 @@ func (b *Block) ValidateBasic(lastBlockHeight uint32, lastBlockHash []byte) erro if b.Header.Network != Config.Network { return ErrBlockInvalidNetwork } - if b.Header.Height != lastBlockHeight { + if b.Header.Height != lastBlockHeight+1 { return ErrBlockInvalidBlockHeight } if !bytes.Equal(b.Header.LastBlockHash, lastBlockHash) { diff --git a/config/config.go b/config/config.go index c7a3e4982..7d2613313 100644 --- a/config/config.go +++ b/config/config.go @@ -19,13 +19,13 @@ import ( var RootDir string var Config Config_ -func initFlags(printHelp *bool) { +func setFlags(printHelp *bool) { flag.BoolVar(printHelp, "help", false, "Print this help message.") flag.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)") flag.StringVar(&Config.Seed, "seed", Config.Seed, "Address of seed node") } -func init() { +func ParseFlags() { RootDir = os.Getenv("TMROOT") if RootDir == "" { RootDir = os.Getenv("HOME") + "/.tendermint" @@ -54,7 +54,7 @@ func init() { // try to parse arg flags, which can override file configuration. var printHelp bool - initFlags(&printHelp) + setFlags(&printHelp) flag.Parse() if printHelp { fmt.Println("----------------------------------") diff --git a/consensus/state.go b/consensus/state.go index 73d8a699e..042fa283a 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -418,7 +418,7 @@ func (cs *ConsensusState) stageBlock(block *Block) error { stateCopy := cs.state.Copy() // Deep copy the state before staging. // Commit block onto the copied state. - err := stateCopy.CommitBlock(block) + err := stateCopy.AppendBlock(block) if err != nil { return err } else { diff --git a/log.go b/log.go index 9f04a5083..25e1d1107 100644 --- a/log.go +++ b/log.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/state" ) var log = logging.MustGetLogger("main") @@ -31,6 +32,7 @@ func init() { blocks.SetBlocksLogger(log) consensus.SetConsensusLogger(log) - p2p.SetP2PLogger(log) mempool.SetMempoolLogger(log) + p2p.SetP2PLogger(log) + state.SetStateLogger(log) } diff --git a/main.go b/main.go index d3a5d775a..9e3ffc8fc 100644 --- a/main.go +++ b/main.go @@ -78,6 +78,9 @@ func (n *Node) inboundConnectionRoutine(l p2p.Listener) { func main() { + // Parse config flags + config.ParseFlags() + // Create & start node n := NewNode() l := p2p.NewDefaultListener("tcp", config.Config.LAddr) diff --git a/mempool/mempool.go b/mempool/mempool.go index 6d74a222a..b721cde21 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -34,7 +34,7 @@ func NewMempool(lastBlock *Block, state *State) *Mempool { func (mem *Mempool) AddTx(tx Tx) (err error) { mem.mtx.Lock() defer mem.mtx.Unlock() - err = mem.state.CommitTx(tx) + err = mem.state.ExecTx(tx) if err != nil { return err } else { @@ -82,7 +82,7 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *State) { // Next, filter all txs that aren't valid given new state. validTxs := []Tx{} for _, tx := range txs { - err := mem.state.CommitTx(tx) + err := mem.state.ExecTx(tx) if err != nil { validTxs = append(validTxs, tx) } else { diff --git a/merkle/util.go b/merkle/util.go index 44e1fedd2..56f808e2c 100644 --- a/merkle/util.go +++ b/merkle/util.go @@ -42,7 +42,7 @@ will be at different levels. func HashFromHashes(hashes [][]byte) []byte { switch len(hashes) { case 0: - panic("Cannot compute hash of empty slice") + return nil case 1: return hashes[0] default: diff --git a/state/log.go b/state/log.go new file mode 100644 index 000000000..323af2627 --- /dev/null +++ b/state/log.go @@ -0,0 +1,15 @@ +package state + +import ( + "github.com/op/go-logging" +) + +var log = logging.MustGetLogger("state") + +func init() { + logging.SetFormatter(logging.MustStringFormatter("[%{level:.1s}] %{message}")) +} + +func SetStatesLogger(l *logging.Logger) { + log = l +} diff --git a/state/state.go b/state/state.go index 182ab84ad..9428dd649 100644 --- a/state/state.go +++ b/state/state.go @@ -13,7 +13,9 @@ import ( ) var ( - ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") + ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") + ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash") + ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash") stateKey = []byte("stateKey") ) @@ -33,6 +35,7 @@ func (abc accountBalanceCodec) Read(accBalBytes []byte) (interface{}, error) { //----------------------------------------------------------------------------- +// TODO: make it unsafe, remove mtx, and export fields? type State struct { mtx sync.Mutex db DB @@ -175,7 +178,17 @@ func (s *State) AppendBlock(b *Block) error { } } + // Increment validator AccumPowers s.validators.IncrementAccum() + + // State hashes should match + if !bytes.Equal(s.validators.Hash(), b.ValidationStateHash) { + return ErrStateInvalidValidationStateHash + } + if !bytes.Equal(s.accountBalances.Tree.Hash(), b.AccountStateHash) { + return ErrStateInvalidAccountStateHash + } + s.height = b.Height s.blockHash = b.Hash() return nil @@ -193,13 +206,20 @@ func (s *State) CommitTime() time.Time { return s.commitTime } -// The returned ValidatorSet gets mutated upon s.Commit*(). +// The returned ValidatorSet gets mutated upon s.ExecTx() and s.AppendBlock(). +// Caller should copy the returned set before mutating. func (s *State) Validators() *ValidatorSet { s.mtx.Lock() defer s.mtx.Unlock() return s.validators } +func (s *State) BlockHash() []byte { + s.mtx.Lock() + defer s.mtx.Unlock() + return s.blockHash +} + func (s *State) AccountBalance(accountId uint64) *AccountBalance { s.mtx.Lock() defer s.mtx.Unlock() diff --git a/state/state_test.go b/state/state_test.go index 03e946b9d..ecfba96e3 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -1,9 +1,12 @@ package state import ( + . "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/common" + . "github.com/tendermint/tendermint/config" . "github.com/tendermint/tendermint/db" + "bytes" "testing" "time" ) @@ -38,9 +41,27 @@ func TestGenesisSaveLoad(t *testing.T) { // Generate a state, save & load it. s0 := randGenesisState(10, 5) - // Mutate the state to append one block. - block := &Block{Data: Data{Txs: []Tx{}}} - s0.AppendBlock(block) + // Figure out what the next state hashes should be. + s0ValsCopy := s0.Validators().Copy() + s0ValsCopy.IncrementAccum() + nextValidationStateHash := s0ValsCopy.Hash() + nextAccountStateHash := s0.accountBalances.Tree.Hash() + // Mutate the state to append one empty block. + block := &Block{ + Header: Header{ + Network: Config.Network, + Height: 1, + ValidationStateHash: nextValidationStateHash, + AccountStateHash: nextAccountStateHash, + }, + Data: Data{ + Txs: []Tx{}, + }, + } + err := s0.AppendBlock(block) + if err != nil { + t.Error("Error appending initial block:", err) + } // Save s0, load s1. commitTime := time.Now() @@ -53,7 +74,15 @@ func TestGenesisSaveLoad(t *testing.T) { t.Error("CommitTime was not the same") } // Compare height & blockHash - // XXX + if s0.Height() != 1 { + t.Error("s0 Height should be 1, got", s0.Height()) + } + if s0.Height() != s1.Height() { + t.Error("Height mismatch") + } + if !bytes.Equal(s0.BlockHash(), s1.BlockHash()) { + t.Error("BlockHash mismatch") + } // Compare Validators s0Vals := s0.Validators() s1Vals := s1.Validators() diff --git a/state/validator.go b/state/validator.go index 8154795ef..0995b75c1 100644 --- a/state/validator.go +++ b/state/validator.go @@ -5,6 +5,7 @@ import ( . "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/merkle" ) // Holds state for a Validator at a given height+round. @@ -155,3 +156,17 @@ func (vset *ValidatorSet) GetProposer() (proposer *Validator) { } return } + +// Should uniquely determine the state of the ValidatorSet. +func (vset *ValidatorSet) Hash() []byte { + ids := []uint64{} + for id, _ := range vset.validators { + ids = append(ids, id) + } + UInt64Slice(ids).Sort() + sortedValidators := make([]Binary, len(ids)) + for i, id := range ids { + sortedValidators[i] = vset.validators[id] + } + return merkle.HashFromBinaries(sortedValidators) +}