Beispiel #1
0
class TestValidateWallet(unittest.TestCase):

    def setUp(self):
        self.testnet_api = BtcTxStore(dryrun=True, testnet=True)
        self.mainnet_api = BtcTxStore(dryrun=True, testnet=False)
        self.testnet_wallet = self.testnet_api.create_wallet()
        self.mainnet_wallet = self.mainnet_api.create_wallet()
        self.testnet_key = self.testnet_api.get_key(self.testnet_wallet)
        self.mainnet_key = self.mainnet_api.get_key(self.mainnet_wallet)

    def test_checks_networks(self):
        self.assertTrue(self.testnet_api.validate_wallet(self.testnet_wallet))
        self.assertTrue(self.mainnet_api.validate_wallet(self.mainnet_wallet))
        self.assertFalse(self.testnet_api.validate_wallet(self.mainnet_wallet))
        self.assertFalse(self.mainnet_api.validate_wallet(self.testnet_wallet))

    def test_doesnt_validate_keys(self):
        self.assertFalse(self.testnet_api.validate_wallet(self.testnet_key))
        self.assertFalse(self.mainnet_api.validate_wallet(self.testnet_key))
        self.assertFalse(self.testnet_api.validate_wallet(self.mainnet_key))
        self.assertFalse(self.mainnet_api.validate_wallet(self.mainnet_key))

    def test_correct_types(self):
        self.assertTrue(self.testnet_api.validate_wallet(S_HWIF))
        self.assertTrue(self.testnet_api.validate_wallet(B_HWIF))
        self.assertTrue(self.testnet_api.validate_wallet(U_HWIF))
Beispiel #2
0
class AppAuthenticationHeadersTest(unittest.TestCase):

    def setUp(self):
        app.config["SKIP_AUTHENTICATION"] = False  # monkey patch
        self.app = app.test_client()
        
        self.btctxstore = BtcTxStore()
        
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_success(self):

        # create header date and authorization signature
        wif = self.btctxstore.create_key()
        btc_addr = self.btctxstore.get_address(wif)
        header_date = formatdate(timeval=mktime(datetime.now().timetuple()),
                                 localtime=True, usegmt=True)
        message = app.config["ADDRESS"] + " " + header_date
        header_authorization = self.btctxstore.sign_unicode(wif, message)
        headers = {"Date": header_date, "Authorization": header_authorization}
        url = '/api/register/{0}'.format(btc_addr)
        rv = self.app.get(url, headers=headers)
        data = json.loads(rv.data.decode("utf-8"))
        self.assertEqual(btc_addr, data["btc_addr"])
        self.assertEqual(rv.status_code, 200)

    def test_fail(self):
        # register without auth headres fails
        btc_addr = self.btctxstore.get_address(self.btctxstore.get_key(self.btctxstore.create_wallet()))
        rv = self.app.get('/api/register/{0}'.format(btc_addr))
        self.assertEqual(rv.status_code, 401)

        # register first because ping is lazy
        wif = self.btctxstore.get_key(self.btctxstore.create_wallet())
        btc_addr = self.btctxstore.get_address(wif)
        header_date = formatdate(timeval=mktime(datetime.now().timetuple()),
                                 localtime=True, usegmt=True)
        message = app.config["ADDRESS"] + " " + header_date
        header_authorization = self.btctxstore.sign_unicode(wif, message)
        headers = {"Date": header_date, "Authorization": header_authorization}
        url = '/api/register/{0}'.format(btc_addr)
        rv = self.app.get(url, headers=headers)
        self.assertEqual(rv.status_code, 200)

        # ping without auth headres fails
        time.sleep(app.config["MAX_PING"])
        rv = self.app.get('/api/ping/{0}'.format(btc_addr))
        self.assertEqual(rv.status_code, 401)

        # set height without auth headres fails
        btc_addr = self.btctxstore.get_address(self.btctxstore.get_key(self.btctxstore.create_wallet()))
        rv = self.app.get('/api/height/{0}/10'.format(btc_addr))
        self.assertEqual(rv.status_code, 401)
Beispiel #3
0
class TestGetWalletKey(unittest.TestCase):
    def setUp(self):
        self.api = BtcTxStore(dryrun=True, testnet=True)

    def test_standard(self):
        hwif = self.api.create_wallet()
        wif = self.api.get_key(hwif)
        self.assertTrue(validate.is_wif_valid(wif, allowable_netcodes=['XTN']))

    def test_input_validation(self):

        # test correct types
        a = self.api.get_key(S_HWIF)
        b = self.api.get_key(B_HWIF)
        c = self.api.get_key(U_HWIF)
        self.assertEqual(a, b, c)

        # TODO invalid types
        # TODO invalid input data

    def test_standards_compliant(self):
        pass  # FIXME check generated against expected output from 3rd parties
Beispiel #4
0
class TestGetWalletKey(unittest.TestCase):

    def setUp(self):
        self.api = BtcTxStore(dryrun=True, testnet=True)

    def test_standard(self):
        hwif = self.api.create_wallet()
        wif = self.api.get_key(hwif)
        self.assertTrue(validate.is_wif_valid(wif, allowable_netcodes=['XTN']))

    def test_input_validation(self):
        
        # test correct types
        a = self.api.get_key(S_HWIF)
        b = self.api.get_key(B_HWIF)
        c = self.api.get_key(U_HWIF)
        self.assertEqual(a, b, c)

        # TODO invalid types
        # TODO invalid input data

    def test_standards_compliant(self):
        pass # FIXME check generated against expected output from 3rd parties
Beispiel #5
0
class Client(object):
    def __init__(self,
                 url=common.DEFAULT_URL,
                 debug=False,
                 max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        self.url = url
        self.messanger = None  # lazy
        self.debug = debug
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)
        self.max_size = deserialize.byte_count(max_size)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        self._ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        self._ensure_path_exists(self.store_path)

        self.cfg = config.get(self.btctxstore, self.cfg_path)

    def _ensure_path_exists(self, path):
        if not os.path.exists(path):
            os.makedirs(path)

    def _init_messanger(self):
        if self.messanger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messanger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def version(self):
        print(__version__)
        return __version__

    def register(self):
        """Attempt to register the config address."""
        self._init_messanger()
        registered = self.messanger.register(self.cfg["payout_address"])
        auth_addr = self.messanger.auth_address()
        if registered:
            print("Address {0} now registered on {1}.".format(
                auth_addr, self.url))
        else:
            print("Failed to register address {0} on {1}.".format(
                auth_addr, self.url))
        return True

    def config(self, set_wallet=None, set_payout_address=None):
        """Display saved config."""
        config_updated = False

        # update payout address if requested
        if set_payout_address:
            self.cfg["payout_address"] = set_payout_address
            config_updated = True
            # FIXME update dataserv here

        # update wallet if requested
        if set_wallet:
            self.cfg["wallet"] = set_wallet
            config_updated = True

        if config_updated:  # save config if updated
            config.save(self.btctxstore, self.cfg_path, self.cfg)

        print(json.dumps(self.cfg, indent=2))
        return self.cfg

    def ping(self):
        """Attempt keep-alive with the server."""
        self._init_messanger()
        print("Pinging {0} with address {1}.".format(
            self.messanger.server_url(), self.messanger.auth_address()))
        self.messanger.ping()
        return True

    def poll(self,
             register_address=False,
             delay=common.DEFAULT_DELAY,
             limit=None):
        """TODO doc string"""
        stop_time = _now() + timedelta(seconds=int(limit)) if limit else None

        if register_address:
            self.register()

        while True:
            self.ping()

            if stop_time and _now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self,
              cleanup=False,
              rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """TODO doc string"""

        self._init_messanger()

        def _on_generate_shard(height, seed, file_hash):
            first = height == 1
            set_height = (height % int(set_height_interval)) == 0
            last = (int(self.max_size / common.SHARD_SIZE) + 1) == height
            if first or set_height or last:
                self.messanger.height(height)

        bldr = builder.Builder(self.cfg["payout_address"],
                               common.SHARD_SIZE,
                               self.max_size,
                               debug=self.debug,
                               on_generate_shard=_on_generate_shard)
        generated = bldr.build(self.store_path,
                               cleanup=cleanup,
                               rebuild=rebuild)
        height = len(generated)
        self.messanger.height(height)
        return generated
