def test_PubKey(self):
     self.assertEqual([
         format(
             PublicKey(
                 "BTS6UtYWWs3rkZGV8JA86qrgkG6tyFksgECefKE1MiH4HkLD8PFGL",
                 prefix="BTS").address, "BTS"),
         format(
             PublicKey(
                 "BTS8YAMLtNcnqGNd3fx28NP3WoyuqNtzxXpwXTkZjbfe9scBmSyGT",
                 prefix="BTS").address, "BTS"),
         format(
             PublicKey(
                 "BTS7HUo6bm7Gfoi3RqAtzwZ83BFCwiCZ4tp37oZjtWxGEBJVzVVGw",
                 prefix="BTS").address, "BTS"),
         format(
             PublicKey(
                 "BTS6676cZ9qmqPnWMrm4McjCuHcnt6QW5d8oRJ4t8EDH8DdCjvh4V",
                 prefix="BTS").address, "BTS"),
         format(
             PublicKey(
                 "BTS7u8m6zUNuzPNK1tPPLtnipxgqV9mVmTzrFNJ9GvovvSTCkVUra",
                 prefix="BTS").address, "BTS")
     ], [
         "BTS66FCjYKzMwLbE3a59YpmFqA9bwporT4L3",
         "BTSKNpRuPX8KhTBsJoFp1JXd7eQEsnCpRw3k",
         "BTS838ENJargbUrxXWuE2xD9HKjQaS17GdCd",
         "BTSNsrLFWTziSZASnNJjWafFtGBfSu8VG8KU",
         "BTSDjAGuXzk3WXabBEgKKc8NsuQM412boBdR"
     ])
 def test_PublicKey(self):
     self.assertEqual([
         str(
             PublicKey(
                 "BTS6UtYWWs3rkZGV8JA86qrgkG6tyFksgECefKE1MiH4HkLD8PFGL",
                 prefix="BTS")),
         str(
             PublicKey(
                 "BTS8YAMLtNcnqGNd3fx28NP3WoyuqNtzxXpwXTkZjbfe9scBmSyGT",
                 prefix="BTS")),
         str(
             PublicKey(
                 "BTS7HUo6bm7Gfoi3RqAtzwZ83BFCwiCZ4tp37oZjtWxGEBJVzVVGw",
                 prefix="BTS")),
         str(
             PublicKey(
                 "BTS6676cZ9qmqPnWMrm4McjCuHcnt6QW5d8oRJ4t8EDH8DdCjvh4V",
                 prefix="BTS")),
         str(
             PublicKey(
                 "BTS7u8m6zUNuzPNK1tPPLtnipxgqV9mVmTzrFNJ9GvovvSTCkVUra",
                 prefix="BTS"))
     ], [
         "BTS6UtYWWs3rkZGV8JA86qrgkG6tyFksgECefKE1MiH4HkLD8PFGL",
         "BTS8YAMLtNcnqGNd3fx28NP3WoyuqNtzxXpwXTkZjbfe9scBmSyGT",
         "BTS7HUo6bm7Gfoi3RqAtzwZ83BFCwiCZ4tp37oZjtWxGEBJVzVVGw",
         "BTS6676cZ9qmqPnWMrm4McjCuHcnt6QW5d8oRJ4t8EDH8DdCjvh4V",
         "BTS7u8m6zUNuzPNK1tPPLtnipxgqV9mVmTzrFNJ9GvovvSTCkVUra"
     ])
Example #3
0
def involved_keys(message):
    " decode structure "
    raw = base58decode(message[1:])
    from_key = PublicKey(raw[:66])
    raw = raw[66:]
    to_key = PublicKey(raw[:66])

    return [from_key, to_key]
