コード例 #1
0
def write_dht_profile(profile):

    resp = None
    dht_client = get_dht_client()

    if is_profile_in_legacy_format(profile):
        key = get_hash(profile)
        value = json.dumps(profile, sort_keys=True)
    else:
        key = hex_hash160(profile)
        value = profile

    if len(value) > MAX_DHT_WRITE:
        log.debug("DHT value too large: %s, %s" % (key, len(value)))
        return resp

    log.debug("DHT write (%s, %s)" % (key, value))

    try:
        resp = dht_client.set(key, value)
        log.debug(pprint(resp))
    except Exception as e:
        log.debug(e)

    return resp
コード例 #2
0
def store_name_zonefile(name, user_zonefile, txid, storage_drivers=None):
    """
    Store JSON user zonefile data to the immutable storage providers, synchronously.
    This is only necessary if we've added/changed/removed immutable data.

    Return (True, hash(user zonefile)) on success
    Return (False, None) on failure
    """

    storage_drivers = [] if storage_drivers is None else storage_drivers

    assert not blockstack_profiles.is_profile_in_legacy_format(
        user_zonefile), 'User zonefile is a legacy profile'
    assert user_db.is_user_zonefile(
        user_zonefile), 'Not a user zonefile (maybe a custom legacy profile?)'

    # serialize and send off
    user_zonefile_txt = blockstack_zones.make_zone_file(user_zonefile,
                                                        origin=name,
                                                        ttl=USER_ZONEFILE_TTL)

    return store_name_zonefile_data(name,
                                    user_zonefile_txt,
                                    txid,
                                    storage_drivers=storage_drivers)
コード例 #3
0
ファイル: network.py プロジェクト: zhilinwww/blockstack-core
def write_dht_profile(profile):

    resp = None
    dht_client = get_dht_client()

    if is_profile_in_legacy_format(profile):
        key = get_hash(profile)
        value = json.dumps(profile, sort_keys=True)
    else:
        key = hex_hash160(profile)
        value = profile

    if len(value) > MAX_DHT_WRITE:
        log.debug("DHT value too large: %s, %s" % (key, len(value)))
        return resp

    log.debug("DHT write (%s, %s)" % (key, value))

    try:
        resp = dht_client.set(key, value)
        log.debug(pprint(resp))
    except Exception as e:
        log.debug(e)

    return resp
コード例 #4
0
def _get_person_profile(name, proxy=None):
    """
    Get the person's zonefile and profile.
    Handle legacy zonefiles, but not legacy profiles.
    Return {'profile': ..., 'zonefile': ..., 'person': ...} on success
    Return {'error': ...} on error
    """

    res = get_profile(name, proxy=proxy, use_legacy_zonefile=True)
    if 'error' in res:
        return {'error': 'Failed to load zonefile: {}'.format(res['error'])}

    profile = res.pop('profile')
    zonefile = res.pop('zonefile')

    if blockstack_profiles.is_profile_in_legacy_format(profile):
        return {'error': 'Legacy profile'}

    person = None
    try:
        person = blockstack_profiles.Person(profile)
    except Exception as e:
        log.exception(e)
        return {'error': 'Failed to parse profile data into a Person record'}

    return {'profile': profile, 'zonefile': zonefile, 'person': person}
コード例 #5
0
def _get_person_profile(name, proxy=None):
    """
    Get the person's zonefile and profile.
    Handle legacy zonefiles, but not legacy profiles.
    Return {'profile': ..., 'zonefile': ..., 'person': ...} on success
    Return {'error': ...} on error
    """

    res = get_profile(name, proxy=proxy, use_legacy_zonefile=True)
    if 'error' in res:
        return {'error': 'Failed to load zonefile: {}'.format(res['error'])}

    profile = res.pop('profile')
    zonefile = res.pop('zonefile')

    if blockstack_profiles.is_profile_in_legacy_format(profile):
        return {'error': 'Legacy profile'}

    person = None
    try:
        person = blockstack_profiles.Person(profile)
    except Exception as e:
        log.exception(e)
        return {'error': 'Failed to parse profile data into a Person record'}
    
    return {'profile': profile, 'zonefile': zonefile, 'person': person}
コード例 #6
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def get_immutable(name, data_hash, data_id=None, proxy=None):
    """
    get_immutable

    Fetch a piece of immutable data.  Use @data_hash to look it up
    in the user's zonefile, and then fetch and verify the data itself
    from the configured storage providers.

    Return {'data': the data, 'hash': hash} on success
    Return {'error': ...} on failure
    """

    if proxy is None:
        proxy = get_default_proxy()

    user_zonefile = get_name_zonefile(name, proxy=proxy)
    if user_zonefile is None:
        return {'error': 'No user zonefile defined'}

    if 'error' in user_zonefile:
        return user_zonefile 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # zonefile is really a legacy profile 
        return {'error': 'Profile is in a legacy format that does not support immutable data.'}

    if data_id is not None:
        # look up hash by name 
        h = user_db.get_immutable_data_hash( user_zonefile, data_id )
        if h is None:
            return {'error': 'No such immutable datum'}
         
        if type(h) == list:
            # this tool doesn't allow this to happen (one ID matches one hash),
            # but that doesn't preclude the user from doing this with other tools.
            if data_hash is not None and data_hash not in h:
                return {'error': 'Data ID/hash mismatch'}

            else:
                return {'error': "Multiple matches for '%s': %s" % (data_id, ",".join(h))}

        if data_hash is not None:
            if h != data_hash:
                return {'error': 'Data ID/hash mismatch'}

        else:
            data_hash = h

    elif not user_db.has_immutable_data( user_zonefile, data_hash ):
        return {'error': 'No such immutable datum'}

    data_url_hint = user_db.get_immutable_data_url( user_zonefile, data_hash )
    data = storage.get_immutable_data( data_hash, fqu=name, data_id=data_id, data_url=data_url_hint )
    if data is None:
        return {'error': 'No immutable data returned'}

    return {'data': data, 'hash': data_hash}
コード例 #7
0
def convert_profile_format(user):

    if is_profile_in_legacy_format(user['profile']):
        data_value = user['profile']
    else:
        if 'zone_file' in user:
            data_value = user['zone_file']
        else:
            data_value = {}

    return data_value
コード例 #8
0
def convert_profile_format(user):

    if is_profile_in_legacy_format(user['profile']):
        data_value = user['profile']
    else:
        if 'zone_file' in user:
            data_value = user['zone_file']
        else:
            data_value = {}

    return data_value
コード例 #9
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def delete_mutable(name, data_id, proxy=None, wallet_keys=None):
    """
    delete_mutable

    Remove a piece of mutable data from the user's profile. Delete it from
    the storage providers as well.

    Returns a dict with {'status': True} on success
    Returns a dict with {'error': ...} on failure
    """

    if proxy is None:
        proxy = get_default_proxy()
 
    fq_data_id = storage.make_fq_data_id( name, data_id )
    legacy = False
    user_profile, user_zonefile = get_name_profile( name, proxy=proxy, wallet_keys=wallet_keys, include_name_record=True )
    if user_profile is None:
        return user_zonefile    # will be an error message 

    name_record = user_zonefile['name_record']
    del user_zonefile['name_record']

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # zonefile is a legacy profile.  There is no immutable data 
        log.info("Profile is in legacy format.  No immutable data.")
        return {'status': True}

    # already deleted?
    if not user_db.has_mutable_data( user_profile, data_id ):
        return {'status': True}

    # unlink
    user_db.remove_mutable_data_zonefile( user_profile, data_id )

    # put new profile 
    data_privkey = get_data_or_owner_privkey( user_zonefile, name_record['address'], wallet_keys=wallet_keys, config_path=proxy.conf['path'] )
    if 'error' in data_privkey:
        return {'error': data_privkey['error']}
    else:
        data_privkey = data_privkey['privatekey']
        assert data_privkey is not None

    rc = storage.put_mutable_data( name, user_profile, data_privkey )
    if not rc:
        return {'error': 'Failed to unlink mutable data from profile'}

    # remove the data itself 
    rc = storage.delete_mutable_data( fq_data_id, data_privkey )
    if not rc:
        return {'error': 'Failed to delete mutable data from storage providers'}

    return {'status': True}
コード例 #10
0
def store_name_zonefile(name, user_zonefile, txid, storage_drivers=None):
    """
    Store JSON user zonefile data to the immutable storage providers, synchronously.
    This is only necessary if we've added/changed/removed immutable data.

    Return (True, hash(user zonefile)) on success
    Return (False, None) on failure
    """

    storage_drivers = [] if storage_drivers is None else storage_drivers

    assert not blockstack_profiles.is_profile_in_legacy_format(user_zonefile), 'User zonefile is a legacy profile'
    assert user_db.is_user_zonefile(user_zonefile), 'Not a user zonefile (maybe a custom legacy profile?)'

    # serialize and send off
    user_zonefile_txt = blockstack_zones.make_zone_file(user_zonefile, origin=name, ttl=USER_ZONEFILE_TTL)

    return store_name_zonefile_data(name, user_zonefile_txt, txid, storage_drivers=storage_drivers)
コード例 #11
0
def format_profile(profile, username, address):
    """ Process profile data and
        1) Insert verifications
        2) Check if profile data is valid JSON
    """

    data = {}

    # save the original profile, in case it's a zone file
    zone_file = profile

    if 'error' in profile:
        data['profile'] = {}
        data['error'] = profile['error']
        data['verifications'] = []

        return data

    try:
        profile = resolve_zone_file_to_profile(profile, address)
    except:
        if 'message' in profile:
            data['profile'] = json.loads(profile)
            data['verifications'] = []
            return data

    if profile is None:
        data['profile'] = {}
        data['error'] = "Malformed profile data."
        data['verifications'] = []
    else:
        if not is_profile_in_legacy_format(profile):
            data['zone_file'] = zone_file
            data['profile'] = profile
            data['verifications'] = profile_v3_to_proofs(
                data['profile'], username)
        else:
            data['profile'] = json.loads(profile)
            data['verifications'] = profile_to_proofs(data['profile'],
                                                      username)

    return data
コード例 #12
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def list_mutable_data( name, proxy=None, wallet_keys=None ):
    """
    List the names and versions of all mutable data in a user's zonefile
    Returns {"data": [{"data_id": data ID, "version": version}]}
    """
    if proxy is None:
        proxy = get_default_proxy()

    user_profile, user_zonefile = get_name_profile( name, proxy=proxy, wallet_keys=wallet_keys )
    if user_zonefile is None:
        # user_profile will contain an error message
        return user_profile 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # zonefile is really a legacy profile
        return {"data": []}

    names_and_versions = user_db.list_mutable_data( user_profile )
    listing = [ {"data_id": nv[0], "version": nv[1]} for nv in names_and_versions ]
    return {"data": listing}
コード例 #13
0
def load_legacy_user_profile(name, expected_hash):
    """
    Load a legacy user profile, and convert it into
    the new zonefile-esque profile format that can
    be serialized into a JWT.

    Verify that the profile hashses to the above expected hash
    """

    # fetch...
    storage_host = 'onename.com'
    assert name.endswith('.id')

    name_without_namespace = '.'.join(name.split('.')[:-1])
    storage_path = '/{}.json'.format(name_without_namespace)

    try:
        req = httplib.HTTPConnection(storage_host)
        resp = req.request('GET', storage_path)
        data = resp.read()
    except Exception as e:
        log.error('Failed to fetch http://{}/{}: {}'.format(
            storage_host, storage_path, e))
        return None

    try:
        data_json = json.loads(data)
    except Exception as e:
        log.error('Unparseable profile data')
        return None

    data_hash = storage.get_blockchain_compat_hash(data_json)
    if expected_hash != data_hash:
        log.error('Hash mismatch: expected {}, got {}'.format(
            expected_hash, data_hash))
        return None

    assert blockstack_profiles.is_profile_in_legacy_format(data_json)
    new_profile = blockstack_profiles.get_person_from_legacy_format(data_json)
    return new_profile
