func newTestHarnessListener(logger log.Logger, cfg TestHarnessConfig) (*privval.SignerListenerEndpoint, error) {
	proto, addr := tmnet.ProtocolAndAddress(cfg.BindAddr)
	if proto == "unix" {
		if tmos.FileExists(addr) {
			if err := os.Remove(addr); err != nil {
				logger.Error("Failed to remove existing Unix domain socket", "addr", addr)
				return nil, err
			}
		}
	}
	ln, err := net.Listen(proto, addr)
	if err != nil {
		return nil, err
	}
	logger.Info("Listening", "proto", proto, "addr", addr)
	var svln net.Listener
	switch proto {
	case "unix":
		unixLn := privval.NewUnixListener(ln)
		privval.UnixListenerTimeoutAccept(cfg.AcceptDeadline)(unixLn)
		privval.UnixListenerTimeoutReadWrite(cfg.ConnDeadline)(unixLn)
		svln = unixLn
	case "tcp":
		tcpLn := privval.NewTCPListener(ln, cfg.SecretConnKey)
		privval.TCPListenerTimeoutAccept(cfg.AcceptDeadline)(tcpLn)
		privval.TCPListenerTimeoutReadWrite(cfg.ConnDeadline)(tcpLn)
		logger.Info("Resolved TCP address for listener", "addr", tcpLn.Addr())
		svln = tcpLn
	default:
		_ = ln.Close()
		logger.Error("Unsupported protocol (must be unix:// or tcp://)", "proto", proto)
		return nil, newTestHarnessError(ErrInvalidParameters, nil, fmt.Sprintf("Unsupported protocol: %s", proto))
	}
	return privval.NewSignerListenerEndpoint(logger, svln), nil
}
newTestHarnessListener creates our client instance which we will use for testing.
package internal
import (
	"bytes"
	"context"
	"fmt"
	"net"
	"os"
	"os/signal"
	"time"
	"github.com/tendermint/tendermint/crypto/tmhash"
	"github.com/tendermint/tendermint/crypto/ed25519"
	"github.com/tendermint/tendermint/internal/state"
	"github.com/tendermint/tendermint/privval"
	"github.com/tendermint/tendermint/libs/log"
	tmnet "github.com/tendermint/tendermint/libs/net"
	tmos "github.com/tendermint/tendermint/libs/os"
	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
	"github.com/tendermint/tendermint/types"
)
const (
	NoError                    int = iota  
	ErrInvalidParameters                   
	ErrMaxAcceptRetriesReached             
	ErrFailedToLoadGenesisFile             
	ErrFailedToCreateListener              
	ErrFailedToStartListener               
	ErrInterrupted                         
	ErrOther                               
	ErrTestPublicKeyFailed                 
	ErrTestSignProposalFailed              
	ErrTestSignVoteFailed                  
)
var voteTypes = []tmproto.SignedMsgType{tmproto.PrevoteType, tmproto.PrecommitType}
type TestHarnessError struct {
	Code int     
	Err  error   
	Info string  
}
var _ error = (*TestHarnessError)(nil)
type TestHarness struct {
	addr             string
	signerClient     *privval.SignerClient
	fpv              *privval.FilePV
	chainID          string
	acceptRetries    int
	logger           log.Logger
	exitWhenComplete bool
	exitCode         int
}
type TestHarnessConfig struct {
	BindAddr string
	KeyFile     string
	StateFile   string
	GenesisFile string
	AcceptDeadline time.Duration
	ConnDeadline   time.Duration
	AcceptRetries  int
	SecretConnKey ed25519.PrivKey
	ExitWhenComplete bool  
}
type timeoutError interface {
	Timeout() bool
}
func NewTestHarness(logger log.Logger, cfg TestHarnessConfig) (*TestHarness, error) {
	keyFile := ExpandPath(cfg.KeyFile)
	stateFile := ExpandPath(cfg.StateFile)
	logger.Info("Loading private validator configuration", "keyFile", keyFile, "stateFile", stateFile)
	fpv, err := privval.LoadFilePV(keyFile, stateFile)
	if err != nil {
		return nil, err
	}
	genesisFile := ExpandPath(cfg.GenesisFile)
	logger.Info("Loading chain ID from genesis file", "genesisFile", genesisFile)
	st, err := state.MakeGenesisDocFromFile(genesisFile)
	if err != nil {
		return nil, newTestHarnessError(ErrFailedToLoadGenesisFile, err, genesisFile)
	}
	logger.Info("Loaded genesis file", "chainID", st.ChainID)
	spv, err := newTestHarnessListener(logger, cfg)
	if err != nil {
		return nil, newTestHarnessError(ErrFailedToCreateListener, err, "")
	}
	signerClient, err := privval.NewSignerClient(spv, st.ChainID)
	if err != nil {
		return nil, newTestHarnessError(ErrFailedToCreateListener, err, "")
	}
	return &TestHarness{
		addr:             cfg.BindAddr,
		signerClient:     signerClient,
		fpv:              fpv,
		chainID:          st.ChainID,
		acceptRetries:    cfg.AcceptRetries,
		logger:           logger,
		exitWhenComplete: cfg.ExitWhenComplete,
		exitCode:         0,
	}, nil
}
func (th *TestHarness) Run() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			th.logger.Info("Caught interrupt, terminating...", "sig", sig)
			th.Shutdown(newTestHarnessError(ErrInterrupted, nil, ""))
		}
	}()
	th.logger.Info("Starting test harness")
	accepted := false
	var startErr error
	for acceptRetries := th.acceptRetries; acceptRetries > 0; acceptRetries-- {
		th.logger.Info("Attempting to accept incoming connection", "acceptRetries", acceptRetries)
		if err := th.signerClient.WaitForConnection(10 * time.Millisecond); err != nil {
			if _, ok := err.(timeoutError); !ok {
				th.logger.Error("Failed to start listener", "err", err)
				th.Shutdown(newTestHarnessError(ErrFailedToStartListener, err, ""))
				return
			}
			startErr = err
		} else {
			th.logger.Info("Accepted external connection")
			accepted = true
			break
		}
	}
	if !accepted {
		th.logger.Error("Maximum accept retries reached", "acceptRetries", th.acceptRetries)
		th.Shutdown(newTestHarnessError(ErrMaxAcceptRetriesReached, startErr, ""))
		return
	}
	if err := th.TestPublicKey(); err != nil {
		th.Shutdown(err)
		return
	}
	if err := th.TestSignProposal(); err != nil {
		th.Shutdown(err)
		return
	}
	if err := th.TestSignVote(); err != nil {
		th.Shutdown(err)
		return
	}
	th.logger.Info("SUCCESS! All tests passed.")
	th.Shutdown(nil)
}
func (th *TestHarness) TestPublicKey() error {
	th.logger.Info("TEST: Public key of remote signer")
	fpvk, err := th.fpv.GetPubKey(context.Background())
	if err != nil {
		return err
	}
	th.logger.Info("Local", "pubKey", fpvk)
	sck, err := th.signerClient.GetPubKey(context.Background())
	if err != nil {
		return err
	}
	th.logger.Info("Remote", "pubKey", sck)
	if !bytes.Equal(fpvk.Bytes(), sck.Bytes()) {
		th.logger.Error("FAILED: Local and remote public keys do not match")
		return newTestHarnessError(ErrTestPublicKeyFailed, nil, "")
	}
	return nil
}
func (th *TestHarness) TestSignProposal() error {
	th.logger.Info("TEST: Signing of proposals")
	hash := tmhash.Sum([]byte("hash"))
	prop := &types.Proposal{
		Type:     tmproto.ProposalType,
		Height:   100,
		Round:    0,
		POLRound: -1,
		BlockID: types.BlockID{
			Hash: hash,
			PartSetHeader: types.PartSetHeader{
				Hash:  hash,
				Total: 1000000,
			},
		},
		Timestamp: time.Now(),
	}
	p := prop.ToProto()
	propBytes := types.ProposalSignBytes(th.chainID, p)
	if err := th.signerClient.SignProposal(context.Background(), th.chainID, p); err != nil {
		th.logger.Error("FAILED: Signing of proposal", "err", err)
		return newTestHarnessError(ErrTestSignProposalFailed, err, "")
	}
	prop.Signature = p.Signature
	th.logger.Debug("Signed proposal", "prop", prop)
	if err := prop.ValidateBasic(); err != nil {
		th.logger.Error("FAILED: Signed proposal is invalid", "err", err)
		return newTestHarnessError(ErrTestSignProposalFailed, err, "")
	}
	sck, err := th.signerClient.GetPubKey(context.Background())
	if err != nil {
		return err
	}
	if sck.VerifySignature(propBytes, prop.Signature) {
		th.logger.Info("Successfully validated proposal signature")
	} else {
		th.logger.Error("FAILED: Proposal signature validation failed")
		return newTestHarnessError(ErrTestSignProposalFailed, nil, "signature validation failed")
	}
	return nil
}
func (th *TestHarness) TestSignVote() error {
	th.logger.Info("TEST: Signing of votes")
	for _, voteType := range voteTypes {
		th.logger.Info("Testing vote type", "type", voteType)
		hash := tmhash.Sum([]byte("hash"))
		vote := &types.Vote{
			Type:   voteType,
			Height: 101,
			Round:  0,
			BlockID: types.BlockID{
				Hash: hash,
				PartSetHeader: types.PartSetHeader{
					Hash:  hash,
					Total: 1000000,
				},
			},
			ValidatorIndex:   0,
			ValidatorAddress: tmhash.SumTruncated([]byte("addr")),
			Timestamp:        time.Now(),
		}
		v := vote.ToProto()
		voteBytes := types.VoteSignBytes(th.chainID, v)
		if err := th.signerClient.SignVote(context.Background(), th.chainID, v); err != nil {
			th.logger.Error("FAILED: Signing of vote", "err", err)
			return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType))
		}
		vote.Signature = v.Signature
		th.logger.Debug("Signed vote", "vote", vote)
		if err := vote.ValidateBasic(); err != nil {
			th.logger.Error("FAILED: Signed vote is invalid", "err", err)
			return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType))
		}
		sck, err := th.signerClient.GetPubKey(context.Background())
		if err != nil {
			return err
		}
		if sck.VerifySignature(voteBytes, vote.Signature) {
			th.logger.Info("Successfully validated vote signature", "type", voteType)
		} else {
			th.logger.Error("FAILED: Vote signature validation failed", "type", voteType)
			return newTestHarnessError(ErrTestSignVoteFailed, nil, "signature validation failed")
		}
	}
	return nil
}
func (th *TestHarness) Shutdown(err error) {
	var exitCode int
	if err == nil {
		exitCode = NoError
	} else if therr, ok := err.(*TestHarnessError); ok {
		exitCode = therr.Code
	} else {
		exitCode = ErrOther
	}
	th.exitCode = exitCode
	if th.exitWhenComplete {
		go func() {
			time.Sleep(time.Duration(5) * time.Second)
			th.logger.Error("Forcibly exiting program after timeout")
			os.Exit(exitCode)
		}()
	}
	err = th.signerClient.Close()
	if err != nil {
		th.logger.Error("Failed to cleanly stop listener: %s", err.Error())
	}
	if th.exitWhenComplete {
		os.Exit(exitCode)
	}
}