def make_outputs( data, inputs, recipient_address, sender_address, update_hash_b58, format='bin'): """ Builds the outputs for a name import: * [0] is the OP_RETURN * [1] is the new owner (recipient) * [2] is the update hash * [3] is the change sent to the original owner """ dust_value = DEFAULT_DUST_FEE outputs = [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # recipient output {"script_hex": make_pay_to_address_script(recipient_address), "value": dust_value}, # update hash output {"script_hex": make_pay_to_address_script(update_hash_b58), "value": dust_value}, # change output {"script_hex": make_pay_to_address_script(sender_address), "value": calculate_change_amount(inputs, 0, 0)} ] dust_fee = tx_dust_fee_from_inputs_and_outputs( inputs, outputs ) outputs[-1]['value'] = calculate_change_amount( inputs, 2*dust_value, dust_fee ) return outputs
def check( state_engine ): global transfer_block # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "no name" return False # owned by if name_rec['address'] != wallets[8].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[8].addr): print "address/sender is wrong: got %s, %s" % (name_rec['address'], name_rec['sender']) return False # has new sender pubkey (from the payment key) if name_rec['sender_pubkey'] != wallets[7].pubkey_hex: print "wrong pubkey: %s != %s" % (name_rec['sender_pubkey'], wallets[7].pubkey_hex) return False # has no value hash if name_rec['value_hash'] is not None: print "still has value hash" return False # transfer happened historic_name_rec = state_engine.get_name_at( "foo.test", transfer_block, include_expired=True ) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % transfer_block return False historic_name_rec = historic_name_rec[0] if historic_name_rec['opcode'] != "NAME_TRANSFER": print "transfer didn't occur at %s" % transfer_block return False if historic_name_rec['address'] != wallets[4].addr or historic_name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): print "address/sender is wrong for transfer: got %s, %s" % (name_rec['address'], name_rec['sender']) return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "'test' not revealed" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "'test' not found" return False if ns['namespace_id'] != 'test': print "'test' not returned" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "'foo.test' still preordered" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "'foo.test' not registered" return False # transferred if name_rec['address'] != wallets[4].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): print "'foo.test' invalid owner" return False return True
def make_outputs( data, inputs, sender_addr, fee, format='bin' ): """ Make outputs for a name preorder: [0] OP_RETURN with the name [1] address with the NAME_PREORDER sender's address [2] pay-to-address with the *burn address* with the fee """ op_fee = max(fee, DEFAULT_DUST_FEE) dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE dust_value = DEFAULT_DUST_FEE bill = op_fee return [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # change address (can be subsidy key) {"script_hex": make_pay_to_address_script(sender_addr), "value": calculate_change_amount(inputs, bill, dust_fee)}, # burn address {"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": op_fee} ]
def make_outputs( data, inputs, change_addr, op_fee, format='bin' ): """ Make outputs for a namespace preorder: [0] OP_RETURN with the name [1] change address with the NAME_PREORDER sender's address [2] pay-to-address with the *burn address* with the fee """ op_fee = max( op_fee, DEFAULT_DUST_FEE ) outputs = [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # change address {"script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, 0, 0)}, # burn address {"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": op_fee} ] dust_fee = tx_dust_fee_from_inputs_and_outputs( inputs, outputs ) outputs[1]['value'] = calculate_change_amount( inputs, op_fee, dust_fee ) return outputs
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns["namespace_id"] != "test": return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: return False # owned by if name_rec["address"] != wallets[3].addr or name_rec["sender"] != pybitcoin.make_pay_to_address_script( wallets[3].addr ): return False return True
def make_outputs( data, inputs, new_name_owner_address, change_address, pay_fee=True, format='bin' ): """ Builds the outputs for a name transfer operation. """ dust_fee = None op_fee = None dust_value = DEFAULT_DUST_FEE if pay_fee: dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE op_fee = DEFAULT_DUST_FEE else: dust_fee = 0 op_fee = 0 return [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # new name owner output {"script_hex": make_pay_to_address_script(new_name_owner_address), "value": dust_value}, # change output {"script_hex": make_pay_to_address_script(change_address), "value": calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_outputs( data, inputs, sender_addr, op_fee, format='bin' ): """ Make outputs for a name preorder: [0] OP_RETURN with the name [1] address with the NAME_PREORDER sender's address [2] pay-to-address with the *burn address* with the fee NOTE: the fee must cover *all* the names """ outputs = [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # change address (can be subsidy key) {"script_hex": make_pay_to_address_script(sender_addr), "value": calculate_change_amount(inputs, 0, 0)}, # burn address {"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": op_fee} ] dust_fee = tx_dust_fee_from_inputs_and_outputs( inputs, outputs ) outputs[1]['value'] = calculate_change_amount( inputs, op_fee, dust_fee ) return outputs
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: return False # updated, and data is preserved if name_rec['value_hash'] != '11' * 20: return False # transferred if name_rec['address'] != wallets[4].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): return False return True
def make_outputs( data, inputs, reveal_addr, change_addr, format='bin', testset=False ): """ Make outputs for a register: [0] OP_RETURN with the name [1] pay-to-address with the *reveal_addr*, not the sender's address. [2] change address with the NAMESPACE_PREORDER sender's address """ outputs = [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # register address {"script_hex": make_pay_to_address_script(reveal_addr), "value": DEFAULT_DUST_FEE}, # change address {"script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, 0, 0)}, ] dust_fee = tx_dust_fee_from_inputs_and_outputs( inputs, outputs ) outputs[-1]['value'] = calculate_change_amount( inputs, DEFAULT_DUST_FEE, dust_fee ) return outputs
def make_outputs( data, inputs, change_addr, fee, pay_fee=True, format='bin' ): """ Make outputs for a namespace preorder: [0] OP_RETURN with the name [1] change address with the NAME_PREORDER sender's address [2] pay-to-address with the *burn address* with the fee """ dust_fee = DEFAULT_OP_RETURN_FEE + (len(inputs) + 2) * DEFAULT_DUST_FEE op_fee = max(fee, DEFAULT_DUST_FEE) dust_value = DEFAULT_DUST_FEE bill = op_fee if not pay_fee: # subsidized dust_fee = 0 op_fee = 0 dust_value = 0 bill = 0 return [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # change address {"script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, bill, dust_fee)}, # burn address {"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": op_fee} ]
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: return False # owned by if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): return False # NOT renewed if name_rec['last_renewed'] != name_rec['first_registered']: print name_rec['last_renewed'] print name_rec['first_registered'] return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "id" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "id" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'id': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.id", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "preorder exists" return False # registered name_rec = state_engine.get_name( "foo.id" ) if name_rec is None: print "name does not exist" return False # owned by if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "sender is wrong" return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print json.dumps(name_rec, indent=4) return False # registered to new owner name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "name rec is None" return False if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print json.dumps(name_rec, indent=4 ) return False return True
def do_interleaving( name, namerecs, order ): # order should be a string of "u", "t", and "r" order = list(order) for i in xrange(0, len(order)): op = order[i] if op == 'r': # renew print "\nrenew '%s' with %s\n" % (name, pybitcoin.make_pay_to_address_script( namerecs[name][0].addr )) resp = testlib.blockstack_name_renew( name, namerecs[name][0].privkey, register_addr=namerecs[name][0].addr ) if 'error' in resp: print json.dumps( resp, indent=4 ) elif op == 'u': # update resp = testlib.blockstack_name_update( name, ("%s%s" % (i, i)) * 20, namerecs[name][0].privkey ) if 'error' in resp: print json.dumps( resp, indent=4 ) elif op == 't': # transfer and exchange wallets print "\ntransfer '%s' from %s to %s" % (name, pybitcoin.make_pay_to_address_script( namerecs[name][0].addr ), pybitcoin.make_pay_to_address_script( namerecs[name][1].addr )) resp = testlib.blockstack_name_transfer( name, namerecs[name][1].addr, True, namerecs[name][0].privkey ) if 'error' in resp: print json.dumps( resp, indent=4 ) tmp = namerecs[name][0] namerecs[name][0] = namerecs[name][1] namerecs[name][1] = tmp
def make_outputs( data, inputs, recipient_address, sender_address, update_hash_b58, tx_fee): """ Builds the outputs for a name import: * [0] is the OP_RETURN * [1] is the new owner (recipient) * [2] is the update hash * [3] is the change sent to the original owner Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = DEFAULT_OP_RETURN_FEE + (len(inputs) + 3) * DEFAULT_DUST_FEE + tx_fee op_fee = 2 * DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE return [ # main output {"script_hex": make_op_return_script(str(data), format='hex'), "value": 0}, # recipient output {"script_hex": make_pay_to_address_script(recipient_address), "value": dust_value}, # update hash output {"script_hex": make_pay_to_address_script(update_hash_b58), "value": dust_value}, # change output {"script_hex": make_pay_to_address_script(sender_address), "value": calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_outputs( data, inputs, recipient_address, sender_address, update_hash_b58, format='bin'): """ Builds the outputs for a name import: * [0] is the OP_RETURN * [1] is the new owner (recipient) * [2] is the update hash * [3] is the change sent to the original owner """ dust_fee = DEFAULT_OP_RETURN_FEE + (len(inputs) + 3) * DEFAULT_DUST_FEE op_fee = 2 * DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE return [ # main output {"script_hex": make_op_return_script(data, format=format), "value": 0}, # recipient output {"script_hex": make_pay_to_address_script(recipient_address), "value": dust_value}, # update hash output {"script_hex": make_pay_to_address_script(update_hash_b58), "value": dust_value}, # change output {"script_hex": make_pay_to_address_script(sender_address), "value": calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_outputs( data, inputs, register_addr, change_addr, renewal_fee=None, format='bin' ): """ Make outputs for a register: [0] OP_RETURN with the name [1] pay-to-address with the *register_addr*, not the sender's address. [2] change address with the NAME_PREORDER sender's address """ total_to_send = DEFAULT_OP_RETURN_FEE + DEFAULT_DUST_FEE if renewal_fee is not None: total_to_send += max(renewal_fee, DEFAULT_DUST_FEE) outputs = [ # main output {"script_hex": make_op_return_script(data, format=format), "value": DEFAULT_OP_RETURN_FEE}, # register address {"script_hex": make_pay_to_address_script(register_addr), "value": DEFAULT_DUST_FEE}, # change address {"script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, total_to_send, (len(inputs) + 3) * DEFAULT_DUST_FEE)}, ] if renewal_fee is not None: outputs.append( # burn address (when renewing) {"script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": max(renewal_fee, DEFAULT_DUST_FEE)} ) return outputs
def make_outputs( data, inputs, new_name_owner_address, change_address, tx_fee=0, pay_fee=True): """ Builds the outputs for a name transfer operation. Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = None op_fee = None dust_value = DEFAULT_DUST_FEE if pay_fee: dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = DEFAULT_DUST_FEE else: dust_fee = 0 op_fee = 0 return [ # main output {"script_hex": make_op_return_script(str(data), format='hex'), "value": 0}, # new name owner output {"script_hex": make_pay_to_address_script(new_name_owner_address), "value": dust_value}, # change output {"script_hex": make_pay_to_address_script(change_address), "value": calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_outputs( data, inputs, change_addr, fee, tx_fee, pay_fee=True ): """ Make outputs for a namespace preorder: [0] OP_RETURN with the name [1] change address with the NAME_PREORDER sender's address [2] pay-to-address with the *burn address* with the fee Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = DEFAULT_OP_RETURN_FEE + (len(inputs) + 2) * DEFAULT_DUST_FEE + tx_fee op_fee = max(fee, DEFAULT_DUST_FEE) dust_value = DEFAULT_DUST_FEE bill = op_fee if not pay_fee: # subsidized dust_fee = 0 op_fee = 0 dust_value = 0 bill = 0 return [ # main output {"script_hex": make_op_return_script(str(data), format='hex'), "value": 0}, # change address {"script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, bill, dust_fee)}, # burn address {"script_hex": make_pay_to_address_script(BLOCKSTACK_BURN_ADDRESS), "value": op_fee} ]
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns["namespace_id"] != "test": return False names = ["foo.test", "bar.test", "baz.test"] name_preorder_wallets = [wallets[2], wallets[3], wallets[4]] name_register_wallets = [wallets[5], wallets[6], wallets[7]] name_transfer_wallets = [wallets[6], wallets[7], wallets[5]] for i in xrange(0, len(names)): name = names[i] name_preorder_wallet = name_preorder_wallets[i] name_register_wallet = name_register_wallets[i] name_transfer_wallet = name_transfer_wallets[i] # not preordered preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(name_preorder_wallet.addr), name_register_wallet.addr ) if preorder is not None: print "%s still preordered" % name return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print "not registered %s" % name return False # updated, and data is gone (since revoked) if name_rec["value_hash"] is not None: print "invalid value hash %s: %s" % (name, name_rec["value_hash"]) return False # owned by the right transfer wallet if name_rec["address"] != name_transfer_wallet.addr or name_rec[ "sender" ] != pybitcoin.make_pay_to_address_script(name_transfer_wallet.addr): print "%s owned by %s" % (name, name_transfer_wallet.addr) return False # revoked if not name_rec["revoked"]: print "%s not revoked" % name return False return True
def check( state_engine ): global wallet_keys, datasets, zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "name has wrong owner" return False # have right hash if name_rec['value_hash'] != zonefile_hash: print "Invalid zonefile hash" return False # have no data test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy( test_proxy ) for i in xrange(0, len(datasets)): print "get hello_world_%s" % (i+1) dat = blockstack_client.get_mutable( "foo.test", "hello_world_%s" % (i+1) ) if dat is not None and 'error' not in dat: print "still have '%s'\n%s" % ("hello_world_%s" % (i+1), json.dumps(dat,indent=4,sort_keys=True)) return False if 'error' in dat and dat['error'] != 'No such mutable datum': print json.dumps(dat, indent=4, sort_keys=True) return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: return False # owned if name_rec['address'] != wallets[4].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): return False # not updated if name_rec['value_hash'] is not None: print "value hash is %s" % name_rec['value_hash'] return False # not transferred if name_rec['address'] != wallets[4].addr: print "address is %s" % name_rec['address'] return False # TODO: check renewal block # no longer revoked if name_rec['revoked']: return False # at each of the fail blocks, confirm that the name has not changed from the initial revocation for fb in fail_blocks: historic_name_rec = state_engine.get_name_at( "foo.test", fb, include_expired=True ) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % fb return False historic_name_rec = historic_name_rec[0] if historic_name_rec['opcode'] != 'NAME_REVOKE': print "accepted opcode %s at %s" % (historic_name_rec['opcode'], fb) return False return True
def check( state_engine ): global update_hashes, update_blocks # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print json.dumps(name_rec, indent=4) return False # registered to new owner name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "name rec is None" return False # updated if name_rec['value_hash'] != '0a' * 20: print "invalid value hash" return False if name_rec['address'] != wallets[0].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[0].addr): print json.dumps(name_rec, indent=4 ) return False # updated historically too for i in xrange(0, len(update_blocks)): update_block = update_blocks[i] update_hash = update_hashes[i] historic_name_rec = state_engine.get_name_at( "foo.test", update_block, include_expired=True ) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % update_block return False historic_name_rec = historic_name_rec[0] if historic_name_rec['opcode'] != 'NAME_UPDATE': print "not an update at %s" % update_block return False if historic_name_rec.get('value_hash', None) != update_hash: print "wrong update hash at %s: expected %s, got %s" % (update_block, historic_name_rec.get('value_hash', None), update_hash) return False return True
def check( state_engine ): global import_block # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "no name" return False # owned by if name_rec['address'] != wallets[8].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[8].addr): print "sender is wrong" return False # has new sender pubkey (from the payment key) if name_rec['sender_pubkey'] != wallets[7].pubkey_hex: print "wrong pubkey: %s != %s" % (name_rec['sender_pubkey'], wallets[7].pubkey_hex) return False # verify historic owner and value hash historic_name_rec = state_engine.get_name_at( "foo.test", import_block, include_expired=True ) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % import_block return False historic_name_rec = historic_name_rec[0] if not historic_name_rec.has_key('value_hash') or historic_name_rec['value_hash'] != '11' * 20: print "wrong historic name import value hash: %s" % historic_name_rec.get("value_hash", "(null)") return False if historic_name_rec['address'] != wallets[3].addr or historic_name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "historic sender is wrong" return False return True
def check( state_engine ): global preorder_consensus # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # there won't be a preorder for an individual name... preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "found name preorder for 'foo.test'" return False # there won't be a preorder for all names either preorder = state_engine.get_name_multi_preorder( ['foo.test', 'bar.test', 'baz.test'], \ pybitcoin.make_pay_to_address_script(wallets[2].addr), \ [wallets[3].addr, wallets[4].addr, wallets[5].addr]) if preorder is not None: print "Preorder found for foo.test, bar.test, baz.test" return False prev_name_rec = None # each name will be registered for name, wallet in [('foo.test', wallets[3]), ('bar.test', wallets[4]), ('baz.test', wallets[5])]: name_rec = state_engine.get_name( name ) if name_rec is None: print "No name record for %s" % name return False if name_rec['address'] != wallet.addr: print "'%s' not owned by '%s'" % (name, wallet.addr) return False if name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallet.addr): print "'%s' not controlled by '%s'" % (name, pybitcoin.make_pay_to_address_script(wallet.addr)) return False if prev_name_rec is not None and prev_name_rec['history'] != name_rec['history']: print "'%s' does not have the same preorder as '%s'" % (prev_name_rec['name'], name_rec['name']) return False prev_name_rec = name_rec return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # there won't be a preorder for an individual name... preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "found name preorder for 'foo.test'" return False # there will be a preorder for all names preorder = state_engine.get_name_preorder_multi( ['foo.test', 'bar.test', 'baz.test'], \ pybitcoin.make_pay_to_address_script(wallets[2].addr), \ [wallets[3].addr, wallets[4].addr, wallets[5].addr]) if preorder is None: print "Preorder not found for foo.test, bar.test, baz.test" return False prev_name_rec = None # none of the multi-preordered names will be registered for name, wallet in [('foo.test', wallets[3]), ('bar.test', wallets[4]), ('baz.test', wallets[5])]: name_rec = state_engine.get_name( name ) if name_rec is not None: print "Registered name record for %s" % name return False # the single preordered name will be registered name_rec = state_engine.get_name( 'goo.test' ) if name_rec is None: print "No name record for %s" % name return False if name_rec['address'] != wallet[3].addr: print "'%s' not owned by '%s'" % (name, wallet.addr) return False if name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallet[3].addr): print "'%s' not controlled by '%s'" % (name, pybitcoin.make_pay_to_address_script(wallet[3].addr)) return False return True
def check( state_engine ): global reveal_blocks # the namespace should not exist ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "still revealed" return False # should not be preordered namespace_preorder_hashes = state_engine.get_all_preordered_namespace_hashes() if len(namespace_preorder_hashes) != 0: print "preorder hashes: %s" % namespace_preorder_hashes return False # examine historical form for count in xrange(0, 3): ns = state_engine.get_namespace_at( "test", reveal_blocks[count] ) if ns is None or len(ns) == 0: print "no namespace state at %s" % (reveal_blocks[count]) return False ns = ns[0] # fields should match for f in ['lifetime', 'coeff', 'base', 'nonalpha_discount', 'no_vowel_discount']: if ns[f] != count + 1: print "%s: expected %s, got %s" % (f, count+1, ns[f]) return False buckets = [count] * 16 if ns['buckets'] != buckets: print "buckets: expected %s, got %s" % ([count+1]*16, ns['buckets']) return False # reveal block should match if ns['reveal_block'] != reveal_blocks[count]: print "reveal block: expected %s, got %s" % (reveal_blocks[count], ns['reveal_block']) return False # sender should match if ns['address'] != wallets[count].addr or ns['sender'] != pybitcoin.make_pay_to_address_script(wallets[count].addr): print "sender: expected %s, got %s" % (ns['address'], wallets[count].addr) return False # recipient should match if ns['recipient_address'] != wallets[count+1].addr or ns['recipient'] != pybitcoin.make_pay_to_address_script(wallets[count+1].addr): print "recipient: expected %s, got %s" % (ns['recipient_address'], wallets[count+1].addr) return False return True
def check( state_engine ): global wallets # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: return False # updated, but revoked, so data should be None if name_rec['value_hash'] is not None: return False # transferred if name_rec['address'] != wallets[4].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): return False # previously owned by... name_rec_prev = state_engine.get_name_at( "foo.test", sorted( name_rec['history'].keys() )[-1] - 2 )[0] if name_rec_prev['address'] != wallets[3].addr or name_rec_prev['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): return False # revoked if not name_rec['revoked']: return False # verify that the user wallets (3 and 4) were *not* debited any btc rc = True for wallet in [wallets[3], wallets[4]]: unspents = testlib.get_unspents( wallet.addr ) value = sum( [us['value'] for us in unspents] ) if value < 100000000000: print "value of '%s' is '%s'" % (wallet.addr, value) rc = False return rc
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: return False ns = state_engine.get_namespace( "test" ) if ns is None: return False if ns['namespace_id'] != 'test': return False names = ['foo.test', 'bar.test', 'baz.test'] name_preorder_wallets = [wallets[2], wallets[3], wallets[4]] name_register_wallets = [wallets[5], wallets[6], wallets[7]] name_transfer_wallets = [wallets[6], wallets[7], wallets[5]] for i in xrange(0, len(names)): name = names[i] name_preorder_wallet = name_preorder_wallets[i] name_register_wallet = name_register_wallets[i] name_transfer_wallet = name_transfer_wallets[i] # not preordered preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(name_preorder_wallet.addr), name_register_wallet.addr ) if preorder is not None: return False # registered name_rec = state_engine.get_name( name ) if name_rec is None: return False # data is gone if name_rec['value_hash'] is not None: return False # owned by the right transfer wallet if name_rec['address'] != name_transfer_wallet.addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(name_transfer_wallet.addr): return False # renewed if name_rec['last_renewed'] == name_rec['first_registered']: return False # revoked if not name_rec['revoked']: return False return True
def check( state_engine ): global wallet_keys, datasets, zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "name has wrong owner" return False # have right hash if name_rec['value_hash'] != zonefile_hash: print "Invalid zonefile hash" return False # zonefile is NOT legacy user_zonefile = blockstack_client.zonefile.load_name_zonefile( 'foo.test', zonefile_hash ) if 'error' in user_zonefile: print json.dumps(user_zonefile, indent=4, sort_keys=True) return False if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ): print "legacy still" print json.dumps(user_zonefile, indent=4, sort_keys=True) return False res = testlib.blockstack_cli_lookup("foo.test") if 'error' in res: print 'error looking up profile: {}'.format(res) return False assert 'profile' in res assert 'zonefile' in res return True
def check( state_engine ): global wallet_keys, error, index_file_data, resource_data if error: print "Key operation failed." return False # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False names = ['foo.test'] wallet_keys_list = [wallet_keys] test_proxy = testlib.TestAPIProxy() for i in xrange(0, len(names)): name = names[i] wallet_payer = 3 * (i+1) - 1 wallet_owner = 3 * (i+1) wallet_data_pubkey = 3 * (i+1) + 1 wallet_keys = wallet_keys_list[i] # not preordered preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr): print "name has wrong owner" return False # get app config app_config = testlib.blockstack_cli_app_get_config( "foo.test", "ping.app" ) if 'error' in app_config: print "failed to get app config\n{}\n".format(json.dumps(app_config, indent=4, sort_keys=True)) return False # inspect... app_config = app_config['config'] if app_config['driver_hints'] != []: print "Invalid driver hints\n{}\n".format(json.dumps(app_config, indent=4, sort_keys=True)) return False if app_config['api_methods'] != ['node_read']: print "Invalid API list\n{}\n".format(json.dumps(app_config, indent=4, sort_keys=True)) return False if len(app_config['index_uris']) != 1: print "Invalid URI records\n{}\n".format(json.dumps(app_config, indent=4, sort_keys=True)) return False if app_config['blockchain_id'] != 'foo.test': print "invalid blockchain ID\n{}\n".format(app_config) return False # get index file index_file = testlib.blockstack_cli_app_get_resource("foo.test", "ping.app", "index.html") if 'error' in index_file: print "failed to get index file\n{}\n".format(json.dumps(index_file, indent=4, sort_keys=True)) return False if index_file['res'] != index_file_data: print "got wrong index file:\n{}\n".format(index_file['index_file']) return False # get resource resource_data_res = testlib.blockstack_cli_app_get_resource("foo.test", "ping.app", "hello_world") if 'error' in resource_data_res: print "failed to get resource hello_world\n{}\n".format(json.dumps(resource_data, indent=4, sort_keys=True)) return False if resource_data_res['res'] != resource_data: print "got wrong resource data:\n{}\nexpected:\n{}\n".format(resource_data_res['res'], resource_data) return False return True
def __init__(self, tx_path=None, tx_list=None, tx_grouping=1, start_block=0, start_time=0x11111111, difficulty=1.0, initial_utxos={}, **kw ): """ Create a mock bitcoind connection, either from a list of serialized transactions on-file, or a given Python list of serialized transactions. Transactions will be bundled into blocks in groups of size tx_grouping. """ self.block_hashes = {} # map block ID to block hash self.blocks = {} # map block hash to block info (including transaction IDs) self.txs = {} # map tx hash to a list of transactions self.next_block_txs = [] # next block's transactions self.difficulty = difficulty self.time = start_time self.start_block = start_block self.end_block = start_block self.block_hashes[ start_block - 1 ] = '00' * 32 self.blocks[ '00' * 32 ] = {} # the initial utxos might be a serialized CSV (i.e. loaded directly from the config file). # if so, then parse it if type(initial_utxos) in [str, unicode]: tmp = {} parts = initial_utxos.split(",") for utxo in parts: privkey, value = utxo.split(':') tmp[ privkey ] = int(value) initial_utxos = tmp tx_recs = [] if tx_path is not None: with open( tx_path, "r" ) as f: tmp = f.readlines() tx_recs = [l.strip() for l in tmp] elif tx_list is not None: tx_recs = tx_list # prepend utxos if len(initial_utxos) > 0: initial_outputs = [] for (privkey, value) in initial_utxos.items(): addr = pybitcoin.BitcoinPrivateKey( privkey ).public_key().address() out = { 'value': value, 'script_hex': pybitcoin.make_pay_to_address_script( addr ) } initial_outputs.append( out ) tx = { 'inputs': [], 'outputs': initial_outputs, 'locktime': 0, 'version': 0xff } tx_hex = tx_serialize( tx['inputs'], tx['outputs'], tx['locktime'], tx['version'] ) tx_recs = [tx_hex] + tx_recs i = 0 while True: txs = [] count = 0 while i < len(tx_recs) and count < tx_grouping: txs.append( tx_recs[i] ) i += 1 if len(txs) > 0: for tx in txs: self.sendrawtransaction( tx ) self.flush_transactions() if i >= len(tx_recs): break
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", "6666", 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", "6666", 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", "6666", 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", "6666", 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", "6666", 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", "6666", 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", "6666", 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 # 0 2 3 39 # |----|--|-----------------------------| # magic op name.ns_id (37 bytes) 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") } 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 = pybitcoin.make_pay_to_address_script( wallets[0].addr ) fake_recipient = pybitcoin.make_pay_to_address_script( wallets[1].addr ) fake_recipient_address = wallets[1].addr fake_import_update_hash = "44" * 20 # 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(): parsed_op = parse_nameop( opcode, testscript[3:], fake_pubkey, \ recipient=fake_recipient, recipient_address=fake_recipient_address, import_update_hash=fake_import_update_hash ) if testname.startswith("valid"): # should work if parsed_op is None: print >> sys.stderr, "Failed to parse valid id%s%s" % (opcode, testscript[3:]) return False else: # should fail if parsed_op is not None: print >> sys.stderr, "Parsed invalid test '%s' (id%s%s)" % (testname, opcode, testscript[3:]) return False return True
def check(state_engine): global last_first_block, first_preorder original_price = 6400000 curr_price = original_price * blockstack_server.lib.config.get_epoch_price_multiplier( 267, "test") # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr, include_failed=True) if preorder is not None: return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "still expired" return False # blocks updated if name_rec['address'] != wallets[3].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print json.dumps(name_rec, indent=4, sort_keys=True) return False # check blocks if name_rec['first_registered'] != last_first_block: print "wrong first_registered; expected %s" % last_first_block print json.dumps(name_rec, indent=4, sort_keys=True) return False if name_rec['block_number'] != first_preorder: print "wrong block_number; expected %s" % last_first_preorder print json.dumps(name_rec, indent=4, sort_keys=True) return False # epoch 2 fee paid on second preorder if abs(name_rec['op_fee'] - curr_price) >= 10e-8: print "wrong fee: %s != %s" % (name_rec['op_fee'], curr_price) return False historic_name_rec = state_engine.get_name_at("foo.test", first_preorder + 1, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % import_block_1 return False # epoch 1 fee paid on first preorder historic_name_rec = historic_name_rec[0] if historic_name_rec['address'] != wallets[3].addr or historic_name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "historic sender is wrong" return False if abs(historic_name_rec['op_fee'] - original_price) >= 10e-8: print "wrong historic fee at epoch 1: %s != %s" % ( historic_name_rec['op_fee'], original_price) return False # epoch 2 fee paid on second preorder historic_name_rec = state_engine.get_name_at("foo.test", last_first_block, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % import_block_1 return False historic_name_rec = historic_name_rec[0] if abs(historic_name_rec['op_fee'] - curr_price) >= 10e-8: print "wrong historic fee at epoch 1: %s != %s" % ( historic_name_rec['op_fee'], original_price) return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is None: print "not revealed" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "no name" return False # updated, and data preserved if name_rec['value_hash'] != "66" * 20: print "wrong value hash" return False # transferred if name_rec['address'] != wallets[5].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script( wallets[5].addr ): print "wrong owner" return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "bar.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name( "bar.test" ) if name_rec is None: print "no name" return False # updated, and data preserved if name_rec['value_hash'] != "77" * 20: print "wrong value hash" return False # transferred if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script( wallets[3].addr ): print "wrong owner" return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "baz.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name( "baz.test" ) if name_rec is None: print "no name" return False # updated, and data preserved if name_rec['value_hash'] != "88" * 20: print "wrong value hash" return False # transferred if name_rec['address'] != wallets[4].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script( wallets[4].addr ): print "wrong owner" return False return True
def check(state_engine): global preorder_consensus # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # there won't be a preorder for an individual name... preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr) if preorder is not None: print "found name preorder for 'foo.test'" return False # there won't be a preorder for all names either preorder = state_engine.get_name_multi_preorder( ['foo.test', 'bar.test', 'baz.test'], \ pybitcoin.make_pay_to_address_script(wallets[2].addr), \ [wallets[3].addr, wallets[4].addr, wallets[5].addr]) if preorder is not None: print "Preorder found for foo.test, bar.test, baz.test" return False prev_name_rec = None # each name will be registered for name, wallet in [('foo.test', wallets[3]), ('bar.test', wallets[4]), ('baz.test', wallets[5])]: name_rec = state_engine.get_name(name) if name_rec is None: print "No name record for %s" % name return False if name_rec['address'] != wallet.addr: print "'%s' not owned by '%s'" % (name, wallet.addr) return False if name_rec['sender'] != pybitcoin.make_pay_to_address_script( wallet.addr): print "'%s' not controlled by '%s'" % ( name, pybitcoin.make_pay_to_address_script(wallet.addr)) return False if prev_name_rec is not None and prev_name_rec['history'] != name_rec[ 'history']: print "'%s' does not have the same preorder as '%s'" % ( prev_name_rec['name'], name_rec['name']) return False prev_name_rec = name_rec return True
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # name recs names = { "utr.test": { "address": wallets[1].addr, "value_hash": "00" * 20 }, "urt.test": { "address": wallets[3].addr, "value_hash": "00" * 20 }, "tur.test": { "address": wallets[5].addr, "value_hash": "11" * 20 }, "tru.test": { "address": wallets[7].addr, "value_hash": "22" * 20 }, "rut.test": { "address": wallets[9].addr, "value_hash": "11" * 20 }, "rtu.test": { "address": wallets[11].addr, "value_hash": "22" * 20 } } for name in names.keys(): name_rec = state_engine.get_name(name) if name_rec is None: print "missing %s" % name return False # check update if name_rec['value_hash'] != names[name]['value_hash']: print "value hash mismatch on %s: expected %s, got %s" % ( name, names[name]['value_hash'], name_rec['value_hash']) return False # check owner if name_rec['address'] != names[name]['address'] or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script( names[name]['address']): print "owner mismatch on %s: expected %s, got %s" % ( name, names[name]['address'], name_rec['address']) return False return True
def check(state_engine): global wallet_keys, datasets, zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "name has wrong owner" return False # have right hash if name_rec['value_hash'] != zonefile_hash: print "Invalid zonefile hash" return False # zonefile is NOT legacy user_zonefile = blockstack_client.zonefile.load_name_zonefile( 'foo.test', zonefile_hash) if 'error' in user_zonefile: print json.dumps(user_zonefile, indent=4, sort_keys=True) return False if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): print "legacy still" print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # zonefile has no key zonefile_key = blockstack_client.user.user_zonefile_data_pubkey( user_zonefile) if zonefile_key is not None: print 'still have zonefile key' print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # have right data test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy(test_proxy) for i in xrange(0, len(datasets)): print "get hello_world_%s" % (i + 1) dat = testlib.blockstack_cli_get_mutable( "foo.test", "hello_world_{}".format(i + 1)) if dat is None: print "No data '%s'" % ("hello_world_%s" % (i + 1)) return False if 'error' in dat: print json.dumps(dat, indent=4, sort_keys=True) return False if json.loads(dat['data']) != datasets[i]: print "Mismatch %s: %s != %s" % (i, dat, datasets[i]) return False res = testlib.blockstack_cli_lookup("foo.test") if 'error' in res: print 'error looking up profile: {}'.format(res) return False assert 'profile' in res assert 'zonefile' in res return True
def check(state_engine): global reveal_blocks # the namespace should not exist ns = state_engine.get_namespace_reveal("test") if ns is not None: print "still revealed" return False # should not be preordered namespace_preorder_hashes = state_engine.get_all_preordered_namespace_hashes( ) if len(namespace_preorder_hashes) != 0: print "preorder hashes: %s" % namespace_preorder_hashes return False # examine historical form for count in xrange(0, 3): ns = state_engine.get_namespace_at("test", reveal_blocks[count]) if ns is None or len(ns) == 0: print "no namespace state at %s" % (reveal_blocks[count]) return False ns = ns[0] # fields should match for f in [ 'lifetime', 'coeff', 'base', 'nonalpha_discount', 'no_vowel_discount' ]: if ns[f] != count + 1: print "%s: expected %s, got %s" % (f, count + 1, ns[f]) return False buckets = [count] * 16 if ns['buckets'] != buckets: print "buckets: expected %s, got %s" % ([count + 1] * 16, ns['buckets']) return False # reveal block should match if ns['reveal_block'] != reveal_blocks[count]: print "reveal block: expected %s, got %s" % (reveal_blocks[count], ns['reveal_block']) return False # sender should match if ns['address'] != wallets[count].addr or ns[ 'sender'] != pybitcoin.make_pay_to_address_script( wallets[count].addr): print "sender: expected %s, got %s" % (ns['address'], wallets[count].addr) return False # recipient should match if ns['recipient_address'] != wallets[count + 1].addr or ns[ 'recipient'] != pybitcoin.make_pay_to_address_script( wallets[count + 1].addr): print "recipient: expected %s, got %s" % (ns['recipient_address'], wallets[count + 1].addr) return False return True
def make_outputs(data, inputs, register_addr, change_addr, renewal_fee=None, pay_fee=True, format='bin'): """ Make outputs for a register: [0] OP_RETURN with the name [1] pay-to-address with the *register_addr*, not the sender's address. [2] change address with the NAME_PREORDER sender's address [3] (OPTIONAL) renewal fee, sent to the burn address """ dust_fee = None dust_value = None op_fee = None bill = None if pay_fee: # sender pays if renewal_fee is not None: # renewing dust_fee = (len(inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE dust_value = DEFAULT_DUST_FEE op_fee = max(renewal_fee, DEFAULT_DUST_FEE) bill = op_fee else: # registering dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE dust_value = DEFAULT_DUST_FEE op_fee = 0 bill = 0 else: # subsidized by another address if renewal_fee is not None: # renewing dust_fee = 0 dust_value = 0 op_fee = max(renewal_fee, DEFAULT_DUST_FEE) bill = 0 else: # registering dust_fee = 0 dust_value = 0 op_fee = 0 bill = 0 outputs = [ # main output { "script_hex": make_op_return_script(data, format=format), "value": 0 }, # register address { "script_hex": make_pay_to_address_script(register_addr), "value": dust_value }, # change address { "script_hex": make_pay_to_address_script(change_addr), "value": calculate_change_amount(inputs, bill, dust_fee) }, ] if renewal_fee is not None: outputs.append( # burn address (when renewing) { "script_hex": make_pay_to_address_script(BLOCKSTORE_BURN_ADDRESS), "value": op_fee }) return outputs
def check(state_engine): global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2 # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered names = ['foo.test', 'bar.test'] wallet_keys_list = [wallet_keys, wallet_keys_2] zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]] for i in xrange(0, len(names)): name = names[i] wallet_payer = 3 * (i + 1) - 1 wallet_owner = 3 * (i + 1) wallet_data_pubkey = 3 * (i + 1) # same as owner key wallet_keys = wallet_keys_list[i] zonefile_hash = zonefile_hashes[i] preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script( wallets[wallet_owner].addr): print "name has wrong owner" return False # zonefile is NOT legacy user_zonefile = blockstack_client.zonefile.load_name_zonefile( name, zonefile_hash) if 'error' in user_zonefile: print json.dumps(user_zonefile, indent=4, sort_keys=True) return False if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): print "legacy still" print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # zonefile has no key zonefile_key = blockstack_client.user.user_zonefile_data_pubkey( user_zonefile) if zonefile_key is not None: print 'still have zonefile key' print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # still have all the right info user_profile = blockstack_client.profile.get_profile( name, user_zonefile=user_zonefile) if user_profile is None or 'error' in user_profile: if user_profile is not None: print json.dumps(user_profile, indent=4, sort_keys=True) else: print "\n\nprofile is None\n\n" return False # can get mutable data with owner address res = testlib.blockstack_cli_get_mutable("bar.test", "hello_world_mutable") if 'error' in res: print json.dumps(res, indent=4, sort_keys=True) return False if json.loads(res['data']) != {'hello': 'world'}: print 'invalid data: {}'.format(res['data']) return False # can get mutable data with explicit public key res = testlib.blockstack_cli_get_mutable( "bar.test", "hello_world_mutable", public_key=keylib.ECPrivateKey( wallet_keys_2['data_privkey']).public_key().to_hex()) print 'mutable: {}'.format(res) if 'error' in res: print json.dumps(res, indent=4, sort_keys=True) return False if json.loads(res['data']) != {'hello': 'world'}: print 'invalid data: {}'.format(res['data']) return False # can get immutable data by name res = testlib.blockstack_cli_get_immutable('foo.test', 'hello_world_immutable') print 'immutable by name: {}'.format(res) if 'error' in res: return res if json.loads(res['data']) != {'hello': 'world_immutable'}: print 'invalid immutable data: {}'.format(res['data']) return False # can get immutable data by hash hsh = res['hash'] res = testlib.blockstack_cli_get_immutable('foo.test', hsh) print 'immutable: {}'.format(res) if 'error' in res: return res if json.loads(res['data']) != {'hello': 'world_immutable'}: print 'invalid immutable data by hash: {}'.format(res['data']) return False return True
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name does not exist" return False # owned by the right address owner_address = wallets[3].addr if name_rec['address'] != owner_address or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(owner_address): print "sender is wrong" return False # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps( queue_info, indent=4, sort_keys=True) return False # have an update hash if 'value_hash' not in name_rec or name_rec.get('value_hash', None) is None: print "No value hash" return False # have a zonefile zonefile = testlib.blockstack_get_zonefile(name_rec['value_hash']) if zonefile is None or 'error' in zonefile: if zonefile is not None: print "zonefile lookup error: %s" % zonefile['error'] else: print "no zonefile returned" return False # hashes to this zonefile if blockstack_client.hash_zonefile(zonefile) != name_rec['value_hash']: print "wrong zonefile: %s != %s" % ( blockstack_client.hash_zonefile(zonefile), name_rec['value_hash']) return False # verify that the profile is there profile = testlib.blockstack_get_profile("foo.test") if profile is None or 'error' in profile: if profile is None: print "no profile returned" else: print "profile lookup error: %s" % profile['error'] return False return True
def check(state_engine): global wallet_keys, datasets, zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr): print "name has wrong owner" return False # have right hash if name_rec['value_hash'] != zonefile_hash: print "Invalid zonefile hash" return False # have no data test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy(test_proxy) for i in xrange(0, len(datasets)): print "get hello_world_%s" % (i + 1) dat = blockstack_client.get_mutable("foo.test", "hello_world_%s" % (i + 1)) if dat is not None and 'error' not in dat: print "still have '%s'\n%s" % ( "hello_world_%s" % (i + 1), json.dumps(dat, indent=4, sort_keys=True)) return False if 'error' in dat and dat['error'] != 'No such mutable datum': print json.dumps(dat, indent=4, sort_keys=True) return False return True
def check( state_engine ): global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2 # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered names = ['foo.test', 'bar.test'] wallet_keys_list = [wallet_keys, wallet_keys_2] zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]] for i in xrange(0, len(names)): name = names[i] wallet_payer = 3 * (i+1) - 1 wallet_owner = 3 * (i+1) wallet_data_pubkey = 3 * (i+1) # same as owner key wallet_keys = wallet_keys_list[i] zonefile_hash = zonefile_hashes[i] preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr): print "name has wrong owner" return False # zonefile is NOT legacy user_zonefile = blockstack_client.profile.load_name_zonefile( name, zonefile_hash ) if 'error' in user_zonefile: print json.dumps(user_zonefile, indent=4, sort_keys=True) return False if blockstack_profiles.is_profile_in_legacy_format( user_zonefile ): print "legacy still" print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # still have all the right info user_profile = blockstack_client.profile.load_name_profile( name, user_zonefile, wallets[wallet_data_pubkey].addr, wallets[wallet_owner].addr ) if user_profile is None: print "Unable to load user profile for %s (%s)" % (name, wallets[wallet_data_pubkey].pubkey_hex) return False if 'error' in user_profile: print json.dumps(user_profile, indent=4, sort_keys=True) return False # can fetch latest by name immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/" ) if 'error' in immutable_data: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if immutable_data['data'] != {'hello': 'world'}: print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'}) return False if immutable_data['hash'] != immutable_hash: print "immutable fetch-latest hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash) return False # can fetch by name and hash immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash ) if 'error' in immutable_data: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if immutable_data['data'] != {'hello': 'world'}: print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % (immutable_data['data'], type(immutable_data['data']), {'hello': 'world'}) return False if immutable_data['hash'] != immutable_hash: print "immutable fetch-by-hash mismatch: %s != %s" % (immutable_data['hash'], immutable_hash) return False # hash must match (if we put the wrong hash, it must fail) try: immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % ("0" * len(immutable_hash))) print "no error" print json.dumps(immutable_data, indent=4, sort_keys=True) return False except urllib2.URLError: pass # can list names and hashes immutable_data_list = get_data( "blockstack://foo.test/#immutable" ) if 'error' in immutable_data_list: print json.dumps(immutable_data, indent=4, sort_keys=True ) return False if len(immutable_data_list['data']) != 1: print "multiple immutable data" print json.dumps(immutable_data_list, indent=4, sort_keys=True ) return False if immutable_data_list['data'][0]['data_id'] != 'hello_world_immutable' or immutable_data_list['data'][0]['hash'] != immutable_hash: print "wrong data ID and/or hash" print json.dumps(immutable_data_list, indent=4, sort_keys=True ) return False # can fetch latest mutable by name mutable_data = get_data( "blockstack://bar.test/hello_world_mutable") if 'error' in mutable_data: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if mutable_data['data'] != {'hello': 'world'}: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if mutable_data['version'] != 1: print "wrong version: %s" % mutable_data['data']['version'] return False # can fetch by version mutable_data = get_data( "blockstack://bar.test/hello_world_mutable#1") if 'error' in mutable_data: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if mutable_data['data'] != {'hello': 'world'}: print json.dumps(mutable_data, indent=4, sort_keys=True) return False # will fail to fetch if we give the wrong version try: mutable_data = get_data("blockstack://bar.test/hello_world_mutable#2") print "mutable fetch by wrong version worked" print json.dumps(mutable_data, indent=4, sort_keys=True) return False except urllib2.URLError: pass # can list mutable data mutable_data_list = get_data( "blockstack://bar.test/#mutable" ) if 'error' in mutable_data_list: print json.dumps(mutable_data_list, indent=4, sort_keys=True ) return False if len(mutable_data_list) != 1: print "multiple mutable data" print json.dumps(mutable_data_list, indent=4, sort_keys=True ) return False if mutable_data_list['data'][0]['data_id'] != 'hello_world_mutable' or mutable_data_list['data'][0]['version'] != 1: print "wrong data id and/or version" print json.dumps(mutable_data_list, indent=4, sort_keys=True) return False return True
def check(state_engine): global snv_block_id_foo, snv_txid_bar, last_consensus # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered for i in xrange(0, len(wallets)): for name in ["foo.test", "bar.test", "baz.test"]: preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i + 1) % 5].addr) if preorder is not None: print "preordered" return False # but still registered for name in ["foo.test", "bar.test", "baz.test"]: name_rec = state_engine.get_name(name) if name_rec is None: print "no name" return False # updated, and data preserved name_rec = state_engine.get_name("baz.test") if name_rec['value_hash'] != "22" * 20: print "wrong value hash" return False # transferred name_rec = state_engine.get_name("bar.test") if name_rec['address'] != wallets[4].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): print "wrong owner" return False # snv lookup works # NOTE: cannot use serial numbers here, since a NAME_IMPORT does not contain a consensus hash. test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy(test_proxy) bitcoind = testlib.get_bitcoind() snv_rec = blockstack_client.snv_lookup("foo.test", snv_block_id_foo, last_consensus, proxy=test_proxy) if 'error' in snv_rec: print json.dumps(snv_rec, indent=4) print "failed to look up foo.test from consensus hash %s" % last_consensus return False # can use bar.test's NAME_TRANSFER txid to verify foo.test, since it has a consensus hash snv_rec_bar_tx = blockstack_client.snv_lookup("foo.test", snv_block_id_foo, snv_txid_bar, proxy=test_proxy) if 'error' in snv_rec_bar_tx: print json.dumps(snv_rec_bar_tx, indent=4) print "failed to look up foo.test from transaction %s" % snv_txid_bar return False if snv_rec != snv_rec_bar_tx: print "--------" print json.dumps(snv_rec, indent=4) print "" print json.dumps(snv_rec_bar_tx, indent=4) print "" print "Not equal" return False print snv_rec return True
def check(state_engine): global update_blocks, transfer_blocks, update_hashes, transfer_recipients, renew_blocks # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr) if preorder is not None: print json.dumps(name_rec, indent=4) return False # registered to new owner name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name rec is None" return False # updated if name_rec['value_hash'] != '04' * 20: print "invalid value hash" return False # transferred if name_rec['address'] != wallets[4].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[4].addr): print json.dumps(name_rec, indent=4) return False # historically updated and transferred and renewed if len(transfer_blocks) != len(update_blocks): print "test bug: did not transfer or update the right amount of times" return False for i in xrange(0, len(transfer_blocks)): transfer_block = transfer_blocks[i] transfer_recipient = transfer_recipients[i] historic_name_rec = state_engine.get_name_at("foo.test", transfer_block, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % transfer_block return False historic_name_rec = historic_name_rec[0] if historic_name_rec['opcode'] != 'NAME_TRANSFER': print "was not transfered at %s" % transfer_block return False if historic_name_rec[ 'address'] != transfer_recipient or historic_name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script( transfer_recipient): print "wrong address/sender, got %s, %s" % ( historic_name_rec['address'], historic_name_rec['sender']) return False for i in xrange(0, len(update_blocks)): update_block = update_blocks[i] update_hash = update_hashes[i] historic_name_rec = state_engine.get_name_at("foo.test", update_block, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "No name at %s" % update_block return False historic_name_rec = historic_name_rec[0] if historic_name_rec['opcode'] != 'NAME_UPDATE': print "was not updated at %s" % update_block return False if historic_name_rec.get('value_hash', None) != update_hash: print "wrong update hash: expected %s, got %s" % ( update_hash, historic_name_rec.get('value_hash', None)) return False for i in xrange(0, len(renew_blocks)): renew_block = renew_blocks[i] historic_name_rec = state_engine.get_name_at("foo.test", renew_block, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "name doesn't exist at %s" % renew_block return False historic_name_rec = historic_name_rec[0] if historic_name_rec['op'] != '::': print "name not renewed at %s" % renew_block return False return True
def check( state_engine ): global wallet_keys, wallet_keys_2, key_names, error, gpghome if error: print "Key operation failed." return False # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered names = ['foo.test', 'bar.test'] wallet_keys_list = [wallet_keys, wallet_keys_2] for i in xrange(0, len(names)): name = names[i] wallet_payer = 3 * (i+1) - 1 wallet_owner = 3 * (i+1) wallet_data_pubkey = 3 * (i+1) # same as owner key wallet_keys = wallet_keys_list[i] key_res = key_names[name] preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[wallet_owner].addr): print "name has wrong owner" return False # account listing exists, and other keys are deleted account_key_listing = blockstack_gpg.gpg_list_profile_keys( name ) secure_app_listing = blockstack_gpg.gpg_list_app_keys( name, "secure_messaging" ) less_secure_app_listing = blockstack_gpg.gpg_list_app_keys( name, "less-secure_messaging" ) if 'error' in account_key_listing: print json.dumps(account_key_listing) return False if len(account_key_listing) != 1: print "Invalid account keys:\n%s" % json.dumps(account_key_listing) return False key_id, key_url = account_key_listing[0]['identifier'], account_key_listing[0]['contentUrl'] if key_url != key_res[0]['key_url']: print "Key ID mismatch (account): %s != %s\nFull listing:\n%s\n\nKeys we generated:\n%s\n" % \ (key_url, key_res[0]['key_url'], account_key_listing[0], json.dumps(key_res, indent=4, sort_keys=True)) return False # immutable listings exist, and other keys are deleted if 'error' in secure_app_listing: print json.dumps(secure_app_listing) return False if len(secure_app_listing) != 1: print "Invalid immutable keys:\n%s" % json.dumps(secure_app_listing) return False key_id, key_url = secure_app_listing[0]['keyName'], secure_app_listing[0]['contentUrl'] if key_url != key_res[1]['key_url']: print "Key ID mismatch (immutable app): %s != %s\nFull listing:\n%s\n\nKeys we generated:\n%s\n" % \ (key_url, key_res[1]['key_url'], secure_app_listing[0], json.dumps(key_res, indent=4, sort_keys=True)) return False # mutable listings exist, and other keys are deleted if 'error' in less_secure_app_listing: print json.dumps(less_secure_app_listing) return False if len(less_secure_app_listing) != 1: print "Invalid mutable keys (mutable app):\n%s" % json.dumps(less_secure_app_listing) return False key_id, key_url = less_secure_app_listing[0]['keyName'], less_secure_app_listing[0]['contentUrl'] if key_url != key_res[2]['key_url']: print "Key ID mismatch: %s != %s\nFull listing:\n%s\n\nKeys we generated:\n%s\n" % \ (key_url, key_res[2]['key_url'], less_secure_app_listing[0], json.dumps(key_res, indent=4, sort_keys=True)) return False profile_key = blockstack_gpg.gpg_profile_get_key( name, account_key_listing[0]['keyName'], gpghome=gpghome ) if 'error' in profile_key: print "no key in account %s: %s" % (account_key_listing, profile_key['error']) return False secure_app_key = blockstack_gpg.gpg_app_get_key( name, "secure_messaging", secure_app_listing[0]['keyName'], \ immutable=True ) if 'error' in secure_app_key: print "no key in secure_messaging listing %s: %s" % (secure_app_listing, secure_app_key['error']) return False less_secure_app_key = blockstack_gpg.gpg_app_get_key( name, "less-secure_messaging", less_secure_app_listing[0]['keyName'] ) if 'error' in less_secure_app_key: print "no key in less-secure_messaging listing %s: %s" % (less_secure_app_listing, less_secure_app_key['error']) return False return True
def check(state_engine): global zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name does not exist" return False # owned by owner_address = wallets[4].addr if name_rec['address'] != owner_address or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(owner_address): print "sender is wrong" return False # value hash if name_rec['value_hash'] != zonefile_hash: print "wrong zonefile hash: %s != %s" % (name_rec['value_hash'], zonefile_hash) return False # replicated? zonefile = testlib.blockstack_get_zonefile(zonefile_hash) if 'error' in zonefile: print "zonefile error: %s" % zonefile['error'] return False # right hash? if blockstack_client.hash_zonefile(zonefile) != zonefile_hash: print "wrong zonefile: %s != %s" % ( blockstack_client.hash_zonefile(zonefile), zonefile_hash) return False # latest key? if not zonefile.has_key("txt"): print "no txt:\n%s" % json.dumps(zonefile, indent=4, sort_keys=True) return False if len(zonefile['txt']) != 2: print "wrong number of txt records:\n%s" % json.dumps( zonefile, indent=4, sort_keys=True) return False for txtrec in zonefile['txt']: if txtrec['name'] == 'pubkey': if new_data_pubkey not in txtrec['txt']: print "wrong pubkey:\n%s" % json.dumps( zonefile, indent=4, sort_keys=True) print "missing %s" % new_data_pubkey return False names_owned = testlib.blockstack_cli_names() if 'error' in names_owned: print "rpc names: %s" % names_owned['error'] return False # we still own the name if len(names_owned['names_owned']) != 1: print "owned: %s" % names_owned['names_owned'] return False # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps( queue_info, indent=4, sort_keys=True) return False return True
def check(state_engine): global wallet_keys, wallet_keys_2, datasets, zonefile_hash, zonefile_hash_2, datastore_name # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace not ready" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered names = ['foo.test', 'bar.test'] wallet_keys_list = [wallet_keys, wallet_keys_2] zonefile_hashes = [zonefile_hash[:], zonefile_hash_2[:]] for i in xrange(0, len(names)): name = names[i] wallet_payer = 3 * (i + 1) - 1 wallet_owner = 3 * (i + 1) wallet_data_pubkey = 3 * (i + 1) + 1 wallet_keys = wallet_keys_list[i] zonefile_hash = zonefile_hashes[i] preorder = state_engine.get_name_preorder( name, pybitcoin.make_pay_to_address_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script( wallets[wallet_owner].addr): print "name has wrong owner" return False # zonefile is NOT legacy user_zonefile = blockstack_client.load_name_zonefile( name, zonefile_hash) if 'error' in user_zonefile: print json.dumps(user_zonefile, indent=4, sort_keys=True) return False if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): print "legacy still" print json.dumps(user_zonefile, indent=4, sort_keys=True) return False # still have all the right info user_profile = blockstack_client.get_profile( name, user_zonefile=user_zonefile) if user_profile is None: print "Unable to load user profile for %s (%s)" % ( name, wallets[wallet_data_pubkey].pubkey_hex) return False if 'error' in user_profile: print json.dumps(user_profile, indent=4, sort_keys=True) return False # can fetch latest by name immutable_data = get_data("blockstack://hello_world_immutable.foo.test/") if 'error' in immutable_data: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if json.loads(immutable_data['data']) != {'hello': 'world'}: print "immutable fetch-latest mismatch:\n%s (%s)\n%s" % ( immutable_data['data'], type(immutable_data['data']), { 'hello': 'world' }) return False if immutable_data['hash'] != immutable_hash: print "immutable fetch-latest hash mismatch: %s != %s" % ( immutable_data['hash'], immutable_hash) return False # can fetch by name and hash immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % immutable_hash) if 'error' in immutable_data: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if json.loads(immutable_data['data']) != {'hello': 'world'}: print "immutable fetch-by-hash mismatch:\n%s (%s)\n%s" % ( immutable_data['data'], type(immutable_data['data']), { 'hello': 'world' }) return False if immutable_data['hash'] != immutable_hash: print "immutable fetch-by-hash mismatch: %s != %s" % ( immutable_data['hash'], immutable_hash) return False # hash must match (if we put the wrong hash, it must fail) try: immutable_data = get_data( "blockstack://hello_world_immutable.foo.test/#%s" % ("0" * len(immutable_hash))) print "no error" print json.dumps(immutable_data, indent=4, sort_keys=True) return False except urllib2.URLError: pass # can list names and hashes immutable_data_list = get_data("blockstack://foo.test/#immutable") if 'error' in immutable_data_list: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if len(immutable_data_list['data']) != 2: print "multiple immutable data" print json.dumps(immutable_data_list, indent=4, sort_keys=True) return False # order preserved if immutable_data_list['data'][0][ 'data_id'] != 'hello_world_immutable' or immutable_data_list[ 'data'][0]['hash'] != immutable_hash: print "wrong data ID and/or hash" print json.dumps(immutable_data_list, indent=4, sort_keys=True) return False device_id = blockstack_client.config.get_local_device_id() data_id = blockstack_client.storage.make_fq_data_id( device_id, 'hello_world_mutable') # can fetch latest mutable by name mutable_data = get_data("blockstack://bar.test/{}".format(data_id)) if 'error' in mutable_data: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if json.loads(mutable_data['data']) != {'hello': 'world'}: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if mutable_data['version'] != 1: print "wrong version: %s" % mutable_data['data']['version'] return False # can fetch by version mutable_data = get_data("blockstack://bar.test/{}#1".format(data_id)) if 'error' in mutable_data: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if json.loads(mutable_data['data']) != {'hello': 'world'}: print json.dumps(mutable_data, indent=4, sort_keys=True) return False # will fail to fetch if we give the wrong version try: mutable_data = get_data("blockstack://bar.test/{}#2".format(data_id)) print "mutable fetch by wrong version worked" print json.dumps(mutable_data, indent=4, sort_keys=True) return False except urllib2.URLError: pass # can fetch mutable data put by URL data_id = blockstack_client.storage.make_fq_data_id(device_id, 'foo_data2') mutable_data = get_data("blockstack://foo.test/{}".format(data_id)) if 'error' in mutable_data or 'data' not in mutable_data or 'version' not in mutable_data: print json.dumps(mutable_data, indent=4, sort_keys=True) return False if json.loads(mutable_data['data']) != {'hello2': 'world2'}: print "Invalid mutable data fetched from blockstack://foo.test/{}".format( data_id) print json.dumps(mutable_data, indent=4, sort_keys=True) return False # can fetch immutable data put by URL immutable_data = get_data("blockstack://foo_immutable.foo.test") if 'error' in immutable_data or 'hash' not in immutable_data: print json.dumps(immutable_data, indent=4, sort_keys=True) return False if json.loads(immutable_data['data']) != {'hello3': 'world3'}: print "Invalid immutable data fetched from blockstack://foo_immutable.foo.test" print json.dumps(immutable_data, indent=4, sort_keys=True) return False # can fetch files and directories mutable_data = get_data( "blockstack://{}@foo-app.com/hello_datastore".format(datastore_name)) if 'error' in mutable_data or 'file' not in mutable_data or mutable_data[ 'file']['idata'] != 'hello datastore': print 'Failed to get blockstack://{}@foo-app.com/hello_datastore'.format( datastore_name) print json.dumps(mutable_data, indent=4, sort_keys=True) return False mutable_data = get_data( "blockstack://{}@foo-app.com/hello_dir/".format(datastore_name)) if 'error' in mutable_data or 'dir' not in mutable_data or 'hello_dir_datastore' not in mutable_data[ 'dir']['idata'].keys(): print 'Failed to get blockstack://{}@foo-app.com/hello_dir/'.format( datastore_name) print json.dumps(mutable_data, indent=4, sort_keys=True) return False mutable_data = get_data( "blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore".format( datastore_name)) if 'error' in mutable_data or 'file' not in mutable_data or mutable_data[ 'file']['idata'] != 'hello dir datastore': print 'Failed to get blockstack://{}@foo-app.com/hello_dir/hello_dir_datastore'.format( datastore_name) print json.dumps(mutable_data, indent=4, sort_keys=True) return False return True
def check(state_engine): global update_block # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: return False ns = state_engine.get_namespace("test") if ns is None: return False if ns['namespace_id'] != 'test': return False # not preordered for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[i].addr), wallets[(i + 1) % 5].addr) if preorder is not None: print "preordered" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "no name" return False # no longer renewed if name_rec['last_renewed'] != name_rec['first_registered']: print name_rec['last_renewed'] print name_rec['first_registered'] return False # owned by if name_rec['address'] != wallets[8].addr or name_rec[ 'sender'] != pybitcoin.make_pay_to_address_script(wallets[8].addr): print "sender is wrong" return False # has new sender pubkey (from the payment key) if name_rec['sender_pubkey'] != wallets[7].pubkey_hex: print "wrong pubkey: %s != %s" % (name_rec['sender_pubkey'], wallets[7].pubkey_hex) return False # has no value hash if name_rec['value_hash'] is not None: print "still has value hash" return False # historically, name was updated historic_name_rec = state_engine.get_name_at("foo.test", update_block, include_expired=True) if historic_name_rec is None or len(historic_name_rec) == 0: print "no name at %s" % update_block return False historic_name_rec = historic_name_rec[0] # was updated? if historic_name_rec['opcode'] != 'NAME_UPDATE': print "not updated at %s" % update_block return False if historic_name_rec.get('value_hash', None) != '22' * 20: print "wrong value hash: %s" % historic_name_rec.get( 'value_hash', '(null)') return False return True
def check( state_engine ): global zonefile_hash # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is None: print "name does not exist" return False # owned by owner_address = wallets[4].addr if name_rec['address'] != owner_address or name_rec['sender'] != pybitcoin.make_pay_to_address_script(owner_address): print "sender is wrong" return False # value hash if name_rec['value_hash'] != zonefile_hash: print "wrong zonefile hash: %s != %s" % (name_rec['value_hash'], zonefile_hash) return False # replicated? zonefile = testlib.blockstack_get_zonefile( zonefile_hash ) if 'error' in zonefile: print "zonefile error: %s" % zonefile['error'] return False # right hash? if blockstack_client.hash_zonefile( zonefile ) != zonefile_hash: print "wrong zonefile: %s != %s" % (blockstack_client.hash_zonefile(zonefile), zonefile_hash) return False # doesn't show up in listing names_owned = testlib.blockstack_rpc_names() if 'error' in names_owned: print "rpc names: %s" % names_owned['error'] return False if len(names_owned['names_owned']) > 0: print "still owned: %s" % names_owned['names_owned'] return False # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps(queue_info, indent=4, sort_keys=True) return False return True