コード例 #14
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def list_immutable_data( name, proxy=None ):
    """
    List the names and hashes of all immutable data in a user's zonefile.
    Returns {"data": [{"data_id": data_id, "hash": hash}]} on success
    """
    if proxy is None:
        proxy = get_default_proxy()

    user_zonefile = get_name_zonefile(name, proxy=proxy)
    if user_zonefile is None:
        return {'error': 'No user zonefile defined'}

    if 'error' in user_zonefile:
        return user_zonefile 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # zonefile is really a legacy profile
        return {"data": []}

    names_and_hashes = user_db.list_immutable_data( user_zonefile )
    listing = [ {"data_id": nh[0], "hash": nh[1]} for nh in names_and_hashes ]
    return {"data": listing}
コード例 #15
0
def load_legacy_user_profile(name, expected_hash):
    """
    Load a legacy user profile, and convert it into
    the new zonefile-esque profile format that can
    be serialized into a JWT.

    Verify that the profile hashses to the above expected hash
    """

    # fetch...
    storage_host = 'onename.com'
    assert name.endswith('.id')

    name_without_namespace = '.'.join(name.split('.')[:-1])
    storage_path = '/{}.json'.format(name_without_namespace)

    try:
        req = httplib.HTTPConnection(storage_host)
        resp = req.request('GET', storage_path)
        data = resp.read()
    except Exception as e:
        log.error('Failed to fetch http://{}/{}: {}'.format(storage_host, storage_path, e))
        return None

    try:
        data_json = json.loads(data)
    except Exception as e:
        log.error('Unparseable profile data')
        return None

    data_hash = storage.get_blockchain_compat_hash(data_json)
    if expected_hash != data_hash:
        log.error('Hash mismatch: expected {}, got {}'.format(expected_hash, data_hash))
        return None

    assert blockstack_profiles.is_profile_in_legacy_format(data_json)
    new_profile = blockstack_profiles.get_person_from_legacy_format(data_json)
    return new_profile
コード例 #16
0
ファイル: profile.py プロジェクト: paulpw/blockstack-cli
def store_name_zonefile( name, user_zonefile, txid ):
    """
    Store JSON user zonefile data to the immutable storage providers, synchronously.
    This is only necessary if we've added/changed/removed immutable data.

    Return (True, hash(user)) on success
    Return (False, None) on failure
    """

    assert not blockstack_profiles.is_profile_in_legacy_format(user_zonefile), "User zonefile is a legacy profile"
    assert user_db.is_user_zonefile(user_zonefile), "Not a user zonefile (maybe a custom legacy profile?)"

    # serialize and send off
    user_zonefile_txt = blockstack_zones.make_zone_file( user_zonefile, origin=name, ttl=USER_ZONEFILE_TTL )
    data_hash = storage.get_zonefile_data_hash( user_zonefile_txt )
    result = storage.put_immutable_data(None, txid, data_hash=data_hash, data_text=user_zonefile_txt )

    rc = None
    if result is None:
        rc = False
    else:
        rc = True

    return (rc, data_hash)
def check( state_engine ):

    global wallet_keys, datasets, zonefile_hash

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr )
    if preorder is not None:
        print "still have preorder"
        return False
    
    # registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is None:
        print "name does not exist"
        return False 

    # owned 
    if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr):
        print "name has wrong owner"
        return False 

    # have right hash 
    if name_rec['value_hash'] != zonefile_hash:
        print "Invalid zonefile hash"
        return False 

    # zonefile is NOT legacy 
    user_zonefile = blockstack_client.zonefile.load_name_zonefile( 'foo.test', zonefile_hash )
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False
    
    res = testlib.blockstack_cli_lookup("foo.test")
    if 'error' in res:
        print 'error looking up profile: {}'.format(res)
        return False

    assert 'profile' in res
    assert 'zonefile' in res

    return True
コード例 #18
0
ファイル: profile.py プロジェクト: paulpw/blockstack-cli
def get_and_migrate_profile( name, zonefile_storage_drivers=None, profile_storage_drivers=None, proxy=None, create_if_absent=False, wallet_keys=None, include_name_record=False ):
    """
    Get a name's profile and zonefile, optionally creating a new one along the way.  Migrate the profile to a new zonefile,
    if the profile is in legacy format.

    Only pass 'create_if_absent=True' for names we own

    If @include_name_record is set, then the resulting zonefile will have a key called 'name_record' that includes the name record.

    Return (user_profile, user_zonefile, migrated:bool) on success
    Return ({'error': ...}, None, False) on error
    """

    if proxy is None:
        proxy = get_default_proxy()

    created_new_zonefile = False
    created_new_profile = False

    name_record = None
    user_zonefile = get_name_zonefile( name, storage_drivers=zonefile_storage_drivers, proxy=proxy, wallet_keys=wallet_keys, include_name_record=True )
    if user_zonefile is None or 'error' in user_zonefile: 
        if not create_if_absent:
            return ({'error': 'No such zonefile'}, None, False)

        # creating. we'd better have a data public key
        log.debug("Creating new profile and zonefile for name '%s'" % name)
            
        data_pubkey = load_data_pubkey_for_new_zonefile( wallet_keys=wallet_keys, config_path=proxy.conf['path'] )
        if data_pubkey is None:
            log.warn("No data keypair set; will fall back to owner private key for data signing")

        user_profile = user_db.make_empty_user_profile()
        user_zonefile = user_db.make_empty_user_zonefile( name, data_pubkey )

        # look up name too 
        name_record = proxy.get_name_blockchain_record(name)
        if name_record is None:
            return ({'error': 'No such name'}, None, False)

        if 'error' in name_record:
            return ({'error': 'Failed to look up name: %s' % name_record['error']}, None, False)

        created_new_zonefile = True
        created_new_profile = True
    
    else:
        name_record = user_zonefile['name_record']
        del user_zonefile['name_record']

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):

        log.debug("Migrating legacy profile to modern zonefile for name '%s'" % name)
        
        data_pubkey = load_data_pubkey_for_new_zonefile( wallet_keys=wallet_keys, config_path=proxy.conf['path'] )
        if data_pubkey is None:
            log.warn("No data keypair set; will fall back to owner private key for data signing")

        user_profile = {}
        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            # traditional profile
            user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile )
        else:
            # custom profile 
            user_profile = copy.deepcopy( user_zonefile )

        user_zonefile = user_db.make_empty_user_zonefile( name, data_pubkey )

        created_new_zonefile = True
        created_new_profile = True

    else:
        if not created_new_profile:
            user_profile, error_msg = get_name_profile( name, zonefile_storage_drivers=zonefile_storage_drivers, profile_storage_drivers=profile_storage_drivers,
                                                        proxy=proxy, wallet_keys=wallet_keys, user_zonefile=user_zonefile, name_record=name_record )
            if user_profile is None:
                return (error_msg, None, False)

        elif create_if_absent:
            log.debug("Creating new profile for existing zonefile for name '%s'" % name)
            user_profile = user_db.make_empty_user_profile()
            created_new_profile = True

        else:
            raise Exception("Should be unreachable")


    if include_name_record:
        # put it back
        user_zonefile['name_record'] = name_record 

    return (user_profile, user_zonefile, created_new_zonefile)
