Exemple #1
0
 def test_valid_address(self):
     """Test address validation"""
     # Null should always be false
     self.assertFalse(Validators.is_valid_address(None))
     os.environ['BANANO'] = '1'
     # Valid
     self.assertTrue(
         Validators.is_valid_address(
             'ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr'
         ))
     # Bad checksum
     self.assertFalse(
         Validators.is_valid_address(
             'ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xa'
         ))
     # Bad length
     self.assertFalse(
         Validators.is_valid_address(
             'ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94x'
         ))
     del os.environ['BANANO']
     # Valid
     self.assertTrue(
         Validators.is_valid_address(
             'nano_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr'
         ))
     # Bad checksum
     self.assertFalse(
         Validators.is_valid_address(
             'nano_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xa'
         ))
     # Bad length
     self.assertFalse(
         Validators.is_valid_address(
             'nano_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94x'
         ))
     # Valid
     self.assertTrue(
         Validators.is_valid_address(
             'xrb_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr'
         ))
     # Bad checksum
     self.assertFalse(
         Validators.is_valid_address(
             'xrb_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xa'
         ))
     # Bad length
     self.assertFalse(
         Validators.is_valid_address(
             'xrb_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94x'
         ))
Exemple #2
0
 def test_valid_block_hash(self):
     # None type
     self.assertFalse(Validators.is_valid_block_hash(None))
     # Non-hex strings
     self.assertFalse(Validators.is_valid_block_hash('1234'))
     self.assertFalse(Validators.is_valid_block_hash('Appditto LLC'))
     self.assertFalse(
         Validators.is_valid_block_hash(
             'ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr'
         ))
     # Too short
     self.assertFalse(
         Validators.is_valid_block_hash(
             '5E5B7C8F97BDA8B90FAA243050D99647F80C25EB4A07E7247114CBB129BDA18'
         ))
     # Too long
     self.assertFalse(
         Validators.is_valid_block_hash(
             '5E5B7C8F97BDA8B90FAA243050D99647F80C25EB4A07E7247114CBB129BDA1888'
         ))
     # Non-hex character
     self.assertFalse(
         Validators.is_valid_block_hash(
             '5E5B7C8F97BDA8B90FAA243050D99647F80C25EB4A07E7247114CBB129BDA18Z'
         ))
     # Valid
     self.assertTrue(
         Validators.is_valid_block_hash(
             '5E5B7C8F97BDA8B90FAA243050D99647F80C25EB4A07E7247114CBB129BDA188'
         ))
    async def send(self, request: web.Request, request_json: dict):
        """RPC send"""
        if 'wallet' not in request_json or 'source' not in request_json or 'destination' not in request_json or 'amount' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_address(request_json['source']):
            return self.json_response(data={'error': 'Invalid source'})
        elif not Validators.is_valid_address(request_json['destination']):
            return self.json_response(data={'error': 'Invalid destination'})

        id = request_json['id'] if 'id' in request_json else None
        work = request_json['work'] if 'work' in request_json else None

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        # Retrieve account on wallet
        account = await wallet.get_account(request_json['source'])
        if account is None:
            return self.json_response(data={'error': 'Account not found'})

        # Try to create and publish send block
        wallet = WalletUtil(account, wallet)
        try:
            resp = await wallet.send(int(request_json['amount']),
                                     request_json['destination'],
                                     id=id,
                                     work=work)
        except AccountNotFound:
            return self.json_response(data={'error': 'Account not found'})
        except BlockNotFound:
            return self.json_response(data={'error': 'Block not found'})
        except WorkFailed:
            return self.json_response(
                data={'error': 'Failed to generate work'})
        except ProcessFailed:
            return self.json_response(data={'error': 'RPC Process failed'})
        except InsufficientBalance:
            return self.json_response(data={'error': 'insufficient balance'})

        if resp is None:
            return self.json_response(
                data={'error': 'Unable to create send block'})

        return self.json_response(data=resp)
    async def wallet_change_seed(self, request: web.Request,
                                 request_json: dict):
        """RPC wallet_change_seed"""
        if 'wallet' not in request_json or 'seed' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_block_hash(request_json['seed']):
            return self.json_response(data={'error': 'Invalid seed'})

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        # Reset password
        if wallet.encrypted:
            await wallet.encrypt_wallet('')

        # Change key
        await wallet.change_seed(request_json['seed'])

        # Get newest account
        newest = await wallet.get_newest_account()

        return self.json_response(
            data={
                "success": "",
                "last_restored_account": newest.address,
                "restored_count": newest.account_index + 1
            })
    async def wallet_representative_set(self, request: web.Request,
                                        request_json: dict):
        """RPC wallet_representative_set"""
        if 'wallet' not in request_json or 'representative' not in request_json or (
                'update_existing_accounts' in request_json and not isinstance(
                    request_json['update_existing_accounts'], bool)):
            return self.generic_error()
        elif not Validators.is_valid_address(request_json['representative']):
            return self.json_response(data={'error': 'Invalid address'})

        update_existing = False
        if 'update_existing_accounts' in request_json:
            update_existing = request_json['update_existing_accounts']

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        wallet.representative = request_json['representative']
        await wallet.save(update_fields=['representative'])

        if update_existing:
            await wallet.bulk_representative_update(
                request_json['representative'])

        return self.json_response(data={'set': '1'})
