mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-04-10 22:41:53 +08:00
more work on incorporating token files: allow lookup to fetch both profiles and token files; allow us to list device IDs in the token file; extract public keys and device IDs from the token file when loading datastore; flesh out migration logic to convert profiles into token files, and more
This commit is contained in:
@@ -81,15 +81,15 @@ from blockstack_client import (
|
||||
list_zonefile_history, lookup_snv, put_immutable, put_mutable, zonefile_data_replicate
|
||||
)
|
||||
|
||||
from blockstack_client.profile import delete_profile, get_profile, \
|
||||
profile_add_device_id, profile_remove_device_id, profile_list_accounts, profile_get_account, \
|
||||
from blockstack_client.profile import get_profile, \
|
||||
profile_list_accounts, profile_get_account, \
|
||||
profile_put_account, profile_delete_account
|
||||
|
||||
from rpc import local_api_connect, local_api_status
|
||||
import rpc as local_rpc
|
||||
import config
|
||||
|
||||
from .config import configure_zonefile, configure, get_utxo_provider_client, get_tx_broadcaster
|
||||
from .config import configure_zonefile, configure, get_utxo_provider_client, get_tx_broadcaster, get_local_device_id
|
||||
from .constants import (
|
||||
CONFIG_PATH, CONFIG_DIR,
|
||||
FIRST_BLOCK_MAINNET, NAME_UPDATE,
|
||||
@@ -123,7 +123,7 @@ from .scripts import UTXOException, is_name_valid, is_valid_hash, is_namespace_v
|
||||
from .user import make_empty_user_profile, user_zonefile_data_pubkey
|
||||
|
||||
from .tx import serialize_tx, sign_tx
|
||||
from .zonefile import make_empty_zonefile, url_to_uri_record
|
||||
from .zonefile import make_empty_zonefile, url_to_uri_record, lookup_name_zonefile_pubkey
|
||||
|
||||
from .utils import exit_with_error, satoshis_to_btc, ScatterGather
|
||||
from .app import app_publish, app_get_config, app_get_resource, \
|
||||
@@ -137,8 +137,9 @@ from .data import datastore_mkdir, datastore_rmdir, make_datastore_info, put_dat
|
||||
|
||||
from .schemas import OP_URLENCODED_PATTERN, OP_NAME_PATTERN, OP_USER_ID_PATTERN, OP_BASE58CHECK_PATTERN
|
||||
|
||||
from .token_file import token_file_profile_serialize, token_file_update_profile, token_file_put, token_file_delete, \
|
||||
lookup_name_privkey, lookup_signing_privkey, lookup_signing_pubkeys, lookup_name_zonefile_pubkey
|
||||
from .token_file import token_file_profile_serialize, token_file_update_profile, token_file_get, token_file_put, token_file_delete, \
|
||||
lookup_name_privkey, lookup_signing_privkey, lookup_signing_pubkeys, token_file_get_key_order, lookup_delegated_device_pubkeys, \
|
||||
token_file_create
|
||||
|
||||
import virtualchain
|
||||
from virtualchain.lib.ecdsalib import *
|
||||
@@ -929,11 +930,14 @@ def cli_lookup(args, config_path=CONFIG_PATH):
|
||||
command: lookup
|
||||
help: Get the zone file and profile for a particular name
|
||||
arg: name (str) 'The name to look up'
|
||||
opt: full (str) 'If True, then get the whole token file'
|
||||
"""
|
||||
data = {}
|
||||
|
||||
blockchain_record = None
|
||||
fqu = str(args.name)
|
||||
full = str(args.full) if hasattr(args, "full") and args.full and len(args.full) > 0 else None
|
||||
full = True if full else False
|
||||
|
||||
error = check_valid_name(fqu)
|
||||
if error:
|
||||
@@ -954,24 +958,44 @@ def cli_lookup(args, config_path=CONFIG_PATH):
|
||||
msg = 'Name is revoked. Use get_name_blockchain_record for details.'
|
||||
return {'error': msg}
|
||||
|
||||
try:
|
||||
res = get_profile(str(args.name), name_record=blockchain_record)
|
||||
if 'error' in res:
|
||||
return res
|
||||
if not full:
|
||||
# just getting the profile
|
||||
try:
|
||||
res = get_profile(str(args.name), name_record=blockchain_record)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
if res['profile'] is not None:
|
||||
data['profile'] = res['profile']
|
||||
else:
|
||||
data['profile'] = res['legacy_profile']
|
||||
if res['profile'] is not None:
|
||||
data['profile'] = res['profile']
|
||||
else:
|
||||
data['profile'] = res['legacy_profile']
|
||||
|
||||
if data['profile'] is None:
|
||||
return {'error': 'Failed to load a profile for this name. Try again with --debug to diagnose.'}
|
||||
if data['profile'] is None:
|
||||
return {'error': 'Failed to load a profile for this name. Try again with --debug to diagnose.'}
|
||||
|
||||
data['zonefile'] = res['raw_zonefile']
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
msg = 'Failed to look up name\n{}'
|
||||
return {'error': msg.format(traceback.format_exc())}
|
||||
data['zonefile'] = res['raw_zonefile']
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
msg = 'Failed to look up name\n{}'
|
||||
return {'error': msg.format(traceback.format_exc())}
|
||||
|
||||
else:
|
||||
# getting the token file
|
||||
try:
|
||||
res = token_file_get(str(args.name), name_record=blockchain_record)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
if not res['token_file']:
|
||||
return {'error': 'Name {} does not have a token file'.format(args.name)}
|
||||
|
||||
del res['token_file']['jwts']
|
||||
data['zonefile'] = res['raw_zonefile']
|
||||
data['token_file'] = res['token_file']
|
||||
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return {'error': 'Failed to look up name\n{}'.format(traceback.format_exc())}
|
||||
|
||||
result = data
|
||||
analytics_event('Name lookup', {})
|
||||
@@ -1245,7 +1269,7 @@ def analyze_zonefile_string(fqu, zonefile_data, force_data=False, check_current=
|
||||
return ret
|
||||
|
||||
|
||||
def cli_register(args, config_path=CONFIG_PATH, force_data=False,
|
||||
def cli_register(args, config_path=CONFIG_PATH, force_data=False, wallet_keys=None,
|
||||
cost_satoshis=None, interactive=True, password=None, proxy=None):
|
||||
"""
|
||||
command: register
|
||||
@@ -1310,22 +1334,24 @@ def cli_register(args, config_path=CONFIG_PATH, force_data=False,
|
||||
|
||||
else:
|
||||
# make a default zonefile
|
||||
_, _, data_pubkey = get_addresses_from_file(config_dir=config_dir)
|
||||
if not data_pubkey:
|
||||
return {'error': 'No data key in wallet. Please add one with `setup_wallet`'}
|
||||
|
||||
user_zonefile_dict = make_empty_zonefile(fqu, data_pubkey)
|
||||
user_zonefile_dict = make_empty_zonefile(fqu, None)
|
||||
user_zonefile = blockstack_zones.make_zone_file(user_zonefile_dict)
|
||||
|
||||
# if we have a data key, then make an empty profile and zonefile
|
||||
user_profile = None
|
||||
new_token_file = None
|
||||
if not transfer_address:
|
||||
# registering for this wallet. Put an empty profile
|
||||
_, _, data_pubkey = get_addresses_from_file(config_dir=config_dir)
|
||||
if not data_pubkey:
|
||||
return {'error': 'No data key in wallet. Please add one with `setup_wallet`'}
|
||||
# registering for this wallet. Put an empty token file, signed with this wallet's signing keys
|
||||
if not wallet_keys:
|
||||
wallet_keys = get_wallet_keys(config_path, password)
|
||||
if 'error' in wallet_keys:
|
||||
return wallet_keys
|
||||
|
||||
user_profile = make_empty_user_profile()
|
||||
res = migrate_profile_to_token_file(fqu, user_profile, get_owner_privkey_info(wallet_keys), config_path=config_path)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to create token file: {}'.format(res['error'])}
|
||||
|
||||
new_token_file = res['token_file']
|
||||
|
||||
# operation checks (API server only)
|
||||
if local_rpc.is_api_server(config_dir=config_dir):
|
||||
@@ -1400,12 +1426,12 @@ def cli_register(args, config_path=CONFIG_PATH, force_data=False,
|
||||
exit(0)
|
||||
|
||||
# forward along to RESTful server (or if we're the RESTful server, call the registrar method)
|
||||
log.debug("Preorder {}, zonefile={}, profile={}, recipient={} min_confs={}".format(fqu, user_zonefile, user_profile, transfer_address, min_payment_confs))
|
||||
log.debug("Preorder {}, zonefile={}, token_file={}, recipient={} min_confs={}".format(fqu, user_zonefile, new_token_file, transfer_address, min_payment_confs))
|
||||
rpc = local_api_connect(config_path=config_path)
|
||||
assert rpc
|
||||
|
||||
try:
|
||||
resp = rpc.backend_preorder(fqu, cost_satoshis, user_zonefile, user_profile, transfer_address, min_payment_confs )
|
||||
resp = rpc.backend_preorder(fqu, cost_satoshis, user_zonefile, new_token_file, transfer_address, min_payment_confs )
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return {'error': 'Error talking to server, try again.'}
|
||||
@@ -1810,13 +1836,47 @@ def cli_revoke(args, config_path=CONFIG_PATH, interactive=True, password=None, p
|
||||
return resp
|
||||
|
||||
|
||||
def cli_migrate(args, config_path=CONFIG_PATH, password=None,
|
||||
def migrate_profile_to_token_file(name, profile, owner_privkey_info, device_id=None, config_path=CONFIG_PATH):
|
||||
"""
|
||||
Given a user's existing profile and the wallet keys,
|
||||
make a serialized token file
|
||||
|
||||
Return {'status': True, 'token_file': token file} on success
|
||||
Return {'error': ...} on error
|
||||
"""
|
||||
config_dir = os.path.dirname(config_path)
|
||||
|
||||
if device_id is None:
|
||||
device_id = get_local_device_id(config_dir)
|
||||
|
||||
name_owner_privkeys = None
|
||||
|
||||
if virtualchain.is_singlesig(owner_privkey_info):
|
||||
name_owner_privkeys = {
|
||||
device_id: str(owner_privkey_info)
|
||||
}
|
||||
|
||||
else:
|
||||
# select the first private key as belonging to this device
|
||||
name_owner_privkeys = {
|
||||
device_id: str(owner_privkey_info['privkeys'][0])
|
||||
}
|
||||
|
||||
res = token_file_create(name, name_owner_privkeys, device_id, profile=profile, config_path=config_path)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to create token file: {}'.format(res['error'])}
|
||||
|
||||
return {'status': True, 'token_file': res['token_file']}
|
||||
|
||||
|
||||
def cli_migrate(args, config_path=CONFIG_PATH, password=None, wallet_keys=None,
|
||||
proxy=None, interactive=True, force=False):
|
||||
"""
|
||||
command: migrate
|
||||
help: Migrate a legacy blockchain-linked profile to the latest zonefile and profile format
|
||||
help: Migrate a legacy profile to the latest zonefile and profile format
|
||||
arg: name (str) 'The blockchain ID with the profile to migrate'
|
||||
opt: force (str) 'Reset the zone file no matter what.'
|
||||
opt: device_id (str) 'If given, use this as the device ID.'
|
||||
"""
|
||||
|
||||
config_dir = os.path.dirname(config_path)
|
||||
@@ -1831,109 +1891,157 @@ def cli_migrate(args, config_path=CONFIG_PATH, password=None,
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
if wallet_keys is None:
|
||||
wallet_keys = get_wallet_keys(config_path, password)
|
||||
if 'error' in wallet_keys:
|
||||
return wallet_keys
|
||||
|
||||
if proxy is None:
|
||||
proxy = get_default_proxy(config_path=config_path)
|
||||
|
||||
fqu = str(args.name)
|
||||
force = (force or (getattr(args, 'force', '').lower() in ['1', 'yes', 'force', 'true']))
|
||||
if hasattr(args, 'force') and args.force is not None:
|
||||
force = args.force.lower() in ['1', 'yes', 'force', 'true']
|
||||
|
||||
error = check_valid_name(fqu)
|
||||
if error:
|
||||
return {'error': error}
|
||||
|
||||
# need data public key
|
||||
_, _, data_pubkey = get_addresses_from_file(config_dir=config_dir)
|
||||
if data_pubkey is None:
|
||||
return {'error': 'No data key in wallet'}
|
||||
|
||||
res = get_name_zonefile(fqu, proxy=proxy)
|
||||
if 'error' in res:
|
||||
log.error("Failed to get zone file for {}: {}".format(fqu, res['error']))
|
||||
return {'error': "Failed to get zone file for {}".format(fqu)}
|
||||
|
||||
user_zonefile = None
|
||||
user_profile = None
|
||||
new_token_file = None
|
||||
|
||||
if 'error' not in res:
|
||||
name_rec = res['name_record']
|
||||
user_zonefile_txt = res['raw_zonefile']
|
||||
user_zonefile_hash = get_zonefile_data_hash(user_zonefile_txt)
|
||||
user_zonefile = None
|
||||
legacy = False
|
||||
nonstandard = False
|
||||
name_rec = res['name_record']
|
||||
existing_profile = res['profile']
|
||||
existing_zonefile = res['zonefile']
|
||||
user_zonefile_txt = res['raw_zonefile']
|
||||
user_zonefile_hash = get_zonefile_data_hash(user_zonefile_txt)
|
||||
user_zonefile = None
|
||||
legacy = False
|
||||
is_token_file = True
|
||||
nonstandard = False
|
||||
need_name_update = False
|
||||
|
||||
# TODO: handle zone files that do not have data keys.
|
||||
# try to parse
|
||||
try:
|
||||
user_zonefile = blockstack_zones.parse_zone_file(user_zonefile_txt)
|
||||
legacy = blockstack_profiles.is_profile_in_legacy_format(user_zonefile)
|
||||
except:
|
||||
log.warning('Non-standard zonefile {}'.format(user_zonefile_hash))
|
||||
nonstandard = True
|
||||
# check the profile. is it a legacy profile, a raw profile, or a token file?
|
||||
res = get_profile(name, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query current profile or token file'}
|
||||
|
||||
if nonstandard:
|
||||
if force:
|
||||
# forcibly reset the zone file
|
||||
user_profile = make_empty_user_profile()
|
||||
user_zonefile = make_empty_zonefile(fqu, data_pubkey)
|
||||
if res['legacy']:
|
||||
log.warn("Zone file is a legacy profile; migration needed")
|
||||
legacy = True
|
||||
|
||||
else:
|
||||
if os.environ.get("BLOCKSTACK_CLIENT_INTERACTIVE_YES", None) != "1":
|
||||
# prompt
|
||||
msg = (
|
||||
''
|
||||
'WARNING! Non-standard zone file detected.'
|
||||
'If you proceed, your zone file will be reset.'
|
||||
''
|
||||
'Proceed? (y/N): '
|
||||
)
|
||||
if res['token_file'] is None:
|
||||
log.warn("Got raw profile; token file migration needed")
|
||||
is_token_file = False
|
||||
|
||||
if res['nonstandard_zonefile']:
|
||||
log.warn("Zone file is non-standard; migration needed")
|
||||
nonstandard = True
|
||||
|
||||
proceed_str = raw_input(msg)
|
||||
proceed = proceed_str.lower() in ['y']
|
||||
if not proceed:
|
||||
return {'error': 'Non-standard zonefile'}
|
||||
|
||||
else:
|
||||
user_profile = make_empty_user_profile()
|
||||
user_zonefile = make_empty_zonefile(fqu, data_pubkey)
|
||||
else:
|
||||
return {'error': 'Non-standard zonefile'}
|
||||
|
||||
# going ahead with zonefile and profile reset
|
||||
if nonstandard:
|
||||
if force:
|
||||
# forcibly reset the zone file
|
||||
user_profile = make_empty_user_profile()
|
||||
user_zonefile = make_empty_zonefile(fqu, None)
|
||||
need_name_update = True
|
||||
|
||||
else:
|
||||
# standard or legacy zone file
|
||||
if not legacy:
|
||||
msg = 'Zone file is in the latest format. No migration needed'
|
||||
return {'error': msg}
|
||||
if os.environ.get("BLOCKSTACK_CLIENT_INTERACTIVE_YES", None) != "1":
|
||||
# prompt
|
||||
msg = (
|
||||
''
|
||||
'WARNING! Non-standard zone file detected.'
|
||||
'If you proceed, your zone file will be reset.'
|
||||
''
|
||||
'Proceed? (y/N): '
|
||||
)
|
||||
|
||||
proceed_str = raw_input(msg)
|
||||
proceed = proceed_str.lower() in ['y']
|
||||
if not proceed:
|
||||
return {'error': 'Non-standard zonefile'}
|
||||
|
||||
else:
|
||||
user_profile = make_empty_user_profile()
|
||||
user_zonefile = make_empty_zonefile(fqu, None)
|
||||
need_name_update = True
|
||||
else:
|
||||
return {'error': 'Non-standard zonefile'}
|
||||
|
||||
# going ahead with zonefile and profile reset
|
||||
|
||||
else:
|
||||
if not legacy:
|
||||
# standard zone file
|
||||
# raw profile?
|
||||
if not is_token_file:
|
||||
log.debug("Migrating raw profile to token file")
|
||||
user_profile = existing_profile
|
||||
user_zonefile = existing_zonefile
|
||||
|
||||
else:
|
||||
log.debug("Zone file, profile, and token file are in the latest format.")
|
||||
return {'status': True}
|
||||
|
||||
else:
|
||||
# legacy zone file that encodes a profile
|
||||
# convert
|
||||
user_profile = blockstack_profiles.get_person_from_legacy_format(user_zonefile)
|
||||
user_zonefile = make_empty_zonefile(fqu, data_pubkey)
|
||||
need_name_update = True
|
||||
|
||||
else:
|
||||
log.error("Failed to get zone file for {}".format(fqu))
|
||||
res = migrate_profile_to_token_file(name, profile, get_owner_privkey_info(wallet_keys), device_id=device_id, config_path=config_path)
|
||||
if 'error' in res:
|
||||
return {'error': res['error']}
|
||||
|
||||
zonefile_txt = blockstack_zones.make_zone_file(user_zonefile)
|
||||
zonefile_hash = get_zonefile_data_hash(zonefile_txt)
|
||||
new_token_file = res['token_file']
|
||||
resp = {}
|
||||
|
||||
rpc = local_api_connect(config_path=config_path)
|
||||
assert rpc
|
||||
if need_name_update:
|
||||
# need to update the zone file
|
||||
zonefile_txt = blockstack_zones.make_zone_file(user_zonefile)
|
||||
zonefile_hash = get_zonefile_data_hash(zonefile_txt)
|
||||
|
||||
try:
|
||||
resp = rpc.backend_update(fqu, zonefile_txt, user_profile, None)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return {'error': 'Error talking to server, try again.'}
|
||||
rpc = local_api_connect(config_path=config_path)
|
||||
assert rpc
|
||||
|
||||
if 'error' in resp:
|
||||
log.debug('RPC error: {}'.format(resp['error']))
|
||||
return resp
|
||||
try:
|
||||
resp = rpc.backend_update(fqu, zonefile_txt, new_token_file, None)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return {'error': 'Error talking to API endpoint, try again.'}
|
||||
|
||||
if (not 'success' in resp or not resp['success']) and 'message' in resp:
|
||||
return {'error': resp['message']}
|
||||
if 'error' in resp:
|
||||
log.debug('RPC error: {}'.format(resp['error']))
|
||||
return resp
|
||||
|
||||
if (not 'success' in resp or not resp['success']) and 'message' in resp:
|
||||
return {'error': resp['message']}
|
||||
|
||||
analytics_event('Migrate name', {})
|
||||
resp['zonefile_hash'] = zonefile_hash
|
||||
|
||||
else:
|
||||
# find the right signing private key to use
|
||||
res = find_signing_privkey(name, wallet_keys=wallet_keys, config_path=config_path, password=password, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
signing_privkey = res['signing_privkey']
|
||||
|
||||
# only need to store the token file
|
||||
res = token_file_put(name, new_token_file, signing_privkey, proxy=proxy, required_drivers=required_storage_drivers, config_path=config_path)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
resp = {'status': True}
|
||||
|
||||
analytics_event('Migrate name', {})
|
||||
|
||||
resp['zonefile_hash'] = zonefile_hash
|
||||
return resp
|
||||
|
||||
|
||||
@@ -2016,7 +2124,7 @@ def cli_setup_wallet(args, config_path=CONFIG_PATH, password=None, interactive=T
|
||||
def cli_get_public_key(args, config_path=CONFIG_PATH, proxy=None):
|
||||
"""
|
||||
command: get_public_key
|
||||
help: Get the ECDSA public key for a blockchain ID
|
||||
help: Get the ECDSA signing public key for a blockchain ID
|
||||
arg: name (str) 'The blockchain ID'
|
||||
"""
|
||||
# reply ENODATA if we can't load the zone file
|
||||
@@ -2033,7 +2141,7 @@ def cli_get_public_key(args, config_path=CONFIG_PATH, proxy=None):
|
||||
return {'error': 'zone file for {} has no public key'.format(fqu), 'errno': errno.EINVAL}
|
||||
|
||||
zfpubkey = keylib.key_formatting.decompress(user_zonefile_data_pubkey(zfinfo['zonefile']))
|
||||
return {'public_key': zfpubkey}
|
||||
return {'status': True, 'public_key': zfpubkey}
|
||||
|
||||
|
||||
def cli_list_accounts( args, proxy=None, config_path=CONFIG_PATH ):
|
||||
@@ -2085,7 +2193,7 @@ def cli_put_account( args, proxy=None, config_path=CONFIG_PATH, password=None, w
|
||||
arg: service (str) 'The service this account is for.'
|
||||
arg: identifier (str) 'The name of the account.'
|
||||
arg: content_url (str) 'The URL that points to external contact data.'
|
||||
arg: signing_privkey (str) 'The device-specific signing private key for this name.'
|
||||
opt: signing_privkey (str) 'The device-specific signing private key for this name.'
|
||||
opt: extra_data (str) 'A comma-separated list of "name1=value1,name2=value2,name3=value3..." with any extra account information you need in the account.'
|
||||
"""
|
||||
password = get_default_password(password)
|
||||
@@ -2101,13 +2209,8 @@ def cli_put_account( args, proxy=None, config_path=CONFIG_PATH, password=None, w
|
||||
service = str(args.service)
|
||||
identifier = str(args.identifier)
|
||||
content_url = str(args.content_url)
|
||||
signing_privkey = str(args.signing_privkey)
|
||||
signing_privkey = None
|
||||
|
||||
try:
|
||||
signing_privkey = ECPrivateKey(signing_privkey).to_hex()
|
||||
except:
|
||||
return {'error': 'Unable to parse private key'}
|
||||
|
||||
if not is_name_valid(args.name):
|
||||
return {'error': 'Invalid name'}
|
||||
|
||||
@@ -2131,6 +2234,13 @@ def cli_put_account( args, proxy=None, config_path=CONFIG_PATH, password=None, w
|
||||
|
||||
v = "=".join(parts[1:])
|
||||
extra_data[k] = v
|
||||
|
||||
# find the right signing private key to use
|
||||
res = find_signing_privkey(name, getattr(args, 'signing_privkey', None), wallet_keys=wallet_keys, config_path=config_path, password=password, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
signing_privkey = res['signing_privkey']
|
||||
|
||||
return profile_put_account(name, service, identifier, content_url, extra_data, signing_privkey, config_path=config_path, proxy=proxy)
|
||||
|
||||
@@ -2142,7 +2252,7 @@ def cli_delete_account( args, proxy=None, config_path=CONFIG_PATH, password=None
|
||||
arg: name (str) 'The name to query.'
|
||||
arg: service (str) 'The service the account is for.'
|
||||
arg: identifier (str) 'The identifier of the account to delete.'
|
||||
arg: signing_privkey (str) 'The device-specific signing private key for this name.'
|
||||
opt: signing_privkey (str) 'The device-specific signing private key for this name.'
|
||||
"""
|
||||
password = get_default_password(password)
|
||||
proxy = get_default_proxy(config_path=config_path) if proxy is None else proxy
|
||||
@@ -2156,12 +2266,14 @@ def cli_delete_account( args, proxy=None, config_path=CONFIG_PATH, password=None
|
||||
name = str(args.name)
|
||||
service = str(args.service)
|
||||
identifier = str(args.identifier)
|
||||
signing_privkey = str(args.signing_privkey)
|
||||
signing_privkey = None
|
||||
|
||||
try:
|
||||
signing_privkey = ECPrivateKey(signing_privkey).to_hex()
|
||||
except:
|
||||
return {'error': 'Unable to parse private key'}
|
||||
# find the right signing private key to use
|
||||
res = find_signing_privkey(name, getattr(args, 'signing_privkey', None), wallet_keys=wallet_keys, config_path=config_path, password=password, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
signing_privkey = res['signing_privkey']
|
||||
|
||||
if not is_name_valid(name):
|
||||
return {'error': 'Invalid name'}
|
||||
@@ -3342,6 +3454,12 @@ def cli_get_mutable(args, config_path=CONFIG_PATH, proxy=None):
|
||||
opt: device_ids (str) 'A CSV of devices to query'
|
||||
"""
|
||||
data_pubkey = str(args.data_pubkey) if hasattr(args, 'data_pubkey') and getattr(args, 'data_pubkey') is not None else None
|
||||
name = str(args.name)
|
||||
proxy = get_default_proxy() if proxy is None else proxy
|
||||
|
||||
pubkeys = []
|
||||
addresses = []
|
||||
device_pubkeys = None
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
@@ -3349,9 +3467,16 @@ def cli_get_mutable(args, config_path=CONFIG_PATH, proxy=None):
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
else:
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
res = find_signing_pubkeys_and_address(name, data_pubkey, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
result = get_mutable(str(args.data_id), device_ids['device_ids'], proxy=proxy, config_path=config_path, blockchain_id=str(args.name), data_pubkey=data_pubkey)
|
||||
pubkeys = res['pubkeys']
|
||||
addresses = [res['address']]
|
||||
device_pubkeys = res['device_pubkeys']
|
||||
device_ids = device_pubkeys.keys()
|
||||
|
||||
result = get_mutable(str(args.data_id), device_ids, proxy=proxy, config_path=config_path, blockchain_id=str(args.name), device_data_pubkeys=device_pubkeys, data_pubkeys=pubkeys, data_pubkey_hashes=addresses)
|
||||
if 'error' in result:
|
||||
return result
|
||||
|
||||
@@ -3775,6 +3900,7 @@ def find_signing_privkey(name, args_signing_privkey, wallet_keys=None, config_pa
|
||||
|
||||
res = lookup_signing_privkey(name, get_owner_privkey_info(wallet_keys), proxy=proxy)
|
||||
if 'error' in res:
|
||||
log.error("Failed to load signing key for {}: {}".format(name, res['error']))
|
||||
return {'error': 'Failed to look up signing key from wallet. Try passing it explicitly as an argument.'}
|
||||
|
||||
signing_privkey = res['signing_privkey']
|
||||
@@ -3786,7 +3912,11 @@ def find_signing_pubkeys_and_address(name, args_signing_pubkey, proxy=None):
|
||||
Find the set of public keys and possibly address to use for a given name.
|
||||
Use either the one given in the args (args_signing_pubkey), or look it up from the token file, zone file, and name record.
|
||||
|
||||
Return {'status': True, 'pubkeys': [public keys, including zone file pubkey], 'address': owner address} on success
|
||||
Return {
|
||||
'status': True,
|
||||
'pubkeys': [public keys, including zone file pubkey],
|
||||
'device_pubkeys': {'$device_id': '$device_signing_pubkey'},
|
||||
'address': owner address} on success
|
||||
Return {'error': ...} on failure
|
||||
"""
|
||||
|
||||
@@ -3809,6 +3939,7 @@ def find_signing_pubkeys_and_address(name, args_signing_pubkey, proxy=None):
|
||||
log.error("Failed to look up signing keys for {}: {}".format(name, res['error']))
|
||||
return {'error': 'Failed to look up public keys for {}'.format(name)}
|
||||
|
||||
device_pubkeys = res['pubkeys']
|
||||
pubkeys = res['pubkeys'].values()
|
||||
|
||||
# also grab the zone file public key, if present
|
||||
@@ -3823,7 +3954,38 @@ def find_signing_pubkeys_and_address(name, args_signing_pubkey, proxy=None):
|
||||
|
||||
owner_address = res['name_record']['address']
|
||||
|
||||
return {'status': True, 'pubkeys': pubkeys, 'address': owner_address}
|
||||
return {'status': True, 'pubkeys': pubkeys, 'device_pubkeys': device_pubkeys, 'address': owner_address}
|
||||
|
||||
|
||||
def find_datastore_device_pubkeys(blockchain_id, args_device_ids, args_device_pubkeys, proxy=None):
|
||||
"""
|
||||
Find the set of device IDs and public keys for a given name.
|
||||
Use either the CSV string given in args (args_device_dis), or look it up from the token file.
|
||||
|
||||
Return {'status': True, 'device_ids': [{'device_id': ..., 'pubkey': ...}...[} on success
|
||||
Return {'errro': ...} on error
|
||||
"""
|
||||
pubkeys = None
|
||||
|
||||
# get the list of device IDs to use
|
||||
if args_device_ids and args_device_pubkeys:
|
||||
device_ids = args_device_ids.split(',')
|
||||
device_pubkeys = args_device_pubkeys.split(',')
|
||||
assert len(device_ids) == len(device_pubkeys)
|
||||
|
||||
pubkeys = {}
|
||||
for i in xrange(0, len(device_ids)):
|
||||
pubkeys[device_ids[i]] = device_pubkeys[i]
|
||||
|
||||
else:
|
||||
# find from token file
|
||||
res = lookup_delegated_device_pubkeys(blockchain_id, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query device list for {}: {}'.format(blockchain_id, res['error'])}
|
||||
|
||||
pubkeys = res['pubkeys'].keys()
|
||||
|
||||
return {'status': True, 'device_ids': pubkeys.keys(), 'pubkeys': pubkeys}
|
||||
|
||||
|
||||
def cli_put_profile(args, config_path=CONFIG_PATH, password=None, proxy=None, force_data=False, wallet_keys=None):
|
||||
@@ -3865,9 +4027,6 @@ def cli_put_profile(args, config_path=CONFIG_PATH, password=None, proxy=None, fo
|
||||
except:
|
||||
return {'error': 'Invalid profile JSON'}
|
||||
|
||||
required_storage_drivers = conf.get('storage_drivers_required_write', config.BLOCKSTACK_REQUIRED_STORAGE_DRIVERS_WRITE)
|
||||
required_storage_drivers = required_storage_drivers.split()
|
||||
|
||||
# get the current profile or token file. If this is a profile, then tell the user to migrate.
|
||||
res = get_profile(name, proxy=proxy)
|
||||
if 'error' in res:
|
||||
@@ -3877,7 +4036,7 @@ def cli_put_profile(args, config_path=CONFIG_PATH, password=None, proxy=None, fo
|
||||
return {'error': 'Profile is in legacy format (version 1). Please use the `migrate` command to migrate your profile to the latest format.'}
|
||||
|
||||
if res['token_file'] is None:
|
||||
return {'error': 'Profile is in legacy format (version 2). Please use the `migrate` command to migrate your profile to the latest format.'}
|
||||
return {'error': 'Name points to a raw profile (version 2). Please use the `migrate` command to migrate your profile to the latest format.'}
|
||||
|
||||
# got a token file
|
||||
parsed_token_file = res['token_file']
|
||||
@@ -3886,7 +4045,7 @@ def cli_put_profile(args, config_path=CONFIG_PATH, password=None, proxy=None, fo
|
||||
return {'error': 'Failed to update token file: {}'.format(res['error'])}
|
||||
|
||||
# save it
|
||||
res = token_file_put(name, res['token_file'], signing_privkey, proxy=proxy, required_drivers=required_storage_drivers, config_path=config_path)
|
||||
res = token_file_put(name, res['token_file'], signing_privkey, proxy=proxy, config_path=config_path)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
@@ -3906,8 +4065,6 @@ def cli_delete_profile(args, config_path=CONFIG_PATH, password=None, proxy=None,
|
||||
signing_privkey = None
|
||||
|
||||
name = str(args.blockchain_id)
|
||||
if not hasattr(arg, 'signing_privkey'):
|
||||
return {'error': 'The "signing_privkey" argument is required at this time, but will be optional in future releases'}
|
||||
|
||||
# find the right signing private key to use
|
||||
res = find_signing_privkey(name, getattr(args, 'signing_privkey', None), wallet_keys=wallet_keys, config_path=config_path, password=password, proxy=proxy)
|
||||
@@ -3916,7 +4073,29 @@ def cli_delete_profile(args, config_path=CONFIG_PATH, password=None, proxy=None,
|
||||
|
||||
signing_privkey = res['signing_privkey']
|
||||
|
||||
res = delete_profile(name, signing_privkey, proxy=proxy)
|
||||
# get the current profile or token file. If this is a profile, then tell the user to migrate.
|
||||
res = get_profile(name, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query current profile or token file'}
|
||||
|
||||
if res['legacy']:
|
||||
return {'error': 'Profile is in legacy format (version 1). Please use the `migrate` command to migrate your profile to the latest format.'}
|
||||
|
||||
if res['token_file'] is None:
|
||||
return {'error': 'Name points to a raw profile (version 2). Please use the `migrate` command to migrate your profile to the latest format.'}
|
||||
|
||||
# got a token file. put an empty profile
|
||||
empty_profile = make_empty_user_profile(config_path=config_path)
|
||||
parsed_token_file = res['token_file']
|
||||
res = token_file_update_profile(parsed_token_file, empty_profile, signing_privkey)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to update token file: {}'.format(res['error'])}
|
||||
|
||||
# save it
|
||||
res = token_file_put(name, res['token_file'], signing_privkey, proxy=proxy, config_path=config_path)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -4079,13 +4258,6 @@ def cli_convert_legacy_profile(args, config_path=CONFIG_PATH):
|
||||
return profile
|
||||
|
||||
|
||||
def get_app_name(appname):
|
||||
"""
|
||||
Get the application name, or if not given, the default name
|
||||
"""
|
||||
return appname if appname is not None else '_default'
|
||||
|
||||
|
||||
def cli_app_publish( args, config_path=CONFIG_PATH, interactive=False, password=None, proxy=None ):
|
||||
"""
|
||||
command: app_publish advanced
|
||||
@@ -4335,6 +4507,7 @@ def cli_sign_profile( args, config_path=CONFIG_PATH, proxy=None, password=None,
|
||||
"""
|
||||
command: sign_profile advanced raw
|
||||
help: Sign a JSON file to be used as a profile.
|
||||
arg: blockchain_id (str) 'The blockchain ID that will own this profile'
|
||||
arg: path (str) 'The path to the profile data on disk.'
|
||||
opt: privkey (str) 'The device-specific signing key'
|
||||
"""
|
||||
@@ -4344,11 +4517,12 @@ def cli_sign_profile( args, config_path=CONFIG_PATH, proxy=None, password=None,
|
||||
password = get_default_password(password)
|
||||
|
||||
config_dir = os.path.dirname(config_path)
|
||||
name = str(args.blockchain_id)
|
||||
path = str(args.path)
|
||||
data_json = None
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
dat = f.read()
|
||||
dat = f.read().strip()
|
||||
data_json = json.loads(dat)
|
||||
except Exception as e:
|
||||
if os.environ.get("BLOCKSTACK_DEBUG") == "1":
|
||||
@@ -4419,7 +4593,7 @@ def cli_verify_profile( args, config_path=CONFIG_PATH, proxy=None, interactive=F
|
||||
return {'error': 'Failed to verify profile with all available public keys'}
|
||||
|
||||
|
||||
def cli_sign_data( args, config_path=CONFIG_PATH, proxy=None, password=None, interactive=False ):
|
||||
def cli_sign_data( args, config_path=CONFIG_PATH, proxy=None, password=None, interactive=False, wallet_keys=None ):
|
||||
"""
|
||||
command: sign_data advanced raw
|
||||
help: Sign data to be used in a data store.
|
||||
@@ -4432,8 +4606,9 @@ def cli_sign_data( args, config_path=CONFIG_PATH, proxy=None, password=None, int
|
||||
proxy = get_default_proxy(config_path=config_path)
|
||||
|
||||
password = get_default_password(password)
|
||||
|
||||
config_dir = os.path.dirname(config_path)
|
||||
|
||||
name = str(args.name)
|
||||
path = str(args.path)
|
||||
data = None
|
||||
try:
|
||||
@@ -4560,22 +4735,31 @@ def cli_list_devices( args, config_path=CONFIG_PATH, proxy=None ):
|
||||
command: list_devices advanced
|
||||
help: Get the list of device IDs and public keys for a particular application
|
||||
arg: blockchain_id (str) 'The blockchain ID whose devices to list'
|
||||
arg: appname (str) 'The name of the application'
|
||||
"""
|
||||
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
proxy = get_default_proxy() if proxy is None else proxy
|
||||
|
||||
res = lookup_delegated_device_pubkeys(str(args.name), proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
pubkeys = res['pubkeys']
|
||||
device_ids = pubkeys.keys()
|
||||
|
||||
return {'status': True, 'device_ids': device_ids}
|
||||
|
||||
|
||||
def cli_add_device( args, config_path=CONFIG_PATH, proxy=None ):
|
||||
def cli_add_device( args, config_path=CONFIG_PATH, proxy=None, wallet_keys=None ):
|
||||
"""
|
||||
command: add_device advanced
|
||||
help: Add a device that can read and write your data
|
||||
arg: blockchain_id (str) 'The blockchain ID whose profile to update'
|
||||
opt: device_id (str) 'The ID of the device to add, if not this one'
|
||||
arg: device_id (str) 'The ID of the device to add'
|
||||
opt: signing_privkey (str) 'The signing private key to use'
|
||||
"""
|
||||
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
|
||||
def cli_remove_device( args, config_path=CONFIG_PATH, proxy=None ):
|
||||
"""
|
||||
@@ -4848,20 +5032,6 @@ def datastore_inode_getinode(datastore_type, blockchain_id, datastore_id, inode_
|
||||
|
||||
res = datastore_getinode( rpc, blockchain_id, datastore, inode_uuid, data_pubkeys, extended=False, force=force, idata=idata, config_path=config_path )
|
||||
return res
|
||||
|
||||
|
||||
def cli_get_device_keys( args, config_path=CONFIG_PATH ):
|
||||
"""
|
||||
command: get_device_keys advanced
|
||||
help: Get the device IDs and public keys for a blockchain ID
|
||||
arg: blockchain_id (str) 'The blockchain Id'
|
||||
"""
|
||||
blockchain_id = str(args.blockchain_id)
|
||||
|
||||
# TODO: implement token file support
|
||||
log.warning("Token file support is NOT IMPLEMENTED")
|
||||
|
||||
# find the "data public key"
|
||||
|
||||
|
||||
def cli_get_datastore( args, config_path=CONFIG_PATH ):
|
||||
@@ -4874,14 +5044,17 @@ def cli_get_datastore( args, config_path=CONFIG_PATH ):
|
||||
"""
|
||||
blockchain_id = str(args.blockchain_id)
|
||||
datastore_id = str(args.datastore_id)
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
if device_ids:
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
device_ids = None
|
||||
if getattr(args, 'device_ids', None) is not None:
|
||||
device_ids = args.device_ids.split(',')
|
||||
|
||||
else:
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
res = lookup_delegated_device_pubkeys(blockchain_id, proxy=proxy)
|
||||
if 'error' in res:
|
||||
return res
|
||||
|
||||
device_ids = res['pubkeys'].keys()
|
||||
|
||||
return get_datastore_by_type('datastore', blockchain_id, datastore_id, device_ids, config_path=config_path )
|
||||
|
||||
@@ -4891,7 +5064,7 @@ def cli_create_datastore( args, config_path=CONFIG_PATH, proxy=None ):
|
||||
command: create_datastore advanced
|
||||
help: Make a new datastore
|
||||
arg: blockchain_id (str) 'The blockchain ID that will own this datastore'
|
||||
arg: privkey (str) 'The ECDSA private key of the datastore'
|
||||
arg: privkey (str) 'The app-specific private key of the datastore'
|
||||
arg: session (str) 'The API session token'
|
||||
opt: drivers (str) 'A CSV of drivers to use.'
|
||||
"""
|
||||
@@ -4913,7 +5086,7 @@ def cli_delete_datastore( args, config_path=CONFIG_PATH ):
|
||||
command: delete_datastore advanced
|
||||
help: Delete a datastore owned by a given user, and all of the data it contains.
|
||||
arg: blockchain_id (str) 'The owner of this datastore'
|
||||
arg: privkey (str) 'The ECDSA private key of the datastore'
|
||||
arg: privkey (str) 'The app-specific private key of the datastore'
|
||||
arg: session (str) 'The API session token'
|
||||
opt: force (str) 'If True, then delete the datastore even if it cannot be emptied'
|
||||
"""
|
||||
@@ -5074,25 +5247,11 @@ def cli_datastore_getfile( args, config_path=CONFIG_PATH, interactive=False ):
|
||||
if hasattr(args, 'force') and args.force.lower() in ['1', 'true']:
|
||||
force = True
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
if device_ids:
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
else:
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
device_pubkeys = None
|
||||
if hasattr(args, 'device_pubkeys'):
|
||||
device_pubkeys = str(args.device_pubkeys).split(',')
|
||||
else:
|
||||
raise NotImplemented("No support for token files")
|
||||
|
||||
assert len(device_ids) == len(device_pubkeys)
|
||||
data_pubkeys = [{
|
||||
'device_id': dev_id,
|
||||
'public_key': pubkey
|
||||
} for (dev_id, pubkey) in zip(device_ids, device_pubkeys)]
|
||||
res = find_datastore_device_pubkeys(name, getattr(args, 'device_ids', None), getattr(args, 'device_pubkeys', None), proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query device list for {}: {}'.format(name, res['error'])}
|
||||
|
||||
data_pubkeys = res['pubkeys']
|
||||
|
||||
res = datastore_file_get('datastore', blockchain_id, datastore_id, path, data_pubkeys, extended=extended, force=force, config_path=config_path)
|
||||
if json_is_error(res):
|
||||
@@ -5128,7 +5287,6 @@ def cli_datastore_listdir(args, config_path=CONFIG_PATH, interactive=False ):
|
||||
path = str(args.path)
|
||||
extended = False
|
||||
force = False
|
||||
device_ids = None
|
||||
|
||||
if hasattr(args, 'extended') and args.extended.lower() in ['1', 'true']:
|
||||
extended = True
|
||||
@@ -5136,25 +5294,11 @@ def cli_datastore_listdir(args, config_path=CONFIG_PATH, interactive=False ):
|
||||
if hasattr(args, 'force') and args.force.lower() in ['1', 'true']:
|
||||
force = True
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
if device_ids:
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
else:
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
device_pubkeys = None
|
||||
if hasattr(args, 'device_pubkeys'):
|
||||
device_pubkeys = str(args.device_pubkeys).split(',')
|
||||
else:
|
||||
raise NotImplemented("No support for token files")
|
||||
|
||||
assert len(device_ids) == len(device_pubkeys)
|
||||
data_pubkeys = [{
|
||||
'device_id': dev_id,
|
||||
'public_key': pubkey
|
||||
} for (dev_id, pubkey) in zip(device_ids, device_pubkeys)]
|
||||
res = find_datastore_device_pubkeys(name, getattr(args, 'device_ids', None), getattr(args, 'device_pubkeys', None), proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query device list for {}: {}'.format(name, res['error'])}
|
||||
|
||||
data_pubkeys = res['pubkeys']
|
||||
|
||||
res = datastore_dir_list('datastore', blockchain_id, datastore_id, path, data_pubkeys, extended=extended, force=force, config_path=config_path )
|
||||
if json_is_error(res):
|
||||
@@ -5198,27 +5342,11 @@ def cli_datastore_stat(args, config_path=CONFIG_PATH, interactive=False ):
|
||||
if hasattr(args, 'force') and args.force.lower() in ['1', 'true']:
|
||||
force = True
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
if device_ids:
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
else:
|
||||
# TODO
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
device_pubkeys = None
|
||||
if hasattr(args, 'device_pubkeys'):
|
||||
device_pubkeys = str(args.device_pubkeys).split(',')
|
||||
else:
|
||||
# TODO
|
||||
raise NotImplemented("No support for token files")
|
||||
|
||||
assert len(device_ids) == len(device_pubkeys)
|
||||
data_pubkeys = [{
|
||||
'device_id': dev_id,
|
||||
'public_key': pubkey
|
||||
} for (dev_id, pubkey) in zip(device_ids, device_pubkeys)]
|
||||
res = find_datastore_device_pubkeys(name, getattr(args, 'device_ids', None), getattr(args, 'device_pubkeys', None), proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query device list for {}: {}'.format(name, res['error'])}
|
||||
|
||||
data_pubkeys = res['pubkeys']
|
||||
|
||||
res = datastore_path_stat('datastore', blockchain_id, datastore_id, path, data_pubkeys, extended=extended, force=force, config_path=config_path)
|
||||
if json_is_error(res):
|
||||
@@ -5270,27 +5398,11 @@ def cli_datastore_getinode(args, config_path=CONFIG_PATH, interactive=False):
|
||||
if hasattr(args, 'idata') and args.idata.lower() in ['1', 'true']:
|
||||
idata = True
|
||||
|
||||
# get the list of device IDs to use
|
||||
device_ids = getattr(args, 'device_ids', None)
|
||||
if device_ids:
|
||||
device_ids = device_ids.split(',')
|
||||
|
||||
else:
|
||||
# TODO
|
||||
raise NotImplemented("Missing token file parsing logic")
|
||||
|
||||
device_pubkeys = None
|
||||
if hasattr(args, 'device_pubkeys'):
|
||||
device_pubkeys = str(args.device_pubkeys).split(',')
|
||||
else:
|
||||
# TODO
|
||||
raise NotImplemented("No support for token files")
|
||||
|
||||
assert len(device_ids) == len(device_pubkeys)
|
||||
data_pubkeys = [{
|
||||
'device_id': dev_id,
|
||||
'public_key': pubkey
|
||||
} for (dev_id, pubkey) in zip(device_ids, device_pubkeys)]
|
||||
res = find_datastore_device_pubkeys(name, getattr(args, 'device_ids', None), getattr(args, 'device_pubkeys', None), proxy=proxy)
|
||||
if 'error' in res:
|
||||
return {'error': 'Failed to query device list for {}: {}'.format(name, res['error'])}
|
||||
|
||||
data_pubkeys = res['pubkeys']
|
||||
|
||||
return datastore_inode_getinode('datastore', blockchain_id, datastore_id, inode_uuid, data_pubkeys, extended=extended, force=force, idata=idata, config_path=config_path)
|
||||
|
||||
@@ -5364,12 +5476,6 @@ def cli_datastore_get_privkey(args, config_path=CONFIG_PATH, interactive=False )
|
||||
"""
|
||||
raise NotImplemented("Token file support not yet implemented")
|
||||
|
||||
app_domain = str(args.app_domain)
|
||||
master_privkey = str(args.master_privkey)
|
||||
|
||||
datastore_privkey = datastore_get_privkey(master_privkey, app_domain, config_path=config_path)
|
||||
return {'status': True, 'datastore_privkey': datastore_privkey}
|
||||
|
||||
|
||||
def cli_datastore_get_id(args, config_path=CONFIG_PATH, interactive=False ):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user