From ebc508a12abd4b314697bbc998471dddfae40ec0 Mon Sep 17 00:00:00 2001
From: Holger <holger@nahrstaedt.de>
Date: Wed, 26 Sep 2018 00:14:53 +0200
Subject: [PATCH] Release of 0.20.0

* Fully supporting hf20
* add get_resource_params(), get_resource_pool(), claim_account(), create_claimed_account() to Steem
* fix 30x fee for create_account
* add find_rc_accounts() to Blockchain
* get_rc(), get_rc_manabar(), get_manabar() added to Account
* get_voting_power() fixed
---
 CHANGELOG.rst      |   9 ++
 beem/account.py    |  63 +++++++++---
 beem/blockchain.py |  28 ++++++
 beem/cli.py        |   8 +-
 beem/steem.py      | 243 ++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 331 insertions(+), 20 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 589952d8..4ca2b310 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,5 +1,14 @@
 Changelog
 =========
+0.20.0
+------
+* Fully supporting hf20
+* add get_resource_params(), get_resource_pool(), claim_account(), create_claimed_account() to Steem
+* fix 30x fee for create_account
+* add find_rc_accounts() to Blockchain
+* get_rc(), get_rc_manabar(), get_manabar() added to Account
+* get_voting_power() fixed
+
 0.19.57
 --------
 * last hf19 release
diff --git a/beem/account.py b/beem/account.py
index e607552d..922dfeee 100644
--- a/beem/account.py
+++ b/beem/account.py
@@ -221,6 +221,20 @@ class Account(BlockchainObject):
         """Deprecated, please use get_similar_account_names"""
         return self.get_similar_account_names(limit=limit)
 
+    def get_rc(self):
+        """Return RC of account"""
+        b = Blockchain(steem_instance=self.steem)
+        return b.find_rc_accounts(self["name"])
+
+    def get_rc_manabar(self):
+        """Returns current_mana and max_mana for RC"""
+        rc_param = self.get_rc()
+        max_mana = int(rc_param["max_rc"])
+        last_update = datetime.utcfromtimestamp(rc_param["rc_manabar"]["last_update_time"])
+        diff_in_seconds = (addTzInfo(datetime.utcnow()) - addTzInfo(last_update)).total_seconds()
+        current_mana = rc_param["rc_manabar"]["current_mana"] + diff_in_seconds * max_mana / (5 * 24 * 60 * 60)
+        return {"current_mana": current_mana, "max_mana": max_mana}
+
     def get_similar_account_names(self, limit=5):
         """ Returns ``limit`` account names similar to the current account
             name as a list
@@ -289,6 +303,11 @@ class Account(BlockchainObject):
             used_kb = bandwidth["used"] / 1024
             allocated_mb = bandwidth["allocated"] / 1024 / 1024
         last_vote_time_str = formatTimedelta(addTzInfo(datetime.utcnow()) - self["last_vote_time"])
+        try:
+            rc_mana = self.get_rc_manabar()
+        except:
+            rc_mana = None
+
         if use_table:
             t = PrettyTable(["Key", "Value"])
             t.align = "l"
@@ -302,6 +321,9 @@ class Account(BlockchainObject):
             if bandwidth["allocated"] > 0:
                 t.add_row(["Remaining Bandwidth", "%.2f %%" % (remaining)])
                 t.add_row(["used/allocated Bandwidth", "(%.0f kb of %.0f mb)" % (used_kb, allocated_mb)])
+            if rc_mana is not None:
+                t.add_row(["Remaining RC", "%.2f %%" % (rc_mana["current_mana"] / rc_mana["max_mana"] * 100)])
+                t.add_row(["used/allocated RC", "(%.0f of %.0f)" % (rc_mana["current_mana"], rc_mana["max_mana"])])
             if return_str:
                 return t.get_string(**kwargs)
             else:
@@ -318,7 +340,11 @@ class Account(BlockchainObject):
             if bandwidth["allocated"] > 0:
                 ret += "--- Bandwidth ---\n"
                 ret += "Remaining: %.2f %%" % (remaining)
-                ret += " (%.0f kb of %.0f mb)" % (used_kb, allocated_mb)
+                ret += " (%.0f kb of %.0f mb)\n" % (used_kb, allocated_mb)
+            if rc_mana is not None:
+                ret += "--- RC manabar ---\n"
+                ret += "Remaining: %.2f %%" % (rc_mana["current_mana"] / rc_mana["max_mana"] * 100)
+                ret += " (%.0f of %.0f)" % (rc_mana["current_mana"], rc_mana["max_mana"])
             if return_str:
                 return ret
             print(ret)
@@ -337,38 +363,51 @@ class Account(BlockchainObject):
             rep = int(self['reputation'])
         return reputation_to_score(rep)
 
+    def get_manabar(self):
+        """"Return manabar"""
+        max_mana = int(self.get_vests())
+        last_update = datetime.utcfromtimestamp(self["voting_manabar"]["last_update_time"])
+        diff_in_seconds = (addTzInfo(datetime.utcnow()) - addTzInfo(last_update)).total_seconds()
+        current_mana = int(self["voting_manabar"]["current_mana"]) + diff_in_seconds * max_mana / (5 * 24 * 60 * 60)
+        return {"current_mana": current_mana, "max_mana": max_mana}
+
     def get_voting_power(self, with_regeneration=True):
         """ Returns the account voting power in the range of 0-100%
         """
         if with_regeneration:
             regenerated_vp = 0
