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": virtualchain.make_data_script(str(data)), "value": 0}, # new name owner output {"script": virtualchain.make_payment_script(new_name_owner_address), "value": dust_value}, # change output {"script": virtualchain.make_payment_script(change_address), "value": virtualchain.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) bill = op_fee if not pay_fee: # subsidized dust_fee = 0 op_fee = 0 bill = 0 return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # change address {"script": virtualchain.make_payment_script( change_addr ), "value": virtualchain.calculate_change_amount(inputs, bill, dust_fee)}, # burn address {"script": virtualchain.make_payment_script(BLOCKSTACK_BURN_ADDRESS), "value": op_fee} ]
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": virtualchain.make_data_script(str(data)), "value": 0}, # recipient output {"script": virtualchain.make_payment_script(recipient_address), "value": dust_value}, # update hash output {"script": virtualchain.make_payment_script(update_hash_b58), "value": dust_value}, # change output {"script": virtualchain.make_payment_script(sender_address), "value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)} ]
def mktx( amt, tx_fee, recipient_addr, privkey, message=None ): """ Make the transaction with the given fee """ change_addr = virtualchain.BitcoinPrivateKey(privkey).public_key().address() inputs = testlib.get_unspents(change_addr) change = virtualchain.calculate_change_amount(inputs, amt, tx_fee) outputs = [ {'script': virtualchain.make_payment_script(recipient_addr), 'value': amt}, ] if change > 0: # need change and tx fee outputs.append( {'script': virtualchain.make_payment_script(change_addr), "value": change} ) if message: outputs = [ {"script": virtualchain.make_data_script(binascii.hexlify(message)), "value": 0} ] + outputs serialized_tx = blockstack_client.tx.serialize_tx(inputs, outputs) signed_tx = blockstack_client.tx.sign_tx(serialized_tx, privkey) return signed_tx
def mktx(satoshis, fee): outputs = None if satoshis is None: # send all satoshis = sum([u['value'] for u in utxos]) print 'WARN: sending all of {} ({}) to {}'.format(payment_addr, satoshis, recipient_addr) outputs = [ {'script': virtualchain.make_payment_script(payment_addr), 'value': virtualchain.calculate_change_amount(utxos, 0, fee)}, ] else: outputs = [ {"script": virtualchain.make_payment_script(payment_addr), "value": satoshis}, {"script": virtualchain.make_payment_script(recipient_addr), "value": virtualchain.calculate_change_amount(utxos, satoshis, fee)}, ] txobj = { 'ins': utxos, 'outs': outputs, 'locktime': 0, 'version': 1 } # log.debug("serialize tx: {}".format(json.dumps(txobj, indent=4, sort_keys=True))) txstr = virtualchain.btc_tx_serialize(txobj) signed_txstr = virtualchain.tx_sign_all_unsigned_inputs(privkey, utxos, txstr) return signed_txstr
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, virtualchain.make_payment_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'] != virtualchain.make_payment_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 make_transaction(name, preorder_addr, register_addr, fee, consensus_hash, blockchain_client, tx_fee=0, subsidize=False, safety=True): """ Builds and broadcasts a preorder transaction. """ preorder_addr = str(preorder_addr) register_addr = str(register_addr) name = str(name) consensus_hash = str(consensus_hash) fee = int(fee) tx_fee = int(tx_fee) assert is_name_valid(name) assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2 inputs = None private_key_obj = None script_pubkey = None # to be mixed into preorder hash pay_fee = True if subsidize: pay_fee = False # tx only inputs = tx_get_unspents( preorder_addr, blockchain_client ) if safety: assert len(inputs) > 0, "No UTXOs for {}".format(preorder_addr) script_pubkey = virtualchain.make_payment_script( preorder_addr ) nulldata = build( name, script_pubkey, register_addr, consensus_hash) outputs = make_outputs(nulldata, inputs, preorder_addr, fee, tx_fee, pay_fee=pay_fee) return (inputs, outputs)
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[4].addr), wallets[3].addr, include_failed=True ) if preorder is not None: print "preorder exists" return False # registered name_rec_foo = state_engine.get_name( "foo.test" ) if name_rec_foo is None: print "name does not exist" return False # owned by if name_rec_foo['address'] != wallets[0].addr or name_rec_foo['sender'] != virtualchain.make_payment_script(wallets[0].addr): print "sender is wrong" return False return True
def make_outputs( data, inputs, change_address, tx_fee, pay_fee=True ): """ Make outputs for a revoke. Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = None op_fee = None dust_value = None if pay_fee: dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE else: # will be subsidized dust_fee = 0 op_fee = 0 dust_value = 0 return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # change output {"script": virtualchain.make_payment_script(change_address), "value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_transaction( namespace_id, register_addr, fee, consensus_hash, preorder_addr, blockchain_client, tx_fee=0, safety=True ): """ Propagate a namespace. Arguments: namespace_id human-readable (i.e. base-40) name of the namespace register_addr the addr of the key that will reveal the namespace (mixed into the preorder to prevent name preimage attack races). Must be a p2pkh address private_key the Bitcoin address that created this namespace, and can populate it. """ namespace_id = str(namespace_id) register_addr = str(register_addr) fee = int(fee) consensus_hash = str(consensus_hash) preorder_addr = str(preorder_addr) tx_fee = int(tx_fee) assert is_namespace_valid(namespace_id) assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2 assert keylib.b58check.b58check_version_byte( preorder_addr ) == virtualchain.version_byte, "Only p2pkh reveal addresses are supported (got {})".format(preorder_addr) script_pubkey = virtualchain.make_payment_script( preorder_addr ) nulldata = build( namespace_id, script_pubkey, register_addr, consensus_hash ) # get inputs and from address inputs = tx_get_unspents( preorder_addr, blockchain_client ) if safety: assert len(inputs) > 0 # build custom outputs here outputs = make_outputs(nulldata, inputs, preorder_addr, fee, tx_fee ) return (inputs, outputs)
def check( state_engine ): global synchronized if not synchronized: print "not synchronized" 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 for i in xrange(0, 10): name = 'foo_{}.test'.format(i) # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_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( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[3].addr): print "name has wrong owner" return False # updated if name_rec['value_hash'] is None: print "wrong value hash: %s" % name_rec['value_hash'] 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 # revoked if not name_rec['revoked']: print "not revoked" return False # owned by owner_address = wallets[4].addr if name_rec['address'] != owner_address or name_rec['sender'] != virtualchain.make_payment_script(owner_address): print "sender is wrong" return False # no zonefile hash if name_rec['value_hash'] is not None: print "still have value hash" return False # doesn't show up in listing names_owned = testlib.blockstack_cli_names() if 'error' in names_owned: print "rpc names: %s" % names_owned['error'] return False if len(names_owned['names_owned']) != 0: 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, error, index_file_data, resource_data config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG") assert config_path 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', 'bar.test'] wallet_keys_list = [wallet_keys] test_proxy = testlib.TestAPIProxy() owners = [3, 4] for i in xrange(0, len(names)): name = names[i] wallet_payer = 5 wallet_owner = owners[i] wallet_data_pubkey = 4 # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_script(wallets[wallet_payer].addr), wallets[wallet_owner].addr ) if preorder is not None: print "still have preorder" return False # registered name_rec = state_engine.get_name( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[wallet_owner].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[wallet_owner].addr): print "name {} has wrong owner".format(name) 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 names = ['foo.test', 'bar.test', 'baz.test', 'goo.test'] addresses = [wallets[2].addr, wallets[3].addr, wallets[4].addr, wallets[5].addr] zonefiles = ["Hello foo.test!", "Hello bar.test!", "Hello baz.test!", "Hello goo.test!"] for i in xrange(0, len(names)): name = names[i] owner_address = addresses[i] zonefile = zonefiles[i] # registered name_rec = state_engine.get_name( name ) if name_rec is None: print "name {} does not exist".format(name) return False # owned by the right address if name_rec['address'] != owner_address or name_rec['sender'] != virtualchain.make_payment_script(owner_address): print "sender is wrong for {}".format(name) return False # has the right zone file zf = testlib.blockstack_get_zonefile(name_rec['value_hash'], parse=False) if zf is None: print "no zonefile for {}".format(name) return False if zf != zonefile: print "zonefile mismatch: expected {}, got {}".format(zonefile, zf) 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 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[3].addr if name_rec['address'] != owner_address or name_rec['sender'] != virtualchain.make_payment_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 # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps(queue_info, indent=4, sort_keys=True) return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: 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'] != virtualchain.make_payment_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 ): # 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", virtualchain.make_payment_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 # updated, and data is preserved if name_rec['value_hash'] != '11' * 20: print "'foo.test' invalid value hash" return False # transferred if name_rec['address'] != wallets[4].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[4].addr): print "'foo.test' invalid owner" return False return True
def make_outputs( nulldata, inputs, change_addr, tx_fee=0 ): """ Make namespace-ready outputs """ return [ { "script": virtualchain.make_data_script(str(nulldata)), "value": 0 }, # change output { "script": virtualchain.make_payment_script(change_addr), "value": virtualchain.calculate_change_amount(inputs, 0, tx_fee + DEFAULT_OP_RETURN_FEE) } ]
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_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.test") 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'] != virtualchain.make_payment_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 for i in xrange(1, 4): name = 'foo{}.test'.format(i) # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr) if preorder is not None: print 'still have preorder: {}'.format(preorder) return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print 'did not get name {}'.format(name) return False # owned by if name_rec['address'] != wallets[3].addr or name_rec[ 'sender'] != virtualchain.make_payment_script(wallets[3].addr): print 'wrong address for {}: {}'.format(name, name_rec) return False return True
def make_transaction(name, preorder_addr, register_addr, fee, consensus_hash, blockchain_client, tx_fee=0, subsidize=False, safety=True, min_payment_confs=TX_MIN_CONFIRMATIONS): """ Builds and broadcasts a preorder transaction. """ preorder_addr = str(preorder_addr) register_addr = str(register_addr) name = str(name) consensus_hash = str(consensus_hash) fee = int(fee) tx_fee = int(tx_fee) assert is_name_valid(name) assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2 inputs = None private_key_obj = None script_pubkey = None # to be mixed into preorder hash pay_fee = True if subsidize: pay_fee = False # tx only inputs = tx_get_unspents(preorder_addr, blockchain_client, min_confirmations=min_payment_confs) if safety: assert len(inputs) > 0, "No UTXOs for {}".format(preorder_addr) script_pubkey = virtualchain.make_payment_script(preorder_addr) nulldata = build(name, script_pubkey, register_addr, consensus_hash) outputs = make_outputs(nulldata, inputs, preorder_addr, fee, tx_fee, pay_fee=pay_fee) return (inputs, outputs)
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'] != virtualchain.make_payment_script(owner_address): print "sender is wrong" return False # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps( queue_info, indent=4, sort_keys=True) return False # doesn't show up in listing names_owned = testlib.blockstack_cli_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 return True
def mktx(satoshis, fee): outputs = None if satoshis is None: # send all satoshis = sum([u['value'] for u in utxos]) print 'WARN: sending all of {} ({}) to {}'.format( payment_addr, satoshis, recipient_addr) outputs = [ { 'script': virtualchain.make_payment_script(payment_addr), 'value': virtualchain.calculate_change_amount(utxos, 0, fee) }, ] else: outputs = [ { "script": virtualchain.make_payment_script(payment_addr), "value": satoshis }, { "script": virtualchain.make_payment_script(recipient_addr), "value": virtualchain.calculate_change_amount(utxos, satoshis, fee) }, ] txobj = {'ins': utxos, 'outs': outputs, 'locktime': 0, 'version': 1} # log.debug("serialize tx: {}".format(json.dumps(txobj, indent=4, sort_keys=True))) txstr = virtualchain.btc_tx_serialize(txobj) signed_txstr = virtualchain.tx_sign_all_unsigned_inputs( privkey, utxos, txstr) return signed_txstr
def check(state_engine): global last_first_block, first_preorder # 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", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr) if preorder is not None: return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[3].addr), wallets[4].addr) if preorder is not None: return False # not registered name_rec = state_engine.get_name("foo.test") if name_rec is not None: print 'registered by accident' 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 # all preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr ) if preorder is None: return False preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[4].addr), wallets[5].addr ) if preorder is None: return False preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[0].addr), wallets[1].addr ) if preorder is None: return False preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[5].addr), wallets[2].addr ) if preorder is None: return False # neone registered name = state_engine.get_name( "foo.test" ) if name is not None: return False return True
def check( state_engine ): global owner_address, payment_address # 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", virtualchain.make_payment_script(payment_address), owner_address ) 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'] != owner_address or name_rec['sender'] != virtualchain.make_payment_script(owner_address): print "name has wrong owner" return False return True
def check( state_engine ): global first_name_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", virtualchain.make_payment_script(wallets[i].addr), wallets[(i+1)%5].addr ) if preorder is not None: print "preordered" return False # this is still preordered under this address preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[3].addr), wallets[0].addr ) if preorder is not None: print 'not preordered' return False # not registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is not None: print "name still registered" return False return True
def make_outputs(data, inputs, sender_addr, fee, tx_fee): """ 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 Raise ValueError if there are not enough inputs to make the transaction """ op_fee = max(fee, DEFAULT_DUST_FEE) dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee dust_value = DEFAULT_DUST_FEE bill = op_fee return [ # main output { "script_hex": make_op_return_script(str(data), format='hex'), "value": 0 }, # change address (can be subsidy key) { "script_hex": virtualchain.make_payment_script(sender_addr), "value": calculate_change_amount(inputs, bill, dust_fee) }, # burn address { "script_hex": virtualchain.make_payment_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 # not preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_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[3].addr or name_rec[ 'sender'] != virtualchain.make_payment_script(wallets[3].addr): return False # updated, but not the stale update hash if name_rec['value_hash'] != '11' * 20: return False return True
def make_outputs(nulldata, inputs, change_addr, fee=0, format='bin'): """ Make namespace-ready outputs """ return [ { "script_hex": make_op_return_script(nulldata, format=format), "value": 0 }, # change output { "script_hex": virtualchain.make_payment_script(change_addr), "value": calculate_change_amount(inputs, 0, fee) } ]
def make_outputs( data, inputs, sender_addr, burn_addr, fee, tx_fee, pay_fee=True ): """ 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 Raise ValueError if there are not enough inputs to make the transaction """ op_fee = max(fee, DEFAULT_DUST_FEE) dust_fee = (len(inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee dust_value = DEFAULT_DUST_FEE bill = 0 if pay_fee: bill = op_fee else: op_fee = 0 bill = 0 dust_fee = 0 return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # change address (can be subsidy key) {"script": virtualchain.make_payment_script(str(sender_addr)), "value": virtualchain.calculate_change_amount(inputs, bill, dust_fee)}, # burn address {"script": virtualchain.make_payment_script(str(burn_addr)), "value": op_fee} ]
def make_outputs( data, inputs, reveal_addr, change_addr, tx_fee): """ 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 Raise ValueError if there are not enough inputs to make the transaction """ total_to_send = DEFAULT_OP_RETURN_FEE + DEFAULT_DUST_FEE return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # register address {"script": virtualchain.make_payment_script(reveal_addr), "value": DEFAULT_DUST_FEE}, # change address {"script": virtualchain.make_payment_script(change_addr), "value": virtualchain.calculate_change_amount(inputs, total_to_send, DEFAULT_DUST_FEE * (len(inputs) + 2) + DEFAULT_OP_RETURN_FEE + tx_fee)}, ]
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 but revoked name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name doesn't exist" return False if not name_rec['revoked']: print "name not revoked" return False # owned by owner_address = wallets[3].addr if name_rec['address'] != owner_address or name_rec[ 'sender'] != virtualchain.make_payment_script(owner_address): print "sender is wrong" return False if name_rec['value_hash'] is not None: print "still have zonefile" return False # all queues are drained queue_info = testlib.ysi_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps( queue_info, indent=4, sort_keys=True) return False return True
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "'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", virtualchain.make_payment_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 # renewed if name_rec['last_renewed'] <= name_rec['first_registered']: print "not renewed" return False # updated, and data is preserved if name_rec['value_hash'] != '11' * 20: print "'foo.test' invalid value hash" return False # transferred if name_rec['address'] != wallets[4].addr: print "'foo.test' invalid owner" return False return True
def check(state_engine): global snv_block_id, 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 preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr) if preorder is not None: return False # not registered name_rec = state_engine.get_name("foo.test") if name_rec is not None: print "Not expired:" print json.dumps(name_rec, indent=4) return False # snv lookup works test_proxy = testlib.TestAPIProxy() ysi_client.set_default_proxy(test_proxy) snv_rec = ysi_client.snv_lookup("foo.test", snv_block_id, last_consensus, proxy=test_proxy) if 'error' in snv_rec: print json.dumps(snv_rec, indent=4) print "Expected (%s, %s) from (%s, %s)" % (snv_block_id, snv_consensus, lastblock, lastconsensus) return False print snv_rec return True
def make_outputs(nulldata, inputs, change_addr, tx_fee=0): """ Make namespace-ready outputs """ return [ { "script": virtualchain.make_data_script(str(nulldata)), "value": 0 }, # change output { "script": virtualchain.make_payment_script(change_addr), "value": virtualchain.calculate_change_amount( inputs, 0, tx_fee + DEFAULT_OP_RETURN_FEE) } ]
def tx_make_subsidization_output(payer_utxo_inputs, payer_address, op_fee, dust_fee): """ Given the set of utxo inputs for both the client and payer, as well as the client's desired tx outputs, generate the inputs and outputs that will cause the payer to pay the operation's fees and dust fees. The client should send its own address as an input, with the same amount of BTC as the output. Return the payer output to include in the transaction on success, which should pay for the operation's fee and dust. Raise ValueError it here aren't enough inputs to subsidize """ return { 'script': virtualchain.make_payment_script(payer_address), 'value': virtualchain.calculate_change_amount(payer_utxo_inputs, op_fee, int(round(dust_fee))) }
def make_transaction(namespace_id, register_addr, fee, consensus_hash, preorder_addr, blockchain_client, tx_fee=0, safety=True): """ Propagate a namespace. Arguments: namespace_id human-readable (i.e. base-40) name of the namespace register_addr the addr of the key that will reveal the namespace (mixed into the preorder to prevent name preimage attack races). Must be a p2pkh address private_key the Bitcoin address that created this namespace, and can populate it. """ namespace_id = str(namespace_id) register_addr = str(register_addr) fee = int(fee) consensus_hash = str(consensus_hash) preorder_addr = str(preorder_addr) tx_fee = int(tx_fee) assert is_namespace_valid(namespace_id) assert len(consensus_hash) == LENGTH_CONSENSUS_HASH * 2 assert keylib.b58check.b58check_version_byte( preorder_addr ) == virtualchain.version_byte, "Only p2pkh reveal addresses are supported (got {})".format( preorder_addr) script_pubkey = virtualchain.make_payment_script(preorder_addr) nulldata = build(namespace_id, script_pubkey, register_addr, consensus_hash) # get inputs and from address inputs = tx_get_unspents(preorder_addr, blockchain_client) if safety: assert len(inputs) > 0 # build custom outputs here outputs = make_outputs(nulldata, inputs, preorder_addr, fee, tx_fee) return (inputs, 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 for i in xrange(0, len(wallets)): preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[i].addr), wallets[(i + 1) % 5].addr) if preorder is not None: print "preordered" return False # registered, but revoked name_rec = state_engine.get_name("foo.test") if name_rec is None: print "no name" return False if not name_rec['revoked']: print "not revoked" return False if name_rec['value_hash'] is not None: print "still have value hash" return False # renewed (12 blocks later, starting from renewal time) if name_rec['last_renewed'] - 12 != name_rec['first_registered']: print name_rec['last_renewed'] print name_rec['first_registered'] return False return True
def check(state_engine): global fail_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", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr) if preorder is not None: return False # not registered name_rec = state_engine.get_name("foo.test") if name_rec is not None: 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_REGISTRATION': print "accepted opcode %s at %s" % (historic_name_rec['opcode'], fb) return False return True
def make_outputs( data, inputs, change_address, tx_fee ): """ Make outputs for an announcement. Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE return [ # main output {"script_hex": make_op_return_script(str(data), format='hex'), "value": 0}, # change output {"script_hex": virtualchain.make_payment_script(change_address), "value": calculate_change_amount(inputs, op_fee, dust_fee)} ]
def make_outputs(data, inputs, change_address, tx_fee, pay_fee=True, dust_included=False): """ Make outputs for an update. Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = None op_fee = None dust_value = None total_tx_fee = tx_fee if pay_fee: if not dust_included: total_tx_fee += (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE op_fee = DEFAULT_DUST_FEE dust_value = DEFAULT_DUST_FEE else: # will be subsidized total_tx_fee = 0 op_fee = 0 dust_value = 0 return [ # main output { "script": virtualchain.make_data_script(str(data)), "value": 0 }, # change output { "script": virtualchain.make_payment_script(change_address), "value": virtualchain.calculate_change_amount(inputs, op_fee, total_tx_fee) } ]
def make_outputs( data, inputs, change_address, tx_fee, pay_fee=True ): """ Make outputs for an announcement. Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = (len(inputs) + 1) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = DEFAULT_DUST_FEE if not pay_fee: op_fee = 0 dust_fee = 0 return [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # change output {"script": virtualchain.make_payment_script(change_address), "value": virtualchain.calculate_change_amount(inputs, op_fee, dust_fee)} ]
def check( state_engine ): global wallet_keys, error, index_file_data, resource_data config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG") assert config_path names = ['bar.test'] owners = [ new_addr ] payers = [ wallets[1].addr ] test_proxy = testlib.TestAPIProxy() for i in xrange(0, len(names)): name = names[i] wallet_payer = 5 wallet_owner = 3 + i for name, wallet_payer, wallet_owner in zip(names, payers, owners): # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_script(wallet_payer), wallet_owner) 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'] != owners[i]: print "name {} has wrong owner".format(name) return False return True
def tx_sign_singlesig(tx, idx, private_key_info, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2pkh input Return the signed transaction TODO: move to virtualchain NOTE: implemented here instead of bitcoin, since bitcoin.sign() can cause a stack overflow while converting the private key to a public key. """ pk = virtualchain.BitcoinPrivateKey(str(private_key_info)) pubk = pk.public_key() pub = pubk.to_hex() addr = pubk.address() script = virtualchain.make_payment_script(addr) sig = tx_make_input_signature(tx, idx, script, private_key_info, hashcode) txobj = bitcoin.deserialize(str(tx)) txobj['ins'][idx]['script'] = bitcoin.serialize_script([sig, pub]) return bitcoin.serialize(txobj)
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 # all foo's should succeed for i in xrange(0, 25): namerec = state_engine.get_name("foo%s.test" % i) if namerec is None: print "missing foo%s.test" % i return False if namerec['address'] != wallets[3].addr or namerec[ 'sender'] != virtualchain.make_payment_script(wallets[3].addr): print "foo%s.test not owned by %s" % (i, wallets[3].addr) return False # last foo should fail namerec = state_engine.get_name("foo26.test") if namerec is not None: print "foo26.test exists" return False # bar should not be registered namerec = state_engine.get_name("bar.test") if namerec is not None: print "bar.test exists" return False return True
def scenario( wallets, **kw ): testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_ready( "test", wallets[1].privkey ) testlib.next_block( **kw ) testlib.blockstack_name_preorder( "foo.test", wallets[2].privkey, wallets[3].addr, wallet=wallets[3] ) testlib.next_block( **kw ) # should fail, since multisig isn't enabled yet testlib.blockstack_name_register( "foo.test", wallets[2].privkey, wallets[3].addr, wallet=wallets[3] ) testlib.next_block( **kw ) testlib.expect_snv_fail_at( "foo.test", testlib.get_current_block(**kw)) # foo.test should be preordered (in both 0.13 and 0.14) state_engine = testlib.get_state_engine() preorder = state_engine.get_name_preorder("foo.test", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr) if preorder is None: print "foo.test not preordered" return False # epoch changes here testlib.blockstack_name_preorder( "bar.test", wallets[3].privkey, wallets[4].addr ) testlib.next_block( **kw ) testlib.blockstack_name_register( "foo.test", wallets[2].privkey, wallets[3].addr, wallet=wallets[3] ) testlib.next_block( **kw ) testlib.blockstack_name_register( "bar.test", wallets[3].privkey, wallets[4].addr ) testlib.next_block( **kw )
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'] != virtualchain.make_payment_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 update_blocks, transfer_blocks, update_hashes, transfer_recipients # 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", virtualchain.make_payment_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'] != virtualchain.make_payment_script(wallets[4].addr): print json.dumps(name_rec, indent=4) return False # historically updated and transferred 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'] != virtualchain.make_payment_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 return True
def check( state_engine ): global synchronized, atlasdb_path, value_hashes, working_dir, atlas_dir if not synchronized: print "not synchronized" 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 for i in xrange(0, 10): name = 'foo_{}.test'.format(i) # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_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( name ) if name_rec is None: print "name does not exist" return False # owned if name_rec['address'] != wallets[3].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[3].addr): print "name has wrong owner" return False # updated if name_rec['value_hash'] is None: print "wrong value hash: %s" % name_rec['value_hash'] return False for i in xrange(0, len(value_hashes)): name = 'foo_{}.test'.format(i) value_hash = value_hashes[i] # atlas logic tried storage (either this node or the atlas peer) zfinfo = blockstack.atlasdb_get_zonefile( value_hash, path=atlasdb_path ) if not zfinfo['tried_storage']: zfinfo2 = blockstack.atlasdb_get_zonefile( value_hash, path=os.path.join(atlas_dir, "localhost:17000/atlas.db") ) if not zfinfo2['tried_storage']: print "didn't get zonefile from storage: test node: %s, atlas peer: %s" % (zfinfo, zfinfo2) return False # zonefile stored to disk? zfdata = blockstack_client.zonefile.load_name_zonefile(name, value_hash, storage_drivers=['disk']) if zfdata is None: print "failed to load zonefile %s from disk" % value_hash return False # zonefile cached? cached_zonefile = blockstack.lib.storage.get_cached_zonefile( value_hash, zonefile_dir=zonefile_dir ) if cached_zonefile is None: print "no cached zonefile %s in %s" % (value_hash, zonefile_dir) return False if cached_zonefile != zfdata: print "zonefile mismatch" print "from disk:\n%s\n" % json.dumps(zfdata, indent=4, sort_keys=True) print "from cache:\n%s\n" % json.dumps(cached_zonefile, indent=4, sort_keys=True) return False return True
def check( state_engine ): global zonefile_hash, error if error: print "exiting with error" return False # 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[3].addr if name_rec['address'] != owner_address or name_rec['sender'] != virtualchain.make_payment_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 # 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 # renewed? # final balance is correct? name_fee = blockstack_server.price_name( "foo", ns, new_expire_block ) preorder_dust_fees = 3 * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE register_dust_fees = 4 * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE renewal_dust_fees = 3 * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE total_fee = name_fee * 2 + preorder_dust_fees + register_dust_fees + renewal_dust_fees payment_address = wallets[2].addr # TODO: check 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'] != virtualchain.make_payment_script(owner_address): print "sender is wrong" print "%s != %s or %s != %s" % (name_rec['address'], owner_address, name_rec['sender'], virtualchain.make_payment_script(owner_address)) return False # all queues are drained queue_info = testlib.blockstack_client_queue_state() if len(queue_info) > 0: print "Still in queue:\n%s" % json.dumps(queue_info, indent=4, sort_keys=True) return False # have an update hash if 'value_hash' not in name_rec or name_rec.get('value_hash', None) is None: print "No value hash" return False # have a zonefile zonefile = testlib.blockstack_get_zonefile( name_rec['value_hash'] ) if zonefile is None or 'error' in zonefile: if zonefile is not None: print "zonefile lookup error: %s" % zonefile['error'] else: print "no zonefile returned" return False # hashes to this zonefile if blockstack_client.hash_zonefile( zonefile ) != name_rec['value_hash']: print "wrong zonefile: %s != %s" % (blockstack_client.hash_zonefile(zonefile), name_rec['value_hash']) return False # verify that the profile is there profile = testlib.blockstack_get_profile( "foo.test" ) if profile is None or 'error' in profile: if profile is None: print "no profile returned" else: print "profile lookup error: %s" % profile['error'] return False return True
def check( state_engine ): original_price = 6400000 curr_price = original_price * blockstack_server.lib.config.get_epoch_price_multiplier( 262, "test" ) # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[2].addr), wallets[3].addr, include_failed=True ) if preorder is not None: print "preorder exists" return False # not preordered preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(wallets[3].addr), wallets[4].addr, include_failed=True ) if preorder is not None: print "preorder exists" return False # registered name_rec_foo = state_engine.get_name( "foo.test" ) if name_rec_foo is None: print "name does not exist" return False # registered name_rec_bar = state_engine.get_name( "bar.test" ) if name_rec_bar is None: print "name does not exist" return False # owned by if name_rec_foo['address'] != wallets[3].addr or name_rec_foo['sender'] != virtualchain.make_payment_script(wallets[3].addr): print "sender is wrong" return False # owned by if name_rec_bar['address'] != wallets[4].addr or name_rec_bar['sender'] != virtualchain.make_payment_script(wallets[4].addr): print "sender is wrong" return False # paid epoch 1 fee if abs(name_rec_foo['op_fee'] - original_price) >= 10e-8: print "foo paid %s, expected %s" % (name_rec_foo['op_fee'], original_price) return False # paid epoch 2 fee if abs(name_rec_bar['op_fee'] - curr_price) >= 10e-8: print "bar paid %s, expected %s" % (name_rec_bar['op_fee'], curr_price) return False return True
def make_outputs( data, change_inputs, register_addr, change_addr, tx_fee, renewal_fee=None, pay_fee=True): """ 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 Raise ValueError if there are not enough inputs to make the transaction """ dust_fee = None dust_value = DEFAULT_DUST_FEE op_fee = None bill = None if pay_fee: # sender pays if renewal_fee is not None: # renewing dust_fee = (len(change_inputs) + 3) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = max(renewal_fee, DEFAULT_DUST_FEE) bill = op_fee else: # registering dust_fee = (len(change_inputs) + 2) * DEFAULT_DUST_FEE + DEFAULT_OP_RETURN_FEE + tx_fee op_fee = 0 bill = DEFAULT_DUST_FEE * 2 else: # subsidized by another address if renewal_fee is not None: # renewing dust_fee = 0 op_fee = max(renewal_fee, DEFAULT_DUST_FEE) bill = 0 else: # registering dust_fee = 0 op_fee = 0 bill = 0 outputs = [ # main output {"script": virtualchain.make_data_script(str(data)), "value": 0}, # register address {"script": virtualchain.make_payment_script(register_addr), "value": dust_value}, # change address (can be the subsidy address) {"script": virtualchain.make_payment_script(change_addr), "value": virtualchain.calculate_change_amount(change_inputs, bill, dust_fee)}, ] if renewal_fee is not None: outputs.append( # burn address (when renewing) {"script": virtualchain.make_payment_script(BLOCKSTACK_BURN_ADDRESS), "value": op_fee} ) return outputs
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'] != virtualchain.make_payment_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