Skip to content

Commit

Permalink
Several improvements and fixes
Browse files Browse the repository at this point in the history
Account
* Example added for transfer
Nodelist
* Doc added
Steem
* steemconnect integration improved and use_sc2 added
* Docu improved
SteemConnect
* compatible with py2.7
* several improvements
* Examles added
Transactionbuilder
* Integration of steemconnect improved
Wallet
* fix bug
SteemNodeRPC
* fix old and removed parameter n_urls
Doc
* Fix bug in tutorial page
  • Loading branch information
holgern committed May 29, 2018
1 parent 7c8b535 commit 97711e5
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 33 deletions.
12 changes: 12 additions & 0 deletions beem/account.py
Expand Up @@ -1549,6 +1549,18 @@ def transfer(self, to, amount, asset, memo="", account=None, **kwargs):
messaging
:param str account: (optional) the source account for the transfer
if not ``default_account``
transfer example:
.. code-block:: python
from beem.account import Account
from beem import Steem
active_wif = "5xxxx"
stm = Steem(keys=[active_wif])
acc = Account("test", steem_instance=stm)
acc.transfer("test1", 1, "STEEM", "test")
"""

if not account:
Expand Down
15 changes: 12 additions & 3 deletions beem/nodelist.py
Expand Up @@ -10,6 +10,15 @@


class NodeList(list):
""" Returns a node list
.. code-block:: python
from beem.nodelist import NodeList
n = NodeList()
nodes_urls = n.get_nodes()
"""
def __init__(self):
nodes = [
{
Expand Down Expand Up @@ -59,7 +68,7 @@ def __init__(self):
"version": "0.19.3",
"type": "normal",
"owner": "ausbitbank",
"score": 175
"score": 125
},
{
"url": "https://rpc.steemviz.com",
Expand Down Expand Up @@ -171,14 +180,14 @@ def __init__(self):
"version": "0.19.3",
"type": "normal",
"owner": "followbtcnews",
"score": 10
"score": 120
},
{
"url": "https://steemd.minnowsupportproject.org",
"version": "0.19.3",
"type": "normal",
"owner": "followbtcnews",
"score": 10
"score": 90
},
{
"url": "https://rpc.curiesteem.com",
Expand Down
18 changes: 16 additions & 2 deletions beem/steem.py
Expand Up @@ -63,6 +63,9 @@ class Steem(object):
NumRetriesReached is raised. Disabled for -1. (default is -1)
:param int num_retries_call: Repeat num_retries_call times a rpc call on node error (default is 5)
:param int timeout: Timeout setting for https nodes (default is 60)
:param bool use_sc2: When True, a steemconnect object is created. Can be used for
broadcast posting op or creating hot_links (default is False)
:param SteemConnect steemconnect: A SteemConnect object can be set manually, set use_sc2 to True
Three wallet operation modes are possible:
Expand Down Expand Up @@ -138,6 +141,10 @@ def __init__(self,
NumRetriesReached is raised. Disabled for -1. (default is -1)
:param int num_retries_call: Repeat num_retries_call times a rpc call on node error (default is 5)
:param int timeout: Timeout setting for https nodes (default is 60)
:param bool use_sc2: When True, a steemconnect object is created. Can be used for broadcast
posting op or creating hot_links (default is False)
:param SteemConnect steemconnect: A SteemConnect object can be set manually, set use_sc2 to True
"""