コード例 #19
0
def get_profile(name,
                zonefile_storage_drivers=None,
                profile_storage_drivers=None,
                proxy=None,
                user_zonefile=None,
                name_record=None,
                include_name_record=False,
                include_raw_zonefile=False,
                use_zonefile_urls=True,
                use_legacy=False,
                use_legacy_zonefile=True,
                decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):
    
    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a 
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns (profile, zonefile) on success.  If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information
    Returns (None, {'error': ...}) on failure
    """

    proxy = get_default_proxy() if proxy is None else proxy

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name,
            proxy=proxy,
            name_record=name_record,
            include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True)

        if 'error' in user_zonefile:
            return None, user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return (None, {'error': 'Profile is in legacy format'})

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(
            user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return (None, {'error': 'Name zonefile is non-standard'})

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = virtualchain.BitcoinPublicKey(
                    user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key
            return (None, {'error': 'Name zonefile is missing a public key'})

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error(
                    'Failed to look up name record for "{}"'.format(name))
                return None, {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record,
                                                           indent=4,
                                                           sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = storage.get_mutable_data(
            name,
            user_data_pubkey,
            data_address=data_address,
            owner_address=owner_address,
            urls=urls,
            drivers=profile_storage_drivers,
            decode=decode_profile,
        )

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.debug('WARN: no user profile for {}'.format(name))
            else:
                log.debug('WARN: failed to load profile for {}: {}'.format(
                    name, user_profile['error']))

            return None, {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return None, {'error': 'Failed to look up name record'}

        user_zonefile['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            user_zonefile['raw_zonefile'] = raw_zonefile

    return user_profile, user_zonefile
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2, datastore_name

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.get_profile(name, user_zonefile=user_zonefile)
        if user_profile is None:
            print "Unable to load user profile for %s" % name
            return False

        if 'error' in user_profile:
            print json.dumps(user_profile, indent=4, sort_keys=True)
            return False


    # can fetch latest by name 
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/" )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-latest hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # can fetch by name and hash
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-by-hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # hash must match (if we put the wrong hash, it must fail)
    try:
        immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % ("0" * len(immutable_hash)))
        print "no error"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list names and hashes
    immutable_data_list = get_data( "blockstack://foo.test/#immutable" )
    if 'error' in immutable_data_list:
        print json.dumps(immutable_data, indent=4, sort_keys=True )
        return False 

    if len(immutable_data_list['data']) != 2:
        print "multiple immutable data"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    # order preserved
    if immutable_data_list['data'][0]['data_id'] != 'hello_world_immutable' or immutable_data_list['data'][0]['hash'] != immutable_hash:
        print "wrong data ID and/or hash"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    device_id = blockstack_client.config.get_local_device_id()
    data_id = blockstack_client.storage.make_fq_data_id( device_id, 'hello_world_mutable' )

    # can fetch latest mutable by name
    mutable_data = get_data( "blockstack://bar.test/{}".format(data_id))
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if json.loads(mutable_data['data']) != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['version'] != 1:
        print "wrong version: %s" % mutable_data['data']['version']
        return False 

    # can fetch by version
    mutable_data = get_data( "blockstack://bar.test/{}#1".format(data_id))
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if json.loads(mutable_data['data']) != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    # will fail to fetch if we give the wrong version 
    try:
        mutable_data = get_data("blockstack://bar.test/{}#2".format(data_id))
        print "mutable fetch by wrong version worked"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can fetch mutable data put by URL
    data_id = blockstack_client.storage.make_fq_data_id(device_id, 'foo_data2')
    mutable_data = get_data( "blockstack://foo.test/{}".format(data_id) )
    if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    
    if json.loads(mutable_data['data']) != {'hello2': 'world2'}:
        print "Invalid mutable data fetched from blockstack://foo.test/{}".format(data_id)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    # can fetch immutable data put by URL 
    immutable_data = get_data( "blockstack://foo_immutable.foo.test" )
    if 'error' in immutable_data or 'hash' not in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(immutable_data['data']) != {'hello3': 'world3'}:
        print "Invalid immutable data fetched from blockstack://foo_immutable.foo.test" 
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    # can fetch files and directories
    mutable_data = get_data( "blockstack://{}@foo-app.com/hello_datastore".format(datastore_name) )
    if 'error' in mutable_data or 'file' not in mutable_data or mutable_data['file']['idata'] != 'hello datastore':
        print 'Failed to get blockstack://{}@foo-app.com/hello_datastore'.format(datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    mutable_data = get_data( "blockstack://{}@foo-app.com/hello_dir/".format(datastore_name) )
    if 'error' in mutable_data or 'dir' not in mutable_data or 'hello_dir_datastore' not in mutable_data['dir']['idata'].keys():
        print 'Failed to get blockstack://{}@foo-app.com/hello_dir/'.format(datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    mutable_data = get_data( "blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore".format(datastore_name) )
    if 'error' in mutable_data or 'file' not in mutable_data or mutable_data['file']['idata'] != 'hello dir datastore':
        print 'Failed to get blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore'.format(datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    
    return True
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2


    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1)  # same as owner key
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr )
        if user_profile is None or 'error' in user_profile:
            if user_profile is not None:
                print json.dumps(user_profile, indent=4, sort_keys=True)
            else:
                print "\n\nprofile is None\n\n"

            return False

    return True
コード例 #22
0
ファイル: profile.py プロジェクト: paulpw/blockstack-cli
    except Exception, e:
        log.error("Failed to fetch http://%s/%s: %s" % (storage_host, storage_path, e))
        return None 

    try:
        data_json = json.loads(data)
    except Exception, e:
        log.error("Unparseable profile data")
        return None

    data_hash = storage.get_blockchain_compat_hash( data_json )
    if expected_hash != data_hash:
        log.error("Hash mismatch: expected %s, got %s" % (expected_hash, data_hash))
        return None

    assert blockstack_profiles.is_profile_in_legacy_format( data_json )
    new_profile = blockstack_profiles.get_person_from_legacy_format( data_json )
    return new_profile


def load_name_profile(name, user_zonefile, data_address, owner_address, use_zonefile_urls=True, storage_drivers=None, decode=True):
    """
    Fetch and load a user profile, given the user zonefile.
    Try to verify using the public key in the zonefile (if one
    is present), and fall back to the user-address if need be
    (it should be the hash of the profile JWT's public key).

    Return the user profile on success (either as a dict, or as a string if decode=False)
    Return None on error
    """
    # get user's data public key
コード例 #23
0
def get_and_migrate_profile(name,
                            zonefile_storage_drivers=None,
                            profile_storage_drivers=None,
                            proxy=None,
                            create_if_absent=False,
                            wallet_keys=None,
                            include_name_record=False):
    """
    Get a name's profile and zonefile, optionally creating a new one along the way.  Migrate the profile to a new zonefile,
    if the profile is in legacy format.

    Only pass 'create_if_absent=True' for names we own

    If @include_name_record is set, then the resulting zonefile will have a key called 'name_record' that includes the name record.

    @wallet_keys, if given, only needs the data public key set.

    Return ({'profile': user_profile}, {'zonefile': user_zonefile}, migrated:bool) on success
    Return ({'error': ...}, None, False) on error
    """

    if proxy is None:
        proxy = get_default_proxy()

    created_new_zonefile = False
    created_new_profile = False

    name_record = None
    user_zonefile = get_name_zonefile(name,
                                      storage_drivers=zonefile_storage_drivers,
                                      proxy=proxy,
                                      wallet_keys=wallet_keys,
                                      include_name_record=True)
    if user_zonefile is None or 'error' in user_zonefile:
        if not create_if_absent:
            return ({'error': 'No such zonefile'}, None, False)

        # creating. we'd better have a data public key
        log.debug("Creating new profile and zonefile for name '%s'" % name)

        data_pubkey = load_data_pubkey_for_new_zonefile(
            wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        if data_pubkey is None:
            log.warn(
                "No data keypair set; will fall back to owner private key for data signing"
            )

        user_profile = user_db.make_empty_user_profile()
        user_zonefile = user_db.make_empty_user_zonefile(name, data_pubkey)

        # look up name too
        name_record = proxy.get_name_blockchain_record(name)
        if name_record is None:
            return ({'error': 'No such name'}, None, False)

        if 'error' in name_record:
            return ({
                'error':
                'Failed to look up name: %s' % name_record['error']
            }, None, False)

        created_new_zonefile = True
        created_new_profile = True

    else:
        name_record = user_zonefile['name_record']
        del user_zonefile['name_record']
        user_zonefile = user_zonefile['zonefile']

    if blockstack_profiles.is_profile_in_legacy_format(
            user_zonefile) or not user_db.is_user_zonefile(user_zonefile):

        log.debug("Migrating legacy profile to modern zonefile for name '%s'" %
                  name)

        data_pubkey = load_data_pubkey_for_new_zonefile(
            wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        if data_pubkey is None:
            log.warn(
                "No data keypair set; will fall back to owner private key for data signing"
            )

        user_profile = {}
        if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
            # traditional profile
            user_profile = blockstack_profiles.get_person_from_legacy_format(
                user_zonefile)
        else:
            # custom profile
            user_profile = copy.deepcopy(user_zonefile)

        user_zonefile = user_db.make_empty_user_zonefile(name, data_pubkey)

        created_new_zonefile = True
        created_new_profile = True

    else:
        if not created_new_profile:
            user_profile, error_msg = get_name_profile(
                name,
                zonefile_storage_drivers=zonefile_storage_drivers,
                profile_storage_drivers=profile_storage_drivers,
                proxy=proxy,
                user_zonefile=user_zonefile,
                name_record=name_record)
            if user_profile is None:
                return (error_msg, None, False)

        elif create_if_absent:
            log.debug(
                "Creating new profile for existing zonefile for name '%s'" %
                name)
            user_profile = user_db.make_empty_user_profile()
            created_new_profile = True

        else:
            raise Exception("Should be unreachable")

    ret_user_profile = {"profile": user_profile}

    ret_user_zonefile = {"zonefile": user_zonefile}

    if include_name_record:
        # put it back
        ret_user_zonefile['name_record'] = name_record

    return (ret_user_profile, ret_user_zonefile, created_new_zonefile)
コード例 #24
0
                  (storage_host, storage_path, e))
        return None

    try:
        data_json = json.loads(data)
    except Exception, e:
        log.error("Unparseable profile data")
        return None

    data_hash = storage.get_blockchain_compat_hash(data_json)
    if expected_hash != data_hash:
        log.error("Hash mismatch: expected %s, got %s" %
                  (expected_hash, data_hash))
        return None

    assert blockstack_profiles.is_profile_in_legacy_format(data_json)
    new_profile = blockstack_profiles.get_person_from_legacy_format(data_json)
    return new_profile


def load_name_profile(name,
                      user_zonefile,
                      data_address,
                      owner_address,
                      use_zonefile_urls=True,
                      storage_drivers=None,
                      decode=True):
    """
    Fetch and load a user profile, given the user zonefile.
    Try to verify using the public key in the zonefile (if one
    is present), and fall back to the user-address if need be
コード例 #25
0
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None,
                proxy=None, user_zonefile=None, name_record=None,
                include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True,
                use_legacy=False, use_legacy_zonefile=True, decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):
    
    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a 
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns {'status': True, 'profile': profile, 'zonefile': zonefile} on success.
    * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information
    * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file

    Returns {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name, proxy=proxy,
            name_record=name_record, include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True
        )

        if 'error' in user_zonefile:
            return user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return {'error': 'Profile is in legacy format'}

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return {'error': 'Name zonefile is non-standard'}

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = keylib.ECPublicKey(user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key 
            return {'error': 'Name zonefile is missing a public key'}

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error('Failed to look up name record for "{}"'.format(name))
                return {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = storage.get_mutable_data(
            name, user_data_pubkey, blockchain_id=name,
            data_address=data_address, owner_address=owner_address,
            urls=urls, drivers=profile_storage_drivers, decode=decode_profile,
        )

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.error('no user profile for {}'.format(name))
            else:
                log.error('failed to load profile for {}: {}'.format(name, user_profile['error']))

            return {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    ret = {
        'status': True,
        'profile': user_profile,
        'zonefile': user_zonefile
    }

    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return {'error': 'Failed to look up name record'}

        ret['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            ret['raw_zonefile'] = raw_zonefile

    return ret
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1) + 1
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr )
        if user_profile is None:
            print "Unable to load user profile for %s (%s)" % (name, wallets[wallet_data_pubkey].pubkey_hex)
            return False

        if 'error' in user_profile:
            print json.dumps(user_profile, indent=4, sort_keys=True)
            return False


    # can fetch latest by name 
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/" )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if immutable_data['data'] != {'hello': 'world'}:
        print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-latest hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # can fetch by name and hash
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if immutable_data['data'] != {'hello': 'world'}:
        print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-by-hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # hash must match (if we put the wrong hash, it must fail)
    try:
        immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % ("0" * len(immutable_hash)))
        print "no error"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list names and hashes
    immutable_data_list = get_data( "blockstack://foo.test/#immutable" )
    if 'error' in immutable_data_list:
        print json.dumps(immutable_data, indent=4, sort_keys=True )
        return False 

    if len(immutable_data_list['data']) != 2:
        print "multiple immutable data"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    # order preserved
    if immutable_data_list['data'][0]['data_id'] != 'hello_world_immutable' or immutable_data_list['data'][0]['hash'] != immutable_hash:
        print "wrong data ID and/or hash"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    # can fetch latest mutable by name
    mutable_data = get_data( "blockstack://bar.test/hello_world_mutable")
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if mutable_data['data'] != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['version'] != 1:
        print "wrong version: %s" % mutable_data['data']['version']
        return False 

    # can fetch by version
    mutable_data = get_data( "blockstack://bar.test/hello_world_mutable#1")
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if mutable_data['data'] != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    # will fail to fetch if we give the wrong version 
    try:
        mutable_data = get_data("blockstack://bar.test/hello_world_mutable#2")
        print "mutable fetch by wrong version worked"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list mutable data
    mutable_data_list = get_data( "blockstack://bar.test/#mutable" )
    if 'error' in mutable_data_list:
        print json.dumps(mutable_data_list, indent=4, sort_keys=True )
        return False 

    if len(mutable_data_list) != 1:
        print "multiple mutable data"
        print json.dumps(mutable_data_list, indent=4, sort_keys=True )
        return False 

    if mutable_data_list['data'][0]['data_id'] != 'hello_world_mutable' or mutable_data_list['data'][0]['version'] != 1:
        print "wrong data id and/or version"
        print json.dumps(mutable_data_list, indent=4, sort_keys=True)
        return False

    # can fetch mutable data put by URL
    mutable_data = get_data( "blockstack://foo.test/foo_data2" )
    if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    
    if mutable_data['data'] != {'hello2': 'world2'}:
        print "Invalid mutable data fetched from blockstack://foo.test/foo_data2"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    # can fetch immutable data put by URL 
    immutable_data = get_data( "blockstack://foo_immutable.foo.test" )
    if 'error' in immutable_data or 'hash' not in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if immutable_data['data'] != {'hello3': 'world3'}:
        print "Invalid immutable data fetched from blockstack://foo_immutable.foo.test" 
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    # can fetch app data put by URL 
    mutable_data = get_data( "blockstack://[email protected]/foo_app_data" )
    if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data:
        print "Failed to get blockstack://[email protected]/foo_app_data"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['data'] != "foo_app_payload":
        print "Invalid data for blockstack://[email protected]/foo_app_data"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    mutable_data = get_data( "blockstack://[email protected]/foo_app_data2#3" )
    if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data:
        print "Failed to get blockstack://[email protected]/foo_app_data2#3"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['data'] != "foo_app_payload2":
        print "Failed to get blockstack://[email protected]/foo_app_data2#3"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    # fetch by wrong version will fail
    try:
        mutable_data = get_data( "blockstack://[email protected]/foo_app_data2#4" )
        print "got stale data with no error"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    return True
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2


    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1)  # same as owner key
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.zonefile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.get_profile( name, user_zonefile=user_zonefile )
        if user_profile is None or 'error' in user_profile:
            if user_profile is not None:
                print json.dumps(user_profile, indent=4, sort_keys=True)
            else:
                print "\n\nprofile is None\n\n"

            return False

    # can get mutable data 
    res = testlib.blockstack_cli_get_mutable( "bar.test", "hello_world_mutable" )
    print 'mutable: {}'.format(res)

    if 'error' in res:
        print json.dumps(res, indent=4, sort_keys=True)
        return False
    
    if json.loads(res['data']) != {'hello': 'world'}:
        print 'invalid data: {}'.format(res['data'])
        return False

    # can get immutable data by name
    res = testlib.blockstack_cli_get_immutable( 'foo.test', 'hello_world_immutable' )
    print 'immutable by name: {}'.format(res)

    if 'error' in res:
        return res

    if json.loads(res['data']) != {'hello': 'world_immutable'}:
        print 'invalid immutable data: {}'.format(res['data'])
        return False

    # can get immutable data by hash
    hsh = res['hash']
    res = testlib.blockstack_cli_get_immutable( 'foo.test', hsh )
    print 'immutable: {}'.format(res)

    if 'error' in res:
        return res

    if json.loads(res['data']) != {'hello': 'world_immutable'}:
        print 'invalid immutable data by hash: {}'.format(res['data'])
        return False

    return True
