diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 2ec0e90df4ff4d5df937f237ca4b3f5aa83b749f..cca20386f7725b2676e8096f145ff48c01d3276b 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -92,98 +92,98 @@
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/account",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/alert",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/blockchain",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/common",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/config",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/consensus",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/db",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/events",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/logger",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/mempool",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/merkle",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/node",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/p2p",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/permission/types",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/rpc",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/state",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/types",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/vm",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tendermint/tendermint/wire",
-			"Comment": "0.1-26-g85d5b16",
-			"Rev": "85d5b16dbc610a6a27f6c3d4ece0316907babec6"
+			"Comment": "0.1-61-g64bf8ec",
+			"Rev": "64bf8ec7a5c48327d4d0d12e74582806ffd1906b"
 		},
 		{
 			"ImportPath": "github.com/tommy351/gin-cors",
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go
index 66be4dc820cd983b702ddd6349473fd8a5192c76..b781dd3dc0973a15ad850157c770506a6f9ee58b 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go
@@ -12,8 +12,8 @@ import (
 
 const (
 	requestIntervalMS         = 250
-	maxTotalRequests          = 300
-	maxPendingRequests        = maxTotalRequests
+	maxTotalRequesters        = 300
+	maxPendingRequests        = maxTotalRequesters
 	maxPendingRequestsPerPeer = 75
 	peerTimeoutSeconds        = 15
 	minRecvRate               = 10240 // 10Kb/s
@@ -36,8 +36,8 @@ type BlockPool struct {
 
 	// block requests
 	mtx        sync.Mutex
-	requests   map[int]*bpRequester
-	height     int   // the lowest key in requests.
+	requesters map[int]*bpRequester
+	height     int   // the lowest key in requesters.
 	numPending int32 // number of requests pending assignment or block response
 
 	// peers
@@ -52,7 +52,7 @@ func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- s
 	bp := &BlockPool{
 		peers: make(map[string]*bpPeer),
 
-		requests:   make(map[int]*bpRequester),
+		requesters: make(map[int]*bpRequester),
 		height:     start,
 		numPending: 0,
 
@@ -65,7 +65,7 @@ func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- s
 
 func (pool *BlockPool) OnStart() error {
 	pool.QuitService.OnStart()
-	go pool.makeRequestsRoutine()
+	go pool.makeRequestersRoutine()
 	pool.startTime = time.Now()
 	return nil
 }
@@ -74,8 +74,8 @@ func (pool *BlockPool) OnStop() {
 	pool.QuitService.OnStop()
 }
 
-// Run spawns requests as needed.
-func (pool *BlockPool) makeRequestsRoutine() {
+// Run spawns requesters as needed.
+func (pool *BlockPool) makeRequestersRoutine() {
 	for {
 		if !pool.IsRunning() {
 			break
@@ -86,14 +86,14 @@ func (pool *BlockPool) makeRequestsRoutine() {
 			time.Sleep(requestIntervalMS * time.Millisecond)
 			// check for timed out peers
 			pool.removeTimedoutPeers()
-		} else if len(pool.requests) >= maxTotalRequests {
+		} else if len(pool.requesters) >= maxTotalRequesters {
 			// sleep for a bit.
 			time.Sleep(requestIntervalMS * time.Millisecond)
 			// check for timed out peers
 			pool.removeTimedoutPeers()
 		} else {
 			// request for more blocks.
-			pool.makeNextRequest()
+			pool.makeNextRequester()
 		}
 	}
 }
@@ -147,10 +147,10 @@ func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block)
 	pool.mtx.Lock() // Lock
 	defer pool.mtx.Unlock()
 
-	if r := pool.requests[pool.height]; r != nil {
+	if r := pool.requesters[pool.height]; r != nil {
 		first = r.getBlock()
 	}
-	if r := pool.requests[pool.height+1]; r != nil {
+	if r := pool.requesters[pool.height+1]; r != nil {
 		second = r.getBlock()
 	}
 	return
@@ -162,14 +162,18 @@ func (pool *BlockPool) PopRequest() {
 	pool.mtx.Lock() // Lock
 	defer pool.mtx.Unlock()
 
-	/*  The block can disappear at any time, due to removePeer().
-	if r := pool.requests[pool.height]; r == nil || r.block == nil {
-		PanicSanity("PopRequest() requires a valid block")
+	if r := pool.requesters[pool.height]; r != nil {
+		/*  The block can disappear at any time, due to removePeer().
+		if r := pool.requesters[pool.height]; r == nil || r.block == nil {
+			PanicSanity("PopRequest() requires a valid block")
+		}
+		*/
+		r.Stop()
+		delete(pool.requesters, pool.height)
+		pool.height++
+	} else {
+		PanicSanity(Fmt("Expected requester to pop, got nothing at height %v", pool.height))
 	}
-	*/
-
-	delete(pool.requests, pool.height)
-	pool.height++
 }
 
 // Invalidates the block at pool.height,
@@ -178,11 +182,11 @@ func (pool *BlockPool) RedoRequest(height int) {
 	pool.mtx.Lock() // Lock
 	defer pool.mtx.Unlock()
 
-	request := pool.requests[height]
+	request := pool.requesters[height]
 	if request.block == nil {
 		PanicSanity("Expected block to be non-nil")
 	}
-	// RemovePeer will redo all requests associated with this peer.
+	// RemovePeer will redo all requesters associated with this peer.
 	// TODO: record this malfeasance
 	pool.RemovePeer(request.peerID) // Lock on peersMtx.
 }
@@ -192,12 +196,12 @@ func (pool *BlockPool) AddBlock(peerID string, block *types.Block, blockSize int
 	pool.mtx.Lock() // Lock
 	defer pool.mtx.Unlock()
 
-	request := pool.requests[block.Height]
-	if request == nil {
+	requester := pool.requesters[block.Height]
+	if requester == nil {
 		return
 	}
 
-	if request.setBlock(block, peerID) {
+	if requester.setBlock(block, peerID) {
 		pool.numPending--
 		peer := pool.getPeer(peerID)
 		peer.decrPending(blockSize)
@@ -228,10 +232,10 @@ func (pool *BlockPool) RemovePeer(peerID string) {
 }
 
 func (pool *BlockPool) removePeer(peerID string) {
-	for _, request := range pool.requests {
-		if request.getPeerID() == peerID {
+	for _, requester := range pool.requesters {
+		if requester.getPeerID() == peerID {
 			pool.numPending++
-			go request.redo() // pick another peer and ...
+			go requester.redo() // pick another peer and ...
 		}
 	}
 	delete(pool.peers, peerID)
@@ -269,14 +273,14 @@ func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer {
 	return nil
 }
 
-func (pool *BlockPool) makeNextRequest() {
+func (pool *BlockPool) makeNextRequester() {
 	pool.mtx.Lock() // Lock
 	defer pool.mtx.Unlock()
 
-	nextHeight := pool.height + len(pool.requests)
+	nextHeight := pool.height + len(pool.requesters)
 	request := newBPRequester(pool, nextHeight)
 
-	pool.requests[nextHeight] = request
+	pool.requesters[nextHeight] = request
 	pool.numPending++
 
 	request.Start()
@@ -301,12 +305,12 @@ func (pool *BlockPool) debug() string {
 	defer pool.mtx.Unlock()
 
 	str := ""
-	for h := pool.height; h < pool.height+len(pool.requests); h++ {
-		if pool.requests[h] == nil {
+	for h := pool.height; h < pool.height+len(pool.requesters); h++ {
+		if pool.requesters[h] == nil {
 			str += Fmt("H(%v):X ", h)
 		} else {
 			str += Fmt("H(%v):", h)
-			str += Fmt("B?(%v) ", pool.requests[h].block != nil)
+			str += Fmt("B?(%v) ", pool.requesters[h].block != nil)
 		}
 	}
 	return str
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go
index bd50d5a679588550e537f29b1f174fe6089831db..1cab4ce61093eefd3fecc1631d45fe7f255071e3 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go
@@ -192,7 +192,7 @@ FOR_LOOP:
 		case _ = <-switchToConsensusTicker.C:
 			height, numPending := bcR.pool.GetStatus()
 			outbound, inbound, _ := bcR.Switch.NumPeers()
-			log.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requests),
+			log.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
 				"outbound", outbound, "inbound", inbound)
 			if bcR.pool.IsCaughtUp() {
 				log.Notice("Time to switch to consensus reactor!", "height", height)
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/io.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/io.go
index 0c33b07569c3977bab84e037a7c567a759ebd923..378c19fc6a7361e9ba49877f2e2df471db19a40c 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/io.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/io.go
@@ -1,6 +1,8 @@
 package common
 
 import (
+	"bytes"
+	"errors"
 	"io"
 )
 
@@ -22,3 +24,52 @@ func (pr *PrefixedReader) Read(p []byte) (n int, err error) {
 		return pr.reader.Read(p)
 	}
 }
+
+// NOTE: Not goroutine safe
+type BufferCloser struct {
+	bytes.Buffer
+	Closed bool
+}
+
+func NewBufferCloser(buf []byte) *BufferCloser {
+	return &BufferCloser{
+		*bytes.NewBuffer(buf),
+		false,
+	}
+}
+
+func (bc *BufferCloser) Close() error {
+	if bc.Closed {
+		return errors.New("BufferCloser already closed")
+	}
+	bc.Closed = true
+	return nil
+}
+
+func (bc *BufferCloser) Write(p []byte) (n int, err error) {
+	if bc.Closed {
+		return 0, errors.New("Cannot write to closed BufferCloser")
+	}
+	return bc.Buffer.Write(p)
+}
+
+func (bc *BufferCloser) WriteByte(c byte) error {
+	if bc.Closed {
+		return errors.New("Cannot write to closed BufferCloser")
+	}
+	return bc.Buffer.WriteByte(c)
+}
+
+func (bc *BufferCloser) WriteRune(r rune) (n int, err error) {
+	if bc.Closed {
+		return 0, errors.New("Cannot write to closed BufferCloser")
+	}
+	return bc.Buffer.WriteRune(r)
+}
+
+func (bc *BufferCloser) WriteString(s string) (n int, err error) {
+	if bc.Closed {
+		return 0, errors.New("Cannot write to closed BufferCloser")
+	}
+	return bc.Buffer.WriteString(s)
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/config.go
index 43963e25ad7f5aa302db7df3e3d26dab5e160850..43bdf09441e6e0fd8d65dbc8a870126acce38a92 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/config.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/config.go
@@ -1,8 +1,11 @@
 package config
 
 import (
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/naoina/toml"
 	"sync"
 	"time"
+
+	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
 )
 
 type Config interface {
@@ -19,28 +22,66 @@ type Config interface {
 	Set(key string, value interface{})
 }
 
-type MapConfig map[string]interface{}
+type MapConfig struct {
+	required map[string]struct{} // blows up if trying to use before setting.
+	data     map[string]interface{}
+}
+
+func ReadMapConfigFromFile(filePath string) (MapConfig, error) {
+	var configData = make(map[string]interface{})
+	fileBytes := MustReadFile(filePath)
+	err := toml.Unmarshal(fileBytes, configData)
+	if err != nil {
+		return MapConfig{}, err
+	}
+	return NewMapConfig(configData), nil
+}
+
+func NewMapConfig(data map[string]interface{}) MapConfig {
+	if data == nil {
+		data = make(map[string]interface{})
+	}
+	return MapConfig{
+		required: make(map[string]struct{}),
+		data:     data,
+	}
+}
 
-func (cfg MapConfig) Get(key string) interface{}    { return cfg[key] }
-func (cfg MapConfig) GetBool(key string) bool       { return cfg[key].(bool) }
-func (cfg MapConfig) GetFloat64(key string) float64 { return cfg[key].(float64) }
-func (cfg MapConfig) GetInt(key string) int         { return cfg[key].(int) }
-func (cfg MapConfig) GetString(key string) string   { return cfg[key].(string) }
+func (cfg MapConfig) Get(key string) interface{} {
+	if _, ok := cfg.required[key]; ok {
+		PanicSanity(Fmt("config key %v is required but was not set.", key))
+	}
+	return cfg.data[key]
+}
+func (cfg MapConfig) GetBool(key string) bool       { return cfg.Get(key).(bool) }
+func (cfg MapConfig) GetFloat64(key string) float64 { return cfg.Get(key).(float64) }
+func (cfg MapConfig) GetInt(key string) int         { return cfg.Get(key).(int) }
+func (cfg MapConfig) GetString(key string) string   { return cfg.Get(key).(string) }
 func (cfg MapConfig) GetStringMap(key string) map[string]interface{} {
-	return cfg[key].(map[string]interface{})
+	return cfg.Get(key).(map[string]interface{})
 }
 func (cfg MapConfig) GetStringMapString(key string) map[string]string {
-	return cfg[key].(map[string]string)
+	return cfg.Get(key).(map[string]string)
+}
+func (cfg MapConfig) GetStringSlice(key string) []string { return cfg.Get(key).([]string) }
+func (cfg MapConfig) GetTime(key string) time.Time       { return cfg.Get(key).(time.Time) }
+func (cfg MapConfig) IsSet(key string) bool              { _, ok := cfg.data[key]; return ok }
+func (cfg MapConfig) Set(key string, value interface{}) {
+	delete(cfg.required, key)
+	cfg.data[key] = value
 }
-func (cfg MapConfig) GetStringSlice(key string) []string { return cfg[key].([]string) }
-func (cfg MapConfig) GetTime(key string) time.Time       { return cfg[key].(time.Time) }
-func (cfg MapConfig) IsSet(key string) bool              { _, ok := cfg[key]; return ok }
-func (cfg MapConfig) Set(key string, value interface{})  { cfg[key] = value }
 func (cfg MapConfig) SetDefault(key string, value interface{}) {
+	delete(cfg.required, key)
+	if cfg.IsSet(key) {
+		return
+	}
+	cfg.data[key] = value
+}
+func (cfg MapConfig) SetRequired(key string) {
 	if cfg.IsSet(key) {
 		return
 	}
-	cfg[key] = value
+	cfg.required[key] = struct{}{}
 }
 
 //--------------------------------------------------------------------------------
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go
index a69e321ae96fccdd5c1e85b894bba43fca4c5f16..ae794c785f7570d7145c21b1ac64189cdc7e4592 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go
@@ -1,7 +1,6 @@
 package tendermint
 
 import (
-	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/naoina/toml"
 	"os"
 	"path"
 	"strings"
@@ -25,7 +24,6 @@ func initTMRoot(rootDir string) {
 	EnsureDir(rootDir)
 
 	configFilePath := path.Join(rootDir, "config.toml")
-	genesisFilePath := path.Join(rootDir, "genesis.json")
 
 	// Write default config file if missing.
 	if !FileExists(configFilePath) {
@@ -33,19 +31,14 @@ func initTMRoot(rootDir string) {
 		// moniker := cfg.Prompt("Type hostname: ", "anonymous")
 		MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")))
 	}
-	if !FileExists(genesisFilePath) {
-		MustWriteFile(genesisFilePath, []byte(defaultGenesis))
-	}
 }
 
 func GetConfig(rootDir string) cfg.Config {
 	rootDir = getTMRoot(rootDir)
 	initTMRoot(rootDir)
 
-	var mapConfig = cfg.MapConfig(make(map[string]interface{}))
 	configFilePath := path.Join(rootDir, "config.toml")
-	configFileBytes := MustReadFile(configFilePath)
-	err := toml.Unmarshal(configFileBytes, mapConfig)
+	mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath)
 	if err != nil {
 		Exit(Fmt("Could not read config: %v", err))
 	}
@@ -54,7 +47,10 @@ func GetConfig(rootDir string) cfg.Config {
 	if mapConfig.IsSet("chain_id") {
 		Exit("Cannot set 'chain_id' via config.toml")
 	}
-	mapConfig.SetDefault("chain_id", "tendermint_testnet_10")
+	if mapConfig.IsSet("revision_file") {
+		Exit("Cannot set 'revision_file' via config.toml. It must match what's in the Makefile")
+	}
+	mapConfig.SetRequired("chain_id") // blows up if you try to use it before setting.
 	mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json")
 	mapConfig.SetDefault("moniker", "anonymous")
 	mapConfig.SetDefault("node_laddr", "0.0.0.0:46656")
@@ -65,24 +61,22 @@ func GetConfig(rootDir string) cfg.Config {
 	mapConfig.SetDefault("priv_validator_file", rootDir+"/priv_validator.json")
 	mapConfig.SetDefault("db_backend", "leveldb")
 	mapConfig.SetDefault("db_dir", rootDir+"/data")
+	mapConfig.SetDefault("vm_log", true)
 	mapConfig.SetDefault("log_level", "info")
 	mapConfig.SetDefault("rpc_laddr", "0.0.0.0:46657")
-	mapConfig.SetDefault("revisions_file", rootDir+"/revisions")
+	mapConfig.SetDefault("prof_laddr", "")
+	mapConfig.SetDefault("revision_file", rootDir+"/revision")
+	mapConfig.SetDefault("local_routing", false)
+	mapConfig.SetDefault("signer", "default")
 	return mapConfig
 }
 
-func ensureDefault(mapConfig cfg.MapConfig, key string, value interface{}) {
-	if !mapConfig.IsSet(key) {
-		mapConfig[key] = value
-	}
-}
-
 var defaultConfigTmpl = `# This is a TOML config file.
 # For more information, see https://github.com/toml-lang/toml
 
 moniker = "__MONIKER__"
 node_laddr = "0.0.0.0:46656"
-seeds = "goldenalchemist.chaintest.net:46656"
+seeds = ""
 fast_sync = true
 db_backend = "leveldb"
 log_level = "notice"
@@ -93,63 +87,3 @@ func defaultConfig(moniker string) (defaultConfig string) {
 	defaultConfig = strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1)
 	return
 }
-
-var defaultGenesis = `{
-    "chain_id": "tendermint_testnet_11",
-    "accounts": [
-        {
-            "address": "9FCBA7F840A0BFEBBE755E853C9947270A912D04",
-            "amount": 1995999998000000
-        },
-        {
-            "address": "964B1493BBE3312278B7DEB94C39149F7899A345",
-            "amount": 100000000000000
-        },
-        {
-            "address": "B9FA4AB462B9C6BF6A62DB4AE77C9E7087209A04",
-            "amount": 1000000000000
-        },
-        {
-            "address": "F171824590D69386F709E7B6704B369C5A370D60",
-            "amount": 1000000000000
-        },
-        {
-            "address": "56EFE746A13D9A6054AC89C3E2A361C2DB8B9EAE",
-            "amount": 1000000000000
-        },
-        {
-            "address": "7C2E032D8407EDF66A04D88CF0E1D9B15D98AE2D",
-            "amount": 1000000000000
-        },
-        {
-            "address": "A88A61069B6660F30F65E8786AFDD4F1D8F625E9",
-            "amount": 1000000
-        },
-        {
-            "address": "EE2EE9247973B4AFC3867CFE5F415410AC251B61",
-            "amount": 1000000
-        }
-    ],
-    "validators": [
-        {
-            "pub_key": [1, "178EC6008A4364508979C70CBF100BD4BCBAA12DDE6251F5F486B4FD09014F06"],
-            "amount": 100000000000
-        },
-        {
-            "pub_key": [1, "2A77777CC51467DE42350D4A8F34720D527734189BE64C7A930DD169E1FED3C6"],
-            "amount": 100000000000
-        },
-        {
-            "pub_key": [1, "3718E69D09B11B3AD3FA31AEF07EC416D2AEED241CACE7B0F30AE9803FFB0F08"],
-            "amount": 100000000000
-        },
-        {
-            "pub_key": [1, "C6B0440DEACD1E4CF1C736CEB8E38E788B700BA2B2045A55CB657A455CF5F889"],
-            "amount": 100000000000
-        },
-        {
-            "pub_key": [1, "3BA1190D54F91EFBF8B0125F7EC116AD4BA2894B6EE38564A5D5FD3230D91F7B"],
-            "amount": 100000000000
-        }
-    ]
-}`
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/genesis.json b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/genesis.json
new file mode 100644
index 0000000000000000000000000000000000000000..eca00696edb4414df42fe6770cc23cde5a758529
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/genesis.json
@@ -0,0 +1,75 @@
+{
+    "chain_id": "tendermint_testnet_11.c",
+    "accounts": [
+        {
+            "address": "9FCBA7F840A0BFEBBE755E853C9947270A912D04",
+            "amount": 1991999998000000
+        },
+        {
+            "address": "964B1493BBE3312278B7DEB94C39149F7899A345",
+            "amount": 100000000000000
+        },
+        {
+            "address": "B9FA4AB462B9C6BF6A62DB4AE77C9E7087209A04",
+            "amount": 1000000000000
+        },
+        {
+            "address": "F171824590D69386F709E7B6704B369C5A370D60",
+            "amount": 1000000000000
+        },
+        {
+            "address": "56EFE746A13D9A6054AC89C3E2A361C2DB8B9EAE",
+            "amount": 1000000000000
+        },
+        {
+            "address": "7C2E032D8407EDF66A04D88CF0E1D9B15D98AE2D",
+            "amount": 1000000000000
+        },
+        {
+            "address": "636EF5823E082AD66EBC203FD4DFB1031F0C61CA",
+            "amount": 1000000000000
+        },
+        {
+            "address": "9008419E6351360A59B124E707E4CA2A5BFB9BE6",
+            "amount": 1000000000000
+        },
+        {
+            "address": "C78F48919B8A4030AD3E5ED643F8D2302E41953D",
+            "amount": 1000000000000
+        },
+        {
+            "address": "5290AC90CE2422DDC3F91F6A246F7E3C542EA51A",
+            "amount": 1000000000000
+        },
+        {
+            "address": "A88A61069B6660F30F65E8786AFDD4F1D8F625E9",
+            "amount": 1000000
+        },
+        {
+            "address": "EE2EE9247973B4AFC3867CFE5F415410AC251B61",
+            "amount": 1000000
+        }
+    ],
+    "validators": [
+        {
+            "pub_key": [1, "178EC6008A4364508979C70CBF100BD4BCBAA12DDE6251F5F486B4FD09014F06"],
+            "amount": 100000000000
+        },
+        {
+            "pub_key": [1, "2A77777CC51467DE42350D4A8F34720D527734189BE64C7A930DD169E1FED3C6"],
+            "amount": 100000000000
+        },
+        {
+            "pub_key": [1, "3718E69D09B11B3AD3FA31AEF07EC416D2AEED241CACE7B0F30AE9803FFB0F08"],
+            "amount": 100000000000
+        },
+        {
+            "pub_key": [1, "C6B0440DEACD1E4CF1C736CEB8E38E788B700BA2B2045A55CB657A455CF5F889"],
+            "amount": 100000000000
+        },
+        {
+            "pub_key": [1, "3BA1190D54F91EFBF8B0125F7EC116AD4BA2894B6EE38564A5D5FD3230D91F7B"],
+            "amount": 100000000000
+        }
+    ]
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go
index 35a1b9b4362a6ce6d8cf1ad8b11f05dc0fc1b154..5347f988e852b580a6e393d3515ecc4548b95420 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go
@@ -3,7 +3,6 @@
 package tendermint_test
 
 import (
-	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/naoina/toml"
 	"os"
 	"path"
 	"strings"
@@ -47,10 +46,8 @@ func GetConfig(rootDir string) cfg.Config {
 	rootDir = getTMRoot(rootDir)
 	initTMRoot(rootDir)
 
-	var mapConfig = cfg.MapConfig(make(map[string]interface{}))
 	configFilePath := path.Join(rootDir, "config.toml")
-	configFileBytes := MustReadFile(configFilePath)
-	err := toml.Unmarshal(configFileBytes, mapConfig)
+	mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath)
 	if err != nil {
 		Exit(Fmt("Could not read config: %v", err))
 	}
@@ -70,17 +67,15 @@ func GetConfig(rootDir string) cfg.Config {
 	mapConfig.SetDefault("db_backend", "memdb")
 	mapConfig.SetDefault("db_dir", rootDir+"/data")
 	mapConfig.SetDefault("log_level", "debug")
+	mapConfig.SetDefault("vm_log", true)
 	mapConfig.SetDefault("rpc_laddr", "0.0.0.0:36657")
-	mapConfig.SetDefault("revisions_file", rootDir+"/revisions")
+	mapConfig.SetDefault("prof_laddr", "")
+	mapConfig.SetDefault("revision_file", rootDir+"/revision")
+	mapConfig.SetDefault("local_routing", false)
+	mapConfig.SetDefault("signer", "default")
 	return mapConfig
 }
 
-func ensureDefault(mapConfig cfg.MapConfig, key string, value interface{}) {
-	if !mapConfig.IsSet(key) {
-		mapConfig[key] = value
-	}
-}
-
 var defaultConfigTmpl = `# This is a TOML config file.
 # For more information, see https://github.com/toml-lang/toml
 
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go
index 0b4378ee0fe4570c2a74a96d640df4d83a1a84ba..ad79bf52fa43f426c794b1b1810dd28e37e1dfba 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go
@@ -22,8 +22,6 @@ const (
 	DataChannel  = byte(0x21)
 	VoteChannel  = byte(0x22)
 
-	PeerStateKey = "ConsensusReactor.peerState"
-
 	peerGossipSleepDuration = 100 * time.Millisecond // Time to sleep if there's nothing to send.
 )
 
@@ -107,7 +105,7 @@ func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer) {
 
 	// Create peerState for peer
 	peerState := NewPeerState(peer)
-	peer.Data.Set(PeerStateKey, peerState)
+	peer.Data.Set(types.PeerStateKey, peerState)
 
 	// Begin gossip routines for this peer.
 	go conR.gossipDataRoutine(peer, peerState)
@@ -138,7 +136,7 @@ func (conR *ConsensusReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte
 	}
 
 	// Get peer states
-	ps := peer.Data.Get(PeerStateKey).(*PeerState)
+	ps := peer.Data.Get(types.PeerStateKey).(*PeerState)
 	_, msg, err := DecodeMessage(msgBytes)
 	if err != nil {
 		log.Warn("Error decoding message", "channel", chID, "peer", peer, "msg", msg, "error", err, "bytes", msgBytes)
@@ -455,35 +453,21 @@ OUTER_LOOP:
 				}
 			}
 			// If there are prevotes to send...
-			if rs.Round == prs.Round && prs.Step <= RoundStepPrevote {
-				if ps.PickSendVote(rs.Votes.Prevotes(rs.Round)) {
-					log.Info("Picked rs.Prevotes(rs.Round) to send")
-					continue OUTER_LOOP
-				}
-			}
-			// If there are precommits to send...
-			if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit {
-				if ps.PickSendVote(rs.Votes.Precommits(rs.Round)) {
-					log.Info("Picked rs.Precommits(rs.Round) to send")
-					continue OUTER_LOOP
-				}
-			}
-			// If there are prevotes to send for the last round...
-			if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrevote {
+			if prs.Step <= RoundStepPrevote && prs.Round != -1 && prs.Round <= rs.Round {
 				if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
 					log.Info("Picked rs.Prevotes(prs.Round) to send")
 					continue OUTER_LOOP
 				}
 			}
-			// If there are precommits to send for the last round...
-			if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrecommit {
+			// If there are precommits to send...
+			if prs.Step <= RoundStepPrecommit && prs.Round != -1 && prs.Round <= rs.Round {
 				if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) {
 					log.Info("Picked rs.Precommits(prs.Round) to send")
 					continue OUTER_LOOP
 				}
 			}
 			// If there are POLPrevotes to send...
-			if 0 <= prs.ProposalPOLRound {
+			if prs.ProposalPOLRound != -1 {
 				if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
 					if ps.PickSendVote(polPrevotes) {
 						log.Info("Picked rs.Prevotes(prs.ProposalPOLRound) to send")
@@ -548,8 +532,8 @@ type PeerRoundState struct {
 	Precommits               *BitArray           // All precommits peer has for this round
 	LastCommitRound          int                 // Round of commit for last height. -1 if none.
 	LastCommit               *BitArray           // All commit precommits of commit for last height.
-	CatchupCommitRound       int                 // Round that we believe commit round is. -1 if none.
-	CatchupCommit            *BitArray           // All commit precommits peer has for this height
+	CatchupCommitRound       int                 // Round that we have commit for. Not necessarily unique. -1 if none.
+	CatchupCommit            *BitArray           // All commit precommits peer has for this height & CatchupCommitRound
 }
 
 //-----------------------------------------------------------------------------
@@ -588,6 +572,14 @@ func (ps *PeerState) GetRoundState() *PeerRoundState {
 	return &prs
 }
 
+// Returns an atomic snapshot of the PeerRoundState's height
+// used by the mempool to ensure peers are caught up before broadcasting new txs
+func (ps *PeerState) GetHeight() int {
+	ps.mtx.Lock()
+	defer ps.mtx.Unlock()
+	return ps.PeerRoundState.Height
+}
+
 func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
 	ps.mtx.Lock()
 	defer ps.mtx.Unlock()
@@ -696,14 +688,18 @@ func (ps *PeerState) getVoteBitArray(height, round int, type_ byte) *BitArray {
 	return nil
 }
 
-// NOTE: 'round' is what we know to be the commit round for height.
+// 'round': A round for which we have a +2/3 commit.
 func (ps *PeerState) ensureCatchupCommitRound(height, round int, numValidators int) {
 	if ps.Height != height {
 		return
 	}
-	if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round {
-		PanicSanity(Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
-	}
+	/*
+		NOTE: This is wrong, 'round' could change.
+		e.g. if orig round is not the same as block LastValidation round.
+		if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round {
+			PanicSanity(Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
+		}
+	*/
 	if ps.CatchupCommitRound == round {
 		return // Nothing to do!
 	}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go
index 72ec17c6b4bb0df9a0e86900bc755e5f7d941f9c..2f8c5e746880374abe125e4901e65c24bf0b5b98 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go
@@ -1308,14 +1308,12 @@ func (cs *ConsensusState) saveBlock(block *types.Block, blockParts *types.PartSe
 	cs.stagedState.Save()
 
 	// Update mempool.
-	cs.mempoolReactor.Mempool.ResetForBlockAndState(block, cs.stagedState)
+	cs.mempoolReactor.ResetForBlockAndState(block, cs.stagedState)
 
 	// Fire off event
 	if cs.evsw != nil && cs.evc != nil {
-		go func(block *types.Block) {
-			cs.evsw.FireEvent(types.EventStringNewBlock(), types.EventDataNewBlock{block})
-			cs.evc.Flush()
-		}(block)
+		cs.evsw.FireEvent(types.EventStringNewBlock(), types.EventDataNewBlock{block})
+		go cs.evc.Flush()
 	}
 
 }
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a3d8b3ae85065f2a3afd35c5cc69a3ddd87fcabc
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go
@@ -0,0 +1,1177 @@
+package consensus
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+	"time"
+
+	_ "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/events"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
+)
+
+/*
+
+ProposeSuite
+x * TestEnterProposeNoValidator - timeout into prevote round
+x * TestEnterPropose - finish propose without timing out (we have the proposal)
+x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevote and precommit nil
+FullRoundSuite
+x * TestFullRound1 - 1 val, full successful round
+x * TestFullRoundNil - 1 val, full round of nil
+x * TestFullRound2 - 2 vals, both required for fuill round
+LockSuite
+x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first.
+x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
+x * TestLockPOLUnlock - 4 vals, one precommits, other 3 polka nil at next round, so we unlock and precomit nil
+x * TestLockPOLSafety1 - 4 vals. We shouldn't change lock based on polka at earlier round
+x * TestLockPOLSafety2 - 4 vals. After unlocking, we shouldn't relock based on polka at earlier round
+  * TestNetworkLock - once +1/3 precommits, network should be locked
+  * TestNetworkLockPOL - once +1/3 precommits, the block with more recent polka is committed
+SlashingSuite
+x * TestSlashingPrevotes - a validator prevoting twice in a round gets slashed
+x * TestSlashingPrecommits - a validator precomitting twice in a round gets slashed
+CatchupSuite
+  * TestCatchup - if we might be behind and we've seen any 2/3 prevotes, round skip to new round, precommit, or prevote
+HaltSuite
+x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we should still commit
+
+*/
+
+//----------------------------------------------------------------------------------------------------
+// ProposeSuite
+
+func init() {
+	fmt.Println("")
+	timeoutPropose = 1000 * time.Millisecond
+}
+
+// a non-validator should timeout into the prevote round
+func TestEnterProposeNoPrivValidator(t *testing.T) {
+	css, _ := simpleConsensusState(1)
+	cs := css[0]
+	cs.SetPrivValidator(nil)
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs.SetFireable(evsw)
+
+	// starts a go routine for EnterPropose
+	cs.EnterNewRound(cs.Height, 0, false)
+
+	// go to prevote
+	<-cs.NewStepCh()
+
+	// if we're not a validator, EnterPropose should timeout
+	select {
+	case rs := <-cs.NewStepCh():
+		log.Info(rs.String())
+		t.Fatal("Expected EnterPropose to timeout")
+	case <-timeoutChan:
+		rs := cs.GetRoundState()
+		if rs.Proposal != nil {
+			t.Error("Expected to make no proposal, since no privValidator")
+		}
+		break
+	}
+}
+
+// a validator should not timeout of the prevote round (TODO: unless the block is really big!)
+func TestEnterPropose(t *testing.T) {
+	css, _ := simpleConsensusState(1)
+	cs := css[0]
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs.SetFireable(evsw)
+
+	// starts a go routine for EnterPropose
+	cs.EnterNewRound(cs.Height, 0, false)
+
+	// go to prevote
+	<-cs.NewStepCh()
+
+	// if we are a validator, we expect it not to timeout
+	select {
+	case <-cs.NewStepCh():
+		rs := cs.GetRoundState()
+
+		// Check that Proposal, ProposalBlock, ProposalBlockParts are set.
+		if rs.Proposal == nil {
+			t.Error("rs.Proposal should be set")
+		}
+		if rs.ProposalBlock == nil {
+			t.Error("rs.ProposalBlock should be set")
+		}
+		if rs.ProposalBlockParts.Total() == 0 {
+			t.Error("rs.ProposalBlockParts should be set")
+		}
+		break
+	case <-timeoutChan:
+		t.Fatal("Expected EnterPropose not to timeout")
+	}
+}
+
+func TestBadProposal(t *testing.T) {
+	css, privVals := simpleConsensusState(2)
+	cs1, cs2 := css[0], css[1]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs1.SetFireable(evsw)
+
+	// make the second validator the proposer
+	propBlock := changeProposer(t, cs1, cs2)
+
+	// make the block bad by tampering with statehash
+	stateHash := propBlock.StateHash
+	stateHash[0] = byte((stateHash[0] + 1) % 255)
+	propBlock.StateHash = stateHash
+	propBlockParts := propBlock.MakePartSet()
+	proposal := types.NewProposal(cs2.Height, cs2.Round, propBlockParts.Header(), cs2.Votes.POLRound())
+	if err := cs2.privValidator.SignProposal(cs2.state.ChainID, proposal); err != nil {
+		t.Fatal("failed to sign bad proposal", err)
+	}
+
+	// start round
+	cs1.EnterNewRound(cs1.Height, 0, false)
+
+	// now we're on a new round and not the proposer
+	<-cs1.NewStepCh()
+	// so set the proposal block (and fix voting power)
+	cs1.mtx.Lock()
+	cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = proposal, propBlock, propBlockParts
+	fixVotingPower(t, cs1, privVals[1].Address)
+	cs1.mtx.Unlock()
+	// and wait for timeout
+	<-timeoutChan
+
+	// go to prevote, prevote for nil (proposal is bad)
+	<-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], nil)
+
+	// add bad prevote from cs2. we should precommit nil
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header())
+	_, _, _ = <-cs1.NewStepCh(), <-timeoutChan, <-cs1.NewStepCh()
+	validatePrecommit(t, cs1, 0, 0, privVals[0], nil, nil)
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header())
+}
+
+//----------------------------------------------------------------------------------------------------
+// FulLRoundSuite
+
+// propose, prevote, and precommit a block
+func TestFullRound1(t *testing.T) {
+	css, privVals := simpleConsensusState(1)
+	cs := css[0]
+
+	// starts a go routine for EnterPropose
+	cs.EnterNewRound(cs.Height, 0, false)
+	// wait to finish propose and prevote
+	_, _ = <-cs.NewStepCh(), <-cs.NewStepCh()
+
+	// we should now be in precommit
+	// verify our prevote is there
+	cs.mtx.Lock()
+	propBlockHash := cs.ProposalBlock.Hash()
+	cs.mtx.Unlock()
+
+	// the proposed block should be prevoted, precommitted, and locked
+	validatePrevoteAndPrecommit(t, cs, 0, 0, privVals[0], propBlockHash, propBlockHash, nil)
+}
+
+// nil is proposed, so prevote and precommit nil
+func TestFullRoundNil(t *testing.T) {
+	css, privVals := simpleConsensusState(1)
+	cs := css[0]
+	cs.newStepCh = make(chan *RoundState) // so it blocks
+	cs.SetPrivValidator(nil)
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs.SetFireable(evsw)
+
+	// starts a go routine for EnterPropose
+	cs.EnterNewRound(cs.Height, 0, false)
+
+	// wait to finish propose (we should time out)
+	<-cs.NewStepCh()
+	cs.SetPrivValidator(privVals[0]) // this might be a race condition (uses the mutex that EnterPropose has just released and EnterPrevote is about to grab)
+	<-timeoutChan
+
+	// wait to finish prevote
+	<-cs.NewStepCh()
+
+	// should prevote and precommit nil
+	validatePrevoteAndPrecommit(t, cs, 0, 0, privVals[0], nil, nil, nil)
+}
+
+// run through propose, prevote, precommit commit with two validators
+// where the first validator has to wait for votes from the second
+func TestFullRound2(t *testing.T) {
+	css, privVals := simpleConsensusState(2)
+	cs1, cs2 := css[0], css[1]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+	cs2.newStepCh = make(chan *RoundState) // so it blocks
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+
+	// we should now be stuck in limbo forever, waiting for more prevotes
+	ensureNoNewStep(t, cs1)
+
+	propBlockHash, propPartsHeader := cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header()
+
+	// prevote arrives from cs2:
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader)
+
+	// wait to finish precommit
+	<-cs1.NewStepCh()
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], propBlockHash, propBlockHash)
+
+	// we should now be stuck in limbo forever, waiting for more precommits
+	ensureNoNewStep(t, cs1)
+
+	// precommit arrives from cs2:
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader)
+
+	// wait to finish commit, propose in next height
+	_, rs := <-cs1.NewStepCh(), <-cs1.NewStepCh()
+	if rs.Height != 2 {
+		t.Fatal("Expected height to increment")
+	}
+}
+
+//------------------------------------------------------------------------------------------
+// LockSuite
+
+// two validators, 4 rounds.
+// val1 proposes the first 2 rounds, and is locked in the first.
+// val2 proposes the next two. val1 should precommit nil on all (except first where he locks)
+func TestLockNoPOL(t *testing.T) {
+	css, privVals := simpleConsensusState(2)
+	cs1, cs2 := css[0], css[1]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs1.SetFireable(evsw)
+
+	/*
+		Round1 (cs1, B) // B B // B B2
+	*/
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+
+	// we should now be stuck in limbo forever, waiting for more prevotes
+	// prevote arrives from cs2:
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+
+	cs1.mtx.Lock() // XXX: sigh
+	theBlockHash := cs1.ProposalBlock.Hash()
+	cs1.mtx.Unlock()
+
+	// wait to finish precommit
+	<-cs1.NewStepCh()
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], theBlockHash, theBlockHash)
+
+	// we should now be stuck in limbo forever, waiting for more precommits
+	// lets add one for a different block
+	// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
+	hash := cs1.ProposalBlock.Hash()
+	hash[0] = byte((hash[0] + 1) % 255)
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+
+	// (note we're entering precommit for a second time this round)
+	// but with invalid args. then we EnterPrecommitWait, and the timeout to new round
+	_, _ = <-cs1.NewStepCh(), <-timeoutChan
+
+	log.Info("#### ONTO ROUND 2")
+	/*
+		Round2 (cs1, B) // B B2
+	*/
+
+	incrementRound(cs2)
+
+	// go to prevote
+	<-cs1.NewStepCh()
+
+	// now we're on a new round and the proposer
+	if cs1.ProposalBlock != cs1.LockedBlock {
+		t.Fatalf("Expected proposal block to be locked block. Got %v, Expected %v", cs1.ProposalBlock, cs1.LockedBlock)
+	}
+
+	// wait to finish prevote
+	<-cs1.NewStepCh()
+
+	// we should have prevoted our locked block
+	validatePrevote(t, cs1, 1, privVals[0], cs1.LockedBlock.Hash())
+
+	// add a conflicting prevote from the other validator
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+
+	// now we're going to enter prevote again, but with invalid args
+	// and then prevote wait, which should timeout. then wait for precommit
+	_, _, _ = <-cs1.NewStepCh(), <-timeoutChan, <-cs1.NewStepCh()
+
+	// the proposed block should still be locked and our precommit added
+	// we should precommit nil and be locked on the proposal
+	validatePrecommit(t, cs1, 1, 0, privVals[0], nil, theBlockHash)
+
+	// add conflicting precommit from cs2
+	// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+
+	// (note we're entering precommit for a second time this round, but with invalid args
+	// then we EnterPrecommitWait and timeout into NewRound
+	_, _ = <-cs1.NewStepCh(), <-timeoutChan
+
+	log.Info("#### ONTO ROUND 3")
+	/*
+		Round3 (cs2, _) // B, B2
+	*/
+
+	incrementRound(cs2)
+
+	// now we're on a new round and not the proposer, so wait for timeout
+	_, _ = <-cs1.NewStepCh(), <-timeoutChan
+	if cs1.ProposalBlock != nil {
+		t.Fatal("Expected proposal block to be nil")
+	}
+
+	// go to prevote, prevote for locked block
+	<-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], cs1.LockedBlock.Hash())
+
+	// TODO: quick fastforward to new round, set proposer
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+	_, _, _ = <-cs1.NewStepCh(), <-timeoutChan, <-cs1.NewStepCh()
+	validatePrecommit(t, cs1, 2, 0, privVals[0], nil, theBlockHash)                             // precommit nil but be locked on proposal
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, cs1.ProposalBlockParts.Header()) // NOTE: conflicting precommits at same height
+
+	<-cs1.NewStepCh()
+
+	// before we time out into new round, set next proposer
+	// and next proposal block
+	_, v1 := cs1.Validators.GetByAddress(privVals[0].Address)
+	v1.VotingPower = 1
+	if updated := cs1.Validators.Update(v1); !updated {
+		t.Fatal("failed to update validator")
+	}
+
+	cs2.decideProposal(cs2.Height, cs2.Round+1)
+	prop, propBlock := cs2.Proposal, cs2.ProposalBlock
+	if prop == nil || propBlock == nil {
+		t.Fatal("Failed to create proposal block with cs2")
+	}
+
+	incrementRound(cs2)
+
+	<-timeoutChan
+
+	log.Info("#### ONTO ROUND 4")
+	/*
+		Round4 (cs2, C) // B C // B C
+	*/
+
+	// now we're on a new round and not the proposer
+	<-cs1.NewStepCh()
+	// so set the proposal block
+	cs1.mtx.Lock()
+	cs1.Proposal, cs1.ProposalBlock = prop, propBlock
+	cs1.mtx.Unlock()
+	// and wait for timeout
+	<-timeoutChan
+	// go to prevote, prevote for locked block (not proposal)
+	<-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], cs1.LockedBlock.Hash())
+
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header())
+	_, _, _ = <-cs1.NewStepCh(), <-timeoutChan, <-cs1.NewStepCh()
+	validatePrecommit(t, cs1, 2, 0, privVals[0], nil, theBlockHash)                                          // precommit nil but locked on proposal
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header()) // NOTE: conflicting precommits at same height
+}
+
+// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
+func TestLockPOLRelock(t *testing.T) {
+	css, privVals := simpleConsensusState(4)
+	cs1, cs2, cs3, cs4 := css[0], css[1], css[2], css[3]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan *types.EventDataRoundState)
+	voteChan := make(chan *types.EventDataVote)
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringVote(), func(data types.EventData) {
+		vote := data.(*types.EventDataVote)
+		// we only fire for our own votes
+		if bytes.Equal(cs1.privValidator.Address, vote.Address) {
+			voteChan <- vote
+		}
+	})
+	cs1.SetFireable(evsw)
+
+	// everything done from perspective of cs1
+
+	/*
+		Round1 (cs1, B) // B B B B// B nil B nil
+
+		eg. cs2 and cs4 didn't see the 2/3 prevotes
+	*/
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
+
+	theBlockHash := cs1.ProposalBlock.Hash()
+
+	// wait to finish precommit after prevotes done
+	// we do this in a go routine with another channel since otherwise
+	// we may get deadlock with EnterPrecommit waiting to send on newStepCh and the final
+	// signAddVoteToFrom waiting for the cs.mtx.Lock
+	donePrecommit := make(chan struct{})
+	go func() {
+		<-voteChan
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), cs2, cs3, cs4)
+	<-donePrecommit
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], theBlockHash, theBlockHash)
+
+	donePrecommitWait := make(chan struct{})
+	go func() {
+		// (note we're entering precommit for a second time this round)
+		// but with invalid args. then we EnterPrecommitWait, twice (?)
+		<-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	// add precommits from the rest
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs4)
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+	<-donePrecommitWait
+
+	// before we time out into new round, set next proposer
+	// and next proposal block
+	_, v1 := cs1.Validators.GetByAddress(privVals[0].Address)
+	v1.VotingPower = 1
+	if updated := cs1.Validators.Update(v1); !updated {
+		t.Fatal("failed to update validator")
+	}
+
+	cs2.decideProposal(cs2.Height, cs2.Round+1)
+	prop, propBlock := cs2.Proposal, cs2.ProposalBlock
+	if prop == nil || propBlock == nil {
+		t.Fatal("Failed to create proposal block with cs2")
+	}
+
+	incrementRound(cs2, cs3, cs4)
+
+	// timeout to new round
+	te := <-timeoutChan
+	if te.Step != RoundStepPrecommitWait.String() {
+		t.Fatalf("expected to timeout of precommit into new round. got %v", te.Step)
+	}
+
+	log.Info("### ONTO ROUND 2")
+
+	/*
+		Round2 (cs2, C) // B C C C // C C C _)
+
+		cs1 changes lock!
+	*/
+
+	// now we're on a new round and not the proposer
+	<-cs1.NewStepCh()
+	cs1.mtx.Lock()
+	// so set the proposal block
+	propBlockHash, propBlockParts := propBlock.Hash(), propBlock.MakePartSet()
+	cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = prop, propBlock, propBlockParts
+	cs1.mtx.Unlock()
+	// and wait for timeout
+	te = <-timeoutChan
+	if te.Step != RoundStepPropose.String() {
+		t.Fatalf("expected to timeout of propose. got %v", te.Step)
+	}
+	// go to prevote, prevote for locked block (not proposal), move on
+	_, _ = <-voteChan, <-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], theBlockHash)
+
+	donePrecommit = make(chan struct{})
+	go func() {
+		//  we need this go routine because if we go into PrevoteWait it has to pull on newStepCh
+		// before the final vote will get added (because it holds the mutex).
+		select {
+		case <-cs1.NewStepCh(): // we're in PrevoteWait, go to Precommit
+			<-voteChan
+		case <-voteChan: // we went straight to Precommit
+		}
+		donePrecommit <- struct{}{}
+	}()
+	// now lets add prevotes from everyone else for the new block
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), cs2, cs3, cs4)
+	<-donePrecommit
+
+	// we should have unlocked and locked on the new block
+	validatePrecommit(t, cs1, 1, 1, privVals[0], propBlockHash, propBlockHash)
+
+	donePrecommitWait = make(chan struct{})
+	go func() {
+		// (note we're entering precommit for a second time this round)
+		// but with invalid args. then we EnterPrecommitWait,
+		<-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, propBlockHash, propBlockParts.Header(), cs2, cs3)
+	<-donePrecommitWait
+
+	<-cs1.NewStepCh()
+	rs := <-cs1.NewStepCh()
+	if rs.Height != 2 {
+		t.Fatal("Expected height to increment")
+	}
+
+	if hash, _, ok := rs.LastCommit.TwoThirdsMajority(); !ok || !bytes.Equal(hash, propBlockHash) {
+		t.Fatal("Expected block to get committed")
+	}
+}
+
+// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
+func TestLockPOLUnlock(t *testing.T) {
+	css, privVals := simpleConsensusState(4)
+	cs1, cs2, cs3, cs4 := css[0], css[1], css[2], css[3]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan *types.EventDataRoundState)
+	voteChan := make(chan *types.EventDataVote)
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringVote(), func(data types.EventData) {
+		vote := data.(*types.EventDataVote)
+		// we only fire for our own votes
+		if bytes.Equal(cs1.privValidator.Address, vote.Address) {
+			voteChan <- vote
+		}
+	})
+	cs1.SetFireable(evsw)
+
+	// everything done from perspective of cs1
+
+	/*
+		Round1 (cs1, B) // B B B B // B nil B nil
+
+		eg. didn't see the 2/3 prevotes
+	*/
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
+
+	theBlockHash := cs1.ProposalBlock.Hash()
+
+	donePrecommit := make(chan struct{})
+	go func() {
+		<-voteChan
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), cs2, cs3, cs4)
+	<-donePrecommit
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], theBlockHash, theBlockHash)
+
+	donePrecommitWait := make(chan struct{})
+	go func() {
+		<-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	// add precommits from the rest
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs4)
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+	<-donePrecommitWait
+
+	// before we time out into new round, set next proposer
+	// and next proposal block
+	_, v1 := cs1.Validators.GetByAddress(privVals[0].Address)
+	v1.VotingPower = 1
+	if updated := cs1.Validators.Update(v1); !updated {
+		t.Fatal("failed to update validator")
+	}
+
+	cs2.decideProposal(cs2.Height, cs2.Round+1)
+	prop, propBlock := cs2.Proposal, cs2.ProposalBlock
+	if prop == nil || propBlock == nil {
+		t.Fatal("Failed to create proposal block with cs2")
+	}
+
+	incrementRound(cs2, cs3, cs4)
+
+	// timeout to new round
+	<-timeoutChan
+
+	log.Info("#### ONTO ROUND 2")
+	/*
+		Round2 (cs2, C) // B nil nil nil // nil nil nil _
+
+		cs1 unlocks!
+	*/
+
+	// now we're on a new round and not the proposer,
+	<-cs1.NewStepCh()
+	cs1.mtx.Lock()
+	// so set the proposal block
+	cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = prop, propBlock, propBlock.MakePartSet()
+	lockedBlockHash := cs1.LockedBlock.Hash()
+	cs1.mtx.Unlock()
+	// and wait for timeout
+	<-timeoutChan
+
+	// go to prevote, prevote for locked block (not proposal)
+	_, _ = <-voteChan, <-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], lockedBlockHash)
+
+	donePrecommit = make(chan struct{})
+	go func() {
+		select {
+		case <-cs1.NewStepCh(): // we're in PrevoteWait, go to Precommit
+			<-voteChan
+		case <-voteChan: // we went straight to Precommit
+		}
+		donePrecommit <- struct{}{}
+	}()
+	// now lets add prevotes from everyone else for the new block
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, types.PartSetHeader{}, cs2, cs3, cs4)
+	<-donePrecommit
+
+	// we should have unlocked
+	// NOTE: we don't lock on nil, so LockedRound is still 0
+	validatePrecommit(t, cs1, 1, 0, privVals[0], nil, nil)
+
+	donePrecommitWait = make(chan struct{})
+	go func() {
+		// the votes will bring us to new round right away
+		// we should timeout of it
+		_, _, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh(), <-timeoutChan
+		donePrecommitWait <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs3)
+	<-donePrecommitWait
+}
+
+// 4 vals
+// a polka at round 1 but we miss it
+// then a polka at round 2 that we lock on
+// then we see the polka from round 1 but shouldn't unlock
+func TestLockPOLSafety1(t *testing.T) {
+	css, privVals := simpleConsensusState(4)
+	cs1, cs2, cs3, cs4 := css[0], css[1], css[2], css[3]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan *types.EventDataRoundState)
+	voteChan := make(chan *types.EventDataVote)
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringVote(), func(data types.EventData) {
+		vote := data.(*types.EventDataVote)
+		// we only fire for our own votes
+		if bytes.Equal(cs1.privValidator.Address, vote.Address) {
+			voteChan <- vote
+		}
+	})
+	cs1.SetFireable(evsw)
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
+
+	propBlock := cs1.ProposalBlock
+
+	validatePrevote(t, cs1, 0, privVals[0], cs1.ProposalBlock.Hash())
+
+	// the others sign a polka but we don't see it
+	prevotes := signVoteMany(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), cs2, cs3, cs4)
+
+	// before we time out into new round, set next proposer
+	// and next proposal block
+	_, v1 := cs1.Validators.GetByAddress(privVals[0].Address)
+	v1.VotingPower = 1
+	if updated := cs1.Validators.Update(v1); !updated {
+		t.Fatal("failed to update validator")
+	}
+
+	log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
+
+	// we do see them precommit nil
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs3, cs4)
+
+	cs2.decideProposal(cs2.Height, cs2.Round+1)
+	prop, propBlock := cs2.Proposal, cs2.ProposalBlock
+	if prop == nil || propBlock == nil {
+		t.Fatal("Failed to create proposal block with cs2")
+	}
+
+	incrementRound(cs2, cs3, cs4)
+
+	log.Info("### ONTO ROUND 2")
+	/*Round2
+	// we timeout and prevote our lock
+	// a polka happened but we didn't see it!
+	*/
+
+	// now we're on a new round and not the proposer,
+	<-cs1.NewStepCh()
+	// so set proposal
+	cs1.mtx.Lock()
+	propBlockHash, propBlockParts := propBlock.Hash(), propBlock.MakePartSet()
+	cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = prop, propBlock, propBlockParts
+	cs1.mtx.Unlock()
+	// and wait for timeout
+	<-timeoutChan
+	if cs1.LockedBlock != nil {
+		t.Fatal("we should not be locked!")
+	}
+	log.Warn("new prop", "hash", fmt.Sprintf("%X", propBlockHash))
+	// go to prevote, prevote for proposal block
+	_, _ = <-voteChan, <-cs1.NewStepCh()
+	validatePrevote(t, cs1, 1, privVals[0], propBlockHash)
+
+	// now we see the others prevote for it, so we should lock on it
+	donePrecommit := make(chan struct{})
+	go func() {
+		select {
+		case <-cs1.NewStepCh(): // we're in PrevoteWait, go to Precommit
+			<-voteChan
+		case <-voteChan: // we went straight to Precommit
+		}
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	// now lets add prevotes from everyone else for nil
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), cs2, cs3, cs4)
+	<-donePrecommit
+
+	// we should have precommitted
+	validatePrecommit(t, cs1, 1, 1, privVals[0], propBlockHash, propBlockHash)
+
+	// now we see precommits for nil
+	donePrecommitWait := make(chan struct{})
+	go func() {
+		// the votes will bring us to new round
+		// we should timeut of it and go to prevote
+		<-cs1.NewStepCh()
+		<-timeoutChan
+		donePrecommitWait <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs3)
+	<-donePrecommitWait
+
+	incrementRound(cs2, cs3, cs4)
+
+	log.Info("### ONTO ROUND 3")
+	/*Round3
+	we see the polka from round 1 but we shouldn't unlock!
+	*/
+
+	// timeout of propose
+	_, _ = <-cs1.NewStepCh(), <-timeoutChan
+
+	// finish prevote
+	_, _ = <-voteChan, <-cs1.NewStepCh()
+
+	// we should prevote what we're locked on
+	validatePrevote(t, cs1, 2, privVals[0], propBlockHash)
+
+	// add prevotes from the earlier round
+	addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
+
+	log.Warn("Done adding prevotes!")
+
+	ensureNoNewStep(t, cs1)
+}
+
+// 4 vals.
+// polka P1 at R1, P2 at R2, and P3 at R3,
+// we lock on P1 at R1, don't see P2, and unlock using P3 at R3
+// then we should make sure we don't lock using P2
+func TestLockPOLSafety2(t *testing.T) {
+	css, privVals := simpleConsensusState(4)
+	cs1, cs2, cs3, cs4 := css[0], css[1], css[2], css[3]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan *types.EventDataRoundState)
+	voteChan := make(chan *types.EventDataVote)
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutPropose(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- data.(*types.EventDataRoundState)
+	})
+	evsw.AddListenerForEvent("tester", types.EventStringVote(), func(data types.EventData) {
+		vote := data.(*types.EventDataVote)
+		// we only fire for our own votes
+		if bytes.Equal(cs1.privValidator.Address, vote.Address) {
+			voteChan <- vote
+		}
+	})
+	cs1.SetFireable(evsw)
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _, _ = <-cs1.NewStepCh(), <-voteChan, <-cs1.NewStepCh()
+
+	theBlockHash := cs1.ProposalBlock.Hash()
+
+	donePrecommit := make(chan struct{})
+	go func() {
+		<-voteChan
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), cs2, cs3, cs4)
+	<-donePrecommit
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], theBlockHash, theBlockHash)
+
+	donePrecommitWait := make(chan struct{})
+	go func() {
+		<-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	// add precommits from the rest
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs4)
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+	<-donePrecommitWait
+
+	// before we time out into new round, set next proposer
+	// and next proposal block
+	_, v1 := cs1.Validators.GetByAddress(privVals[0].Address)
+	v1.VotingPower = 1
+	if updated := cs1.Validators.Update(v1); !updated {
+		t.Fatal("failed to update validator")
+	}
+
+	cs2.decideProposal(cs2.Height, cs2.Round+1)
+	prop, propBlock := cs2.Proposal, cs2.ProposalBlock
+	if prop == nil || propBlock == nil {
+		t.Fatal("Failed to create proposal block with cs2")
+	}
+
+	incrementRound(cs2, cs3, cs4)
+
+	// timeout to new round
+	<-timeoutChan
+
+	log.Info("### ONTO Round 2")
+	/*Round2
+	// we timeout and prevote our lock
+	// a polka happened but we didn't see it!
+	*/
+
+	// now we're on a new round and not the proposer, so wait for timeout
+	_, _ = <-cs1.NewStepCh(), <-timeoutChan
+	// go to prevote, prevote for locked block
+	_, _ = <-voteChan, <-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], cs1.LockedBlock.Hash())
+
+	// the others sign a polka but we don't see it
+	prevotes := signVoteMany(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), cs2, cs3, cs4)
+
+	// once we see prevotes for the next round we'll skip ahead
+
+	incrementRound(cs2, cs3, cs4)
+
+	log.Info("### ONTO Round 3")
+	/*Round3
+	a polka for nil causes us to unlock
+	*/
+
+	// these prevotes will send us straight to precommit at the higher round
+	donePrecommit = make(chan struct{})
+	go func() {
+		select {
+		case <-cs1.NewStepCh(): // we're in PrevoteWait, go to Precommit
+			<-voteChan
+		case <-voteChan: // we went straight to Precommit
+		}
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	// now lets add prevotes from everyone else for nil
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, types.PartSetHeader{}, cs2, cs3, cs4)
+	<-donePrecommit
+
+	// we should have unlocked
+	// NOTE: we don't lock on nil, so LockedRound is still 0
+	validatePrecommit(t, cs1, 2, 0, privVals[0], nil, nil)
+
+	donePrecommitWait = make(chan struct{})
+	go func() {
+		// the votes will bring us to new round right away
+		// we should timeut of it and go to prevote
+		<-cs1.NewStepCh()
+		// set the proposal block to be that which got a polka in R2
+		cs1.mtx.Lock()
+		cs1.Proposal, cs1.ProposalBlock, cs1.ProposalBlockParts = prop, propBlock, propBlock.MakePartSet()
+		cs1.mtx.Unlock()
+		// timeout into prevote, finish prevote
+		_, _, _ = <-timeoutChan, <-voteChan, <-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, cs2, cs3)
+	<-donePrecommitWait
+
+	log.Info("### ONTO ROUND 4")
+	/*Round4
+	we see the polka from R2
+	make sure we don't lock because of it!
+	*/
+	// new round and not proposer
+	// (we already timed out and stepped into prevote)
+
+	log.Warn("adding prevotes from round 2")
+
+	addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
+
+	log.Warn("Done adding prevotes!")
+
+	// we should prevote it now
+	validatePrevote(t, cs1, 3, privVals[0], cs1.ProposalBlock.Hash())
+
+	// but we shouldn't precommit it
+	precommits := cs1.Votes.Precommits(3)
+	vote := precommits.GetByIndex(0)
+	if vote != nil {
+		t.Fatal("validator precommitted at round 4 based on an old polka")
+	}
+}
+
+//------------------------------------------------------------------------------------------
+// SlashingSuite
+
+func TestSlashingPrevotes(t *testing.T) {
+	css, _ := simpleConsensusState(2)
+	cs1, cs2 := css[0], css[1]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+
+	// we should now be stuck in limbo forever, waiting for more prevotes
+	// add one for a different block should cause us to go into prevote wait
+	hash := cs1.ProposalBlock.Hash()
+	hash[0] = byte(hash[0]+1) % 255
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+
+	// pass prevote wait
+	<-cs1.NewStepCh()
+
+	// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
+	// away and ignore more prevotes (and thus fail to slash!)
+
+	// add the conflicting vote
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+
+	// conflicting vote should cause us to broadcast dupeout tx on mempool
+	txs := cs1.mempoolReactor.Mempool.GetProposalTxs()
+	if len(txs) != 1 {
+		t.Fatal("expected to find a transaction in the mempool after double signing")
+	}
+	dupeoutTx, ok := txs[0].(*types.DupeoutTx)
+	if !ok {
+		t.Fatal("expected to find DupeoutTx in mempool after double signing")
+	}
+
+	if !bytes.Equal(dupeoutTx.Address, cs2.privValidator.Address) {
+		t.Fatalf("expected DupeoutTx for %X, got %X", cs2.privValidator.Address, dupeoutTx.Address)
+	}
+
+	// TODO: validate the sig
+}
+
+func TestSlashingPrecommits(t *testing.T) {
+	css, _ := simpleConsensusState(2)
+	cs1, cs2 := css[0], css[1]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+
+	// add prevote from cs2
+	signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+
+	// wait to finish precommit
+	<-cs1.NewStepCh()
+
+	// we should now be stuck in limbo forever, waiting for more prevotes
+	// add one for a different block should cause us to go into prevote wait
+	hash := cs1.ProposalBlock.Hash()
+	hash[0] = byte(hash[0]+1) % 255
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, cs1.ProposalBlockParts.Header())
+
+	// pass prevote wait
+	<-cs1.NewStepCh()
+
+	// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
+	// away and ignore more prevotes (and thus fail to slash!)
+
+	// add precommit from cs2
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+
+	// conflicting vote should cause us to broadcast dupeout tx on mempool
+	txs := cs1.mempoolReactor.Mempool.GetProposalTxs()
+	if len(txs) != 1 {
+		t.Fatal("expected to find a transaction in the mempool after double signing")
+	}
+	dupeoutTx, ok := txs[0].(*types.DupeoutTx)
+	if !ok {
+		t.Fatal("expected to find DupeoutTx in mempool after double signing")
+	}
+
+	if !bytes.Equal(dupeoutTx.Address, cs2.privValidator.Address) {
+		t.Fatalf("expected DupeoutTx for %X, got %X", cs2.privValidator.Address, dupeoutTx.Address)
+	}
+
+	// TODO: validate the sig
+
+}
+
+//------------------------------------------------------------------------------------------
+// CatchupSuite
+
+//------------------------------------------------------------------------------------------
+// HaltSuite
+
+// 4 vals.
+// we receive a final precommit after going into next round, but others might have gone to commit already!
+func TestHalt1(t *testing.T) {
+	css, privVals := simpleConsensusState(4)
+	cs1, cs2, cs3, cs4 := css[0], css[1], css[2], css[3]
+	cs1.newStepCh = make(chan *RoundState) // so it blocks
+
+	timeoutChan := make(chan struct{})
+	evsw := events.NewEventSwitch()
+	evsw.OnStart()
+	evsw.AddListenerForEvent("tester", types.EventStringTimeoutWait(), func(data types.EventData) {
+		timeoutChan <- struct{}{}
+	})
+	cs1.SetFireable(evsw)
+
+	// start round and wait for propose and prevote
+	cs1.EnterNewRound(cs1.Height, 0, false)
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+
+	theBlockHash := cs1.ProposalBlock.Hash()
+
+	donePrecommit := make(chan struct{})
+	go func() {
+		<-cs1.NewStepCh()
+		donePrecommit <- struct{}{}
+	}()
+	signAddVoteToFromMany(types.VoteTypePrevote, cs1, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), cs3, cs4)
+	<-donePrecommit
+
+	// the proposed block should now be locked and our precommit added
+	validatePrecommit(t, cs1, 0, 0, privVals[0], theBlockHash, theBlockHash)
+
+	donePrecommitWait := make(chan struct{})
+	go func() {
+		<-cs1.NewStepCh()
+		donePrecommitWait <- struct{}{}
+	}()
+	// add precommits from the rest
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, nil, types.PartSetHeader{}) // didnt receive proposal
+	signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+	// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
+	precommit4 := signVote(cs4, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header())
+	<-donePrecommitWait
+
+	incrementRound(cs2, cs3, cs4)
+
+	// timeout to new round
+	<-timeoutChan
+
+	log.Info("### ONTO ROUND 2")
+	/*Round2
+	// we timeout and prevote our lock
+	// a polka happened but we didn't see it!
+	*/
+
+	// go to prevote, prevote for locked block
+	_, _ = <-cs1.NewStepCh(), <-cs1.NewStepCh()
+	validatePrevote(t, cs1, 0, privVals[0], cs1.LockedBlock.Hash())
+
+	// now we receive the precommit from the previous round
+	addVoteToFrom(cs1, cs4, precommit4)
+
+	// receiving that precommit should take us straight to commit
+	ensureNewStep(t, cs1)
+	log.Warn("done enter commit!")
+
+	// update to state
+	ensureNewStep(t, cs1)
+
+	if cs1.Height != 2 {
+		t.Fatal("expected height to increment")
+	}
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go
index eb5ac9391afa748238672187cccd1df2d81e6aba..6c4b7d38a308f037bba8bc03df999417656e9e9f 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go
@@ -8,6 +8,7 @@ import (
 
 	bc "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain"
 	dbm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/db"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/events"
 	mempl "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p"
 	sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state"
@@ -206,6 +207,9 @@ func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivVali
 		cs := NewConsensusState(state, blockStore, mempoolReactor)
 		cs.SetPrivValidator(privVals[i])
 
+		evsw := events.NewEventSwitch()
+		cs.SetFireable(evsw)
+
 		// read off the NewHeightStep
 		<-cs.NewStepCh()
 
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..233bb88c2de02d6e4b61e803a9950893c5769d18
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/config.go
@@ -0,0 +1,13 @@
+package mempool
+
+import (
+	cfg "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config"
+)
+
+var config cfg.Config = nil
+
+func init() {
+	cfg.OnConfig(func(newConfig cfg.Config) {
+		config = newConfig
+	})
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go
index c1b580f481742ec2a078a971e329749f5f372140..625b56010117b9dcc1783a339070884b1b2aeae3 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go
@@ -19,7 +19,7 @@ type Mempool struct {
 	mtx   sync.Mutex
 	state *sm.State
 	cache *sm.BlockCache
-	txs   []types.Tx
+	txs   []types.Tx // TODO: we need to add a map to facilitate replace-by-fee
 }
 
 func NewMempool(state *sm.State) *Mempool {
@@ -37,6 +37,12 @@ func (mem *Mempool) GetCache() *sm.BlockCache {
 	return mem.cache
 }
 
+func (mem *Mempool) GetHeight() int {
+	mem.mtx.Lock()
+	defer mem.mtx.Unlock()
+	return mem.state.LastBlockHeight
+}
+
 // Apply tx to the state and remember it.
 func (mem *Mempool) AddTx(tx types.Tx) (err error) {
 	mem.mtx.Lock()
@@ -59,11 +65,23 @@ func (mem *Mempool) GetProposalTxs() []types.Tx {
 	return mem.txs
 }
 
+// We use this to inform peer routines of how the mempool has been updated
+type ResetInfo struct {
+	Height   int
+	Included []Range
+	Invalid  []Range
+}
+
+type Range struct {
+	Start  int
+	Length int
+}
+
 // "block" is the new block being committed.
 // "state" is the result of state.AppendBlock("block").
 // Txs that are present in "block" are discarded from mempool.
 // Txs that have become invalid in the new "state" are also discarded.
-func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) {
+func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) ResetInfo {
 	mem.mtx.Lock()
 	defer mem.mtx.Unlock()
 	mem.state = state.Copy()
@@ -75,33 +93,51 @@ func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) {
 		blockTxsMap[string(types.TxID(state.ChainID, tx))] = struct{}{}
 	}
 
-	// Next, filter all txs from mem.txs that are in blockTxsMap
-	txs := []types.Tx{}
-	for _, tx := range mem.txs {
+	// Now we filter all txs from mem.txs that are in blockTxsMap,
+	// and ExecTx on what remains. Only valid txs are kept.
+	// We track the ranges of txs included in the block and invalidated by it
+	// so we can tell peer routines
+	var ri = ResetInfo{Height: block.Height}
+	var validTxs []types.Tx
+	includedStart, invalidStart := -1, -1
+	for i, tx := range mem.txs {
 		txID := types.TxID(state.ChainID, tx)
 		if _, ok := blockTxsMap[string(txID)]; ok {
+			startRange(&includedStart, i)           // start counting included txs
+			endRange(&invalidStart, i, &ri.Invalid) // stop counting invalid txs
 			log.Info("Filter out, already committed", "tx", tx, "txID", txID)
-			continue
-		} else {
-			log.Info("Filter in, still new", "tx", tx, "txID", txID)
-			txs = append(txs, tx)
-		}
-	}
-
-	// Next, filter all txs that aren't valid given new state.
-	validTxs := []types.Tx{}
-	for _, tx := range txs {
-		err := sm.ExecTx(mem.cache, tx, false, nil)
-		if err == nil {
-			log.Info("Filter in, valid", "tx", tx)
-			validTxs = append(validTxs, tx)
 		} else {
-			// tx is no longer valid.
-			log.Info("Filter out, no longer valid", "tx", tx, "error", err)
+			endRange(&includedStart, i, &ri.Included) // stop counting included txs
+			err := sm.ExecTx(mem.cache, tx, false, nil)
+			if err != nil {
+				startRange(&invalidStart, i) // start counting invalid txs
+				log.Info("Filter out, no longer valid", "tx", tx, "error", err)
+			} else {
+				endRange(&invalidStart, i, &ri.Invalid) // stop counting invalid txs
+				log.Info("Filter in, new, valid", "tx", tx, "txID", txID)
+				validTxs = append(validTxs, tx)
+			}
 		}
 	}
+	endRange(&includedStart, len(mem.txs)-1, &ri.Included) // stop counting included txs
+	endRange(&invalidStart, len(mem.txs)-1, &ri.Invalid)   // stop counting invalid txs
 
 	// We're done!
 	log.Info("New txs", "txs", validTxs, "oldTxs", mem.txs)
 	mem.txs = validTxs
+	return ri
+}
+
+func startRange(start *int, i int) {
+	if *start < 0 {
+		*start = i
+	}
+}
+
+func endRange(start *int, i int, ranger *[]Range) {
+	if *start >= 0 {
+		length := i - *start
+		*ranger = append(*ranger, Range{*start, length})
+		*start = -1
+	}
 }
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ab6e4e993dc27275b67a201fa6295c1a908986a2
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool_test.go
@@ -0,0 +1,273 @@
+package mempool
+
+import (
+	"fmt"
+	"sync"
+	"testing"
+	"time"
+
+	acm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account"
+	_ "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test"
+	sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
+)
+
+var someAddr = []byte("ABCDEFGHIJABCDEFGHIJ")
+
+// number of txs
+var nTxs = 100
+
+// what the ResetInfo should look like after ResetForBlockAndState
+var TestResetInfoData = ResetInfo{
+	Included: []Range{
+		Range{0, 5},
+		Range{10, 10},
+		Range{30, 5},
+	},
+	Invalid: []Range{
+		Range{5, 5},
+		Range{20, 8},  // let 28 and 29 be valid
+		Range{35, 64}, // let 99 be valid
+	},
+}
+
+// inverse of the ResetInfo
+var notInvalidNotIncluded = map[int]struct{}{
+	28: struct{}{},
+	29: struct{}{},
+	99: struct{}{},
+}
+
+func newSendTx(t *testing.T, mempool *Mempool, from *acm.PrivAccount, to []byte, amt int64) types.Tx {
+	tx := types.NewSendTx()
+	tx.AddInput(mempool.GetCache(), from.PubKey, amt)
+	tx.AddOutput(to, amt)
+	tx.SignInput(config.GetString("chain_id"), 0, from)
+	if err := mempool.AddTx(tx); err != nil {
+		t.Fatal(err)
+	}
+	return tx
+}
+
+func addTxs(t *testing.T, mempool *Mempool, lastAcc *acm.PrivAccount, privAccs []*acm.PrivAccount) []types.Tx {
+	txs := make([]types.Tx, nTxs)
+	for i := 0; i < nTxs; i++ {
+		if _, ok := notInvalidNotIncluded[i]; ok {
+			txs[i] = newSendTx(t, mempool, lastAcc, someAddr, 10)
+		} else {
+			txs[i] = newSendTx(t, mempool, privAccs[i%len(privAccs)], privAccs[(i+1)%len(privAccs)].Address, 5)
+		}
+	}
+	return txs
+}
+
+func makeBlock(mempool *Mempool) *types.Block {
+	txs := mempool.GetProposalTxs()
+	var includedTxs []types.Tx
+	for _, rid := range TestResetInfoData.Included {
+		includedTxs = append(includedTxs, txs[rid.Start:rid.Start+rid.Length]...)
+	}
+
+	mempool.mtx.Lock()
+	state := mempool.state
+	state.LastBlockHeight += 1
+	mempool.mtx.Unlock()
+	return &types.Block{
+		Header: &types.Header{
+			ChainID: state.ChainID,
+			Height:  state.LastBlockHeight,
+			NumTxs:  len(includedTxs),
+		},
+		Data: &types.Data{
+			Txs: includedTxs,
+		},
+	}
+}
+
+// Add txs. Grab chunks to put in block. All the others become invalid because of nonce errors except those in notInvalidNotIncluded
+func TestResetInfo(t *testing.T) {
+	amtPerAccount := int64(100000)
+	state, privAccs, _ := sm.RandGenesisState(6, false, amtPerAccount, 1, true, 100)
+
+	mempool := NewMempool(state)
+
+	lastAcc := privAccs[5] // we save him (his tx wont become invalid)
+	privAccs = privAccs[:5]
+
+	txs := addTxs(t, mempool, lastAcc, privAccs)
+
+	// its actually an invalid block since we're skipping nonces
+	// but all we care about is how the mempool responds after
+	block := makeBlock(mempool)
+
+	ri := mempool.ResetForBlockAndState(block, state)
+
+	if len(ri.Included) != len(TestResetInfoData.Included) {
+		t.Fatalf("invalid number of included ranges. Got %d, expected %d\n", len(ri.Included), len(TestResetInfoData.Included))
+	}
+
+	if len(ri.Invalid) != len(TestResetInfoData.Invalid) {
+		t.Fatalf("invalid number of invalid ranges. Got %d, expected %d\n", len(ri.Invalid), len(TestResetInfoData.Invalid))
+	}
+
+	for i, rid := range ri.Included {
+		inc := TestResetInfoData.Included[i]
+		if rid.Start != inc.Start {
+			t.Fatalf("Invalid start of range. Got %d, expected %d\n", inc.Start, rid.Start)
+		}
+		if rid.Length != inc.Length {
+			t.Fatalf("Invalid length of range. Got %d, expected %d\n", inc.Length, rid.Length)
+		}
+	}
+
+	txs = mempool.GetProposalTxs()
+	if len(txs) != len(notInvalidNotIncluded) {
+		t.Fatalf("Expected %d txs left in mempool. Got %d", len(notInvalidNotIncluded), len(txs))
+	}
+}
+
+//------------------------------------------------------------------------------------------
+
+type TestPeer struct {
+	sync.Mutex
+	running bool
+	height  int
+
+	t *testing.T
+
+	received int
+	txs      map[string]int
+
+	timeoutFail int
+
+	done chan int
+}
+
+func newPeer(t *testing.T, state *sm.State) *TestPeer {
+	return &TestPeer{
+		running: true,
+		height:  state.LastBlockHeight,
+		t:       t,
+		txs:     make(map[string]int),
+		done:    make(chan int),
+	}
+}
+
+func (tp *TestPeer) IsRunning() bool {
+	tp.Lock()
+	defer tp.Unlock()
+	return tp.running
+}
+
+func (tp *TestPeer) SetRunning(running bool) {
+	tp.Lock()
+	defer tp.Unlock()
+	tp.running = running
+}
+
+func (tp *TestPeer) Send(chID byte, msg interface{}) bool {
+	if tp.timeoutFail > 0 {
+		time.Sleep(time.Second * time.Duration(tp.timeoutFail))
+		return false
+	}
+	tx := msg.(*TxMessage).Tx
+	id := types.TxID(config.GetString("chain_id"), tx)
+	if _, ok := tp.txs[string(id)]; ok {
+		tp.t.Fatal("received the same tx twice!")
+	}
+	tp.txs[string(id)] = tp.received
+	tp.received += 1
+	tp.done <- tp.received
+	return true
+}
+
+func (tp *TestPeer) Get(key string) interface{} {
+	return tp
+}
+
+func (tp *TestPeer) GetHeight() int {
+	return tp.height
+}
+
+func TestBroadcast(t *testing.T) {
+	state, privAccs, _ := sm.RandGenesisState(6, false, 10000, 1, true, 100)
+	mempool := NewMempool(state)
+	reactor := NewMempoolReactor(mempool)
+	reactor.Start()
+
+	lastAcc := privAccs[5] // we save him (his tx wont become invalid)
+	privAccs = privAccs[:5]
+
+	peer := newPeer(t, state)
+	newBlockChan := make(chan ResetInfo)
+	tickerChan := make(chan time.Time)
+	go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
+
+	// we don't broadcast any before updating
+	fmt.Println("dont broadcast any")
+	addTxs(t, mempool, lastAcc, privAccs)
+	block := makeBlock(mempool)
+	ri := mempool.ResetForBlockAndState(block, state)
+	newBlockChan <- ri
+	peer.height = ri.Height
+	tickerChan <- time.Now()
+	pullTxs(t, peer, len(mempool.txs)) // should have sent whatever txs are left (3)
+
+	toBroadcast := []int{1, 3, 7, 9, 11, 12, 18, 20, 21, 28, 29, 30, 31, 34, 35, 36, 50, 90, 99, 100}
+	for _, N := range toBroadcast {
+		peer = resetPeer(t, reactor, mempool, state, tickerChan, newBlockChan, peer)
+
+		// we broadcast N txs before updating
+		fmt.Println("broadcast", N)
+		addTxs(t, mempool, lastAcc, privAccs)
+		txsToSendPerCheck = N
+		tickerChan <- time.Now()
+		pullTxs(t, peer, txsToSendPerCheck) // should have sent N txs
+		block = makeBlock(mempool)
+		ri := mempool.ResetForBlockAndState(block, state)
+		newBlockChan <- ri
+		peer.height = ri.Height
+		txsToSendPerCheck = 100
+		tickerChan <- time.Now()
+		left := len(mempool.txs)
+		if N > 99 {
+			left -= 3
+		} else if N > 29 {
+			left -= 2
+		} else if N > 28 {
+			left -= 1
+		}
+		pullTxs(t, peer, left) // should have sent whatever txs are left that havent been sent
+	}
+}
+
+func pullTxs(t *testing.T, peer *TestPeer, N int) {
+	timer := time.NewTicker(time.Second * 2)
+	for i := 0; i < N; i++ {
+		select {
+		case <-peer.done:
+		case <-timer.C:
+			panic(fmt.Sprintf("invalid number of received messages. Got %d, expected %d\n", i, N))
+		}
+	}
+
+	if N == 0 {
+		select {
+		case <-peer.done:
+			t.Fatalf("should not have sent any more txs")
+		case <-timer.C:
+		}
+	}
+}
+
+func resetPeer(t *testing.T, reactor *MempoolReactor, mempool *Mempool, state *sm.State, tickerChan chan time.Time, newBlockChan chan ResetInfo, peer *TestPeer) *TestPeer {
+	// reset peer
+	mempool.txs = []types.Tx{}
+	mempool.state = state
+	mempool.cache = sm.NewBlockCache(state)
+	peer.SetRunning(false)
+	tickerChan <- time.Now()
+	peer = newPeer(t, state)
+	go reactor.broadcastTxRoutine(tickerChan, newBlockChan, peer)
+	return peer
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/reactor.go
index fb3ff7d74cb6b7eaa235a5868fc0c527a2884b6c..01d714666a0c45a0432eff26bf6643e689470e1d 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/reactor.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/reactor.go
@@ -2,18 +2,25 @@ package mempool
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"reflect"
+	"time"
 
 	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/events"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p"
+	sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/wire"
 )
 
 var (
 	MempoolChannel = byte(0x30)
+
+	checkExecutedTxsMilliseconds = 1   // check for new mempool txs to send to peer
+	txsToSendPerCheck            = 64  // send up to this many txs from the mempool per check
+	newBlockChCapacity           = 100 // queue to process this many ResetInfos per peer
 )
 
 // MempoolReactor handles mempool tx broadcasting amongst peers.
@@ -44,11 +51,17 @@ func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor {
 }
 
 // Implements Reactor
-func (pexR *MempoolReactor) AddPeer(peer *p2p.Peer) {
+func (memR *MempoolReactor) AddPeer(peer *p2p.Peer) {
+	// Each peer gets a go routine on which we broadcast transactions in the same order we applied them to our state.
+	newBlockChan := make(chan ResetInfo, newBlockChCapacity)
+	peer.Data.Set(types.PeerMempoolChKey, newBlockChan)
+	timer := time.NewTicker(time.Millisecond * time.Duration(checkExecutedTxsMilliseconds))
+	go memR.broadcastTxRoutine(timer.C, newBlockChan, peer)
 }
 
 // Implements Reactor
-func (pexR *MempoolReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
+func (memR *MempoolReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
+	// broadcast routine checks if peer is gone and returns
 }
 
 // Implements Reactor
@@ -70,29 +83,128 @@ func (memR *MempoolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
 		} else {
 			log.Info("Added valid tx", "tx", msg.Tx)
 		}
-		// Share tx.
-		// We use a simple shotgun approach for now.
-		// TODO: improve efficiency
-		for _, peer := range memR.Switch.Peers().List() {
-			if peer.Key == src.Key {
+		// broadcasting happens from go routines per peer
+	default:
+		log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
+	}
+}
+
+// "block" is the new block being committed.
+// "state" is the result of state.AppendBlock("block").
+// Txs that are present in "block" are discarded from mempool.
+// Txs that have become invalid in the new "state" are also discarded.
+func (memR *MempoolReactor) ResetForBlockAndState(block *types.Block, state *sm.State) {
+	ri := memR.Mempool.ResetForBlockAndState(block, state)
+	for _, peer := range memR.Switch.Peers().List() {
+		peerMempoolCh := peer.Data.Get(types.PeerMempoolChKey).(chan ResetInfo)
+		select {
+		case peerMempoolCh <- ri:
+		default:
+			memR.Switch.StopPeerForError(peer, errors.New("Peer's mempool push channel full"))
+		}
+	}
+}
+
+// Just an alias for AddTx since broadcasting happens in peer routines
+func (memR *MempoolReactor) BroadcastTx(tx types.Tx) error {
+	return memR.Mempool.AddTx(tx)
+}
+
+type PeerState interface {
+	GetHeight() int
+}
+
+type Peer interface {
+	IsRunning() bool
+	Send(byte, interface{}) bool
+	Get(string) interface{}
+}
+
+// send new mempool txs to peer, strictly in order we applied them to our state.
+// new blocks take chunks out of the mempool, but we've already sent some txs to the peer.
+// so we wait to hear that the peer has progressed to the new height, and then continue sending txs from where we left off
+func (memR *MempoolReactor) broadcastTxRoutine(tickerChan <-chan time.Time, newBlockChan chan ResetInfo, peer Peer) {
+	var height = memR.Mempool.GetHeight()
+	var txsSent int // new txs sent for height. (reset every new height)
+
+	for {
+		select {
+		case <-tickerChan:
+			if !peer.IsRunning() {
+				return
+			}
+
+			// make sure the peer is up to date
+			if peerState_i := peer.Get(types.PeerStateKey); peerState_i != nil {
+				peerState := peerState_i.(PeerState)
+				if peerState.GetHeight() < height {
+					continue
+				}
+			} else {
 				continue
 			}
-			peer.TrySend(MempoolChannel, msg)
+
+			// check the mempool for new transactions
+			newTxs := memR.getNewTxs(height)
+			txsSentLoop := 0
+			start := time.Now()
+
+		TX_LOOP:
+			for i := txsSent; i < len(newTxs) && txsSentLoop < txsToSendPerCheck; i++ {
+				tx := newTxs[i]
+				msg := &TxMessage{Tx: tx}
+				success := peer.Send(MempoolChannel, msg)
+				if !success {
+					break TX_LOOP
+				} else {
+					txsSentLoop += 1
+				}
+			}
+
+			if txsSentLoop > 0 {
+				txsSent += txsSentLoop
+				log.Info("Sent txs to peer", "txsSentLoop", txsSentLoop,
+					"took", time.Since(start), "txsSent", txsSent, "newTxs", len(newTxs))
+			}
+
+		case ri := <-newBlockChan:
+			height = ri.Height
+
+			// find out how many txs below what we've sent were included in a block and how many became invalid
+			included := tallyRangesUpTo(ri.Included, txsSent)
+			invalidated := tallyRangesUpTo(ri.Invalid, txsSent)
+
+			txsSent -= included + invalidated
 		}
+	}
+}
 
-	default:
-		log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
+// fetch new txs from the mempool
+func (memR *MempoolReactor) getNewTxs(height int) (txs []types.Tx) {
+	memR.Mempool.mtx.Lock()
+	defer memR.Mempool.mtx.Unlock()
+
+	// if the mempool got ahead of us just return empty txs
+	if memR.Mempool.state.LastBlockHeight != height {
+		return
 	}
+	return memR.Mempool.txs
 }
 
-func (memR *MempoolReactor) BroadcastTx(tx types.Tx) error {
-	err := memR.Mempool.AddTx(tx)
-	if err != nil {
-		return err
+// return the size of ranges less than upTo
+func tallyRangesUpTo(ranger []Range, upTo int) int {
+	totalUpTo := 0
+	for _, r := range ranger {
+		if r.Start >= upTo {
+			break
+		}
+		if r.Start+r.Length >= upTo {
+			totalUpTo += upTo - r.Start
+			break
+		}
+		totalUpTo += r.Length
 	}
-	msg := &TxMessage{Tx: tx}
-	memR.Switch.Broadcast(MempoolChannel, msg)
-	return nil
+	return totalUpTo
 }
 
 // implements events.Eventable
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go
index c30a958ce03dbcf8b1912a8c3b17693dd0f64f9e..d35c5d8491c779418bcf5e74b1dcc6ca47816d60 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go
@@ -2,10 +2,10 @@ package node
 
 import (
 	"bytes"
+	"io/ioutil"
 	"math/rand"
 	"net"
 	"net/http"
-	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -24,6 +24,7 @@ import (
 	sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state"
 	stypes "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/vm"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/wire"
 )
 
@@ -44,7 +45,15 @@ type Node struct {
 	privKey          acm.PrivKeyEd25519
 }
 
-func NewNode() *Node {
+func NewNodeDefaultPrivVal() *Node {
+	// Get PrivValidator
+	privValidatorFile := config.GetString("priv_validator_file")
+	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
+
+	return NewNode(privValidator)
+}
+
+func NewNode(privValidator *types.PrivValidator) *Node {
 	// Get BlockStore
 	blockStoreDB := dbm.GetDB("blockstore")
 	blockStore := bc.NewBlockStore(blockStoreDB)
@@ -74,20 +83,6 @@ func NewNode() *Node {
 	// add the chainid to the global config
 	config.Set("chain_id", state.ChainID)
 
-	// Get PrivValidator
-	var privValidator *types.PrivValidator
-	privValidatorFile := config.GetString("priv_validator_file")
-	if _, err := os.Stat(privValidatorFile); err == nil {
-		privValidator = types.LoadPrivValidator(privValidatorFile)
-		log.Notice("Loaded PrivValidator",
-			"file", privValidatorFile, "privValidator", privValidator)
-	} else {
-		privValidator = types.GenPrivValidator()
-		privValidator.SetFile(privValidatorFile)
-		privValidator.Save()
-		log.Notice("Generated PrivValidator", "file", privValidatorFile)
-	}
-
 	// Generate node PrivKey
 	privKey := acm.GenPrivKeyEd25519()
 
@@ -127,6 +122,17 @@ func NewNode() *Node {
 	// they should all satisfy events.Eventable
 	SetFireable(eventSwitch, pexReactor, bcReactor, mempoolReactor, consensusReactor)
 
+	// run the profile server
+	profileHost := config.GetString("prof_laddr")
+	if profileHost != "" {
+		go func() {
+			log.Warn("Profile server", "error", http.ListenAndServe(profileHost, nil))
+		}()
+	}
+
+	// set vm log level
+	vm.SetDebug(config.GetBool("vm_log"))
+
 	return &Node{
 		sw:               sw,
 		evsw:             eventSwitch,
@@ -256,7 +262,7 @@ func makeNodeInfo(sw *p2p.Switch, privKey acm.PrivKeyEd25519) *types.NodeInfo {
 	}
 
 	// include git hash in the nodeInfo if available
-	if rev, err := ReadFile(config.GetString("revisions_file")); err == nil {
+	if rev, err := ReadFile(config.GetString("revision_file")); err == nil {
 		nodeInfo.Version.Revision = string(rev)
 	}
 
@@ -286,8 +292,31 @@ func makeNodeInfo(sw *p2p.Switch, privKey acm.PrivKeyEd25519) *types.NodeInfo {
 //------------------------------------------------------------------------------
 
 func RunNode() {
+
+	// Wait until the genesis doc becomes available
+	genDocFile := config.GetString("genesis_file")
+	if !FileExists(genDocFile) {
+		log.Notice(Fmt("Waiting for genesis file %v...", genDocFile))
+		for {
+			time.Sleep(time.Second)
+			if FileExists(genDocFile) {
+				break
+			}
+			jsonBlob, err := ioutil.ReadFile(genDocFile)
+			if err != nil {
+				Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
+			}
+			genDoc := stypes.GenesisDocFromJSON(jsonBlob)
+			if genDoc.ChainID == "" {
+				PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
+			}
+			config.Set("chain_id", genDoc.ChainID)
+			config.Set("genesis_doc", genDoc)
+		}
+	}
+
 	// Create & start node
-	n := NewNode()
+	n := NewNodeDefaultPrivVal()
 	l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
 	n.AddListener(l)
 	err := n.Start()
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node_test.go
index 01f601cf1a4a29288d1a3ba115acd5ec7340b5b3..84eb1014bd5e92e7bfb4655df3bbf29248bcabe7 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node_test.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node_test.go
@@ -10,7 +10,7 @@ import (
 
 func TestNodeStartStop(t *testing.T) {
 	// Create & start node
-	n := NewNode()
+	n := NewNodeDefaultPrivVal()
 	l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
 	n.AddListener(l)
 	n.Start()
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/netaddress.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/netaddress.go
index 8588ff73b82a3f2276698b1f8127a652f726e5c6..983cda622219e16c1c680dfb0e5b5d62ba254840 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/netaddress.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/netaddress.go
@@ -110,6 +110,10 @@ func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) {
 }
 
 func (na *NetAddress) Routable() bool {
+	if config.GetBool("local_routing") {
+		return na.Valid()
+	}
+
 	// TODO(oga) bitcoind doesn't include RFC3849 here, but should we?
 	return na.Valid() && !(na.RFC1918() || na.RFC3927() || na.RFC4862() ||
 		na.RFC4193() || na.RFC4843() || na.Local())
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go
index 82ff986b917618ac1f9b5c5e74a27e522d3e2570..7b291f80371e69647de9ca4b87e3be741f9f0d20 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go
@@ -128,3 +128,7 @@ func (p *Peer) String() string {
 func (p *Peer) Equals(other *Peer) bool {
 	return p.Key == other.Key
 }
+
+func (p *Peer) Get(key string) interface{} {
+	return p.Data.Get(key)
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go
index d0f3a71e52bd4e7d1aac6f6a68769331137014c4..4bec4e620a86cf98da448f49724155cff80e8bc4 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go
@@ -31,7 +31,7 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
 	peerRoundStates := []string{}
 	for _, peer := range p2pSwitch.Peers().List() {
 		// TODO: clean this up?
-		peerState := peer.Data.Get(cm.PeerStateKey).(*cm.PeerState)
+		peerState := peer.Data.Get(types.PeerStateKey).(*cm.PeerState)
 		peerRoundState := peerState.GetRoundState()
 		peerRoundStateStr := peer.Key + ":" + string(wire.JSONBytes(peerRoundState))
 		peerRoundStates = append(peerRoundStates, peerRoundStateStr)
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go
index 61cd4b95ed0e592c09531515e7370db77e9c0039..c2c7f95f0842c6b095623332cc293955132b9a8d 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go
@@ -30,5 +30,6 @@ func BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
 }
 
 func ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) {
-	return &ctypes.ResultListUnconfirmedTxs{mempoolReactor.Mempool.GetProposalTxs()}, nil
+	txs := mempoolReactor.Mempool.GetProposalTxs()
+	return &ctypes.ResultListUnconfirmedTxs{len(txs), txs}, nil
 }
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go
index bfc35523094de0ec21731046fac236a28fc845d3..45d4a2f305d1854efc5a612761465045e4cf3cfc 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go
@@ -98,6 +98,7 @@ type Receipt struct {
 }
 
 type ResultListUnconfirmedTxs struct {
+	N   int        `json:"n_txs"`
 	Txs []types.Tx `json:"txs"`
 }
 
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/test/helpers.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/test/helpers.go
index 53c69a9bfcfc11625f90d71575fc7ded3b0bb014..5f3539711f515539f78dca2565bd17fca6e37bb9 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/test/helpers.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/test/helpers.go
@@ -49,7 +49,7 @@ func makeUsers(n int) []*acm.PrivAccount {
 // create a new node and sleep forever
 func newNode(ready chan struct{}) {
 	// Create & start node
-	node = nm.NewNode()
+	node = nm.NewNodeDefaultPrivVal()
 	l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"))
 	node.AddListener(l)
 	node.Start()
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go
index 4abbc242e6481bca9515cec4a7c6372d13e9d3e1..a5cfc458bde8ff1361aaa3202f910eea77614cbb 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go
@@ -473,3 +473,11 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
 		nameReg:              nameReg,
 	}
 }
+
+func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*State, []*acm.PrivAccount, []*types.PrivValidator) {
+	db := dbm.NewMemDB()
+	genDoc, privAccounts, privValidators := RandGenesisDoc(numAccounts, randBalance, minBalance, numValidators, randBonded, minBonded)
+	s0 := MakeGenesisState(db, genDoc)
+	s0.Save()
+	return s0, privAccounts, privValidators
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go
deleted file mode 100644
index ca6eae68c62a593e0d0292a26d5edb59a7a604f3..0000000000000000000000000000000000000000
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package state
-
-import (
-	"sort"
-	"time"
-
-	acm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account"
-	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
-	dbm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/db"
-	ptypes "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types"
-	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types"
-	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
-)
-
-func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAccount) {
-	privAccount := acm.GenPrivAccount()
-	perms := ptypes.DefaultAccountPermissions
-	acc := &acm.Account{
-		Address:     privAccount.PubKey.Address(),
-		PubKey:      privAccount.PubKey,
-		Sequence:    RandInt(),
-		Balance:     minBalance,
-		Permissions: perms,
-	}
-	if randBalance {
-		acc.Balance += int64(RandUint32())
-	}
-	return acc, privAccount
-}
-
-func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) {
-	accounts := make([]GenesisAccount, numAccounts)
-	privAccounts := make([]*acm.PrivAccount, numAccounts)
-	defaultPerms := ptypes.DefaultAccountPermissions
-	for i := 0; i < numAccounts; i++ {
-		account, privAccount := RandAccount(randBalance, minBalance)
-		accounts[i] = GenesisAccount{
-			Address:     account.Address,
-			Amount:      account.Balance,
-			Permissions: &defaultPerms, // This will get copied into each state.Account.
-		}
-		privAccounts[i] = privAccount
-	}
-	validators := make([]GenesisValidator, numValidators)
-	privValidators := make([]*types.PrivValidator, numValidators)
-	for i := 0; i < numValidators; i++ {
-		valInfo, _, privVal := types.RandValidator(randBonded, minBonded)
-		validators[i] = GenesisValidator{
-			PubKey: valInfo.PubKey,
-			Amount: valInfo.FirstBondAmount,
-			UnbondTo: []BasicAccount{
-				{
-					Address: valInfo.PubKey.Address(),
-					Amount:  valInfo.FirstBondAmount,
-				},
-			},
-		}
-		privValidators[i] = privVal
-	}
-	sort.Sort(types.PrivValidatorsByAddress(privValidators))
-	return &GenesisDoc{
-		GenesisTime: time.Now(),
-		ChainID:     "tendermint_test",
-		Accounts:    accounts,
-		Validators:  validators,
-	}, privAccounts, privValidators
-
-}
-
-func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*State, []*acm.PrivAccount, []*types.PrivValidator) {
-	db := dbm.NewMemDB()
-	genDoc, privAccounts, privValidators := RandGenesisDoc(numAccounts, randBalance, minBalance, numValidators, randBonded, minBonded)
-	s0 := MakeGenesisState(db, genDoc)
-	s0.Save()
-	return s0, privAccounts, privValidators
-}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache_test.go
index ef6544925948eab6ddaad9b681f4331bf2454cfa..ee8e96a058a61d51211cbed37de5a33b8a6b619f 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache_test.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache_test.go
@@ -4,11 +4,12 @@ import (
 	"bytes"
 	"testing"
 
+	stypes "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/wire"
 )
 
 func TestStateToFromVMAccount(t *testing.T) {
-	acmAcc1, _ := RandAccount(true, 456)
+	acmAcc1, _ := stypes.RandAccount(true, 456)
 	vmAcc := toVMAccount(acmAcc1)
 	acmAcc2 := toStateAccount(vmAcc)
 
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types/genesis.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types/genesis.go
index ed90b01bf6623ac4079194db7876d513480b9aef..d850fcb1a3a877b83384808fa3f4b449d2d551c4 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types/genesis.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/types/genesis.go
@@ -1,11 +1,13 @@
 package types
 
 import (
+	"sort"
 	"time"
 
 	acm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account"
 	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
 	ptypes "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types"
+	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types"
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/wire"
 )
 
@@ -59,3 +61,61 @@ func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
 	}
 	return
 }
+
+//------------------------------------------------------------
+// Make random genesis state
+
+func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAccount) {
+	privAccount := acm.GenPrivAccount()
+	perms := ptypes.DefaultAccountPermissions
+	acc := &acm.Account{
+		Address:     privAccount.PubKey.Address(),
+		PubKey:      privAccount.PubKey,
+		Sequence:    RandInt(),
+		Balance:     minBalance,
+		Permissions: perms,
+	}
+	if randBalance {
+		acc.Balance += int64(RandUint32())
+	}
+	return acc, privAccount
+}
+
+func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) {
+	accounts := make([]GenesisAccount, numAccounts)
+	privAccounts := make([]*acm.PrivAccount, numAccounts)
+	defaultPerms := ptypes.DefaultAccountPermissions
+	for i := 0; i < numAccounts; i++ {
+		account, privAccount := RandAccount(randBalance, minBalance)
+		accounts[i] = GenesisAccount{
+			Address:     account.Address,
+			Amount:      account.Balance,
+			Permissions: &defaultPerms, // This will get copied into each state.Account.
+		}
+		privAccounts[i] = privAccount
+	}
+	validators := make([]GenesisValidator, numValidators)
+	privValidators := make([]*types.PrivValidator, numValidators)
+	for i := 0; i < numValidators; i++ {
+		valInfo, _, privVal := types.RandValidator(randBonded, minBonded)
+		validators[i] = GenesisValidator{
+			PubKey: valInfo.PubKey,
+			Amount: valInfo.FirstBondAmount,
+			UnbondTo: []BasicAccount{
+				{
+					Address: valInfo.PubKey.Address(),
+					Amount:  valInfo.FirstBondAmount,
+				},
+			},
+		}
+		privValidators[i] = privVal
+	}
+	sort.Sort(types.PrivValidatorsByAddress(privValidators))
+	return &GenesisDoc{
+		GenesisTime: time.Now(),
+		ChainID:     "tendermint_test",
+		Accounts:    accounts,
+		Validators:  validators,
+	}, privAccounts, privValidators
+
+}
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/wtf b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/wtf
new file mode 100644
index 0000000000000000000000000000000000000000..0e10d8a5e714545eb97c2ae22cbf433a2eaaf7eb
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/wtf
@@ -0,0 +1,68 @@
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=send
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=call
+INFO[07-22|18:13:12] Out account: Account{DFF89D4BE8CDDC7A1993C91A1C7FFE32D3778570:<nil> B:1536646667 C:120 S: P:{Base: 100011111110; Set: 11111111111111 []}} module=state
+INFO[07-22|18:13:12] Calling contract 000000000000000000000000DFF89D4BE8CDDC7A1993C91A1C7FFE32D3778570 with code 60606040526000357C0100000000000000000000000000000000000000000000000000000000900480632FEE78D7146037576035565B005B6040600450606C565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60003390506075565B9056 module=state
+INFO[07-22|18:13:12] Code for this contract: 60606040526000357C0100000000000000000000000000000000000000000000000000000000900480632FEE78D7146037576035565B005B6040600450606C565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60003390506075565B9056 module=state
+(1) (00000000) 000000000000000000000000DFF89D4BE8CDDC7A1993C91A1C7FFE32D3778570 (code=120) gas: 1000 (d) 2FEE78D7
+(pc) 0   (op) PUSH1          (st) 0     => 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 2   (op) PUSH1          (st) 1     => 0x0000000000000000000000000000000000000000000000000000000000000040
+(pc) 4   (op) MSTORE         (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 5   (op) PUSH1          (st) 0     => 0x0000000000000000000000000000000000000000000000000000000000000000
+(pc) 7   (op) CALLDATALOAD   (st) 1     => 0x2FEE78D700000000000000000000000000000000000000000000000000000000
+(pc) 8   (op) PUSH29         (st) 1     => 0x0000000100000000000000000000000000000000000000000000000000000000
+(pc) 38  (op) SWAP1          (st) 2     => [2] 2FEE78D700000000000000000000000000000000000000000000000000000000
+(pc) 39  (op) DIV            (st) 2     21680047490780924029029108358227327141478116379726555908761811030043504148480 / 26959946667150639794667015087019630673637144422540572481103610249216 = 804157655 (000000000000000000000000000000000000000000000000000000002FEE78D7)
+(pc) 40  (op) DUP1           (st) 1     => [1] 0x000000000000000000000000000000000000000000000000000000002FEE78D7
+(pc) 41  (op) PUSH4          (st) 2     => 0x000000000000000000000000000000000000000000000000000000002FEE78D7
+(pc) 46  (op) EQ             (st) 3     000000000000000000000000000000000000000000000000000000002FEE78D7 == 000000000000000000000000000000000000000000000000000000002FEE78D7 = 1
+(pc) 47  (op) PUSH1          (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000037
+(pc) 49  (op) JUMPI          (st) 3     ~> 55
+(pc) 55  (op) JUMPDEST       (st) 1    
+(pc) 56  (op) PUSH1          (st) 1     => 0x0000000000000000000000000000000000000000000000000000000000000040
+(pc) 58  (op) PUSH1          (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000004
+(pc) 60  (op) POP            (st) 3     => 10000000
+(pc) 61  (op) PUSH1          (st) 2     => 0x000000000000000000000000000000000000000000000000000000000000006C
+(pc) 63  (op) JUMP           (st) 3     ~> 108
+(pc) 108 (op) JUMPDEST       (st) 2    
+(pc) 109 (op) PUSH1          (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000000
+(pc) 111 (op) CALLER         (st) 3     => 000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+(pc) 112 (op) SWAP1          (st) 4     => [2] 0000000000000000000000000000000000000000000000000000000000000000
+(pc) 113 (op) POP            (st) 4     => 10000000
+(pc) 114 (op) PUSH1          (st) 3     => 0x0000000000000000000000000000000000000000000000000000000000000075
+(pc) 116 (op) JUMP           (st) 4     ~> 117
+(pc) 117 (op) JUMPDEST       (st) 3    
+(pc) 118 (op) SWAP1          (st) 3     => [2] 0000000000000000000000000000000000000000000000000000000000000040
+(pc) 119 (op) JUMP           (st) 3     ~> 64
+(pc) 64  (op) JUMPDEST       (st) 2    
+(pc) 65  (op) PUSH1          (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000040
+(pc) 67  (op) MLOAD          (st) 3     => 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 68  (op) DUP1           (st) 3     => [1] 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 69  (op) DUP3           (st) 4     => [3] 0x000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+(pc) 70  (op) PUSH20         (st) 5     => 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+(pc) 91  (op) AND            (st) 6     000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF & 000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6 = 000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+(pc) 92  (op) DUP2           (st) 5     => [2] 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 93  (op) MSTORE         (st) 6     => 0x000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+(pc) 94  (op) PUSH1          (st) 4     => 0x0000000000000000000000000000000000000000000000000000000000000020
+(pc) 96  (op) ADD            (st) 5     32 + 96 = 128 (0000000000000000000000000000000000000000000000000000000000000080)
+(pc) 97  (op) SWAP2          (st) 4     => [3] 000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+(pc) 98  (op) POP            (st) 4     => 10000000
+(pc) 99  (op) POP            (st) 3     => 10000000
+(pc) 100 (op) PUSH1          (st) 2     => 0x0000000000000000000000000000000000000000000000000000000000000040
+(pc) 102 (op) MLOAD          (st) 3     => 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 103 (op) DUP1           (st) 3     => [1] 0x0000000000000000000000000000000000000000000000000000000000000060
+(pc) 104 (op) SWAP2          (st) 4     => [3] 0000000000000000000000000000000000000000000000000000000000000080
+(pc) 105 (op) SUB            (st) 4     128 - 96 = 32 (0000000000000000000000000000000000000000000000000000000000000020)
+(pc) 106 (op) SWAP1          (st) 3     => [2] 0000000000000000000000000000000000000000000000000000000000000060
+(pc) 107 (op) RETURN         (st) 3     => [96, 32] (32) 0x000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6
+INFO[07-22|18:13:12] Successful execution                     module=state
+NOTE[07-22|18:13:12] VM call complete                         module=state caller="VMAccount{000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6 B:1529024268 C: N:1 S:0000000000000000000000000000000000000000000000000000000000000000}" callee="VMAccount{000000000000000000000000DFF89D4BE8CDDC7A1993C91A1C7FFE32D3778570 B:1536646668 C:60606040526000357C0100000000000000000000000000000000000000000000000000000000900480632FEE78D7146037576035565B005B6040600450606C565B604051808273FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16815260200191505060405180910390F35B60003390506075565B9056 N:0 S:0000000000000000000000000000000000000000000000000000000000000000}" return=000000000000000000000000C91B248EFBD6841FF1A5E1615E4661C6293A84F6 err=nil
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=name
+INFO[07-22|18:13:12] New NameTx                               module=state value=10000 costPerBlock=1237 expiresIn=8 lastBlock=0
+INFO[07-22|18:13:12] Creating namereg entry                   module=state name=satoshi expiresIn=8
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=name
+INFO[07-22|18:13:12] Invalid characters found in NameTx.Data (�€Èû). Only the kind of things found in a JSON file are allowed module=state
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=create_account
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=bond
+INFO[07-22|18:13:12] Account has permission                   module=state address=C91B248EFBD6841FF1A5E1615E4661C6293A84F6 perm=bond
+PASS
+ok  	github.com/tendermint/tendermint/state	0.311s
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/keys.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/keys.go
new file mode 100644
index 0000000000000000000000000000000000000000..90591b95936e5dc6af8d616a099169f95c9ceca0
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/keys.go
@@ -0,0 +1,6 @@
+package types
+
+var (
+	PeerStateKey     = "ConsensusReactor.peerState"
+	PeerMempoolChKey = "MempoolReactor.peerMempoolCh"
+)
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/myfile b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/myfile
new file mode 100644
index 0000000000000000000000000000000000000000..d4401eddd73a489b7f045232f80c08e4c7f2dc41
Binary files /dev/null and b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/myfile differ
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go
index e548a5779c46c7e2515bd67355705f4ee9613250..8e320cba28f249fc9c44e357f474ab21273773fe 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go
@@ -19,7 +19,7 @@ type NodeInfo struct {
 
 type Versions struct {
 	Revision   string `json:"revision"`
-	Tendermint string `json"tendermint"`
+	Tendermint string `json:"tendermint"`
 	P2P        string `json:"p2p"`
 	RPC        string `json:"rpc"`
 	Wire       string `json:"wire"`
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/priv_validator.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/priv_validator.go
index aad8d118c9561fd4931e89b77f71fbef5036154b..ee6f9e27c9cee45d37ff30cea28b544fb801af3e 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/priv_validator.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/priv_validator.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"math"
+	"os"
 	"sync"
 
 	acm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account"
@@ -35,12 +36,15 @@ func voteToStep(vote *Vote) int8 {
 }
 
 type PrivValidator struct {
-	Address    []byte             `json:"address"`
-	PubKey     acm.PubKeyEd25519  `json:"pub_key"`
-	PrivKey    acm.PrivKeyEd25519 `json:"priv_key"`
-	LastHeight int                `json:"last_height"`
-	LastRound  int                `json:"last_round"`
-	LastStep   int8               `json:"last_step"`
+	Address    []byte            `json:"address"`
+	PubKey     acm.PubKeyEd25519 `json:"pub_key"`
+	LastHeight int               `json:"last_height"`
+	LastRound  int               `json:"last_round"`
+	LastStep   int8              `json:"last_step"`
+
+	// NOTE: A safer implemention leaves PrivKey empty and uses a secure key signing service.
+	PrivKey acm.PrivKeyEd25519 `json:"priv_key"`
+	signer  Signer
 
 	// For persistence.
 	// Overloaded for testing.
@@ -48,6 +52,28 @@ type PrivValidator struct {
 	mtx      sync.Mutex
 }
 
+type Signer interface {
+	Sign(msg []byte) acm.SignatureEd25519
+}
+
+// Implements Signer
+type DefaultSigner struct {
+	priv acm.PrivKeyEd25519
+}
+
+func NewDefaultSigner(priv acm.PrivKeyEd25519) *DefaultSigner {
+	return &DefaultSigner{priv: priv}
+}
+
+// Implements Signer
+func (ds *DefaultSigner) Sign(msg []byte) acm.SignatureEd25519 {
+	return ds.priv.Sign(msg).(acm.SignatureEd25519)
+}
+
+func (privVal *PrivValidator) SetSigner(s Signer) {
+	privVal.signer = s
+}
+
 // Generates a new validator with private key.
 func GenPrivValidator() *PrivValidator {
 	privKeyBytes := new([64]byte)
@@ -63,6 +89,7 @@ func GenPrivValidator() *PrivValidator {
 		LastRound:  0,
 		LastStep:   stepNone,
 		filePath:   "",
+		signer:     NewDefaultSigner(privKey),
 	}
 }
 
@@ -76,9 +103,25 @@ func LoadPrivValidator(filePath string) *PrivValidator {
 		Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
 	}
 	privVal.filePath = filePath
+	privVal.signer = NewDefaultSigner(privVal.PrivKey)
 	return privVal
 }
 
+func LoadOrGenPrivValidator(filePath string) *PrivValidator {
+	var privValidator *PrivValidator
+	if _, err := os.Stat(filePath); err == nil {
+		privValidator = LoadPrivValidator(filePath)
+		log.Notice("Loaded PrivValidator",
+			"file", filePath, "privValidator", privValidator)
+	} else {
+		privValidator = GenPrivValidator()
+		privValidator.SetFile(filePath)
+		privValidator.Save()
+		log.Notice("Generated PrivValidator", "file", filePath)
+	}
+	return privValidator
+}
+
 func (privVal *PrivValidator) SetFile(filePath string) {
 	privVal.mtx.Lock()
 	defer privVal.mtx.Unlock()
@@ -130,14 +173,10 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error {
 	privVal.save()
 
 	// Sign
-	privVal.SignVoteUnsafe(chainID, vote)
+	vote.Signature = privVal.signer.Sign(acm.SignBytes(chainID, vote))
 	return nil
 }
 
-func (privVal *PrivValidator) SignVoteUnsafe(chainID string, vote *Vote) {
-	vote.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, vote)).(acm.SignatureEd25519)
-}
-
 func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error {
 	privVal.mtx.Lock()
 	defer privVal.mtx.Unlock()
@@ -152,7 +191,7 @@ func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) e
 		privVal.save()
 
 		// Sign
-		proposal.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, proposal)).(acm.SignatureEd25519)
+		proposal.Signature = privVal.signer.Sign(acm.SignBytes(chainID, proposal))
 		return nil
 	} else {
 		return errors.New(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v", proposal.Height, proposal.Round))
@@ -172,7 +211,7 @@ func (privVal *PrivValidator) SignRebondTx(chainID string, rebondTx *RebondTx) e
 		privVal.save()
 
 		// Sign
-		rebondTx.Signature = privVal.PrivKey.Sign(acm.SignBytes(chainID, rebondTx)).(acm.SignatureEd25519)
+		rebondTx.Signature = privVal.signer.Sign(acm.SignBytes(chainID, rebondTx))
 		return nil
 	} else {
 		return errors.New(fmt.Sprintf("Attempt of duplicate signing of rebondTx: Height %v", rebondTx.Height))
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set.go
index a1c551e9202f7b7fcf16fdaadd8b42ab143c7f5e..4aaa772fc0ca4d85ed34912f2260cdcf96083c7f 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set.go
@@ -204,6 +204,9 @@ func (voteSet *VoteSet) IsCommit() bool {
 	if voteSet == nil {
 		return false
 	}
+	if voteSet.type_ != VoteTypePrecommit {
+		return false
+	}
 	voteSet.mtx.Lock()
 	defer voteSet.mtx.Unlock()
 	return len(voteSet.maj23Hash) > 0
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set_test.go
index 10a42f31e67c964fa91075945f7550e9b25fd59c..137d73c3897e9ac282dd3dcc6a6c713fb236613d 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set_test.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote_set_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"sort"
 
+	acm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account"
 	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
 	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common/test"
 	_ "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test"
@@ -60,7 +61,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
 }
 
 func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
-	privVal.SignVoteUnsafe(config.GetString("chain_id"), vote)
+	vote.Signature = privVal.signer.Sign(acm.SignBytes(config.GetString("chain_id"), vote))
 	added, _, err := voteSet.AddByAddress(privVal.Address, vote)
 	return added, err
 }
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go
index 899bb2b7b8b27e57903d42b2a9a03af20ae3a891..900a3ff4dded1fc950dc8a62c05cbf36735e1122 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go
@@ -69,6 +69,37 @@ func TestVM(t *testing.T) {
 	}
 }
 
+func TestJumpErr(t *testing.T) {
+	ourVm := NewVM(newAppState(), newParams(), Zero256, nil)
+
+	// Create accounts
+	account1 := &Account{
+		Address: Int64ToWord256(100),
+	}
+	account2 := &Account{
+		Address: Int64ToWord256(101),
+	}
+
+	var gas int64 = 100000
+	code := []byte{0x60, 0x10, 0x56} // jump to position 16, a clear failure
+	var output []byte
+	var err error
+	ch := make(chan struct{})
+	go func() {
+		output, err = ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
+		ch <- struct{}{}
+	}()
+	tick := time.NewTicker(time.Second * 2)
+	select {
+	case <-tick.C:
+		t.Fatal("VM ended up in an infinite loop from bad jump dest (it took too long!)")
+	case <-ch:
+		if err == nil {
+			t.Fatal("Expected invalid jump dest err")
+		}
+	}
+}
+
 // Tests the code for a subcurrency contract compiled by serpent
 func TestSubcurrency(t *testing.T) {
 	st := newAppState()
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go
index 38ce23f7919d9bf453d2e40945314ef0a6b5825a..1af1735035f95080cf61709b3c5ac7633c44d64f 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go
@@ -36,15 +36,20 @@ func (err ErrPermission) Error() string {
 	return fmt.Sprintf("Contract does not have permission to %s", err.typ)
 }
 
-type Debug bool
-
 const (
-	dataStackCapacity       = 1024
-	callStackCapacity       = 100         // TODO ensure usage.
-	memoryCapacity          = 1024 * 1024 // 1 MB
-	dbg               Debug = true
+	dataStackCapacity = 1024
+	callStackCapacity = 100         // TODO ensure usage.
+	memoryCapacity    = 1024 * 1024 // 1 MB
 )
 
+type Debug bool
+
+var dbg Debug
+
+func SetDebug(d bool) {
+	dbg = Debug(d)
+}
+
 func (d Debug) Printf(s string, a ...interface{}) {
 	if d {
 		fmt.Printf(s, a...)
@@ -109,6 +114,8 @@ func (vm *VM) fireCallEvent(exception *string, output *[]byte, caller, callee *A
 }
 
 // CONTRACT appState is aware of caller and callee, so we can just mutate them.
+// CONTRACT code and input are not mutated.
+// CONTRACT returned 'ret' is a new compact slice.
 // value: To be transferred from caller to callee. Refunded upon error.
 // gas:   Available gas. No refunds for gas.
 // code: May be nil, since the CALL opcode may be used to send value from contracts to accounts
@@ -632,13 +639,17 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
 			dbg.Printf(" {0x%X : 0x%X}\n", loc, data)
 
 		case JUMP: // 0x56
-			err = jump(code, stack.Pop64(), &pc)
+			if err = jump(code, stack.Pop64(), &pc); err != nil {
+				return nil, err
+			}
 			continue
 
 		case JUMPI: // 0x57
 			pos, cond := stack.Pop64(), stack.Pop()
 			if !cond.IsZero() {
-				err = jump(code, pos, &pc)
+				if err = jump(code, pos, &pc); err != nil {
+					return nil, err
+				}
 				continue
 			}
 			dbg.Printf(" ~> false\n")
@@ -691,6 +702,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
 			if !ok {
 				return nil, firstErr(err, ErrMemoryOutOfBounds)
 			}
+			data = copyslice(data)
 			if vm.evc != nil {
 				eventID := types.EventStringLogEvent(callee.Address.Postfix(20))
 				fmt.Printf("eventID: %s\n", eventID)
@@ -724,11 +736,12 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
 
 			newAccount := vm.appState.CreateAccount(callee)
 			// Run the input to get the contract code.
+			// NOTE: no need to copy 'input' as per Call contract.
 			ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas)
 			if err_ != nil {
 				stack.Push(Zero256)
 			} else {
-				newAccount.Code = ret // Set the code
+				newAccount.Code = ret // Set the code (ret need not be copied as per Call contract)
 				stack.Push(newAccount.Address)
 			}
 
@@ -747,6 +760,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
 			if !ok {
 				return nil, firstErr(err, ErrMemoryOutOfBounds)
 			}
+			args = copyslice(args)
 
 			// Ensure that gasLimit is reasonable
 			if *gas < gasLimit {
@@ -827,7 +841,8 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas
 				return nil, firstErr(err, ErrMemoryOutOfBounds)
 			}
 			dbg.Printf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(ret), ret)
-			return ret, nil
+			output = copyslice(ret)
+			return output, nil
 
 		case SUICIDE: // 0xFF
 			addr := stack.Pop()
@@ -870,10 +885,15 @@ func subslice(data []byte, offset, length int64) (ret []byte, ok bool) {
 	} else {
 		ret, ok = data[offset:offset+length], true
 	}
-
 	return
 }
 
+func copyslice(src []byte) (dest []byte) {
+	dest = make([]byte, len(src))
+	copy(dest, src)
+	return dest
+}
+
 func rightMostBytes(data []byte, n int) []byte {
 	size := MinInt(len(data), n)
 	offset := len(data) - size
diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/wire/util.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/wire/util.go
index 91aa6f5c31526aec683c6346381c766ebfe7b836..bfe32d726599f7acc2b1730ba950fa8ec335678b 100644
--- a/Godeps/_workspace/src/github.com/tendermint/tendermint/wire/util.go
+++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/wire/util.go
@@ -3,6 +3,8 @@ package wire
 import (
 	"bytes"
 	"crypto/sha256"
+	"encoding/json"
+
 	"github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160"
 
 	. "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common"
@@ -26,6 +28,21 @@ func JSONBytes(o interface{}) []byte {
 	return w.Bytes()
 }
 
+// NOTE: inefficient
+func JSONBytesPretty(o interface{}) []byte {
+	jsonBytes := JSONBytes(o)
+	var object interface{}
+	err := json.Unmarshal(jsonBytes, &object)
+	if err != nil {
+		PanicSanity(err)
+	}
+	jsonBytes, err = json.MarshalIndent(object, "", "\t")
+	if err != nil {
+		PanicSanity(err)
+	}
+	return jsonBytes
+}
+
 // NOTE: does not care about the type, only the binary representation.
 func BinaryEqual(a, b interface{}) bool {
 	aBytes := BinaryBytes(a)