-            if "last_vote_time" in self:
+            if "voting_manabar" in self:
+                last_vote_time = datetime.utcfromtimestamp(self["voting_manabar"]["last_update_time"])
+                diff_in_seconds = (addTzInfo(datetime.utcnow()) - addTzInfo(last_vote_time)).total_seconds()
+                regenerated_vp = diff_in_seconds * STEEM_100_PERCENT / STEEM_VOTING_MANA_REGENERATION_SECONDS / 100
+            elif "last_vote_time" in self:
                 last_vote_time = self["last_vote_time"]
                 diff_in_seconds = (addTzInfo(datetime.utcnow()) - (last_vote_time)).total_seconds()
                 regenerated_vp = diff_in_seconds * STEEM_100_PERCENT / STEEM_VOTE_REGENERATION_SECONDS / 100
-            elif "voting_manabar" in self:
-                last_vote_time = self["voting_manabar"]["last_update_time"]
-                diff_in_seconds = (addTzInfo(datetime.utcnow()) - (last_vote_time)).total_seconds()
-                regenerated_vp = diff_in_seconds * STEEM_100_PERCENT / STEEM_VOTING_MANA_REGENERATION_SECONDS / 100
         else:
             regenerated_vp = 0
         if "voting_power" in self:
             total_vp = (self["voting_power"] / 100 + regenerated_vp)
         elif "voting_manabar" in self:
-            total_vp = int(self["voting_manabar"]["current_mana"]) / 100 + regenerated_vp
+            total_vp = int(self["voting_manabar"]["current_mana"]) / int(self.get_vests()) + regenerated_vp
         if total_vp > 100:
             return 100
         if total_vp < 0:
             return 0
         return total_vp
 
-    def get_steem_power(self, onlyOwnSP=False):
-        """ Returns the account steem power
+    def get_vests(self, only_own_vests=False):
+        """ Returns the account vests
         """
         vests = (self["vesting_shares"])
-        if not onlyOwnSP and "delegated_vesting_shares" in self and "received_vesting_shares" in self:
+        if not only_own_vests and "delegated_vesting_shares" in self and "received_vesting_shares" in self:
             vests = vests - (self["delegated_vesting_shares"]) + (self["received_vesting_shares"])
-        return self.steem.vests_to_sp(vests)
+        return vests
+
+    def get_steem_power(self, onlyOwnSP=False):
+        """ Returns the account steem power
+        """
+        return self.steem.vests_to_sp(self.get_vests(only_own_vests=onlyOwnSP))
 
     def get_voting_value_SBD(self, voting_weight=100, voting_power=None, steem_power=None, not_broadcasted_vote=True):
         """ Returns the account voting value in SBD
@@ -437,7 +476,7 @@ class Account(BlockchainObject):
         missing_vp = voting_power_goal - self.get_voting_power()
         if missing_vp < 0:
             return 0
-        recharge_seconds = missing_vp * 100 * STEEM_VOTE_REGENERATION_SECONDS / STEEM_100_PERCENT
+        recharge_seconds = missing_vp * 100 * STEEM_VOTING_MANA_REGENERATION_SECONDS / STEEM_100_PERCENT
         return timedelta(seconds=recharge_seconds)
 
     def get_recharge_time(self, voting_power_goal=100):
diff --git a/beem/blockchain.py b/beem/blockchain.py
index 347b59c9..5226da94 100644
--- a/beem/blockchain.py
+++ b/beem/blockchain.py
@@ -910,3 +910,31 @@ class Blockchain(object):
                 return account["accounts"]
         else:
             return self.steem.rpc.lookup_accounts(name, limit)