def check( state_engine ):

    global wallet_keys, datasets, zonefile_hash

    if error:
        return False

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    name = "foo.test"
    wallet_payer = 2
    wallet_owner = 3
    wallet_data_pubkey = 4

    # not preordered
    preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
    if preorder is not None:
        print "still have preorder"
        return False

    # registered 
    name_rec = state_engine.get_name( name )
    if name_rec is None:
        print "name does not exist"
        return False 

    # owned 
    if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
        print "name has wrong owner"
        return False 

    # zonefile is NOT legacy 
    user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash )
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # still have a profile with data
    user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr )
    if user_profile is None or 'error' in user_profile:
        if user_profile is not None:
            print json.dumps(user_profile, indent=4, sort_keys=True)
        else:
            print "\n\nprofile is None\n\n"
                    
        return False

    # still have immutable data
    immutable_data_by_name = blockstack_client.get_immutable_by_name( "foo.test", "hello_world_immutable" )
    if immutable_data_by_name is None:
        print "No data received by name for dataset %s" % i
        return False 

    if 'error' in immutable_data_by_name:
        print "No data received for dataset hello_world_immutable"
        return False 

    if not immutable_data_by_name.has_key('data'):
        print "Misisng data\n%s" % json.dumps(immutable_data_by_name, indent=4, sort_keys=True)
        return False 

    data_json = immutable_data_by_name['data']
    if data_json != {'hello': 'world'}:
        print "did not get dataset hello_world_immutable\ngot %s\nexpected %s" % (data_json, {'hello': 'world'})
        return False 

    # still have mutable data
    dat = blockstack_client.get_mutable( "foo.test", "hello_world_mutable" )
    if dat is None:
        print "No hello_world_mutable"
        return False 

    if 'error' in dat:
        print json.dumps(dat, indent=4, sort_keys=True)
        return False 

    if dat['data'] != {'hello': 'world'}:
        print "did not get mutable dataset"
        return False

    return True
def check(state_engine):

    global wallet_keys, datasets, zonefile_hash

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace not ready"
        return False

    ns = state_engine.get_namespace("test")
    if ns is None:
        print "no namespace"
        return False

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False

    # not preordered
    preorder = state_engine.get_name_preorder(
        "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr),
        wallets[3].addr)
    if preorder is not None:
        print "still have preorder"
        return False

    # registered
    name_rec = state_engine.get_name("foo.test")
    if name_rec is None:
        print "name does not exist"
        return False

    # owned
    if name_rec['address'] != wallets[3].addr or name_rec[
            'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr):
        print "name has wrong owner"
        return False

    # have right hash
    if name_rec['value_hash'] != zonefile_hash:
        print "Invalid zonefile hash"
        return False

    # zonefile is NOT legacy
    user_zonefile = blockstack_client.zonefile.load_name_zonefile(
        'foo.test', zonefile_hash)
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # zonefile has no key
    zonefile_key = blockstack_client.user.user_zonefile_data_pubkey(
        user_zonefile)
    if zonefile_key is not None:
        print 'still have zonefile key'
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # have right data
    test_proxy = testlib.TestAPIProxy()
    blockstack_client.set_default_proxy(test_proxy)

    for i in xrange(0, len(datasets)):
        print "get hello_world_%s" % (i + 1)
        dat = testlib.blockstack_cli_get_mutable(
            "foo.test",
            "hello_world_{}".format(i + 1),
            public_key=wallets[4].pubkey_hex)
        if dat is None:
            print "No data '%s'" % ("hello_world_%s" % (i + 1))
            return False

        if 'error' in dat:
            print json.dumps(dat, indent=4, sort_keys=True)
            return False

        if json.loads(dat['data']) != datasets[i]:
            print "Mismatch %s: %s != %s" % (i, dat, datasets[i])
            return False

    res = testlib.blockstack_cli_lookup("foo.test")
    if 'error' in res:
        print 'error looking up profile: {}'.format(res)
        return False

    assert 'profile' in res
    assert 'zonefile' in res

    return True
コード例 #30
0
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2


    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1) + 1
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr )
        if user_profile is None or 'error' in user_profile:
            if user_profile is not None:
                print json.dumps(user_profile, indent=4, sort_keys=True)
            else:
                print "\n\nprofile is None\n\n"
                        
            return False

    return True
コード例 #31
0
def check(state_engine):

    global wallet_keys, datasets, zonefile_hash

    if error:
        return False

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace not ready"
        return False

    ns = state_engine.get_namespace("test")
    if ns is None:
        print "no namespace"
        return False

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False

    name = "foo.test"
    wallet_payer = 2
    wallet_owner = 3
    wallet_data_pubkey = 4

    # not preordered
    preorder = state_engine.get_name_preorder(
        name, virtualchain.make_payment_script(wallets[wallet_payer].addr),
        wallets[wallet_owner].addr)
    if preorder is not None:
        print "still have preorder"
        return False

    # registered
    name_rec = state_engine.get_name(name)
    if name_rec is None:
        print "name does not exist"
        return False

    # owned
    if name_rec['address'] != wallets[wallet_owner].addr or name_rec[
            'sender'] != virtualchain.make_payment_script(
                wallets[wallet_owner].addr):
        print "name has wrong owner"
        return False

    # zonefile is NOT legacy
    user_zonefile = blockstack_client.zonefile.load_name_zonefile(
        name, zonefile_hash)
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # still have a profile with data
    user_profile = blockstack_client.profile.get_profile(
        name, user_zonefile=user_zonefile)
    if user_profile is None or 'error' in user_profile:
        if user_profile is not None:
            print json.dumps(user_profile, indent=4, sort_keys=True)
        else:
            print "\n\nprofile is None\n\n"

        return False

    # still have immutable data
    immutable_data_by_name = testlib.get_immutable_by_name(
        'foo.test', 'hello_world_immutable')
    if immutable_data_by_name is None:
        print "No data received by name for dataset %s" % i
        return False

    if 'error' in immutable_data_by_name:
        print "No data received for dataset hello_world_immutable"
        return False

    if not immutable_data_by_name.has_key('data'):
        print "Misisng data\n%s" % json.dumps(
            immutable_data_by_name, indent=4, sort_keys=True)
        return False

    data_json = json.loads(immutable_data_by_name['data'])
    if data_json != {'hello': 'world'}:
        print "did not get dataset hello_world_immutable\ngot %s\nexpected %s" % (
            data_json, {
                'hello': 'world'
            })
        return False

    # still have mutable data
    dat = testlib.blockstack_cli_get_mutable('foo.test', 'hello_world_mutable')
    if dat is None:
        print "No hello_world_mutable"
        return False

    if 'error' in dat:
        print json.dumps(dat, indent=4, sort_keys=True)
        return False

    if json.loads(dat['data']) != {'hello': 'world'}:
        print "did not get mutable dataset"
        return False

    return True
