def __init__(self, blockchain_instance=None, *args, **kwargs): if blockchain_instance is None: if kwargs.get("steem_instance"): blockchain_instance = kwargs["steem_instance"] elif kwargs.get("hive_instance"): blockchain_instance = kwargs["hive_instance"] self.blockchain = blockchain_instance or shared_blockchain_instance() self.access_token = None config = self.blockchain.config self.get_refresh_token = kwargs.get("get_refresh_token", False) self.hot_sign_redirect_uri = kwargs.get("hot_sign_redirect_uri", config["hot_sign_redirect_uri"]) if self.hot_sign_redirect_uri == "": self.hot_sign_redirect_uri = None self.client_id = kwargs.get("client_id", config["hs_client_id"]) self.scope = kwargs.get("scope", "login") self.hs_oauth_base_url = kwargs.get("hs_oauth_base_url", config["hs_oauth_base_url"]) self.hs_api_url = kwargs.get("hs_api_url", config["hs_api_url"]) if "token" in kwargs and len(kwargs["token"]) > 0: from beemstorage import InRamPlainTokenStore self.store = InRamPlainTokenStore() token = kwargs["token"] self.set_access_token(token) name = self.me()["user"] self.setToken({name: token}) else: """ If no keys are provided manually we load the SQLite keyStorage """ from beemstorage import SqliteEncryptedTokenStore self.store = kwargs.get( "token_store", SqliteEncryptedTokenStore(config=config, **kwargs), )
class HiveSigner(object): """ HiveSigner :param str scope: comma separated string with scopes login,offline,vote,comment,delete_comment,comment_options,custom_json,claim_reward_balance .. code-block:: python # Run the login_app in examples and login with a account from beem import Steem from beem.HiveSigner import HiveSigner from beem.comment import Comment hs = HiveSigner(client_id="beem.app") steem = Steem(HiveSigner=hs) steem.wallet.unlock("supersecret-passphrase") post = Comment("author/permlink", blockchain_instance=steem) post.upvote(voter="test") # replace "test" with your account Examples for creating HiveSigner urls for broadcasting in browser: .. testoutput:: from beem import Steem from beem.account import Account from beem.HiveSigner import HiveSigner from pprint import pprint steem = Steem(nobroadcast=True, unsigned=True) hs = HiveSigner(blockchain_instance=steem) acc = Account("test", blockchain_instance=steem) pprint(hs.url_from_tx(acc.transfer("test1", 1, "HIVE", "test"))) .. testcode:: 'https://hivesigner.com/sign/transfer?from=test&to=test1&amount=1.000+HIVE&memo=test' .. testoutput:: from beem import Steem from beem.transactionbuilder import TransactionBuilder from beembase import operations from beem.HiveSigner import HiveSigner from pprint import pprint stm = Steem(nobroadcast=True, unsigned=True) hs = HiveSigner(blockchain_instance=stm) tx = TransactionBuilder(blockchain_instance=stm) op = operations.Transfer(**{"from": 'test', "to": 'test1', "amount": '1.000 HIVE', "memo": 'test'}) tx.appendOps(op) pprint(hs.url_from_tx(tx.json())) .. testcode:: 'https://hivesigner.com/sign/transfer?from=test&to=test1&amount=1.000+HIVE&memo=test' """ def __init__(self, blockchain_instance=None, *args, **kwargs): if blockchain_instance is None: if kwargs.get("steem_instance"): blockchain_instance = kwargs["steem_instance"] elif kwargs.get("hive_instance"): blockchain_instance = kwargs["hive_instance"] self.blockchain = blockchain_instance or shared_blockchain_instance() self.access_token = None config = self.blockchain.config self.get_refresh_token = kwargs.get("get_refresh_token", False) self.hot_sign_redirect_uri = kwargs.get("hot_sign_redirect_uri", config["hot_sign_redirect_uri"]) if self.hot_sign_redirect_uri == "": self.hot_sign_redirect_uri = None self.client_id = kwargs.get("client_id", config["hs_client_id"]) self.scope = kwargs.get("scope", "login") self.hs_oauth_base_url = kwargs.get("hs_oauth_base_url", config["hs_oauth_base_url"]) self.hs_api_url = kwargs.get("hs_api_url", config["hs_api_url"]) if "token" in kwargs and len(kwargs["token"]) > 0: from beemstorage import InRamPlainTokenStore self.store = InRamPlainTokenStore() token = kwargs["token"] self.set_access_token(token) name = self.me()["user"] self.setToken({name: token}) else: """ If no keys are provided manually we load the SQLite keyStorage """ from beemstorage import SqliteEncryptedTokenStore self.store = kwargs.get( "token_store", SqliteEncryptedTokenStore(config=config, **kwargs), ) @property def headers(self): return {'Authorization': self.access_token} def setToken(self, loadtoken): """ This method is strictly only for in memory token that are passed to Wallet/Steem with the ``token`` argument """ log.debug( "Force setting of private token. Not using the wallet database!") if not isinstance(loadtoken, (dict)): raise ValueError("token must be a dict variable!") for name in loadtoken: self.store.add(loadtoken[name], name) def is_encrypted(self): """ Is the key store encrypted? """ return self.store.is_encrypted() def unlock(self, pwd): """ Unlock the wallet database """ unlock_ok = None if self.store.is_encrypted(): unlock_ok = self.store.unlock(pwd) return unlock_ok def lock(self): """ Lock the wallet database """ lock_ok = False if self.store.is_encrypted(): lock_ok = self.store.lock() return lock_ok def unlocked(self): """ Is the wallet database unlocked? """ unlocked = True if self.store.is_encrypted(): unlocked = not self.store.locked() return unlocked def locked(self): """ Is the wallet database locked? """ if self.store.is_encrypted(): return self.store.locked() else: return False def changePassphrase(self, new_pwd): """ Change the passphrase for the wallet database """ self.store.change_password(new_pwd) def created(self): """ Do we have a wallet database already? """ if len(self.store.getPublicKeys()): # Already keys installed return True else: return False def create(self, pwd): """ Alias for :func:`newWallet` :param str pwd: Passphrase for the created wallet """ self.newWallet(pwd) def newWallet(self, pwd): """ Create a new wallet database :param str pwd: Passphrase for the created wallet """ if self.created(): raise WalletExists("You already have created a wallet!") self.store.unlock(pwd) def addToken(self, name, token): if str(name) in self.store: raise KeyAlreadyInStoreException("Token already in the store") self.store.add(str(token), str(name)) def getTokenForAccountName(self, name): """ Obtain the private token for a given public name :param str name: Public name """ if str(name) not in self.store: raise MissingKeyError return self.store.getPrivateKeyForPublicKey(str(name)) def removeTokenFromPublicName(self, name): """ Remove a token from the wallet database :param str name: token to be removed """ self.store.delete(str(name)) def getPublicNames(self): """ Return all installed public token """ if self.store is None: return return self.store.getPublicNames() def get_login_url(self, redirect_uri, **kwargs): """ Returns a login url for receiving token from HiveSigner """ client_id = kwargs.get("client_id", self.client_id) scope = kwargs.get("scope", self.scope) get_refresh_token = kwargs.get("get_refresh_token", self.get_refresh_token) params = { "client_id": client_id, "redirect_uri": redirect_uri, "scope": scope, } if get_refresh_token: params.update({ "response_type": "code", }) if PY2: return urljoin( self.hs_oauth_base_url, "authorize?" + urlencode(params).replace('%2C', ',')) else: return urljoin( self.hs_oauth_base_url, "authorize?" + urlencode(params, safe=",")) def get_access_token(self, code): post_data = { "grant_type": "authorization_code", "code": code, "client_id": self.client_id, "client_secret": self.getTokenForAccountName(self.client_id), } r = requests.post( urljoin(self.hs_api_url, "oauth2/token/"), data=post_data ) return r.json() def me(self, username=None): """ Calls the me function from HiveSigner .. code-block:: python from beem.HiveSigner import HiveSigner hs = HiveSigner() hs.steem.wallet.unlock("supersecret-passphrase") hs.me(username="******") """ if username: self.set_username(username) url = urljoin(self.hs_api_url, "me/") r = requests.post(url, headers=self.headers) return r.json() def set_access_token(self, access_token): """ Is needed for :func:`broadcast` and :func:`me` """ self.access_token = access_token def set_username(self, username, permission="posting"): """ Set a username for the next :func:`broadcast` or :func:`me` operation. The necessary token is fetched from the wallet """ if permission != "posting": self.access_token = None return self.access_token = self.getTokenForAccountName(username) def broadcast(self, operations, username=None): """ Broadcast an operation Sample operations: .. code-block:: js [ [ 'vote', { 'voter': 'gandalf', 'author': 'gtg', 'permlink': 'steem-pressure-4-need-for-speed', 'weight': 10000 } ] ] """ url = urljoin(self.hs_api_url, "broadcast/") data = { "operations": operations, } if username: self.set_username(username) headers = self.headers.copy() headers.update({ "Content-Type": "application/json; charset=utf-8", "Accept": "application/json", }) r = requests.post(url, headers=headers, data=json.dumps(data)) try: return r.json() except ValueError: return r.content def refresh_access_token(self, code, scope): post_data = { "grant_type": "refresh_token", "refresh_token": code, "client_id": self.client_id, "client_secret": self.getTokenForAccountName(self.client_id), "scope": scope, } r = requests.post( urljoin(self.hs_api_url, "oauth2/token/"), data=post_data, ) return r.json() def revoke_token(self, access_token): post_data = { "access_token": access_token, } r = requests.post( urljoin(self.hs_api_url, "oauth2/token/revoke"), data=post_data ) return r.json() def update_user_metadata(self, metadata): put_data = { "user_metadata": metadata, } r = requests.put( urljoin(self.hs_api_url, "me/"), data=put_data, headers=self.headers) return r.json() def url_from_tx(self, tx, redirect_uri=None): """ Creates a link for broadcasting an operation :param dict tx: includes the operation, which should be broadcast :param str redirect_uri: Redirects to this uri, when set """ if not isinstance(tx, dict): tx = tx.json() if "operations" not in tx or not tx["operations"]: return '' urls = [] operations = tx["operations"] for op in operations: operation = op[0] params = op[1] for key in params: value = params[key] if isinstance(value, list) and len(value) == 3: try: amount = Amount(value, blockchain_instance=self.blockchain) params[key] = str(amount) except: amount = None elif isinstance(value, bool): if value: params[key] = 1 else: params[key] = 0 urls.append(self.create_hot_sign_url(operation, params, redirect_uri=redirect_uri)) if len(urls) == 1: return urls[0] else: return urls def create_hot_sign_url(self, operation, params, redirect_uri=None): """ Creates a link for broadcasting an operation :param str operation: operation name (e.g.: vote) :param dict params: operation dict params :param str redirect_uri: Redirects to this uri, when set """ if not isinstance(operation, str) or not isinstance(params, dict): raise ValueError("Invalid Request.") base_url = self.hs_api_url.replace("https://api.", "https://").replace("/api", "") if redirect_uri == "": redirect_uri = None if redirect_uri is None and self.hot_sign_redirect_uri is not None: redirect_uri = self.hot_sign_redirect_uri if redirect_uri is not None: params.update({"redirect_uri": redirect_uri}) for key in params: if isinstance(params[key], list): params[key] = json.dumps(params[key]) params = urlencode(params) url = urljoin(base_url, "sign/%s" % operation) url += "?" + params return url