def check( state_engine ):

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace reveal exists"
        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 
    
    # registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is None:
        print "name does not exist"
        return False 

    # owned by the right address 
    owner_address = wallets[3].addr
    if name_rec['address'] != owner_address or name_rec['sender'] != virtualchain.make_payment_script(owner_address):
        print "sender is wrong"
        print "%s != %s or %s != %s" % (name_rec['address'], owner_address, name_rec['sender'], virtualchain.make_payment_script(owner_address))
        return False 

    # all queues are drained 
    queue_info = testlib.blockstack_client_queue_state()
    if len(queue_info) > 0:
        print "Still in queue:\n%s" % json.dumps(queue_info, indent=4, sort_keys=True)
        return False

    # have an update hash 
    if 'value_hash' not in name_rec or name_rec.get('value_hash', None) is None:
        print "No value hash"
        return False 

    # have a zonefile 
    zonefile = testlib.blockstack_get_zonefile( name_rec['value_hash'] )
    if zonefile is None or 'error' in zonefile:
        if zonefile is not None:
            print "zonefile lookup error: %s" % zonefile['error']
        else:
            print "no zonefile returned"
        return False

    # hashes to this zonefile 
    if blockstack_client.hash_zonefile( zonefile ) != name_rec['value_hash']:
        print "wrong zonefile: %s != %s" % (blockstack_client.hash_zonefile(zonefile), name_rec['value_hash'])
        return False

    # verify that the profile is there 
    profile = testlib.blockstack_get_profile( "foo.test" )
    if profile is None or 'error' in profile:
        if profile is None:
            print "no profile returned"
        else:
            print "profile lookup error: %s" % profile['error']

        return False

    return True
Exemple #2
0
def check(state_engine):

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace reveal exists"
        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

    for i in xrange(0, 5):
        # registered
        name_rec = state_engine.get_name("foo_%s.test" % i)
        if name_rec is None:
            print "name does not exist"
            return False

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

        # have a zonefile
        zonefile = testlib.blockstack_get_zonefile(name_rec['value_hash'])
        if zonefile is None or 'error' in zonefile:
            if zonefile is not None:
                print "zonefile lookup error: %s" % zonefile['error']
            else:
                print "no zonefile returned"
            return False

        # hashes to this zonefile
        if blockstack_client.hash_zonefile(zonefile) != name_rec['value_hash']:
            print "wrong zonefile: %s != %s" % (
                blockstack_client.hash_zonefile(zonefile),
                name_rec['value_hash'])
            return False

        # verify that the profile is there
        profile = testlib.blockstack_get_profile("foo_%s.test" % i)
        if profile is None or 'error' in profile:
            if profile is None:
                print "no profile returned"
            else:
                print "profile lookup error: %s" % profile['error']

            return False

    # all queues are drained
    queue_info = testlib.blockstack_client_queue_state()
    if len(queue_info) > 0:
        print "Still in queue:\n%s" % json.dumps(
            queue_info, indent=4, sort_keys=True)
        return False

    return True
def check(state_engine):

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace reveal exists"
        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

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

    # owned by the right address
    owner_address = wallets[4].addr
    if name_rec['address'] != owner_address or name_rec[
            'sender'] != virtualchain.make_payment_script(owner_address):
        print "sender is wrong"
        return False

    # all queues are drained
    queue_info = testlib.blockstack_client_queue_state()
    if len(queue_info) > 0:
        print "Still in queue:\n%s" % json.dumps(
            queue_info, indent=4, sort_keys=True)
        return False

    # have an update hash
    if 'value_hash' not in name_rec or name_rec.get('value_hash',
                                                    None) is None:
        print "No value hash"
        return False

    # have a zonefile
    zonefile = testlib.blockstack_get_zonefile(name_rec['value_hash'])
    if zonefile is None or 'error' in zonefile:
        if zonefile is not None:
            print "zonefile lookup error: %s" % zonefile['error']
        else:
            print "no zonefile returned"
        return False

    # hashes to this zonefile
    if blockstack_client.hash_zonefile(zonefile) != name_rec['value_hash']:
        print "wrong zonefile: %s != %s" % (
            blockstack_client.hash_zonefile(zonefile), name_rec['value_hash'])
        return False

    # zonefile has the right data public key
    zonefile_pubk = blockstack_client.user.user_zonefile_data_pubkey(zonefile)
    if keylib.key_formatting.compress(
            zonefile_pubk) != keylib.key_formatting.compress(
                wallets[4].pubkey_hex) or zonefile_pubk is None:
        print 'pubkey mismatch: {} != {}'.format(zonefile_pubk,
                                                 wallets[4].pubkey_hex)
        return False

    # zonefile has the right drivers
    zonefile_urls = blockstack_client.user.user_zonefile_urls(zonefile)
    driver_urls = blockstack_client.storage.make_mutable_data_urls(
        'foo.test', use_only=['dht', 'disk'])
    if driver_urls != zonefile_urls:
        print 'url mismatch: {} != {}'.format(driver_urls, zonefile_urls)
        return False

    # verify that the profile is NOT there
    profile = testlib.blockstack_get_profile("foo.test")
    if profile is not None and 'error' not in profile:
        print 'make a profile by mistake: {}'.format(profile)
        return False

    return True