Beispiel #6
0
class FarmerUpTime(unittest.TestCase):
    def setUp(self):
        app.config["SKIP_AUTHENTICATION"] = True  # monkey patch
        app.config["DISABLE_CACHING"] = True

        self.btctxstore = BtcTxStore()

        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_register(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 100
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

        # reg_time = max online time -> 100% uptime
        delta = timedelta(minutes=app.config["ONLINE_TIME"])
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": delta.seconds,
            "uptime": 100
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

        # reg_time = 2 * max online time -> 50% uptime
        delta = timedelta(minutes=(2 * app.config["ONLINE_TIME"]))
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.ping()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 50
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_ping_100(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # lastest ping for 100%
        delta = timedelta(minutes=app.config["ONLINE_TIME"])
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.ping()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 100
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_ping_50(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # lastest ping for 100%
        delta = timedelta(minutes=(2 * app.config["ONLINE_TIME"]))
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.ping()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 50
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_ping_25(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # ping to late -> 50%
        delta = timedelta(minutes=(4 * app.config["ONLINE_TIME"]))
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.ping()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 25
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_ping_days(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # ping to late -> 50%
        delta = timedelta(days=2)
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.uptime = 86401  # 1 / 2 days farmer was online
        farmer.ping()

        test_json = {
            "height": 0,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 50
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_height_100(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # lastest ping for 100%
        delta = timedelta(minutes=app.config["ONLINE_TIME"])
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.set_height(100)

        test_json = {
            "height": 100,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 100
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_height_50(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # lastest ping for 100%
        delta = timedelta(minutes=(2 * app.config["ONLINE_TIME"]))
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.set_height(50)

        test_json = {
            "height": 50,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 50
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)

    def test_height_25(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        #ping to late -> 50%
        delta = timedelta(minutes=(4 * app.config["ONLINE_TIME"]))
        farmer.last_seen = datetime.utcnow() - delta
        farmer.reg_time = datetime.utcnow() - delta
        farmer.set_height(25)

        test_json = {
            "height": 25,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 25
        }
        call_payload = json.loads(farmer.to_json())
        call_payload["uptime"] = round(call_payload["uptime"])
        self.assertEqual(test_json, call_payload)
Beispiel #7
0
class FarmerTest(unittest.TestCase):
    def setUp(self):
        app.config["SKIP_AUTHENTICATION"] = True  # monkey patch
        app.config["DISABLE_CACHING"] = True

        self.btctxstore = BtcTxStore()
        self.bad_addr = 'notvalidaddress'

        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_repr(self):
        farmer = Farmer('191GVvAaTRxLmz3rW3nU5jAV1rF186VxQc')
        ans = "<Farmer BTC Address: '191GVvAaTRxLmz3rW3nU5jAV1rF186VxQc'>"
        self.assertEqual(repr(farmer), ans)

    def test_sha256(self):
        an = 'c059c8035bbd74aa81f4c787c39390b57b974ec9af25a7248c46a3ebfe0f9dc8'
        self.assertEqual(sha256("storj"), an)
        self.assertNotEqual(sha256("not storj"), an)

    def test_register(self):
        # test success
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer1 = Farmer(btc_addr)
        self.assertFalse(farmer1.exists())
        farmer1.register()
        self.assertTrue(farmer1.exists())

        # test duplicate error
        self.assertRaises(LookupError, farmer1.register)

        def callback_a():
            Farmer(self.bad_addr)

        self.assertRaises(ValueError, callback_a)

    def test_ping(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)

        # test ping before registration
        self.assertRaises(LookupError, farmer.ping)

        # register farmer
        farmer.register()

        # get register time, and make sure the ping work
        register_time = farmer.last_seen
        # ping faster than max_ping would be ignored
        time.sleep(app.config["MAX_PING"] + 1)
        farmer.ping()  # update last seen
        ping_time = farmer.last_seen
        self.assertTrue(register_time < ping_time)

    def test_ping_time_limit(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        register_time = farmer.last_seen
        time.sleep(2)
        farmer.ping()

        # should still be around 0
        delta_seconds = int((farmer.last_seen - register_time).seconds)
        self.assertEqual(delta_seconds, 0)

    def test_height(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        # set height and check function output
        self.assertEqual(farmer.height, 0)
        self.assertEqual(farmer.set_height(5), 5)
        self.assertEqual(farmer.height, 5)

        # check the db object as well
        farmer2 = farmer.lookup()
        self.assertEqual(farmer2.height, 5)

    def test_audit(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)

        # test audit before registration
        self.assertRaises(LookupError, farmer.audit)

        # register farmer
        farmer.register()

        # get register time, and make sure the ping work
        register_time = farmer.last_seen
        # ping faster than max_ping would be ignored
        time.sleep(app.config["MAX_PING"] + 1)
        farmer.audit()
        ping_time = farmer.last_seen
        self.assertTrue(register_time < ping_time)

    def test_to_json(self):
        btc_addr = self.btctxstore.get_address(
            self.btctxstore.get_key(self.btctxstore.create_wallet()))
        farmer = Farmer(btc_addr)
        farmer.register()

        farmer.ping()
        farmer.set_height(50)

        test_json = {
            "height": 50,
            "btc_addr": btc_addr,
            'payout_addr': btc_addr,
            "last_seen": 0,
            "uptime": 100
        }
        call_payload = json.loads(farmer.to_json())
        self.assertEqual(test_json, call_payload)
Beispiel #8
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False, quiet=False,
                 use_folder_tree=False, max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        debug = deserialize.flag(debug)
        quiet = deserialize.flag(quiet)

        self.url = deserialize.url(url)
        self.use_folder_tree = deserialize.flag(use_folder_tree)
        self.max_size = deserialize.byte_count(max_size)

        self.messenger = None  # lazy
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        control.util.ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        control.util.ensure_path_exists(self.store_path)

        # check for vfat partions
        fstype = control.util.get_fs_type(self.store_path)
        if fstype == "vfat":
            logger.info("Detected vfat partition, using folder tree.")
            self.use_folder_tree = True
        if fstype is None:
            msg = "Couldn't detected partition type for '{0}'"
            logger.warning(msg.format(self.store_path))

        self.cfg = control.config.get(self.btctxstore, self.cfg_path)

    @staticmethod
    def version():
        print(__version__)
        return __version__

    def _init_messenger(self):
        """Make sure messenger exists."""
        if self.messenger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messenger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def register(self):
        """Attempt to register the config address."""
        self._init_messenger()
        payout_address = self.cfg["payout_address"]
        self.messenger.register(payout_address)
        logger.info("Registered on server '{0}'.".format(self.url))
        return True

    def config(self, set_wallet=None, set_payout_address=None):
        """
        Set and then show the config settings.

        :param set_wallet: Set the HWIF for registration/auth address.
        :param set_payout_address:  Set the payout address.
        :return: Configuation object.
        """
        if((set_payout_address is not None) and
                (not self.btctxstore.validate_address(set_payout_address))):
            raise exceptions.InvalidAddress(set_payout_address)
        if((set_wallet is not None) and
                (not self.btctxstore.validate_wallet(set_wallet))):
            raise exceptions.InvalidHWIF(set_wallet)

        self._init_messenger()
        config_updated = False

        # update payout address if requested
        if set_payout_address:
            self.cfg["payout_address"] = set_payout_address
            config_updated = True

        # update wallet if requested
        if set_wallet:
            self.cfg["wallet"] = set_wallet
            config_updated = True

        # save config if updated
        if config_updated:
            control.config.save(self.btctxstore, self.cfg_path, self.cfg)

        # display config
        print(SHOW_CONFIG_TEMPLATE.format(
            self.messenger.auth_address(),
            self.cfg["payout_address"]
        ))
        return self.cfg

    def ping(self):
        """Attempt one keep-alive with the server."""
        self._init_messenger()

        msg = "Pinging server '{0}' at {1:%Y-%m-%d %H:%M:%S}."
        logger.info(msg.format(self.messenger.server_url(), datetime.now()))
        self.messenger.ping()

        return True

    def poll(self, delay=common.DEFAULT_DELAY, limit=None):
        """Attempt continuous keep-alive with the server.

        :param delay: Delay in seconds per ping of the server.
        :param limit: Number of seconds in the future to stop polling.
        :return: True, if limit is reached. None, if otherwise.
        """
        delay = deserialize.positive_integer(delay)
        stop_time = None
        if limit is not None:
            stop_time = datetime.now() + timedelta(seconds=int(limit))

        while True:  # ping the server every X seconds
            self.ping()

            if stop_time and datetime.now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self, cleanup=False, rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """Generate test files deterministically based on address.

        :param cleanup: Remove files in shard directory.
        :param rebuild: Re-generate any file shards.
        :param set_height_interval: Number of shards to generate before
                                    notifying the server.
        """
        set_height_interval = deserialize.positive_nonzero_integer(
            set_height_interval
        )
        cleanup = deserialize.flag(cleanup)
        rebuild = deserialize.flag(rebuild)

        self._init_messenger()
        logger.info("Starting build")

        def _on_generate_shard(cur_height, cur_seed, cur_file_hash):
            """
            Because URL requests are slow, only update the server when we are
            at the first height, at some height_interval, or the last height.

            :param cur_height: Current height in the building process.
            """
            first = cur_height == 1
            set_height = (cur_height % int(set_height_interval)) == 0
            last = int(self.max_size / common.SHARD_SIZE) == cur_height

            if first or set_height or last:
                self.messenger.height(cur_height)
                logger.info("Current height at {0}.".format(cur_height))

        # Initialize builder and generate/re-generate shards
        bldr = builder.Builder(self.cfg["payout_address"],
                               common.SHARD_SIZE, self.max_size,
                               on_generate_shard=_on_generate_shard,
                               use_folder_tree=self.use_folder_tree)
        generated = bldr.build(self.store_path, cleanup=cleanup,
                               rebuild=rebuild)

        logger.info("Build finished")
        return generated

    def farm(self, cleanup=False, rebuild=False, set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL, delay=common.DEFAULT_DELAY, limit=None):
        """ Fully automatic client for users wishing a simple turnkey solution.
        This will run all functions automatically with the most sane defaults
        and as little user interface as possible.
        """
        
        set_height_interval = deserialize.positive_nonzero_integer(
            set_height_interval
        )
        cleanup = deserialize.flag(cleanup)
        rebuild = deserialize.flag(rebuild)

        # farmer never gives up
        self._init_messenger()
        self.messenger.retry_limit = 99999999999999999999999999999999999999

        try:
            self.register()
        except exceptions.AddressAlreadyRegistered:
            pass  # already registered ...
        self.build(cleanup=cleanup, rebuild=rebuild, set_height_interval=set_height_interval)
        self.poll(delay=delay, limit=limit)
        return True
Beispiel #9
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False,
                 max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 set_master_secret=None, set_payout_address=None,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        # FIXME validate master_secret

        self.url = url
        self.config = None  # lazy
        self.messanger = None  # lazy
        self.debug = debug  # TODO validate
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)
        self.max_size = deserialize.byte_count(max_size)

        # paths
        self.config_path = os.path.realpath(config_path)  # TODO validate
        self._ensure_path_exists(os.path.dirname(self.config_path))
        self.store_path = os.path.realpath(store_path)
        self._ensure_path_exists(self.store_path)

        # validate payout address
        self.btctxstore = BtcTxStore()
        if set_payout_address and (not self.btctxstore.validate_address(set_payout_address)):
            raise exceptions.InvalidAddress(set_payout_address)

        self._initialize_config(set_master_secret, set_payout_address)

    def _initialize_config(self, set_master_secret, set_payout_address):
        if os.path.exists(self.config_path):
            self._load_config()
        else:  # initialize config
            if not set_master_secret:  # create random master secret
                secret_data = base64.b64encode(os.urandom(256))
                set_master_secret = secret_data.decode('utf-8')
            if not set_payout_address:  # use root address if not given
                set_payout_address = self._get_root_address(set_master_secret)

        if set_master_secret or set_payout_address:
            self.config = {
                "version": __version__,
                "master_secret": set_master_secret,
                "payout_address": set_payout_address,
            }
            config.save(self.config_path, self.config)

    def _ensure_path_exists(self, path):
        if not os.path.exists(path):
            os.makedirs(path)

    def _load_config(self):
        if self.config is None:
            if not os.path.exists(self.config_path):
                raise exceptions.ConfigNotFound(self.config_path)
            self.config = config.load(self.config_path)

    def _get_root_wif(self, master_secret):
        master_secret = base64.b64decode(master_secret)
        hwif = self.btctxstore.create_wallet(master_secret=master_secret)
        return self.btctxstore.get_key(hwif)

    def _get_root_address(self, master_secret):
            wif = self._get_root_wif(master_secret)
            return self.btctxstore.get_address(wif)

    def _init_messanger(self):
        if self.messanger is None:
            wif = self._get_root_wif(self.config["master_secret"])
            self.messanger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def version(self):
        print(__version__)
        return __version__

    def register(self):
        """Attempt to register the config address."""
        self._init_messanger()
        registered = self.messanger.register(self.config["payout_address"])
        auth_addr = self.messanger.auth_address()
        if registered:
            print("Address {0} now registered on {1}.".format(auth_addr,
                                                              self.url))
        else:
            print("Failed to register address {0} on {1}.".format(auth_addr,
                                                                  self.url))
        return True

    def show_config(self):
        """Display saved config."""
        print(json.dumps(self.config, indent=2))
        return self.config

    def ping(self):
        """Attempt keep-alive with the server."""
        self._init_messanger()
        print("Pinging {0} with address {1}.".format(
            self.messanger.server_url(), self.messanger.auth_address()))
        self.messanger.ping()
        return True

    def poll(self, register_address=False, delay=common.DEFAULT_DELAY,
             limit=None):
        """TODO doc string"""
        stop_time = _now() + timedelta(seconds=int(limit)) if limit else None

        if register_address:
            self.register()

        while True:
            self.ping()

            if stop_time and _now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self, cleanup=False, rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """TODO doc string"""

        self._init_messanger()
        def _on_generate_shard(height, seed, file_hash):
            first = height == 1
            set_height = (height % set_height_interval) == 0
            last = (int(self.max_size / common.SHARD_SIZE) + 1) == height
            if first or set_height or last:
                self.messanger.height(height)

        bldr = builder.Builder(self.config["payout_address"],
                               common.SHARD_SIZE, self.max_size,
                               debug=self.debug,
                               on_generate_shard=_on_generate_shard)
        generated = bldr.build(self.store_path, cleanup=cleanup,
                               rebuild=rebuild)
        height = len(generated)
        self.messanger.height(height)
        return generated
Beispiel #10
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False, quiet=False,
                 use_folder_tree=False, max_size=common.DEFAULT_MAX_SIZE,
                 min_free_size=common.DEFAULT_MIN_FREE_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        debug = deserialize.flag(debug)
        quiet = deserialize.flag(quiet)

        self.url = deserialize.url(url)
        self.use_folder_tree = deserialize.flag(use_folder_tree)
        self.max_size = deserialize.byte_count(max_size)
        self.min_free_size = deserialize.byte_count(min_free_size)

        self.messenger = None  # lazy
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        storjnode.util.ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        storjnode.util.ensure_path_exists(self.store_path)

        # check for vfat partions
        try:
            fstype = storjnode.util.get_fs_type(self.store_path)

        # FileNotFoundError: [Errno 2] No such file or directory: '/etc/mtab'
        # psutil: https://code.google.com/p/psutil/issues/detail?id=434
        except EnvironmentError as e:
            logger.warning(e)
            fstype = None

        if fstype == "vfat":
            logger.info("Detected vfat partition, using folder tree.")
            self.use_folder_tree = True
        if fstype is None:
            msg = "Couldn't detected partition type for '{0}'"
            logger.warning(msg.format(self.store_path))

        self.cfg = storjnode.config.get(self.btctxstore, self.cfg_path)

    @staticmethod
    def version():
        print(__version__)
        return __version__

    def _init_messenger(self):
        """Make sure messenger exists."""
        if self.messenger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messenger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def register(self):
        """Attempt to register the config address."""
        self._init_messenger()
        payout_address = self.cfg["payout_address"]
        self.messenger.register(payout_address)
        logger.info("Registered on server '{0}'.".format(self.url))
        return True

    def config(self, set_wallet=None, set_payout_address=None):
        """
        Set and then show the config settings.

        :param set_wallet: Set the HWIF for registration/auth address.
        :param set_payout_address:  Set the payout address.
        :return: Configuation object.
        """
        if((set_payout_address is not None) and
                (not self.btctxstore.validate_address(set_payout_address))):
            raise exceptions.InvalidAddress(set_payout_address)
        if((set_wallet is not None) and
                (not self.btctxstore.validate_wallet(set_wallet))):
            raise exceptions.InvalidHWIF(set_wallet)

        self._init_messenger()
        config_updated = False

        # update payout address if requested
        if set_payout_address:
            self.cfg["payout_address"] = set_payout_address
            config_updated = True

        # update wallet if requested
        if set_wallet:
            self.cfg["wallet"] = set_wallet
            config_updated = True

        # save config if updated
        if config_updated:
            storjnode.config.save(self.btctxstore, self.cfg_path, self.cfg)

        # display config
        print(SHOW_CONFIG_TEMPLATE.format(
            self.messenger.get_nodeid(),
            self.cfg["payout_address"]
        ))
        return self.cfg

    def ping(self):
        """Attempt one keep-alive with the server."""
        self._init_messenger()

        msg = "Pinging server '{0}' at {1:%Y-%m-%d %H:%M:%S}."
        logger.info(msg.format(self.messenger.server_url(), datetime.now()))
        self.messenger.ping()

        return True

    def poll(self, delay=common.DEFAULT_DELAY, limit=None):
        """Attempt continuous keep-alive with the server.

        :param delay: Delay in seconds per ping of the server.
        :param limit: Number of seconds in the future to stop polling.
        :return: True, if limit is reached. None, if otherwise.
        """
        delay = deserialize.positive_integer(delay)
        stop_time = None
        if limit is not None:
            stop_time = datetime.now() + timedelta(seconds=int(limit))

        while True:  # ping the server every X seconds
            self.ping()

            if stop_time and datetime.now() >= stop_time:
                return True
            time.sleep(int(delay))

    def freespace(self):
        freespace = psutil.disk_usage(self.store_path).free
        print(freespace)
        return freespace

    def build(self, workers=1, cleanup=False, rebuild=False, repair=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """Generate test files deterministically based on address.

        :param workers: Number of Number of threadpool workers.
        :param cleanup: Remove files in shard directory.
        :param rebuild: Re-generate any file shards.
        :param set_height_interval: Number of shards to generate before
                                    notifying the server.
        """

        workers = deserialize.positive_nonzero_integer(workers)

        set_height_interval = deserialize.positive_nonzero_integer(
            set_height_interval
        )
        cleanup = deserialize.flag(cleanup)
        rebuild = deserialize.flag(rebuild)
        repair = deserialize.flag(repair)

        self._init_messenger()
        logger.info("Starting build")

        def _on_generate_shard(cur_height, last):
            """
            Because URL requests are slow, only update the server when we are
            at the first height, at some height_interval, or the last height.

            :param cur_height: Current height in the building process.
            """
            first = cur_height == 1
            set_height = (cur_height % int(set_height_interval)) == 0

            if first or set_height or last:
                self.messenger.height(cur_height)
                logger.info("Current height at {0}.".format(cur_height))

        # Initialize builder and generate/re-generate shards
        bldr = builder.Builder(address=self.cfg["payout_address"],
                               shard_size=common.SHARD_SIZE,
                               max_size=self.max_size,
                               min_free_size=self.min_free_size,
                               on_generate_shard=_on_generate_shard,
                               use_folder_tree=self.use_folder_tree)
        generated = bldr.build(self.store_path, workers=workers,
                               cleanup=cleanup, rebuild=rebuild, repair=repair)

        logger.info("Build finished")
        return generated

    def audit(self, delay=common.DEFAULT_AUDIT_DELAY, limit=None):

        self._init_messenger()

        # Initialize builder and audit shards
        bldr = builder.Builder(address=self.cfg["payout_address"],
                               shard_size=common.SHARD_SIZE,
                               max_size=self.max_size,
                               min_free_size=self.min_free_size,
                               use_folder_tree=self.use_folder_tree)

        delay = deserialize.positive_integer(delay)
        stop_time = None
        if limit is not None:
            stop_time = datetime.now() + timedelta(seconds=int(limit))

        btc_index = 0
        while True:
            btc_block = bldr.btc_last_confirmed_block(
                min_confirmations=common.DEFAULT_MIN_CONFIRMATIONS
            )
            if btc_block['block_no'] != btc_index:
                btc_hash = btc_block['blockhash']
                btc_index = btc_block['block_no']

                logger.debug("Using bitcoin block {0} hash {1}.".format(
                    btc_index, btc_hash))

                wif = self.btctxstore.get_key(self.cfg["wallet"])
                address = self.btctxstore.get_address(wif)
                response_data = address + btc_hash + str(bldr.audit(
                                                        self.store_path,
                                                        btc_block['block_no'],
                                                        btc_block['blockhash']))
                response = hashlib.sha256(
                    response_data.encode('utf-8')
                ).hexdigest()

                # New Dataserv Server version is needed
                self.messenger.audit(btc_block['block_no'], response)
            else:
                msg = "Bitcoin block {0} already used. Waiting for new block."
                logger.debug(msg.format(btc_index))

            if stop_time and datetime.now() >= stop_time:
                return True
            time.sleep(int(delay))

    def farm(self, workers=1, cleanup=False, rebuild=False, repair=False,
             set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL,
             delay=common.DEFAULT_DELAY, limit=None):
        """ Fully automatic client for users wishing a simple turnkey solution.
        This will run all functions automatically with the most sane defaults
        and as little user interface as possible.

        :param workers: Number of Number of threadpool workers.
        :param cleanup: Remove files in shard directory.
        :param rebuild: Re-generate any file shards.
        :param set_height_interval: Number of shards to generate before
                                    notifying the server.
        :param delay: Delay in seconds per ping of the server.
        :param limit: Number of seconds in the future to stop polling.
        """

        workers = deserialize.positive_nonzero_integer(workers)

        set_height_interval = deserialize.positive_nonzero_integer(
            set_height_interval
        )
        cleanup = deserialize.flag(cleanup)
        rebuild = deserialize.flag(rebuild)
        repair = deserialize.flag(repair)

        # farmer never gives up
        self._init_messenger()
        self.messenger.retry_limit = 99999999999999999999999999999999999999

        try:
            self.register()
        except exceptions.AddressAlreadyRegistered:
            pass  # already registered ...

        self.set_bandwidth()

        self.build(workers=workers, cleanup=cleanup, rebuild=rebuild,
                   repair=repair, set_height_interval=set_height_interval)
        self.poll(delay=delay, limit=limit)
        return True

    def set_bandwidth(self):
        results = speedtest()
        self.messenger.set_bandwidth(results["upload"],
                                     results["download"])
Beispiel #11
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False, quiet=False,
                 use_folder_tree=False, max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        debug = deserialize.flag(debug)
        quiet = deserialize.flag(quiet)

        self.url = deserialize.url(url)
        self.use_folder_tree = deserialize.flag(use_folder_tree)
        self.max_size = deserialize.byte_count(max_size)

        self.messenger = None  # lazy
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        control.util.ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        control.util.ensure_path_exists(self.store_path)

        # check for vfat partions
        if control.util.get_fs_type(self.store_path) == "vfat":
            self.use_folder_tree = True

        self.cfg = control.config.get(self.btctxstore, self.cfg_path)

    @staticmethod
    def version():
        print(__version__)
        return __version__

    def _init_messenger(self):
        """Make sure messenger exists."""
        if self.messenger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messenger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def register(self):
        """Attempt to register the config address."""
        self._init_messenger()
        payout_address = self.cfg["payout_address"]
        self.messenger.register(payout_address)
        logger.info("Registered on server '{0}'.".format(self.url))
        return True

    def config(self, set_wallet=None, set_payout_address=None):
        """
        Set and then show the config settings.

        :param set_wallet: Set the HWIF for registration/auth address.
        :param set_payout_address:  Set the payout address.
        :return: Configuation object.
        """
        if((set_payout_address is not None) and
                (not self.btctxstore.validate_address(set_payout_address))):
            raise exceptions.InvalidAddress(set_payout_address)
        if((set_wallet is not None) and
                (not self.btctxstore.validate_wallet(set_wallet))):
            raise exceptions.InvalidHWIF(set_wallet)

        self._init_messenger()
        config_updated = False

        # update payout address if requested
        if set_payout_address:
            self.cfg["payout_address"] = set_payout_address
            config_updated = True

        # update wallet if requested
        if set_wallet:
            self.cfg["wallet"] = set_wallet
            config_updated = True

        # save config if updated
        if config_updated:
            control.config.save(self.btctxstore, self.cfg_path, self.cfg)

        # display config
        print(SHOW_CONFIG_TEMPLATE.format(
            self.messenger.auth_address(),
            self.cfg["payout_address"]
        ))
        return self.cfg

    def ping(self):
        """Attempt one keep-alive with the server."""
        self._init_messenger()

        msg = "Pinging server '{0}' at {1:%Y-%m-%d %H:%M:%S}."
        logger.info(msg.format(self.messenger.server_url(), datetime.now()))
        self.messenger.ping()

        return True

    def poll(self, delay=common.DEFAULT_DELAY, limit=None):
        """Attempt continuous keep-alive with the server.

        :param delay: Delay in seconds per ping of the server.
        :param limit: Number of seconds in the future to stop polling.
        :return: True, if limit is reached. None, if otherwise.
        """
        delay = deserialize.positive_integer(delay)
        stop_time = None
        if limit:
            stop_time = datetime.now() + timedelta(seconds=int(limit))

        while True:  # ping the server every X seconds
            self.ping()

            if stop_time and datetime.now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self, cleanup=False, rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """Generate test files deterministically based on address.

        :param cleanup: Remove files in shard directory.
        :param rebuild: Re-generate any file shards.
        :param set_height_interval: Number of shards to generate before
                                    notifying the server.
        """
        set_height_interval = deserialize.positive_nonzero_integer(
            set_height_interval
        )
        cleanup = deserialize.flag(cleanup)
        rebuild = deserialize.flag(rebuild)

        self._init_messenger()
        logger.info("Starting build")

        def _on_generate_shard(cur_height, cur_seed, cur_file_hash):
            """
            Because URL requests are slow, only update the server when we are
            at the first height, at some height_interval, or the last height.

            :param cur_height: Current height in the building process.
            """
            first = cur_height == 1
            set_height = (cur_height % int(set_height_interval)) == 0
            last = int(self.max_size / common.SHARD_SIZE) == cur_height

            if first or set_height or last:
                self.messenger.height(cur_height)
                logger.info("Current height at {0}.".format(cur_height))

        # Initialize builder and generate/re-generate shards
        bldr = builder.Builder(self.cfg["payout_address"],
                               common.SHARD_SIZE, self.max_size,
                               on_generate_shard=_on_generate_shard,
                               use_folder_tree=self.use_folder_tree)
        generated = bldr.build(self.store_path, cleanup=cleanup,
                               rebuild=rebuild)

        logger.info("Build finished")
        return generated

    def farm(self):
        """ Fully automatic client for users wishing a simple turnkey solution.
        This will run all functions automatically with the most sane defaults
        and as little user interface as possible.
        """

        # farmer never gives up
        self._init_messenger()
        self.messenger.retry_limit = 99999999999999999999999999999999999999

        try:
            self.register()
        except exceptions.AddressAlreadyRegistered:
            pass  # already registered ...
        self.build()
        self.poll()
Beispiel #12
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False,
                 max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        self.url = url
        self.messanger = None  # lazy
        self.debug = debug
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)
        self.max_size = deserialize.byte_count(max_size)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        self._ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        self._ensure_path_exists(self.store_path)

        self.cfg = config.get(self.btctxstore, self.cfg_path)

    def _ensure_path_exists(self, path):
        if not os.path.exists(path):
            os.makedirs(path)

    def _init_messanger(self):
        if self.messanger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messanger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def version(self):
        print(__version__)
        return __version__

    def register(self):
        """Attempt to register the config address."""
        self._init_messanger()
        registered = self.messanger.register(self.cfg["payout_address"])
        auth_addr = self.messanger.auth_address()
        if registered:
            print("Address {0} now registered on {1}.".format(auth_addr,
                                                              self.url))
        else:
            print("Failed to register address {0} on {1}.".format(auth_addr,
                                                                  self.url))
        return True

    def config(self, set_wallet=None, set_payout_address=None):
        """Display saved config."""
        config_updated = False

        # update payout address if requested
        if set_payout_address: 
            self.cfg["payout_address"] = set_payout_address
            config_updated = True
            # FIXME update dataserv here

        # update wallet if requested
        if set_wallet: 
            self.cfg["wallet"] = set_wallet
            config_updated = True

        if config_updated: # save config if updated
            config.save(self.btctxstore, self.cfg_path, self.cfg)

        print(json.dumps(self.cfg, indent=2))
        return self.cfg

    def ping(self):
        """Attempt keep-alive with the server."""
        self._init_messanger()
        print("Pinging {0} with address {1}.".format(
            self.messanger.server_url(), self.messanger.auth_address()))
        self.messanger.ping()
        return True

    def poll(self, register_address=False, delay=common.DEFAULT_DELAY,
             limit=None):
        """TODO doc string"""
        stop_time = _now() + timedelta(seconds=int(limit)) if limit else None

        if register_address:
            self.register()

        while True:
            self.ping()

            if stop_time and _now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self, cleanup=False, rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """TODO doc string"""

        self._init_messanger()
        def _on_generate_shard(height, seed, file_hash):
            first = height == 1
            set_height = (height % int(set_height_interval)) == 0
            last = (int(self.max_size / common.SHARD_SIZE) + 1) == height
            if first or set_height or last:
                self.messanger.height(height)

        bldr = builder.Builder(self.cfg["payout_address"],
                               common.SHARD_SIZE, self.max_size,
                               debug=self.debug,
                               on_generate_shard=_on_generate_shard)
        generated = bldr.build(self.store_path, cleanup=cleanup,
                               rebuild=rebuild)
        height = len(generated)
        self.messanger.height(height)
        return generated
Beispiel #13
0
class Client(object):

    def __init__(self, url=common.DEFAULT_URL, debug=False,
                 max_size=common.DEFAULT_MAX_SIZE,
                 store_path=common.DEFAULT_STORE_PATH,
                 config_path=common.DEFAULT_CONFIG_PATH,
                 connection_retry_limit=common.DEFAULT_CONNECTION_RETRY_LIMIT,
                 connection_retry_delay=common.DEFAULT_CONNECTION_RETRY_DELAY):

        self.url = url
        self.debug = debug
        self.max_size = deserialize.byte_count(max_size)

        self.messenger = None  # lazy
        self.btctxstore = BtcTxStore()
        self.retry_limit = deserialize.positive_integer(connection_retry_limit)
        self.retry_delay = deserialize.positive_integer(connection_retry_delay)

        # paths
        self.cfg_path = os.path.realpath(config_path)
        self._ensure_path_exists(os.path.dirname(self.cfg_path))
        self.store_path = os.path.realpath(store_path)
        self._ensure_path_exists(self.store_path)

        self.cfg = config.get(self.btctxstore, self.cfg_path)

    @staticmethod
    def version():
        print(__version__)
        return __version__

    @staticmethod
    def _ensure_path_exists(path):
        """To keep front writing to non-existant paths."""
        if not os.path.exists(path):
            os.makedirs(path)

    def _init_messenger(self):
        """Make sure messenger exists."""
        if self.messenger is None:
            wif = self.btctxstore.get_key(self.cfg["wallet"])
            self.messenger = messaging.Messaging(self.url, wif,
                                                 self.retry_limit,
                                                 self.retry_delay)

    def register(self):
        """Attempt to register the config address."""
        self._init_messenger()
        auth_address = self.messenger.auth_address()
        payout_address = self.cfg["payout_address"]
        registered = self.messenger.register(payout_address)
        if registered:
            msg = "Address {0} registered on {1} with payout address {2}."
            print(msg.format(auth_address, self.url, payout_address))
        else:
            msg = ("Failed to register address {0} "
                   "on {1} with payout address {2}!")
            print(msg.format(auth_address, self.url, payout_address))
        return registered

    def config(self, set_wallet=None, set_payout_address=None):
        """
        Set and then show the config settings.

        :param set_wallet: Set the HWIF for registration/auth address.
        :param set_payout_address:  Set the payout address.
        :return: Configuation object.
        """
        config_updated = False

        # update payout address if requested
        if set_payout_address:
            self.cfg["payout_address"] = set_payout_address
            config_updated = True

        # update wallet if requested
        if set_wallet:
            self.cfg["wallet"] = set_wallet
            config_updated = True

        # save config if updated
        if config_updated:
            config.save(self.btctxstore, self.cfg_path, self.cfg)

        print(json.dumps(self.cfg, indent=2))  # should we output this?
        return self.cfg

    def ping(self):
        """Attempt one keep-alive with the server."""
        self._init_messenger()

        print("Pinging {0} with address {1}.".format(
            self.messenger.server_url(), self.messenger.auth_address()))
        self.messenger.ping()

        return True

    def poll(self, register_address=False, delay=common.DEFAULT_DELAY,
             limit=None):
        """
        Attempt continuous keep-alive with the server.

        :param register_address: Registration/auth Bitcoin address.
        :param delay: Delay in seconds per ping of the server.
        :param limit: Number of seconds in the future to stop polling.
        :return: True, if limit is reached. None, if otherwise.
        """
        stop_time = _now() + timedelta(seconds=int(limit)) if limit else None

        if register_address:  # in case the user forgot to register
            self.register()

        while True:  # ping the server every X seconds
            self.ping()

            if stop_time and _now() >= stop_time:
                return True
            time.sleep(int(delay))

    def build(self, cleanup=False, rebuild=False,
              set_height_interval=common.DEFAULT_SET_HEIGHT_INTERVAL):
        """
        Generate test files deterministically based on address.

        :param cleanup: Remove files in shard directory.
        :param rebuild: Re-generate any file shards.
        :param set_height_interval: Number of shards to generate before
                                    notifying the server.
        """

        self._init_messenger()

        def _on_generate_shard(cur_height, cur_seed, cur_file_hash):
            """
            Because URL requests are slow, only update the server when we are
            at the first height, at some height_interval, or the last height.

            :param cur_height: Current height in the building process.
            """
            first = cur_height == 1
            set_height = (cur_height % int(set_height_interval)) == 0
            last = (int(self.max_size / common.SHARD_SIZE) + 1) == cur_height

            if first or set_height or last:
                self.messenger.height(cur_height)

        # Initialize builder and generate/re-generate shards
        bldr = builder.Builder(self.cfg["payout_address"],
                               common.SHARD_SIZE, self.max_size,
                               debug=self.debug,
                               on_generate_shard=_on_generate_shard)
        generated = bldr.build(self.store_path, cleanup=cleanup,
                               rebuild=rebuild)

        return generated
Beispiel #14
0
class TestConfig(unittest.TestCase):
    def setUp(self):
        self.btctxstore = BtcTxStore()

    def test_roundtrip_unencrypted(self):
        path = tempfile.mktemp()
        saved_data = config.create(self.btctxstore, path)
        loaded_data = config.get(self.btctxstore, path)
        self.assertEqual(saved_data, loaded_data)
        os.remove(path)

    def test_save_overwrites(self):
        path = tempfile.mktemp()

        # create config
        created_data = config.create(self.btctxstore, path)

        # update config
        updated_data = copy.deepcopy(created_data)
        updated_data["payout_address"] = "1A8WqiJDh3tGVeEefbMN5BVDYxx2XSoWgG"
        config.save(self.btctxstore, path, updated_data)

        # confirm overwriten
        loaded_data = config.get(self.btctxstore, path)
        self.assertEqual(updated_data, loaded_data)
        os.remove(path)

    def test_password_validation(self):
        pass  # TODO implement

    def test_validation(self):
        wallet = self.btctxstore.create_wallet()
        key = self.btctxstore.get_key(wallet)
        address = self.btctxstore.get_address(key)

        # must be a dict
        def callback():
            config.validate(self.btctxstore, None)

        self.assertRaises(exceptions.InvalidConfig, callback)

        # must have the correct version
        def callback():
            config.validate(self.btctxstore, {
                "payout_address": address,
                "wallet": wallet,
            })

        self.assertRaises(exceptions.InvalidConfig, callback)

        # must have a valid payout address
        def callback():
            config.validate(self.btctxstore, {
                "version": __version__,
                "wallet": wallet,
            })

        self.assertRaises(exceptions.InvalidConfig, callback)

        # must have a valid wallet
        def callback():
            config.validate(self.btctxstore, {
                "version": __version__,
                "payout_address": address,
            })

        self.assertRaises(exceptions.InvalidConfig, callback)

        # valid config
        self.assertTrue(
            config.validate(
                self.btctxstore, {
                    "version": __version__,
                    "payout_address": address,
                    "wallet": wallet,
                }))

    def test_migrate(self):
        path = tempfile.mktemp()

        # initial unmigrated 2.0.0 config
        cfg = {
            "version": "2.0.0",
            "master_secret": "test_master_secret",
            "payout_address": "1A8WqiJDh3tGVeEefbMN5BVDYxx2XSoWgG",
        }

        # test its invalid with current build
        def callback():
            config.validate(self.btctxstore, cfg)

        self.assertRaises(exceptions.InvalidConfig, callback)

        # migrate
        cfg = config.migrate(self.btctxstore, path, cfg)

        # test its now valid
        self.assertTrue(config.validate(self.btctxstore, cfg))