def decrypt(self, memo): """ Decrypt a memo :param str memo: encrypted memo message :returns: encrypted memo :rtype: str """ if not memo: return None memo_wif = self.peerplays.wallet.getPrivateKeyForPublicKey( self.to_account["options"]["memo_key"]) if not memo_wif: raise MissingKeyError("Memo key for %s missing!" % self.to_account["name"]) # TODO: Use pubkeys of the message, not pubkeys of account! print( PrivateKey(memo_wif), PublicKey(self.from_account["options"]["memo_key"], prefix=self.peerplays.rpc.chain_params["prefix"]), memo.get("nonce"), memo.get("message")) return PPYMemo.decode_memo( PrivateKey(memo_wif), PublicKey(self.from_account["options"]["memo_key"], prefix=self.peerplays.rpc.chain_params["prefix"]), memo.get("nonce"), memo.get("message"))
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: if "default_account" in config: account = config["default_account"] if not account: raise ValueError("You need to provide an account") PublicKey(key, prefix=self.rpc.chain_params["prefix"]) account = Account(account, peerplays_instance=self) account["options"]["memo_key"] = key op = operations.Account_update( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "account": account["id"], "new_options": account["options"], "extensions": {} }) return self.finalizeOp(op, account["name"], "active")
def decrypt(self, memo): """ Decrypt a memo :param str memo: encrypted memo message :returns: encrypted memo :rtype: str """ if not memo: return None # We first try to decode assuming we received the memo try: memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey( memo["to"]) pubkey = memo["from"] except KeyNotFound: try: # if that failed, we assume that we have sent the memo memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey( memo["from"]) pubkey = memo["to"] except KeyNotFound: # if all fails, raise exception raise MissingKeyError( "Non of the required memo keys are installed!" "Need any of {}".format([memo["to"], memo["from"]])) if not hasattr(self, 'chain_prefix'): self.chain_prefix = self.blockchain.prefix return PPYMemo.decode_memo(PrivateKey(memo_wif), PublicKey(pubkey, prefix=self.chain_prefix), memo.get("nonce"), memo.get("message"))
def encrypt(self, memo): """ Encrypt a memo :param str memo: clear text memo message :returns: encrypted memo :rtype: str """ if not memo: return None nonce = str(random.getrandbits(64)) memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey( self.from_account["options"]["memo_key"]) if not memo_wif: raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"]) if not hasattr(self, 'chain_prefix'): self.chain_prefix = self.blockchain.prefix enc = PPYMemo.encode_memo( PrivateKey(memo_wif), PublicKey(self.to_account["options"]["memo_key"], prefix=self.chain_prefix), nonce, memo) return { "message": enc, "nonce": nonce, "from": self.from_account["options"]["memo_key"], "to": self.to_account["options"]["memo_key"] }
def claim(self, account=None, **kwargs): """ Claim a balance from the genesis block :param str balance_id: The identifier that identifies the balance to claim (1.15.x) :param str account: (optional) the account that owns the bet (defaults to ``default_account``) """ from peerplaysbase.account import Address, PublicKey if not account: if "default_account" in self.blockchain.config: account = self.blockchain.config["default_account"] if not account: raise ValueError("You need to provide an account") account = Account(account) pubkeys = self.blockchain.wallet.getPublicKeys() addresses = dict() for p in pubkeys: pubkey = PublicKey(p) addresses[str( Address.from_pubkey(pubkey, compressed=False, version=0))] = pubkey addresses[str( Address.from_pubkey(pubkey, compressed=True, version=0))] = pubkey addresses[str( Address.from_pubkey(pubkey, compressed=False, version=56))] = pubkey addresses[str( Address.from_pubkey(pubkey, compressed=True, version=56))] = pubkey if self["owner"] not in addresses.keys(): raise MissingKeyError("Need key for address {}".format( self["owner"])) op = operations.Balance_claim( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "deposit_to_account": account["id"], "balance_to_claim": self["id"], "balance_owner_key": addresses[self["owner"]], "total_claimed": self["balance"], "prefix": self.blockchain.prefix }) signers = [ account["name"], # The fee payer and receiver account addresses.get(self["owner"]) # The genesis balance! ] return self.blockchain.finalizeOp(op, signers, "active", **kwargs)
def __init__(self, **kwargs): BlockchainInstance.__init__(self, **kwargs) pubkeys = self.blockchain.wallet.getPublicKeys() addresses = list() for p in pubkeys: pubkey = PublicKey(p) addresses.append( str(Address.from_pubkey(pubkey, compressed=False, version=0))) addresses.append( str(Address.from_pubkey(pubkey, compressed=True, version=0))) addresses.append( str(Address.from_pubkey(pubkey, compressed=False, version=56))) addresses.append( str(Address.from_pubkey(pubkey, compressed=True, version=56))) balancess = self.blockchain.rpc.get_balance_objects(addresses) super(GenesisBalances, self).__init__([ GenesisBalance(x, **kwargs, blockchain_instance=self.blockchain) for x in balancess ])
def verify(self, **kwargs): """ Verify a message with an account's memo key :param str account: (optional) the account that owns the bet (defaults to ``default_account``) :returns: True if the message is verified successfully :raises InvalidMessageSignature if the signature is not ok """ # Split message into its parts parts = re.split("|".join(MESSAGE_SPLIT), self.message) parts = [x for x in parts if x.strip()] assert len(parts) > 2, "Incorrect number of message parts" # Strip away all whitespaces before and after the message message = parts[0].strip() signature = parts[2].strip() # Parse the meta data meta = dict(re.findall(r'(\S+)=(.*)', parts[1])) log.info("Message is: {}".format(message)) log.info("Meta is: {}".format(json.dumps(meta))) log.info("Signature is: {}".format(signature)) # Ensure we have all the data in meta assert "account" in meta, "No 'account' could be found in meta data" assert "memokey" in meta, "No 'memokey' could be found in meta data" assert "block" in meta, "No 'block' could be found in meta data" assert "timestamp" in meta, \ "No 'timestamp' could be found in meta data" account_name = meta.get("account").strip() memo_key = meta["memokey"].strip() try: PublicKey(memo_key, prefix=self.blockchain.prefix) except Exception: raise InvalidMemoKeyException( "The memo key in the message is invalid") # Load account from blockchain try: account = Account(account_name, blockchain_instance=self.blockchain) except AccountDoesNotExistsException: raise AccountDoesNotExistsException( "Could not find account {}. Are you connected to the right chain?" .format(account_name)) # Test if memo key is the same as on the blockchain if not account["options"]["memo_key"] == memo_key: raise WrongMemoKey( "Memo Key of account {} on the Blockchain".format( account["name"]) + "differs from memo key in the message: {} != {}".format( account["options"]["memo_key"], memo_key)) # Reformat message enc_message = SIGNED_MESSAGE_META.format(**locals()) # Verify Signature pubkey = verify_message(enc_message, unhexlify(signature)) # Verify pubky pk = PublicKey(hexlify(pubkey).decode("ascii")) if format(pk, self.blockchain.prefix) != memo_key: raise InvalidMessageSignature return True
def test_shared_secret(self): for s in test_shared_secrets: priv = PrivateKey(s[0]) pub = PublicKey(s[1], prefix="GPH") shared_secret = get_shared_secret(priv, pub) self.assertEqual(s[2], shared_secret)
def test_encrypt(self): for memo in test_cases: enc = encode_memo(PrivateKey(memo["wif"]), PublicKey(memo["to"], prefix="GPH"), memo["nonce"], memo["plain"]) self.assertEqual(memo["message"], enc)
def disallow(self, foreign, permission="active", 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 ``active``) :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: if "default_account" in config: account = config["default_account"] if not account: raise ValueError("You need to provide an account") if permission not in ["owner", "active"]: raise ValueError( "Permission needs to be either 'owner', or 'active") account = Account(account, peerplays_instance=self) authority = account[permission] try: pubkey = PublicKey(foreign, prefix=self.rpc.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: try: foreign_account = Account(foreign, peerplays_instance=self) affected_items = list( filter(lambda x: x[0] == foreign_account["id"], authority["account_auths"])) authority["account_auths"] = list( filter(lambda x: x[0] != foreign_account["id"], authority["account_auths"])) except: 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: 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.Account_update( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "account": account["id"], permission: authority, "extensions": {} }) 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="active", 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 ``active``) :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 """ from copy import deepcopy if not account: if "default_account" in config: account = config["default_account"] if not account: raise ValueError("You need to provide an account") if permission not in ["owner", "active"]: raise ValueError( "Permission needs to be either 'owner', or 'active") account = Account(account, peerplays_instance=self) if not weight: weight = account[permission]["weight_threshold"] authority = deepcopy(account[permission]) try: pubkey = PublicKey(foreign, prefix=self.rpc.chain_params["prefix"]) authority["key_auths"].append([str(pubkey), weight]) except: try: foreign_account = Account(foreign, peerplays_instance=self) authority["account_auths"].append( [foreign_account["id"], weight]) except: raise ValueError( "Unknown foreign account or invalid public key") if threshold: authority["weight_threshold"] = threshold self._test_weights_treshold(authority) op = operations.Account_update( **{ "fee": { "amount": 0, "asset_id": "1.3.0" }, "account": account["id"], permission: authority, "extensions": {}, "prefix": self.rpc.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, registrar=None, referrer="1.2.0", referrer_percent=50, owner_key=None, active_key=None, memo_key=None, password=None, additional_owner_keys=[], additional_active_keys=[], additional_owner_accounts=[], additional_active_accounts=[], proxy_account="proxy-to-self", storekeys=True, ): """ Create new account on PeerPlays The brainkey/password can be used to recover all generated keys (see `peerplaysbase.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. However, it **does not import the owner key** for security reasons. 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 registrar: which account should pay the registration fee (defaults to ``default_account``) :param str owner_key: Main owner key :param str active_key: Main active 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_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``) :raises AccountExistsException: if the account already exists on the blockchain """ if not registrar and config["default_account"]: registrar = config["default_account"] if not registrar: raise ValueError( "Not registrar account given. Define it with " + "registrar=x, or set the default_account using 'peerplays'") if password and (owner_key or active_key or memo_key): raise ValueError("You cannot use 'password' AND provide keys!") try: Account(account_name, peerplays_instance=self) raise AccountExistsException except: pass referrer = Account(referrer, peerplays_instance=self) registrar = Account(registrar, peerplays_instance=self) " Generate new keys from password" from peerplaysbase.account import PasswordKey, PublicKey if password: active_key = PasswordKey(account_name, password, role="active") owner_key = PasswordKey(account_name, password, role="owner") memo_key = PasswordKey(account_name, password, role="memo") active_pubkey = active_key.get_public_key() owner_pubkey = owner_key.get_public_key() memo_pubkey = memo_key.get_public_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(memo_privkey) elif (owner_key and active_key and memo_key): active_pubkey = PublicKey(active_key, prefix=self.rpc.chain_params["prefix"]) owner_pubkey = PublicKey(owner_key, prefix=self.rpc.chain_params["prefix"]) memo_pubkey = PublicKey(memo_key, prefix=self.rpc.chain_params["prefix"]) else: raise ValueError( "Call incomplete! Provide either a password or public keys!") owner = format(owner_pubkey, self.rpc.chain_params["prefix"]) active = format(active_pubkey, self.rpc.chain_params["prefix"]) memo = format(memo_pubkey, self.rpc.chain_params["prefix"]) owner_key_authority = [[owner, 1]] active_key_authority = [[active, 1]] owner_accounts_authority = [] active_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_owner_accounts: addaccount = Account(k, peerplays_instance=self) owner_accounts_authority.append([addaccount["id"], 1]) for k in additional_active_accounts: addaccount = Account(k, peerplays_instance=self) active_accounts_authority.append([addaccount["id"], 1]) # voting account voting_account = Account(proxy_account or "proxy-to-self") op = { "fee": { "amount": 0, "asset_id": "1.3.0" }, "registrar": registrar["id"], "referrer": referrer["id"], "referrer_percent": referrer_percent * 100, "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 }, "options": { "memo_key": memo, "voting_account": voting_account["id"], "num_witness": 0, "num_committee": 0, "votes": [], "extensions": [] }, "extensions": {}, "prefix": self.rpc.chain_params["prefix"] } op = operations.Account_create(**op) return self.finalizeOp(op, registrar, "active")