Exemple #4
0
def scenario( wallets, **kw ):
    global GAIA_READ_URL
    global GAIA_READ_PORT
    global GAIA_WRITE_PORT
    global GAIA_WRITE_URL
    global owner_privkey
    global owner_address

    # get gaia hub info 
    with open(os.path.join(os.environ['BLOCKSTACK_WORKING_DIR'], 'gaia.conf'), 'r') as f:
        GAIA_CONF = json.loads(f.read().strip())

    try:
        GAIA_READ_PORT = urlparse.urlparse(GAIA_CONF['readURL']).netloc.split(':')[-1]
        GAIA_READ_PORT = int(GAIA_READ_PORT)
    except:
        GAIA_READ_PORT = 80

    if os.environ.get('BLOCKSTACK_PUBLIC_TESTNET_GAIA_READ_PORT'):
        GAIA_READ_PORT = int(os.environ['BLOCKSTACK_PUBLIC_TESTNET_GAIA_READ_PORT'])

    read_urlinfo = urlparse.urlparse(GAIA_CONF['readURL'])

    GAIA_READ_URL = 'http://{}:{}'.format(read_urlinfo.netloc.split(':')[0], GAIA_READ_PORT)

    GAIA_WRITE_PORT = GAIA_CONF['port']
    if os.environ.get('BLOCKSTACK_PUBLIC_TESTNET_GAIA_WRITE_PORT'):
        GAIA_WRITE_PORT = int(os.environ['BLOCKSTACK_PUBLIC_TESTNET_GAIA_WRITE_PORT'])

    GAIA_WRITE_URL = 'http://{}:{}'.format(GAIA_CONF['servername'], GAIA_WRITE_PORT)

    testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey )
    testlib.next_block( **kw )

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, -1, 250, 4, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey, version_bits=1)
    testlib.next_block( **kw )

    testlib.blockstack_namespace_ready( "test", wallets[1].privkey )
    testlib.next_block( **kw )

    # register this user under a keychain 
    res = testlib.nodejs_cli('make_keychain')
    res = json.loads(res)
    mnemonic = res['mnemonic']
    owner_privkey = res['ownerKeyInfo']['privateKey']
    owner_address = res['ownerKeyInfo']['idAddress'][3:]

    profile = {
        'type': '@Person',
        'account': [],
        'name': 'Testy McTestface',
    }

    testlib.blockstack_register_user('foo.test', wallets[3].privkey, owner_privkey, profile=profile, profile_name='0/profile.json', **kw)

    # verify that we can look up this profile
    res = testlib.blockstack_get_profile('foo.test')
    if res['profile'] != profile:
        print 'wrong profile'
        print profile
        print res['profile']
        return False

    # patch the profile to insert an app URL 
    res = testlib.nodejs_cli('gaia_sethub', 'foo.test', GAIA_WRITE_URL, 'http://www.testapp.com', GAIA_WRITE_URL, mnemonic)

    res = testlib.blockstack_get_profile('foo.test')
    updated_profile = {}
    updated_profile.update(res['profile'])

    apps = updated_profile['apps']
    del updated_profile['apps']

    if updated_profile != profile:
        print 'wrong profile after sethub'
        print expected_profile
        print res['profile']
        return False

    if not apps['http://www.testapp.com'].startswith(GAIA_READ_URL):
        print 'wrong app entry for testapp.com'
        print apps
        print GAIA_WRITE_URL
        return False

    print ""
    print "mnemnic: {}".format(mnemonic)
    print "hub url: {}".format(GAIA_WRITE_URL)
    print ""