Exemple #6
0
async def wallet_representative_set(wallet_id: str, rep: str, update_existing: bool = False):
    # Retrieve wallet
    # Retrieve wallet
    crypt = None
    password=None
    if not Validators.is_valid_address(rep):
        print("Invalid representative")
        exit(1)
    try:
        wallet = await Wallet.get_wallet(wallet_id)
    except WalletNotFound:
        print(f"No wallet found with ID: {wallet_id}")
        exit(1)
    except WalletLocked as wl:
        wallet = wl.wallet
        if update_existing:
            while True:
                try:
                    npass = getpass.getpass(prompt='Enter current password to decrypt wallet:')
                    crypt = AESCrypt(npass)
                    try:
                        decrypted = crypt.decrypt(wl.wallet.seed)
                        wallet = wl.wallet
                        wallet.seed = decrypted
                        password=npass
                    except DecryptionError:
                        print("**Invalid password**")
                except KeyboardInterrupt:
                    break
                    exit(0)

    wallet.representative = rep
    await wallet.save(update_fields=['representative'])
    await wallet.bulk_representative_update(rep)
    print(f"Representative changed")
    async def account_representative_set(self, request: web.Request,
                                         request_json: dict):
        """RPC account_representative_set"""
        if 'wallet' not in request_json or 'account' not in request_json or 'representative' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_address(request_json['account']):
            return self.json_response(data={'error': 'Invalid account'})
        elif not Validators.is_valid_address(request_json['representative']):
            return self.json_response(data={'error': 'Invalid representative'})

        work = request_json['work'] if 'work' in request_json else None

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        # Retrieve account on wallet
        account = await wallet.get_account(request_json['account'])
        if account is None:
            return self.json_response(data={'error': 'Account not found'})

        # Try to create and publish CHANGE block
        wallet = WalletUtil(account, wallet)
        try:
            resp = await wallet.representative_set(
                request_json['representative'], work=work)
        except AccountNotFound:
            return self.json_response(data={'error': 'Account not found'})
        except WorkFailed:
            return self.json_response(
                data={'error': 'Failed to generate work'})
        except ProcessFailed:
            return self.json_response(data={'error': 'RPC Process failed'})

        if resp is None:
            return self.json_response(
                data={'error': 'Unable to create change block'})

        return self.json_response(data=resp)
    async def receive(self, request: web.Request, request_json: dict):
        """RPC receive"""
        if 'wallet' not in request_json or 'account' not in request_json or 'block' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_address(request_json['account']):
            return self.json_response(data={'error': 'Invalid address'})
        elif not Validators.is_valid_block_hash(request_json['block']):
            return self.json_response(data={'error': 'Invalid block'})

        work = request_json['work'] if 'work' in request_json else None

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        # Retrieve account on wallet
        account = await wallet.get_account(request_json['account'])
        if account is None:
            return self.json_response(data={'error': 'Account not found'})

        # Try to receive block
        wallet = WalletUtil(account, wallet)
        try:
            response = await wallet.receive(request_json['block'], work=work)
        except BlockNotFound:
            return self.json_response(data={'error': 'Block not found'})
        except WorkFailed:
            return self.json_response(
                data={'error': 'Failed to generate work'})
        except ProcessFailed:
            return self.json_response(data={'error': 'RPC Process failed'})

        if response is None:
            return self.json_response(
                data={'error': 'Unable to receive block'})

        return self.json_response(data=response)
 async def wallet_create(self, request: web.Request, request_json: dict):
     """Route for creating new wallet"""
     if 'seed' in request_json:
         if not Validators.is_valid_block_hash(request_json['seed']):
             return self.json_response(data={'error': 'Invalid seed'})
         new_seed = request_json['seed']
     else:
         new_seed = RandomUtil.generate_seed()
     async with in_transaction() as conn:
         wallet = Wallet(seed=new_seed)
         await wallet.save(using_db=conn)
         await wallet.account_create(using_db=conn)
     return self.json_response(data={'wallet': str(wallet.id)})
    async def wallet_contains(self, request: web.Request, request_json: dict):
        """RPC wallet_contains"""
        if 'wallet' not in request_json or 'account' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_address(request_json['account']):
            return self.json_response(data={'error': 'Invalid account'})

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        exists = (await
                  wallet.get_account(request_json['account'])) is not None

        return self.json_response(data={'exists': '1' if exists else '0'})
