package internal
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
"github.com/tendermint/tendermint/privval"
|
|
"github.com/tendermint/tendermint/types"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
)
|
|
|
|
const (
|
|
keyFileContents = `{
|
|
"address": "D08FCA3BA74CF17CBFC15E64F9505302BB0E2748",
|
|
"pub_key": {
|
|
"type": "tendermint/PubKeyEd25519",
|
|
"value": "ZCsuTjaczEyon70nmKxwvwu+jqrbq5OH3yQjcK0SFxc="
|
|
},
|
|
"priv_key": {
|
|
"type": "tendermint/PrivKeyEd25519",
|
|
"value": "8O39AkQsoe1sBQwud/Kdul8lg8K9SFsql9aZvwXQSt1kKy5ONpzMTKifvSeYrHC/C76Oqturk4ffJCNwrRIXFw=="
|
|
}
|
|
}`
|
|
|
|
stateFileContents = `{
|
|
"height": "0",
|
|
"round": "0",
|
|
"step": 0
|
|
}`
|
|
|
|
genesisFileContents = `{
|
|
"genesis_time": "2019-01-15T11:56:34.8963Z",
|
|
"chain_id": "test-chain-0XwP5E",
|
|
"consensus_params": {
|
|
"block": {
|
|
"max_bytes": "22020096",
|
|
"max_gas": "-1",
|
|
"time_iota_ms": "1000"
|
|
},
|
|
"evidence": {
|
|
"max_age": "100000"
|
|
},
|
|
"validator": {
|
|
"pub_key_types": [
|
|
"ed25519"
|
|
]
|
|
}
|
|
},
|
|
"validators": [
|
|
{
|
|
"address": "D08FCA3BA74CF17CBFC15E64F9505302BB0E2748",
|
|
"pub_key": {
|
|
"type": "tendermint/PubKeyEd25519",
|
|
"value": "ZCsuTjaczEyon70nmKxwvwu+jqrbq5OH3yQjcK0SFxc="
|
|
},
|
|
"power": "10",
|
|
"name": ""
|
|
}
|
|
],
|
|
"app_hash": ""
|
|
}`
|
|
|
|
defaultConnDeadline = 100
|
|
)
|
|
|
|
func TestRemoteSignerTestHarnessMaxAcceptRetriesReached(t *testing.T) {
|
|
cfg := makeConfig(t, 1, 2)
|
|
defer cleanup(cfg)
|
|
|
|
th, err := NewTestHarness(log.TestingLogger(), cfg)
|
|
require.NoError(t, err)
|
|
th.Run()
|
|
assert.Equal(t, ErrMaxAcceptRetriesReached, th.exitCode)
|
|
}
|
|
|
|
func TestRemoteSignerTestHarnessSuccessfulRun(t *testing.T) {
|
|
harnessTest(
|
|
t,
|
|
func(th *TestHarness) *privval.SignerServiceEndpoint {
|
|
return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, false)
|
|
},
|
|
NoError,
|
|
)
|
|
}
|
|
|
|
func TestRemoteSignerPublicKeyCheckFailed(t *testing.T) {
|
|
harnessTest(
|
|
t,
|
|
func(th *TestHarness) *privval.SignerServiceEndpoint {
|
|
return newMockRemoteSigner(t, th, ed25519.GenPrivKey(), false, false)
|
|
},
|
|
ErrTestPublicKeyFailed,
|
|
)
|
|
}
|
|
|
|
func TestRemoteSignerProposalSigningFailed(t *testing.T) {
|
|
harnessTest(
|
|
t,
|
|
func(th *TestHarness) *privval.SignerServiceEndpoint {
|
|
return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, true, false)
|
|
},
|
|
ErrTestSignProposalFailed,
|
|
)
|
|
}
|
|
|
|
func TestRemoteSignerVoteSigningFailed(t *testing.T) {
|
|
harnessTest(
|
|
t,
|
|
func(th *TestHarness) *privval.SignerServiceEndpoint {
|
|
return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, true)
|
|
},
|
|
ErrTestSignVoteFailed,
|
|
)
|
|
}
|
|
|
|
func newMockRemoteSigner(t *testing.T, th *TestHarness, privKey crypto.PrivKey, breakProposalSigning bool, breakVoteSigning bool) *privval.SignerServiceEndpoint {
|
|
return privval.NewSignerServiceEndpoint(
|
|
th.logger,
|
|
th.chainID,
|
|
types.NewMockPVWithParams(privKey, breakProposalSigning, breakVoteSigning),
|
|
privval.DialTCPFn(
|
|
th.addr,
|
|
time.Duration(defaultConnDeadline)*time.Millisecond,
|
|
ed25519.GenPrivKey(),
|
|
),
|
|
)
|
|
}
|
|
|
|
// For running relatively standard tests.
|
|
func harnessTest(t *testing.T, rsMaker func(th *TestHarness) *privval.SignerServiceEndpoint, expectedExitCode int) {
|
|
cfg := makeConfig(t, 100, 3)
|
|
defer cleanup(cfg)
|
|
|
|
th, err := NewTestHarness(log.TestingLogger(), cfg)
|
|
require.NoError(t, err)
|
|
donec := make(chan struct{})
|
|
go func() {
|
|
defer close(donec)
|
|
th.Run()
|
|
}()
|
|
|
|
rs := rsMaker(th)
|
|
require.NoError(t, rs.Start())
|
|
assert.True(t, rs.IsRunning())
|
|
defer rs.Stop()
|
|
|
|
<-donec
|
|
assert.Equal(t, expectedExitCode, th.exitCode)
|
|
}
|
|
|
|
func makeConfig(t *testing.T, acceptDeadline, acceptRetries int) TestHarnessConfig {
|
|
return TestHarnessConfig{
|
|
BindAddr: testFreeTCPAddr(t),
|
|
KeyFile: makeTempFile("tm-testharness-keyfile", keyFileContents),
|
|
StateFile: makeTempFile("tm-testharness-statefile", stateFileContents),
|
|
GenesisFile: makeTempFile("tm-testharness-genesisfile", genesisFileContents),
|
|
AcceptDeadline: time.Duration(acceptDeadline) * time.Millisecond,
|
|
ConnDeadline: time.Duration(defaultConnDeadline) * time.Millisecond,
|
|
AcceptRetries: acceptRetries,
|
|
SecretConnKey: ed25519.GenPrivKey(),
|
|
ExitWhenComplete: false,
|
|
}
|
|
}
|
|
|
|
func cleanup(cfg TestHarnessConfig) {
|
|
os.Remove(cfg.KeyFile)
|
|
os.Remove(cfg.StateFile)
|
|
os.Remove(cfg.GenesisFile)
|
|
}
|
|
|
|
func makeTempFile(name, content string) string {
|
|
tempFile, err := ioutil.TempFile("", fmt.Sprintf("%s-*", name))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if _, err := tempFile.Write([]byte(content)); err != nil {
|
|
tempFile.Close()
|
|
panic(err)
|
|
}
|
|
if err := tempFile.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
return tempFile.Name()
|
|
}
|
|
|
|
// testFreeTCPAddr claims a free port so we don't block on listener being ready.
|
|
func testFreeTCPAddr(t *testing.T) string {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
require.NoError(t, err)
|
|
defer ln.Close()
|
|
|
|
return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port)
|
|
}
|