コード例 #32
0
def check(state_engine):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2, datastore_name

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace not ready"
        return False

    ns = state_engine.get_namespace("test")
    if ns is None:
        print "no namespace"
        return False

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i + 1) - 1
        wallet_owner = 3 * (i + 1)
        wallet_data_pubkey = 3 * (i + 1) + 1
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder(
            name,
            pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr),
            wallets[wallet_owner].addr)
        if preorder is not None:
            print "still have preorder"
            return False

        # registered
        name_rec = state_engine.get_name(name)
        if name_rec is None:
            print "name does not exist"
            return False

        # owned
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec[
                'sender'] != pybitcoin.make_pay_to_address_script(
                    wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False

        # zonefile is NOT legacy
        user_zonefile = blockstack_client.load_name_zonefile(
            name, zonefile_hash)
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info
        user_profile = blockstack_client.get_profile(
            name, user_zonefile=user_zonefile)
        if user_profile is None:
            print "Unable to load user profile for %s (%s)" % (
                name, wallets[wallet_data_pubkey].pubkey_hex)
            return False

        if 'error' in user_profile:
            print json.dumps(user_profile, indent=4, sort_keys=True)
            return False

    # can fetch latest by name
    immutable_data = get_data("blockstack://hello_world_immutable.foo.test/")
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % (
            immutable_data['data'], type(immutable_data['data']), {
                'hello': 'world'
            })
        return False

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-latest hash mismatch: %s != %s" % (
            immutable_data['hash'], immutable_hash)
        return False

    # can fetch by name and hash
    immutable_data = get_data(
        "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash)
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % (
            immutable_data['data'], type(immutable_data['data']), {
                'hello': 'world'
            })
        return False

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-by-hash mismatch: %s != %s" % (
            immutable_data['hash'], immutable_hash)
        return False

    # hash must match (if we put the wrong hash, it must fail)
    try:
        immutable_data = get_data(
            "blockstack://hello_world_immutable.foo.test/#%s" %
            ("0" * len(immutable_hash)))
        print "no error"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list names and hashes
    immutable_data_list = get_data("blockstack://foo.test/#immutable")
    if 'error' in immutable_data_list:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if len(immutable_data_list['data']) != 2:
        print "multiple immutable data"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True)
        return False

    # order preserved
    if immutable_data_list['data'][0][
            'data_id'] != 'hello_world_immutable' or immutable_data_list[
                'data'][0]['hash'] != immutable_hash:
        print "wrong data ID and/or hash"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True)
        return False

    device_id = blockstack_client.config.get_local_device_id()
    data_id = blockstack_client.storage.make_fq_data_id(
        device_id, 'hello_world_mutable')

    # can fetch latest mutable by name
    mutable_data = get_data("blockstack://bar.test/{}".format(data_id))
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(mutable_data['data']) != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['version'] != 1:
        print "wrong version: %s" % mutable_data['data']['version']
        return False

    # can fetch by version
    mutable_data = get_data("blockstack://bar.test/{}#1".format(data_id))
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(mutable_data['data']) != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    # will fail to fetch if we give the wrong version
    try:
        mutable_data = get_data("blockstack://bar.test/{}#2".format(data_id))
        print "mutable fetch by wrong version worked"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can fetch mutable data put by URL
    data_id = blockstack_client.storage.make_fq_data_id(device_id, 'foo_data2')
    mutable_data = get_data("blockstack://foo.test/{}".format(data_id))
    if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(mutable_data['data']) != {'hello2': 'world2'}:
        print "Invalid mutable data fetched from blockstack://foo.test/{}".format(
            data_id)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    # can fetch immutable data put by URL
    immutable_data = get_data("blockstack://foo_immutable.foo.test")
    if 'error' in immutable_data or 'hash' not in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if json.loads(immutable_data['data']) != {'hello3': 'world3'}:
        print "Invalid immutable data fetched from blockstack://foo_immutable.foo.test"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    # can fetch files and directories
    mutable_data = get_data(
        "blockstack://{}@foo-app.com/hello_datastore".format(datastore_name))
    if 'error' in mutable_data or 'file' not in mutable_data or mutable_data[
            'file']['idata'] != 'hello datastore':
        print 'Failed to get blockstack://{}@foo-app.com/hello_datastore'.format(
            datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    mutable_data = get_data(
        "blockstack://{}@foo-app.com/hello_dir/".format(datastore_name))
    if 'error' in mutable_data or 'dir' not in mutable_data or 'hello_dir_datastore' not in mutable_data[
            'dir']['idata'].keys():
        print 'Failed to get blockstack://{}@foo-app.com/hello_dir/'.format(
            datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    mutable_data = get_data(
        "blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore".format(
            datastore_name))
    if 'error' in mutable_data or 'file' not in mutable_data or mutable_data[
            'file']['idata'] != 'hello dir datastore':
        print 'Failed to get blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore'.format(
            datastore_name)
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    return True
コード例 #33
0
def get_name_profile(name,
                     zonefile_storage_drivers=None,
                     profile_storage_drivers=None,
                     create_if_absent=False,
                     proxy=None,
                     user_zonefile=None,
                     name_record=None,
                     include_name_record=False,
                     include_raw_zonefile=False,
                     use_zonefile_urls=True,
                     decode_profile=True):
    """
    Given the name of the user, look up the user's record hash,
    and then get the record itself from storage.

    If the user's zonefile is really a legacy profile, then 
    the profile will be the converted legacy profile.  The
    returned zonefile will still be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    Returns (profile, zonefile) on success.  If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information
    Returns (None, {'error': ...}) on failure
    """

    if proxy is None:
        proxy = get_default_proxy()

    raw_zonefile = None

    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name,
            create_if_absent=create_if_absent,
            proxy=proxy,
            name_record=name_record,
            include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile)
        if user_zonefile is None:
            return (None, {'error': 'No user zonefile'})

        if 'error' in user_zonefile:
            return (None, user_zonefile)

        name_record = user_zonefile['name_record']
        del user_zonefile['name_record']

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile['raw_zonefile']
            del user_zonefile['raw_zonefile']

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        # convert it
        log.debug("Converting legacy profile to modern profile")
        user_profile = blockstack_profiles.get_person_from_legacy_format(
            user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        # not a legacy profile, but a custom profile
        log.debug("Using custom legacy profile")
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        user_address = None
        old_address = None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                user_address = virtualchain.BitcoinPublicKey(
                    user_data_pubkey).address()

        except ValueError:
            # user decided to put multiple keys under the same name into the zonefile.
            # so don't use them.
            user_data_pubkey = None

        # convert to address
        if name_record is None:
            name_record = proxy.get_name_blockchain_record(name)
            if name_record is None or 'error' in name_record:
                log.error("Failed to look up name record for '%s'" % name)
                return (None, {'error': 'Failed to look up name record'})

        old_address = name_record['address']
        if user_address is None:
            # cut to the chase
            user_address = old_address

        user_profile = load_name_profile(
            name,
            user_zonefile,
            user_address,
            old_address,
            use_zonefile_urls=use_zonefile_urls,
            storage_drivers=profile_storage_drivers,
            decode=decode_profile)

        if user_profile is not None:
            if decode_profile:
                assert isinstance(user_profile, dict)
            else:
                assert type(user_profile) in [str, unicode]

        else:
            log.debug("WARN: no user profile for %s" % name)
            if create_if_absent:
                user_profile = user_db.make_empty_user_profile()
            else:
                return (None, {'error': 'Failed to load user profile'})

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    if include_name_record:
        if name_record is None:
            name_record = proxy.get_name_blockchain_record(name)
            if name_record is None or 'error' in name_record:
                log.error("Failed to look up name record for '%s'" % name)
                return (None, {'error': 'Failed to look up name record'})

        user_zonefile['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            user_zonefile['raw_zonefile'] = raw_zonefile

    return (user_profile, user_zonefile)
def check( state_engine ):

    global wallet_keys, datasets, zonefile_hash

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr )
    if preorder is not None:
        print "still have preorder"
        return False
    
    # registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is None:
        print "name does not exist"
        return False 

    # owned 
    if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr):
        print "name has wrong owner"
        return False 

    # have right hash 
    if name_rec['value_hash'] != zonefile_hash:
        print "Invalid zonefile hash"
        return False 

    # zonefile is NOT legacy 
    user_zonefile = blockstack_client.zonefile.load_name_zonefile( 'foo.test', zonefile_hash )
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False 

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # zonefile has no key 
    zonefile_key = blockstack_client.user.user_zonefile_data_pubkey(user_zonefile)
    if zonefile_key is not None:
        print 'still have zonefile key'
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    # have right data 
    test_proxy = testlib.TestAPIProxy()
    blockstack_client.set_default_proxy( test_proxy )

    for i in xrange(0, len(datasets)):
        print "get hello_world_%s" % (i+1)
        dat = testlib.blockstack_cli_get_mutable( "foo.test", "hello_world_{}".format(i+1), public_key=wallets[4].pubkey_hex )
        if dat is None:
            print "No data '%s'" % ("hello_world_%s" % (i+1))
            return False

        if 'error' in dat:
            print json.dumps(dat, indent=4, sort_keys=True)
            return False

        if json.loads(dat['data']) != datasets[i]:
            print "Mismatch %s: %s != %s" % (i, dat, datasets[i])
            return False
    
    res = testlib.blockstack_cli_lookup("foo.test")
    if 'error' in res:
        print 'error looking up profile: {}'.format(res)
        return False

    assert 'profile' in res
    assert 'zonefile' in res

    return True
コード例 #35
0
ファイル: profile.py プロジェクト: paulpw/blockstack-cli
def get_name_profile(name, zonefile_storage_drivers=None,
                           profile_storage_drivers=None,
                           create_if_absent=False,
                           proxy=None,
                           wallet_keys=None,
                           user_zonefile=None,
                           name_record=None,
                           include_name_record=False,
                           use_zonefile_urls=True,
                           decode_profile=True ):

    """
    Given the name of the user, look up the user's record hash,
    and then get the record itself from storage.

    If the user's zonefile is really a legacy profile, then 
    the profile will be the converted legacy profile.  The
    returned zonefile will still be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    Returns (profile, zonefile) on success.  If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information
    Returns (None, {'error': ...}) on failure
    """

    if proxy is None:
        proxy = get_default_proxy()
 
    if user_zonefile is None:
        user_zonefile = get_name_zonefile( name, create_if_absent=create_if_absent, proxy=proxy, wallet_keys=wallet_keys, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers )
        if user_zonefile is None:
            return (None, {'error': 'No user zonefile'})

        if 'error' in user_zonefile:
            return (None, user_zonefile)

        name_record = user_zonefile['name_record']
        del user_zonefile['name_record']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
        # convert it
        log.debug("Converting legacy profile to modern profile")
        user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile )
     
    elif not user_db.is_user_zonefile( user_zonefile ):
        # not a legacy profile, but a custom profile
        log.debug("Using custom legacy profile")
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        user_address = None
        old_address = None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey( user_zonefile )
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                user_address = pybitcoin.BitcoinPublicKey(user_data_pubkey).address()

        except ValueError:
            # user decided to put multiple keys under the same name into the zonefile.
            # so don't use them.
            user_data_pubkey = None 

        # convert to address
        if name_record is None:
            name_record = proxy.get_name_blockchain_record( name )
            if name_record is None or 'error' in name_record:
                log.error("Failed to look up name record for '%s'" % name)
                return (None, {'error': 'Failed to look up name record'})

        old_address = name_record['address']
        if user_address is None:
            # cut to the chase
            user_address = old_address

        user_profile = load_name_profile( name, user_zonefile, user_address, old_address, use_zonefile_urls=use_zonefile_urls, storage_drivers=profile_storage_drivers, decode=decode_profile )
        if user_profile is None or (type(user_profile) not in [str, unicode] and 'error' in user_profile):

            if user_profile is None:
                log.debug("WARN: no user profile for %s" % name)
            else:
                log.debug("WARN: failed to load profile for %s: %s" % (name, user_profile['error']))

            if create_if_absent:
                user_profile = user_db.make_empty_user_profile()
            else:
                return (None, {'error': 'Failed to load user profile'})

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    if include_name_record:
        if name_record is None:
            name_record = proxy.get_name_blockchain_record( name )
            if name_record is None or 'error' in name_record:
                log.error("Failed to look up name record for '%s'" % name)
                return (None, {'error': 'Failed to look up name record'})

        user_zonefile['name_record'] = name_record

    return (user_profile, user_zonefile)