Example #4
0
def decode_memo(priv, message) :
    """ Decode a message with a shared secret between Alice and Bob

        :param PrivateKey priv: Private Key (of Bob)
        :param base58encoded message: Encrypted Memo message
        :return: Decrypted message
        :rtype: str
        :raise ValueError: if message cannot be decoded as valid UTF-8
               string

    """
    " decode structure "
    raw = base58decode(message[1:])
    from_key = PublicKey(raw[:66])
    raw = raw[66:]
    to_key = PublicKey(raw[:66])
    raw = raw[66:]
    nonce = str(struct.unpack_from("<Q", unhexlify(raw[:16]))[0])
    raw = raw[16:]
    check = struct.unpack_from("<I", unhexlify(raw[:8]))[0]
    raw = raw[8:]
    cipher = raw

    if repr(to_key) == repr(priv.pubkey):
        shared_secret = get_shared_secret(priv, from_key)
    elif repr(from_key) == repr(priv.pubkey):
        shared_secret = get_shared_secret(priv, to_key)
    else:
        raise ValueError("Incorrect PrivateKey")

    " Init encryption "
    aes, checksum = init_aes(shared_secret, nonce)

    " Check "
    assert check == checksum, "Checksum failure"

    " Encryption "
    # remove the varint prefix (FIXME, long messages!)
    message    = cipher[2:]
    from graphenebase.types import varintdecode
    message    = aes.decrypt(unhexlify(bytes(message, 'ascii')))
    length     = varintdecode(message.decode('utf8'))
    message    = message[len(varint(length)):]
    try :
        return _unpad(message.decode('utf8'), 16)
    except :
        raise ValueError(message)
Example #5
0
    def transfer(self, to, amount, asset, memo="", account=None):
        """ Transfer SBD or STEEM to another account.

            :param str to: Recipient

            :param float amount: Amount to transfer

            :param str asset: Asset to transfer (``SBD`` or ``STEEM``)

            :param str memo: (optional) Memo, may begin with `#` for encrypted
            messaging

            :param str account: (optional) the source account for the transfer
            if not ``default_account``

        """
        if not account:
            account = configStorage.get("default_account")
        if not account:
            raise ValueError("You need to provide an account")

        assert asset in ['STEEM', 'SBD']

        if memo and memo[0] == "#":
            from steembase import memo as Memo
            memo_wif = self.wallet.getMemoKeyForAccount(account)
            if not memo_wif:
                raise MissingKeyError("Memo key for %s missing!" % account)
            to_account = Account(to, steemd_instance=self.steemd)
            nonce = random.getrandbits(64)
            memo = Memo.encode_memo(
                PrivateKey(memo_wif),
                PublicKey(to_account["memo_key"],
                          prefix=self.steemd.chain_params["prefix"]),
                nonce,
                memo,
                prefix=self.steemd.chain_params["prefix"])

        op = operations.Transfer(
            **{
                "from":
                account,
                "to":
                to,
                "amount":
                '{:.{prec}f} {asset}'.format(
                    float(amount), prec=3, asset=asset),
                "memo":
                memo
            })
        return self.finalizeOp(op, account, "active")
Example #6
0
 def __init__(self, *args, **kwargs):
     if isArgsThisClass(self, args):
         self.data = args[0].data
     else:
         if len(args) == 1 and len(kwargs) == 0:
             kwargs = args[0]
         super().__init__(
             OrderedDict([
                 ('owner', String(kwargs["owner"])),
                 ('url', String(kwargs["url"])),
                 ('block_signing_key',
                  PublicKey(kwargs["block_signing_key"])),
                 ('props', Witness_props(kwargs["props"])),
                 ('fee', Amount(kwargs["fee"])),
             ]))
Example #7
0
    def transfer(self, to, amount, asset, memo="", account=None):
        """ Transfer SBD or STEEM to another account.

            :param str to: Recipient
            :param float amount: Amount to transfer
            :param str asset: Asset to transfer (``SBD`` or ``STEEM``)
            :param str memo: (optional) Memo, may begin with `#` for encrypted messaging
            :param str account: (optional) the source account for the transfer if not ``default_account``
        """
        if not account:
            if "default_account" in config:
                account = config["default_account"]
        if not account:
            raise ValueError("You need to provide an account")

        assert asset == "SBD" or asset == "STEEM"

        if memo and memo[0] == "#":
            from steembase import memo as Memo
            memo_wif = self.wallet.getMemoKeyForAccount(account)
            if not memo_wif:
                raise MissingKeyError("Memo key for %s missing!" % account)
            to_account = self.rpc.get_account(to)
            nonce = str(random.getrandbits(64))
            memo = Memo.encode_memo(
                PrivateKey(memo_wif),
                PublicKey(to_account["memo_key"], prefix=prefix), nonce, memo)

        op = transactions.Transfer(
            **{
                "from":
                account,
                "to":
                to,
                "amount":
                '{:.{prec}f} {asset}'.format(
                    float(amount), prec=3, asset=asset),
                "memo":
                memo
            })
        wif = self.wallet.getActiveKeyForAccount(account)
        return self.executeOp(op, wif)