+
+    def find_rc_accounts(self, name):
+        """ Returns limit similar accounts with name as list
+
+        :param str name: account name to search rc params for (can also be a list of accounts)
+        :returns: RC params
+        :rtype: list
+
+        .. code-block:: python
+
+            >>> from beem.blockchain import Blockchain
+            >>> blockchain = Blockchain()
+            >>> ret = blockchain.find_rc_accounts("test")
+            >>> len(ret) == 1
+            True
+
+        """
+        if not self.steem.is_connected():
+            return None
+        self.steem.rpc.set_next_node_on_empty_reply(False)
+        if isinstance(name, list):
+            account = self.steem.rpc.find_rc_accounts({'accounts': name}, api="rc")
+            if bool(account):
+                return account["rc_accounts"]
+        else:
+            account = self.steem.rpc.find_rc_accounts({'accounts': [name]}, api="rc")
+            if bool(account):
+                return account["rc_accounts"][0]
diff --git a/beem/cli.py b/beem/cli.py
index cddf28a1..eaaae6d0 100644
--- a/beem/cli.py
+++ b/beem/cli.py
@@ -2004,12 +2004,12 @@ def witnesscreate(witness, pub_signing_key, maximum_block_size, account_creation
 @cli.command()
 @click.argument('witness', nargs=1)
 @click.argument('wif', nargs=1)
-@click.option('--account_creation_fee', help='Account creation fee')
-@click.option('--account_subsidy_budget', help='Max block size')
-@click.option('--account_subsidy_decay', help='Max block size')
+@click.option('--account_creation_fee', help='Account creation fee (float)')
+@click.option('--account_subsidy_budget', help='Account subisidy per block')
+@click.option('--account_subsidy_decay', help='Per block decay of the account subsidy pool')
 @click.option('--maximum_block_size', help='Max block size')
 @click.option('--sbd_interest_rate', help='SBD interest rate in percent')
-@click.option('--new_signing_key', help='SBD interest rate in percent')
+@click.option('--new_signing_key', help='Set new signing key')
 @click.option('--url', help='Witness URL')
 def witnessproperties(witness, wif, account_creation_fee, account_subsidy_budget, account_subsidy_decay, maximum_block_size, sbd_interest_rate, new_signing_key, url):
     """Update witness properties without pricefeed"""
diff --git a/beem/steem.py b/beem/steem.py
index 4d256f5a..91ea0a48 100644
--- a/beem/steem.py
+++ b/beem/steem.py
@@ -452,6 +452,14 @@ class Steem(object):
             dust_threshold = 0
         return dust_threshold
 
+    def get_resource_params(self):
+        """Returns the resource parameter"""
+        return self.rpc.get_resource_params(api="rc")["resource_params"]
+
+    def get_resource_pool(self):
+        """Returns the resource pool"""
+        return self.rpc.get_resource_pool(api="rc")["resource_pool"]
+
     def rshares_to_sbd(self, rshares, not_broadcasted_vote=False, use_stored_data=True):
         """ Calculates the current SBD value of a vote
         """
@@ -568,7 +576,7 @@ class Steem(object):
         vesting_shares = int(self.sp_to_vests(steem_power, use_stored_data=use_stored_data))
         return self.vests_to_rshares(vesting_shares, voting_power=voting_power, vote_pct=vote_pct, use_stored_data=use_stored_data)
 
-    def vests_to_rshares(self, vests, voting_power=STEEM_100_PERCENT, vote_pct=STEEM_100_PERCENT, use_stored_data=True):
+    def vests_to_rshares(self, vests, voting_power=STEEM_100_PERCENT, vote_pct=STEEM_100_PERCENT, subtract_dust_threshold=True, use_stored_data=True):
         """ Obtain the r-shares from vests
 
             :param number vests: vesting shares
@@ -579,7 +587,7 @@ class Steem(object):
         used_power = self._calc_resulting_vote(voting_power=voting_power, vote_pct=vote_pct, use_stored_data=use_stored_data)
         # calculate vote rshares
         rshares = int(math.copysign(vests * 1e6 * used_power / STEEM_100_PERCENT, vote_pct))
-        if self.hardfork >= 20:
+        if subtract_dust_threshold:
             if abs(rshares) <= self.get_dust_threshold(use_stored_data=use_stored_data):
                 return 0
             rshares -= math.copysign(self.get_dust_threshold(use_stored_data=use_stored_data), vote_pct)
@@ -985,6 +993,233 @@ class Steem(object):
     # -------------------------------------------------------------------------
     # Account related calls
     # -------------------------------------------------------------------------
+    def claim_account(self, creator, fee="0 STEEM", **kwargs):
+        """"Claim account for claimed account creation.
+
+            When fee is 0 STEEM a subsidized account is claimed and can be created
+            later with create_claimed_account.
+            The number of subsidized account is limited.
+
+            :param str creator: which account should pay the registration fee (RC or STEEM)
+                    (defaults to ``default_account``)
+            :param str fee: when set to 0 STEEM, claim account is paid by RC
+        """
+        if not creator and config["default_account"]:
+            creator = config["default_account"]
+        if not creator:
+            raise ValueError(
+                "Not creator account given. Define it with " +
+                "creator=x, or set the default_account using beempy")
+        op = {
+            "fee": Amount(fee, steem_instance=self),
+            "creator": creator["name"],
+            "prefix": self.prefix,
+        }
+        op = operations.Claim_account(**op)
+        return self.finalizeOp(op, creator, "active", **kwargs)
+
+    def create_claimed_account(
+        self,
+        account_name,
+        creator=None,
+        owner_key=None,
+        active_key=None,
+        memo_key=None,
+        posting_key=None,
+        password=None,
+        additional_owner_keys=[],
+        additional_active_keys=[],
+        additional_posting_keys=[],
+        additional_owner_accounts=[],
+        additional_active_accounts=[],
+        additional_posting_accounts=[],
+        storekeys=True,
+        store_owner_key=False,
+        json_meta=None,
+        combine_with_claim_account=False,
+        fee="0 STEEM",
+        **kwargs
+    ):
+        """ Create new claimed account on Steem
+
+            The brainkey/password can be used to recover all generated keys
+            (see `beemgraphenebase.account` for more details.
+
+            By default, this call will use ``default_account`` to
+            register a new name ``account_name`` with all keys being
+            derived from a new brain key that will be returned. The
+            corresponding keys will automatically be installed in the
+            wallet.
+
+            .. warning:: Don't call this method unless you know what
+                          you are doing! Be sure to understand what this
+                          method does and where to find the private keys
+                          for your account.
+
+            .. note:: Please note that this imports private keys
+                      (if password is present) into the wallet by
+                      default when nobroadcast is set to False.
+                      However, it **does not import the owner
+                      key** for security reasons by default.
+                      If you set store_owner_key to True, the
+                      owner key is stored.
+                      Do NOT expect to be able to recover it from
+                      the wallet if you lose your password!
+
+            .. note:: Account creations cost a fee that is defined by
+                       the network. If you create an account, you will
+                       need to pay for that fee!
+
+            :param str account_name: (**required**) new account name
+            :param str json_meta: Optional meta data for the account
+            :param str owner_key: Main owner key
+            :param str active_key: Main active key
+            :param str posting_key: Main posting key
+            :param str memo_key: Main memo_key
+            :param str password: Alternatively to providing keys, one
+                                 can provide a password from which the
+                                 keys will be derived
+            :param array additional_owner_keys:  Additional owner public keys
+            :param array additional_active_keys: Additional active public keys
+            :param array additional_posting_keys: Additional posting public keys
+            :param array additional_owner_accounts: Additional owner account
+                names
+            :param array additional_active_accounts: Additional acctive account
+                names
+            :param bool storekeys: Store new keys in the wallet (default:
+                ``True``)
+            :param bool combine_with_claim_account: When set to True, a
+                claim_account operation is additionally broadcasted
+            :param str fee: When combine_with_claim_account is set to True,
+                this parameter is used for the claim_account operation
+
+            :param str creator: which account should pay the registration fee
+                                (defaults to ``default_account``)
+            :raises AccountExistsException: if the account already exists on
+                the blockchain
+
+        """
+        if not creator and config["default_account"]:
+            creator = config["default_account"]
+        if not creator:
+            raise ValueError(
+                "Not creator account given. Define it with " +
+                "creator=x, or set the default_account using beempy")
+        if password and (owner_key or active_key or memo_key):
+            raise ValueError(
+                "You cannot use 'password' AND provide keys!"
+            )
+
+        try:
+            Account(account_name, steem_instance=self)
+            raise AccountExistsException
+        except AccountDoesNotExistsException:
+            pass
+
+        creator = Account(creator, steem_instance=self)
+
+        " Generate new keys from password"
+        from beemgraphenebase.account import PasswordKey
+        if password:
+            active_key = PasswordKey(account_name, password, role="active", prefix=self.prefix)
+            owner_key = PasswordKey(account_name, password, role="owner", prefix=self.prefix)
+            posting_key = PasswordKey(account_name, password, role="posting", prefix=self.prefix)
+            memo_key = PasswordKey(account_name, password, role="memo", prefix=self.prefix)
+            active_pubkey = active_key.get_public_key()
+            owner_pubkey = owner_key.get_public_key()
+            posting_pubkey = posting_key.get_public_key()
+            memo_pubkey = memo_key.get_public_key()
+            active_privkey = active_key.get_private_key()
+            posting_privkey = posting_key.get_private_key()
+            owner_privkey = owner_key.get_private_key()
+            memo_privkey = memo_key.get_private_key()
+            # store private keys
+            try:
+                if storekeys and not self.nobroadcast:
+                    if store_owner_key:
+                        self.wallet.addPrivateKey(str(owner_privkey))
+                    self.wallet.addPrivateKey(str(active_privkey))
+                    self.wallet.addPrivateKey(str(memo_privkey))
+                    self.wallet.addPrivateKey(str(posting_privkey))
+            except ValueError as e:
+                log.info(str(e))
+
+        elif (owner_key and active_key and memo_key and posting_key):
+            active_pubkey = PublicKey(
+                active_key, prefix=self.prefix)
+            owner_pubkey = PublicKey(
+                owner_key, prefix=self.prefix)
+            posting_pubkey = PublicKey(
+                posting_key, prefix=self.prefix)
+            memo_pubkey = PublicKey(
+                memo_key, prefix=self.prefix)
+        else:
+            raise ValueError(
+                "Call incomplete! Provide either a password or public keys!"
+            )
+        owner = format(owner_pubkey, self.prefix)
+        active = format(active_pubkey, self.prefix)
+        posting = format(posting_pubkey, self.prefix)
+        memo = format(memo_pubkey, self.prefix)
+
+        owner_key_authority = [[owner, 1]]
+        active_key_authority = [[active, 1]]
+        posting_key_authority = [[posting, 1]]
+        owner_accounts_authority = []
+        active_accounts_authority = []
+        posting_accounts_authority = []
+
+        # additional authorities
+        for k in additional_owner_keys:
+            owner_key_authority.append([k, 1])
+        for k in additional_active_keys:
+            active_key_authority.append([k, 1])
+        for k in additional_posting_keys:
+            posting_key_authority.append([k, 1])
+
+        for k in additional_owner_accounts:
+            addaccount = Account(k, steem_instance=self)
+            owner_accounts_authority.append([addaccount["name"], 1])
+        for k in additional_active_accounts:
+            addaccount = Account(k, steem_instance=self)
+            active_accounts_authority.append([addaccount["name"], 1])
+        for k in additional_posting_accounts:
+            addaccount = Account(k, steem_instance=self)
+            posting_accounts_authority.append([addaccount["name"], 1])
+        if combine_with_claim_account:
+            op = {
+                "fee": Amount(fee, steem_instance=self),
+                "creator": creator["name"],
+                "prefix": self.prefix,
+            }
+            op = operations.Claim_account(**op)
+            ops = [op]
+        op = {
+            "creator": creator["name"],
+            "new_account_name": account_name,
+            'owner': {'account_auths': owner_accounts_authority,
+                      'key_auths': owner_key_authority,
+                      "address_auths": [],
+                      'weight_threshold': 1},
+            'active': {'account_auths': active_accounts_authority,
+                       'key_auths': active_key_authority,
+                       "address_auths": [],
+                       'weight_threshold': 1},
+            'posting': {'account_auths': active_accounts_authority,
+                        'key_auths': posting_key_authority,
+                        "address_auths": [],
+                        'weight_threshold': 1},
+            'memo_key': memo,
+            "json_metadata": json_meta or {},
+            "prefix": self.prefix,
+        }
+        op = operations.Create_claimed_account(**op)
+        if combine_with_claim_account:
+            ops.append(op)
+            return self.finalizeOp(ops, creator, "active", **kwargs)
+        else:
+            return self.finalizeOp(op, creator, "active", **kwargs)
+
     def create_account(
         self,
         account_name,
@@ -1064,8 +1299,8 @@ class Steem(object):
             creator = config["default_account"]
         if not creator:
             raise ValueError(
-                "Not registrar account given. Define it with " +
-                "registrar=x, or set the default_account using uptick")
+                "Not creator account given. Define it with " +
+                "creator=x, or set the default_account using beempy")
         if password and (owner_key or active_key or memo_key):
             raise ValueError(
                 "You cannot use 'password' AND provide keys!"
-- 
GitLab