def check( state_engine ):

    global preorder_info, register_info, update_info, balance_before, balance_after, names_owned_before, names_owned_after, whois, blockchain_record, deposit_info, price_info
    global blockchain_history, zonefile_info, all_names_info, namespace_names_info, wallet_info, lookup_info, update_history, zonefile_history, names_info

    # not revealed, but ready 
    ns = state_engine.get_namespace_reveal( "test" )
    if ns is not None:
        print "namespace reveal exists"
        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 
    
    # registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is None:
        print "name does not exist"
        return False 

    # owned by the right address 
    owner_address = wallets[3].addr
    if name_rec['address'] != owner_address or name_rec['sender'] != pybitcoin.make_pay_to_address_script(owner_address):
        print "sender is wrong"
        return False 

    # all queues are drained 
    queue_info = testlib.blockstack_client_queue_state()
    if len(queue_info) > 0:
        print "Still in queue:\n%s" % json.dumps(queue_info, indent=4, sort_keys=True)
        return False

    # have an update hash 
    if 'value_hash' not in name_rec or name_rec.get('value_hash', None) is None:
        print "No value hash"
        return False 

    # have a zonefile 
    zonefile = testlib.blockstack_get_zonefile( name_rec['value_hash'] )
    if zonefile is None or 'error' in zonefile:
        if zonefile is not None:
            print "zonefile lookup error: %s" % zonefile['error']
        else:
            print "no zonefile returned"
        return False

    # hashes to this zonefile 
    if blockstack_client.hash_zonefile( zonefile ) != name_rec['value_hash']:
        print "wrong zonefile: %s != %s" % (blockstack_client.hash_zonefile(zonefile), name_rec['value_hash'])
        return False

    # verify that the profile is there 
    profile = testlib.blockstack_get_profile( "foo.test" )
    if profile is None or 'error' in profile:
        if profile is None:
            print "no profile returned"
        else:
            print "profile lookup error: %s" % profile['error']

        return False

    # check queue operations 
    for queue_type, queue_state in [("preorder", preorder_info), ("register", register_info), ("update", update_info)]:
        if not queue_state.has_key('queues'):
            print "missing queues:\n%s" % json.dumps(queue_state, indent=4, sort_keys=True)
            return False

        for k in ['name', 'confirmations', 'tx_hash']:
            for q in queue_state['queues'][queue_type]:
                if not q.has_key(k):
                    print "missing key %s\n%s" % (k, json.dumps(queue_state, indent=4, sort_keys=True))
                    return False
            
                if q['name'] != 'foo.test':
                    print "wrong name: %s" % queue_state['name']
                    return False

    # check price
    for k in ['preorder_tx_fee', 'register_tx_fee', 'update_tx_fee', 'total_estimated_cost', 'name_price']:
        if not price_info.has_key(k):
            print "bad price info (missing %s):\n%s" % (k, json.dumps(price_info, indent=4, sort_keys=True))
            return False

    
    # deposit info 
    if not deposit_info.has_key('address') or deposit_info['address'] != wallets[2].addr:
        print "bad deposit info:\n%s" % json.dumps(deposit_info, indent=4, sort_keys=True)
        return False

    # whois info
    for k in ['block_preordered_at', 'block_renewed_at', 'last_transaction_id', 'owner_address', 'owner_script', 'expire_block', 'has_zonefile', 'zonefile_hash']:
        if not whois.has_key(k):
            print "bad whois: missing %s\n%s" % (k, json.dumps(whois, indent=4, sort_keys=True))
            return False
    
    # balance 
    for balance_info in [balance_before, balance_after]:
        for k in ['total_balance', 'addresses']:
            if not balance_info.has_key(k):
                print "missing '%s'\n%s" % (k, json.dumps(balance_info, indent=4, sort_keys=True))
                return False

    # name listing
    if len(names_owned_before) != 0:
        print "owned before: %s" % names_owned_before
        return False

    if len(names_owned_after) != 1 or names_owned_after[0] != 'foo.test':
        print "owned after: %s" % names_owned_after
        return False

    # blockchain record 
    for k in ['name', 'op', 'op_fee', 'opcode', 'vtxindex', 'txid', 'value_hash', 'sender', 'address', 'history']:
        if not blockchain_record.has_key(k):
            print "missing %s\n%s" % (k, json.dumps(blockchain_record, indent=4, sort_keys=True))
            return False

    # blockchain history (should have a preorder, register, and 2 updates)
    if len(blockchain_history) != 4:
        print "invalid history\n%s\n" % json.dumps(blockchain_history, indent=4, sort_keys=True)
        return False

    block_heights = blockchain_history.keys()
    block_heights.sort()
    expected_opcodes = ['NAME_PREORDER', 'NAME_REGISTRATION', 'NAME_UPDATE', 'NAME_UPDATE']
    for bh, opcode in zip(block_heights, expected_opcodes):
        if len(blockchain_history[bh]) != 1:
            print "invalid history: multiple ops at %s\n%s" % (bh, json.dumps(blockchain_history, indent=4, sort_keys=True))
            return False

        if blockchain_history[bh][0]['opcode'] != opcode:
            print "invalid history: expected %s at %s\n%s" % (opcode, bh, json.dumps(blockchain_history, indent=4, sort_keys=True))
            return False

    # zonefile info
    if zonefile_info is None or type(zonefile_info) != str:
        print "invalid zonefile\n%s\n" % zonefile_info
        return False

    # name query
    if type(all_names_info) == dict and 'error' in all_names_info:
        print "error in all_names: %s" % all_names_info
        return False

    all_names = all_names_info
    if len(all_names) != 1 or all_names != ['foo.test']:
        print "all names: %s" % all_names
        return False

    # namespace query
    if type(namespace_names_info) == dict and 'error' in namespace_names_info:
        print "error in namesace_names: %s" % namespace_names_info
        return False

    namespace_names = namespace_names_info
    if len(namespace_names) != 1 or namespace_names != ['foo.test']:
        print "all namespace names: %s" % namespace_names
        return False

    # wallet info
    for k in ['payment_privkey', 'owner_privkey', 'data_privkey', 'payment_address', 'owner_address', 'data_pubkey']:
        if not wallet_info.has_key(k):
            print "missing %s\n%s" % (k, json.dumps(wallet_info, indent=4, sort_keys=True))
            return False

    # profile info
    for k in ['profile', 'zonefile']:
        if not lookup_info.has_key(k):
            print "missing '%s'\n%s" % (k, json.dumps(lookup_info, indent=4, sort_keys=True))
            return False

    if lookup_info['zonefile'] != zonefile_info:
        print "unequal zonefiles:\n%s\n%s" % (json.dumps(lookup_info['zonefile'], indent=4, sort_keys=True), json.dumps(zonefile_info, indent=4, sort_keys=True))
        return False

    # update history (2 items)
    if len(update_history) != 2 or update_history[1] != blockchain_record['value_hash']:
        print "invalid update history\n%s" % json.dumps(update_history, indent=4, sort_keys=True)
        return False

    # zonefile history (expect 2 items)
    if len(zonefile_history) != 2 or zonefile_history[1] != zonefile_info:
        print "invalid zonefile history\n%s" % json.dumps(zonefile_history, indent=4, sort_keys=True)
        print "zonefile current:\n%s" % json.dumps(zonefile_info, indent=4, sort_keys=True)
        return False

    # names info
    if type(names_info) != dict:
        print "invalid names info: %s" % names_info
        return False
        
    for k in ['names_owned', 'addresses']:
        if not names_info.has_key(k):
            print "invalid names info (missing %s): %s" % (k, names_info)
            return False

    if len(names_info['addresses']) != 1:
        print "invalid names info (addresses): %s" % names_info
        return False

    if names_info['addresses'][0]['names_owned'] != ['foo.test']:
        print "invalid names info (names_owned): %s" % names_info
        return False

    if names_info['addresses'][0]['address'] != wallets[3].addr:
        print "invalid names info (addresses.address): %s" % names_info
        return False

    # immutable data 
    immutable_data = testlib.blockstack_cli_get_immutable( "foo.test", "hello_world" )
    if 'error' in immutable_data:
        print "Failed to get immutable data 'hello_world'"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if 'data' not in immutable_data:
        print "invalid immutable_data: %s" % immutable_data
        return False 

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "failed to get immutable data"
        print 'exected %s, got %s' % ({'hello': 'world'}, immutable_data['data'])
        return False

    return True