self.rpc = None
Expand All @@ -149,8 +156,7 @@ def __init__(self,
self.expiration = int(kwargs.get("expiration", 30))
self.bundle = bool(kwargs.get("bundle", False))
self.steemconnect = kwargs.get("steemconnect", None)
if self.steemconnect is not None and not isinstance(self.steemconnect, SteemConnect):
raise ValueError("steemconnect musst be SteemConnect object")
self.use_sc2 = bool(kwargs.get("use_sc2", False))
self.blocking = kwargs.get("blocking", False)

# Store config for access through other Classes
Expand All @@ -174,6 +180,14 @@ def __init__(self,

self.wallet = Wallet(steem_instance=self, **kwargs)

# set steemconnect
if self.steemconnect is not None and not isinstance(self.steemconnect, SteemConnect):
raise ValueError("steemconnect musst be SteemConnect object")
if self.steemconnect is None and self.use_sc2:
self.steemconnect = SteemConnect(steem_instance=self, **kwargs)
elif self.steemconnect is not None and not self.use_sc2:
self.use_sc2 = True

# -------------------------------------------------------------------------
# Basic Calls
# -------------------------------------------------------------------------
Expand Down
80 changes: 66 additions & 14 deletions beem/steemconnect.py
Expand Up @@ -5,7 +5,10 @@
from __future__ import unicode_literals
from builtins import str
import json
import urllib.parse
try:
from urllib.parse import urlparse, urlencode, urljoin
except ImportError:
from urlparse import urlparse, urlencode, urljoin
import requests
from .storage import configStorage as config
from beem.instance import shared_steem_instance
Expand All @@ -27,15 +30,43 @@ class SteemConnect(object):
sc2 = SteemConnect(client_id="beem.app")
steem = Steem(steemconnect=sc2)
steem.wallet.unlock("supersecret-passphrase")
post = Comment("author/permlink", steem_instance=stm)
post = Comment("author/permlink", steem_instance=steem)
post.upvote(voter="test") # replace "test" with your account
hot_sign example:
.. code-block:: python
from beem import Steem
from beem.steemconnect import SteemConnect
from beem.comment import Comment
sc2 = SteemConnect(client_id="beem.app", hot_sign=True)
steem = Steem(steemconnect=sc2)
post = Comment("author/permlink", steem_instance=steem)
post.upvote(voter="test") # replace "test" with your account
transfer example:
.. testoutput::
from beem import Steem
from beem.steemconnect import SteemConnect
from beem.account import Account
from pprint import pprint
steem = Steem(use_sc2=True, hot_sign=True)
acc = Account("test", steem_instance=steem)
pprint(acc.transfer("test1", 1, "STEEM", "test"))
.. testcode::
'https://v2.steemconnect.com/sign/transfer?from=test&to=test1&amount=1.000+STEEM&memo=test'
"""

def __init__(self, steem_instance=None, *args, **kwargs):
self.steem = steem_instance or shared_steem_instance()
self.access_token = None
self.get_refresh_token = kwargs.get("get_refresh_token", False)
self.hot_sign = kwargs.get("hot_sign", False)
self.hot_sign_redirect_uri = kwargs.get("hot_sign_redirect_uri", None)
self.client_id = kwargs.get("client_id", config["sc2_client_id"])
self.scope = kwargs.get("scope", config["sc2_scope"])
self.oauth_base_url = kwargs.get("oauth_base_url", config["oauth_base_url"])
Expand All @@ -56,9 +87,9 @@ def get_login_url(self, redirect_uri):
"response_type": "code",
})

return urllib.parse.urljoin(
return urljoin(
self.oauth_base_url,
"authorize?" + urllib.parse.urlencode(params, safe=","))
"authorize?" + urlencode(params, safe=","))

def get_access_token(self, code):
post_data = {
Expand All @@ -69,7 +100,7 @@ def get_access_token(self, code):
}

r = requests.post(
urllib.parse.urljoin(self.sc2_api_url, "oauth2/token/"),
urljoin(self.sc2_api_url, "oauth2/token/"),
data=post_data
)

Expand All @@ -78,7 +109,7 @@ def get_access_token(self, code):
def me(self, username=None):
if username:
self.set_username(username)
url = urllib.parse.urljoin(self.sc2_api_url, "me/")
url = urljoin(self.sc2_api_url, "me/")
r = requests.post(url, headers=self.headers)
return r.json()

Expand All @@ -87,12 +118,27 @@ def set_access_token(self, access_token):
"""
self.access_token = access_token

def set_username(self, username):
def set_username(self, username, permission):
""" Set a username for the next broadcast() or me operation()
The necessary token is fetched from the wallet
"""
if self.hot_sign or permission != "posting":
self.access_token = None
return
self.access_token = self.steem.wallet.getTokenForAccountName(username)

def boadcast_or_hot_sign(self, operations, username=None):
if self.hot_sign or self.access_token is None:
urls = []
for op in operations:
urls.append(self.create_hot_sign_url(op[0], op[1]))
if len(urls) == 1:
return urls[0]
else:
return urls
else:
return self.broadcast(operations, username=None)

def broadcast(self, operations, username=None):
""" Broadcast a operations
Expand All @@ -112,7 +158,7 @@ def broadcast(self, operations, username=None):
]
"""
url = urllib.parse.urljoin(self.sc2_api_url, "broadcast/")
url = urljoin(self.sc2_api_url, "broadcast/")
data = {
"operations": operations,
}
Expand Down Expand Up @@ -140,7 +186,7 @@ def refresh_access_token(self, code, scope):
}

r = requests.post(
urllib.parse.urljoin(self.sc2_api_url, "oauth2/token/"),
urljoin(self.sc2_api_url, "oauth2/token/"),
data=post_data,
)

Expand All @@ -152,7 +198,7 @@ def revoke_token(self, access_token):
}