Exemple #11
0
    def instance(cls) -> 'Config':
        if cls._instance is None:
            cls._instance = cls.__new__(cls)
            try:
                with open(f"{Utils.get_project_root().joinpath(pathlib.PurePath('config.yaml'))}", "r") as in_yaml:
                    cls.yaml = list(yaml.load_all(in_yaml, Loader=yaml.FullLoader))[0]
            except FileNotFoundError:
                cls.yaml = None
            # Parse options
            cls.banano = cls.get_yaml_property('wallet', 'banano', False)
            cls.log_file = cls.get_yaml_property('server', 'log_file', default='/tmp/pippin_wallet.log')
            cls.debug = cls.get_yaml_property('server', 'debug', default=False)
            cls.stdout = cls.get_yaml_property('server', 'log_to_stdout', default=False)
            cls.node_url = cls.get_yaml_property('server', 'node_rpc_url', default='http://[::1]:7072' if cls.banano else 'http://[::1]:7076')
            cls.node_ws_url = cls.get_yaml_property('server', 'node_ws_url', None)
            cls.port = cls.get_yaml_property('server', 'port', default=11338)
            cls.host = cls.get_yaml_property('server', 'host', default='127.0.0.1')
            cls.work_peers = cls.get_yaml_property('wallet', 'work_peers', [])
            cls.node_work_generate = cls.get_yaml_property('wallet', 'node_work_generate', False)
            cls.receive_minimum = cls.get_yaml_property('wallet', 'receive_minimum', 1000000000000000000000000000 if cls.banano else 1000000000000000000000000)
            cls.auto_receive_on_send = cls.get_yaml_property('wallet', 'auto_receive_on_send', True)
            cls.max_work_processes = cls.get_yaml_property('wallet', 'max_work_processes', 1)
            cls.max_sign_threads = cls.get_yaml_property('wallet', 'max_sign_threads', 1)
            if not cls.banano:
                cls.preconfigured_reps = cls.get_yaml_property('wallet', 'preconfigured_representatives_nano', default=None)
            else:
                cls.preconfigured_reps = cls.get_yaml_property('wallet', 'preconfigured_representatives_banano', default=None)
            # Enforce that all reps are valid
            if cls.preconfigured_reps is not None:
                cls.preconfigured_reps = set(cls.preconfigured_reps)
                for r in cls.preconfigured_reps:
                    if not Validators.is_valid_address(r):
                        log.server_logger.warn(f"{r} is not a valid representative!")
                        cls.preconfigured_reps.remove(r)
                if len(cls.preconfigured_reps) == 0:
                    cls.preconfigured_reps = None
            # Go to default if None
            if cls.preconfigured_reps is None:
                cls.preconfigured_reps = DEFAULT_BANANO_REPS if cls.banano else DEFAULT_NANO_REPS

        return cls._instance
    async def work_generate(self, request: web.Request, request_json: dict):
        """Route for running work_generate"""
        if 'hash' in request_json:
            if not Validators.is_valid_block_hash(request_json['hash']):
                return self.json_response(data={'error': 'Invalid hash'})
        else:
            return self.generic_error()

        difficulty = DifficultyModel.instance().send_difficulty
        if 'difficulty' in request_json:
            difficulty = request_json['difficulty']
        elif 'subtype' in request_json and request_json['subtype'] == 'receive':
            difficulty = DifficultyModel.instance().receive_difficulty

        # Generate work
        work = await WorkClient.instance().work_generate(
            request_json['hash'], difficulty)
        if work is None:
            return self.json_response(
                data={'error': 'Failed to generate work'})
        return self.json_response(data={'work': work})
    async def wallet_add(self, request: web.Request, request_json: dict):
        """RPC wallet_add"""
        if 'wallet' not in request_json or 'key' not in request_json:
            return self.generic_error()
        elif not Validators.is_valid_block_hash(request_json['key']):
            return self.json_response(data={'error': 'Invalid key'})

        # Retrieve wallet
        try:
            wallet = await Wallet.get_wallet(request_json['wallet'])
        except WalletNotFound:
            return self.json_response(data={'error': 'wallet not found'})
        except WalletLocked:
            return self.json_response(data={'error': 'wallet locked'})

        # Add account
        try:
            address = await wallet.adhoc_account_create(request_json['key'])
        except AccountAlreadyExists:
            return self.json_response(data={'error': 'account already exists'})

        return self.json_response(data={'account': address})