コード例 #36
0
def run_cli():
    """ run cli
    """

    conf = config.get_config()

    if conf is None:
        log.error("Failed to load config")
        sys.exit(1)

    advanced_mode = conf['advanced_mode']

    parser = argparse.ArgumentParser(
        description='Blockstack cli version {}'.format(config.VERSION))

    parser.register('action', 'parsers', AliasedSubParsersAction)

    subparsers = parser.add_subparsers(dest='action')

    add_subparsers(subparsers)

    if advanced_mode == "on":
        add_advanced_subparsers(subparsers)

    # Print default help message, if no argument is given
    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    args, unknown_args = parser.parse_known_args()
    result = {}

    conf = config.get_config()

    blockstack_server = conf['server']
    blockstack_port = conf['port']

    proxy = client.session(conf=conf,
                           server_host=blockstack_server,
                           server_port=blockstack_port,
                           set_global=True)

    # start the two background processes (rpc daemon and monitor queue)
    start_background_daemons()

    if args.action == 'balance':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        total_balance, addresses = get_total_balance()
        result['total_balance'] = total_balance
        if args.details:
            result['addresses'] = addresses

    elif args.action == 'price':

        fqu = str(args.name)
        check_valid_name(fqu)

        try:
            resp = client.get_name_cost(fqu)
        except socket_error:
            exit_with_error("Error connecting to server")

        if 'error' in resp:
            exit_with_error(resp['error'])

        data = get_total_fees(resp)

        result = data

    elif args.action == 'config':
        data = {}

        settings_updated = False

        data["message"] = "Updated settings for"

        if args.host is not None:
            config.update_config('blockstack-client', 'server', args.host)
            data["message"] += " host"
            settings_updated = True

        if args.port is not None:
            config.update_config('blockstack-client', 'port', args.port)
            data["message"] += " port"
            settings_updated = True

        if args.advanced is not None:

            if args.advanced != "on" and args.advanced != "off":
                exit_with_error("Use --advanced=on or --advanced=off")
            else:
                config.update_config('blockstack-client', 'advanced_mode',
                                     args.advanced)
                data["message"] += " advanced"
                settings_updated = True

        # reload conf
        conf = config.get_config()

        if settings_updated:
            result['message'] = data['message']
        else:
            result['message'] = "No config settings were updated."

    elif args.action == 'deposit':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        result['message'] = 'Send bitcoins to the address specified.'
        result['address'], owner_address = get_addresses_from_file()

    elif args.action == 'import':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        result['message'] = 'Send the name you want to receive to the'
        result['message'] += ' address specified.'
        payment_address, result['address'] = get_addresses_from_file()

    elif args.action == 'names':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        result['names_owned'] = get_all_names_owned()

        if args.details:
            result['addresses'] = get_owner_addresses()

    elif args.action in ('info', 'status', 'ping', 'details'):

        resp = client.getinfo()

        result = {}

        result['server_host'] = conf['server']
        result['server_port'] = str(conf['port'])
        result['cli_version'] = config.VERSION
        result['advanced_mode'] = conf['advanced_mode']

        if 'error' in resp:
            result['server_alive'] = False
            result['server_error'] = resp['error']
        else:
            result['server_alive'] = True
            result['server_version'] = resp['blockstore_version']
            try:
                result['last_block_processed'] = resp['last_block']
            except:
                result['last_block_processed'] = resp['blocks']
            result['last_block_seen'] = resp['bitcoind_blocks']
            result['consensus_hash'] = resp['consensus']

            if advanced_mode == 'on':
                result['testset'] = resp['testset']

            proxy = get_local_proxy()

            if proxy is not False:

                current_state = json.loads(proxy.state())

                queue = {}
                pending_queue = []
                preorder_queue = []
                register_queue = []
                update_queue = []
                transfer_queue = []

                def format_new_entry(entry):
                    new_entry = {}
                    new_entry['name'] = entry['fqu']
                    confirmations = get_tx_confirmations(entry['tx_hash'])
                    if confirmations is None:
                        confirmations = 0
                    new_entry['confirmations'] = confirmations
                    return new_entry

                def format_queue_display(preorder_queue, register_queue):

                    for entry in register_queue:

                        name = entry['name']

                        for check_entry in preorder_queue:

                            if check_entry['name'] == name:
                                preorder_queue.remove(check_entry)

                for entry in current_state:

                    if 'type' in entry:
                        if entry['type'] == 'preorder':
                            preorder_queue.append(format_new_entry(entry))
                        elif entry['type'] == 'register':
                            register_queue.append(format_new_entry(entry))
                        elif entry['type'] == 'update':
                            update_queue.append(format_new_entry(entry))
                        elif entry['type'] == 'transfer':
                            transfer_queue.append(format_new_entry(entry))

                format_queue_display(preorder_queue, register_queue)

                if len(preorder_queue) != 0:
                    queue['preorder'] = preorder_queue

                if len(register_queue) != 0:
                    queue['register'] = register_queue

                if len(update_queue) != 0:
                    queue['update'] = update_queue

                if len(transfer_queue) != 0:
                    queue['transfer'] = transfer_queue

                if queue != {}:
                    result['queue'] = queue

    elif args.action == 'lookup':
        data = {}

        blockchain_record = None
        fqu = str(args.name)

        check_valid_name(fqu)

        try:
            blockchain_record = client.get_name_blockchain_record(fqu)
        except socket_error:
            exit_with_error("Error connecting to server.")

        if 'value_hash' not in blockchain_record:
            exit_with_error("%s is not registered" % fqu)

        data_id = blockchain_record['value_hash']
        owner_address = blockchain_record['address']
        profile = client.get_immutable(str(args.name), data_id)['data']

        zone_file = profile
        profile = resolve_zone_file_to_profile(profile, owner_address)

        if not is_profile_in_legacy_format(profile):
            data['data_record'] = profile
            data['zone_file'] = zone_file
        else:
            data['data_record'] = json.loads(profile)
        #except Exception as e:
        #    print e
        #    data['data_record'] = None

        result = data

    elif args.action == 'whois':
        data = {}

        record = None
        fqu = str(args.name)

        check_valid_name(fqu)

        try:
            record = client.get_name_blockchain_record(fqu)
        except socket_error:
            exit_with_error("Error connecting to server.")

        if 'value_hash' not in record:
            result['registered'] = False
        else:
            result['registered'] = True
            result['block_preordered_at'] = record['preorder_block_number']
            result['block_renewed_at'] = record['last_renewed']
            result['owner_address'] = record['address']
            result['owner_public_key'] = record['sender_pubkey']
            result['owner_script'] = record['sender']
            result['preorder_transaction_id'] = record['txid']

    elif args.action == 'register':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        result = {}
        fqu = str(args.name)
        check_valid_name(fqu)
        cost = client.get_name_cost(fqu)

        if 'error' in cost:
            exit_with_error(cost['error'])

        if nameRegistered(fqu):
            exit_with_error("%s is already registered." % fqu)

        if not walletUnlocked():
            unlock_wallet()

        fees = get_total_fees(cost)

        try:
            cost = fees['total_estimated_cost']
            input_prompt = "Registering %s will cost %s BTC." % (fqu, cost)
            input_prompt += " Continue? (y/n): "
            user_input = raw_input(input_prompt)
            user_input = user_input.lower()

            if user_input != 'y':
                print "Not registering."
                exit(0)
        except KeyboardInterrupt:
            print "\nExiting."
            exit(0)

        payment_address, owner_address = get_addresses_from_file()

        if not hasEnoughBalance(payment_address, fees['total_estimated_cost']):
            msg = "Address %s doesn't have enough balance." % payment_address
            exit_with_error(msg)

        if recipientNotReady(owner_address):
            msg = "Address %s owns too many names already." % owner_address
            exit_with_error(msg)

        if dontuseAddress(payment_address):
            msg = "Address %s has pending transactions." % payment_address
            msg += " Wait and try later."
            exit_with_error(msg)

        proxy = get_local_proxy()

        try:
            resp = proxy.preorder(fqu)
        except:
            exit_with_error("Error talking to server, try again.")

        if 'success' in resp and resp['success']:
            result = resp
        else:
            if 'error' in resp:
                exit_with_error(resp['error'])

            if 'message' in resp:
                exit_with_error(resp['message'])

    elif args.action == 'update':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        fqu = str(args.name)
        check_valid_name(fqu)

        user_data = str(args.data)
        try:
            user_data = json.loads(user_data)
        except:
            exit_with_error("Data is not in JSON format.")

        tests_for_update_and_transfer(fqu)

        if profileonBlockchain(fqu, user_data):
            msg = "Data is same as current data record, update not needed."
            exit_with_error(msg)

        if not walletUnlocked():
            unlock_wallet()

        proxy = get_local_proxy()

        try:
            resp = proxy.update(fqu, user_data)
        except:
            exit_with_error("Error talking to server, try again.")

        if 'success' in resp and resp['success']:
            result = resp
        else:
            if 'error' in resp:
                exit_with_error(resp['error'])

            if 'message' in resp:
                exit_with_error(resp['message'])

    elif args.action == 'transfer':

        if not os.path.exists(WALLET_PATH):
            initialize_wallet()

        fqu = str(args.name)
        check_valid_name(fqu)
        transfer_address = str(args.address)

        tests_for_update_and_transfer(fqu, transfer_address=transfer_address)

        if not walletUnlocked():
            unlock_wallet()

        proxy = get_local_proxy()

        try:
            resp = proxy.transfer(fqu, transfer_address)
        except:
            exit_with_error("Error talking to server, try again.")

        if 'success' in resp and resp['success']:
            result = resp
        else:
            if 'error' in resp:
                exit_with_error(resp['error'])

            if 'message' in resp:
                exit_with_error(resp['message'])

    # ---------------------- Advanced options ---------------------------------
    elif args.action == 'wallet':

        if not os.path.exists(WALLET_PATH):
            result = initialize_wallet()
        else:
            unlock_wallet(display_enabled=True)

    elif args.action == 'consensus':

        if args.block_height is None:
            # by default get last indexed block
            resp = client.getinfo()

            if 'error' in resp:
                exit_with_error("Error connecting to server.")

            elif 'last_block' in resp or 'blocks' in resp:

                if 'last_block' in resp:
                    args.block_height = client.getinfo()['last_block']
                elif 'blocks' in resp:
                    args.block_height = client.getinfo()['blocks']
                else:
                    result['error'] = "Server is indexing. Try again"
                    exit(0)

        resp = client.get_consensus_at(int(args.block_height))

        data = {}
        data['consensus'] = resp
        data['block_height'] = args.block_height

        result = data

    elif args.action == 'register_tx':
        result = client.register(str(args.name),
                                 str(args.privatekey),
                                 str(args.addr),
                                 tx_only=True)

    elif args.action == 'register_subsidized':
        result = client.register_subsidized(str(args.name),
                                            str(args.privatekey),
                                            str(args.addr),
                                            str(args.subsidy_key))

    elif args.action == 'update_tx':

        txid = None
        if args.txid is not None:
            txid = str(args.txid)

        result = client.update(str(args.name),
                               str(args.record_json),
                               str(args.privatekey),
                               txid=txid,
                               tx_only=True)

    elif args.action == 'update_subsidized':

        txid = None
        if args.txid is not None:
            txid = str(args.txid)

        result = client.update_subsidized(str(args.name),
                                          str(args.record_json),
                                          str(args.public_key),
                                          str(args.subsidy_key),
                                          txid=txid)

    elif args.action == 'transfer_tx':
        keepdata = False
        if args.keepdata.lower() not in ["on", "false"]:
            print >> sys.stderr, "Pass 'true' or 'false' for keepdata"
            sys.exit(1)

        if args.keepdata.lower() == "on":
            keepdata = True

        result = client.transfer(str(args.name),
                                 str(args.address),
                                 keepdata,
                                 str(args.privatekey),
                                 tx_only=True)

    elif args.action == 'preorder':

        register_addr = None
        if args.address is not None:
            register_addr = str(args.address)

        result = client.preorder(str(args.name),
                                 str(args.privatekey),
                                 register_addr=register_addr)

    elif args.action == 'preorder_tx':

        register_addr = None
        if args.address is not None:
            register_addr = str(args.address)

        result = client.preorder(str(args.name),
                                 str(args.privatekey),
                                 register_addr=register_addr,
                                 tx_only=True)

    elif args.action == 'preorder_subsidized':

        result = client.preorder_subsidized(str(args.name),
                                            str(args.public_key),
                                            str(args.address),
                                            str(args.subsidy_key))

    elif args.action == 'transfer_subsidized':
        keepdata = False
        if args.keepdata.lower() not in ["on", "false"]:
            print >> sys.stderr, "Pass 'true' or 'false' for keepdata"
            sys.exit(1)

        if args.keepdata.lower() == "on":
            keepdata = True

        result = client.transfer_subsidized(str(args.name), str(args.address),
                                            keepdata, str(args.public_key),
                                            str(args.subsidy_key))

    elif args.action == 'renew':
        result = client.renew(str(args.name), str(args.privatekey))

    elif args.action == 'renew_tx':
        result = client.renew(str(args.name),
                              str(args.privatekey),
                              tx_only=True)

    elif args.action == 'renew_subsidized':
        result = client.renew_subsidized(str(args.name), str(args.public_key),
                                         str(args.subsidy_key))

    elif args.action == 'revoke':
        result = client.revoke(str(args.name), str(args.privatekey))

    elif args.action == 'revoke_tx':
        result = client.revoke(str(args.name),
                               str(args.privatekey),
                               tx_only=True)

    elif args.action == 'revoke_subsidized':
        result = client.revoke_subsidized(str(args.name), str(args.public_key),
                                          str(args.subsidy_key))

    elif args.action == 'name_import':
        result = client.name_import(str(args.name), str(args.address),
                                    str(args.hash), str(args.privatekey))

    elif args.action == 'namespace_preorder':

        reveal_addr = None
        if args.address is not None:
            reveal_addr = str(args.address)

        result = client.namespace_preorder(str(args.namespace_id),
                                           str(args.privatekey),
                                           reveal_addr=reveal_addr)

    elif args.action == 'namespace_reveal':
        bucket_exponents = args.bucket_exponents.split(',')
        if len(bucket_exponents) != 16:
            raise Exception("bucket_exponents must be a 16-value CSV \
                             of integers")

        for i in xrange(0, len(bucket_exponents)):
            try:
                bucket_exponents[i] = int(bucket_exponents[i])
            except:
                raise Exception("bucket_exponents must contain integers in \
                                range [0, 16)")

        lifetime = int(args.lifetime)
        if lifetime < 0:
            lifetime = 0xffffffff  # means "infinite" to blockstack-server

        result = client.namespace_reveal(str(args.namespace_id),
                                         str(args.addr), lifetime,
                                         int(args.coeff), int(args.base),
                                         bucket_exponents,
                                         int(args.nonalpha_discount),
                                         int(args.no_vowel_discount),
                                         str(args.privatekey))

    elif args.action == 'namespace_ready':
        result = client.namespace_ready(str(args.namespace_id),
                                        str(args.privatekey))

    elif args.action == 'put_mutable':
        result = client.put_mutable(str(args.name), str(args.data_id),
                                    str(args.data), str(args.privatekey))

    elif args.action == 'put_immutable':
        result = client.put_immutable(str(args.name),
                                      str(args.data),
                                      str(args.privatekey),
                                      conf=conf)

    elif args.action == 'get_mutable':
        result = client.get_mutable(str(args.name),
                                    str(args.data_id),
                                    conf=conf)

    elif args.action == 'get_immutable':
        result = client.get_immutable(str(args.name), str(args.hash))

    elif args.action == 'delete_immutable':
        result = client.delete_immutable(str(args.name), str(args.hash),
                                         str(args.privatekey))

    elif args.action == 'delete_mutable':
        result = client.delete_mutable(str(args.name), str(args.data_id),
                                       str(args.privatekey))

    elif args.action == 'get_name_blockchain_record':
        result = client.get_name_blockchain_record(str(args.name))

    elif args.action == 'get_namespace_blockchain_record':
        result = client.get_namespace_blockchain_record(str(args.namespace_id))

    elif args.action == 'lookup_snv':
        result = client.lookup_snv(str(args.name), int(args.block_id),
                                   str(args.consensus_hash))

    elif args.action == 'get_name_record':
        result = client.get_name_record(str(args.name))

    elif args.action == 'get_names_owned_by_address':
        result = client.get_names_owned_by_address(str(args.address))

    elif args.action == 'get_namespace_cost':
        result = client.get_namespace_cost(str(args.namespace_id))

    elif args.action == 'get_all_names':
        offset = None
        count = None

        if args.offset is not None:
            offset = int(args.offset)

        if args.count is not None:
            count = int(args.count)

        result = client.get_all_names(offset, count)

    elif args.action == 'get_names_in_namespace':
        offset = None
        count = None

        if args.offset is not None:
            offset = int(args.offset)

        if args.count is not None:
            count = int(args.count)

        result = client.get_names_in_namespace(str(args.namespace_id), offset,
                                               count)

    elif args.action == 'get_nameops_at':
        result = client.get_nameops_at(int(args.block_id))

    print_result(result)
