diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8ed961d..24b6ef4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,17 +8,23 @@ jobs: runs-on: ubuntu-latest steps: + - name: Set environment variable + run: echo "RUN_TESTS=false" >> $GITHUB_ENV # Adjust this to 'true' or 'false' + - name: Checkout + if: env.RUN_TESTS != 'false' uses: actions/checkout@v3 with: submodules: recursive - name: setup python + if: env.RUN_TESTS != 'false' uses: actions/setup-python@v4 with: - python-version: '3.7' + python-version: '3.9' - name: install packages + if: env.RUN_TESTS != 'false' run: | sudo apt-get update sudo apt-get -y install python3-pip jshon jq virtualenv pkg-config openssl libssl-dev autoconf libtool libsecp256k1-dev @@ -26,4 +32,5 @@ jobs: pip3 install -r requirements-dev.txt - name: Run tests + if: env.RUN_TESTS != 'false' run: ./test.sh diff --git a/chief_keeper/chief_keeper.py b/chief_keeper/chief_keeper.py index 6f6dd75..b6c9553 100644 --- a/chief_keeper/chief_keeper.py +++ b/chief_keeper/chief_keeper.py @@ -18,11 +18,15 @@ import argparse import logging import sys +import os import requests import time import types from web3 import Web3, HTTPProvider +from web3.exceptions import TimeExhausted + +from urllib.parse import urlparse from chief_keeper.database import SimpleDatabase from chief_keeper.spell import DSSSpell @@ -35,6 +39,27 @@ from pymaker.deployment import DssDeployment HEALTHCHECK_FILE_PATH = "/tmp/health.log" +BACKOFF_MAX_TIME = 120 + +class ExitOnCritical(logging.StreamHandler): + """Custom class to terminate script execution once + log records with severity level ERROR or higher occurred""" + + def emit(self, record): + super().emit(record) + if record.levelno > logging.ERROR: + sys.exit(1) + + +logging.basicConfig( + format="%(asctime)s [%(levelname)s] %(name)s - %(message)s", + datefmt="%Y-%m-%dT%H:%M:%S%z", + force=True, + handlers=[ExitOnCritical()], +) +logger = logging.getLogger() +log_level = logging.getLevelName(os.environ.get("LOG_LEVEL") or "INFO") +logger.setLevel(log_level) def healthy(func): @@ -51,110 +76,43 @@ def wrapper(*args, **kwargs): class ChiefKeeper: """Keeper that lifts the hat and streamlines executive actions""" - logger = logging.getLogger("chief-keeper") - - def __init__(self, args: list, **kwargs): """Pass in arguements assign necessary variables/objects and instantiate other Classes""" parser = argparse.ArgumentParser("chief-keeper") - parser.add_argument( - "--rpc-host", - type=str, - required=True, - help="JSON-RPC host url", - ) - - parser.add_argument( - "--rpc-timeout", - type=int, - default=60, - help="JSON-RPC timeout (in seconds, default: 60)", - ) - - parser.add_argument( - "--network", - type=str, - required=True, - help="Network that you're running the Keeper on (options, 'mainnet', 'kovan', 'testnet')", - ) - - parser.add_argument( - "--eth-from", - type=str, - required=True, - help="Ethereum address from which to send transactions; checksummed (e.g. '0x12AebC')", - ) - - parser.add_argument( - "--eth-key", - type=str, - nargs="*", - help="Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')", - ) - - parser.add_argument( - "--dss-deployment-file", - type=str, - required=False, - help="Json description of all the system addresses (e.g. /Full/Path/To/configFile.json)", - ) - - parser.add_argument( - "--chief-deployment-block", - type=int, - required=False, - default=0, - help=" Block that the Chief from dss-deployment-file was deployed at (e.g. 8836668", - ) - - parser.add_argument( - "--max-errors", - type=int, - default=100, - help="Maximum number of allowed errors before the keeper terminates (default: 100)", - ) - - parser.add_argument( - "--debug", dest="debug", action="store_true", help="Enable debug output" - ) - - parser.add_argument( - "--blocknative-api-key", - type=str, - default=None, - help="Blocknative API key", - ) - - parser.add_argument( - "--gas-initial-multiplier", - type=str, - default=1.0, - help="gas multiplier", - ) - parser.add_argument( - "--gas-reactive-multiplier", - type=str, - default=2.25, - help="gas strategy tuning", - ) - parser.add_argument( - "--gas-maximum", type=str, default=5000, help="gas strategy tuning" - ) + parser.add_argument("--rpc-primary-url", type=str, required=True, help="Primary JSON-RPC host URL") + parser.add_argument("--rpc-primary-timeout", type=int, default=1200, help="Primary JSON-RPC timeout (in seconds, default: 1200)") + parser.add_argument("--rpc-backup-url", type=str, required=True, help="Backup JSON-RPC host URL") + parser.add_argument("--rpc-backup-timeout", type=int, default=1200, help="Backup JSON-RPC timeout (in seconds, default: 1200)") + parser.add_argument("--network", type=str, required=True, help="Network that you're running the Keeper on (options, 'mainnet', 'kovan', 'testnet')") + parser.add_argument("--eth-from", type=str, required=True, help="Ethereum address from which to send transactions; checksummed (e.g. '0x12AebC')") + parser.add_argument("--eth-key", type=str, nargs="*", help="Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')") + parser.add_argument("--dss-deployment-file", type=str, required=False, help="Json description of all the system addresses (e.g. /Full/Path/To/configFile.json)") + parser.add_argument("--chief-deployment-block", type=int, required=False, default=0, help="Block that the Chief from dss-deployment-file was deployed at (e.g. 8836668") + parser.add_argument("--max-errors", type=int, default=100, help="Maximum number of allowed errors before the keeper terminates (default: 100)") + parser.add_argument("--debug", dest="debug", action="store_true", help="Enable debug output") + parser.add_argument("--blocknative-api-key", type=str, default=None, help="Blocknative API key") + parser.add_argument("--gas-initial-multiplier", type=float, default=1.0, help="gas multiplier") + parser.add_argument("--gas-reactive-multiplier", type=float, default=2.25, help="gas strategy tuning") + parser.add_argument("--gas-maximum", type=int, default=5000, help="gas strategy tuning") parser.set_defaults(cageFacilitated=False) self.arguments = parser.parse_args(args) - self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(HTTPProvider(endpoint_uri=self.arguments.rpc_host, - request_kwargs={"timeout": self.arguments.rpc_timeout})) + # Initialize logger before any method that uses it + self.logger = logger - self.web3.eth.defaultAccount = self.arguments.eth_from - register_keys(self.web3, self.arguments.eth_key) - self.our_address = Address(self.arguments.eth_from) + self.print_arguments() + + self.web3 = None + self.node_type = None + self._initialize_blockchain_connection() - isConnected = self.web3.isConnected() - self.logger.info(f'web3 isConntected is: {isConnected}') + # Set the Ethereum address and register keys + # self.web3.eth.defaultAccount = self.arguments.eth_from + # register_keys(self.web3, self.arguments.eth_key) + self.our_address = Address(self.arguments.eth_from) if self.arguments.dss_deployment_file: self.dss = DssDeployment.from_json( @@ -173,11 +131,58 @@ def __init__(self, args: list, **kwargs): self.confirmations = 0 - logging.basicConfig( - format="%(asctime)-15s %(levelname)-8s %(message)s", - level=(logging.DEBUG if self.arguments.debug else logging.INFO), + def print_arguments(self): + """Print all the arguments passed to the script.""" + for arg in vars(self.arguments): + self.logger.info(f"{arg}: {getattr(self.arguments, arg)}") + + def _initialize_blockchain_connection(self): + """Initialize connection with Ethereum node.""" + if not self._connect_to_primary_node(): + self.logger.info("Switching to backup node.") + if not self._connect_to_backup_node(): + self.logger.critical( + "Error: Couldn't connect to the primary and backup Ethereum nodes." + ) + + def _connect_to_primary_node(self): + """Connect to the primary Ethereum node""" + return self._connect_to_node( + self.arguments.rpc_primary_url, self.arguments.rpc_primary_timeout, "primary" ) + def _connect_to_backup_node(self): + """Connect to the backup Ethereum node""" + return self._connect_to_node( + self.arguments.rpc_backup_url, self.arguments.rpc_backup_timeout, "backup" + ) + + def _connect_to_node(self, rpc_url, rpc_timeout, node_type): + """Connect to an Ethereum node""" + try: + _web3 = Web3(HTTPProvider(rpc_url, {"timeout": rpc_timeout})) + except (TimeExhausted, Exception) as e: + self.logger.error(f"Error connecting to Ethereum node: {e}") + return False + else: + if _web3.isConnected(): + self.web3 = _web3 + self.node_type = node_type + return self._configure_web3() + return False + + def _configure_web3(self): + """Configure Web3 connection with private key""" + try: + self.web3.eth.defaultAccount = self.arguments.eth_from + register_keys(self.web3, self.arguments.eth_key) + except Exception as e: + self.logger.error(f"Error configuring Web3: {e}") + return False + else: + node_hostname = urlparse(self.web3.provider.endpoint_uri).hostname + self.logger.info(f"Connected to Ethereum node at {node_hostname}") + return True def main(self): """Initialize the lifecycle and enter into the Keeper Lifecycle controller. @@ -244,14 +249,18 @@ def process_block(self): """Callback called on each new block. If too many errors, terminate the keeper. This is the entrypoint to the Keeper's monitoring logic """ - isConnected = self.web3.isConnected() - self.logger.info(f'web3 isConntected is: {isConnected}') - - if self.errors >= self.max_errors: - self.lifecycle.terminate() - else: - self.check_hat() - self.check_eta() + try: + isConnected = self.web3.isConnected() + self.logger.info(f'web3 isConnected: {isConnected}') + + if self.errors >= self.max_errors: + self.lifecycle.terminate() + else: + self.check_hat() + self.check_eta() + except (TimeExhausted, Exception) as e: + self.logger.error(f"Error processing block: {e}") + self.errors += 1 def check_hat(self): """Ensures the Hat is on the proposal (spell, EOA, multisig, etc) with the most approval. @@ -265,7 +274,13 @@ def check_hat(self): blockNumber = self.web3.eth.blockNumber self.logger.info(f"Checking Hat on block {blockNumber}") - self.database.update_db_yays(blockNumber) + try: + self.database.update_db_yays(blockNumber) + except (TimeExhausted, Exception) as e: + self.logger.error(f"Error updating database yays: {e}") + self.errors += 1 + return + yays = self.database.db.get(doc_id=2)["yays"] hat = self.dss.ds_chief.get_hat().address diff --git a/chief_keeper/database.py b/chief_keeper/database.py index fd86421..5f91df7 100644 --- a/chief_keeper/database.py +++ b/chief_keeper/database.py @@ -21,6 +21,7 @@ from tinydb import TinyDB, Query from web3 import Web3 +from web3.exceptions import TimeExhausted from chief_keeper.spell import DSSSpell diff --git a/chief_keeper/database/db_mainnet.json b/chief_keeper/database/db_mainnet.json index dbc8aeb..c06a23a 100644 --- a/chief_keeper/database/db_mainnet.json +++ b/chief_keeper/database/db_mainnet.json @@ -1 +1 @@ -{"_default": {"1": {"last_block_checked_for_yays": 18522491}, "2": {"yays": ["0x2A8E8588ae9d420656c49C910C2c820450a01F95", "0xFc5154dc5F980A3377374864fbE7a25AFc9Fe5ED", "0xD8CAe22CDC75ab61Ec7663a355D043De4277dC1E", "0xa3F971B97E1e8d98061e58809748d525D8AE295F", "0x77bdCbD18A064B97a71F3982D6ae981EF32e72D8", "0x2EaCCAC62E9D9a55417c35df3491Ec6F6343e311", "0xd79E320fDF9a738E9ADa8C6644563c3E1923B33d", "0xcf50ef07DebcB608b8b1133A13bF5AC823E47D9D", "0xE3d9458cf864C9028363464c7D45C66078F54240", "0xAa0087F0AB8ffD4A8F72Bb1bb09EE7b046566412", "0x0aF27f917864a4f05D18CeeF22F0d23CD73C10aD", "0x74c63Bb8610C7a7538733ECf915Fa217605C01a5", "0xf282371EA8eFDE8e47D78131c3Af70a6e2A28617", "0xe9a7e8AB8e2b8118eC1f108a370b7151635D06B7", "0xeDc016ACE4618284F289696545760AC0dB5bc79B", "0x22E9B678daF00B41f2127837A521F92f18802674", "0x46e5E4502f99867405fC239Ca695f1569ef4d3e6", "0x3FfE82e0Ea5ABC48c3C4cA9ceF2F9C81515e800b", "0x899E41f14eE23BF3Dca12bDe066F35994DC4e7bc", "0x3Ef533927c4FE49B3dA53aC3241322eB98F9717d", "0xae220fa15DEd4e6e63310D2f6C8d3DFAaF4F8A1b", "0x6fBe6d79BD2aeEfA4B6929bD47babf236023902e", "0x15732a23db6A0D54E3240F72913950c8c9a64c20", "0xB81ddbFF61420A181bB725D5F06cA99486Bb4e6a", "0xD665104a2800FaDFd232961d718E2108e8ec6BDD", "0x3920f14096E89b1bc2f4Ebf3A847c913170a1F92", "0xeC17e6122578A6E5B92e904aD46b03Fe07568359", "0x28498dDD408001A7011DA2edE4136104e035f2F4", "0xBd144df1031f1718d50715e0ade8856C7b06AaE1", "0xB6b375aCebC0068F7414Cc6c81f4392d0760c845", "0x6fC17C824f3E39dF6D21b2740B676755E71466d6", "0xf8587266d56F3e3480F693a0AD2e36E6E2c12cDA", "0x5a53768b01AEb799572998819638C63c8EEEea52", "0xC1136F15C605222793F60E328FE3F01Ec2c3F264", "0xca988c4D38d562DDeDEbFb7a44ec5a72a6698f5c", "0xbD01665f3768D793DA1Ffc4DCD70114B31A3954C", "0x784e85ef0f6e05Ff5252629c2e2649c89bdC88Da", "0xE460E2588d8cb9c460F63Bd2f746724bD0e55Aa0", "0x25222057EF01A3BDe7F59d10695da6dAe42C531A", "0x74eD57e3c12Ee6Eec8E583BFFF77Ea9D33A602e2", "0x20B4a73B34Fb2893D60D24c1b3996cbC2664F7cC", "0xE75A8Ae8075425b4D2F8f53B55996afb9157D980", "0xaE071879962A36d338ed89Bb0fF61D0A5b8C6201", "0x31a5577D98251c6C08CAcfEE6faE2e2D7A040b50", "0x2bd13D445aD7a40c393FAA93b43b24AA35DcD973", "0xBA57238e70116fB61fdE07387c6dD79AebF92E5E", "0xA453B4C7BA03FBea2a1ddAD12Bb3Bc5dC41cEee8", "0x58339aB7f27e66768713aF28E8e539Ca56432423", "0x87471BEd520edf4B54E0091Ff754E3E69666A9D6", "0xceafE30A12d00D35beCdE807c5e360ce5124A750", "0xB5010b4bC4e506B933C2f0aca7B8214089167e33", "0x3d6F4c40A2C53964ddB4D63fBB50FA854e048499", "0x5abf3e4C8d144DB9d3603A76e524671040Aa4859", "0x38e13DaF8024f682deA2022c849152BbD9b071DF", "0x7a87C5dd42Fed47b85e23C3F5C02b735FD69A38F", "0xFd27AE89d4976Bb11d4FB7e0e215Eced997F13a6", "0xcD0E28B9DFCc1ee71d9D6834EF66C1E103E94B27", "0xa5e85cAC3Fb448E16699C97DA86124bD288017d6", "0x606395F1B167AA73134b8A2B34C25Bb7D0564920", "0xCa9f54957c61b58843628051561896d0557D42c3", "0x7aB726CdF93443302CC4c90e83cb7ce4210bb724", "0xf00Ad3314E08e0E8E713D8155E3c867580cD811b", "0x6571e4B3432e856CdA8fae95dB3e23B573019e8B", "0x3DcD037bB1D3A898f6CbbE136ae2e1959FF74e3c", "0x6E2850E425d89836F1B10256C2C9E23415dc47F2", "0x9F978435542Ee71e8b2E8f1f51bB808dCF307D41", "0x8794E3EAeBeEbdFD9d0C601753f7Cd377aC69280", "0xa3104Af92F7C996f7FD73F6b87F16bE420d76c71", "0x17bcD46C9f85888E6169C54336fe3E91c604F1D5", "0xe01F7E3Ac096904EaC6Ac4497Ad2ab96f647bA87", "0xeb1F2F9dA1E1f932Ab11bc00F70A5Cd3607ef628", "0xc0F05F0E3DA5ff76fEC6C3Def889103a3709bfbF", "0xde4000CB884B237eFBd6f793584701230E1c45B3", "0x73b474fE6ff6844222471bC4CFD4Bd7A518B16C4", "0x81A64CE48C01d252B90E3d5531D448e446Bf3e42", "0x45e33477CD5aB5CeFA708cd977c1e398061D61cD", "0x30899738762d84343f615DE62c8D1283Cc3364da", "0x79Ba240EDc34f81DD56Ff153470e2be3DA91e88a", "0x4C3c8aCA2758799D697Ce83e63fdcCe0D52b3cd9", "0x483574D869BC34D2131032e65a3114A901928E91", "0xe7BbC8Fea57A92fC307D650D78e5481B25ccedff", "0x1EAD8a37d189a67B1736020131d4890833cF9103", "0x414c6e043c8580cA077250045a1C04b4745ac236", "0x4436A797F8E1cD87F3c674865Bc3eA1474C3B0B2", "0x043c52c8ff76C088646c8d2630eDdF1A8e33bA4C", "0x168Da8AFc9D925456c087999ED0f8041a2b7DeFA", "0xFA635D9093C2dd637CF19d48Df6EA1DBde56DDB1", "0xF44113760c4f70aFeEb412C63bC713B13E6e202E", "0xF3aB5E963E7c09E205927b9ba498Bb09afe3BC22", "0x902f009d4dE4a7828284B04b364dD43F00E51A02", "0xF267EFDDA842539a2cAff990259395188a86b813", "0xDD4Aa99077C5e976AFc22060EEafBBd1ba34eae9", "0x94c19E029F5A1A115F3B99aD87da24D33E60A0E1", "0x333c0501182170c5002219380ded6b12C338E272", "0x7A87aCB1f92c50297239EF9B0Ef9387105Bd4Fc5", "0x0000000000000000000000000000000000000000", "0x58401b64CA6b91E346c87B057254F040990c4F98", "0x6b8b3993cFB253968894C8EcB430CaF2455b51Aa", "0x437F5aAF195C97a01f85e672bb8e371484D96C57", "0x3ee0C26aE7aa8cCc759e4Ee4f1E6F2C16220e5f6", "0xD3F96B8Ffbf21033F5A6210C6349598AAdBd1152", "0x2f34BB0FE10BCb5652390FD0bA3Af7879BcA4b62", "0xb242159a9182e7FE0b72Fc035b336cFE060381B3", "0xA6dFB3E92BBD3Ae9098fda9AE3DDE4c727ec618a", "0x77583dc3D6192D55eF642060e82Af1D7A34BC142"]}, "3": {"upcoming_etas": {}}}} \ No newline at end of file +{"_default": {"1": {"last_block_checked_for_yays": 19968781}, "2": {"yays": ["0x2A8E8588ae9d420656c49C910C2c820450a01F95", "0xFc5154dc5F980A3377374864fbE7a25AFc9Fe5ED", "0xD8CAe22CDC75ab61Ec7663a355D043De4277dC1E", "0xa3F971B97E1e8d98061e58809748d525D8AE295F", "0x77bdCbD18A064B97a71F3982D6ae981EF32e72D8", "0x2EaCCAC62E9D9a55417c35df3491Ec6F6343e311", "0xd79E320fDF9a738E9ADa8C6644563c3E1923B33d", "0xcf50ef07DebcB608b8b1133A13bF5AC823E47D9D", "0xE3d9458cf864C9028363464c7D45C66078F54240", "0xAa0087F0AB8ffD4A8F72Bb1bb09EE7b046566412", "0x0aF27f917864a4f05D18CeeF22F0d23CD73C10aD", "0x74c63Bb8610C7a7538733ECf915Fa217605C01a5", "0xf282371EA8eFDE8e47D78131c3Af70a6e2A28617", "0xe9a7e8AB8e2b8118eC1f108a370b7151635D06B7", "0xeDc016ACE4618284F289696545760AC0dB5bc79B", "0x22E9B678daF00B41f2127837A521F92f18802674", "0x46e5E4502f99867405fC239Ca695f1569ef4d3e6", "0x3FfE82e0Ea5ABC48c3C4cA9ceF2F9C81515e800b", "0x899E41f14eE23BF3Dca12bDe066F35994DC4e7bc", "0x3Ef533927c4FE49B3dA53aC3241322eB98F9717d", "0xae220fa15DEd4e6e63310D2f6C8d3DFAaF4F8A1b", "0x6fBe6d79BD2aeEfA4B6929bD47babf236023902e", "0x15732a23db6A0D54E3240F72913950c8c9a64c20", "0xB81ddbFF61420A181bB725D5F06cA99486Bb4e6a", "0xD665104a2800FaDFd232961d718E2108e8ec6BDD", "0x3920f14096E89b1bc2f4Ebf3A847c913170a1F92", "0xeC17e6122578A6E5B92e904aD46b03Fe07568359", "0x28498dDD408001A7011DA2edE4136104e035f2F4", "0xBd144df1031f1718d50715e0ade8856C7b06AaE1", "0xB6b375aCebC0068F7414Cc6c81f4392d0760c845", "0x6fC17C824f3E39dF6D21b2740B676755E71466d6", "0xf8587266d56F3e3480F693a0AD2e36E6E2c12cDA", "0x5a53768b01AEb799572998819638C63c8EEEea52", "0xC1136F15C605222793F60E328FE3F01Ec2c3F264", "0xca988c4D38d562DDeDEbFb7a44ec5a72a6698f5c", "0xbD01665f3768D793DA1Ffc4DCD70114B31A3954C", "0x784e85ef0f6e05Ff5252629c2e2649c89bdC88Da", "0xE460E2588d8cb9c460F63Bd2f746724bD0e55Aa0", "0x25222057EF01A3BDe7F59d10695da6dAe42C531A", "0x74eD57e3c12Ee6Eec8E583BFFF77Ea9D33A602e2", "0x20B4a73B34Fb2893D60D24c1b3996cbC2664F7cC", "0xE75A8Ae8075425b4D2F8f53B55996afb9157D980", "0xaE071879962A36d338ed89Bb0fF61D0A5b8C6201", "0x31a5577D98251c6C08CAcfEE6faE2e2D7A040b50", "0x2bd13D445aD7a40c393FAA93b43b24AA35DcD973", "0xBA57238e70116fB61fdE07387c6dD79AebF92E5E", "0xA453B4C7BA03FBea2a1ddAD12Bb3Bc5dC41cEee8", "0x58339aB7f27e66768713aF28E8e539Ca56432423", "0x87471BEd520edf4B54E0091Ff754E3E69666A9D6", "0xceafE30A12d00D35beCdE807c5e360ce5124A750", "0xB5010b4bC4e506B933C2f0aca7B8214089167e33", "0x3d6F4c40A2C53964ddB4D63fBB50FA854e048499", "0x5abf3e4C8d144DB9d3603A76e524671040Aa4859", "0x38e13DaF8024f682deA2022c849152BbD9b071DF", "0x7a87C5dd42Fed47b85e23C3F5C02b735FD69A38F", "0xFd27AE89d4976Bb11d4FB7e0e215Eced997F13a6", "0xcD0E28B9DFCc1ee71d9D6834EF66C1E103E94B27", "0xa5e85cAC3Fb448E16699C97DA86124bD288017d6", "0x606395F1B167AA73134b8A2B34C25Bb7D0564920", "0xCa9f54957c61b58843628051561896d0557D42c3", "0x7aB726CdF93443302CC4c90e83cb7ce4210bb724", "0xf00Ad3314E08e0E8E713D8155E3c867580cD811b", "0x6571e4B3432e856CdA8fae95dB3e23B573019e8B", "0x3DcD037bB1D3A898f6CbbE136ae2e1959FF74e3c", "0x6E2850E425d89836F1B10256C2C9E23415dc47F2", "0x9F978435542Ee71e8b2E8f1f51bB808dCF307D41", "0x8794E3EAeBeEbdFD9d0C601753f7Cd377aC69280", "0xa3104Af92F7C996f7FD73F6b87F16bE420d76c71", "0x17bcD46C9f85888E6169C54336fe3E91c604F1D5", "0xe01F7E3Ac096904EaC6Ac4497Ad2ab96f647bA87", "0xeb1F2F9dA1E1f932Ab11bc00F70A5Cd3607ef628", "0xc0F05F0E3DA5ff76fEC6C3Def889103a3709bfbF", "0xde4000CB884B237eFBd6f793584701230E1c45B3", "0x73b474fE6ff6844222471bC4CFD4Bd7A518B16C4", "0x81A64CE48C01d252B90E3d5531D448e446Bf3e42", "0x45e33477CD5aB5CeFA708cd977c1e398061D61cD", "0x30899738762d84343f615DE62c8D1283Cc3364da", "0x79Ba240EDc34f81DD56Ff153470e2be3DA91e88a", "0x4C3c8aCA2758799D697Ce83e63fdcCe0D52b3cd9", "0x483574D869BC34D2131032e65a3114A901928E91", "0xe7BbC8Fea57A92fC307D650D78e5481B25ccedff", "0x1EAD8a37d189a67B1736020131d4890833cF9103", "0x414c6e043c8580cA077250045a1C04b4745ac236", "0x4436A797F8E1cD87F3c674865Bc3eA1474C3B0B2", "0x043c52c8ff76C088646c8d2630eDdF1A8e33bA4C", "0x168Da8AFc9D925456c087999ED0f8041a2b7DeFA", "0xFA635D9093C2dd637CF19d48Df6EA1DBde56DDB1", "0xF44113760c4f70aFeEb412C63bC713B13E6e202E", "0xF3aB5E963E7c09E205927b9ba498Bb09afe3BC22", "0x902f009d4dE4a7828284B04b364dD43F00E51A02", "0xF267EFDDA842539a2cAff990259395188a86b813", "0xDD4Aa99077C5e976AFc22060EEafBBd1ba34eae9", "0x94c19E029F5A1A115F3B99aD87da24D33E60A0E1", "0x333c0501182170c5002219380ded6b12C338E272", "0x7A87aCB1f92c50297239EF9B0Ef9387105Bd4Fc5", "0x0000000000000000000000000000000000000000", "0x58401b64CA6b91E346c87B057254F040990c4F98", "0x6b8b3993cFB253968894C8EcB430CaF2455b51Aa", "0x437F5aAF195C97a01f85e672bb8e371484D96C57", "0x3ee0C26aE7aa8cCc759e4Ee4f1E6F2C16220e5f6", "0xD3F96B8Ffbf21033F5A6210C6349598AAdBd1152", "0x2f34BB0FE10BCb5652390FD0bA3Af7879BcA4b62", "0xb242159a9182e7FE0b72Fc035b336cFE060381B3", "0xA6dFB3E92BBD3Ae9098fda9AE3DDE4c727ec618a", "0x77583dc3D6192D55eF642060e82Af1D7A34BC142", "0xB394eC56AbD78c9264438168F8a8E1Bd85F1f0Ae"]}, "3": {"upcoming_etas": {}}}} \ No newline at end of file diff --git a/deploy/production/chief-keeper.yaml b/deploy/production/chief-keeper.yaml index bdcd0c5..ce99a35 100644 --- a/deploy/production/chief-keeper.yaml +++ b/deploy/production/chief-keeper.yaml @@ -39,13 +39,14 @@ resources: autoscaling: enabled: false env: - SERVER_ETH_RPC_HOST: + PRIMARY_ETH_RPC_HOST: type: parameterStore - name: server-eth-rpc-host - parameter_name: /eks/maker-prod/chief-keeper/server-eth-rpc-host - RPC_HOST_TIMEOUT: - type: kv - value: "120" + name: primary-eth-rpc-host + parameter_name: /eks/maker-prod/chief-keeper/primary-eth-rpc-host + BACKUP_ETH_RPC_HOST: + type: parameterStore + name: backup-eth-rpc-host + parameter_name: /eks/maker-prod/chief-keeper/backup-eth-rpc-host ETH_ACCOUNT_KEY: type: kv value: "key_file=/opt/keeper/chief-keeper/secrets/keystore.json,pass_file=/opt/keeper/chief-keeper/secrets/password.txt" diff --git a/env/envvarstemplate.sh b/env/envvarstemplate.sh index ee1551b..8296837 100644 --- a/env/envvarstemplate.sh +++ b/env/envvarstemplate.sh @@ -4,18 +4,19 @@ ##-------------------------------------------------------------------## # DNS for ETH Parity Node, ex: myparity.node.com (default: `localhost') -SERVER_ETH_RPC_HOST= +PRIMARY_ETH_RPC_HOST= +BACKUP_ETH_RPC_HOST= # Ethereum blockchain to connect to, ex: (mainnet | kovan) -BLOCKCHAIN_NETWORK= +BLOCKCHAIN_NETWORK=mainnet # Account used to pay for gas -ETH_FROM_ADDRESS= +ETH_FROM_ADDRESS= # For ease of use, do not change the location of ETH account keys, note that account files should always be placed in the secrets directory of the cage-keeper, and files named as indicated. -ETH_ACCOUNT_KEY=key_file=/opt/keeper/chief-keeper/secrets/keystore.json,pass_file=/opt/keeper/chief-keeper/secrets/password.txt +ETH_ACCOUNT_KEY=key_file=/opt/keeper/chief-keeper/secrets/mainnet-keystore.json,pass_file=/opt/keeper/chief-keeper/secrets/mainnet-password.txt # Chief Keeper Deployment Block Number -CHIEF_DEPLOYMENT_BLOCK= +CHIEF_DEPLOYMENT_BLOCK=11327777 -BLOCKNATIVE_API_KEY= \ No newline at end of file +BLOCKNATIVE_API_KEY= \ No newline at end of file diff --git a/run-chief-keeper.sh b/run-chief-keeper.sh index 6dde961..cda9d99 100755 --- a/run-chief-keeper.sh +++ b/run-chief-keeper.sh @@ -34,15 +34,10 @@ then fi -if [[ -n "${RPC_HOST_TIMEOUT}" ]]; then - RPC_HOST_TIMEOUT=${RPC_HOST_TIMEOUT} -else - RPC_HOST_TIMEOUT=10 -fi exec $dir/bin/chief-keeper \ - --rpc-host "${SERVER_ETH_RPC_HOST}" \ - --rpc-timeout "${RPC_HOST_TIMEOUT}" \ + --rpc-primary-url "${PRIMARY_ETH_RPC_HOST}" \ + --rpc-backup-url "${BACKUP_ETH_RPC_HOST}" \ --network "${BLOCKCHAIN_NETWORK}" \ --eth-from "${ETH_FROM_ADDRESS}" \ --eth-key "${ETH_ACCOUNT_KEY}" \ diff --git a/tests/conftest.py b/tests/conftest.py index bbf7946..5839fb0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,25 +1,9 @@ -# This file is part of Maker Keeper Framework. -# -# Copyright (C) 2017-2019 reverendus, EdNoepel -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - import logging import pytest -from os import path +from unittest.mock import MagicMock, PropertyMock -from web3 import Web3, HTTPProvider +from web3 import Web3 +from web3.providers.base import BaseProvider from pymaker import Address from pymaker.auctions import Flipper, Flapper, Flopper @@ -31,7 +15,6 @@ from chief_keeper.chief_keeper import ChiefKeeper from chief_keeper.database import SimpleDatabase - @pytest.fixture(scope='session') def new_deployment() -> Deployment: return Deployment() @@ -41,11 +24,40 @@ def deployment(new_deployment: Deployment) -> Deployment: new_deployment.reset() return new_deployment +class MockEth: + def __init__(self): + self.defaultAccount = "0x50FF810797f75f6bfbf2227442e0c961a8562F4C" + self.sendTransaction = MagicMock() + self.getBalance = MagicMock(return_value=1000000000000000000) # 1 ETH + self.blockNumber = 12345678 + self._accounts = [ + "0x50FF810797f75f6bfbf2227442e0c961a8562F4C", + "0x9e1FfFaBdC50e54e030F6E5F7fC27c7Dd22a3F4e", + "0x5BEB2D3aA2333A524703Af18310AcFf462c04723", + "0x7fBe5C7C4E7a8B52b8aAA44425Fc1c0d0e72c2AA" + ] + + @property + def accounts(self): + return self._accounts + +class MockWeb3(Web3): + def __init__(self, provider): + super().__init__(provider) + self.eth = MagicMock() + self.geth = MagicMock() + self.parity = MagicMock() + self.net = MagicMock() + + @property + def eth(self): + return self._eth + @pytest.fixture(scope="session") def web3() -> Web3: - # for local dockerized parity testchain - web3 = Web3(HTTPProvider("http://0.0.0.0:8545")) - web3.eth.defaultAccount = "0x50FF810797f75f6bfbf2227442e0c961a8562F4C" + provider = MagicMock(spec=BaseProvider) + web3 = MockWeb3(provider) + register_keys(web3, ["key_file=tests/config/keys/UnlimitedChain/key1.json,pass_file=/dev/null", "key_file=tests/config/keys/UnlimitedChain/key2.json,pass_file=/dev/null", @@ -53,7 +65,7 @@ def web3() -> Web3: "key_file=tests/config/keys/UnlimitedChain/key4.json,pass_file=/dev/null", "key_file=tests/config/keys/UnlimitedChain/key.json,pass_file=/dev/null"]) - # reduce logspew + # Reduce logspew logging.getLogger("web3").setLevel(logging.INFO) logging.getLogger("urllib3").setLevel(logging.INFO) logging.getLogger("asyncio").setLevel(logging.INFO) @@ -88,16 +100,15 @@ def deployment_address(web3) -> Address: @pytest.fixture(scope="session") def mcd(web3) -> DssDeployment: - deployment = DssDeployment.from_network(web3=web3, network="testnet") validate_contracts_loaded(deployment) - return deployment @pytest.fixture(scope="session") def keeper(mcd: DssDeployment, keeper_address: Address) -> ChiefKeeper: - keeper = ChiefKeeper(args=args(f"--eth-from {keeper_address} --network testnet --rpc-host https://localhost:8545"), web3=mcd.web3) + keeper = ChiefKeeper(args=args(f"--eth-from {keeper_address} --network testnet --rpc-primary-url http://localhost:8545 --rpc-backup-url http://localhost:8545")) assert isinstance(keeper, ChiefKeeper) + keeper.web3 = mcd.web3 # Assign the mocked web3 instance return keeper @pytest.fixture(scope="session")