r = requests.post(
urllib.parse.urljoin(self.sc2_api_url, "oauth2/token/revoke"),
urljoin(self.sc2_api_url, "oauth2/token/revoke"),
data=post_data
)

Expand All @@ -163,12 +209,12 @@ def update_user_metadata(self, metadata):
"user_metadata": metadata,
}
r = requests.put(
urllib.parse.urljoin(self.sc2_api_url, "me/"),
urljoin(self.sc2_api_url, "me/"),
data=put_data, headers=self.headers)

return r.json()

def hot_sign(self, operation, params, redirect_uri=None):
def create_hot_sign_url(self, operation, params, redirect_uri=None):
""" Creates a link for broadcasting a operation
:param str operation: operation name (e.g.: vote)
Expand All @@ -180,11 +226,17 @@ def hot_sign(self, operation, params, redirect_uri=None):

base_url = self.sc2_api_url.replace("/api", "")

if not redirect_uri and self.hot_sign_redirect_uri:
redirect_uri = self.hot_sign_redirect_uri
if redirect_uri:
params.update({"redirect_uri": redirect_uri})

params = urllib.parse.urlencode(params)
url = urllib.parse.urljoin(base_url, "sign/%s" % operation)
params = urlencode(params)
url = urljoin(base_url, "sign/%s" % operation)
url += "?" + params

return url


# https://steemconnect.com/authorize/@steemauto/?redirect_uri=https://steemauto.com/dash.php
# https://steemconnect.com/oauth2/authorize?client_id=steem.app&redirect_uri=https://steemauto.com/callback.php&scope=login
18 changes: 11 additions & 7 deletions beem/transactionbuilder.py
Expand Up @@ -149,11 +149,15 @@ def appendSigner(self, account, permission):

required_treshold = account[permission]["weight_threshold"]

if self.steem.wallet.locked():
raise WalletLocked()
if self.steem.steemconnect is not None:
self.steem.steemconnect.set_username(account["name"])
if self.steem.use_sc2:
if not self.steem.steemconnect.hot_sign and permission == "posting":
if self.steem.wallet.locked():
raise WalletLocked()
self.steem.steemconnect.set_username(account["name"], permission)
return
else:
if self.steem.wallet.locked():
raise WalletLocked()

def fetchkeys(account, perm, level=0):
if level > 2:
Expand Down Expand Up @@ -266,7 +270,7 @@ def sign(self, reconstruct_tx=True):
self.constructTx()
if "operations" not in self or not self["operations"]:
return
if self.steem.steemconnect is not None:
if self.steem.use_sc2:
return
# We need to set the default prefix, otherwise pubkeys are
# presented wrongly!
Expand Down Expand Up @@ -368,8 +372,8 @@ def broadcast(self, max_block_age=-1):
return ret
# Broadcast
try:
if self.steem.steemconnect is not None:
ret = self.steem.steemconnect.broadcast(self["operations"])
if self.steem.use_sc2:
ret = self.steem.steemconnect.boadcast_or_hot_sign(self["operations"])
elif self.steem.blocking:
ret = self.steem.rpc.broadcast_transaction_synchronous(
args, api="network_broadcast")
Expand Down
5 changes: 3 additions & 2 deletions beem/wallet.py
Expand Up @@ -117,7 +117,7 @@ def __init__(self, steem_instance=None, *args, **kwargs):
# Compatibility after name change from wif->keys
if "wif" in kwargs and "keys" not in kwargs:
kwargs["keys"] = kwargs["wif"]

master_password_set = False
if "keys" in kwargs:
self.setKeys(kwargs["keys"])
else:
Expand All @@ -127,6 +127,7 @@ def __init__(self, steem_instance=None, *args, **kwargs):
from .storage import (keyStorage,
MasterPassword)
self.MasterPassword = MasterPassword
master_password_set = True
self.keyStorage = keyStorage

if "token" in kwargs:
Expand All @@ -136,7 +137,7 @@ def __init__(self, steem_instance=None, *args, **kwargs):
keyStorage
"""
from .storage import tokenStorage
if MasterPassword is None:
if not master_password_set:
from .storage import MasterPassword
self.MasterPassword = MasterPassword
self.tokenStorage = tokenStorage
Expand Down
8 changes: 4 additions & 4 deletions beemapi/steemnoderpc.py
Expand Up @@ -61,7 +61,7 @@ def rpcexec(self, payload):
try:
# Forward call to GrapheneWebsocketRPC and catch+evaluate errors
reply = super(SteemNodeRPC, self).rpcexec(payload)
if self.next_node_on_empty_reply and not bool(reply) and self.n_urls > 1:
if self.next_node_on_empty_reply and not bool(reply) and self.nodes.working_nodes_count > 1:
self._retry_on_next_node("Empty Reply")
doRetry = True
self.next_node_on_empty_reply = True
Expand All @@ -74,7 +74,7 @@ def rpcexec(self, payload):
self.nodes.sleep_and_check_retries(str(msg), call_retry=True)
doRetry = True
except exceptions.CallRetriesReached:
if self.n_urls > 1:
if self.nodes.working_nodes_count > 1:
self._retry_on_next_node(msg)
doRetry = True
else:
Expand All @@ -84,7 +84,7 @@ def rpcexec(self, payload):
doRetry = self._check_error_message(e, self.error_cnt_call)
except exceptions.CallRetriesReached:
msg = exceptions.decodeRPCErrorMsg(e).strip()
if self.n_urls > 1:
if self.nodes.working_nodes_count > 1:
self._retry_on_next_node(msg)
doRetry = True
else:
Expand Down Expand Up @@ -126,7 +126,7 @@ def _check_error_message(self, e, cnt):
self.nodes.sleep_and_check_retries(str(msg), call_retry=True)
doRetry = True
elif re.search("!check_max_block_age", str(e)):
if self.n_urls == 1:
if self.nodes.working_nodes_count == 1:
raise exceptions.UnhandledRPCError(msg)
self.nodes.increase_error_cnt()
self.nodes.sleep_and_check_retries(str(msg), sleep=False)
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials.rst
Expand Up @@ -45,7 +45,7 @@ one comment operation from each sender.
# Upvote post with 25%
c.upvote(25, voter=account)
pprint(testnet.broadcast())
pprint(stm.broadcast())
Use nobroadcast for testing
Expand Down

0 comments on commit 97711e5

Please sign in to comment.