def __init__(self, wallet, username=None): """Initialize the bittransfer with wallet and username.""" from two1.server.machine_auth_wallet import MachineAuthWallet super().__init__() if isinstance(wallet, MachineAuthWallet): self.wallet = wallet else: self.wallet = MachineAuthWallet(wallet) if username is None: self.username = config.Config().username else: self.username = username
def mock_machine_auth(mock_wallet): """ Fixture that injects a MachineAuthWallet using a MockWallet Returns: MockTwo1Wallet: an initialized mock two1 wallet """ return MachineAuthWallet(mock_wallet)
def machine_auth_wallet(wallet): """ Fixture which injects a MachineAuthWallet Returns: MachineAuthWallet: machine auth wallet """ return MachineAuthWallet(wallet)
def mock_config(mock_wallet): """ Fixture that injects a MockConfig Returns: MockConfig: an mock config object initialized with a mock wallet """ _mock_config = mock_objects.MockConfig() _mock_config.machine_auth = MachineAuthWallet(mock_wallet) return _mock_config
def __init__(self, wallet, username=None, client=None): """Initialize the bittransfer with wallet and username.""" from two1.server.machine_auth_wallet import MachineAuthWallet from two1.server import rest_client super().__init__() if isinstance(wallet, MachineAuthWallet): self.wallet = wallet else: self.wallet = MachineAuthWallet(wallet) if username is None: self.username = config.Config().username else: self.username = username if client is None: self.client = rest_client.TwentyOneRestClient( two1.TWO1_HOST, self.wallet, self.username) else: self.client = client
def mock_rest_client(monkeypatch, mock_config, mock_wallet): """ Fixture that injects a MockTwentyOneRestClient Returns: MockTwentyOneRestClient: an mock rest client initialized with a mock config and mock wallet """ machine_auth = MachineAuthWallet(mock_wallet) _mock_rest_client = mock_objects.MockTwentyOneRestClient( None, machine_auth, mock_config.username) for func_name in mock_objects.MockTwentyOneRestClient.DEFAULT_VALUES.keys( ): monkeypatch.setattr(server.rest_client.TwentyOneRestClient, func_name, getattr(_mock_rest_client, func_name)) return _mock_rest_client
def patch_rest_client(monkeypatch, mock_config, mock_wallet): """ Fixture that injects a MockTwentyOneRestClient that monkeypathces the regular TwentyOneRestClient Returns: MockTwentyOneRestClient: a mock rest client object """ machine_auth = MachineAuthWallet(mock_wallet) _patch_rest_client = mock_objects.MockTwentyOneRestClient( None, machine_auth, mock_config.username) for mock_function in mock_objects.MockTwentyOneRestClient.DEFAULT_VALUES.keys( ): monkeypatch.setattr(server.rest_client.TwentyOneRestClient, mock_function, getattr(_patch_rest_client, mock_function)) return _patch_rest_client
def request(*args, payment_method=None, **kwargs): """Instantiate, or use a cached BitRequests object, and make a request.""" global _requests global _current_method global _current_wallet payment_method = payment_method or _current_method if not _current_wallet: from two1.wallet import Wallet _current_wallet = Wallet() if payment_method not in _requests: if payment_method == OFF_CHAIN: from two1.server.machine_auth_wallet import MachineAuthWallet _requests[_current_method] = BitTransferRequests( MachineAuthWallet(_current_wallet)) elif payment_method == ON_CHAIN: _requests[_current_method] = OnChainRequests(_current_wallet) elif payment_method == CHANNEL: _requests[_current_method] = ChannelRequests(_current_wallet) else: raise ValueError('That method is not supported.') return _requests[payment_method].request(*args, **kwargs)
class BitTransferRequests(BitRequests): """BitRequests for making bit-transfer payments.""" HTTP_BITCOIN_PRICE = 'price' HTTP_BITCOIN_ADDRESS = 'bitcoin-address' HTTP_BITCOIN_USERNAME = '******' def __init__(self, wallet, username=None, client=None): """Initialize the bittransfer with wallet and username.""" from two1.server.machine_auth_wallet import MachineAuthWallet from two1.server import rest_client super().__init__() if isinstance(wallet, MachineAuthWallet): self.wallet = wallet else: self.wallet = MachineAuthWallet(wallet) if username is None: self.username = config.Config().username else: self.username = username if client is None: self.client = rest_client.TwentyOneRestClient( two1.TWO1_HOST, self.wallet, self.username) else: self.client = client def make_402_payment(self, response, max_price): """Make a bit-transfer payment to the payment-handling service.""" # Retrieve payment headers headers = response.headers price = headers.get(BitTransferRequests.HTTP_BITCOIN_PRICE) payee_address = headers.get(BitTransferRequests.HTTP_BITCOIN_ADDRESS) payee_username = headers.get(BitTransferRequests.HTTP_BITCOIN_USERNAME) # Verify that the payment method is supported if price is None or payee_address is None or payee_username is None: raise UnsupportedPaymentMethodError( 'Resource does not support that payment method.') # Convert string headers into correct data types price = int(price) # verify that we have the money to purchase the resource buffer_balance = self.client.get_earnings()["total_earnings"] if price > buffer_balance: insuff_funds_err = 'Resource price ({}) exceeds buffer balance ({}).' raise InsufficientBalanceError(insuff_funds_err.format(price, buffer_balance)) # Verify resource cost against our budget if max_price and price > max_price: max_price_err = 'Resource price ({}) exceeds max price ({}).' raise ResourcePriceGreaterThanMaxPriceError(max_price_err.format(price, max_price)) # Get the signing public key pubkey = self.wallet.get_public_key() compressed_pubkey = codecs.encode(pubkey.compressed_bytes, 'base64').decode() # Create and sign BitTranfer bittransfer = json.dumps({ 'payer': self.username, 'payer_pubkey': compressed_pubkey, 'payee_address': payee_address, 'payee_username': payee_username, 'amount': price, 'timestamp': time.time(), 'description': response.url }) if not isinstance(bittransfer, str): raise TypeError("Serialized bittransfer must be a string") signature = self.wallet.sign_message(bittransfer) logger.debug('[BitTransferRequests] Signature: {}'.format(signature)) logger.debug('[BitTransferRequests] BitTransfer: {}'.format(bittransfer)) return { 'Bitcoin-Transfer': bittransfer, 'Authorization': signature } def get_402_info(self, url): """Get bit-transfer payment information about the resource.""" headers = requests.get(url).headers price = headers.get(BitTransferRequests.HTTP_BITCOIN_PRICE, 0) payee_address = headers.get(BitTransferRequests.HTTP_BITCOIN_ADDRESS) payee_username = headers.get(BitTransferRequests.HTTP_BITCOIN_USERNAME) return {BitTransferRequests.HTTP_BITCOIN_PRICE: int(price), BitTransferRequests.HTTP_BITCOIN_ADDRESS: payee_address, BitTransferRequests.HTTP_BITCOIN_USERNAME: payee_username}
# Setup the dashboard from two1.commands import status from two1.commands import log from two1.commands import flush from two1.commands import mine from two1.wallet import Wallet from two1.server.machine_auth_wallet import MachineAuthWallet from two1.server import rest_client from two1.commands.config import Config from two1 import TWO1_HOST wallet = Wallet() host = TWO1_HOST conf = Config() username = Config().username client = rest_client.TwentyOneRestClient(host, MachineAuthWallet(wallet), username) admin = Admin(app, name='Admin', template_mode='bootstrap3') class DashboardView(BaseView): @expose('/', methods=('GET', 'POST')) def dashboard(self): flush_message = "" status_mining = status.status_mining(client) if request.method == 'POST': print(request.form) if request.form['submit'] == 'Flush Earnings': flush_message = self.doFlush() else:
def buy(config, resource, data, method, data_file, output_file, payment_method, max_price, info_only): """Buy from any machine payable endpoint Note: The two1lib _buy function does not support simply returning an object, until then, include a local copy here """ # If resource is a URL string, then bypass seller search if URL_REGEXP.match(resource): target_url = resource seller = target_url elif resource in DEMOS: target_url = TWO1_WWW_HOST + DEMOS[resource]["path"] data = json.dumps(data) else: raise NotImplementedError('Endpoint search is not implemented!') # Change default HTTP method from "GET" to "POST", if we have data if method == "GET" and (data or data_file): method = "POST" # Set default headers for making bitrequests with JSON-like data headers = {'Content-Type': 'application/json'} try: # Find the correct payment method if payment_method == 'offchain': bit_req = BitTransferRequests(MachineAuthWallet(wallet), username) elif payment_method == 'onchain': bit_req = OnChainRequests(wallet) else: raise Exception('Payment method does not exist.') # Make the request if info_only: res = bit_req.get_402_info(target_url) else: res = bit_req.request(method.lower(), target_url, max_price=max_price, data=data or data_file, headers=headers) except ResourcePriceGreaterThanMaxPriceError as e: config.log( uxstring.Error.resource_price_greater_than_max_price.format(e)) return except Exception as e: if 'Insufficient funds.' in str(e): config.log( uxstring.Error.insufficient_funds_mine_more.format( DEFAULT_ONCHAIN_BUY_FEE)) else: config.log(str(e), fg="red") return # Output results to user if output_file: # Write response output file output_file.write(res.content) elif info_only: # Print headers that are related to 402 payment required for key, val in res.items(): config.log('{}: {}'.format(key, val)) elif resource in DEMOS: config.log(DEMOS[resource]["formatter"](res)) else: response = res.json() # Clean up names for index, elem in enumerate(response): if elem['name'] is None: response[index]['name'] = 'Please name me' elif len(elem['name']) == 0: response[index]['name'] = 'Please name me' else: response[index]['name'] = response[index]['name'].title() print(elem['description']) if elem['description'] is None: try: response[index]['description'] = elem['owner'].title( ) + ' is a bad endpoint operator and forgot to place a description' except: response[index][ 'description'] = 'Anonymous is a bad endpoint operator and forgot to place a description' # Any description greater than 66 characters causes the text to overflow, this enforces a limit elif len(elem['description']) > 63: response[index]['description'] = response[index][ 'description'][:63] + '...' # Write response to console return response # Write the amount paid out if something was truly paid if not info_only and hasattr(res, 'amount_paid'): client = rest_client.TwentyOneRestClient(TWO1_HOST, MachineAuthWallet(wallet), username) user_balances = _get_balances(config, client) if payment_method == 'offchain': balance_amount = user_balances.twentyone balance_type = '21.co' elif payment_method == 'onchain': balance_amount = user_balances.onchain balance_type = 'blockchain' # Record the transaction if it was a payable request if hasattr(res, 'paid_amount'): config.log_purchase(s=seller, r=resource, p=res.paid_amount, d=str(datetime.datetime.today()))