コード例 #37
0
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2


    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1) + 1
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.zonefile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.get_profile( name, user_zonefile=user_zonefile )
        if user_profile is None or 'error' in user_profile:
            if user_profile is not None:
                print json.dumps(user_profile, indent=4, sort_keys=True)
            else:
                print "\n\nprofile is None\n\n"
                        
            return False

    # can get mutable data 
    res = testlib.blockstack_cli_get_mutable( "bar.test", "hello_world_mutable" )
    print 'mutable: {}'.format(res)

    if 'error' in res:
        print json.dumps(res, indent=4, sort_keys=True)
        return False
    
    if json.loads(res['data']) != {'hello': 'world'}:
        print 'invalid data: {}'.format(res['data'])
        return False

    # can get immutable data by name
    res = testlib.blockstack_cli_get_immutable( 'foo.test', 'hello_world_immutable' )
    print 'immutable by name: {}'.format(res)

    if 'error' in res:
        return res

    if json.loads(res['data']) != {'hello': 'world_immutable'}:
        print 'invalid immutable data: {}'.format(res['data'])
        return False

    # can get immutable data by hash
    hsh = res['hash']
    res = testlib.blockstack_cli_get_immutable( 'foo.test', hsh )
    print 'immutable: {}'.format(res)

    if 'error' in res:
        return res

    if json.loads(res['data']) != {'hello': 'world_immutable'}:
        print 'invalid immutable data by hash: {}'.format(res['data'])
        return False

    return True
コード例 #38
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def get_mutable(name, data_id, proxy=None, ver_min=None, ver_max=None, ver_check=None, conf=None, wallet_keys=None):
    """
    get_mutable

    Fetch a piece of mutable data.  Use @data_id to look it up in the user's
    profile, and then fetch and erify the data itself from the configured 
    storage providers.

    If @ver_min is given, ensure the data's version is greater or equal to it.
    If @ver_max is given, ensure the data's version is less than it.
    If @ver_check is given, it must be a callable that takes the name, data and version and returns True/False

    Return {'data': the data, 'version': the version} on success
    Return {'error': ...} on error
    """

    if proxy is None:
        proxy = get_default_proxy()

    if conf is None:
        conf = proxy.conf

    fq_data_id = storage.make_fq_data_id( name, data_id )
    user_profile, user_zonefile = get_name_profile( name, proxy=proxy, wallet_keys=wallet_keys, include_name_record=True )
    if user_profile is None:
        return user_zonefile    # will be an error message
   
    # recover name record 
    name_record = user_zonefile['name_record']
    del user_zonefile['name_record']

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # profile has not been converted to the new zonefile format yet.
        return {'error': 'Profile is in a legacy format that does not support mutable data.'}

    # get the mutable data zonefile
    if not user_db.has_mutable_data( user_profile, data_id ):
        return {'error': "No such mutable datum"}

    mutable_data_zonefile = user_db.get_mutable_data_zonefile( user_profile, data_id )
    assert mutable_data_zonefile is not None, "BUG: could not look up mutable datum '%s'.'%s'" % (name, data_id)

    # get user's data public key and owner address
    data_pubkey = user_db.user_zonefile_data_pubkey( user_zonefile )
    data_address = name_record['address']
    if data_pubkey is None:
        log.warn("Falling back to owner address for authentication")

    # get the mutable data itself
    urls = user_db.mutable_data_zonefile_urls( mutable_data_zonefile )
    mutable_data = storage.get_mutable_data(fq_data_id, data_pubkey, urls=urls, data_address=data_address )
    if mutable_data is None:
        return {'error': "Failed to look up mutable datum"}

    expected_version = load_mutable_data_version( conf, name, data_id )
    if expected_version is None:
        expected_version = 0

    # check consistency
    version = user_db.mutable_data_version( user_profile, data_id )
    if ver_min is not None and ver_min > version:
        return {'error': 'Mutable data is stale'}

    if ver_max is not None and ver_max <= version:
        return {'error': 'Mutable data is in the future'}

    if ver_check is not None:
        rc = ver_check( name, mutable_data, version )
        if not rc:
            return {'error': 'Mutable data consistency check failed'}

    elif expected_version > version:
        return {'error': 'Mutable data is stale; a later version was previously fetched'}

    rc = store_mutable_data_version( conf, fq_data_id, version )
    if not rc:
        return {'error': 'Failed to store consistency information'}

    return {'data': mutable_data, 'version': version}