Exemple #14
0
def main():
    loop = asyncio.new_event_loop()
    try:
        loop.run_until_complete(DBConfig().init_db())
        if options.command == 'wallet_list':
            loop.run_until_complete(wallet_list())
        elif options.command == 'wallet_create':
            if options.seed is not None:
                if not Validators.is_valid_block_hash(options.seed):
                    print("Invalid seed specified")
                    exit(1)
            loop.run_until_complete(wallet_create(options.seed))
        elif options.command == 'wallet_change_seed':
            if options.seed is not None:
                if not Validators.is_valid_block_hash(options.seed):
                    print("Invalid seed specified")
                    exit(1)
            else:
                while True:
                    try:
                        options.seed = getpass.getpass(prompt='Enter new wallet seed:')
                        if Validators.is_valid_block_hash(options.seed):
                            break
                        print("**Invalid seed**, should be a 64-character hex string")
                    except KeyboardInterrupt:
                        break
                        exit(0)
            password = ''
            if options.encrypt:
                while True:
                    try:
                        password = getpass.getpass(prompt='Enter password to encrypt wallet:')
                        if password.strip() == '':
                            print("**Bad password** - cannot be blanke")
                        break
                    except KeyboardInterrupt:
                        break
                        exit(0)
            loop.run_until_complete(wallet_change_seed(options.wallet, options.seed, password))
        elif options.command == 'wallet_view_seed':
            loop.run_until_complete(wallet_view_seed(options.wallet, options.password, options.all_keys))
        elif options.command == 'account_create':
            if options.key is not None:
                if not Validators.is_valid_block_hash(options.key):
                    print("Invalid Private Key")
                    exit(0)
            elif options.key is not None and options.count is not None:
                print("You can only specify one: --key or --count")
                print("--count can only be used for deterministic accounts")
            elif options.count is not None:
                if options.count < 1:
                    print("Count needs to be at least 1...")
            loop.run_until_complete(account_create(options.wallet, options.key, options.count))
        elif options.command == 'wallet_destroy':
            loop.run_until_complete(wallet_destroy(options.wallet))
        elif options.command == 'wallet_representative_get':
            loop.run_until_complete(wallet_representative_get(options.wallet))
        elif options.command == 'wallet_representative_set':
            loop.run_until_complete(wallet_representative_set(options.wallet, options.representatives, update_existing=options.update_existing))
        else:
            parser.print_help()
    except Exception as e:
        print(str(e))
        raise e
    finally:
        loop.run_until_complete(Tortoise.close_connections())
        loop.close()