Example #1
0
def parse_DID(did, name_type=None):
    """
    Given a DID string, parse it into {'address': ..., 'index': ..., 'name_type'}
    Raise on invalid DID
    """
    did_pattern = '^did:stack:v0:({}{{25,35}})-([0-9]+)$'.format(OP_BASE58CHECK_CLASS)

    m = re.match(did_pattern, did)
    assert m, 'Invalid DID: {}'.format(did)

    original_address = str(m.groups()[0])
    name_index = int(m.groups()[1])
    vb = keylib.b58check.b58check_version_byte(original_address)
    name_type = None

    if vb in [SUBDOMAIN_ADDRESS_VERSION_BYTE, SUBDOMAIN_ADDRESS_MULTISIG_VERSION_BYTE]:
        name_type = 'subdomain'

        # decode version 
        if vb == SUBDOMAIN_ADDRESS_VERSION_BYTE:
            vb = bitcoin_blockchain.version_byte
        else:
            vb = bitcoin_blockchain.multisig_version_byte

        original_address = virtualchain.address_reencode(original_address, version_byte=vb)

    else:
        name_type = 'name'
        original_address = virtualchain.address_reencode(original_address)

    return {'address': original_address, 'index': name_index, 'name_type': name_type}
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

    # not preordered
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(
        virtualchain.get_privkey_address(pk2))
    preorder = state_engine.get_name_preorder(
        "foo.test", virtualchain.make_payment_script(addr), addr2)
    if preorder is not None:
        print "preorder exists"
        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
    if name_rec['address'] != addr2 or name_rec[
            'sender'] != virtualchain.make_payment_script(addr2):
        print "sender is wrong"
        return False

    # paid with Stacks
    stacks_price = blockstack.lib.scripts.price_name_stacks(
        'foo', ns, state_engine.lastblock)
    if name_rec['token_fee'] != stacks_price:
        print 'paid wrong token fee'
        print 'expected {} ({}), got {} ({})'.format(
            stacks_price, type(stacks_price), name_rec['token_fee'],
            type(name_rec['token_fee']))
        return False

    if name_rec['op_fee'] > 5500:  # dust minimum
        print 'paid in BTC ({})'.format(name_rec['op_fee'])
        return False

    # renewed on third attempt
    if name_rec['first_registered'] + 3 != name_rec['last_renewed']:
        print 'renewed at the wrong time: {} + 3 != {}'.format(
            name_rec['first_registered'], name_rec['last_renewed'])
        return False

    return True
Example #3
0
def unlock_wallet(password=None, config_dir=CONFIG_DIR, wallet_path=None):
    """
    Unlock the wallet, and store it to the running RPC daemon.
    
    This will only work if the wallet is in the latest supported state.
    Otherwise, the caller may need to migrate the wallet first.
    
    Return {'status': True} on success
    return {'error': ...} on error
    """
    config_path = os.path.join(config_dir, CONFIG_FILENAME)
    wallet_path = os.path.join(config_dir, WALLET_FILENAME) if wallet_path is None else wallet_path

    if is_wallet_unlocked(config_dir):
        return {'status': True}

    try:
        if password is None:
            password = prompt_wallet_password()

        with open(wallet_path, "r") as f:
            data = f.read()
            data = json.loads(data)

        # decrypt...
        wallet_info = decrypt_wallet( data, password, config_path=config_path )
        if 'error' in wallet_info:
            log.error('Failed to decrypt wallet: {}'.format(wallet_info['error']))
            return wallet_info

        wallet = wallet_info['wallet']
        if wallet_info['migrated']:
            # need to have the user migrate the wallet first
            return {'error': 'Wallet is in legacy format.  Please migrate it with the `setup_wallet` command.', 'legacy': True}

        # save to RPC daemon
        try:
            res = save_keys_to_memory( wallet, config_path=config_path )
        except KeyError as ke:
            if BLOCKSACK_DEBUG is not None:
                data = json.dumps(wallet, indent=4, sort_keys=True)
                log.error('data:\n{}\n'.format(data))
            raise

        if 'error' in res:
            return res

        addresses = {
            'payment_address': virtualchain.address_reencode(wallet['payment_addresses'][0]),
            'owner_address': virtualchain.address_reencode(wallet['owner_addresses'][0]),
            'data_pubkey': virtualchain.address_reencode(wallet['data_pubkeys'][0])
        }

        return {'status': True, 'addresses': addresses}
    except KeyboardInterrupt:
        return {'error': 'Interrupted'}
def unlock_wallet(password=None, config_dir=CONFIG_DIR, wallet_path=None):
    """
    Unlock the wallet, and store it to the running RPC daemon.
    
    This will only work if the wallet is in the latest supported state.
    Otherwise, the caller may need to migrate the wallet first.
    
    Return {'status': True} on success
    return {'error': ...} on error
    """
    config_path = os.path.join(config_dir, CONFIG_FILENAME)
    wallet_path = os.path.join(config_dir, WALLET_FILENAME) if wallet_path is None else wallet_path

    if is_wallet_unlocked(config_dir):
        return {'status': True}

    try:
        if password is None:
            password = prompt_wallet_password()

        with open(wallet_path, "r") as f:
            data = f.read()
            data = json.loads(data)

        # decrypt...
        wallet_info = decrypt_wallet( data, password, config_path=config_path )
        if 'error' in wallet_info:
            log.error('Failed to decrypt wallet: {}'.format(wallet_info['error']))
            return wallet_info

        wallet = wallet_info['wallet']
        if wallet_info['migrated']:
            # need to have the user migrate the wallet first
            return {'error': 'Wallet is in legacy format.  Please migrate it with the `setup_wallet` command.', 'legacy': True}

        # save to RPC daemon
        try:
            res = save_keys_to_memory( wallet, config_path=config_path )
        except KeyError as ke:
            if BLOCKSTACK_DEBUG is not None:
                data = json.dumps(wallet, indent=4, sort_keys=True)
                log.error('data:\n{}\n'.format(data))
            raise

        if 'error' in res:
            return res

        addresses = {
            'payment_address': virtualchain.address_reencode(wallet['payment_addresses'][0]),
            'owner_address': virtualchain.address_reencode(wallet['owner_addresses'][0]),
            'data_pubkey': virtualchain.address_reencode(wallet['data_pubkeys'][0])
        }

        return {'status': True, 'addresses': addresses}
    except KeyboardInterrupt:
        return {'error': 'Interrupted'}
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 

    # not preordered
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2))
    preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(addr), wallets[3].addr )
    if preorder is not None:
        print "preorder exists for foo.test"
        return False
    
    # not registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is not None:
        print "name exists"
        return False 

    # not preordered
    preorder = state_engine.get_name_preorder('bar.test', virtualchain.make_payment_script(addr2), wallets[3].addr)
    if preorder is not None:
        print 'preorder exists for bar.test'
        return False

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

    # owned
    if name_rec['address'] != wallets[3].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[3].addr):
        print 'bar.test not owned'
        return False

    # overpaid
    if name_rec['op_fee'] > 5500 or name_rec['token_fee'] != blockstack.lib.scripts.price_name_stacks('bar', ns, state_engine.lastblock) + 1:
        print 'bar.test improperly paid'
        return False

    return True
Example #6
0
def scenario( wallets, **kw ):
    global pk, pk2

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

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey, version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR)
    testlib.next_block( **kw )

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

    # pay for a name in a v1 namespace with Stacks
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2))

    # calculate the cost of doing so
    namespace = testlib.get_state_engine().get_namespace('test')
    stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw))
    btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw))

    print ''
    print 'price of {} in Stacks is {}'.format('foo.test', stacks_price)
    print 'price of {} in BTC is {}'.format('foo.test', btc_price)
    print ''

    testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 2, wallets[0].privkey)
    testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey)
    testlib.send_funds(wallets[0].privkey, 3*btc_price, addr)
    testlib.send_funds(wallets[0].privkey, 3*btc_price, addr2)
    testlib.next_block(**kw)

    # preorder/register using Stacks
    testlib.blockstack_name_preorder( "foo.test", pk, addr2)
    testlib.blockstack_name_preorder( "bar.test", pk2, addr)
    testlib.next_block( **kw )

    testlib.blockstack_name_register( "foo.test", pk, addr2 )
    testlib.blockstack_name_register( "bar.test", pk2, addr )
    testlib.next_block( **kw )
    testlib.next_block( **kw )
    testlib.next_block( **kw )
    testlib.next_block( **kw ) # end of pay to namespace creator

    # renew using more stacks than we have (should fail)
    # bar.test should succeed
    testlib.blockstack_name_renew('foo.test', pk2, price={'units': 'STACKS', 'amount': stacks_price + 2}, expect_fail=True, safety_checks=False)
    testlib.blockstack_name_renew('bar.test', pk, price={'units': 'STACKS', 'amount': stacks_price + 1})
    testlib.next_block(**kw)
    testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw))
Example #7
0
def convert_funds_to_segwit(payment_key, tx_fee):
    # convert payment key to bech32
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(payment_key))
    pubk = virtualchain.lib.ecdsalib.ecdsa_private_key(payment_key, compressed=True).public_key().to_hex()
    addrhash = virtualchain.lib.hashing.bin_hash160(pubk.decode('hex')).encode('hex')
    segwit_addr = virtualchain.segwit_addr_encode(addrhash.decode('hex'), hrp='bcrt')
    
    # fund the segwit address, and then use the same payment key to send the transaction 
    fund_inputs = testlib.get_utxos(addr)
    fund_outputs = [
        {"script": '0014' + addrhash,
         "value": sum(inp['value'] for inp in fund_inputs) - tx_fee},
    ]
    fund_prev_outputs = [{'out_script': inp['out_script'], 'value': inp['value']} for inp in fund_inputs]
    fund_serialized_tx = testlib.serialize_tx(fund_inputs, fund_outputs)
    fund_signed_tx = virtualchain.tx_sign_all_unsigned_inputs(payment_key, fund_prev_outputs, fund_serialized_tx)

    print fund_signed_tx

    res = testlib.broadcast_transaction(fund_signed_tx)
    assert 'error' not in res, res

    res.update({
        'utxos': fund_outputs
    })
    return res
    def _tx_pay_btc(txhex, privk, burn_price, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS):
        tx = virtualchain.btc_tx_deserialize(txhex)

        # up the burn amount 
        tx['outs'][3]['script'] = virtualchain.btc_make_payment_script(burn_addr)
        tx['outs'][3]['value'] = burn_price

        tx['outs'][4]['value'] -= burn_price

        # re-sign 
        for i in tx['ins']:
            i['script'] = ''

        txhex = virtualchain.btc_tx_serialize(tx)
        _addr = virtualchain.address_reencode(virtualchain.get_privkey_address(privk))
        txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(privk, testlib.get_utxos(_addr), txhex)

        # re-sign the last output with the payment key
        tx_signed = virtualchain.btc_tx_deserialize(txhex_signed)
        tx_signed['ins'][-1]['script'] = ''
        txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(testlib.get_default_payment_wallet().privkey, testlib.get_utxos(testlib.get_default_payment_wallet().addr), virtualchain.btc_tx_serialize(tx_signed))
        
        print txhex_signed

        res = testlib.broadcast_transaction(txhex_signed)
        assert 'error' not in res
        return res
