Hive Developer Portal
Account Recovery
How to recover an account when password is lost/changed.
Full, runnable src of Account Recovery can be downloaded as part of: tutorials/python (or download just this tutorial: devportal-master-tutorials-python-35_account_recovery.zip).
In this tutorial we show you how to request account recovery for a user and also recover that account on the Hive blockchain using the beem library.
The recovery system is used to recover hacked accounts. In the event that you lose your password, your account is lost forever. You must know at least one old password that was used on your account within 30 days. This recovery process can only be executed once an hour. Stolen Account Recovery gives the rightful account owner 30 days to recover their account from the moment the thief changed their owner key.
Intro
There are two parties involved in recovering an account. The user that requires their account to be recovered, and the recovery account (or trustee) which is the account that created the username on the blockchain. For example, anyone creating their account through the Peakd webiste, their recovery account would be Peakd. If however your account was created for you by another user, that user is the one that would have to initialize your account recovery. The recovery account can be changed however to whichever user you require.
For other recovery options, see: Hive Account Recovery - Major update and new User Interface
For this tutorial we are using the beem-python
library which contains the required functions to execute this recovery process. There are two main sections to this process. Firstly, there is the request_account_recovery
function where the owner is verified and the intent for account recovery is transmitted to the blockchain. The second part is the actual recover_account
process. Along with this we also create a complete set of new account keys (posting, active, owner and memo) in order for the account to function properly. If these keys are not generated you will receive an error when trying to log in with your new password: This password is bound to your account's owner key and can not be used to login to this site. However, you can use it to update your password to obtain a more secure set of keys
The request_account_recovery
function has the following parameters:
- account to recover - The account name that wishes to be recovered
- recovery account - The trustee account / account owner that will recover the account
- new owner authority - The new owner PUBLIC key of the account to be recovered
- extensions - empty array
The recover_account
function has the following parameters:
- account_to_recover - The account name that wishes to be recovered
- new_owner_authority - The new owner PUBLIC key of the account to be recovered
- recent_owner_authority - The OLD owner PUBLIC key of the account to be recovered
- extensions - empty array
Also see:
Steps
- App setup - Library install and import. Input user info and connection to production
- Owner key creation - Creation of new and old owner keys
- Recovery request operation and transmission - creation of data object, operation and transmission of recovery request function
- Account recovery and new account keys data objects - creation of new account keys and objects for account update and recovery
- Account recovery commit - transmit account recovery to blockchain
- Account update commit - transmit account key update to blockchain
1. App setup and connection
In this tutorial we use 1 packages:
beem
- beem-python library and interaction with Blockchain
We import the libraries for the application.
import getpass
import beembase
from beem.account import Account
from beem import Hive
from beem.transactionbuilder import TransactionBuilder
from beemgraphenebase.account import PasswordKey
from beembase.objects import Permission
There are 5 inputs required. The account name to be recovered along with the old and new passwords. We also require the account name and private active key of the recovery account (account owner / trustee). For the first step in the process we initialize the beem class with the private key from the recovery account. The values are supplied via the terminal/console.
# capture user information
account = input('account to be recovered: ')
old_password = getpass.getpass('recent password for account: ')
new_password = getpass.getpass('new password for account: ')
recovery_account = input('account owner (recovery account name): ')
recovery_account_private_key = getpass.getpass('account owner private ACTIVE key: ')
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
client = Hive(node_url, keys=[recovery_account_private_key])
account = Account(account, blockchain_instance=client)
recovery_account = Account(recovery_account, blockchain_instance=client)
The new password for the account to be recovered must be at least 32 characters long.
2. Owner key creation
Both new and old owner keys are generated from the passwords supplied in the first step. For a more in depth look at creating keys please refer to this tutorial on changing your password and keys.
# create new account owner keys
new_account_owner_private_key = PasswordKey(account.name, new_password, role='owner').get_private_key()
new_account_owner_private_key_string = str(new_account_owner_private_key)
new_account_owner_public_key = str(new_account_owner_private_key.pubkey)
# create old account owner keys
old_account_owner_private_key = PasswordKey(account.name, old_password, role='owner').get_private_key()
old_account_owner_private_key_string = str(old_account_owner_private_key)
old_account_owner_public_key = str(old_account_owner_private_key.pubkey)
The Hive blockchain knows the history of your account, and every owner key that has ever been used for it. When you enter your recent password, it uses that to generate an owner key that can match up to a previous owner public key on the account. Without that password and owner key, the recovery account can’t do anything to recover your account.
3. Recovery request operation and transmission
The new_owner_authority
containing the new public key is formatted in order to be used in the request_account_recovery
operation. Once the data object has been created, the operation is transmitted to the blockchain to confirm that the account in question is going to be recovered.
# owner key format
new_owner_authority = {
"key_auths": [
[new_account_owner_public_key, 1]
],
"account_auths": [],
"weight_threshold": 1
}
# recovery request data object creation
request_op_data = {
'account_to_recover': account.name,
'recovery_account': recovery_account.name,
'new_owner_authority': new_owner_authority,
'extensions': []
}
# recovery request operation creation
request_op = beembase.operations.Request_account_recovery(**request_op_data)
print('request_op_data')
print(request_op_data)
# recovery request broadcast
request_result = client.finalizeOp(request_op, recovery_account.name, "active")
print('request_result')
print(request_result)
4. Account recovery and new account keys data objects
The old owner key is formatted and the object for the account recovery function is created with the required parameters.
# owner key format
recent_owner_authority = {
"key_auths": [
[old_account_owner_public_key, 1]
],
"account_auths": [],
"weight_threshold": 1
}
# recover account data object
op_recover_account_data = {
'account_to_recover': account.name,
'new_owner_authority': new_owner_authority,
'recent_owner_authority': recent_owner_authority,
'extensions': []
}
The object for the account key update operation is created with the relevant keys created in the correct format within the object.
# account keys update data object
op_account_update_data = {
"account": account.name,
"active": {
"key_auths": [
[str(PasswordKey(account.name, new_password, role='active').get_private_key().pubkey), 1]
],
"account_auths": [],
"weight_threshold": 1
},
"posting": {
"key_auths": [
[str(PasswordKey(account.name, new_password, role='posting').get_private_key().pubkey), 1]
],
"account_auths": [],
"weight_threshold": 1
},
"memo_key": str(PasswordKey(account.name, new_password, role='memo').get_private_key().pubkey),
"json_metadata": ""
}
5. Account recovery commit
The beem class is initialized once more but with the required WIF for this specific section. This is necessary when different keys are required at various steps. The recover_account
function is transmitted to the blockchain via the TransactionBuilder
operation in order to append the new private keys. The operation is then broadcast.
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
# recover account initialisation and transmission
client = Hive(node_url, keys=[recovery_account_private_key])
op_recover_account = beembase.operations.Recover_account(**op_recover_account_data)
print('op_recover_account')
print(op_recover_account)
tb = TransactionBuilder(blockchain_instance=client)
tb.appendOps([op_recover_account])
tb.appendWif(str(old_account_owner_private_key))
tb.appendWif(str(new_account_owner_private_key))
tb.sign()
result = tb.broadcast()
print('result')
print(result)
6. Account update commit
The same basic process is followed as in the previous step. For this step however we require the new owner private key which is initialized in the beem class. The TransactionBuilder
operation is used once more for the transmission to the blockchain.
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
# update account keys initialisation and transmission
client = Hive(node_url, keys=[new_account_owner_private_key])
op_account_update = beembase.operations.Account_update(**op_account_update_data)
print('op_account_update')
print(op_account_update)
tb = TransactionBuilder(blockchain_instance=client)
tb.appendOps([op_account_update])
tb.appendWif(str(new_account_owner_private_key))
tb.sign()
result = tb.broadcast()
print('result')
print(result)
Final code:
import getpass
import beembase
from beem.account import Account
from beem import Hive
from beem.transactionbuilder import TransactionBuilder
from beemgraphenebase.account import PasswordKey
from beembase.objects import Permission
# capture user information
account = input('account to be recovered: ')
old_password = getpass.getpass('recent password for account: ')
new_password = getpass.getpass('new password for account: ')
recovery_account = input('account owner (recovery account name): ')
recovery_account_private_key = getpass.getpass('account owner private ACTIVE key: ')
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
client = Hive(node_url, keys=[recovery_account_private_key])
account = Account(account, blockchain_instance=client)
recovery_account = Account(recovery_account, blockchain_instance=client)
# create new account owner keys
new_account_owner_private_key = PasswordKey(account.name, new_password, role='owner').get_private_key()
new_account_owner_private_key_string = str(new_account_owner_private_key)
new_account_owner_public_key = str(new_account_owner_private_key.pubkey)
# create old account owner keys
old_account_owner_private_key = PasswordKey(account.name, old_password, role='owner').get_private_key()
old_account_owner_private_key_string = str(old_account_owner_private_key)
old_account_owner_public_key = str(old_account_owner_private_key.pubkey)
# owner key format
new_owner_authority = {
"key_auths": [
[new_account_owner_public_key, 1]
],
"account_auths": [],
"weight_threshold": 1
}
# recovery request data object creation
request_op_data = {
'account_to_recover': account.name,
'recovery_account': recovery_account.name,
'new_owner_authority': new_owner_authority,
'extensions': []
}
# recovery request operation creation
request_op = beembase.operations.Request_account_recovery(**request_op_data)
print('request_op_data')
print(request_op_data)
# recovery request broadcast
request_result = client.finalizeOp(request_op, recovery_account.name, "active")
print('request_result')
print(request_result)
# owner key format
recent_owner_authority = {
"key_auths": [
[old_account_owner_public_key, 1]
],
"account_auths": [],
"weight_threshold": 1
}
# recover account data object
op_recover_account_data = {
'account_to_recover': account.name,
'new_owner_authority': new_owner_authority,
'recent_owner_authority': recent_owner_authority,
'extensions': []
}
# account keys update data object
op_account_update_data = {
"account": account.name,
"active": {
"key_auths": [
[str(PasswordKey(account.name, new_password, role='active').get_private_key().pubkey), 1]
],
"account_auths": [],
"weight_threshold": 1
},
"posting": {
"key_auths": [
[str(PasswordKey(account.name, new_password, role='posting').get_private_key().pubkey), 1]
],
"account_auths": [],
"weight_threshold": 1
},
"memo_key": str(PasswordKey(account.name, new_password, role='memo').get_private_key().pubkey),
"json_metadata": ""
}
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
# recover account initialisation and transmission
client = Hive(node_url, keys=[recovery_account_private_key])
op_recover_account = beembase.operations.Recover_account(**op_recover_account_data)
print('op_recover_account')
print(op_recover_account)
tb = TransactionBuilder(blockchain_instance=client)
tb.appendOps([op_recover_account])
tb.appendWif(str(old_account_owner_private_key))
tb.appendWif(str(new_account_owner_private_key))
tb.sign()
result = tb.broadcast()
print('result')
print(result)
# node_url = 'https://testnet.openhive.network' # Public Testnet
node_url = 'http://127.0.0.1:8090' # Local Testnet
# update account keys initialisation and transmission
client = Hive(node_url, keys=[new_account_owner_private_key])
op_account_update = beembase.operations.Account_update(**op_account_update_data)
print('op_account_update')
print(op_account_update)
tb = TransactionBuilder(blockchain_instance=client)
tb.appendOps([op_account_update])
tb.appendWif(str(new_account_owner_private_key))
tb.sign()
result = tb.broadcast()
print('result')
print(result)
To Run the tutorial
You can launch a local testnet, with port 8090 mapped locally to the docker container:
docker run -d -p 8090:8090 inertia/tintoy:latest
For details on running a local testnet, see: Setting Up a Testnet
- review dev requirements
git clone https://gitlab.syncad.com/hive/devportal.git
cd devportal/tutorials/python/35_account_recovery
pip install -r requirements.txt
python index.py
- After a few moments, you should see a prompt for input in terminal screen.