You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
12 KiB

Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes
6 years ago
Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes
6 years ago
Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes
6 years ago
Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes
6 years ago
Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes
6 years ago
Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process
6 years ago
  1. package internal
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "os/signal"
  7. "time"
  8. "github.com/tendermint/tendermint/crypto/tmhash"
  9. "github.com/tendermint/tendermint/crypto/ed25519"
  10. "github.com/tendermint/tendermint/privval"
  11. "github.com/tendermint/tendermint/state"
  12. cmn "github.com/tendermint/tendermint/libs/common"
  13. "github.com/tendermint/tendermint/libs/log"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. // Test harness error codes (which act as exit codes when the test harness fails).
  17. const (
  18. NoError int = iota // 0
  19. ErrInvalidParameters // 1
  20. ErrMaxAcceptRetriesReached // 2
  21. ErrFailedToLoadGenesisFile // 3
  22. ErrFailedToCreateListener // 4
  23. ErrFailedToStartListener // 5
  24. ErrInterrupted // 6
  25. ErrOther // 7
  26. ErrTestPublicKeyFailed // 8
  27. ErrTestSignProposalFailed // 9
  28. ErrTestSignVoteFailed // 10
  29. )
  30. var voteTypes = []types.SignedMsgType{types.PrevoteType, types.PrecommitType}
  31. // TestHarnessError allows us to keep track of which exit code should be used
  32. // when exiting the main program.
  33. type TestHarnessError struct {
  34. Code int // The exit code to return
  35. Err error // The original error
  36. Info string // Any additional information
  37. }
  38. var _ error = (*TestHarnessError)(nil)
  39. // TestHarness allows for testing of a remote signer to ensure compatibility
  40. // with this version of Tendermint.
  41. type TestHarness struct {
  42. addr string
  43. spv *privval.SignerValidatorEndpoint
  44. fpv *privval.FilePV
  45. chainID string
  46. acceptRetries int
  47. logger log.Logger
  48. exitWhenComplete bool
  49. exitCode int
  50. }
  51. // TestHarnessConfig provides configuration to set up a remote signer test
  52. // harness.
  53. type TestHarnessConfig struct {
  54. BindAddr string
  55. KeyFile string
  56. StateFile string
  57. GenesisFile string
  58. AcceptDeadline time.Duration
  59. ConnDeadline time.Duration
  60. AcceptRetries int
  61. SecretConnKey ed25519.PrivKeyEd25519
  62. ExitWhenComplete bool // Whether or not to call os.Exit when the harness has completed.
  63. }
  64. // timeoutError can be used to check if an error returned from the netp package
  65. // was due to a timeout.
  66. type timeoutError interface {
  67. Timeout() bool
  68. }
  69. // NewTestHarness will load Tendermint data from the given files (including
  70. // validator public/private keypairs and chain details) and create a new
  71. // harness.
  72. func NewTestHarness(logger log.Logger, cfg TestHarnessConfig) (*TestHarness, error) {
  73. keyFile := ExpandPath(cfg.KeyFile)
  74. stateFile := ExpandPath(cfg.StateFile)
  75. logger.Info("Loading private validator configuration", "keyFile", keyFile, "stateFile", stateFile)
  76. // NOTE: LoadFilePV ultimately calls os.Exit on failure. No error will be
  77. // returned if this call fails.
  78. fpv := privval.LoadFilePV(keyFile, stateFile)
  79. genesisFile := ExpandPath(cfg.GenesisFile)
  80. logger.Info("Loading chain ID from genesis file", "genesisFile", genesisFile)
  81. st, err := state.MakeGenesisDocFromFile(genesisFile)
  82. if err != nil {
  83. return nil, newTestHarnessError(ErrFailedToLoadGenesisFile, err, genesisFile)
  84. }
  85. logger.Info("Loaded genesis file", "chainID", st.ChainID)
  86. spv, err := newTestHarnessSocketVal(logger, cfg)
  87. if err != nil {
  88. return nil, newTestHarnessError(ErrFailedToCreateListener, err, "")
  89. }
  90. return &TestHarness{
  91. addr: cfg.BindAddr,
  92. spv: spv,
  93. fpv: fpv,
  94. chainID: st.ChainID,
  95. acceptRetries: cfg.AcceptRetries,
  96. logger: logger,
  97. exitWhenComplete: cfg.ExitWhenComplete,
  98. exitCode: 0,
  99. }, nil
  100. }
  101. // Run will execute the tests associated with this test harness. The intention
  102. // here is to call this from one's `main` function, as the way it succeeds or
  103. // fails at present is to call os.Exit() with an exit code related to the error
  104. // that caused the tests to fail, or exit code 0 on success.
  105. func (th *TestHarness) Run() {
  106. c := make(chan os.Signal, 1)
  107. signal.Notify(c, os.Interrupt)
  108. go func() {
  109. for sig := range c {
  110. th.logger.Info("Caught interrupt, terminating...", "sig", sig)
  111. th.Shutdown(newTestHarnessError(ErrInterrupted, nil, ""))
  112. }
  113. }()
  114. th.logger.Info("Starting test harness")
  115. accepted := false
  116. var startErr error
  117. for acceptRetries := th.acceptRetries; acceptRetries > 0; acceptRetries-- {
  118. th.logger.Info("Attempting to accept incoming connection", "acceptRetries", acceptRetries)
  119. if err := th.spv.Start(); err != nil {
  120. // if it wasn't a timeout error
  121. if _, ok := err.(timeoutError); !ok {
  122. th.logger.Error("Failed to start listener", "err", err)
  123. th.Shutdown(newTestHarnessError(ErrFailedToStartListener, err, ""))
  124. // we need the return statements in case this is being run
  125. // from a unit test - otherwise this function will just die
  126. // when os.Exit is called
  127. return
  128. }
  129. startErr = err
  130. } else {
  131. accepted = true
  132. break
  133. }
  134. }
  135. if !accepted {
  136. th.logger.Error("Maximum accept retries reached", "acceptRetries", th.acceptRetries)
  137. th.Shutdown(newTestHarnessError(ErrMaxAcceptRetriesReached, startErr, ""))
  138. return
  139. }
  140. // Run the tests
  141. if err := th.TestPublicKey(); err != nil {
  142. th.Shutdown(err)
  143. return
  144. }
  145. if err := th.TestSignProposal(); err != nil {
  146. th.Shutdown(err)
  147. return
  148. }
  149. if err := th.TestSignVote(); err != nil {
  150. th.Shutdown(err)
  151. return
  152. }
  153. th.logger.Info("SUCCESS! All tests passed.")
  154. th.Shutdown(nil)
  155. }
  156. // TestPublicKey just validates that we can (1) fetch the public key from the
  157. // remote signer, and (2) it matches the public key we've configured for our
  158. // local Tendermint version.
  159. func (th *TestHarness) TestPublicKey() error {
  160. th.logger.Info("TEST: Public key of remote signer")
  161. th.logger.Info("Local", "pubKey", th.fpv.GetPubKey())
  162. th.logger.Info("Remote", "pubKey", th.spv.GetPubKey())
  163. if th.fpv.GetPubKey() != th.spv.GetPubKey() {
  164. th.logger.Error("FAILED: Local and remote public keys do not match")
  165. return newTestHarnessError(ErrTestPublicKeyFailed, nil, "")
  166. }
  167. return nil
  168. }
  169. // TestSignProposal makes sure the remote signer can successfully sign
  170. // proposals.
  171. func (th *TestHarness) TestSignProposal() error {
  172. th.logger.Info("TEST: Signing of proposals")
  173. // sha256 hash of "hash"
  174. hash := tmhash.Sum([]byte("hash"))
  175. prop := &types.Proposal{
  176. Type: types.ProposalType,
  177. Height: 12345,
  178. Round: 23456,
  179. POLRound: -1,
  180. BlockID: types.BlockID{
  181. Hash: hash,
  182. PartsHeader: types.PartSetHeader{
  183. Hash: hash,
  184. Total: 1000000,
  185. },
  186. },
  187. Timestamp: time.Now(),
  188. }
  189. propBytes := prop.SignBytes(th.chainID)
  190. if err := th.spv.SignProposal(th.chainID, prop); err != nil {
  191. th.logger.Error("FAILED: Signing of proposal", "err", err)
  192. return newTestHarnessError(ErrTestSignProposalFailed, err, "")
  193. }
  194. th.logger.Debug("Signed proposal", "prop", prop)
  195. // first check that it's a basically valid proposal
  196. if err := prop.ValidateBasic(); err != nil {
  197. th.logger.Error("FAILED: Signed proposal is invalid", "err", err)
  198. return newTestHarnessError(ErrTestSignProposalFailed, err, "")
  199. }
  200. // now validate the signature on the proposal
  201. if th.spv.GetPubKey().VerifyBytes(propBytes, prop.Signature) {
  202. th.logger.Info("Successfully validated proposal signature")
  203. } else {
  204. th.logger.Error("FAILED: Proposal signature validation failed")
  205. return newTestHarnessError(ErrTestSignProposalFailed, nil, "signature validation failed")
  206. }
  207. return nil
  208. }
  209. // TestSignVote makes sure the remote signer can successfully sign all kinds of
  210. // votes.
  211. func (th *TestHarness) TestSignVote() error {
  212. th.logger.Info("TEST: Signing of votes")
  213. for _, voteType := range voteTypes {
  214. th.logger.Info("Testing vote type", "type", voteType)
  215. hash := tmhash.Sum([]byte("hash"))
  216. vote := &types.Vote{
  217. Type: voteType,
  218. Height: 12345,
  219. Round: 23456,
  220. BlockID: types.BlockID{
  221. Hash: hash,
  222. PartsHeader: types.PartSetHeader{
  223. Hash: hash,
  224. Total: 1000000,
  225. },
  226. },
  227. ValidatorIndex: 0,
  228. ValidatorAddress: tmhash.SumTruncated([]byte("addr")),
  229. Timestamp: time.Now(),
  230. }
  231. voteBytes := vote.SignBytes(th.chainID)
  232. // sign the vote
  233. if err := th.spv.SignVote(th.chainID, vote); err != nil {
  234. th.logger.Error("FAILED: Signing of vote", "err", err)
  235. return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType))
  236. }
  237. th.logger.Debug("Signed vote", "vote", vote)
  238. // validate the contents of the vote
  239. if err := vote.ValidateBasic(); err != nil {
  240. th.logger.Error("FAILED: Signed vote is invalid", "err", err)
  241. return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType))
  242. }
  243. // now validate the signature on the proposal
  244. if th.spv.GetPubKey().VerifyBytes(voteBytes, vote.Signature) {
  245. th.logger.Info("Successfully validated vote signature", "type", voteType)
  246. } else {
  247. th.logger.Error("FAILED: Vote signature validation failed", "type", voteType)
  248. return newTestHarnessError(ErrTestSignVoteFailed, nil, "signature validation failed")
  249. }
  250. }
  251. return nil
  252. }
  253. // Shutdown will kill the test harness and attempt to close all open sockets
  254. // gracefully. If the supplied error is nil, it is assumed that the exit code
  255. // should be 0. If err is not nil, it will exit with an exit code related to the
  256. // error.
  257. func (th *TestHarness) Shutdown(err error) {
  258. var exitCode int
  259. if err == nil {
  260. exitCode = NoError
  261. } else if therr, ok := err.(*TestHarnessError); ok {
  262. exitCode = therr.Code
  263. } else {
  264. exitCode = ErrOther
  265. }
  266. th.exitCode = exitCode
  267. // in case sc.Stop() takes too long
  268. if th.exitWhenComplete {
  269. go func() {
  270. time.Sleep(time.Duration(5) * time.Second)
  271. th.logger.Error("Forcibly exiting program after timeout")
  272. os.Exit(exitCode)
  273. }()
  274. }
  275. if th.spv.IsRunning() {
  276. if err := th.spv.Stop(); err != nil {
  277. th.logger.Error("Failed to cleanly stop listener: %s", err.Error())
  278. }
  279. }
  280. if th.exitWhenComplete {
  281. os.Exit(exitCode)
  282. }
  283. }
  284. // newTestHarnessSocketVal creates our client instance which we will use for
  285. // testing.
  286. func newTestHarnessSocketVal(logger log.Logger, cfg TestHarnessConfig) (*privval.SignerValidatorEndpoint, error) {
  287. proto, addr := cmn.ProtocolAndAddress(cfg.BindAddr)
  288. if proto == "unix" {
  289. // make sure the socket doesn't exist - if so, try to delete it
  290. if cmn.FileExists(addr) {
  291. if err := os.Remove(addr); err != nil {
  292. logger.Error("Failed to remove existing Unix domain socket", "addr", addr)
  293. return nil, err
  294. }
  295. }
  296. }
  297. ln, err := net.Listen(proto, addr)
  298. if err != nil {
  299. return nil, err
  300. }
  301. logger.Info("Listening at", "proto", proto, "addr", addr)
  302. var svln net.Listener
  303. switch proto {
  304. case "unix":
  305. unixLn := privval.NewUnixListener(ln)
  306. privval.UnixListenerTimeoutAccept(cfg.AcceptDeadline)(unixLn)
  307. privval.UnixListenerTimeoutReadWrite(cfg.ConnDeadline)(unixLn)
  308. svln = unixLn
  309. case "tcp":
  310. tcpLn := privval.NewTCPListener(ln, cfg.SecretConnKey)
  311. privval.TCPListenerTimeoutAccept(cfg.AcceptDeadline)(tcpLn)
  312. privval.TCPListenerTimeoutReadWrite(cfg.ConnDeadline)(tcpLn)
  313. logger.Info("Resolved TCP address for listener", "addr", tcpLn.Addr())
  314. svln = tcpLn
  315. default:
  316. logger.Error("Unsupported protocol (must be unix:// or tcp://)", "proto", proto)
  317. return nil, newTestHarnessError(ErrInvalidParameters, nil, fmt.Sprintf("Unsupported protocol: %s", proto))
  318. }
  319. return privval.NewSignerValidatorEndpoint(logger, svln), nil
  320. }
  321. func newTestHarnessError(code int, err error, info string) *TestHarnessError {
  322. return &TestHarnessError{
  323. Code: code,
  324. Err: err,
  325. Info: info,
  326. }
  327. }
  328. func (e *TestHarnessError) Error() string {
  329. var msg string
  330. switch e.Code {
  331. case ErrInvalidParameters:
  332. msg = "Invalid parameters supplied to application"
  333. case ErrMaxAcceptRetriesReached:
  334. msg = "Maximum accept retries reached"
  335. case ErrFailedToLoadGenesisFile:
  336. msg = "Failed to load genesis file"
  337. case ErrFailedToCreateListener:
  338. msg = "Failed to create listener"
  339. case ErrFailedToStartListener:
  340. msg = "Failed to start listener"
  341. case ErrInterrupted:
  342. msg = "Interrupted"
  343. case ErrTestPublicKeyFailed:
  344. msg = "Public key validation test failed"
  345. case ErrTestSignProposalFailed:
  346. msg = "Proposal signing validation test failed"
  347. case ErrTestSignVoteFailed:
  348. msg = "Vote signing validation test failed"
  349. default:
  350. msg = "Unknown error"
  351. }
  352. if len(e.Info) > 0 {
  353. msg = fmt.Sprintf("%s: %s", msg, e.Info)
  354. }
  355. if e.Err != nil {
  356. msg = fmt.Sprintf("%s (original error: %s)", msg, e.Err.Error())
  357. }
  358. return msg
  359. }