Exemple #6
0
def check(state_engine):

    global preorder_info, register_info, update_info, balance_before, balance_after, names_owned_before, names_owned_after, whois, blockchain_record, deposit_info, price_info
    global blockchain_history, zonefile_info, all_names_info, namespace_names_info, wallet_info, lookup_info, update_history, zonefile_history, names_info

    # not revealed, but ready
    ns = state_engine.get_namespace_reveal("test")
    if ns is not None:
        print "namespace reveal exists"
        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

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

    # owned by the right address
    owner_address = wallets[3].addr
    if name_rec['address'] != owner_address or name_rec[
            'sender'] != pybitcoin.make_pay_to_address_script(owner_address):
        print "sender is wrong"
        return False

    # all queues are drained
    queue_info = testlib.blockstack_client_queue_state()
    if len(queue_info) > 0:
        print "Still in queue:\n%s" % json.dumps(
            queue_info, indent=4, sort_keys=True)
        return False

    # have an update hash
    if 'value_hash' not in name_rec or name_rec.get('value_hash',
                                                    None) is None:
        print "No value hash"
        return False

    # have a zonefile
    zonefile = testlib.blockstack_get_zonefile(name_rec['value_hash'])
    if zonefile is None or 'error' in zonefile:
        if zonefile is not None:
            print "zonefile lookup error: %s" % zonefile['error']
        else:
            print "no zonefile returned"
        return False

    # hashes to this zonefile
    if blockstack_client.hash_zonefile(zonefile) != name_rec['value_hash']:
        print "wrong zonefile: %s != %s" % (
            blockstack_client.hash_zonefile(zonefile), name_rec['value_hash'])
        return False

    # verify that the profile is there
    profile = testlib.blockstack_get_profile("foo.test")
    if profile is None or 'error' in profile:
        if profile is None:
            print "no profile returned"
        else:
            print "profile lookup error: %s" % profile['error']

        return False

    # check queue operations
    for queue_type, queue_state in [("preorder", preorder_info),
                                    ("register", register_info),
                                    ("update", update_info)]:
        if not queue_state.has_key('queues'):
            print "missing queues:\n%s" % json.dumps(
                queue_state, indent=4, sort_keys=True)
            return False

        for k in ['name', 'confirmations', 'tx_hash']:
            for q in queue_state['queues'][queue_type]:
                if not q.has_key(k):
                    print "missing key %s\n%s" % (
                        k, json.dumps(queue_state, indent=4, sort_keys=True))
                    return False

                if q['name'] != 'foo.test':
                    print "wrong name: %s" % queue_state['name']
                    return False

    # check price
    for k in [
            'preorder_tx_fee', 'register_tx_fee', 'update_tx_fee',
            'total_estimated_cost', 'name_price'
    ]:
        if not price_info.has_key(k):
            print "bad price info (missing %s):\n%s" % (
                k, json.dumps(price_info, indent=4, sort_keys=True))
            return False

    # deposit info
    if not deposit_info.has_key(
            'address') or deposit_info['address'] != wallets[2].addr:
        print "bad deposit info:\n%s" % json.dumps(
            deposit_info, indent=4, sort_keys=True)
        return False

    # whois info
    for k in [
            'block_preordered_at', 'block_renewed_at', 'last_transaction_id',
            'owner_address', 'owner_script', 'expire_block', 'has_zonefile',
            'zonefile_hash'
    ]:
        if not whois.has_key(k):
            print "bad whois: missing %s\n%s" % (
                k, json.dumps(whois, indent=4, sort_keys=True))
            return False

    # balance
    for balance_info in [balance_before, balance_after]:
        for k in ['total_balance', 'addresses']:
            if not balance_info.has_key(k):
                print "missing '%s'\n%s" % (
                    k, json.dumps(balance_info, indent=4, sort_keys=True))
                return False

    # name listing
    if len(names_owned_before) != 0:
        print "owned before: %s" % names_owned_before
        return False

    if len(names_owned_after) != 1 or names_owned_after[0] != 'foo.test':
        print "owned after: %s" % names_owned_after
        return False

    # blockchain record
    for k in [
            'name', 'op', 'op_fee', 'opcode', 'vtxindex', 'txid', 'value_hash',
            'sender', 'address', 'history'
    ]:
        if not blockchain_record.has_key(k):
            print "missing %s\n%s" % (
                k, json.dumps(blockchain_record, indent=4, sort_keys=True))
            return False

    # blockchain history (should have a preorder, register, and 2 updates)
    if len(blockchain_history) != 4:
        print "invalid history\n%s\n" % json.dumps(
            blockchain_history, indent=4, sort_keys=True)
        return False

    block_heights = blockchain_history.keys()
    block_heights.sort()
    expected_opcodes = [
        'NAME_PREORDER', 'NAME_REGISTRATION', 'NAME_UPDATE', 'NAME_UPDATE'
    ]
    for bh, opcode in zip(block_heights, expected_opcodes):
        if len(blockchain_history[bh]) != 1:
            print "invalid history: multiple ops at %s\n%s" % (
                bh, json.dumps(blockchain_history, indent=4, sort_keys=True))
            return False

        if blockchain_history[bh][0]['opcode'] != opcode:
            print "invalid history: expected %s at %s\n%s" % (
                opcode, bh,
                json.dumps(blockchain_history, indent=4, sort_keys=True))
            return False

    # zonefile info
    if zonefile_info is None or type(zonefile_info) != dict:
        print "invalid zonefile\n%s\n" % zonefile_info
        return False

    if not zonefile_info.has_key('zonefile'):
        print "missing zonefile\n%s\n" % zonefile_info
        return False

    # name query
    if type(all_names_info) == dict and 'error' in all_names_info:
        print "error in all_names: %s" % all_names_info
        return False

    all_names = all_names_info
    if len(all_names) != 1 or all_names != ['foo.test']:
        print "all names: %s" % all_names
        return False

    # namespace query
    if type(namespace_names_info) == dict and 'error' in namespace_names_info:
        print "error in namesace_names: %s" % namespace_names_info
        return False

    namespace_names = namespace_names_info
    if len(namespace_names) != 1 or namespace_names != ['foo.test']:
        print "all namespace names: %s" % namespace_names
        return False

    # wallet info
    for k in [
            'payment_privkey', 'owner_privkey', 'data_privkey',
            'payment_address', 'owner_address', 'data_pubkey'
    ]:
        if not wallet_info.has_key(k):
            print "missing %s\n%s" % (
                k, json.dumps(wallet_info, indent=4, sort_keys=True))
            return False

    # profile info
    for k in ['profile', 'zonefile']:
        if not lookup_info.has_key(k):
            print "missing '%s'\n%s" % (
                k, json.dumps(lookup_info, indent=4, sort_keys=True))
            return False

    if lookup_info['zonefile'] != zonefile_info['zonefile']:
        print "unequal zonefiles:\n%s\n%s" % (
            json.dumps(lookup_info['zonefile'], indent=4, sort_keys=True),
            json.dumps(zonefile_info['zonefile'], indent=4, sort_keys=True))
        return False

    # update history (2 items)
    if len(update_history
           ) != 2 or update_history[1] != blockchain_record['value_hash']:
        print "invalid update history\n%s" % json.dumps(
            update_history, indent=4, sort_keys=True)
        return False

    # zonefile history (expect 2 items)
    if len(zonefile_history
           ) != 2 or zonefile_history[1] != zonefile_info['zonefile']:
        print "invalid zonefile history\n%s" % json.dumps(
            zonefile_history, indent=4, sort_keys=True)
        return False

    # names info
    if type(names_info) != dict:
        print "invalid names info: %s" % names_info
        return False

    for k in ['names_owned', 'addresses']:
        if not names_info.has_key(k):
            print "invalid names info (missing %s): %s" % (k, names_info)
            return False

    if len(names_info['addresses']) != 1:
        print "invalid names info (addresses): %s" % names_info
        return False

    if names_info['addresses'][0]['names_owned'] != ['foo.test']:
        print "invalid names info (names_owned): %s" % names_info
        return False

    if names_info['addresses'][0]['address'] != wallets[3].addr:
        print "invalid names info (addresses.address): %s" % names_info
        return False

    # immutable data
    immutable_data = testlib.blockstack_cli_get_immutable(
        "foo.test", "hello_world")
    if 'error' in immutable_data:
        print "Failed to get immutable data 'hello_world'"
        print json.dumps(immutable_data, indent=4, sort_keys=True)
        return False

    if 'data' not in immutable_data:
        print "invalid immutable_data: %s" % immutable_data
        return False

    if json.loads(immutable_data['data']) != {'hello': 'world'}:
        print "failed to get immutable data"
        print 'exected %s, got %s' % ({
            'hello': 'world'
        }, immutable_data['data'])
        return False

    return True