コード例 #39
0
def get_profile(name,
                zonefile_storage_drivers=None,
                profile_storage_drivers=None,
                proxy=None,
                user_zonefile=None,
                name_record=None,
                include_name_record=False,
                include_raw_zonefile=False,
                use_zonefile_urls=True,
                use_legacy=False,
                use_legacy_zonefile=True,
                decode_profile=True):
    """
    Given a name, look up an associated profile.
    Do so by first looking up the zonefile the name points to,
    and then loading the profile from that zonefile's public key.

    Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True):

    * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then
    the profile returned will be the converted legacy profile.  The returned zonefile will still
    be a legacy profile, however.
    The caller can check this and perform the conversion automatically.

    * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a
    data public key, then the owner address of the name will be used to verify
    the profile's authenticity.

    Returns {'status': True, 'profile': profile, 'zonefile': zonefile, 'public_key': ...} on success.
    * If include_name_record is True, then include 'name_record': name_record with the user's blockchain information
    * If include_raw_zonefile is True, then include 'raw_zonefile': raw_zonefile with unparsed zone file

    Returns {'error': ...} on error
    """

    proxy = get_default_proxy() if proxy is None else proxy
    user_profile_pubkey = None

    res = subdomains.is_address_subdomain(str(name))
    if res:
        subdomain, domain = res[1]
        try:
            return subdomains.resolve_subdomain(subdomain, domain)
        except subdomains.SubdomainNotFound as e:
            log.exception(e)
            return {
                'error': "Failed to find name {}.{}".format(subdomain, domain)
            }

    raw_zonefile = None
    if user_zonefile is None:
        user_zonefile = get_name_zonefile(
            name,
            proxy=proxy,
            name_record=name_record,
            include_name_record=True,
            storage_drivers=zonefile_storage_drivers,
            include_raw_zonefile=include_raw_zonefile,
            allow_legacy=True)

        if 'error' in user_zonefile:
            return user_zonefile

        raw_zonefile = None
        if include_raw_zonefile:
            raw_zonefile = user_zonefile.pop('raw_zonefile')

        user_zonefile = user_zonefile['zonefile']

    # is this really a legacy profile?
    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        if not use_legacy:
            return {'error': 'Profile is in legacy format'}

        # convert it
        log.debug('Converting legacy profile to modern profile')
        user_profile = blockstack_profiles.get_person_from_legacy_format(
            user_zonefile)

    elif not user_db.is_user_zonefile(user_zonefile):
        if not use_legacy:
            return {'error': 'Name zonefile is non-standard'}

        # not a legacy profile, but a custom profile
        log.debug('Using custom legacy profile')
        user_profile = copy.deepcopy(user_zonefile)

    else:
        # get user's data public key
        data_address, owner_address = None, None

        try:
            user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile)
            if user_data_pubkey is not None:
                user_data_pubkey = str(user_data_pubkey)
                data_address = keylib.ECPublicKey(user_data_pubkey).address()

        except ValueError:
            # multiple keys defined; we don't know which one to use
            user_data_pubkey = None

        if not use_legacy_zonefile and user_data_pubkey is None:
            # legacy zonefile without a data public key
            return {'error': 'Name zonefile is missing a public key'}

        # find owner address
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)
            if name_record is None or 'error' in name_record:
                log.error(
                    'Failed to look up name record for "{}"'.format(name))
                return {'error': 'Failed to look up name record'}

        assert 'address' in name_record.keys(), json.dumps(name_record,
                                                           indent=4,
                                                           sort_keys=True)
        owner_address = name_record['address']

        # get user's data public key from the zonefile
        urls = None
        if use_zonefile_urls and user_zonefile is not None:
            urls = user_db.user_zonefile_urls(user_zonefile)

        user_profile = None
        user_profile_pubkey = None

        try:
            user_profile_res = storage.get_mutable_data(
                name,
                user_data_pubkey,
                blockchain_id=name,
                data_address=data_address,
                owner_address=owner_address,
                urls=urls,
                drivers=profile_storage_drivers,
                decode=decode_profile,
                return_public_key=True)

            user_profile = user_profile_res['data']
            user_profile_pubkey = user_profile_res['public_key']

        except Exception as e:
            log.exception(e)
            return {'error': 'Failure in parsing and fetching profile'}

        if user_profile is None or json_is_error(user_profile):
            if user_profile is None:
                log.error('no user profile for {}'.format(name))
            else:
                log.error('failed to load profile for {}: {}'.format(
                    name, user_profile['error']))

            return {'error': 'Failed to load user profile'}

    # finally, if the caller asked for the name record, and we didn't get a chance to look it up,
    # then go get it.
    ret = {
        'status': True,
        'profile': user_profile,
        'zonefile': user_zonefile,
        'public_key': user_profile_pubkey
    }

    if include_name_record:
        if name_record is None:
            name_record = get_name_blockchain_record(name, proxy=proxy)

        if name_record is None or 'error' in name_record:
            log.error('Failed to look up name record for "{}"'.format(name))
            return {'error': 'Failed to look up name record'}

        ret['name_record'] = name_record

    if include_raw_zonefile:
        if raw_zonefile is not None:
            ret['raw_zonefile'] = raw_zonefile

    return ret
コード例 #40
0
ファイル: data.py プロジェクト: paulpw/blockstack-cli
def delete_immutable(name, data_key, data_id=None, proxy=None, txid=None, wallet_keys=None):
    """
    delete_immutable

    Remove an immutable datum from a name's profile, given by @data_key.
    Return a dict with {'status': True} on success
    Return a dict with {'error': ...} on failure
    """

    from backend.nameops import do_update

    if proxy is None:
        proxy = get_default_proxy()

    legacy = False
    user_zonefile = get_name_zonefile( name, proxy=proxy, include_name_record=True )
    if user_zonefile is None or 'error' in user_zonefile:
        if user_zonefile is None:
            return {'error': 'No user zonefile'}
        else:
            return user_zonefile

    name_record = user_zonefile['name_record']
    del user_zonefile['name_record']

    if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ) or not user_db.is_user_zonefile( user_zonefile ):
        # zonefile is a legacy profile.  There is no immutable data 
        log.info("Profile is in legacy format.  No immutable data.")
        return {'status': True}

    if data_key is None:
        if data_id is not None:
            # look up the key (or list of keys)
            # shouldn't be a list--this tool prevents that--but deal with it nevertheless
            data_key = user_db.get_immutable_data_hash( user_zonefile, data_id )
            if type(data_key) == list:
                return {'error': "Multiple hashes for '%s': %s" % (data_id, ",".join(data_key)) }

            if data_key is None:
                return {'error': "No hash for '%s'" % data_id}

        else:
            return {'error': 'No data hash or data ID given'}

    # already deleted?
    if not user_db.has_immutable_data( user_zonefile, data_key ):
        return {'status': True}

    # remove 
    user_db.remove_immutable_data_zonefile( user_zonefile, data_key )

    zonefile_hash = hash_zonefile( user_zonefile )
    
    if txid is None:
        # actually send the transaction
        _, payment_privkey = get_payment_keypair(wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        _, owner_privkey = get_owner_keypair(wallet_keys=wallet_keys, config_path=proxy.conf['path'])
        utxo_client = get_utxo_provider_client( config_path=proxy.conf['path'] )
        broadcaster_client = get_tx_broadcaster( config_path=proxy.conf['path'] )

        update_result = do_update( name, zonefile_hash, owner_privkey, payment_privkey, utxo_client, broadcaster_client, config_path=proxy.conf['path'], proxy=proxy )
        if 'error' in update_result:
            # failed to remove from zonefile 
            return update_result 

        txid = update_result['transaction_hash']

    result = {
        'zonefile_hash': zonefile_hash,
        'transaction_hash': txid
    }
    
    # put new zonefile 
    rc = store_name_zonefile( name, user_zonefile, txid )
    if not rc:
        result['error'] = 'Failed to put new zonefile'
        return result

    # delete immutable data 
    data_privkey = get_data_or_owner_privkey( user_zonefile, name_record['address'], wallet_keys=wallet_keys, config_path=proxy.conf['path'] )
    if 'error' in data_privkey:
        return {'error': data_privkey['error']}
    else:
        data_privkey = data_privkey['privatekey']
        assert data_privkey is not None

    rc = storage.delete_immutable_data( data_key, txid, data_privkey )
    if not rc:
        result['error'] = 'Failed to delete immutable data'
        return result

    else:
        result['status'] = True
        return result
def check( state_engine ):

    global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace not ready"
        return False 

    ns = state_engine.get_namespace( "test" )
    if ns is None:
        print "no namespace"
        return False 

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False 

    # not preordered
    names = ['foo.test', 'bar.test']
    wallet_keys_list = [wallet_keys, wallet_keys_2]
    zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]]

    for i in xrange(0, len(names)):
        name = names[i]
        wallet_payer = 3 * (i+1) - 1
        wallet_owner = 3 * (i+1)
        wallet_data_pubkey = 3 * (i+1)  # same as owner key
        wallet_keys = wallet_keys_list[i]
        zonefile_hash = zonefile_hashes[i]

        preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr )
        if preorder is not None:
            print "still have preorder"
            return False
    
        # registered 
        name_rec = state_engine.get_name( name )
        if name_rec is None:
            print "name does not exist"
            return False 

        # owned 
        if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr):
            print "name has wrong owner"
            return False 

        # zonefile is NOT legacy 
        user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash )
        if 'error' in user_zonefile:
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False 

        if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ):
            print "legacy still"
            print json.dumps(user_zonefile, indent=4, sort_keys=True)
            return False

        # still have all the right info 
        user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr )
        if user_profile is None:
            print "Unable to load user profile for %s (%s)" % (name, wallets[wallet_data_pubkey].pubkey_hex)
            return False

        if 'error' in user_profile:
            print json.dumps(user_profile, indent=4, sort_keys=True)
            return False

    # can fetch latest by name 
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/" )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if immutable_data['data'] != {'hello': 'world'}:
        print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-latest hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # can fetch by name and hash
    immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash )
    if 'error' in immutable_data:
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False 

    if immutable_data['data'] != {'hello': 'world'}:
        print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'})
        return False 

    if immutable_data['hash'] != immutable_hash:
        print "immutable fetch-by-hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash)
        return False 

    # hash must match (if we put the wrong hash, it must fail)
    try:
        immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % ("0" * len(immutable_hash)))
        print "no error"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list names and hashes
    immutable_data_list = get_data( "blockstack://foo.test/#immutable" )
    if 'error' in immutable_data_list:
        print json.dumps(immutable_data, indent=4, sort_keys=True )
        return False 

    if len(immutable_data_list['data']) != 1:
        print "multiple immutable data"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    if immutable_data_list['data'][0]['data_id'] != 'hello_world_immutable' or immutable_data_list['data'][0]['hash'] != immutable_hash:
        print "wrong data ID and/or hash"
        print json.dumps(immutable_data_list, indent=4, sort_keys=True )
        return False 

    # can fetch latest mutable by name
    mutable_data = get_data( "blockstack://bar.test/hello_world_mutable")
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if mutable_data['data'] != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False

    if mutable_data['version'] != 1:
        print "wrong version: %s" % mutable_data['data']['version']
        return False 

    # can fetch by version
    mutable_data = get_data( "blockstack://bar.test/hello_world_mutable#1")
    if 'error' in mutable_data:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    if mutable_data['data'] != {'hello': 'world'}:
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False 

    # will fail to fetch if we give the wrong version 
    try:
        mutable_data = get_data("blockstack://bar.test/hello_world_mutable#2")
        print "mutable fetch by wrong version worked"
        print json.dumps(mutable_data, indent=4, sort_keys=True)
        return False
    except urllib2.URLError:
        pass

    # can list mutable data
    mutable_data_list = get_data( "blockstack://bar.test/#mutable" )
    if 'error' in mutable_data_list:
        print json.dumps(mutable_data_list, indent=4, sort_keys=True )
        return False 

    if len(mutable_data_list) != 1:
        print "multiple mutable data"
        print json.dumps(mutable_data_list, indent=4, sort_keys=True )
        return False 

    if mutable_data_list['data'][0]['data_id'] != 'hello_world_mutable' or mutable_data_list['data'][0]['version'] != 1:
        print "wrong data id and/or version"
        print json.dumps(mutable_data_list, indent=4, sort_keys=True)
        return False

    return True
def check(state_engine):

    global wallet_keys, datasets, zonefile_hash

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace not ready"
        return False

    ns = state_engine.get_namespace("test")
    if ns is None:
        print "no namespace"
        return False

    if ns['namespace_id'] != 'test':
        print "wrong namespace"
        return False

    # not preordered
    preorder = state_engine.get_name_preorder(
        "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr),
        wallets[3].addr)
    if preorder is not None:
        print "still have preorder"
        return False

    # registered
    name_rec = state_engine.get_name("foo.test")
    if name_rec is None:
        print "name does not exist"
        return False

    # owned
    if name_rec['address'] != wallets[3].addr or name_rec[
            'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr):
        print "name has wrong owner"
        return False

    # have right hash
    if name_rec['value_hash'] != zonefile_hash:
        print "Invalid zonefile hash"
        return False

    # zonefile is NOT legacy
    user_zonefile = blockstack_client.zonefile.load_name_zonefile(
        'foo.test', zonefile_hash)
    if 'error' in user_zonefile:
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    if blockstack_profiles.is_profile_in_legacy_format(user_zonefile):
        print "legacy still"
        print json.dumps(user_zonefile, indent=4, sort_keys=True)
        return False

    res = testlib.blockstack_cli_lookup("foo.test")
    if 'error' in res:
        print 'error looking up profile: {}'.format(res)
        return False

    assert 'profile' in res
    assert 'zonefile' in res

    return True