Example #9
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 

    # not preordered
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(addr), wallets[3].addr )
    if preorder is not None:
        print "preorder exists"
        return False
    
    # not registered 
    name_rec = state_engine.get_name( "foo.test" )
    if name_rec is not None:
        print "name exists"
        return False 

    return True
    def get_master_address(self):
        if self.master_address is not None:
            return self.master_address

        hex_privkey = self.get_master_privkey()
        hex_pubkey = get_pubkey_hex(hex_privkey)
        return virtualchain.address_reencode(keylib.public_key_to_address(hex_pubkey))
def scenario( wallets, **kw ):
    global pk, pk2

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

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey, version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR)
    testlib.next_block( **kw )

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

    # pay for a name in a v1 namespace with Stacks
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2))

    # calculate the cost of doing so
    namespace = testlib.get_state_engine().get_namespace('test')
    stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw))
    btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw))

    print ''
    print 'price of {} in Stacks is {}'.format('foo.test', stacks_price)
    print ''

    testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 1, wallets[0].privkey)
    testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey)
    testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr)  # deliberately insufficient funds for ordering the name in BTC
    testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr2)  # deliberately insufficient funds for ordering the name in BTC
    testlib.next_block(**kw)
    testlib.next_block(**kw)
    testlib.next_block(**kw)
    testlib.next_block(**kw)    # move beyond pay-to-creator requirement

    # preorder/register using Stacks, but overpay (foo.test fails, bar.test succeeds)
    testlib.blockstack_name_preorder( "foo.test", pk, wallets[3].addr, price={'units': 'STACKS', 'amount': stacks_price + 2}, expect_fail=True, safety_checks=False)
    testlib.blockstack_name_preorder( "bar.test", pk2, wallets[3].addr, price={'units': 'STACKS', 'amount': stacks_price + 1})
    testlib.next_block( **kw )
    testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw))

    testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr)  # deliberately insufficient funds for ordering the name in BTC
    testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr2)  # deliberately insufficient funds for ordering the name in BTC
    testlib.blockstack_name_register( "foo.test", pk, wallets[3].addr ) # should fail
    testlib.blockstack_name_register( "bar.test", pk2, wallets[3].addr ) # should succeed
    testlib.next_block( **kw )
    testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw))
Example #12
0
    def get_master_address(self):
        if self.master_address is not None:
            return self.master_address

        hex_privkey = self.get_master_privkey()
        hex_pubkey = get_pubkey_hex(hex_privkey)
        return virtualchain.address_reencode(
            keylib.public_key_to_address(hex_pubkey))
Example #13
0
def send_as_segwit_bech32(txhex, payment_key):
    print 'txhex: {}'.format(txhex)

    # get op-return data
    tx = virtualchain.btc_tx_deserialize(txhex)
    payload = tx['outs'][0]['script']
    
    print json.dumps(tx, indent=4, sort_keys=True)

    # convert payment key to bech32
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(payment_key))
    pubk = virtualchain.lib.ecdsalib.ecdsa_private_key(payment_key, compressed=True).public_key().to_hex()
    addrhash = virtualchain.lib.hashing.bin_hash160(pubk.decode('hex')).encode('hex')
    segwit_addr = virtualchain.segwit_addr_encode(addrhash.decode('hex'), hrp='bcrt')
    
    print 'privk = {}'.format(payment_key)
    print 'pubk = {}'.format(pubk)
    print 'addr = {}'.format(addr)
    print 'segwit addr = {}'.format(segwit_addr)
    print 'script = 00{}'.format(addrhash)

    tx_fee = 5500
    res = convert_funds_to_segwit(payment_key, tx_fee)
    fund_outputs = res['utxos']
    fund_txid = res['tx_hash']

    new_tx = {
        'locktime': 0,
        'version': 2,
        'ins': [
            {'outpoint': {'hash': fund_txid, 'index': 0},
             'script': '',
             'witness_script': '',
             'sequence': 4294967295},
        ],
        'outs': [
            {'script': tx['outs'][0]['script'],
             'value': tx['outs'][0]['value']},
            {'script': '0014' + addrhash,
             'value': fund_outputs[0]['value'] - tx_fee * 2},
            {'script': tx['outs'][2]['script'],
             'value': tx['outs'][2]['value']}
        ]
    }

    unsigned_txhex = virtualchain.btc_tx_serialize(new_tx)
    print 'unsigned: {}'.format(unsigned_txhex)

    pk_segwit = virtualchain.make_segwit_info(payment_key)
    print json.dumps(pk_segwit, indent=4, sort_keys=True)

    signed_txhex = virtualchain.tx_sign_input(unsigned_txhex, 0, fund_outputs[0]['script'], fund_outputs[0]['value'], pk_segwit, segwit=True, scriptsig_type='p2wpkh')
    print 'signed: {}'.format(signed_txhex)

    res = testlib.broadcast_transaction(signed_txhex)
    assert 'error' not in res

    return res
Example #14
0
    def do_POST(self):
        content_type = self.headers.getheader('content-type')
        postvars = {}

        if content_type is not None:
            ctype, pdict = cgi.parse_header(content_type)
            if ctype == 'multipart/form-data':
                postvars = cgi.parse_multipart(self.rfile, pdict)
            elif ctype == 'application/x-www-form-urlencoded':
                length = int(self.headers.getheader('content-length'))
                postvars = cgi.parse_qs(self.rfile.read(length),
                                        keep_blank_values=1)

        if self.path == '/sendfunds':
            # fund an address
            addr = postvars.get('addr', [None])
            value = postvars.get('value', [None])

            if addr[0] is None or value[0] is None:
                log.error("Missing addr or value")
                self.send_response(400,
                                   "Invalid request: missing addr or value")
                self.end_headers()
                return

            try:
                value = int(value[0])
                addr = virtualchain.address_reencode(addr[0])
            except:
                log.error("Failed to read addr and/or value")
                log.error("postvars = {}".format(postvars))
                self.send_response(400, "Invalid addr or value")
                self.end_headers()
                return

            # can't take more than 0.1 BTC per address

            # send funds
            res = testlib.send_funds(
                testlib.get_default_payment_wallet().privkey, value, addr)
            if 'error' in res:
                log.error("Failed to send {} from {} to {}: {}".format(
                    value,
                    testlib.get_default_payment_wallet().privkey, addr, res))
                self.send_response(400, "Failed to send value")
                self.end_headers()
                return

            self.send_response(302)
            self.send_header('location', '/')
            self.end_headers()
            return

        else:
            log.error("Unsupported path {}".format(self.path))
            self.send_response(400, "Only support /sendfunds at this time")
            self.end_headers()
            return
def scenario(wallets, **kw):

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

    testlib.blockstack_namespace_reveal(
        "test",
        wallets[1].addr,
        52595,
        250,
        4, [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        10,
        10,
        wallets[0].privkey,
        version_bits=blockstack.NAMESPACE_VERSION_PAY_WITH_STACKS)
    testlib.next_block(**kw)

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

    balances = testlib.get_wallet_balances(wallets[2])
    assert balances[wallets[2].addr][STACKS] == 0

    # should fail--not enough stacks
    testlib.blockstack_name_preorder("foo.test",
                                     wallets[2].privkey,
                                     wallets[3].addr,
                                     safety_checks=False,
                                     expect_fail=True)
    testlib.next_block(**kw)

    name_cost = testlib.blockstack_get_name_token_cost('foo.test')
    assert name_cost['units'] == STACKS
    assert name_cost['amount'] > 0

    # send tokens and preorder multiple times in the block
    # should all succeed, BUT: force them to go in order through UTXO chaining
    for i in range(0, 5):
        name_recipient_privkey = wallets[-(i + 1)].privkey
        name_recipient_addr = virtualchain.address_reencode(
            virtualchain.get_privkey_address(name_recipient_privkey))

        testlib.blockstack_send_tokens(name_recipient_addr, "STACKS",
                                       name_cost['amount'], wallets[0].privkey)
        testlib.send_funds(wallets[0].privkey, 1000000, name_recipient_addr)
        testlib.blockstack_name_preorder("foo_{}.test".format(i),
                                         name_recipient_privkey,
                                         wallets[3].addr,
                                         safety_checks=False)
        testlib.blockstack_name_register("foo_{}.test".format(i),
                                         name_recipient_privkey,
                                         wallets[3].addr,
                                         safety_checks=False)

    testlib.next_block(**kw)
Example #16
0
def decrypt_wallet_legacy(data, key_defaults, password):
    """
    Decrypt 0.14.1 and earlier wallets, given the wallet data, the default key values,
    and the password.

    Return {'status': True, 'wallet': wallet} on success 
    Raise on error
    """
    new_wallet = {}

    # NOTE: 'owner' must come before 'data', since we may use it to generate the data key
    keynames = ['payment', 'owner', 'data']
    for keyname in keynames:

        # get the key's private key info and address
        keyname_privkey = '{}_privkey'.format(keyname)
        keyname_addresses = '{}_addresses'.format(keyname)
        encrypted_keyname = 'encrypted_{}_privkey'.format(keyname)

        if encrypted_keyname in data:
            # This key was explicitly defined in the wallet.
            # It is not guaranteed to be a child key of the
            # master private key.
            field = decrypt_private_key_info(data[encrypted_keyname], password)

            if 'error' in field:
                ret = {
                    'error':
                    "Failed to decrypt {}: {}".format(encrypted_keyname,
                                                      field['error'])
                }
                log.debug('Failed to decrypt {}: {}'.format(
                    encrypted_keyname, field['error']))
                return ret

            new_wallet[keyname_privkey] = field['private_key_info']
            new_wallet[keyname_addresses] = [field['address']]

        else:

            # Legacy migration: this key is not defined in the wallet
            # use the appopriate default key
            assert keyname in key_defaults, 'BUG: no legacy private key for {}'.format(
                keyname)

            default_privkey = key_defaults[keyname]
            new_wallet[keyname_privkey] = default_privkey
            new_wallet[keyname_addresses] = [
                virtualchain.address_reencode(
                    keylib.ECPrivateKey(
                        default_privkey,
                        compressed=False).public_key().address())
            ]

    return {'status': True, 'wallet': new_wallet}
def scenario( wallets, **kw ):
    global pk, pk2

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

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey )
    testlib.next_block( **kw )

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

    # pay for a name in a v1 namespace with Stacks
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2))

    # calculate the cost of doing so
    namespace = testlib.get_state_engine().get_namespace('test')
    stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw))
    btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw))

    print ''
    print 'price of {} in Stacks is {}'.format('foo.test', stacks_price)
    print 'price of {} in BTC is {}'.format('foo.test', btc_price)
    print ''

    testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 1, wallets[0].privkey)
    testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey)
    testlib.send_funds(wallets[0].privkey, 2*btc_price, addr)
    testlib.send_funds(wallets[0].privkey, 2*btc_price, addr2)
    testlib.next_block(**kw)

    # preorder/register using Stacks
    testlib.blockstack_name_preorder( "foo.test", pk, addr2, price={'units': 'STACKS', 'amount': stacks_price + 1})
    testlib.next_block( **kw )

    testlib.blockstack_name_register( "foo.test", pk, addr2 )
    testlib.next_block( **kw )

    # renew using Stacks
    testlib.blockstack_name_renew('foo.test', pk2, price={'units': 'STACKS', 'amount': stacks_price + 1})
    testlib.next_block(**kw)
Example #18
0
def get_addresses_from_file(config_dir=CONFIG_DIR, wallet_path=None):
    """
    Load up the set of addresses from the wallet
    Not all fields may be set in older wallets.
    """

    data_pubkey = None
    payment_address = None
    owner_address = None

    if wallet_path is None:
        wallet_path = os.path.join(config_dir, WALLET_FILENAME)

    if not os.path.exists(wallet_path):
        log.error('No such file or directory: {}'.format(wallet_path))
        return None, None, None

    with open(wallet_path, 'r') as f:
        data = f.read()

    try:
        data = json.loads(data)

        # best we can do is guarantee that this is a dict
        assert isinstance(data, dict)
    except:
        log.error('Invalid wallet data: not a JSON object (in {})'.format(
            wallet_path))
        return None, None, None

    # extract addresses
    if data.has_key('payment_addresses'):
        payment_address = virtualchain.address_reencode(
            str(data['payment_addresses'][0]))
    if data.has_key('owner_addresses'):
        owner_address = virtualchain.address_reencode(
            str(data['owner_addresses'][0]))
    if data.has_key('data_pubkeys'):
        data_pubkey = str(data['data_pubkeys'][0])

    return payment_address, owner_address, data_pubkey
Example #19
0
def make_DID(name_type, address, index):
    """
    Standard way of making a DID.
    name_type is "name" or "subdomain"
    """
    if name_type not in ['name', 'subdomain']:
        raise ValueError("Require 'name' or 'subdomain' for name_type")

    if name_type == 'name':
        address = virtualchain.address_reencode(address)
    else:
        # what's the current version byte?
        vb = keylib.b58check.b58check_version_byte(address)
        if vb == bitcoin_blockchain.version_byte:
            # singlesig
            vb = SUBDOMAIN_ADDRESS_VERSION_BYTE
        else:
            vb = SUBDOMAIN_ADDRESS_MULTISIG_VERSION_BYTE

        address = virtualchain.address_reencode(address, version_byte=vb)

    return 'did:stack:v0:{}-{}'.format(address, index)
def get_addresses_from_file(config_dir=CONFIG_DIR, wallet_path=None):
    """
    Load up the set of addresses from the wallet
    Not all fields may be set in older wallets.
    """ 

    data_pubkey = None
    payment_address = None
    owner_address = None

    if wallet_path is None:
        wallet_path = os.path.join(config_dir, WALLET_FILENAME)

    if not os.path.exists(wallet_path):
        log.error('No such file or directory: {}'.format(wallet_path))
        return None, None, None

    with open(wallet_path, 'r') as f:
        data = f.read()

    try:
        data = json.loads(data)
        
        # best we can do is guarantee that this is a dict
        assert isinstance(data, dict)
    except:
        log.error('Invalid wallet data: not a JSON object (in {})'.format(wallet_path))
        return None, None, None 
   
    # extract addresses
    if data.has_key('payment_addresses'):
        payment_address = virtualchain.address_reencode(str(data['payment_addresses'][0]))
    if data.has_key('owner_addresses'):
        owner_address = virtualchain.address_reencode(str(data['owner_addresses'][0]))
    if data.has_key('data_pubkeys'):
        data_pubkey = str(data['data_pubkeys'][0])

    return payment_address, owner_address, data_pubkey
    def get_child_address(self, index=0):
        """
        @index is the child index

        Returns:
        child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        # force decompressed...
        hex_privkey = self.get_child_privkey(index)
        hex_pubkey = get_pubkey_hex(hex_privkey)
        return virtualchain.address_reencode(keylib.public_key_to_address(hex_pubkey))
Example #22
0
    def get_child_address(self, index=0):
        """
        @index is the child index

        Returns:
        child address for given @index
        """

        if self.child_addresses is not None:
            return self.child_addresses[index]

        # force decompressed...
        hex_privkey = self.get_child_privkey(index)
        hex_pubkey = get_pubkey_hex(hex_privkey)
        return virtualchain.address_reencode(
            keylib.public_key_to_address(hex_pubkey))
def decrypt_wallet_legacy(data, key_defaults, password):
    """
    Decrypt 0.14.1 and earlier wallets, given the wallet data, the default key values,
    and the password.

    Return {'status': True, 'wallet': wallet} on success 
    Raise on error
    """
    new_wallet = {}

    # NOTE: 'owner' must come before 'data', since we may use it to generate the data key
    keynames = ['payment', 'owner', 'data']
    for keyname in keynames:

        # get the key's private key info and address
        keyname_privkey = '{}_privkey'.format(keyname)
        keyname_addresses = '{}_addresses'.format(keyname)
        encrypted_keyname = 'encrypted_{}_privkey'.format(keyname)

        if encrypted_keyname in data:
            # This key was explicitly defined in the wallet.
            # It is not guaranteed to be a child key of the
            # master private key.
            field = decrypt_private_key_info(data[encrypted_keyname], password)

            if 'error' in field:
                ret = {'error': "Failed to decrypt {}: {}".format(encrypted_keyname, field['error'])}
                log.debug('Failed to decrypt {}: {}'.format(encrypted_keyname, field['error']))
                return ret

            new_wallet[keyname_privkey] = field['private_key_info']
            new_wallet[keyname_addresses] = [field['address']]

        else:

            # Legacy migration: this key is not defined in the wallet
            # use the appopriate default key
            assert keyname in key_defaults, 'BUG: no legacy private key for {}'.format(keyname)

            default_privkey = key_defaults[keyname]
            new_wallet[keyname_privkey] = default_privkey
            new_wallet[keyname_addresses] = [
                virtualchain.address_reencode( keylib.ECPrivateKey(default_privkey, compressed=False).public_key().address() )
            ]

    return {'status': True, 'wallet': new_wallet}
def scenario( wallets, **kw ):

    # try to spend tokens to ourselves
    testlib.blockstack_send_tokens(wallets[0].addr, "STACKS", 100000, wallets[0].privkey, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw) # end of 689

    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0

    # try to spend more tokens than we have 
    testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 600001, wallets[0].privkey, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw)

    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0

    # try to spend tokens that don't exist
    testlib.blockstack_send_tokens(wallets[1].addr, "noop", 600000, wallets[0].privkey, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw)

    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0
   
    # try to spend tokens with an invalid consensus hash (note that this is epoch 4)
    testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 600000, wallets[0].privkey, consensus_hash='00' * 16, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw)
    
    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0

    # try to spend tokens from an account that doesn't exist
    pk = virtualchain.lib.ecdsalib.ecdsa_private_key().to_hex()
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    testlib.send_funds(wallets[0].privkey, 10240000, addr)

    testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 100000, pk, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw)

    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0

    # try to send 0 tokens
    testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 0, wallets[0].privkey, safety_checks=False, expect_fail=True)
    testlib.next_block(**kw)

    assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0
Example #25
0
def replace_output_with_bech32(txhex, output_index, payment_key, addrhash):
    print 'txhex: {}'.format(txhex)
    tx = virtualchain.btc_tx_deserialize(txhex)

    new_tx = {
        'locktime': 0,
        'version': 1,
        'ins': tx['ins'],
        'outs': tx['outs'],
    }

    for inp in new_tx['ins']:
        inp['script'] = ''
        inp['witness_script'] = ''

    new_tx['outs'][output_index] = {
        'script': '0014' + addrhash,
        'value': tx['outs'][output_index]['value']
    }

    unsigned_txhex = virtualchain.btc_tx_serialize(new_tx)
    print 'unsigned: {}'.format(unsigned_txhex)

    addr = virtualchain.address_reencode(
        virtualchain.get_privkey_address(payment_key))
    utxos = testlib.get_utxos(addr)
    prev_outputs = [{
        'out_script': inp['out_script'],
        'value': inp['value']
    } for inp in utxos]

    signed_txhex = virtualchain.tx_sign_all_unsigned_inputs(
        payment_key, prev_outputs, unsigned_txhex)

    print 'signed: {}'.format(signed_txhex)

    res = testlib.broadcast_transaction(signed_txhex)
    assert 'error' not in res

    return res
    def _tx_pay_btc(txhex, privk, btc_paid, burn_addr):
        tx = virtualchain.btc_tx_deserialize(txhex)

        # up the burn amount 
        btc_price = blockstack.lib.scripts.price_name('baz', namespace, testlib.get_current_block(**kw))
        tx['outs'][2]['script'] = virtualchain.btc_make_payment_script(burn_addr)
        tx['outs'][2]['value'] = btc_paid

        tx['outs'][1]['value'] -= btc_paid

        # re-sign 
        for i in tx['ins']:
            i['script'] = ''

        txhex = virtualchain.btc_tx_serialize(tx)
        _addr = virtualchain.address_reencode(virtualchain.get_privkey_address(privk))
        txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(privk, testlib.get_utxos(_addr), txhex)
        
        print txhex_signed
        res = testlib.broadcast_transaction(txhex_signed)
        assert 'error' not in res, res['error']
        return res
Example #27
0
def make_subdomain_zone_file(domain_name, subdomain_pattern,
                             subdomain_zonefile_pattern, sequence, iters,
                             target_size, seed_privkey):
    zf_template = "$ORIGIN {}\n$TTL 3600\n{}"
    private_keychain = keychain.PrivateKeychain.from_private_key(seed_privkey)

    i = 0
    txtrecs = []
    prev_zf = None

    for i in range(0, iters):

        subdomain_privkey = private_keychain.child(i).private_key()
        subdomain_addr = virtualchain.address_reencode(
            virtualchain.ecdsalib.ecdsa_private_key(
                subdomain_privkey).public_key().address())

        subdomain_name = subdomain_pattern.format(i)
        subdomain_zf = subdomain_zonefile_pattern.format(subdomain_name, i)

        i += 1

        txtrec = subdomains.make_subdomain_txt(subdomain_name, domain_name,
                                               subdomain_addr, sequence,
                                               subdomain_zf, subdomain_privkey)
        txtrecs.append(txtrec)

        zf = zf_template.format(domain_name, '\n'.join(txtrecs))
        if len(zf) > target_size:
            raise Exception(
                "Could not pack {} subdomains (at {}) into {} bytes".format(
                    iters, len(txtrecs), target_size))

        prev_zf = zf

    return prev_zf
Example #28
0
def check( state_engine ):

    all_tests = {}

    # namespace preorder wire format
    # 0     2   3                                      23               39
    # |-----|---|--------------------------------------|----------------|
    #  magic op  hash(ns_id,script_pubkey,reveal_addr)   consensus hash  

    namespace_preorders = {
        "valid":     "%s%s" % ("11" * 20, "22" * 16),
        "too_short": "%s%s" % ("11" * 20, "33" * 15),
        "too_long":  "%s%s00" % ("11" * 20, "22" * 16),
    }

    all_tests["*"] = compile_test( "*", namespace_preorders )

    # namespace reveal wire format
    # 0     2   3        7     8     9    10   11   12   13   14    15    16    17       18        20                        39
    # |-----|---|--------|-----|-----|----|----|----|----|----|-----|-----|-----|--------|----------|-------------------------|
    # magic  op  life    coeff. base 1-2  3-4  5-6  7-8  9-10 11-12 13-14 15-16  nonalpha  version   namespace ID
    #                                                   bucket exponents         no-vowel
    #                                                                            discounts
   
    namespace_reveals = {
        "valid":      "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("hello")),
        "non-b38":    "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("Hello")),
        "period2":    "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("He.l.lo")),
        "period":     "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify(".")),
        "no-plus":    "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("hel+lo")),
        "null_name":  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("")),
        "too_long":   "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ("11111111", "02", "03", "40", "41", "42", "43", "44", "45", "46", "47", "15", "0001", binascii.hexlify("hellohellohellohello"))
    }

    all_tests["&"] = compile_test( "&", namespace_reveals )

    # namespace ready wire format 
    # 0     2  3  4           23
    # |-----|--|--|------------|
    # magic op  .  ns_id

    namespace_readys = {
        "valid":     binascii.hexlify(".hello"),
        "non-b38":   binascii.hexlify(".Hello"),
        "period":    binascii.hexlify("."),
        "period2":   binascii.hexlify(".hel.lo"),
        "no-plus":   binascii.hexlify(".hel+lo"),
        "null_name": binascii.hexlify(""),
        "no-period": binascii.hexlify("hello"),
        "too_long":  binascii.hexlify(".hellohellohellohello")
    }

    all_tests["!"] = compile_test( "!", namespace_readys )

    # name preorder wire format
    # 0     2  3                                              23             39
    # |-----|--|----------------------------------------------|--------------|
    # magic op  hash(name.ns_id,script_pubkey,register_addr)   consensus hash

    name_preorders = {
        "valid":     "%s%s" % ("11" * 20, "22" * 16),
        "too_short": "%s%s" % ("11" * 20, "33" * 15),
        "too_long":  "%s%s00" % ("11" * 20, "22" * 16),
    }

    all_tests["?"] = compile_test( "?", name_preorders )

    # name register/renew wire format (pre F-day 2017) 
    # 0    2  3                             39
    # |----|--|-----------------------------|
    # magic op   name.ns_id (37 bytes)

    # name register/renew wire format (post F-day 2017)
    # 0    2  3                                  39                  59
    # |----|--|----------------------------------|-------------------|
    # magic op   name.ns_id (37 bytes, 0-padded)       value hash
    
    name_registrations = {
        "valid":     binascii.hexlify("hello.test"),
        "null_name": binascii.hexlify(""),
        "non-b38":   binascii.hexlify("Hello.test"),
        "no-namespace": binascii.hexlify("hello"),
        "null-namespace": binascii.hexlify("hello."),
        "2period":   binascii.hexlify("hello.tes.t"),
        "no-plus":   binascii.hexlify("hel+lo.test"),
        "too-long":  binascii.hexlify("hellohellohellohellohellohellohel.test"),

        "valid_2":        binascii.hexlify("hello.test" + "\x00" * 27 + "\x11" * 20),
        "null_name_2":    binascii.hexlify("\x00" * 37 + "\x11" * 20),
        "non-b38_2":      binascii.hexlify("Hello.test" + "\x00" * 27 + "\x11" * 20),
        "no-namespace_2": binascii.hexlify("hello" + "\x00" * 32 + "\x11" * 20),
        "null-namespace_2":  binascii.hexlify("hello." + "\x00" * 31 + "\x11" * 20),
        "2period_2":      binascii.hexlify("hello.tes.t" + "\x00" * 26 + "\x11" * 20),
        "no-plus_2":      binascii.hexlify("hel+lo.test" + "\x00" * 26 + "\x11" * 20),
        "too-long_2":     binascii.hexlify("hellohellohellohellohellohellohel.test" + "\x11" * 20),
        "no_hash":      binascii.hexlify("hello.test" + "\x00" * 27),
        "hash_too_long": binascii.hexlify("hello.test" + "\x00" * 27 + "\x11" * 21),
        "padding_too_short": binascii.hexlify("hello.test" + "\x00" * 26 + "\x11" * 21),
        "op_too_short": binascii.hexlify("hello.test" + "\x00" * 26 + "\x11" * 20),
    }

    all_tests[":"] = compile_test( ":", name_registrations )

    # name update wire format
    #  0     2  3                                   19                      39
    # |-----|--|-----------------------------------|-----------------------|
    # magic op  hash128(name.ns_id,consensus hash) hash160(data)
        
    name_updates = {
        "valid":        "%s%s" % ("11" * 16, "22" * 20),
        "too_short":    "%s%s" % ("11" * 16, "33" * 19),
        "too_long":     "%s%s00" % ("11" * 16, "22" * 20),
    }

    all_tests["+"] = compile_test( "+", name_updates )

    # name transfer wire format 
    # 0     2  3    4                   20              36
    # |-----|--|----|-------------------|---------------|
    # magic op keep  hash128(name.ns_id) consensus hash
    #          data?

    name_transfers = {
        "valid":        "%s%s%s" % (binascii.hexlify(">"), "11" * 16, "22" * 16),
        "valid2":        "%s%s%s" % (binascii.hexlify("~"), "11" * 16, "22" * 16),
        "too_short":    "%s%s%s" % (binascii.hexlify(">"), "11" * 16, "33" * 15),
        "too_long":     "%s%s%s00" % (binascii.hexlify(">"), "11" * 16, "22" * 16),
        "too_short2":    "%s%s%s" % (binascii.hexlify("~"), "11" * 16, "33" * 15),
        "too_long2":     "%s%s%s00" % (binascii.hexlify("~"), "11" * 16, "22" * 16),
        "invalid-opcode": "%s%s%s" % (binascii.hexlify("!"), "11" * 16, "22" * 16)
    }

    all_tests[">"] = compile_test( ">", name_transfers )

    # name revoke wire format
    # 0    2  3                             39
    # |----|--|-----------------------------|
    # magic op   name.ns_id (37 bytes)
    
    name_revokes = {
        "valid":     binascii.hexlify("hello.test"),
        "null_name": binascii.hexlify(""),
        "non-b38":   binascii.hexlify("Hello.test"),
        "no-namespace": binascii.hexlify("hello"),
        "null-namespace": binascii.hexlify("hello."),
        "2period":   binascii.hexlify("hello.tes.t"),
        "no-plus":   binascii.hexlify("hel+lo.test"),
        "too-long":  binascii.hexlify("hellohellohellohellohellohellohel.test")
    }

    all_tests["~"] = compile_test( "~", name_revokes )

    # name import wire format
    # 0    2  3                             39
    # |----|--|-----------------------------|
    # magic op   name.ns_id (37 bytes)
    
    name_imports = {
        "valid":     binascii.hexlify("hello.test"),
        "null_name": binascii.hexlify(""),
        "non-b38":   binascii.hexlify("Hello.test"),
        "no-namespace": binascii.hexlify("hello"),
        "null-namespace": binascii.hexlify("hello."),
        "2period":   binascii.hexlify("hello.tes.t"),
        "no-plus":   binascii.hexlify("hel+lo.test"),
        "too-long":  binascii.hexlify("hellohellohellohellohellohellohel.test")
    }
    
    all_tests[";"] = compile_test( ";", name_imports )

    # announce wire format 
    # 0    2  3                             23
    # |----|--|-----------------------------|
    # magic op   message hash (160-bit)
    
    announces = {
        "valid":    "11" * 20,
        "too-short": "11" * 19,
        "too-long": "11" * 21
    }

    all_tests["#"] = compile_test( "#", announces )

    fake_pubkey = wallets[0].pubkey_hex
    fake_sender = virtualchain.make_payment_script( wallets[0].addr )
    fake_recipient = virtualchain.make_payment_script( wallets[1].addr )
    fake_recipient_address = wallets[1].addr
    fake_import_update_hash = "44" * 20
    fake_burn_address = virtualchain.address_reencode('1111111111111111111114oLvT2')
    fake_reveal_address = fake_burn_address

    # only 'valid' tests should return non-NULL
    # all other tests should return None
    for opcode, tests in all_tests.items():
        print "test %s" % opcode
        for testname, testscript in tests.items():
          
            bin_testscript = binascii.unhexlify(testscript)[3:]
            print 'script: {}'.format(bin_testscript)

            burn_addr = None
            reveal_addr = None
            import_hash = None

            if opcode in ['*', '?']:
                burn_addr = fake_burn_address

            elif opcode == ';':
                import_hash = fake_import_update_hash

            elif opcode == '&':
                reveal_addr = fake_reveal_address

            parsed_op = parse_nameop( opcode, bin_testscript, fake_pubkey, \
                    recipient=fake_recipient, recipient_address=fake_recipient_address, import_update_hash=import_hash, burn_address=burn_addr, reveal_address=reveal_addr )

            if testname.startswith("valid"):
                # should work
                if parsed_op is None:
                    print >> sys.stderr, "Failed to parse %s id%s%s (%s)" % (testname, opcode, bin_testscript, binascii.hexlify(bin_testscript))
                    return False 

            else:
                # should fail
                if parsed_op is not None:
                    print >> sys.stderr, "Parsed invalid test '%s' (id%s%s)" % (testname, opcode, bin_testscript)
                    return False

    return True
def scenario(wallets, **kw):

    global wallet_keys, wallet_keys_2, error, index_file_data, resource_data

    wallet_keys = testlib.ysi_client_initialize_wallet("0123456789abcdef",
                                                       wallets[5].privkey,
                                                       wallets[3].privkey,
                                                       wallets[4].privkey)
    test_proxy = testlib.TestAPIProxy()
    ysi_client.set_default_proxy(test_proxy)

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

    testlib.ysi_namespace_reveal(
        "test", wallets[1].addr, 52595, 250, 4,
        [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 10, 10,
        wallets[0].privkey)
    testlib.next_block(**kw)

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

    testlib.ysi_name_preorder("foo.test", wallets[2].privkey, wallets[3].addr)
    testlib.next_block(**kw)

    testlib.ysi_name_register("foo.test", wallets[2].privkey, wallets[3].addr)
    testlib.next_block(**kw)

    # migrate profiles
    res = testlib.migrate_profile("foo.test",
                                  proxy=test_proxy,
                                  wallet_keys=wallet_keys)
    if 'error' in res:
        res['test'] = 'Failed to initialize foo.test profile'
        print json.dumps(res, indent=4, sort_keys=True)
        error = True
        return

    # tell serialization-checker that value_hash can be ignored here
    print "BLOCKSTACK_SERIALIZATION_CHECK_IGNORE value_hash"
    sys.stdout.flush()

    testlib.next_block(**kw)

    data_pk = wallets[-1].privkey
    data_pub = wallets[-1].pubkey_hex

    config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG", None)

    # make a session
    datastore_pk = keylib.ECPrivateKey(wallets[-1].privkey).to_hex()
    res = testlib.ysi_cli_app_signin(
        "foo.test", datastore_pk, 'register.app', [
            'names', 'register', 'transfer', 'prices', 'zonefiles',
            'blockchain', 'node_read'
        ])
    if 'error' in res:
        print json.dumps(res, indent=4, sort_keys=True)
        error = True
        return

    ses = res['token']

    # register the name bar.test
    res = testlib.ysi_REST_call('POST',
                                '/v1/names',
                                ses,
                                data={'name': 'bar.test'})
    if 'error' in res:
        res['test'] = 'Failed to register user'
        print json.dumps(res)
        error = True
        return False

    print res
    tx_hash = res['response']['transaction_hash']

    # wait for preorder to get confirmed...
    for i in xrange(0, 6):
        testlib.next_block(**kw)

    res = testlib.verify_in_queue(ses, 'bar.test', 'preorder', tx_hash)
    if not res:
        return False

    # wait for the preorder to get confirmed
    for i in xrange(0, 4):
        testlib.next_block(**kw)

    # wait for register to go through
    print 'Wait for register to be submitted'
    time.sleep(10)

    # wait for the register to get confirmed
    for i in xrange(0, 6):
        testlib.next_block(**kw)

    res = testlib.verify_in_queue(ses, 'bar.test', 'register', None)
    if not res:
        return False

    for i in xrange(0, 4):
        testlib.next_block(**kw)

    print 'Wait for update to be submitted'
    time.sleep(10)

    # wait for update to get confirmed
    for i in xrange(0, 6):
        testlib.next_block(**kw)

    res = testlib.verify_in_queue(ses, 'bar.test', 'update', None)
    if not res:
        return False

    for i in xrange(0, 4):
        testlib.next_block(**kw)

    print 'Wait for update to be confirmed'
    time.sleep(10)

    res = testlib.ysi_REST_call("GET", "/v1/names/bar.test", ses)
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to get name bar.test'
        print json.dumps(res)
        return False

    zonefile_hash = res['response']['zonefile_hash']

    # transfer it
    res = testlib.ysi_REST_call("PUT",
                                "/v1/names/bar.test/owner",
                                ses,
                                data={'owner': wallets[7].addr})
    if 'error' in res or res['http_status'] != 202:
        res['test'] = 'Failed to transfer name'
        print json.dumps(res)
        return False

    # wait for transfer to get confirmed
    for i in xrange(0, 6):
        testlib.next_block(**kw)

    res = testlib.verify_in_queue(ses, 'bar.test', 'transfer', None)
    if not res:
        return False

    for i in xrange(0, 4):
        testlib.next_block(**kw)

    print 'waiting for transfer to get confirmed'
    time.sleep(10)

    # poll
    res = testlib.ysi_REST_call("GET", "/v1/names/bar.test", ses)
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to query name'
        print json.dumps(res)
        error = True
        return False

    # new owner?
    if not res['response'].has_key('address'):
        res['test'] = 'No address given'
        print json.dumps(res)
        return False

    address_testnet = virtualchain.address_reencode(str(
        res['response']['address']),
                                                    network='testnet')
    if address_testnet != wallets[7].addr:
        res['test'] = 'Failed to transfer name to new address {}'.format(
            wallets[7].addr)
        res['owner_address_testnet'] = address_testnet
        print json.dumps(res)
        return False

    # do we have the history for the name?
    res = testlib.ysi_REST_call("GET", "/v1/names/bar.test/history", ses)
    if 'error' in res or res['http_status'] != 200:
        res['test'] = "Failed to get name history for foo.test"
        print json.dumps(res)
        return False

    # valid history?
    hist = res['response']
    if len(hist.keys()) != 4:
        res['test'] = 'Failed to get update history'
        res['history'] = hist
        print json.dumps(res, indent=4, sort_keys=True)
        return False

    # get the zonefile
    res = testlib.ysi_REST_call(
        "GET", "/v1/names/bar.test/zonefile/{}".format(zonefile_hash), ses)
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to get name zonefile'
        print json.dumps(res)
        return False
def scenario(wallets, **kw):
    global pk

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

    # all names below are the same price
    testlib.blockstack_namespace_reveal(
        "test",
        wallets[1].addr,
        52595,
        250,
        4, [6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        1,
        1,
        wallets[0].privkey,
        version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR)
    testlib.next_block(**kw)

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

    # pay for a name in a v1 namespace with Stacks
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))

    # calculate the cost of doing so
    namespace = testlib.get_state_engine().get_namespace('test')
    stacks_price = blockstack.lib.scripts.price_name_stacks(
        'foo', namespace, testlib.get_current_block(**kw))
    btc_price = blockstack.lib.scripts.price_name(
        'foo', namespace, testlib.get_current_block(**kw))

    print ''
    print 'price of {} in Stacks is {}'.format('foo.test', stacks_price)
    print ''

    testlib.blockstack_send_tokens(addr, "STACKS", stacks_price * 5,
                                   wallets[0].privkey)
    testlib.send_funds(wallets[0].privkey, btc_price * 10, addr)
    testlib.next_block(**kw)

    # preorder/register using Stacks (preorders should fail since we're using the wrong burn address for tokens)
    testlib.blockstack_name_preorder("foo.test",
                                     pk,
                                     wallets[3].addr,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price
                                     },
                                     burn_addr=wallets[0].addr,
                                     safety_checks=False,
                                     expect_fail=True)
    testlib.blockstack_name_preorder("bar.test",
                                     pk,
                                     wallets[3].addr,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price - 1
                                     },
                                     burn_addr=wallets[0].addr,
                                     safety_checks=False,
                                     expect_fail=True)
    testlib.next_block(**kw)

    op_info = virtualchain.lib.indexer.StateEngine.get_block_statistics(
        testlib.get_current_block(**kw))
    if op_info['num_processed_ops'] > 0:
        print 'handled ops this block'
        print op_info
        return False

    # preorder/register using Stacks (preorders should be accepted, but bar2 will fail to register)
    testlib.blockstack_name_preorder(
        "foo2.test",
        pk,
        wallets[2].addr,
        price={
            'units': 'STACKS',
            'amount': stacks_price
        },
        burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS,
        safety_checks=False)
    testlib.blockstack_name_preorder(
        "bar2.test",
        pk,
        wallets[2].addr,
        price={
            'units': 'STACKS',
            'amount': stacks_price - 1
        },
        burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS,
        safety_checks=False)
    testlib.next_block(**kw)

    # preorder/register using Stacks (preorders should succeed now, but bar3.test will fail to register since we're not paying enough stacks)
    testlib.blockstack_name_register(
        "bar2.test", pk, wallets[2].addr
    )  # should fail at this point, since the preorder sent to the wrong burn address
    testlib.blockstack_name_register(
        "foo2.test", pk, wallets[2].addr
    )  # should fail at this point, since the preorder sent to the wrong burn address

    testlib.blockstack_name_preorder(
        "foo3.test",
        pk,
        wallets[2].addr,
        price={
            'units': 'STACKS',
            'amount': stacks_price
        },
        burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS,
        safety_checks=False)
    testlib.blockstack_name_preorder(
        "bar3.test",
        pk,
        wallets[2].addr,
        price={
            'units': 'STACKS',
            'amount': stacks_price - 1
        },
        burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS,
        safety_checks=False)
    testlib.next_block(**kw)
    testlib.expect_snv_fail_at('bar2.test', testlib.get_current_block(**kw))
    testlib.expect_snv_fail_at('foo2.test', testlib.get_current_block(**kw))

    # tokens are not yet accepted
    if testlib.get_state_engine().get_name('bar2.test'):
        print 'registered bar2.test'
        return False

    if testlib.get_state_engine().get_name('foo2.test'):
        print 'registered foo2.test'
        return False

    # preorder/register using Stacks (should succeed without safety checks or overrides)
    testlib.blockstack_name_preorder("foo.test",
                                     pk,
                                     wallets[2].addr,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price
                                     })
    testlib.next_block(**kw)

    testlib.blockstack_name_register("foo.test", pk,
                                     wallets[2].addr)  # should succeed
    testlib.blockstack_name_register("bar2.test", pk,
                                     wallets[2].addr)  # should fail
    testlib.blockstack_name_register("foo2.test", pk,
                                     wallets[2].addr)  # should succeed
    testlib.blockstack_name_register("bar3.test", pk,
                                     wallets[2].addr)  # should fail
    testlib.blockstack_name_register("foo3.test", pk,
                                     wallets[2].addr)  # should succeed
    testlib.next_block(**kw)
    testlib.expect_snv_fail_at('bar2.test', testlib.get_current_block(**kw))
    testlib.expect_snv_fail_at('bar3.test', testlib.get_current_block(**kw))

    if testlib.get_state_engine().get_name('bar2.test'):
        print 'registered bar2.test'
        return False

    if testlib.get_state_engine().get_name('bar3.test'):
        print 'registered bar3.test'
        return False
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

    if ns['version'] != blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR:
        print 'wrong version'
        return False

    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))

    for name in [
            'foo.test', 'bar2.test', 'foo2.test', 'bar3.test', 'foo3.test'
    ]:
        # not preordered, unless bar2.test
        stacks_price = blockstack.lib.scripts.price_name_stacks(
            name.split('.')[0], ns, state_engine.lastblock)
        preorder = state_engine.get_name_preorder(
            name, virtualchain.make_payment_script(addr), wallets[2].addr)
        if name == 'bar2.test' or name == 'bar3.test':
            if preorder is None:
                print 'missing {} preorder'.format(name)
                return False

            if preorder['token_fee'] != stacks_price - 1:
                print 'wrong token fee for {}'.format(name)
                return False

            if preorder['op_fee'] > 5500:
                print 'paid too much btc'
                return False

            continue

        else:
            if preorder is not None:
                print "preorder exists for {}".format(name)
                return False

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

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

        # paid with Stacks in all 3 cases, and not Bitcoin
        if name_rec['token_fee'] != stacks_price:
            print 'paid wrong token fee'
            print 'expected {} ({}), got {} ({})'.format(
                stacks_price, type(stacks_price), name_rec['token_fee'],
                type(name_rec['token_fee']))
            return False

        if name_rec['op_fee'] > 5500:  # dust minimum
            print 'paid in BTC ({})'.format(name_rec['op_fee'])
            return False

    return True
def scenario(wallets, **kw):

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

    testlib.blockstack_namespace_reveal(
        "test", wallets[1].addr, 52595, 250, 4,
        [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 10, 10,
        wallets[0].privkey)
    testlib.next_block(**kw)

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

    testlib.blockstack_name_preorder("foo1.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo2.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo3.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo4.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo5.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo6.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_preorder("foo7.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.next_block(**kw)

    zf_template = "$ORIGIN {}\n$TTL 3600\n{}"
    zf_default_url = '_https._tcp URI 10 1 "https://raw.githubusercontent.com/nobody/content/profile.md"'

    zonefiles = {
        'foo1.test':
        zf_template.format(
            'foo1.test',
            subdomains.make_subdomain_txt(
                'bar.foo1.test', 'foo1.test', wallets[4].addr, 0,
                zf_template.format('bar.foo1.test', zf_default_url),
                wallets[4].privkey)),
        'foo2.test':
        zf_template.format(
            'foo2.test',
            subdomains.make_subdomain_txt(
                'bar.foo2.test', 'foo2.test', wallets[4].addr, 0,
                zf_template.format('bar.foo2.test', zf_default_url),
                wallets[4].privkey)),
        'foo3.test':
        zf_template.format(
            'foo3.test',
            subdomains.make_subdomain_txt(
                'bar.foo3.test', 'foo3.test', wallets[4].addr, 0,
                zf_template.format('bar.foo3.test', zf_default_url),
                wallets[4].privkey)),
    }

    testlib.blockstack_name_register(
        "foo1.test",
        wallets[2].privkey,
        wallets[3].addr,
        zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo1.test']))
    testlib.blockstack_name_register(
        "foo2.test",
        wallets[2].privkey,
        wallets[3].addr,
        zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo2.test']))
    testlib.blockstack_name_register(
        "foo3.test",
        wallets[2].privkey,
        wallets[3].addr,
        zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo3.test']))
    testlib.blockstack_name_register("foo4.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_register("foo5.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_register("foo6.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.blockstack_name_register("foo7.test", wallets[2].privkey,
                                     wallets[3].addr)
    testlib.next_block(**kw)

    assert testlib.blockstack_put_zonefile(zonefiles['foo1.test'])
    assert testlib.blockstack_put_zonefile(zonefiles['foo2.test'])
    assert testlib.blockstack_put_zonefile(zonefiles['foo3.test'])

    # kick off indexing and check
    testlib.next_block(**kw)

    def _query_subdomains(subdomain_names, expected_sequence, expected_owner,
                          expect_pending):
        # query each subdomain.  Should get the latest
        proxy = testlib.make_proxy()
        for fqn in subdomain_names:
            res = client.get_name_record(fqn, proxy=proxy)
            if 'error' in res:
                print res
                print 'failed to query {}'.format(fqn)
                return False

            # should have right sequence
            if res['sequence'] != expected_sequence:
                print 'wrong sequence; expected {}'.format(expected_sequence)
                print res
                return False

            # should have right owner
            if res['address'] != expected_owner:
                print 'wrong owner'
                print 'expected {}'.format(res['address'])
                print res
                return False

            # do we expect pending?
            if res['pending'] != expect_pending:
                print 'wrong pending (expected {})'.format(expect_pending)
                print res
                return False

        return True

    assert _query_subdomains(
        ['bar.foo1.test', 'bar.foo2.test', 'bar.foo3.test'], 0,
        wallets[4].addr, False)

    expected_owners_before = [wallets[4].addr]
    expected_owners_after = [wallets[4].addr]

    # update and transfer, but if i % 2 == 0, transfer to a different address
    # use a different domain name in each case.
    # verify that only transfers on the creator domain are valid.
    wallet_schedule = [
        (4, 0),  # not broadcast initially 
        (4, 1),
        (0, 1),  # not broadcast initially
        (1, 2),
    ]
    sequence_schedule = [
        1,
        1,
        2,
        2,
    ]

    expected_zf_default_url = '_https._tcp URI 10 1 "https://test.com/?index={}"'.format(
        4)
    expect_pending = False
    expect_sequence = 0
    expect_owner = wallets[4].addr

    unsent_zonefiles = []

    # send updates too, and transfer subdomains
    for i in range(0, 4):
        zf_template = "$ORIGIN {}\n$TTL 3600\n{}"
        zf_default_url = '_https._tcp URI 10 1 "https://test.com/?index={}"'.format(
            i + 1)

        names = [
            'foo1.test',
            'foo2.test',
            'foo3.test',
        ]

        k = wallet_schedule[i][0]
        k2 = wallet_schedule[i][1]
        s = sequence_schedule[i]

        zonefiles = {
            'foo1.test':
            zf_template.format(
                names[0],
                subdomains.make_subdomain_txt(
                    'bar.foo1.test', names[0], wallets[k2].addr, s,
                    zf_template.format('bar.foo1.test', zf_default_url),
                    wallets[k].privkey)),
            'foo2.test':
            zf_template.format(
                names[1],
                subdomains.make_subdomain_txt(
                    'bar.foo2.test', names[1], wallets[k2].addr, s,
                    zf_template.format('bar.foo2.test', zf_default_url),
                    wallets[k].privkey)),
            'foo3.test':
            zf_template.format(
                names[2],
                subdomains.make_subdomain_txt(
                    'bar.foo3.test', names[2], wallets[k2].addr, s,
                    zf_template.format('bar.foo3.test', zf_default_url),
                    wallets[k].privkey)),
        }

        testlib.blockstack_name_update(
            names[0], storage.get_zonefile_data_hash(zonefiles['foo1.test']),
            wallets[3].privkey)
        testlib.blockstack_name_update(
            names[1], storage.get_zonefile_data_hash(zonefiles['foo2.test']),
            wallets[3].privkey)
        testlib.blockstack_name_update(
            names[2], storage.get_zonefile_data_hash(zonefiles['foo3.test']),
            wallets[3].privkey)
        testlib.next_block(**kw)

        if i % 2 == 1:
            # only broadcast periodically
            assert testlib.blockstack_put_zonefile(zonefiles['foo1.test'])
            assert testlib.blockstack_put_zonefile(zonefiles['foo2.test'])
            assert testlib.blockstack_put_zonefile(zonefiles['foo3.test'])
            expect_owner = wallets[k2].addr
            expect_sequence += 1
            expected_owners_before.append(expect_owner)

        else:
            expect_pending = True
            unsent_zonefiles.append(zonefiles)
            expected_owners_after.append(wallets[k2].addr)

        # kick off subdomain indexing
        testlib.next_block(**kw)

        # verify history
        assert _query_subdomains(
            ['bar.foo1.test', 'bar.foo2.test', 'bar.foo3.test'],
            expect_sequence, expect_owner, expect_pending)

    # query subdomain history
    proxy = testlib.make_proxy()
    for subd in ['bar.foo1.test', 'bar.foo2.test', 'bar.foo3.test']:
        res = client.get_name_record(subd, include_history=True, proxy=proxy)
        if 'error' in res:
            print res
            return False

        if not res['pending']:
            print 'not pending, but it should be'
            print res
            return False

        # should be at 2
        if res['sequence'] != 2:
            print 'wrong sequence'
            print res
            return False

        if virtualchain.address_reencode(str(
                res['address'])) != virtualchain.address_reencode(
                    expect_owner):
            print 'wrong owner'
            print res
            return False

        for i, block_height in enumerate(sorted(res['history'])):
            if virtualchain.address_reencode(
                    str(res['history'][block_height][0]['address'])
            ) != virtualchain.address_reencode(expected_owners_before[i]):
                print 'wrong owner at {}: expected {}'.format(
                    block_height, expected_owners_before[i])
                print json.dumps(res, indent=4, sort_keys=True)
                print expected_owners_before
                return False

            if res['history'][block_height][0]['sequence'] != i:
                print 'wrong sequence at {}: expected {}'.format(
                    block_height, i)
                print json.dumps(res, indent=4, sort_keys=True)
                return False

    # send all missing subdomains.
    # should cause a cascading owner reorg.
    for zfbatch in unsent_zonefiles:
        for k in zfbatch:
            assert testlib.blockstack_put_zonefile(zfbatch[k])

    testlib.next_block(**kw)

    # query subdomain history again.  pending and owner should change
    proxy = testlib.make_proxy()
    for subd in ['bar.foo1.test', 'bar.foo2.test', 'bar.foo3.test']:
        res = client.get_name_record(subd, include_history=True, proxy=proxy)
        if 'error' in res:
            print res
            return False

        if res['pending']:
            print 'pending, but it should not be'
            print res
            return False

        if res['sequence'] != 2:
            print 'wrong sequence'
            print res
            return False

        if virtualchain.address_reencode(str(
                res['address'])) != virtualchain.address_reencode(
                    wallets[1].addr):
            print 'wrong owner again'
            print res
            return False

        for i, block_height in enumerate(sorted(res['history'])):
            if virtualchain.address_reencode(
                    str(res['history'][block_height][0]['address'])
            ) != virtualchain.address_reencode(str(expected_owners_after[i])):
                print 'wrong owner at {}: expected {}'.format(
                    block_height, expected_owners_after[i])
                print json.dumps(res, indent=4, sort_keys=True)
                print expected_owners_after
                print expected_owners_before
                print[wallets[i].addr for i in range(0, len(wallets))]
                return False

            if res['history'][block_height][0]['sequence'] != i:
                print 'wrong sequence at {}: expected {}'.format(
                    block_height, i)
                print json.dumps(res, indent=4, sort_keys=True)
                return False

    # reindex
    assert testlib.check_subdomain_db(**kw)
Example #33
0
def scenario(wallets, **kw):
    global pk, pk2

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

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

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

    # pay for a name in a v1 namespace with Stacks
    addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk))
    addr2 = virtualchain.address_reencode(
        virtualchain.get_privkey_address(pk2))

    # calculate the cost of doing so
    namespace = testlib.get_state_engine().get_namespace('test')
    stacks_price = blockstack.lib.scripts.price_name_stacks(
        'baz', namespace, testlib.get_current_block(**kw))
    btc_price = blockstack.lib.scripts.price_name(
        'baz', namespace, testlib.get_current_block(**kw))

    print ''
    print 'price of {} in Stacks is {}'.format('baz.test', stacks_price)
    print 'price of {} in BTC is {}'.format('baz.test', btc_price)
    print ''

    testlib.blockstack_send_tokens(addr, "STACKS", stacks_price,
                                   wallets[0].privkey)
    testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price * 2,
                                   wallets[0].privkey)
    testlib.send_funds(wallets[0].privkey, 10 * btc_price, addr)
    testlib.send_funds(wallets[0].privkey, 10 * btc_price, addr2)
    testlib.next_block(**kw)

    # preorder/register using Stacks
    testlib.blockstack_name_preorder("baz.test",
                                     wallets[2].privkey,
                                     addr2,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price
                                     })
    testlib.blockstack_name_preorder("goo.test",
                                     wallets[2].privkey,
                                     addr2,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price
                                     })
    testlib.blockstack_name_preorder("nop.test",
                                     wallets[2].privkey,
                                     addr2,
                                     price={
                                         'units': 'STACKS',
                                         'amount': stacks_price
                                     })
    testlib.next_block(**kw)

    testlib.blockstack_name_register("baz.test", wallets[2].privkey, addr2)
    testlib.blockstack_name_register("goo.test", wallets[2].privkey, addr2)
    testlib.blockstack_name_register("nop.test", wallets[2].privkey, addr2)
    testlib.next_block(**kw)

    balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS']

    # pay with both Stacks and Bitcoin
    # should favor Stacks payment over Bitcoin payment if we pay enough stacks.
    # Stacks should have been burned, as well as BTC.
    res = testlib.blockstack_name_renew('baz.test',
                                        pk2,
                                        price={
                                            'units': 'STACKS',
                                            'amount': stacks_price
                                        },
                                        tx_only=True,
                                        expect_success=True)
    txhex = res['transaction']
    tx = virtualchain.btc_tx_deserialize(txhex)

    # up the burn amount
    btc_price = blockstack.lib.scripts.price_name(
        'baz', namespace, testlib.get_current_block(**kw))
    tx['outs'][3]['script'] = virtualchain.btc_make_payment_script(
        blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS)
    tx['outs'][3]['value'] = btc_price

    tx['outs'][4]['value'] -= btc_price

    # re-sign
    for i in tx['ins']:
        i['script'] = ''

    txhex = virtualchain.btc_tx_serialize(tx)
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        pk2, testlib.get_utxos(addr2), txhex)

    # re-sign the last output with the payment key
    tx_signed = virtualchain.btc_tx_deserialize(txhex_signed)
    tx_signed['ins'][-1]['script'] = ''
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        testlib.get_default_payment_wallet().privkey,
        testlib.get_utxos(testlib.get_default_payment_wallet().addr),
        virtualchain.btc_tx_serialize(tx_signed))

    print txhex_signed

    res = testlib.broadcast_transaction(txhex_signed)
    if 'error' in res:
        print res
        return False

    testlib.next_block(**kw)

    # should have paid in Stacks
    balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS']
    if balance_after != balance_before - stacks_price:
        print 'baz.test cost {}'.format(balance_before - balance_after)
        return False

    balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS']

    # try to renew a name where we pay not enough stacks, but enough bitcoin.
    # should be rejected.
    res = testlib.blockstack_name_renew('goo.test',
                                        pk2,
                                        price={
                                            'units': 'STACKS',
                                            'amount': stacks_price - 1
                                        },
                                        tx_only=True)
    txhex = res['transaction']
    tx = virtualchain.btc_tx_deserialize(txhex)

    # up the burn amount to the name price
    btc_price = blockstack.lib.scripts.price_name(
        'goo', namespace, testlib.get_current_block(**kw))
    tx['outs'][3]['script'] = virtualchain.btc_make_payment_script(
        blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS)
    tx['outs'][3]['value'] = btc_price

    tx['outs'][4]['value'] -= btc_price

    # re-sign
    for i in tx['ins']:
        i['script'] = ''

    txhex = virtualchain.btc_tx_serialize(tx)
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        pk2, testlib.get_utxos(addr2), txhex)

    # re-sign the last output with the payment key
    tx_signed = virtualchain.btc_tx_deserialize(txhex_signed)
    tx_signed['ins'][-1]['script'] = ''
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        testlib.get_default_payment_wallet().privkey,
        testlib.get_utxos(testlib.get_default_payment_wallet().addr),
        virtualchain.btc_tx_serialize(tx_signed))

    print txhex_signed

    res = testlib.broadcast_transaction(txhex_signed)
    if 'error' in res:
        print res
        return False

    testlib.next_block(**kw)

    # should NOT have paid in Stacks
    balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS']
    if balance_after != balance_before:
        print 'goo.test paid {}'.format(balance_before - balance_after)
        return False

    balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS']

    # underpay in both Stacks and Bitcoin.
    # only bitcoin will be burned; transaction will not be processed
    res = testlib.blockstack_name_renew('nop.test',
                                        pk2,
                                        price={
                                            'units': 'STACKS',
                                            'amount': stacks_price - 1
                                        },
                                        tx_only=True)
    txhex = res['transaction']
    tx = virtualchain.btc_tx_deserialize(txhex)

    # up the burn amount to the name price
    btc_price = blockstack.lib.scripts.price_name(
        'goo', namespace, testlib.get_current_block(**kw))
    tx['outs'][3]['script'] = virtualchain.btc_make_payment_script(
        blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS)
    tx['outs'][3]['value'] = btc_price - 1

    tx['outs'][4]['value'] -= btc_price + 1

    # re-sign
    for i in tx['ins']:
        i['script'] = ''

    txhex = virtualchain.btc_tx_serialize(tx)
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        pk2, testlib.get_utxos(addr2), txhex)

    # re-sign the last output with the payment key
    tx_signed = virtualchain.btc_tx_deserialize(txhex_signed)
    tx_signed['ins'][-1]['script'] = ''
    txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(
        testlib.get_default_payment_wallet().privkey,
        testlib.get_utxos(testlib.get_default_payment_wallet().addr),
        virtualchain.btc_tx_serialize(tx_signed))

    print txhex_signed

    res = testlib.broadcast_transaction(txhex_signed)
    if 'error' in res:
        print res
        return False

    testlib.next_block(**kw)
    testlib.expect_snv_fail_at('nop.test', testlib.get_current_block(**kw))

    balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS']
    if balance_after != balance_before:
        print 'paid {} for nop.test'.format(balance_before - balance_after)
        return False
Example #34
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

    # name should exist, but not be renewed
    addr2 = virtualchain.address_reencode(
        virtualchain.get_privkey_address(pk2))
    name_rec = state_engine.get_name('nop.test')
    if name_rec is None:
        print 'name record does not exist for nop.test'
        return False

    if name_rec['first_registered'] != name_rec['last_renewed']:
        print 'name nop.test was renewed'
        return False

    # name should exist, but not be renewed
    addr2 = virtualchain.address_reencode(
        virtualchain.get_privkey_address(pk2))
    name_rec = state_engine.get_name('goo.test')
    if name_rec is None:
        print 'name record does not exist for goo.test'
        return False

    if name_rec['first_registered'] != name_rec['last_renewed']:
        print 'name goo.test was renewed'
        return False

    for name in ['baz.test']:
        # not preordered
        preorder = state_engine.get_name_preorder(
            name, virtualchain.make_payment_script(wallets[2].addr), addr2)
        if preorder is not None:
            print "preorder exists"
            return False

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

        # owned by
        if name_rec['address'] != addr2 or name_rec[
                'sender'] != virtualchain.make_payment_script(addr2):
            print "sender is wrong"
            return False

    # paid for baz.test with Stacks, but nevertheless burned Bitcoin
    # however, baz.test's burn output is equal to the bitcoin price
    for name in ['baz']:
        name_rec = state_engine.get_name(name + '.test')
        stacks_price = blockstack.lib.scripts.price_name_stacks(
            name, ns, state_engine.lastblock)
        if name_rec['token_fee'] != stacks_price:
            print 'paid wrong token fee for {}.test'.format(name)
            print 'expected {} ({}), got {} ({})'.format(
                stacks_price, type(stacks_price), name_rec['token_fee'],
                type(name_rec['token_fee']))
            return False

        if name_rec['op_fee'] != blockstack.lib.scripts.price_name(
                name, ns, state_engine.lastblock):
            print 'paid wrong BTC for baz.test ({})'.format(name_rec['op_fee'])
            return False

    return True
def scenario( wallets, **kw ):

    global wallet_keys, wallet_keys_2, error, index_file_data, resource_data

    wallet_keys = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[5].privkey, wallets[3].privkey, wallets[4].privkey )
    test_proxy = testlib.TestAPIProxy()
    blockstack_client.set_default_proxy( test_proxy )

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

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey )
    testlib.next_block( **kw )

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

    testlib.blockstack_name_preorder( "foo.test", wallets[2].privkey, wallets[3].addr )
    testlib.next_block( **kw )
    
    testlib.blockstack_name_register( "foo.test", wallets[2].privkey, wallets[3].addr )
    testlib.next_block( **kw )
    
    # migrate profiles 
    res = testlib.migrate_profile( "foo.test", proxy=test_proxy, wallet_keys=wallet_keys )
    if 'error' in res:
        res['test'] = 'Failed to initialize foo.test profile'
        print json.dumps(res, indent=4, sort_keys=True)
        error = True
        return 

    # tell serialization-checker that value_hash can be ignored here
    print "BLOCKSTACK_SERIALIZATION_CHECK_IGNORE value_hash"
    sys.stdout.flush()
    
    testlib.next_block( **kw )

    data_pk = wallets[-1].privkey
    data_pub = wallets[-1].pubkey_hex
    
    config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG", None)

    # make a session 
    datastore_pk = keylib.ECPrivateKey(wallets[-1].privkey).to_hex()
    res = testlib.blockstack_cli_app_signin("foo.test", datastore_pk, 'register.app', ['names', 'register', 'transfer', 'prices', 'zonefiles', 'blockchain', 'node_read'])
    if 'error' in res:
        print json.dumps(res, indent=4, sort_keys=True)
        error = True
        return 

    ses = res['token']
    
    # register the name bar.test 
    res = testlib.blockstack_REST_call('POST', '/v1/names', ses, data={'name': 'bar.test'})
    if 'error' in res:
        res['test'] = 'Failed to register user'
        print json.dumps(res)
        error = True
        return False

    print res
    tx_hash = res['response']['transaction_hash']

    # wait for preorder to get confirmed...
    for i in xrange(0, 6):
        testlib.next_block( **kw )

    res = testlib.verify_in_queue(ses, 'bar.test', 'preorder', tx_hash )
    if not res:
        return False

    # wait for the preorder to get confirmed
    for i in xrange(0, 6):
        testlib.next_block( **kw )

    # wait for register to go through 
    print 'Wait for register to be submitted'
    time.sleep(10)

    # wait for the register to get confirmed 
    for i in xrange(0, 6):
        testlib.next_block( **kw )

    res = testlib.verify_in_queue(ses, 'bar.test', 'register', None )
    if not res:
        return False

    for i in xrange(0, 6):
        testlib.next_block( **kw )

    print 'Wait for update to be submitted'
    time.sleep(10)

    # wait for update to get confirmed 
    for i in xrange(0, 6):
        testlib.next_block( **kw )

    res = testlib.verify_in_queue(ses, 'bar.test', 'update', None )
    if not res:
        return False

    for i in xrange(0, 6):
        testlib.next_block( **kw )
  
    print 'Wait for update to be confirmed'
    time.sleep(10)

    res = testlib.blockstack_REST_call("GET", "/v1/names/bar.test", ses)
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to get name bar.test'
        print json.dumps(res)
        return False

    zonefile_hash = res['response']['zonefile_hash']

    # transfer it
    res = testlib.blockstack_REST_call("PUT", "/v1/names/bar.test/owner", ses, data={'owner': wallets[7].addr})
    if 'error' in res or res['http_status'] != 202:
        res['test'] = 'Failed to transfer name'
        print json.dumps(res)
        return False

    # wait for transfer to get confirmed 
    for i in xrange(0, 6):
        testlib.next_block( **kw )

    res = testlib.verify_in_queue(ses, 'bar.test', 'transfer', None )
    if not res:
        return False

    for i in xrange(0, 6):
        testlib.next_block( **kw )

    print 'waiting for transfer to get confirmed'
    time.sleep(10)

    # poll 
    res = testlib.blockstack_REST_call("GET", "/v1/names/bar.test", ses )
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to query name'
        print json.dumps(res)
        error = True
        return False

    # new owner?
    if not res['response'].has_key('address'):
        res['test'] = 'No address given'
        print json.dumps(res)
        return False

    address_testnet = virtualchain.address_reencode(str(res['response']['address']), network='testnet')
    if address_testnet != wallets[7].addr:
        res['test'] = 'Failed to transfer name to new address {}'.format(wallets[7].addr)
        res['owner_address_testnet'] = address_testnet
        print json.dumps(res)
        return False

    # do we have the history for the name?
    res = testlib.blockstack_REST_call("GET", "/v1/names/bar.test/history", ses )
    if 'error' in res or res['http_status'] != 200:
        res['test'] = "Failed to get name history for foo.test"
        print json.dumps(res)
        return False

    # valid history?
    hist = res['response']
    if len(hist.keys()) != 4:
        res['test'] = 'Failed to get update history'
        res['history'] = hist
        print json.dumps(res, indent=4, sort_keys=True)
        return False

    # get the zonefile
    res = testlib.blockstack_REST_call("GET", "/v1/names/bar.test/zonefile/{}".format(zonefile_hash), ses )
    if 'error' in res or res['http_status'] != 200:
        res['test'] = 'Failed to get name zonefile'
        print json.dumps(res)
        return False
def scenario( wallets, **kw ):

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

    testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey )
    testlib.next_block( **kw )

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

    testlib.blockstack_name_preorder( "foo1.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo2.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo3.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo4.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo5.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo6.test", wallets[2].privkey, wallets[3].addr )
    testlib.blockstack_name_preorder( "foo7.test", wallets[2].privkey, wallets[3].addr )
    testlib.next_block( **kw )

    zf_template = "$ORIGIN {}\n$TTL 3600\n{}"
    zf_default_url = '_https._tcp URI 10 1 "https://raw.githubusercontent.com/nobody/content/profile.md"'

    zonefiles = {
        'foo1.test': zf_template.format('foo1.test', subdomains.make_subdomain_txt('bar.foo1.test', 'foo1.test', wallets[4].addr, 0, zf_template.format('bar.foo1.test', zf_default_url), wallets[4].privkey)),
        'foo2.test': zf_template.format('foo2.test', subdomains.make_subdomain_txt('bar.foo2.test', 'foo2.test', wallets[4].addr, 0, zf_template.format('bar.foo2.test', zf_default_url), wallets[4].privkey)),
        'foo3.test': zf_template.format('foo3.test', subdomains.make_subdomain_txt('bar.foo3.test', 'foo3.test', wallets[4].addr, 0, zf_template.format('bar.foo3.test', zf_default_url), wallets[4].privkey)),
    }

    testlib.blockstack_name_register( "foo1.test", wallets[2].privkey, wallets[3].addr, zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo1.test']))
    testlib.blockstack_name_register( "foo2.test", wallets[2].privkey, wallets[3].addr, zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo2.test']))
    testlib.blockstack_name_register( "foo3.test", wallets[2].privkey, wallets[3].addr, zonefile_hash=storage.get_zonefile_data_hash(zonefiles['foo3.test']))
    testlib.blockstack_name_register( "foo4.test", wallets[2].privkey, wallets[3].addr, zonefile_hash='11' * 20)
    testlib.blockstack_name_register( "foo5.test", wallets[2].privkey, wallets[3].addr, zonefile_hash='11' * 20)
    testlib.blockstack_name_register( "foo6.test", wallets[2].privkey, wallets[3].addr, zonefile_hash='11' * 20)
    testlib.blockstack_name_register( "foo7.test", wallets[2].privkey, wallets[3].addr, zonefile_hash='11' * 20)
    testlib.next_block( **kw )
    
    assert testlib.blockstack_put_zonefile(zonefiles['foo1.test'])
    assert testlib.blockstack_put_zonefile(zonefiles['foo2.test'])
    assert testlib.blockstack_put_zonefile(zonefiles['foo3.test'])
    
    # update and transfer, but if i % 2 == 0, transfer to a different address
    # use a different domain name in each case.
    # verify that only transfers on the creator domain are valid.
    wallet_schedule = [
        (4, 0), # won't be accepted due to domain independence
        (4, 1), # will be accepted
        (1, 2), # won't be accepted due to domain independence
        (1, 3), # will be accepted
    ]
    sequence_schedule = [
        1,  # won't be accepted due to domain independence
        1,  # will be accepted
        2,  # won't be accepted due to domain independence
        2,  # will be accepted
    ]

    expected_zf_default_url = '_https._tcp URI 10 1 "https://test.com/?index={}"'.format(4)

    for i in range(0, 4):
        zf_template = "$ORIGIN {}\n$TTL 3600\n{}"
        zf_default_url = '_https._tcp URI 10 1 "https://test.com/?index={}"'.format(i+1)

        if i % 2 == 0:
            names = [
                'foo{}.test'.format(i+4),
                'foo{}.test'.format(i+4),
                'foo{}.test'.format(i+4),
            ]
        else:
            names = [
                'foo1.test',
                'foo2.test',
                'foo3.test',
            ]

        k = wallet_schedule[i][0]
        k2 = wallet_schedule[i][1]

        s = sequence_schedule[i]

        zonefiles = {
            'foo1.test': zf_template.format(names[0], subdomains.make_subdomain_txt('bar.foo1.test', names[0], wallets[k2].addr, s, zf_template.format('bar.foo1.test', zf_default_url), wallets[k].privkey)),
            'foo2.test': zf_template.format(names[1], subdomains.make_subdomain_txt('bar.foo2.test', names[1], wallets[k2].addr, s, zf_template.format('bar.foo2.test', zf_default_url), wallets[k].privkey)),
            'foo3.test': zf_template.format(names[2], subdomains.make_subdomain_txt('bar.foo3.test', names[2], wallets[k2].addr, s, zf_template.format('bar.foo3.test', zf_default_url), wallets[k].privkey)),
        }
        
        testlib.blockstack_name_update(names[0], storage.get_zonefile_data_hash(zonefiles['foo1.test']), wallets[3].privkey)
        testlib.blockstack_name_update(names[1], storage.get_zonefile_data_hash(zonefiles['foo2.test']), wallets[3].privkey)
        testlib.blockstack_name_update(names[2], storage.get_zonefile_data_hash(zonefiles['foo3.test']), wallets[3].privkey)
        testlib.next_block(**kw)

        assert testlib.blockstack_put_zonefile(zonefiles['foo1.test'])
        assert testlib.blockstack_put_zonefile(zonefiles['foo2.test'])
        assert testlib.blockstack_put_zonefile(zonefiles['foo3.test'])
    
    # kick off subdomain indexing
    testlib.next_block(**kw)
    
    # query each subdomain
    proxy = testlib.make_proxy()
    for i in xrange(1, 4):
        fqn = 'bar.foo{}.test'.format(i)
        res = client.get_name_record(fqn, proxy=proxy)
        if 'error' in res:
            print res
            return False
       
        if res['sequence'] != 2:
            print 'wrong sequence'
            print res
            return False

        if virtualchain.address_reencode(str(res['address'])) != virtualchain.address_reencode(wallets[3].addr):
            print 'wrong owner'
            print res
            return False

        expected_zonefile = zf_template.format(fqn, expected_zf_default_url)
        if base64.b64decode(res['zonefile']) != expected_zonefile:
            print 'zonefile mismatch'
            print 'expected\n{}'.format(expected_zonefile)
            print 'got\n{}'.format(base64.b64decode(res['zonefile']))
            return False

        # should be in atlas as well
        zf = testlib.blockstack_get_zonefile(res['value_hash'], parse=False)
        if not zf:
            print 'no zone file {} in atlas'.format(res['value_hash'])
            return False

        if zf != expected_zonefile:
            print 'zonefile mismatch in atlas'
            print 'expected\n{}'.format(expected_zonefile)
            print 'got\n{}'.format(base64.b64decode(res['zonefile']))
            return False

        # there should only be three history items per name
        hist = client.get_name_record(fqn, proxy=proxy, include_history=True)
        if 'error' in hist:
            print res
            return False

        if len(hist['history']) != 3:
            print 'wrong history length'
            print res
            return False

    # reindex
    assert testlib.check_subdomain_db(**kw)