Skip to content
Snippets Groups Projects
Commit 64b51c01 authored by Holger Nahrstaedt's avatar Holger Nahrstaedt
Browse files

More function added to cli

account
* set_withdraw_vesting_route added
cli
 * powerdownroute, convert and interest added
steemnoderpc
* error messages improved
* _check_api_name added
* ApiNotSupported when Api is not supported by node but exists
graphenerpc
* error messages improved
rpcutils
* error messages in sleep_and_check_retries improved
unit tests
* new function added
parent 3ac7c459
No related branches found
No related tags found
No related merge requests found
......@@ -1362,6 +1362,38 @@ class Account(BlockchainObject):
return self.steem.finalizeOp(op, account, "active")
def set_withdraw_vesting_route(self,
to,
percentage=100,
account=None,
auto_vest=False):
""" Set up a vesting withdraw route. When vesting shares are
withdrawn, they will be routed to these accounts based on the
specified weights.
:param str to: Recipient of the vesting withdrawal
:param float percentage: The percent of the withdraw to go
to the 'to' account.
:param str account: (optional) the vesting account
:param bool auto_vest: Set to true if the from account
should receive the VESTS as VESTS, or false if it should
receive them as STEEM. (defaults to ``False``)
"""
if not account:
account = self
if not account:
raise ValueError("You need to provide an account")
STEEMIT_100_PERCENT = 10000
STEEMIT_1_PERCENT = (STEEMIT_100_PERCENT / 100)
op = operations.Set_withdraw_vesting_route(
**{
"from_account": account["name"],
"to_account": to,
"percent": int(percentage * STEEMIT_1_PERCENT),
"auto_vest": auto_vest
})
return self.steem.finalizeOp(op, account, "active")
def allow(
self, foreign, weight=None, permission="posting",
account=None, threshold=None, **kwargs
......
......@@ -395,6 +395,49 @@ def powerdown(amount, password, account):
print(tx)
@cli.command()
@click.argument('to', nargs=1)
@click.option('--percentage', default=100, help='The percent of the withdraw to go to the "to" account')
@click.option('--password', prompt=True, hide_input=True,
confirmation_prompt=False, help='Password to unlock wallet')
@click.option('--account', '-a', help='Powerup from this account')
@click.option('--auto_vest', help='Set to true if the from account should receive the VESTS as'
'VESTS, or false if it should receive them as STEEM.', is_flag=True)
def powerdownroute(to, percentage, password, account, auto_vest):
"""Setup a powerdown route"""
stm = shared_steem_instance()
if not account:
account = stm.config["default_account"]
if not unlock_wallet(stm, password):
return
acc = Account(account, steem_instance=stm)
tx = acc.set_withdraw_vesting_route(to, percentage, auto_vest=auto_vest)
tx = json.dumps(tx, indent=4)
print(tx)
@cli.command()
@click.argument('amount', nargs=1)
@click.option('--password', prompt=True, hide_input=True,
confirmation_prompt=False, help='Password to unlock wallet')
@click.option('--account', '-a', help='Powerup from this account')
def convert(amount, password, account):
"""Convert STEEMDollars to Steem (takes a week to settle)"""
stm = shared_steem_instance()
if not account:
account = stm.config["default_account"]
if not unlock_wallet(stm, password):
return
acc = Account(account, steem_instance=stm)
try:
amount = float(amount)
except:
amount = str(amount)
tx = acc.convert(amount)
tx = json.dumps(tx, indent=4)
print(tx)
@cli.command()
@click.option('--password', prompt=True, hide_input=True,
confirmation_prompt=True)
......@@ -406,8 +449,7 @@ def changewalletpassphrase(password):
@cli.command()
@click.option(
'--account', '-a', multiple=True)
@click.argument('account', nargs=-1)
def balance(account):
""" Shows balance
"""
......@@ -447,6 +489,34 @@ def balance(account):
print(t)
@cli.command()
@click.argument('account', nargs=-1, required=False)
def interest(account):
""" Get information about interest payment
"""
stm = shared_steem_instance()
if not account:
if "default_account" in stm.config:
account = [stm.config["default_account"]]
t = PrettyTable([
"Account", "Last Interest Payment", "Next Payment",
"Interest rate", "Interest"
])
t.align = "r"
for a in account:
a = Account(a, steem_instance=stm)
i = a.interest()
t.add_row([
a["name"],
i["last_payment"],
"in %s" % (i["next_payment_duration"]),
"%.1f%%" % i["interest_rate"],
"%.3f %s" % (i["interest"], "SBD"),
])
print(t)
@cli.command()
@click.argument('objects', nargs=-1)
def info(objects):
......
......@@ -41,6 +41,10 @@ class NoApiWithName(RPCError):
pass
class ApiNotSupported(RPCError):
pass
class UnhandledRPCError(RPCError):
pass
......
......@@ -57,7 +57,7 @@ class SteemNodeRPC(GrapheneRPC):
self.error_cnt_call = cnt
reply = super(SteemNodeRPC, self).rpcexec(payload)
if self.next_node_on_empty_reply and not bool(reply) and self.n_urls > 1:
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str("Empty reply"), sleep=False)
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str("Empty reply"), sleep=False, call_retry=True)
self.error_cnt[self.url] += 1
self.next()
cnt = 0
......@@ -69,7 +69,7 @@ class SteemNodeRPC(GrapheneRPC):
return reply
except exceptions.RPCErrorDoRetry as e:
msg = exceptions.decodeRPCErrorMsg(e).strip()
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg))
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg), call_retry=True)
doRetry = True
except exceptions.RPCError as e:
doRetry = self._check_error_message(e, cnt)
......@@ -94,19 +94,25 @@ class SteemNodeRPC(GrapheneRPC):
elif re.search("Could not find method", msg):
raise exceptions.NoMethodWithName(msg)
elif re.search("Could not find API", msg):
raise exceptions.NoApiWithName(msg)
if self._check_api_name(msg):
raise exceptions.ApiNotSupported(msg)
else:
raise exceptions.NoApiWithName(msg)
elif re.search("irrelevant signature included", msg):
raise exceptions.UnnecessarySignatureDetected(msg)
elif re.search("WinError", msg):
raise exceptions.RPCError(msg)
elif re.search("Unable to acquire database lock", msg):
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg))
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg), call_retry=True)
doRetry = True
elif re.search("Internal Error", msg):
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg))
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg), call_retry=True)
doRetry = True
elif re.search("!check_max_block_age", str(e)):
sleep_and_check_retries(self.num_retries_call, cnt, self.url, str(msg), sleep=False)
if self.n_urls == 1:
raise exceptions.UnhandledRPCError(msg)
self.error_cnt[self.url] += 1
sleep_and_check_retries(self.num_retries, self.error_cnt[self.url], self.url, str(msg), sleep=False)
self.next()
doRetry = True
elif re.search("out_of_rangeEEEE: unknown key", msg) or re.search("unknown key:unknown key", msg):
......@@ -117,6 +123,37 @@ class SteemNodeRPC(GrapheneRPC):
raise e
return doRetry
def _check_api_name(self, msg):
error_start = "Could not find API "
if re.search(error_start + "account_history_api", msg):
return True
elif re.search(error_start + "tags_api", msg):
return True
elif re.search(error_start + "database_api", msg):
return True
elif re.search(error_start + "market_history_api", msg):
return True
elif re.search(error_start + "block_api", msg):
return True
elif re.search(error_start + "account_by_key_api", msg):
return True
elif re.search(error_start + "chain_api", msg):
return True
elif re.search(error_start + "follow_api", msg):
return True
elif re.search(error_start + "condenser_api", msg):
return True
elif re.search(error_start + "debug_node_api", msg):
return True
elif re.search(error_start + "witness_api", msg):
return True
elif re.search(error_start + "test_api", msg):
return True
elif re.search(error_start + "network_broadcast_api", msg):
return True
else:
return False
def get_account(self, name, **kwargs):
""" Get full account details from account name
......
......@@ -254,7 +254,6 @@ class GrapheneRPC(object):
reply = {}
while True:
self.error_cnt_call += 1
try:
if self.current_rpc == 0 or self.current_rpc == 2:
reply = self.ws_send(json.dumps(payload, ensure_ascii=False).encode('utf8'))
......@@ -262,7 +261,7 @@ class GrapheneRPC(object):
reply = self.request_send(json.dumps(payload, ensure_ascii=False).encode('utf8'))
if not bool(reply):
self.error_cnt[self.url] += 1
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, "Empty Reply")
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, "Empty Reply", call_retry=True)
self.rpcconnect()
else:
break
......@@ -273,10 +272,10 @@ class GrapheneRPC(object):
self.rpcconnect(next_url=False)
except ConnectionError as e:
self.error_cnt[self.url] += 1
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, str(e))
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, str(e), call_retry=True)
except Exception as e:
self.error_cnt[self.url] += 1
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, str(e))
sleep_and_check_retries(self.num_retries_call, self.error_cnt_call, self.url, str(e), call_retry=True)
# retry
self.rpcconnect()
......
......@@ -83,13 +83,16 @@ def get_api_name(appbase, *args, **kwargs):
return api_name
def sleep_and_check_retries(num_retries, cnt, url, errorMsg=None, sleep=True):
def sleep_and_check_retries(num_retries, cnt, url, errorMsg=None, sleep=True, call_retry=False):
"""Sleep and check if num_retries is reached"""
if errorMsg:
log.warning("Error: {}\n".format(errorMsg))
log.warning("Error: {}".format(errorMsg))
if call_retry:
log.warning("Retry RPC Call on node: %s (%d/%d) \n" % (url, cnt, num_retries))
else:
log.warning("Lost connection or internal error on node: %s (%d/%d) \n" % (url, cnt, num_retries))
if (num_retries >= 0 and cnt > num_retries):
raise NumRetriesReached()
log.warning("\nLost connection or internal error on node: %s (%d/%d) " % (url, cnt, num_retries))
if not sleep:
return
if cnt < 1:
......
......@@ -35,7 +35,12 @@ class Testcases(unittest.TestCase):
def test_balance(self):
runner = CliRunner()
result = runner.invoke(cli, ['balance', '-atest'])
result = runner.invoke(cli, ['balance', 'beem', 'beem1'])
self.assertEqual(result.exit_code, 0)
def test_interest(self):
runner = CliRunner()
result = runner.invoke(cli, ['interest', 'beem', 'beem1'])
self.assertEqual(result.exit_code, 0)
def test_config(self):
......@@ -95,15 +100,25 @@ class Testcases(unittest.TestCase):
def test_upvote(self):
runner = CliRunner()
result = runner.invoke(cli, ['upvote', '@test/abcd', '--weight 100'], input='test\n')
result = runner.invoke(cli, ['upvote', '@test/abcd', '--weight 100' '--password test'], input='test\n')
self.assertEqual(result.exit_code, 0)
def test_downvote(self):
runner = CliRunner()
result = runner.invoke(cli, ['downvote', '@test/abcd', '--weight 100'], input='test\n')
result = runner.invoke(cli, ['downvote', '@test/abcd', '--weight 100' '--password test'], input='test\n')
self.assertEqual(result.exit_code, 0)
def test_transfer(self):
runner = CliRunner()
result = runner.invoke(cli, ['transfer', 'beem1', '1', 'SBD', 'test'], input='test\n')
result = runner.invoke(cli, ['transfer', 'beem1', '1', 'SBD', 'test' '--password test'], input='test\n')
self.assertEqual(result.exit_code, 0)
def test_powerdownroute(self):
runner = CliRunner()
result = runner.invoke(cli, ['powerdownroute', 'beem1', '--password test'], input='test\n')
self.assertEqual(result.exit_code, 0)
def test_convert(self):
runner = CliRunner()
result = runner.invoke(cli, ['convert', '1', '--password test'], input='test\n')
self.assertEqual(result.exit_code, 0)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment