示例#1
0
// 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
	}
}
示例#2
0
// subscribes to an AccCall, runs the vm, returns the exception
func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas int64) string {
	// we need to catch the event from the CALL to check for exceptions
	evsw := events.NewEventSwitch()
	evsw.Start()
	ch := make(chan interface{})
	fmt.Printf("subscribe to %x\n", subscribeAddr)
	evsw.AddListenerForEvent("test", types.EventStringAccCall(subscribeAddr), func(msg types.EventData) {
		ch <- msg
	})
	evc := events.NewEventCache(evsw)
	ourVm.SetFireable(evc)
	go func() {
		start := time.Now()
		output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas)
		fmt.Printf("Output: %v Error: %v\n", output, err)
		fmt.Println("Call took:", time.Since(start))
		if err != nil {
			ch <- err.Error()
		}
		evc.Flush()
	}()
	msg := <-ch
	switch ev := msg.(type) {
	case types.EventDataTx:
		return ev.Exception
	case types.EventDataCall:
		return ev.Exception
	case string:
		return ev
	}
	return ""
}
示例#3
0
// 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 ExecTx and wait for the Call event on given addr
// returns the msg data and an error/exception
func execTxWaitEvent(t *testing.T, blockCache *BlockCache, tx types.Tx, eventid string) (interface{}, string) {
	evsw := events.NewEventSwitch()
	evsw.Start()
	ch := make(chan interface{})
	evsw.AddListenerForEvent("test", eventid, func(msg types.EventData) {
		ch <- msg
	})
	evc := events.NewEventCache(evsw)
	go func() {
		if err := ExecTx(blockCache, tx, true, evc); err != nil {
			ch <- err.Error()
		}
		evc.Flush()
	}()
	ticker := time.NewTicker(5 * time.Second)
	var msg interface{}
	select {
	case msg = <-ch:
	case <-ticker.C:
		return nil, ExceptionTimeOut
	}

	switch ev := msg.(type) {
	case types.EventDataTx:
		return ev, ev.Exception
	case types.EventDataCall:
		return ev, ev.Exception
	case string:
		return nil, ev
	default:
		return ev, ""
	}
}
示例#5
0
func simpleConsensusState(nValidators int) ([]*ConsensusState, []*types.PrivValidator) {
	// Get State
	state, privAccs, privVals := sm.RandGenesisState(10, true, 1000, nValidators, false, 10)
	_, _ = privAccs, privVals

	fmt.Println(state.BondedValidators)

	css := make([]*ConsensusState, nValidators)
	for i := 0; i < nValidators; i++ {
		// Get BlockStore
		blockDB := dbm.NewMemDB()
		blockStore := bc.NewBlockStore(blockDB)

		// Make MempoolReactor
		mempool := mempl.NewMempool(state.Copy())
		mempoolReactor := mempl.NewMempoolReactor(mempool)

		mempoolReactor.SetSwitch(p2p.NewSwitch())

		// Make ConsensusReactor
		cs := NewConsensusState(state, blockStore, mempoolReactor)
		cs.SetPrivValidator(privVals[i])

		evsw := events.NewEventSwitch()
		cs.SetFireable(evsw)

		// read off the NewHeightStep
		<-cs.NewStepCh()

		css[i] = cs
	}

	return css, privVals
}
示例#6
0
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())
}
示例#7
0
func simpleConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
	// Get State
	state, privVals := randGenesisState(nValidators, false, 10)

	// fmt.Println(state.Validators)

	vss := make([]*validatorStub, nValidators)

	// make consensus state for lead validator

	// Get BlockStore
	blockDB := dbm.NewMemDB()
	blockStore := bc.NewBlockStore(blockDB)

	// one for mempool, one for consensus
	app := example.NewCounterApplication(false)
	appCMem := app.Open()
	appCCon := app.Open()
	proxyAppCtxMem := proxy.NewLocalAppContext(appCMem)
	proxyAppCtxCon := proxy.NewLocalAppContext(appCCon)

	// Make Mempool
	mempool := mempl.NewMempool(proxyAppCtxMem)

	// Make ConsensusReactor
	cs := NewConsensusState(state, proxyAppCtxCon, blockStore, mempool)
	cs.SetPrivValidator(privVals[0])

	evsw := events.NewEventSwitch()
	cs.SetEventSwitch(evsw)
	evsw.Start()

	// start the transition routines
	//	cs.startRoutines()

	for i := 0; i < nValidators; i++ {
		vss[i] = NewValidatorStub(privVals[i])
	}
	// since cs1 starts at 1
	incrementHeight(vss[1:]...)

	return cs, vss
}
示例#8
0
// 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")
	}
}
示例#9
0
func NewNode() *Node {
	// Get BlockStore
	blockStoreDB := dbm.GetDB("blockstore")
	blockStore := bc.NewBlockStore(blockStoreDB)

	// Get State
	state := getState()

	// Create two proxyAppCtx connections,
	// one for the consensus and one for the mempool.
	proxyAddr := config.GetString("proxy_app")
	proxyAppCtxMempool := getProxyApp(proxyAddr, state.LastAppHash)
	proxyAppCtxConsensus := getProxyApp(proxyAddr, state.LastAppHash)

	// add the chainid to the global config
	config.Set("chain_id", state.ChainID)

	// Get PrivValidator
	privValidatorFile := config.GetString("priv_validator_file")
	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)

	// Generate node PrivKey
	privKey := crypto.GenPrivKeyEd25519()

	// Make event switch
	eventSwitch := events.NewEventSwitch()
	_, err := eventSwitch.Start()
	if err != nil {
		Exit(Fmt("Failed to start switch: %v", err))
	}

	// Make BlockchainReactor
	bcReactor := bc.NewBlockchainReactor(state.Copy(), proxyAppCtxConsensus, blockStore, config.GetBool("fast_sync"))

	// Make MempoolReactor
	mempool := mempl.NewMempool(proxyAppCtxMempool)
	mempoolReactor := mempl.NewMempoolReactor(mempool)

	// Make ConsensusReactor
	consensusState := consensus.NewConsensusState(state.Copy(), proxyAppCtxConsensus, blockStore, mempool)
	consensusReactor := consensus.NewConsensusReactor(consensusState, blockStore, config.GetBool("fast_sync"))
	if privValidator != nil {
		consensusReactor.SetPrivValidator(privValidator)
	}

	// Make p2p network switch
	sw := p2p.NewSwitch()
	sw.AddReactor("MEMPOOL", mempoolReactor)
	sw.AddReactor("BLOCKCHAIN", bcReactor)
	sw.AddReactor("CONSENSUS", consensusReactor)

	// add the event switch to all services
	// they should all satisfy events.Eventable
	SetEventSwitch(eventSwitch, 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))
		}()
	}

	return &Node{
		sw:               sw,
		evsw:             eventSwitch,
		blockStore:       blockStore,
		bcReactor:        bcReactor,
		mempoolReactor:   mempoolReactor,
		consensusState:   consensusState,
		consensusReactor: consensusReactor,
		privValidator:    privValidator,
		genesisDoc:       state.GenesisDoc,
		privKey:          privKey,
	}
}
示例#10
0
func NewNode() *Node {
	// Get BlockStore
	blockStoreDB := dbm.GetDB("blockstore")
	blockStore := bc.NewBlockStore(blockStoreDB)

	// Get State
	stateDB := dbm.GetDB("state")
	state := sm.LoadState(stateDB)
	var genDoc *stypes.GenesisDoc
	if state == nil {
		genDoc, state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file"))
		state.Save()
		// write the gendoc to db
		buf, n, err := new(bytes.Buffer), new(int64), new(error)
		wire.WriteJSON(genDoc, buf, n, err)
		stateDB.Set(stypes.GenDocKey, buf.Bytes())
		if *err != nil {
			Exit(Fmt("Unable to write gendoc to db: %v", err))
		}
	} else {
		genDocBytes := stateDB.Get(stypes.GenDocKey)
		err := new(error)
		wire.ReadJSONPtr(&genDoc, genDocBytes, err)
		if *err != nil {
			Exit(Fmt("Unable to read gendoc from db: %v", err))
		}
	}
	// add the chainid to the global config
	config.Set("chain_id", state.ChainID)

	// Get PrivValidator
	privValidatorFile := config.GetString("priv_validator_file")
	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)

	// Generate node PrivKey
	privKey := acm.GenPrivKeyEd25519()

	// Make event switch
	eventSwitch := events.NewEventSwitch()
	_, err := eventSwitch.Start()
	if err != nil {
		Exit(Fmt("Failed to start switch: %v", err))
	}

	// Make PEXReactor
	book := p2p.NewAddrBook(config.GetString("addrbook_file"))
	pexReactor := p2p.NewPEXReactor(book)

	// Make BlockchainReactor
	bcReactor := bc.NewBlockchainReactor(state.Copy(), blockStore, config.GetBool("fast_sync"))

	// Make MempoolReactor
	mempool := mempl.NewMempool(state.Copy())
	mempoolReactor := mempl.NewMempoolReactor(mempool)

	// Make ConsensusReactor
	consensusState := consensus.NewConsensusState(state.Copy(), blockStore, mempoolReactor)
	consensusReactor := consensus.NewConsensusReactor(consensusState, blockStore, config.GetBool("fast_sync"))
	if privValidator != nil {
		consensusReactor.SetPrivValidator(privValidator)
	}

	// Make p2p network switch
	sw := p2p.NewSwitch()
	sw.AddReactor("PEX", pexReactor)
	sw.AddReactor("MEMPOOL", mempoolReactor)
	sw.AddReactor("BLOCKCHAIN", bcReactor)
	sw.AddReactor("CONSENSUS", consensusReactor)

	// add the event switch to all services
	// 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,
		book:             book,
		blockStore:       blockStore,
		pexReactor:       pexReactor,
		bcReactor:        bcReactor,
		mempoolReactor:   mempoolReactor,
		consensusState:   consensusState,
		consensusReactor: consensusReactor,
		privValidator:    privValidator,
		genDoc:           genDoc,
		privKey:          privKey,
	}
}
示例#11
0
// 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")
	}
}
示例#12
0
// 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)
}
示例#13
0
// 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
}
示例#14
0
// 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")
	}
}
示例#15
0
// 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
}
示例#16
0
// 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")
	}
}
示例#17
0
// Tests logs and events.
func TestLog4(t *testing.T) {

	st := newAppState()
	// Create accounts
	account1 := &Account{
		Address: LeftPadWord256(makeBytes(20)),
	}
	account2 := &Account{
		Address: LeftPadWord256(makeBytes(20)),
	}
	st.accounts[account1.Address.String()] = account1
	st.accounts[account2.Address.String()] = account2

	ourVm := NewVM(st, newParams(), Zero256, nil)

	eventSwitch := events.NewEventSwitch()
	_, err := eventSwitch.Start()
	if err != nil {
		t.Errorf("Failed to start eventSwitch: %v", err)
	}
	eventID := types.EventStringLogEvent(account2.Address.Postfix(20))

	doneChan := make(chan struct{}, 1)

	eventSwitch.AddListenerForEvent("test", eventID, func(event types.EventData) {
		logEvent := event.(types.EventDataLog)
		// No need to test address as this event would not happen if it wasn't correct
		if !reflect.DeepEqual(logEvent.Topics, expectedTopics) {
			t.Errorf("Event topics are wrong. Got: %v. Expected: %v", logEvent.Topics, expectedTopics)
		}
		if !bytes.Equal(logEvent.Data, expectedData) {
			t.Errorf("Event data is wrong. Got: %s. Expected: %s", logEvent.Data, expectedData)
		}
		if logEvent.Height != expectedHeight {
			t.Errorf("Event block height is wrong. Got: %d. Expected: %d", logEvent.Height, expectedHeight)
		}
		doneChan <- struct{}{}
	})

	ourVm.SetFireable(eventSwitch)

	var gas int64 = 100000

	mstore8 := byte(MSTORE8)
	push1 := byte(PUSH1)
	log4 := byte(LOG4)
	stop := byte(STOP)

	code := []byte{
		push1, 16, // data value
		push1, 0, // memory slot
		mstore8,
		push1, 4, // topic 4
		push1, 3, // topic 3
		push1, 2, // topic 2
		push1, 1, // topic 1
		push1, 1, // size of data
		push1, 0, // data starts at this offset
		log4,
		stop,
	}

	_, err = ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
	<-doneChan
	if err != nil {
		t.Fatal(err)
	}
}