mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-07 20:42:14 +00:00
Merge pull request #343 from tendermint/restart_test
Crash/Restart tests
This commit is contained in:
commit
12d92fd5db
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,3 +10,5 @@ rpc/test/.tendermint
|
|||||||
remote_dump
|
remote_dump
|
||||||
.revision
|
.revision
|
||||||
vendor
|
vendor
|
||||||
|
.vagrant
|
||||||
|
test/p2p/data/
|
||||||
|
@ -101,6 +101,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int) error {
|
|||||||
// Search for height marker
|
// Search for height marker
|
||||||
gr, found, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight))
|
gr, found, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight))
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
log.Warn("Replay: wal.group.Search returned EOF", "height", csHeight)
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -347,6 +348,23 @@ func (cs *ConsensusState) OnStart() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the latest block was applied in the tmsp handshake,
|
||||||
|
// we may not have written the current height to the wal,
|
||||||
|
// so check here and write it if not found.
|
||||||
|
// TODO: remove this and run the handhsake/replay
|
||||||
|
// through the consensus state with a mock app
|
||||||
|
gr, found, err := cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(cs.Height))
|
||||||
|
if (err == io.EOF || !found) && cs.Step == RoundStepNewHeight {
|
||||||
|
log.Warn("Height not found in wal. Writing new height", "height", cs.Height)
|
||||||
|
rs := cs.RoundStateEvent()
|
||||||
|
cs.wal.Save(rs)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if gr != nil {
|
||||||
|
gr.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// we need the timeoutRoutine for replay so
|
// we need the timeoutRoutine for replay so
|
||||||
// we don't block on the tick chan.
|
// we don't block on the tick chan.
|
||||||
// NOTE: we will get a build up of garbage go routines
|
// NOTE: we will get a build up of garbage go routines
|
||||||
|
@ -104,4 +104,9 @@ func (wal *WAL) Save(wmsg WALMessage) {
|
|||||||
|
|
||||||
func (wal *WAL) writeHeight(height int) {
|
func (wal *WAL) writeHeight(height int) {
|
||||||
wal.group.WriteLine(Fmt("#HEIGHT: %v", height))
|
wal.group.WriteLine(Fmt("#HEIGHT: %v", height))
|
||||||
|
|
||||||
|
// TODO: only flush when necessary
|
||||||
|
if err := wal.group.Flush(); err != nil {
|
||||||
|
PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,8 @@ func DefaultClientCreator(config cfg.Config) ClientCreator {
|
|||||||
switch addr {
|
switch addr {
|
||||||
case "dummy":
|
case "dummy":
|
||||||
return NewLocalClientCreator(dummy.NewDummyApplication())
|
return NewLocalClientCreator(dummy.NewDummyApplication())
|
||||||
|
case "persistent_dummy":
|
||||||
|
return NewLocalClientCreator(dummy.NewPersistentDummyApplication(config.GetString("db_dir")))
|
||||||
case "nilapp":
|
case "nilapp":
|
||||||
return NewLocalClientCreator(nilapp.NewNilApplication())
|
return NewLocalClientCreator(nilapp.NewNilApplication())
|
||||||
default:
|
default:
|
||||||
|
@ -56,7 +56,9 @@ func (s *State) ExecBlock(eventCache types.Fireable, proxyAppConn proxy.AppConnC
|
|||||||
|
|
||||||
// save state with updated height/blockhash/validators
|
// save state with updated height/blockhash/validators
|
||||||
// but stale apphash, in case we fail between Commit and Save
|
// but stale apphash, in case we fail between Commit and Save
|
||||||
s.Save()
|
s.SaveIntermediate()
|
||||||
|
|
||||||
|
fail.Fail() // XXX
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -264,7 +266,6 @@ func (s *State) CommitStateUpdateMempool(proxyAppConn proxy.AppConnConsensus, bl
|
|||||||
|
|
||||||
// Set the state's new AppHash
|
// Set the state's new AppHash
|
||||||
s.AppHash = res.Data
|
s.AppHash = res.Data
|
||||||
s.AppHashIsStale = false
|
|
||||||
|
|
||||||
// Update mempool.
|
// Update mempool.
|
||||||
mempool.Update(block.Height, block.Txs)
|
mempool.Update(block.Height, block.Txs)
|
||||||
@ -322,7 +323,7 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Notice("TMSP Handshake", "height", blockInfo.BlockHeight, "app_hash", blockInfo.AppHash)
|
log.Notice("TMSP Handshake", "appHeight", blockInfo.BlockHeight, "appHash", blockInfo.AppHash)
|
||||||
|
|
||||||
blockHeight := int(blockInfo.BlockHeight) // XXX: beware overflow
|
blockHeight := int(blockInfo.BlockHeight) // XXX: beware overflow
|
||||||
appHash := blockInfo.AppHash
|
appHash := blockInfo.AppHash
|
||||||
@ -343,6 +344,9 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
|||||||
return errors.New(Fmt("Error on replay: %v", err))
|
return errors.New(Fmt("Error on replay: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save the state
|
||||||
|
h.state.Save()
|
||||||
|
|
||||||
// TODO: (on restart) replay mempool
|
// TODO: (on restart) replay mempool
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -352,29 +356,45 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
|||||||
func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, appConnConsensus proxy.AppConnConsensus) error {
|
func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, appConnConsensus proxy.AppConnConsensus) error {
|
||||||
|
|
||||||
storeBlockHeight := h.store.Height()
|
storeBlockHeight := h.store.Height()
|
||||||
if storeBlockHeight < appBlockHeight {
|
stateBlockHeight := h.state.LastBlockHeight
|
||||||
|
log.Notice("TMSP Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)
|
||||||
|
|
||||||
|
if storeBlockHeight == 0 {
|
||||||
|
return nil
|
||||||
|
} else if storeBlockHeight < appBlockHeight {
|
||||||
// if the app is ahead, there's nothing we can do
|
// if the app is ahead, there's nothing we can do
|
||||||
return ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight}
|
return ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight}
|
||||||
|
|
||||||
} else if storeBlockHeight == appBlockHeight {
|
} else if storeBlockHeight == appBlockHeight {
|
||||||
// if we crashed between Commit and SaveState,
|
// We ran Commit, but if we crashed before state.Save(),
|
||||||
// the state's app hash is stale
|
// load the intermediate state and update the state.AppHash.
|
||||||
// otherwise we're synced
|
// NOTE: If TMSP allowed rollbacks, we could just replay the
|
||||||
if h.state.AppHashIsStale {
|
// block even though it's been committed
|
||||||
h.state.AppHashIsStale = false
|
stateAppHash := h.state.AppHash
|
||||||
|
lastBlockAppHash := h.store.LoadBlock(storeBlockHeight).AppHash
|
||||||
|
|
||||||
|
if bytes.Equal(stateAppHash, appHash) {
|
||||||
|
// we're all synced up
|
||||||
|
log.Debug("TMSP RelpayBlocks: Already synced")
|
||||||
|
} else if bytes.Equal(stateAppHash, lastBlockAppHash) {
|
||||||
|
// we crashed after commit and before saving state,
|
||||||
|
// so load the intermediate state and update the hash
|
||||||
|
h.state.LoadIntermediate()
|
||||||
h.state.AppHash = appHash
|
h.state.AppHash = appHash
|
||||||
|
log.Debug("TMSP RelpayBlocks: Loaded intermediate state and updated state.AppHash")
|
||||||
|
} else {
|
||||||
|
PanicSanity(Fmt("Unexpected state.AppHash: state.AppHash %X; app.AppHash %X, lastBlock.AppHash %X", stateAppHash, appHash, lastBlockAppHash))
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
} else if h.state.LastBlockHeight == appBlockHeight {
|
} else if storeBlockHeight == appBlockHeight+1 &&
|
||||||
// store is ahead of app but core's state height is at apps height
|
storeBlockHeight == stateBlockHeight+1 {
|
||||||
// this happens if we crashed after saving the block,
|
// We crashed after saving the block
|
||||||
// but before committing it. We should be 1 ahead
|
// but before Commit (both the state and app are behind),
|
||||||
if storeBlockHeight != appBlockHeight+1 {
|
// so just replay the block
|
||||||
PanicSanity(Fmt("core.state.height == app.height but store.height (%d) > app.height+1 (%d)", storeBlockHeight, appBlockHeight+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that the blocks last apphash is the states apphash
|
// check that the lastBlock.AppHash matches the state apphash
|
||||||
block := h.store.LoadBlock(storeBlockHeight)
|
block := h.store.LoadBlock(storeBlockHeight)
|
||||||
if !bytes.Equal(block.Header.AppHash, appHash) {
|
if !bytes.Equal(block.Header.AppHash, appHash) {
|
||||||
return ErrLastStateMismatch{storeBlockHeight, block.Header.AppHash, appHash}
|
return ErrLastStateMismatch{storeBlockHeight, block.Header.AppHash, appHash}
|
||||||
@ -385,13 +405,22 @@ func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, appConnCon
|
|||||||
h.nBlocks += 1
|
h.nBlocks += 1
|
||||||
var eventCache types.Fireable // nil
|
var eventCache types.Fireable // nil
|
||||||
|
|
||||||
// replay the block against the actual tendermint state
|
// replay the latest block
|
||||||
return h.state.ApplyBlock(eventCache, appConnConsensus, block, blockMeta.PartsHeader, MockMempool{})
|
return h.state.ApplyBlock(eventCache, appConnConsensus, block, blockMeta.PartsHeader, MockMempool{})
|
||||||
|
} else if storeBlockHeight != stateBlockHeight {
|
||||||
|
// unless we failed before committing or saving state (previous 2 case),
|
||||||
|
// the store and state should be at the same height!
|
||||||
|
PanicSanity(Fmt("Expected storeHeight (%d) and stateHeight (%d) to match.", storeBlockHeight, stateBlockHeight))
|
||||||
} else {
|
} else {
|
||||||
// either we're caught up or there's blocks to replay
|
// store is more than one ahead,
|
||||||
|
// so app wants to replay many blocks
|
||||||
|
|
||||||
// replay all blocks starting with appBlockHeight+1
|
// replay all blocks starting with appBlockHeight+1
|
||||||
var eventCache types.Fireable // nil
|
var eventCache types.Fireable // nil
|
||||||
|
|
||||||
|
// TODO: use stateBlockHeight instead and let the consensus state
|
||||||
|
// do the replay
|
||||||
|
|
||||||
var appHash []byte
|
var appHash []byte
|
||||||
for i := appBlockHeight + 1; i <= storeBlockHeight; i++ {
|
for i := appBlockHeight + 1; i <= storeBlockHeight; i++ {
|
||||||
h.nBlocks += 1
|
h.nBlocks += 1
|
||||||
@ -413,8 +442,9 @@ func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, appConnCon
|
|||||||
appHash = res.Data
|
appHash = res.Data
|
||||||
}
|
}
|
||||||
if !bytes.Equal(h.state.AppHash, appHash) {
|
if !bytes.Equal(h.state.AppHash, appHash) {
|
||||||
return errors.New(Fmt("Tendermint state.AppHash does not match AppHash after replay", "expected", h.state.AppHash, "got", appHash))
|
return errors.New(Fmt("Tendermint state.AppHash does not match AppHash after replay. Got %X, expected %X", appHash, h.state.AppHash))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
stateKey = []byte("stateKey")
|
stateKey = []byte("stateKey")
|
||||||
|
stateIntermediateKey = []byte("stateIntermediateKey")
|
||||||
)
|
)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -36,15 +37,17 @@ type State struct {
|
|||||||
Validators *types.ValidatorSet
|
Validators *types.ValidatorSet
|
||||||
LastValidators *types.ValidatorSet // block.LastCommit validated against this
|
LastValidators *types.ValidatorSet // block.LastCommit validated against this
|
||||||
|
|
||||||
// AppHash is updated after Commit;
|
// AppHash is updated after Commit
|
||||||
// it's stale after ExecBlock and before Commit
|
|
||||||
AppHashIsStale bool
|
|
||||||
AppHash []byte
|
AppHash []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadState(db dbm.DB) *State {
|
func LoadState(db dbm.DB) *State {
|
||||||
|
return loadState(db, stateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadState(db dbm.DB, key []byte) *State {
|
||||||
s := &State{db: db}
|
s := &State{db: db}
|
||||||
buf := db.Get(stateKey)
|
buf := db.Get(key)
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
@ -60,9 +63,6 @@ func LoadState(db dbm.DB) *State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) Copy() *State {
|
func (s *State) Copy() *State {
|
||||||
if s.AppHashIsStale {
|
|
||||||
PanicSanity(Fmt("App hash is stale: %v", s))
|
|
||||||
}
|
|
||||||
return &State{
|
return &State{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
GenesisDoc: s.GenesisDoc,
|
GenesisDoc: s.GenesisDoc,
|
||||||
@ -72,7 +72,6 @@ func (s *State) Copy() *State {
|
|||||||
LastBlockTime: s.LastBlockTime,
|
LastBlockTime: s.LastBlockTime,
|
||||||
Validators: s.Validators.Copy(),
|
Validators: s.Validators.Copy(),
|
||||||
LastValidators: s.LastValidators.Copy(),
|
LastValidators: s.LastValidators.Copy(),
|
||||||
AppHashIsStale: false,
|
|
||||||
AppHash: s.AppHash,
|
AppHash: s.AppHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +82,35 @@ func (s *State) Save() {
|
|||||||
s.db.SetSync(stateKey, s.Bytes())
|
s.db.SetSync(stateKey, s.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) SaveIntermediate() {
|
||||||
|
s.mtx.Lock()
|
||||||
|
defer s.mtx.Unlock()
|
||||||
|
s.db.SetSync(stateIntermediateKey, s.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the intermediate state into the current state
|
||||||
|
// and do some sanity checks
|
||||||
|
func (s *State) LoadIntermediate() {
|
||||||
|
s2 := loadState(s.db, stateIntermediateKey)
|
||||||
|
if s.ChainID != s2.ChainID {
|
||||||
|
PanicSanity(Fmt("State mismatch for ChainID. Got %v, Expected %v", s2.ChainID, s.ChainID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.LastBlockHeight+1 != s2.LastBlockHeight {
|
||||||
|
PanicSanity(Fmt("State mismatch for LastBlockHeight. Got %v, Expected %v", s2.LastBlockHeight, s.LastBlockHeight+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(s.Validators.Hash(), s2.LastValidators.Hash()) {
|
||||||
|
PanicSanity(Fmt("State mismatch for LastValidators. Got %X, Expected %X", s2.LastValidators.Hash(), s.Validators.Hash()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(s.AppHash, s2.AppHash) {
|
||||||
|
PanicSanity(Fmt("State mismatch for AppHash. Got %X, Expected %X", s2.AppHash, s.AppHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setBlockAndValidators(s2.LastBlockHeight, s2.LastBlockID, s2.LastBlockTime, s2.Validators.Copy(), s2.LastValidators.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *State) Equals(s2 *State) bool {
|
func (s *State) Equals(s2 *State) bool {
|
||||||
return bytes.Equal(s.Bytes(), s2.Bytes())
|
return bytes.Equal(s.Bytes(), s2.Bytes())
|
||||||
}
|
}
|
||||||
@ -97,15 +125,22 @@ func (s *State) Bytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mutate state variables to match block and validators
|
// Mutate state variables to match block and validators
|
||||||
// Since we don't have the new AppHash yet, we set s.AppHashIsStale=true
|
// after running EndBlock
|
||||||
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, prevValSet, nextValSet *types.ValidatorSet) {
|
func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader types.PartSetHeader, prevValSet, nextValSet *types.ValidatorSet) {
|
||||||
s.LastBlockHeight = header.Height
|
s.setBlockAndValidators(header.Height,
|
||||||
s.LastBlockID = types.BlockID{header.Hash(), blockPartsHeader}
|
types.BlockID{header.Hash(), blockPartsHeader}, header.Time,
|
||||||
s.LastBlockTime = header.Time
|
prevValSet, nextValSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) setBlockAndValidators(
|
||||||
|
height int, blockID types.BlockID, blockTime time.Time,
|
||||||
|
prevValSet, nextValSet *types.ValidatorSet) {
|
||||||
|
|
||||||
|
s.LastBlockHeight = height
|
||||||
|
s.LastBlockID = blockID
|
||||||
|
s.LastBlockTime = blockTime
|
||||||
s.Validators = nextValSet
|
s.Validators = nextValSet
|
||||||
s.LastValidators = prevValSet
|
s.LastValidators = prevValSet
|
||||||
|
|
||||||
s.AppHashIsStale = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) {
|
func (s *State) GetValidators() (*types.ValidatorSet, *types.ValidatorSet) {
|
||||||
|
@ -21,5 +21,8 @@ COPY . $REPO
|
|||||||
RUN go install ./cmd/tendermint
|
RUN go install ./cmd/tendermint
|
||||||
RUN bash scripts/install_tmsp_apps.sh
|
RUN bash scripts/install_tmsp_apps.sh
|
||||||
|
|
||||||
|
# expose the volume for debugging
|
||||||
|
VOLUME $REPO
|
||||||
|
|
||||||
EXPOSE 46656
|
EXPOSE 46656
|
||||||
EXPOSE 46657
|
EXPOSE 46657
|
||||||
|
@ -6,13 +6,14 @@ NETWORK_NAME=$2
|
|||||||
ID=$3
|
ID=$3
|
||||||
CMD=$4
|
CMD=$4
|
||||||
|
|
||||||
|
NAME=test_container_$ID
|
||||||
|
|
||||||
echo "starting test client container with CMD=$CMD"
|
echo "starting test client container with CMD=$CMD"
|
||||||
# run the test container on the local network
|
# run the test container on the local network
|
||||||
docker run -t \
|
docker run -t --rm \
|
||||||
-v $GOPATH/src/github.com/tendermint/tendermint/test/p2p/:/go/src/github.com/tendermint/tendermint/test/p2p \
|
-v $GOPATH/src/github.com/tendermint/tendermint/test/p2p/:/go/src/github.com/tendermint/tendermint/test/p2p \
|
||||||
--net=$NETWORK_NAME \
|
--net=$NETWORK_NAME \
|
||||||
--ip=$(test/p2p/ip.sh "-1") \
|
--ip=$(test/p2p/ip.sh "-1") \
|
||||||
--name test_container_$ID \
|
--name $NAME \
|
||||||
--entrypoint bash \
|
--entrypoint bash \
|
||||||
$DOCKER_IMAGE $CMD
|
$DOCKER_IMAGE $CMD
|
||||||
|
|
||||||
|
@ -4,12 +4,13 @@ set -eu
|
|||||||
DOCKER_IMAGE=$1
|
DOCKER_IMAGE=$1
|
||||||
NETWORK_NAME=$2
|
NETWORK_NAME=$2
|
||||||
N=$3
|
N=$3
|
||||||
|
PROXY_APP=$4
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
# run it on each of them
|
# run it on each of them
|
||||||
for i in `seq 1 $N`; do
|
for i in `seq 1 $N`; do
|
||||||
bash test/p2p/fast_sync/test_peer.sh $DOCKER_IMAGE $NETWORK_NAME $i $N
|
bash test/p2p/fast_sync/test_peer.sh $DOCKER_IMAGE $NETWORK_NAME $i $N $PROXY_APP
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@ set -eu
|
|||||||
|
|
||||||
DOCKER_IMAGE=$1
|
DOCKER_IMAGE=$1
|
||||||
NETWORK_NAME=$2
|
NETWORK_NAME=$2
|
||||||
COUNT=$3
|
ID=$3
|
||||||
N=$4
|
N=$4
|
||||||
|
PROXY_APP=$5
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
# this runs on each peer:
|
# this runs on each peer:
|
||||||
@ -14,11 +15,11 @@ N=$4
|
|||||||
###############################################################
|
###############################################################
|
||||||
|
|
||||||
|
|
||||||
echo "Testing fasysync on node $COUNT"
|
echo "Testing fastsync on node $ID"
|
||||||
|
|
||||||
# kill peer
|
# kill peer
|
||||||
set +e # circle sigh :(
|
set +e # circle sigh :(
|
||||||
docker rm -vf local_testnet_$COUNT
|
docker rm -vf local_testnet_$ID
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# restart peer - should have an empty blockchain
|
# restart peer - should have an empty blockchain
|
||||||
@ -26,10 +27,10 @@ SEEDS="$(test/p2p/ip.sh 1):46656"
|
|||||||
for j in `seq 2 $N`; do
|
for j in `seq 2 $N`; do
|
||||||
SEEDS="$SEEDS,$(test/p2p/ip.sh $j):46656"
|
SEEDS="$SEEDS,$(test/p2p/ip.sh $j):46656"
|
||||||
done
|
done
|
||||||
bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $COUNT $SEEDS
|
bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $ID $PROXY_APP $SEEDS
|
||||||
|
|
||||||
# wait for peer to sync and check the app hash
|
# wait for peer to sync and check the app hash
|
||||||
bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME fs_$COUNT "test/p2p/fast_sync/check_peer.sh $COUNT"
|
bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME fs_$ID "test/p2p/fast_sync/check_peer.sh $ID"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "PASS"
|
echo "PASS"
|
||||||
|
48
test/p2p/kill_all/check_peers.sh
Normal file
48
test/p2p/kill_all/check_peers.sh
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
NUM_OF_PEERS=$1
|
||||||
|
|
||||||
|
# how many attempts for each peer to catch up by height
|
||||||
|
MAX_ATTEMPTS_TO_CATCH_UP=10
|
||||||
|
|
||||||
|
echo "Waiting for nodes to come online"
|
||||||
|
set +e
|
||||||
|
for i in $(seq 1 "$NUM_OF_PEERS"); do
|
||||||
|
addr=$(test/p2p/ip.sh "$i"):46657
|
||||||
|
curl -s "$addr/status" > /dev/null
|
||||||
|
ERR=$?
|
||||||
|
while [ "$ERR" != 0 ]; do
|
||||||
|
sleep 1
|
||||||
|
curl -s "$addr/status" > /dev/null
|
||||||
|
ERR=$?
|
||||||
|
done
|
||||||
|
echo "... node $i is up"
|
||||||
|
done
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# get the first peer's height
|
||||||
|
addr=$(test/p2p/ip.sh 1):46657
|
||||||
|
h1=$(curl -s "$addr/status" | jq .result[1].latest_block_height)
|
||||||
|
echo "1st peer is on height $h1"
|
||||||
|
|
||||||
|
echo "Waiting until other peers reporting a height higher than the 1st one"
|
||||||
|
for i in $(seq 2 "$NUM_OF_PEERS"); do
|
||||||
|
attempt=1
|
||||||
|
hi=0
|
||||||
|
|
||||||
|
while [[ $hi -le $h1 ]] ; do
|
||||||
|
addr=$(test/p2p/ip.sh "$i"):46657
|
||||||
|
hi=$(curl -s "$addr/status" | jq .result[1].latest_block_height)
|
||||||
|
|
||||||
|
echo "... peer $i is on height $hi"
|
||||||
|
|
||||||
|
((attempt++))
|
||||||
|
if [ "$attempt" -ge $MAX_ATTEMPTS_TO_CATCH_UP ] ; then
|
||||||
|
echo "$attempt unsuccessful attempts were made to catch up"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
done
|
32
test/p2p/kill_all/test.sh
Normal file
32
test/p2p/kill_all/test.sh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
DOCKER_IMAGE=$1
|
||||||
|
NETWORK_NAME=$2
|
||||||
|
NUM_OF_PEERS=$3
|
||||||
|
NUM_OF_CRASHES=$4
|
||||||
|
|
||||||
|
cd "$GOPATH/src/github.com/tendermint/tendermint"
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# NUM_OF_CRASHES times:
|
||||||
|
# restart all peers
|
||||||
|
# wait for them to sync and check that they are making progress
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
for i in $(seq 1 "$NUM_OF_CRASHES"); do
|
||||||
|
echo ""
|
||||||
|
echo "Restarting all peers! Take $i ..."
|
||||||
|
|
||||||
|
# restart all peers
|
||||||
|
for j in $(seq 1 "$NUM_OF_PEERS"); do
|
||||||
|
docker stop "local_testnet_$j"
|
||||||
|
docker start "local_testnet_$j"
|
||||||
|
done
|
||||||
|
|
||||||
|
bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" kill_all_$i "test/p2p/kill_all/check_peers.sh $NUM_OF_PEERS"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "PASS"
|
||||||
|
echo ""
|
@ -4,6 +4,7 @@ set -eu
|
|||||||
DOCKER_IMAGE=$1
|
DOCKER_IMAGE=$1
|
||||||
NETWORK_NAME=$2
|
NETWORK_NAME=$2
|
||||||
N=$3
|
N=$3
|
||||||
|
APP_PROXY=$4
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
@ -17,5 +18,5 @@ done
|
|||||||
echo "Seeds: $seeds"
|
echo "Seeds: $seeds"
|
||||||
|
|
||||||
for i in `seq 1 $N`; do
|
for i in `seq 1 $N`; do
|
||||||
bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $i $seeds
|
bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $i $APP_PROXY $seeds
|
||||||
done
|
done
|
12
test/p2p/local_testnet_stop.sh
Normal file
12
test/p2p/local_testnet_stop.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -u
|
||||||
|
|
||||||
|
NETWORK_NAME=$1
|
||||||
|
N=$2
|
||||||
|
|
||||||
|
for i in `seq 1 $N`; do
|
||||||
|
docker stop local_testnet_$i
|
||||||
|
docker rm -vf local_testnet_$i
|
||||||
|
done
|
||||||
|
|
||||||
|
docker network rm $NETWORK_NAME
|
@ -4,9 +4,10 @@ set -eu
|
|||||||
DOCKER_IMAGE=$1
|
DOCKER_IMAGE=$1
|
||||||
NETWORK_NAME=$2
|
NETWORK_NAME=$2
|
||||||
ID=$3
|
ID=$3
|
||||||
|
APP_PROXY=$4
|
||||||
|
|
||||||
set +u
|
set +u
|
||||||
SEEDS=$4
|
SEEDS=$5
|
||||||
set -u
|
set -u
|
||||||
if [[ "$SEEDS" != "" ]]; then
|
if [[ "$SEEDS" != "" ]]; then
|
||||||
SEEDS=" --seeds $SEEDS "
|
SEEDS=" --seeds $SEEDS "
|
||||||
@ -20,4 +21,4 @@ docker run -d \
|
|||||||
--name local_testnet_$ID \
|
--name local_testnet_$ID \
|
||||||
--entrypoint tendermint \
|
--entrypoint tendermint \
|
||||||
-e TMROOT=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core \
|
-e TMROOT=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core \
|
||||||
$DOCKER_IMAGE node $SEEDS --proxy_app=dummy
|
$DOCKER_IMAGE node $SEEDS --proxy_app=$APP_PROXY
|
||||||
|
@ -4,11 +4,18 @@ set -eu
|
|||||||
DOCKER_IMAGE=$1
|
DOCKER_IMAGE=$1
|
||||||
NETWORK_NAME=local_testnet
|
NETWORK_NAME=local_testnet
|
||||||
N=4
|
N=4
|
||||||
|
PROXY_APP=persistent_dummy
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
|
# stop the existing testnet and remove local network
|
||||||
|
set +e
|
||||||
|
bash test/p2p/local_testnet_stop.sh $NETWORK_NAME $N
|
||||||
|
set -e
|
||||||
|
|
||||||
# start the testnet on a local network
|
# start the testnet on a local network
|
||||||
bash test/p2p/local_testnet.sh $DOCKER_IMAGE $NETWORK_NAME $N
|
# NOTE we re-use the same network for all tests
|
||||||
|
bash test/p2p/local_testnet_start.sh $DOCKER_IMAGE $NETWORK_NAME $N $PROXY_APP
|
||||||
|
|
||||||
# test basic connectivity and consensus
|
# test basic connectivity and consensus
|
||||||
# start client container and check the num peers and height for all nodes
|
# start client container and check the num peers and height for all nodes
|
||||||
@ -20,4 +27,7 @@ bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME ab "test/p2p/atomic_broadcas
|
|||||||
|
|
||||||
# test fast sync (from current state of network):
|
# test fast sync (from current state of network):
|
||||||
# for each node, kill it and readd via fast sync
|
# for each node, kill it and readd via fast sync
|
||||||
bash test/p2p/fast_sync/test.sh $DOCKER_IMAGE $NETWORK_NAME $N
|
bash test/p2p/fast_sync/test.sh $DOCKER_IMAGE $NETWORK_NAME $N $PROXY_APP
|
||||||
|
|
||||||
|
# test killing all peers
|
||||||
|
bash test/p2p/kill_all/test.sh $DOCKER_IMAGE $NETWORK_NAME $N 3
|
||||||
|
@ -1,70 +1,5 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
|
||||||
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
export TMROOT=$HOME/.tendermint_persist
|
bash ./test/persist/test_failure_indices.sh
|
||||||
|
|
||||||
rm -rf $TMROOT
|
|
||||||
tendermint init
|
|
||||||
|
|
||||||
function start_procs(){
|
|
||||||
name=$1
|
|
||||||
echo "Starting persistent dummy and tendermint"
|
|
||||||
dummy --persist $TMROOT/dummy &> "dummy_${name}.log" &
|
|
||||||
PID_DUMMY=$!
|
|
||||||
tendermint node &> tendermint_${name}.log &
|
|
||||||
PID_TENDERMINT=$!
|
|
||||||
sleep 5
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill_procs(){
|
|
||||||
kill -9 $PID_DUMMY $PID_TENDERMINT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function send_txs(){
|
|
||||||
# send a bunch of txs over a few blocks
|
|
||||||
echo "Sending txs"
|
|
||||||
for i in `seq 1 5`; do
|
|
||||||
for j in `seq 1 100`; do
|
|
||||||
tx=`head -c 8 /dev/urandom | hexdump -ve '1/1 "%.2X"'`
|
|
||||||
curl -s 127.0.0.1:46657/broadcast_tx_async?tx=\"$tx\" &> /dev/null
|
|
||||||
done
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
start_procs 1
|
|
||||||
send_txs
|
|
||||||
kill_procs
|
|
||||||
|
|
||||||
start_procs 2
|
|
||||||
|
|
||||||
# wait for node to handshake and make a new block
|
|
||||||
addr="localhost:46657"
|
|
||||||
curl -s $addr/status > /dev/null
|
|
||||||
ERR=$?
|
|
||||||
i=0
|
|
||||||
while [ "$ERR" != 0 ]; do
|
|
||||||
sleep 1
|
|
||||||
curl -s $addr/status > /dev/null
|
|
||||||
ERR=$?
|
|
||||||
i=$(($i + 1))
|
|
||||||
if [[ $i == 10 ]]; then
|
|
||||||
echo "Timed out waiting for tendermint to start"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# wait for a new block
|
|
||||||
h1=`curl -s $addr/status | jq .result[1].latest_block_height`
|
|
||||||
h2=$h1
|
|
||||||
while [ "$h2" == "$h1" ]; do
|
|
||||||
sleep 1
|
|
||||||
h2=`curl -s $addr/status | jq .result[1].latest_block_height`
|
|
||||||
done
|
|
||||||
|
|
||||||
kill_procs
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
echo "Passed Test: Persistence"
|
|
||||||
|
@ -14,11 +14,11 @@ function start_procs(){
|
|||||||
PID_DUMMY=$!
|
PID_DUMMY=$!
|
||||||
if [[ "$indexToFail" == "" ]]; then
|
if [[ "$indexToFail" == "" ]]; then
|
||||||
# run in background, dont fail
|
# run in background, dont fail
|
||||||
tendermint node &> tendermint_${name}.log &
|
tendermint node --log_level=debug &> tendermint_${name}.log &
|
||||||
PID_TENDERMINT=$!
|
PID_TENDERMINT=$!
|
||||||
else
|
else
|
||||||
# run in foreground, fail
|
# run in foreground, fail
|
||||||
FAIL_TEST_INDEX=$indexToFail tendermint node &> tendermint_${name}.log
|
FAIL_TEST_INDEX=$indexToFail tendermint node --log_level=debug &> tendermint_${name}.log
|
||||||
PID_TENDERMINT=$!
|
PID_TENDERMINT=$!
|
||||||
fi
|
fi
|
||||||
}
|
}
|
70
test/persist/test_simple.sh
Normal file
70
test/persist/test_simple.sh
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
export TMROOT=$HOME/.tendermint_persist
|
||||||
|
|
||||||
|
rm -rf $TMROOT
|
||||||
|
tendermint init
|
||||||
|
|
||||||
|
function start_procs(){
|
||||||
|
name=$1
|
||||||
|
echo "Starting persistent dummy and tendermint"
|
||||||
|
dummy --persist $TMROOT/dummy &> "dummy_${name}.log" &
|
||||||
|
PID_DUMMY=$!
|
||||||
|
tendermint node &> tendermint_${name}.log &
|
||||||
|
PID_TENDERMINT=$!
|
||||||
|
sleep 5
|
||||||
|
}
|
||||||
|
|
||||||
|
function kill_procs(){
|
||||||
|
kill -9 $PID_DUMMY $PID_TENDERMINT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function send_txs(){
|
||||||
|
# send a bunch of txs over a few blocks
|
||||||
|
echo "Sending txs"
|
||||||
|
for i in `seq 1 5`; do
|
||||||
|
for j in `seq 1 100`; do
|
||||||
|
tx=`head -c 8 /dev/urandom | hexdump -ve '1/1 "%.2X"'`
|
||||||
|
curl -s 127.0.0.1:46657/broadcast_tx_async?tx=\"$tx\" &> /dev/null
|
||||||
|
done
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
start_procs 1
|
||||||
|
send_txs
|
||||||
|
kill_procs
|
||||||
|
|
||||||
|
start_procs 2
|
||||||
|
|
||||||
|
# wait for node to handshake and make a new block
|
||||||
|
addr="localhost:46657"
|
||||||
|
curl -s $addr/status > /dev/null
|
||||||
|
ERR=$?
|
||||||
|
i=0
|
||||||
|
while [ "$ERR" != 0 ]; do
|
||||||
|
sleep 1
|
||||||
|
curl -s $addr/status > /dev/null
|
||||||
|
ERR=$?
|
||||||
|
i=$(($i + 1))
|
||||||
|
if [[ $i == 10 ]]; then
|
||||||
|
echo "Timed out waiting for tendermint to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# wait for a new block
|
||||||
|
h1=`curl -s $addr/status | jq .result[1].latest_block_height`
|
||||||
|
h2=$h1
|
||||||
|
while [ "$h2" == "$h1" ]; do
|
||||||
|
sleep 1
|
||||||
|
h2=`curl -s $addr/status | jq .result[1].latest_block_height`
|
||||||
|
done
|
||||||
|
|
||||||
|
kill_procs
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo "Passed Test: Persistence"
|
Loading…
x
Reference in New Issue
Block a user