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" ])
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]
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)
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")
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"])), ]))
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)
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")
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")
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)
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")
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")
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")