Example #8
0
    def witness_update(self, signing_key, url, props, account=None):
        """ Update witness

            :param pubkey signing_key: Signing key
            :param str url: URL
            :param dict props: Properties
            :param str account: (optional) witness account name

             Properties:::

                {
                    "account_creation_fee": x,
                    "maximum_block_size": x,
                }

        """
        if not account:
            account = configStorage.get("default_account")
        if not account:
            raise ValueError("You need to provide an account")

        try:
            PublicKey(signing_key)
        except Exception as e:
            raise e

        op = operations.WitnessUpdate(
            **{
                "owner": account,
                "url": url,
                "block_signing_key": signing_key,
                "props": props,
                "fee": "0.000 STEEM",
                "prefix": self.steemd.chain_params["prefix"]
            })
        return self.finalizeOp(op, account, "active")
Example #9
0
    def update_memo_key(self, key, account=None):
        """ Update an account's memo public key

            This method does **not** add any private keys to your
            wallet but merely changes the memo public key.

            :param str key: New memo public key
            :param str account: (optional) the account to allow access
                to (defaults to ``default_account``)
        """
        if not account:
            account = configStorage.get("default_account")
        if not account:
            raise ValueError("You need to provide an account")

        PublicKey(key)  # raises exception if invalid
        account = Account(account, steemd_instance=self.steemd)
        op = operations.AccountUpdate(
            **{
                "account": account["name"],
                "memo_key": key,
                "json_metadata": account["json_metadata"]
            })
        return self.finalizeOp(op, account["name"], "active")
Example #10
0
    def create_account(
        self,
        account_name,
        json_meta={},
        creator=None,
        owner_key=None,
        active_key=None,
        posting_key=None,
        memo_key=None,
        password=None,
        additional_owner_keys=[],
        additional_active_keys=[],
        additional_posting_keys=[],
        additional_owner_accounts=[],
        additional_active_accounts=[],
        additional_posting_accounts=[],
        storekeys=True,
    ):
        """ Create new account in Steem

            The brainkey/password can be used to recover all generated keys (see
            `steembase.account` for more details.

            By default, this call will use ``default_author`` 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.

            .. 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!

            .. 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. However, it **does not import the owner
                      key** for security reasons. Do NOT expect to be
                      able to recover it from piston if you lose your
                      password!

            :param str account_name: (**required**) new account name
            :param str json_meta: Optional meta data for the account
            :param str creator: which account should pay the registration fee
                                (defaults to ``default_author``)
            :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 array additional_posting_accounts: Additional posting account names
            :param bool storekeys: Store new keys in the wallet (default: ``True``)
            :raises AccountExistsException: if the account already exists on the blockchain

        """
        if not creator and config["default_author"]:
            creator = config["default_author"]
        if not creator:
            raise ValueError("Not creator account given. Define it with " +
                             "creator=x, or set the default_author in piston")
        if password and (owner_key or posting_key or active_key or memo_key):
            raise ValueError("You cannot use 'password' AND provide keys!")

        account = None
        try:
            account = self.rpc.get_account(account_name)
        except:
            pass
        if account:
            raise AccountExistsException

        " Generate new keys from password"
        from steembase.account import PasswordKey, PublicKey
        if password:
            posting_key = PasswordKey(account_name, password, role="posting")
            active_key = PasswordKey(account_name, password, role="active")
            owner_key = PasswordKey(account_name, password, role="owner")
            memo_key = PasswordKey(account_name, password, role="memo")
            posting_pubkey = posting_key.get_public_key()
            active_pubkey = active_key.get_public_key()
            owner_pubkey = owner_key.get_public_key()
            memo_pubkey = memo_key.get_public_key()
            posting_privkey = posting_key.get_private_key()
            active_privkey = active_key.get_private_key()
            # owner_privkey   = owner_key.get_private_key()
            memo_privkey = memo_key.get_private_key()
            # store private keys
            if storekeys:
                # self.wallet.addPrivateKey(owner_privkey)
                self.wallet.addPrivateKey(active_privkey)
                self.wallet.addPrivateKey(posting_privkey)
                self.wallet.addPrivateKey(memo_privkey)
        elif (owner_key and posting_key and active_key and memo_key):
            posting_pubkey = PublicKey(posting_key, prefix=prefix)
            active_pubkey = PublicKey(active_key, prefix=prefix)
            owner_pubkey = PublicKey(owner_key, prefix=prefix)
            memo_pubkey = PublicKey(memo_key, prefix=prefix)
        else:
            raise ValueError(
                "Call incomplete! Provide either a password or public keys!")

        owner = format(posting_pubkey, prefix)
        active = format(active_pubkey, prefix)
        posting = format(owner_pubkey, prefix)
        memo = format(memo_pubkey, 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:
            owner_accounts_authority.append([k, 1])
        for k in additional_active_accounts:
            active_accounts_authority.append([k, 1])
        for k in additional_posting_accounts:
            posting_accounts_authority.append([k, 1])

        props = self.rpc.get_chain_properties()
        fee = props["account_creation_fee"]
        s = {
            'creator': creator,
            'fee': fee,
            'json_metadata': json_meta,
            'memo_key': memo,
            'new_account_name': account_name,
            'owner': {
                'account_auths': owner_accounts_authority,
                'key_auths': owner_key_authority,
                'weight_threshold': 1
            },
            'active': {
                'account_auths': active_accounts_authority,
                'key_auths': active_key_authority,
                'weight_threshold': 1
            },
            'posting': {
                'account_auths': posting_accounts_authority,
                'key_auths': posting_key_authority,
                'weight_threshold': 1
            }
        }
        op = transactions.Account_create(**s)
        wif = self.wallet.getActiveKeyForAccount(creator)
        return self.executeOp(op, wif)
Example #11
0
    def disallow(self,
                 foreign,
                 permission="posting",
                 account=None,
                 threshold=None):
        """ Remove additional access to an account by some other public
            key or account.

            :param str foreign: The foreign account that will obtain access
            :param str permission: (optional) The actual permission to
                modify (defaults to ``posting``)
            :param str account: (optional) the account to allow access
                to (defaults to ``default_account``)
            :param int threshold: The threshold that needs to be reached
                by signatures to be able to interact
        """
        if not account:
            account = configStorage.get("default_account")
        if not account:
            raise ValueError("You need to provide an account")

        if permission not in ["owner", "posting", "active"]:
            raise ValueError(
                "Permission needs to be either 'owner', 'posting', or 'active")
        account = Account(account, steemd_instance=self.steemd)
        authority = account[permission]

        try:
            pubkey = PublicKey(foreign,
                               prefix=self.steemd.chain_params["prefix"])
            affected_items = list(
                filter(lambda x: x[0] == str(pubkey), authority["key_auths"]))
            authority["key_auths"] = list(
                filter(lambda x: x[0] != str(pubkey), authority["key_auths"]))
        except:  # noqa FIXME(sneak)
            try:
                foreign_account = Account(foreign, steemd_instance=self.steemd)
                affected_items = list(
                    filter(lambda x: x[0] == foreign_account["name"],
                           authority["account_auths"]))
                authority["account_auths"] = list(
                    filter(lambda x: x[0] != foreign_account["name"],
                           authority["account_auths"]))
            except:  # noqa FIXME(sneak)
                raise ValueError(
                    "Unknown foreign account or unvalid public key")

        removed_weight = affected_items[0][1]

        # Define threshold
        if threshold:
            authority["weight_threshold"] = threshold

        # Correct threshold (at most by the amount removed from the
        # authority)
        try:
            self._test_weights_treshold(authority)
        except:  # noqa FIXME(sneak)
            log.critical("The account's threshold will be reduced by %d" %
                         removed_weight)
            authority["weight_threshold"] -= removed_weight
            self._test_weights_treshold(authority)

        op = operations.AccountUpdate(
            **{
                "account": account["name"],
                permission: authority,
                "memo_key": account["memo_key"],
                "json_metadata": account["json_metadata"]
            })
        if permission == "owner":
            return self.finalizeOp(op, account["name"], "owner")
        else:
            return self.finalizeOp(op, account["name"], "active")
Example #12
0
    def allow(self,
              foreign,
              weight=None,
              permission="posting",
              account=None,
              threshold=None):
        """ Give additional access to an account by some other public
            key or account.

            :param str foreign: The foreign account that will obtain access
            :param int weight: (optional) The weight to use. If not
                define, the threshold will be used. If the weight is
                smaller than the threshold, additional signatures will
                be required. (defaults to threshold)
            :param str permission: (optional) The actual permission to
                modify (defaults to ``posting``)
            :param str account: (optional) the account to allow access
                to (defaults to ``default_account``)
            :param int threshold: The threshold that needs to be reached
                by signatures to be able to interact
        """
        if not account:
            account = configStorage.get("default_account")
        if not account:
            raise ValueError("You need to provide an account")

        if permission not in ["owner", "posting", "active"]:
            raise ValueError(
                "Permission needs to be either 'owner', 'posting', or 'active")
        account = Account(account, steemd_instance=self.steemd)
        if not weight:
            weight = account[permission]["weight_threshold"]

        authority = account[permission]
        try:
            pubkey = PublicKey(foreign)
            authority["key_auths"].append([str(pubkey), weight])
        except:  # noqa FIXME(sneak)
            try:
                foreign_account = Account(foreign, steemd_instance=self.steemd)
                authority["account_auths"].append(
                    [foreign_account["name"], weight])
            except:  # noqa FIXME(sneak)
                raise ValueError(
                    "Unknown foreign account or unvalid public key")
        if threshold:
            authority["weight_threshold"] = threshold
            self._test_weights_treshold(authority)

        op = operations.AccountUpdate(
            **{
                "account": account["name"],
                permission: authority,
                "memo_key": account["memo_key"],
                "json_metadata": account["json_metadata"],
                'prefix': self.steemd.chain_params["prefix"]
            })
        if permission == "owner":
            return self.finalizeOp(op, account["name"], "owner")
        else:
            return self.finalizeOp(op, account["name"], "active")
Example #13
0
    def create_account(
        self,
        account_name,
        json_meta=None,
        password=None,
        owner_key=None,
        active_key=None,
        posting_key=None,
        memo_key=None,
        additional_owner_keys=[],
        additional_active_keys=[],
        additional_posting_keys=[],
        additional_owner_accounts=[],
        additional_active_accounts=[],
        additional_posting_accounts=[],
        store_keys=True,
        store_owner_key=False,
        delegation_fee_steem='0 STEEM',
        creator=None,
    ):
        """ Create new account in Steem

            The brainkey/password can be used to recover all generated keys
            (see `steembase.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.

            .. 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!

               **You can partially pay that fee by delegating VESTS.**

               To pay the fee in full in STEEM, leave
               ``delegation_fee_steem`` set to ``0 STEEM`` (Default).

               To pay the fee partially in STEEM, partially with delegated
               VESTS, set ``delegation_fee_steem`` to a value greater than ``1
               STEEM``. `Required VESTS will be calculated automatically.`

               To pay the fee with maximum amount of delegation, set
               ``delegation_fee_steem`` to ``1 STEEM``. `Required VESTS will be
               calculated automatically.`

            .. 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. However, it **does not import
        the owner key** unless `store_owner_key` is set to True (default
        False). Do NOT expect to be able to recover it from the wallet if
        you lose your password!

            :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 list additional_owner_keys:  Additional owner public keys

            :param list additional_active_keys: Additional active public keys

            :param list additional_posting_keys: Additional posting public keys

            :param list additional_owner_accounts: Additional owner account
            names

            :param list additional_active_accounts: Additional active account
            names

            :param list additional_posting_accounts: Additional posting account
            names

            :param bool store_keys: Store new keys in the wallet (default:
            ``True``)

            :param bool store_owner_key: Store owner key in the wallet
            (default: ``False``)

            :param str delegation_fee_steem: (Optional) If set, `creator` pay a
            fee of this amount, and delegate the rest with VESTS (calculated
            automatically). Minimum: 1 STEEM. If left to 0 (Default), full fee
            is paid without VESTS delegation.

            :param str creator: which account should pay the registration fee
                                (defaults to ``default_account``)

        :raises AccountExistsException: if the account already exists on the
        blockchain

        """
        assert len(
            account_name) <= 16, "Account name must be at most 16 chars long"

        if not creator:
            creator = configStorage.get("default_account")
        if not creator:
            raise ValueError(
                "Not creator account given. Define it with " +
                "creator=x, or set the default_account using steempy")
        if password and (owner_key or posting_key or active_key or memo_key):
            raise ValueError("You cannot use 'password' AND provide keys!")

        # check if account already exists
        try:
            Account(account_name, steemd_instance=self.steemd)
        except:  # noqa FIXME(sneak)
            pass
        else:
            raise AccountExistsException

        " Generate new keys from password"
        from steembase.account import PasswordKey, PublicKey
        if password:
            posting_key = PasswordKey(account_name, password, role="posting")
            active_key = PasswordKey(account_name, password, role="active")
            owner_key = PasswordKey(account_name, password, role="owner")
            memo_key = PasswordKey(account_name, password, role="memo")
            posting_pubkey = posting_key.get_public_key()
            active_pubkey = active_key.get_public_key()
            owner_pubkey = owner_key.get_public_key()
            memo_pubkey = memo_key.get_public_key()
            posting_privkey = posting_key.get_private_key()
            active_privkey = active_key.get_private_key()
            owner_privkey = owner_key.get_private_key()
            memo_privkey = memo_key.get_private_key()
            # store private keys
            if store_keys:
                if store_owner_key:
                    self.wallet.addPrivateKey(owner_privkey)
                self.wallet.addPrivateKey(active_privkey)
                self.wallet.addPrivateKey(posting_privkey)
                self.wallet.addPrivateKey(memo_privkey)
        elif owner_key and posting_key and active_key and memo_key:
            posting_pubkey = PublicKey(
                posting_key, prefix=self.steemd.chain_params["prefix"])
            active_pubkey = PublicKey(
                active_key, prefix=self.steemd.chain_params["prefix"])
            owner_pubkey = PublicKey(owner_key,
                                     prefix=self.steemd.chain_params["prefix"])
            memo_pubkey = PublicKey(memo_key,
                                    prefix=self.steemd.chain_params["prefix"])
        else:
            raise ValueError(
                "Call incomplete! Provide either a password or public keys!")

        owner = format(owner_pubkey, self.steemd.chain_params["prefix"])
        active = format(active_pubkey, self.steemd.chain_params["prefix"])
        posting = format(posting_pubkey, self.steemd.chain_params["prefix"])
        memo = format(memo_pubkey, self.steemd.chain_params["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:
            owner_accounts_authority.append([k, 1])
        for k in additional_active_accounts:
            active_accounts_authority.append([k, 1])
        for k in additional_posting_accounts:
            posting_accounts_authority.append([k, 1])

        props = self.steemd.get_chain_properties()
        required_fee_steem = Amount(props["account_creation_fee"]).amount * 30

        required_fee_vests = 0
        delegation_fee_steem = Amount(delegation_fee_steem).amount
        if delegation_fee_steem:
            # creating accounts without delegation requires 30x
            # account_creation_fee creating account with delegation allows one
            # to use VESTS to pay the fee where the ratio must satisfy 1 STEEM
            # in fee == 5 STEEM in delegated VESTS

            delegated_sp_fee_mult = 5

            if delegation_fee_steem < 1:
                raise ValueError(
                    "When creating account with delegation, at least " +
                    "1 STEEM in fee must be paid.")

            # calculate required remaining fee in vests
            remaining_fee = required_fee_steem - delegation_fee_steem
            if remaining_fee > 0:
                required_sp = remaining_fee * delegated_sp_fee_mult
                required_fee_vests = Converter().sp_to_vests(required_sp) + 1

        s = {
            'creator': creator,
            'fee': '%s STEEM' % (delegation_fee_steem or required_fee_steem),
            'delegation': '%s VESTS' % required_fee_vests,
            'json_metadata': json_meta or {},
            'memo_key': memo,
            'new_account_name': account_name,
            'owner': {
                'account_auths': owner_accounts_authority,
                'key_auths': owner_key_authority,
                'weight_threshold': 1
            },
            'active': {
                'account_auths': active_accounts_authority,
                'key_auths': active_key_authority,
                'weight_threshold': 1
            },
            'posting': {
                'account_auths': posting_accounts_authority,
                'key_auths': posting_key_authority,
                'weight_threshold': 1
            },
            'prefix': self.steemd.chain_params["prefix"]
        }

        op = operations.AccountCreateWithDelegation(**s)

        return self.finalizeOp(op, creator, "active")