def scenario(wallets, **kw): # pass 100 stacks around in a circle. new_keys = [wallets[0].privkey] + [ virtualchain.lib.ecdsalib.ecdsa_private_key().to_hex() for i in range(0, 3) ] for i in range(0, 3 * len(new_keys)): new_addr = virtualchain.get_privkey_address(new_keys[(i + 1) % len(new_keys)]) cur_addr = virtualchain.get_privkey_address(new_keys[i % len(new_keys)]) initial_new_balance_info = json.loads( testlib.nodejs_cli('balance', new_addr)) initial_cur_balance_info = json.loads( testlib.nodejs_cli('balance', cur_addr)) print '\n initial new balance info: {} \n'.format( initial_new_balance_info) if 'STACKS' not in initial_new_balance_info: initial_new_balance_info['STACKS'] = 0 if i > 0: testlib.send_funds(wallets[0].privkey, 800000, cur_addr) testlib.send_funds(wallets[0].privkey, 800000, new_addr) testlib.blockstack_send_tokens(new_addr, 'STACKS', 100, new_keys[i % len(new_keys)]) testlib.next_block(**kw) balance_info = json.loads(testlib.nodejs_cli('balance', new_addr)) assert int(balance_info['STACKS']) == 100 + int( initial_new_balance_info['STACKS']) if (i + 1) % len(new_keys) != 0: assert int(balance_info['STACKS']) == 100 balance_info = json.loads(testlib.nodejs_cli('balance', cur_addr)) assert int(balance_info['STACKS']) == int( initial_cur_balance_info['STACKS']) - 100 if i % len(new_keys) != 0: assert int(balance_info['STACKS']) == 0 # each *new* address has 6 history items -- three spends, three receives for new_key in new_keys[1:]: new_addr = virtualchain.get_privkey_address(new_key) history = requests.get( 'http://localhost:16268/v1/accounts/{}/history?page=0'.format( new_addr)).json() # should have gotten 3 debits for 100, and 3 credits for 100 assert int(history[0]['credit_value']) == 300, history assert int(history[0]['debit_value']) == 300, history assert len(history) == 6, history
def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None, } if data_privkey is not None: if not virtualchain.is_singlesig(data_privkey): raise ValueError('Invalid data key info') pk_data = ecdsa_private_key(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if virtualchain.is_multisig(owner_privkey): pks = owner_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript(owner_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['owner_privkey'] = multisig_info ret['owner_addresses'] = [virtualchain.get_privkey_address(multisig_info)] elif virtualchain.is_singlesig(owner_privkey): pk_owner = ecdsa_private_key(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner ret['owner_addresses'] = [virtualchain.get_privkey_address(pk_owner)] else: raise ValueError('Invalid owner key info') if payment_privkey is None: return ret if virtualchain.is_multisig(payment_privkey): pks = payment_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript(payment_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['payment_privkey'] = multisig_info ret['payment_addresses'] = [virtualchain.get_privkey_address(multisig_info)] elif virtualchain.is_singlesig(payment_privkey): pk_payment = ecdsa_private_key(payment_privkey).to_hex() ret['payment_privkey'] = pk_payment ret['payment_addresses'] = [virtualchain.get_privkey_address(pk_payment)] else: raise ValueError('Invalid payment key info') ret['data_pubkey'] = ecdsa_private_key(ret['data_privkey']).public_key().to_hex() ret['data_pubkeys'] = [ret['data_pubkey']] return ret
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode( virtualchain.get_privkey_address(pk2)) preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(addr), addr2) if preorder is not None: print "preorder exists" return False # registered name_rec = state_engine.get_name("foo.test") if name_rec is None: print "name does not exist" return False # owned by if name_rec['address'] != addr2 or name_rec[ 'sender'] != virtualchain.make_payment_script(addr2): print "sender is wrong" return False # paid with Stacks stacks_price = blockstack.lib.scripts.price_name_stacks( 'foo', ns, state_engine.lastblock) if name_rec['token_fee'] != stacks_price: print 'paid wrong token fee' print 'expected {} ({}), got {} ({})'.format( stacks_price, type(stacks_price), name_rec['token_fee'], type(name_rec['token_fee'])) return False if name_rec['op_fee'] > 5500: # dust minimum print 'paid in BTC ({})'.format(name_rec['op_fee']) return False # renewed on third attempt if name_rec['first_registered'] + 3 != name_rec['last_renewed']: print 'renewed at the wrong time: {} + 3 != {}'.format( name_rec['first_registered'], name_rec['last_renewed']) return False return True
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2)) preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(addr), wallets[3].addr ) if preorder is not None: print "preorder exists for foo.test" return False # not registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is not None: print "name exists" return False # not preordered preorder = state_engine.get_name_preorder('bar.test', virtualchain.make_payment_script(addr2), wallets[3].addr) if preorder is not None: print 'preorder exists for bar.test' return False # registered name_rec = state_engine.get_name('bar.test') if name_rec is None: print 'bar.test does not exist' return False # owned if name_rec['address'] != wallets[3].addr or name_rec['sender'] != virtualchain.make_payment_script(wallets[3].addr): print 'bar.test not owned' return False # overpaid if name_rec['op_fee'] > 5500 or name_rec['token_fee'] != blockstack.lib.scripts.price_name_stacks('bar', ns, state_engine.lastblock) + 1: print 'bar.test improperly paid' return False return True
def make_wallet(password, config_path=CONFIG_PATH, payment_privkey_info=None, owner_privkey_info=None, data_privkey_info=None, test_legacy=False, encrypt=True): """ Make a new, encrypted wallet structure. The owner and payment keys will be 2-of-3 multisig key bundles. The data keypair will be a single-key bundle. Return the new wallet on success. Return {'error': ...} on failure """ if test_legacy and not BLOCKSTACK_TEST: raise Exception("Not in testing but tried to make a legacy wallet") # default to 2-of-3 multisig key info if data isn't given payment_privkey_info = virtualchain.make_multisig_wallet(2, 3) if payment_privkey_info is None and not test_legacy else payment_privkey_info owner_privkey_info = virtualchain.make_multisig_wallet(2, 3) if owner_privkey_info is None and not test_legacy else owner_privkey_info data_privkey_info = ecdsa_private_key().to_hex() if data_privkey_info is None and not test_legacy else data_privkey_info decrypted_wallet = { 'owner_addresses': [virtualchain.get_privkey_address(owner_privkey_info)], 'owner_privkey': owner_privkey_info, 'payment_addresses': [virtualchain.get_privkey_address(payment_privkey_info)], 'payment_privkey': payment_privkey_info, 'data_pubkey': ecdsa_private_key(data_privkey_info).public_key().to_hex(), 'data_pubkeys': [ecdsa_private_key(data_privkey_info).public_key().to_hex()], 'data_privkey': data_privkey_info, 'version': SERIES_VERSION, } if not test_legacy: jsonschema.validate(decrypted_wallet, WALLET_SCHEMA_CURRENT) if encrypt: encrypted_wallet = encrypt_wallet(decrypted_wallet, password, test_legacy=test_legacy) if 'error' in encrypted_wallet: return encrypted_wallet # sanity check try: jsonschema.validate(encrypted_wallet, ENCRYPTED_WALLET_SCHEMA_CURRENT) except ValidationError as ve: if test_legacy: # no data key is permitted assert BLOCKSTACK_TEST jsonschema.validate(encrypted_wallet, ENCRYPTED_WALLET_SCHEMA_CURRENT_NODATAKEY) else: raise return encrypted_wallet else: return decrypted_wallet
def scenario( wallets, **kw ): global pk, pk2 testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey, version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR) testlib.next_block( **kw ) testlib.blockstack_namespace_ready( "test", wallets[1].privkey ) testlib.next_block( **kw ) # pay for a name in a v1 namespace with Stacks addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2)) # calculate the cost of doing so namespace = testlib.get_state_engine().get_namespace('test') stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw)) btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw)) print '' print 'price of {} in Stacks is {}'.format('foo.test', stacks_price) print 'price of {} in BTC is {}'.format('foo.test', btc_price) print '' testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 2, wallets[0].privkey) testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey) testlib.send_funds(wallets[0].privkey, 3*btc_price, addr) testlib.send_funds(wallets[0].privkey, 3*btc_price, addr2) testlib.next_block(**kw) # preorder/register using Stacks testlib.blockstack_name_preorder( "foo.test", pk, addr2) testlib.blockstack_name_preorder( "bar.test", pk2, addr) testlib.next_block( **kw ) testlib.blockstack_name_register( "foo.test", pk, addr2 ) testlib.blockstack_name_register( "bar.test", pk2, addr ) testlib.next_block( **kw ) testlib.next_block( **kw ) testlib.next_block( **kw ) testlib.next_block( **kw ) # end of pay to namespace creator # renew using more stacks than we have (should fail) # bar.test should succeed testlib.blockstack_name_renew('foo.test', pk2, price={'units': 'STACKS', 'amount': stacks_price + 2}, expect_fail=True, safety_checks=False) testlib.blockstack_name_renew('bar.test', pk, price={'units': 'STACKS', 'amount': stacks_price + 1}) testlib.next_block(**kw) testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw))
def convert_funds_to_segwit(payment_key, tx_fee): # convert payment key to bech32 addr = virtualchain.address_reencode(virtualchain.get_privkey_address(payment_key)) pubk = virtualchain.lib.ecdsalib.ecdsa_private_key(payment_key, compressed=True).public_key().to_hex() addrhash = virtualchain.lib.hashing.bin_hash160(pubk.decode('hex')).encode('hex') segwit_addr = virtualchain.segwit_addr_encode(addrhash.decode('hex'), hrp='bcrt') # fund the segwit address, and then use the same payment key to send the transaction fund_inputs = testlib.get_utxos(addr) fund_outputs = [ {"script": '0014' + addrhash, "value": sum(inp['value'] for inp in fund_inputs) - tx_fee}, ] fund_prev_outputs = [{'out_script': inp['out_script'], 'value': inp['value']} for inp in fund_inputs] fund_serialized_tx = testlib.serialize_tx(fund_inputs, fund_outputs) fund_signed_tx = virtualchain.tx_sign_all_unsigned_inputs(payment_key, fund_prev_outputs, fund_serialized_tx) print fund_signed_tx res = testlib.broadcast_transaction(fund_signed_tx) assert 'error' not in res, res res.update({ 'utxos': fund_outputs }) return res
def check( state_engine ): # not revealed, but ready ns = state_engine.get_namespace_reveal( "test" ) if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace( "test" ) if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # not preordered addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) preorder = state_engine.get_name_preorder( "foo.test", virtualchain.make_payment_script(addr), wallets[3].addr ) if preorder is not None: print "preorder exists" return False # not registered name_rec = state_engine.get_name( "foo.test" ) if name_rec is not None: print "name exists" return False return True
def _tx_pay_btc(txhex, privk, burn_price, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS): tx = virtualchain.btc_tx_deserialize(txhex) # up the burn amount tx['outs'][3]['script'] = virtualchain.btc_make_payment_script(burn_addr) tx['outs'][3]['value'] = burn_price tx['outs'][4]['value'] -= burn_price # re-sign for i in tx['ins']: i['script'] = '' txhex = virtualchain.btc_tx_serialize(tx) _addr = virtualchain.address_reencode(virtualchain.get_privkey_address(privk)) txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(privk, testlib.get_utxos(_addr), txhex) # re-sign the last output with the payment key tx_signed = virtualchain.btc_tx_deserialize(txhex_signed) tx_signed['ins'][-1]['script'] = '' txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(testlib.get_default_payment_wallet().privkey, testlib.get_utxos(testlib.get_default_payment_wallet().addr), virtualchain.btc_tx_serialize(tx_signed)) print txhex_signed res = testlib.broadcast_transaction(txhex_signed) assert 'error' not in res return res
def scenario( wallets, **kw ): global wallet_keys, wallet_keys_2, error, index_file_data, resource_data empty_key = ECPrivateKey().to_hex() wallet_keys = testlib.ysi_client_initialize_wallet( "0123456789abcdef", wallets[1].privkey, wallets[2].privkey, wallets[0].privkey) test_proxy = testlib.TestAPIProxy() ysi_client.set_default_proxy( test_proxy ) testlib.ysi_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.ysi_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey ) testlib.next_block( **kw ) testlib.ysi_namespace_ready( "test", wallets[1].privkey ) testlib.next_block( **kw ) # tell serialization-checker that value_hash can be ignored here print "BLOCKSTACK_SERIALIZATION_CHECK_IGNORE value_hash" sys.stdout.flush() testlib.next_block( **kw ) config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG", None) config_dir = os.path.dirname(config_path) conf = ysi_client.get_config(config_path) assert conf api_pass = conf['api_password'] # let's do a withdraw of all res = testlib.ysi_REST_call('POST', '/v1/wallet/balance', None, api_pass=api_pass, data= { 'address' : virtualchain.get_privkey_address(empty_key), }) if 'error' in res['response']: res['test'] = 'Failed to perform withdraw' print json.dumps(res) error = True return False for i in xrange (0, 1): testlib.next_block( **kw ) print 'Waiting for the withdraw to go through' res = testlib.ysi_REST_call('GET', '/v1/wallet/balance/0', None, api_pass=api_pass) if 'error' in res['response']: res['test'] = 'Failed to get wallet balance' print json.dumps(res) error = True return False res = testlib.ysi_REST_call('GET', '/v1/wallet/balance/0', None, api_pass=api_pass) if 'error' in res['response']: res['test'] = 'Failed to get wallet balance' print json.dumps(res) error = True return False
def scenario( wallets, **kw ): global pk, pk2 testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey, version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR) testlib.next_block( **kw ) testlib.blockstack_namespace_ready( "test", wallets[1].privkey ) testlib.next_block( **kw ) # pay for a name in a v1 namespace with Stacks addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2)) # calculate the cost of doing so namespace = testlib.get_state_engine().get_namespace('test') stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw)) btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw)) print '' print 'price of {} in Stacks is {}'.format('foo.test', stacks_price) print '' testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 1, wallets[0].privkey) testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey) testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr) # deliberately insufficient funds for ordering the name in BTC testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr2) # deliberately insufficient funds for ordering the name in BTC testlib.next_block(**kw) testlib.next_block(**kw) testlib.next_block(**kw) testlib.next_block(**kw) # move beyond pay-to-creator requirement # preorder/register using Stacks, but overpay (foo.test fails, bar.test succeeds) testlib.blockstack_name_preorder( "foo.test", pk, wallets[3].addr, price={'units': 'STACKS', 'amount': stacks_price + 2}, expect_fail=True, safety_checks=False) testlib.blockstack_name_preorder( "bar.test", pk2, wallets[3].addr, price={'units': 'STACKS', 'amount': stacks_price + 1}) testlib.next_block( **kw ) testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw)) testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr) # deliberately insufficient funds for ordering the name in BTC testlib.send_funds(wallets[0].privkey, btc_price - 5500 - 1, addr2) # deliberately insufficient funds for ordering the name in BTC testlib.blockstack_name_register( "foo.test", pk, wallets[3].addr ) # should fail testlib.blockstack_name_register( "bar.test", pk2, wallets[3].addr ) # should succeed testlib.next_block( **kw ) testlib.expect_snv_fail_at('foo.test', testlib.get_current_block(**kw))
def send_as_segwit_bech32(txhex, payment_key): print 'txhex: {}'.format(txhex) # get op-return data tx = virtualchain.btc_tx_deserialize(txhex) payload = tx['outs'][0]['script'] print json.dumps(tx, indent=4, sort_keys=True) # convert payment key to bech32 addr = virtualchain.address_reencode(virtualchain.get_privkey_address(payment_key)) pubk = virtualchain.lib.ecdsalib.ecdsa_private_key(payment_key, compressed=True).public_key().to_hex() addrhash = virtualchain.lib.hashing.bin_hash160(pubk.decode('hex')).encode('hex') segwit_addr = virtualchain.segwit_addr_encode(addrhash.decode('hex'), hrp='bcrt') print 'privk = {}'.format(payment_key) print 'pubk = {}'.format(pubk) print 'addr = {}'.format(addr) print 'segwit addr = {}'.format(segwit_addr) print 'script = 00{}'.format(addrhash) tx_fee = 5500 res = convert_funds_to_segwit(payment_key, tx_fee) fund_outputs = res['utxos'] fund_txid = res['tx_hash'] new_tx = { 'locktime': 0, 'version': 2, 'ins': [ {'outpoint': {'hash': fund_txid, 'index': 0}, 'script': '', 'witness_script': '', 'sequence': 4294967295}, ], 'outs': [ {'script': tx['outs'][0]['script'], 'value': tx['outs'][0]['value']}, {'script': '0014' + addrhash, 'value': fund_outputs[0]['value'] - tx_fee * 2}, {'script': tx['outs'][2]['script'], 'value': tx['outs'][2]['value']} ] } unsigned_txhex = virtualchain.btc_tx_serialize(new_tx) print 'unsigned: {}'.format(unsigned_txhex) pk_segwit = virtualchain.make_segwit_info(payment_key) print json.dumps(pk_segwit, indent=4, sort_keys=True) signed_txhex = virtualchain.tx_sign_input(unsigned_txhex, 0, fund_outputs[0]['script'], fund_outputs[0]['value'], pk_segwit, segwit=True, scriptsig_type='p2wpkh') print 'signed: {}'.format(signed_txhex) res = testlib.broadcast_transaction(signed_txhex) assert 'error' not in res return res
def scenario(wallets, **kw): testlib.blockstack_namespace_preorder("test", wallets[1].addr, wallets[0].privkey) testlib.next_block(**kw) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 10, 10, wallets[0].privkey, version_bits=blockstack.NAMESPACE_VERSION_PAY_WITH_STACKS) testlib.next_block(**kw) testlib.blockstack_namespace_ready("test", wallets[1].privkey) testlib.next_block(**kw) balances = testlib.get_wallet_balances(wallets[2]) assert balances[wallets[2].addr][STACKS] == 0 # should fail--not enough stacks testlib.blockstack_name_preorder("foo.test", wallets[2].privkey, wallets[3].addr, safety_checks=False, expect_fail=True) testlib.next_block(**kw) name_cost = testlib.blockstack_get_name_token_cost('foo.test') assert name_cost['units'] == STACKS assert name_cost['amount'] > 0 # send tokens and preorder multiple times in the block # should all succeed, BUT: force them to go in order through UTXO chaining for i in range(0, 5): name_recipient_privkey = wallets[-(i + 1)].privkey name_recipient_addr = virtualchain.address_reencode( virtualchain.get_privkey_address(name_recipient_privkey)) testlib.blockstack_send_tokens(name_recipient_addr, "STACKS", name_cost['amount'], wallets[0].privkey) testlib.send_funds(wallets[0].privkey, 1000000, name_recipient_addr) testlib.blockstack_name_preorder("foo_{}.test".format(i), name_recipient_privkey, wallets[3].addr, safety_checks=False) testlib.blockstack_name_register("foo_{}.test".format(i), name_recipient_privkey, wallets[3].addr, safety_checks=False) testlib.next_block(**kw)
def scenario( wallets, **kw ): global pk, pk2 testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_ready( "test", wallets[1].privkey ) testlib.next_block( **kw ) # pay for a name in a v1 namespace with Stacks addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode(virtualchain.get_privkey_address(pk2)) # calculate the cost of doing so namespace = testlib.get_state_engine().get_namespace('test') stacks_price = blockstack.lib.scripts.price_name_stacks('foo', namespace, testlib.get_current_block(**kw)) btc_price = blockstack.lib.scripts.price_name('foo', namespace, testlib.get_current_block(**kw)) print '' print 'price of {} in Stacks is {}'.format('foo.test', stacks_price) print 'price of {} in BTC is {}'.format('foo.test', btc_price) print '' testlib.blockstack_send_tokens(addr, "STACKS", stacks_price + 1, wallets[0].privkey) testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price + 1, wallets[0].privkey) testlib.send_funds(wallets[0].privkey, 2*btc_price, addr) testlib.send_funds(wallets[0].privkey, 2*btc_price, addr2) testlib.next_block(**kw) # preorder/register using Stacks testlib.blockstack_name_preorder( "foo.test", pk, addr2, price={'units': 'STACKS', 'amount': stacks_price + 1}) testlib.next_block( **kw ) testlib.blockstack_name_register( "foo.test", pk, addr2 ) testlib.next_block( **kw ) # renew using Stacks testlib.blockstack_name_renew('foo.test', pk2, price={'units': 'STACKS', 'amount': stacks_price + 1}) testlib.next_block(**kw)
def decrypt_private_key_info(privkey_info, password): """ LEGACY COMPATIBILITY CODE Decrypt a particular private key info bundle. It can be either a single-signature private key, or a multisig key bundle. Return {'address': ..., 'private_key_info': ...} on success. Return {'error': ...} on error. """ from .backend.crypto.utils import aes_decrypt ret = {} if is_encrypted_multisig(privkey_info): ret = decrypt_multisig_info(privkey_info, password) if 'error' in ret: return { 'error': 'Failed to decrypt multisig wallet: {}'.format(ret['error']) } address = virtualchain.get_privkey_address(ret) return {'address': address, 'private_key_info': ret} if is_encrypted_singlesig(privkey_info): try: hex_password = hexlify(password) pk = aes_decrypt(privkey_info, hex_password) pk = ecdsa_private_key(pk).to_hex() except Exception as e: if BLOCKSTACK_TEST: log.exception(e) return {'error': 'Invalid password'} address = virtualchain.get_privkey_address(pk) return {'address': address, 'private_key_info': pk} return {'error': 'Invalid encrypted private key info'}
def _convert_key(given_privkey, key_type): if virtualchain.is_multisig(given_privkey): pks = given_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( given_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['{}_privkey'.format(key_type)] = multisig_info ret['{}_addresses'.format(key_type)] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(given_privkey): pk = ecdsa_private_key(given_privkey).to_hex() ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [ virtualchain.get_privkey_address(pk) ] elif virtualchain.btc_is_singlesig_segwit(given_privkey): pk = virtualchain.make_segwit_info( virtualchain.get_singlesig_privkey(given_privkey)) ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [pk['address']] elif virtualchain.btc_is_multisig_segwit(given_privkey): pks = given_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( given_privkey['redeem_script']) assert m <= len(pks) pk = virtualchain.make_multisig_segwit_info(m, pks) ret['{}_privkey'.format(key_type)] = pk ret['{}_addresses'.format(key_type)] = [pk['address']] else: raise ValueError('Invalid owner key info')
def tx_get_address_and_utxos(private_key_info, utxo_client, address=None): """ Get information about a private key (or a set of private keys used for multisig). Return (payer_address, payer_utxos) on success. UTXOs will be in BTC, not satoshis! """ if private_key_info is None: # just go with the address unspents = get_unspents(address, utxo_client) return addr, unspents addr = virtualchain.get_privkey_address(private_key_info) payer_utxos = get_unspents(addr, utxo_client) return addr, payer_utxos
def decrypt_private_key_info(privkey_info, password): """ LEGACY COMPATIBILITY CODE Decrypt a particular private key info bundle. It can be either a single-signature private key, or a multisig key bundle. Return {'address': ..., 'private_key_info': ...} on success. Return {'error': ...} on error. """ from .backend.crypto.utils import aes_decrypt ret = {} if is_encrypted_multisig(privkey_info): ret = decrypt_multisig_info(privkey_info, password) if 'error' in ret: return {'error': 'Failed to decrypt multisig wallet: {}'.format(ret['error'])} address = virtualchain.get_privkey_address(ret) return {'address': address, 'private_key_info': ret} if is_encrypted_singlesig(privkey_info): try: hex_password = hexlify(password) pk = aes_decrypt(privkey_info, hex_password) pk = ecdsa_private_key(pk).to_hex() except Exception as e: if BLOCKSTACK_TEST: log.exception(e) return {'error': 'Invalid password'} address = virtualchain.get_privkey_address(pk) return {'address': address, 'private_key_info': pk} return {'error': 'Invalid encrypted private key info'}
def tx_get_address_and_utxos(private_key_info, utxo_client, address=None): """ Get information about a private key (or a set of private keys used for multisig). Return (payer_address, payer_utxos) on success. UTXOs will be in BTC, not satoshis! """ if private_key_info is None: # just go with the address unspents = get_unspents(address, utxo_client) return address, unspents addr = virtualchain.get_privkey_address(private_key_info) payer_utxos = get_unspents(addr, utxo_client) return addr, payer_utxos
def scenario( wallets, **kw ): # try to spend tokens to ourselves testlib.blockstack_send_tokens(wallets[0].addr, "STACKS", 100000, wallets[0].privkey, safety_checks=False, expect_fail=True) testlib.next_block(**kw) # end of 689 assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0 # try to spend more tokens than we have testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 600001, wallets[0].privkey, safety_checks=False, expect_fail=True) testlib.next_block(**kw) assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0 # try to spend tokens that don't exist testlib.blockstack_send_tokens(wallets[1].addr, "noop", 600000, wallets[0].privkey, safety_checks=False, expect_fail=True) testlib.next_block(**kw) assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0 # try to spend tokens with an invalid consensus hash (note that this is epoch 4) testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 600000, wallets[0].privkey, consensus_hash='00' * 16, safety_checks=False, expect_fail=True) testlib.next_block(**kw) assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0 # try to spend tokens from an account that doesn't exist pk = virtualchain.lib.ecdsalib.ecdsa_private_key().to_hex() addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) testlib.send_funds(wallets[0].privkey, 10240000, addr) testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 100000, pk, safety_checks=False, expect_fail=True) testlib.next_block(**kw) assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0 # try to send 0 tokens testlib.blockstack_send_tokens(wallets[1].addr, "STACKS", 0, wallets[0].privkey, safety_checks=False, expect_fail=True) testlib.next_block(**kw) assert virtualchain.lib.indexer.StateEngine.get_block_statistics(testlib.get_current_block(**kw))['num_processed_ops'] == 0
def replace_output_with_bech32(txhex, output_index, payment_key, addrhash): print 'txhex: {}'.format(txhex) tx = virtualchain.btc_tx_deserialize(txhex) new_tx = { 'locktime': 0, 'version': 1, 'ins': tx['ins'], 'outs': tx['outs'], } for inp in new_tx['ins']: inp['script'] = '' inp['witness_script'] = '' new_tx['outs'][output_index] = { 'script': '0014' + addrhash, 'value': tx['outs'][output_index]['value'] } unsigned_txhex = virtualchain.btc_tx_serialize(new_tx) print 'unsigned: {}'.format(unsigned_txhex) addr = virtualchain.address_reencode( virtualchain.get_privkey_address(payment_key)) utxos = testlib.get_utxos(addr) prev_outputs = [{ 'out_script': inp['out_script'], 'value': inp['value'] } for inp in utxos] signed_txhex = virtualchain.tx_sign_all_unsigned_inputs( payment_key, prev_outputs, unsigned_txhex) print 'signed: {}'.format(signed_txhex) res = testlib.broadcast_transaction(signed_txhex) assert 'error' not in res return res
def _tx_pay_btc(txhex, privk, btc_paid, burn_addr): tx = virtualchain.btc_tx_deserialize(txhex) # up the burn amount btc_price = blockstack.lib.scripts.price_name('baz', namespace, testlib.get_current_block(**kw)) tx['outs'][2]['script'] = virtualchain.btc_make_payment_script(burn_addr) tx['outs'][2]['value'] = btc_paid tx['outs'][1]['value'] -= btc_paid # re-sign for i in tx['ins']: i['script'] = '' txhex = virtualchain.btc_tx_serialize(tx) _addr = virtualchain.address_reencode(virtualchain.get_privkey_address(privk)) txhex_signed = virtualchain.tx_sign_all_unsigned_inputs(privk, testlib.get_utxos(_addr), txhex) print txhex_signed res = testlib.broadcast_transaction(txhex_signed) assert 'error' not in res, res['error'] return res
def encrypt_wallet(decrypted_wallet, password, test_legacy=False): """ Encrypt the wallet. Return the encrypted dict on success Return {'error': ...} on error """ if test_legacy: assert BLOCKSTACK_TEST, 'test_legacy only works in test mode' # must be conformant to the current schema if not test_legacy: jsonschema.validate(decrypted_wallet, WALLET_SCHEMA_CURRENT) owner_address = virtualchain.get_privkey_address(decrypted_wallet['owner_privkey']) payment_address = virtualchain.get_privkey_address(decrypted_wallet['payment_privkey']) data_pubkey = None data_privkey_info = None if decrypted_wallet.has_key('data_privkey'): # make sure data key is hex encoded data_privkey_info = decrypted_wallet.get('data_privkey', None) if not test_legacy: assert data_privkey_info if data_privkey_info: if not is_singlesig_hex(data_privkey_info): data_privkey_info = ecdsa_private_key(data_privkey_info).to_hex() if not virtualchain.is_singlesig(data_privkey_info): log.error('Invalid data private key') return {'error': 'Invalid data private key'} data_pubkey = ecdsa_private_key(data_privkey_info).public_key().to_hex() wallet = { 'owner_addresses': [owner_address], 'payment_addresses': decrypted_wallet['payment_addresses'], 'version': decrypted_wallet['version'], 'enc': None, # to be filled in } if data_pubkey: wallet['data_pubkey'] = data_pubkey wallet['data_pubkeys'] = [data_pubkey] wallet_enc = { 'owner_privkey': decrypted_wallet['owner_privkey'], 'payment_privkey': decrypted_wallet['payment_privkey'], } if data_privkey_info: wallet_enc['data_privkey'] = data_privkey_info # extra sanity check: make sure that when re-combined with the wallet, # we're still valid recombined_wallet = copy.deepcopy(wallet) recombined_wallet.update(wallet_enc) try: jsonschema.validate(recombined_wallet, WALLET_SCHEMA_CURRENT) except ValidationError as ve: if test_legacy: # no data key is allowed if we're testing the absence of a data key jsonschema.validate(recombined_wallet, WALLET_SCHEMA_CURRENT_NODATAKEY) else: raise # good to go! # encrypt secrets wallet_secret_str = json.dumps(wallet_enc, sort_keys=True) password_hex = hexlify(password) encrypted_secret_str = aes_encrypt(wallet_secret_str, password_hex) # fulfill wallet wallet['enc'] = encrypted_secret_str # sanity check try: jsonschema.validate(wallet, ENCRYPTED_WALLET_SCHEMA_CURRENT) except ValidationError as ve: if test_legacy: jsonschema.validate(wallet, ENCRYPTED_WALLET_SCHEMA_CURRENT_NODATAKEY) else: raise return wallet
def scenario(wallets, **kw): global pk testlib.blockstack_namespace_preorder("test", wallets[1].addr, wallets[0].privkey) testlib.next_block(**kw) # all names below are the same price testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1, 1, wallets[0].privkey, version_bits=blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR) testlib.next_block(**kw) testlib.blockstack_namespace_ready("test", wallets[1].privkey) testlib.next_block(**kw) # pay for a name in a v1 namespace with Stacks addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) # calculate the cost of doing so namespace = testlib.get_state_engine().get_namespace('test') stacks_price = blockstack.lib.scripts.price_name_stacks( 'foo', namespace, testlib.get_current_block(**kw)) btc_price = blockstack.lib.scripts.price_name( 'foo', namespace, testlib.get_current_block(**kw)) print '' print 'price of {} in Stacks is {}'.format('foo.test', stacks_price) print '' testlib.blockstack_send_tokens(addr, "STACKS", stacks_price * 5, wallets[0].privkey) testlib.send_funds(wallets[0].privkey, btc_price * 10, addr) testlib.next_block(**kw) # preorder/register using Stacks (preorders should fail since we're using the wrong burn address for tokens) testlib.blockstack_name_preorder("foo.test", pk, wallets[3].addr, price={ 'units': 'STACKS', 'amount': stacks_price }, burn_addr=wallets[0].addr, safety_checks=False, expect_fail=True) testlib.blockstack_name_preorder("bar.test", pk, wallets[3].addr, price={ 'units': 'STACKS', 'amount': stacks_price - 1 }, burn_addr=wallets[0].addr, safety_checks=False, expect_fail=True) testlib.next_block(**kw) op_info = virtualchain.lib.indexer.StateEngine.get_block_statistics( testlib.get_current_block(**kw)) if op_info['num_processed_ops'] > 0: print 'handled ops this block' print op_info return False # preorder/register using Stacks (preorders should be accepted, but bar2 will fail to register) testlib.blockstack_name_preorder( "foo2.test", pk, wallets[2].addr, price={ 'units': 'STACKS', 'amount': stacks_price }, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS, safety_checks=False) testlib.blockstack_name_preorder( "bar2.test", pk, wallets[2].addr, price={ 'units': 'STACKS', 'amount': stacks_price - 1 }, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS, safety_checks=False) testlib.next_block(**kw) # preorder/register using Stacks (preorders should succeed now, but bar3.test will fail to register since we're not paying enough stacks) testlib.blockstack_name_register( "bar2.test", pk, wallets[2].addr ) # should fail at this point, since the preorder sent to the wrong burn address testlib.blockstack_name_register( "foo2.test", pk, wallets[2].addr ) # should fail at this point, since the preorder sent to the wrong burn address testlib.blockstack_name_preorder( "foo3.test", pk, wallets[2].addr, price={ 'units': 'STACKS', 'amount': stacks_price }, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS, safety_checks=False) testlib.blockstack_name_preorder( "bar3.test", pk, wallets[2].addr, price={ 'units': 'STACKS', 'amount': stacks_price - 1 }, burn_addr=blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS, safety_checks=False) testlib.next_block(**kw) testlib.expect_snv_fail_at('bar2.test', testlib.get_current_block(**kw)) testlib.expect_snv_fail_at('foo2.test', testlib.get_current_block(**kw)) # tokens are not yet accepted if testlib.get_state_engine().get_name('bar2.test'): print 'registered bar2.test' return False if testlib.get_state_engine().get_name('foo2.test'): print 'registered foo2.test' return False # preorder/register using Stacks (should succeed without safety checks or overrides) testlib.blockstack_name_preorder("foo.test", pk, wallets[2].addr, price={ 'units': 'STACKS', 'amount': stacks_price }) testlib.next_block(**kw) testlib.blockstack_name_register("foo.test", pk, wallets[2].addr) # should succeed testlib.blockstack_name_register("bar2.test", pk, wallets[2].addr) # should fail testlib.blockstack_name_register("foo2.test", pk, wallets[2].addr) # should succeed testlib.blockstack_name_register("bar3.test", pk, wallets[2].addr) # should fail testlib.blockstack_name_register("foo3.test", pk, wallets[2].addr) # should succeed testlib.next_block(**kw) testlib.expect_snv_fail_at('bar2.test', testlib.get_current_block(**kw)) testlib.expect_snv_fail_at('bar3.test', testlib.get_current_block(**kw)) if testlib.get_state_engine().get_name('bar2.test'): print 'registered bar2.test' return False if testlib.get_state_engine().get_name('bar3.test'): print 'registered bar3.test' return False
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False if ns['version'] != blockstack.lib.config.NAMESPACE_VERSION_PAY_TO_CREATOR: print 'wrong version' return False addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) for name in [ 'foo.test', 'bar2.test', 'foo2.test', 'bar3.test', 'foo3.test' ]: # not preordered, unless bar2.test stacks_price = blockstack.lib.scripts.price_name_stacks( name.split('.')[0], ns, state_engine.lastblock) preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_script(addr), wallets[2].addr) if name == 'bar2.test' or name == 'bar3.test': if preorder is None: print 'missing {} preorder'.format(name) return False if preorder['token_fee'] != stacks_price - 1: print 'wrong token fee for {}'.format(name) return False if preorder['op_fee'] > 5500: print 'paid too much btc' return False continue else: if preorder is not None: print "preorder exists for {}".format(name) return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print "name does not exist" return False # owned by if name_rec['address'] != wallets[2].addr or name_rec[ 'sender'] != virtualchain.make_payment_script(wallets[2].addr): print "sender is wrong" return False # paid with Stacks in all 3 cases, and not Bitcoin if name_rec['token_fee'] != stacks_price: print 'paid wrong token fee' print 'expected {} ({}), got {} ({})'.format( stacks_price, type(stacks_price), name_rec['token_fee'], type(name_rec['token_fee'])) return False if name_rec['op_fee'] > 5500: # dust minimum print 'paid in BTC ({})'.format(name_rec['op_fee']) return False return True
testlib.Wallet( "5J39aXEeHh9LwfQ4Gy5Vieo7sbqiUMBXkPH7SaMHixJhSSBpAqz", 100000000000 ), testlib.Wallet( "5K9LmMQskQ9jP1p7dyieLDAeB6vsAj4GK8dmGNJAXS1qHDqnWhP", 100000000000 ), testlib.Wallet( "5KcNen67ERBuvz2f649t9F2o1ddTjC5pVUEqcMtbxNgHqgxG2gZ", 100000000000 ), testlib.Wallet( "5KMbNjgZt29V6VNbcAmebaUT2CZMxqSridtM46jv4NkKTP8DHdV", 100000000000 ), ] consensus = "17ac43c1d8549c3181b200f1bf97eb7d" wallet_keys = None wallet_keys_2 = None error = False index_file_data = "<html><head></head><body>foo.test hello world</body></html>" resource_data = "hello world" new_key = "cPo24qGYz76xSbUCug6e8LzmzLGJPZoowQC7fCVPLN2tzCUJgfcW" new_addr = virtualchain.get_privkey_address(new_key) # "mqnupoveYRrSHmrxFT9nQQEZt3RLsetbBQ" insanity_key = "cSCyE5Q1AFVyDAL8LkHo1sFMVqmwdvFcCbGJ71xEvto2Nrtzjm67" def scenario( wallets, **kw ): global wallet_keys, wallet_keys_2, error, index_file_data, resource_data wallet_keys = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[5].privkey, wallets[3].privkey, wallets[4].privkey ) test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy( test_proxy ) testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw ) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey )
testlib.Wallet( "5J39aXEeHh9LwfQ4Gy5Vieo7sbqiUMBXkPH7SaMHixJhSSBpAqz", 100000000000 ), testlib.Wallet( "5K9LmMQskQ9jP1p7dyieLDAeB6vsAj4GK8dmGNJAXS1qHDqnWhP", 100000000000 ), testlib.Wallet( "5KcNen67ERBuvz2f649t9F2o1ddTjC5pVUEqcMtbxNgHqgxG2gZ", 100000000000 ), testlib.Wallet( "5KMbNjgZt29V6VNbcAmebaUT2CZMxqSridtM46jv4NkKTP8DHdV", 100000000000 ), ] consensus = "17ac43c1d8549c3181b200f1bf97eb7d" wallet_keys = None wallet_keys_2 = None error = False index_file_data = "<html><head></head><body>foo.test hello world</body></html>" resource_data = "hello world" new_key = "cPo24qGYz76xSbUCug6e8LzmzLGJPZoowQC7fCVPLN2tzCUJgfcW" new_addr = virtualchain.get_privkey_address(new_key) # "mqnupoveYRrSHmrxFT9nQQEZt3RLsetbBQ" insanity_key = "cSCyE5Q1AFVyDAL8LkHo1sFMVqmwdvFcCbGJ71xEvto2Nrtzjm67" def scenario( wallets, **kw ): global wallet_keys, wallet_keys_2, error, index_file_data, resource_data wallet_keys = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[5].privkey, wallets[3].privkey, wallets[4].privkey ) test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy( test_proxy ) testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey ) testlib.next_block( **kw )
def scenario(wallets, **kw): global wallet_keys, wallet_keys_2, error, index_file_data, resource_data empty_key = ECPrivateKey().to_hex() wallet_keys = testlib.ysi_client_initialize_wallet("0123456789abcdef", empty_key, empty_key, empty_key) test_proxy = testlib.TestAPIProxy() ysi_client.set_default_proxy(test_proxy) testlib.ysi_namespace_preorder("test", wallets[1].addr, wallets[0].privkey) testlib.next_block(**kw) testlib.ysi_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 10, 10, wallets[0].privkey) testlib.next_block(**kw) testlib.ysi_namespace_ready("test", wallets[1].privkey) testlib.next_block(**kw) # tell serialization-checker that value_hash can be ignored here print "BLOCKSTACK_SERIALIZATION_CHECK_IGNORE value_hash" sys.stdout.flush() testlib.next_block(**kw) config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG", None) config_dir = os.path.dirname(config_path) conf = ysi_client.get_config(config_path) assert conf api_pass = conf['api_password'] payment_key = wallets[1].privkey # make zonefile for recipient driver_urls = ysi_client.storage.make_mutable_data_urls( 'bar.test', use_only=['dht', 'disk']) zonefile = ysi_client.zonefile.make_empty_zonefile('bar.test', wallets[4].pubkey_hex, urls=driver_urls) zonefile_txt = ysi_zones.make_zone_file(zonefile, origin='bar.test', ttl=3600) no_key_postage = {'name': 'bar.test', 'zonefile': zonefile_txt} key_postage = dict(no_key_postage) key_postage['payment_key'] = payment_key key_postage['owner_key'] = new_key res = testlib.ysi_REST_call('POST', '/v1/names', None, api_pass=api_pass, data=no_key_postage) if 'error' not in res['response']: print "Successfully registered user with should-have-been-bad keys" print res return False # let's do a small withdraw res = testlib.ysi_REST_call( 'POST', '/v1/wallet/balance', None, api_pass=api_pass, data={ 'address': virtualchain.get_privkey_address(empty_key), 'amount': int(1e4), 'payment_key': payment_key }) if 'error' in res['response']: res['test'] = 'Failed to perform withdraw' print json.dumps(res) error = True return False for i in xrange(0, 1): testlib.next_block(**kw) print 'Waiting for the withdraw to go through' res = testlib.ysi_REST_call('GET', '/v1/wallet/balance/0', None, api_pass=api_pass) if 'error' in res['response']: res['test'] = 'Failed to get wallet balance' print json.dumps(res) error = True return False if int(res['response']['balance']['satoshis']) <= 0: res['test'] = 'Wallet balance did not increment!' print json.dumps(res) error = True return False res = testlib.ysi_REST_call('POST', '/v1/names', None, api_pass=api_pass, data=key_postage) if 'error' in res['response']: res['test'] = 'Failed to register user' print json.dumps(res) error = True return False print "Registering bar.test" for i in xrange(0, 6): testlib.next_block(**kw) if not res: return False # wait for the preorder to get confirmed for i in xrange(0, 4): testlib.next_block(**kw) # wait for register to go through print 'Wait for register to be submitted' time.sleep(10) # wait for the register to get confirmed for i in xrange(0, 6): testlib.next_block(**kw) res = testlib.verify_in_queue(None, 'bar.test', 'register', None, api_pass=api_pass) if not res: return False for i in xrange(0, 4): testlib.next_block(**kw) print 'Wait for update to be submitted' time.sleep(10) # wait for update to get confirmed for i in xrange(0, 6): testlib.next_block(**kw) res = testlib.verify_in_queue(None, 'bar.test', 'update', None, api_pass=api_pass) if not res: print res print "update error in first update" return False for i in xrange(0, 4): testlib.next_block(**kw) print 'Wait for zonefile to be sent' time.sleep(10) res = testlib.ysi_REST_call("GET", "/v1/names/bar.test", None, api_pass=api_pass) if 'error' in res or res['http_status'] != 200: res['test'] = 'Failed to get name bar.test' print json.dumps(res) return False print res['response'] zonefile_hash = res['response']['zonefile_hash'] # should still be registered if res['response']['status'] != 'registered': print "register not complete" print json.dumps(res) return False # do we have the history for the name? res = testlib.ysi_REST_call("GET", "/v1/names/bar.test/history", None, api_pass=api_pass) if 'error' in res or res['http_status'] != 200: res['test'] = "Failed to get name history for foo.test" print json.dumps(res) return False # valid history? hist = res['response'] if len(hist.keys()) != 3: res['test'] = 'Failed to get update history' res['history'] = hist print json.dumps(res, indent=4, sort_keys=True) return False # get the zonefile res = testlib.ysi_REST_call( "GET", "/v1/names/bar.test/zonefile/{}".format(zonefile_hash), None, api_pass=api_pass) if 'error' in res or res['http_status'] != 200: res['test'] = 'Failed to get name zonefile' print json.dumps(res) return False # same zonefile we put? if res['response']['zonefile'] != zonefile_txt: res['test'] = 'mismatched zonefile, expected\n{}\n'.format( zonefile_txt) print json.dumps(res) return False # okay, now let's try to do an update. # make zonefile for recipient driver_urls = ysi_client.storage.make_mutable_data_urls( 'bar.test', use_only=['http', 'disk']) zonefile = ysi_client.zonefile.make_empty_zonefile('bar.test', wallets[3].pubkey_hex, urls=driver_urls) zonefile_txt = ysi_zones.make_zone_file(zonefile, origin='bar.test', ttl=3600) # let's do this update. res = testlib.ysi_REST_call('PUT', '/v1/names/bar.test/zonefile', None, api_pass=api_pass, data={ 'zonefile': zonefile_txt, 'owner_key': new_key, 'payment_key': payment_key }) if 'error' in res or res['http_status'] != 202: res['test'] = 'Failed to register user' print json.dumps(res) error = True return False else: print "Submitted update!" print res print 'Wait for update to be submitted' time.sleep(10) # wait for update to get confirmed for i in xrange(0, 6): testlib.next_block(**kw) res = testlib.verify_in_queue(None, 'bar.test', 'update', None, api_pass=api_pass) if not res: print "update error in second update" print res return False for i in xrange(0, 4): testlib.next_block(**kw) # wait for zonefile to propagate time.sleep(10) res = testlib.ysi_REST_call("GET", "/v1/names/bar.test", None, api_pass=api_pass) if 'error' in res or res['http_status'] != 200: res['test'] = 'Failed to get name bar.test' print json.dumps(res) return False zonefile_hash = res['response']['zonefile_hash'] # get the zonefile res = testlib.ysi_REST_call( "GET", "/v1/names/bar.test/zonefile/{}".format(zonefile_hash), None, api_pass=api_pass) if 'error' in res or res['http_status'] != 200: res['test'] = 'Failed to get name zonefile' print json.dumps(res) return False # same zonefile we put? if res['response']['zonefile'] != zonefile_txt: res['test'] = 'mismatched zonefile, expected\n{}\n'.format( zonefile_txt) print json.dumps(res) return False
def scenario(wallets, **kw): global pk, pk2 testlib.blockstack_namespace_preorder("test", wallets[1].addr, wallets[0].privkey) testlib.next_block(**kw) testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 10, 10, wallets[0].privkey) testlib.next_block(**kw) testlib.blockstack_namespace_ready("test", wallets[1].privkey) testlib.next_block(**kw) # pay for a name in a v1 namespace with Stacks addr = virtualchain.address_reencode(virtualchain.get_privkey_address(pk)) addr2 = virtualchain.address_reencode( virtualchain.get_privkey_address(pk2)) # calculate the cost of doing so namespace = testlib.get_state_engine().get_namespace('test') stacks_price = blockstack.lib.scripts.price_name_stacks( 'baz', namespace, testlib.get_current_block(**kw)) btc_price = blockstack.lib.scripts.price_name( 'baz', namespace, testlib.get_current_block(**kw)) print '' print 'price of {} in Stacks is {}'.format('baz.test', stacks_price) print 'price of {} in BTC is {}'.format('baz.test', btc_price) print '' testlib.blockstack_send_tokens(addr, "STACKS", stacks_price, wallets[0].privkey) testlib.blockstack_send_tokens(addr2, "STACKS", stacks_price * 2, wallets[0].privkey) testlib.send_funds(wallets[0].privkey, 10 * btc_price, addr) testlib.send_funds(wallets[0].privkey, 10 * btc_price, addr2) testlib.next_block(**kw) # preorder/register using Stacks testlib.blockstack_name_preorder("baz.test", wallets[2].privkey, addr2, price={ 'units': 'STACKS', 'amount': stacks_price }) testlib.blockstack_name_preorder("goo.test", wallets[2].privkey, addr2, price={ 'units': 'STACKS', 'amount': stacks_price }) testlib.blockstack_name_preorder("nop.test", wallets[2].privkey, addr2, price={ 'units': 'STACKS', 'amount': stacks_price }) testlib.next_block(**kw) testlib.blockstack_name_register("baz.test", wallets[2].privkey, addr2) testlib.blockstack_name_register("goo.test", wallets[2].privkey, addr2) testlib.blockstack_name_register("nop.test", wallets[2].privkey, addr2) testlib.next_block(**kw) balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS'] # pay with both Stacks and Bitcoin # should favor Stacks payment over Bitcoin payment if we pay enough stacks. # Stacks should have been burned, as well as BTC. res = testlib.blockstack_name_renew('baz.test', pk2, price={ 'units': 'STACKS', 'amount': stacks_price }, tx_only=True, expect_success=True) txhex = res['transaction'] tx = virtualchain.btc_tx_deserialize(txhex) # up the burn amount btc_price = blockstack.lib.scripts.price_name( 'baz', namespace, testlib.get_current_block(**kw)) tx['outs'][3]['script'] = virtualchain.btc_make_payment_script( blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS) tx['outs'][3]['value'] = btc_price tx['outs'][4]['value'] -= btc_price # re-sign for i in tx['ins']: i['script'] = '' txhex = virtualchain.btc_tx_serialize(tx) txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( pk2, testlib.get_utxos(addr2), txhex) # re-sign the last output with the payment key tx_signed = virtualchain.btc_tx_deserialize(txhex_signed) tx_signed['ins'][-1]['script'] = '' txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( testlib.get_default_payment_wallet().privkey, testlib.get_utxos(testlib.get_default_payment_wallet().addr), virtualchain.btc_tx_serialize(tx_signed)) print txhex_signed res = testlib.broadcast_transaction(txhex_signed) if 'error' in res: print res return False testlib.next_block(**kw) # should have paid in Stacks balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS'] if balance_after != balance_before - stacks_price: print 'baz.test cost {}'.format(balance_before - balance_after) return False balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS'] # try to renew a name where we pay not enough stacks, but enough bitcoin. # should be rejected. res = testlib.blockstack_name_renew('goo.test', pk2, price={ 'units': 'STACKS', 'amount': stacks_price - 1 }, tx_only=True) txhex = res['transaction'] tx = virtualchain.btc_tx_deserialize(txhex) # up the burn amount to the name price btc_price = blockstack.lib.scripts.price_name( 'goo', namespace, testlib.get_current_block(**kw)) tx['outs'][3]['script'] = virtualchain.btc_make_payment_script( blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS) tx['outs'][3]['value'] = btc_price tx['outs'][4]['value'] -= btc_price # re-sign for i in tx['ins']: i['script'] = '' txhex = virtualchain.btc_tx_serialize(tx) txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( pk2, testlib.get_utxos(addr2), txhex) # re-sign the last output with the payment key tx_signed = virtualchain.btc_tx_deserialize(txhex_signed) tx_signed['ins'][-1]['script'] = '' txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( testlib.get_default_payment_wallet().privkey, testlib.get_utxos(testlib.get_default_payment_wallet().addr), virtualchain.btc_tx_serialize(tx_signed)) print txhex_signed res = testlib.broadcast_transaction(txhex_signed) if 'error' in res: print res return False testlib.next_block(**kw) # should NOT have paid in Stacks balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS'] if balance_after != balance_before: print 'goo.test paid {}'.format(balance_before - balance_after) return False balance_before = testlib.get_addr_balances(addr2)[addr2]['STACKS'] # underpay in both Stacks and Bitcoin. # only bitcoin will be burned; transaction will not be processed res = testlib.blockstack_name_renew('nop.test', pk2, price={ 'units': 'STACKS', 'amount': stacks_price - 1 }, tx_only=True) txhex = res['transaction'] tx = virtualchain.btc_tx_deserialize(txhex) # up the burn amount to the name price btc_price = blockstack.lib.scripts.price_name( 'goo', namespace, testlib.get_current_block(**kw)) tx['outs'][3]['script'] = virtualchain.btc_make_payment_script( blockstack.lib.config.BLOCKSTACK_BURN_ADDRESS) tx['outs'][3]['value'] = btc_price - 1 tx['outs'][4]['value'] -= btc_price + 1 # re-sign for i in tx['ins']: i['script'] = '' txhex = virtualchain.btc_tx_serialize(tx) txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( pk2, testlib.get_utxos(addr2), txhex) # re-sign the last output with the payment key tx_signed = virtualchain.btc_tx_deserialize(txhex_signed) tx_signed['ins'][-1]['script'] = '' txhex_signed = virtualchain.tx_sign_all_unsigned_inputs( testlib.get_default_payment_wallet().privkey, testlib.get_utxos(testlib.get_default_payment_wallet().addr), virtualchain.btc_tx_serialize(tx_signed)) print txhex_signed res = testlib.broadcast_transaction(txhex_signed) if 'error' in res: print res return False testlib.next_block(**kw) testlib.expect_snv_fail_at('nop.test', testlib.get_current_block(**kw)) balance_after = testlib.get_addr_balances(addr2)[addr2]['STACKS'] if balance_after != balance_before: print 'paid {} for nop.test'.format(balance_before - balance_after) return False
def check(state_engine): # not revealed, but ready ns = state_engine.get_namespace_reveal("test") if ns is not None: print "namespace reveal exists" return False ns = state_engine.get_namespace("test") if ns is None: print "no namespace" return False if ns['namespace_id'] != 'test': print "wrong namespace" return False # name should exist, but not be renewed addr2 = virtualchain.address_reencode( virtualchain.get_privkey_address(pk2)) name_rec = state_engine.get_name('nop.test') if name_rec is None: print 'name record does not exist for nop.test' return False if name_rec['first_registered'] != name_rec['last_renewed']: print 'name nop.test was renewed' return False # name should exist, but not be renewed addr2 = virtualchain.address_reencode( virtualchain.get_privkey_address(pk2)) name_rec = state_engine.get_name('goo.test') if name_rec is None: print 'name record does not exist for goo.test' return False if name_rec['first_registered'] != name_rec['last_renewed']: print 'name goo.test was renewed' return False for name in ['baz.test']: # not preordered preorder = state_engine.get_name_preorder( name, virtualchain.make_payment_script(wallets[2].addr), addr2) if preorder is not None: print "preorder exists" return False # registered name_rec = state_engine.get_name(name) if name_rec is None: print "name does not exist" return False # owned by if name_rec['address'] != addr2 or name_rec[ 'sender'] != virtualchain.make_payment_script(addr2): print "sender is wrong" return False # paid for baz.test with Stacks, but nevertheless burned Bitcoin # however, baz.test's burn output is equal to the bitcoin price for name in ['baz']: name_rec = state_engine.get_name(name + '.test') stacks_price = blockstack.lib.scripts.price_name_stacks( name, ns, state_engine.lastblock) if name_rec['token_fee'] != stacks_price: print 'paid wrong token fee for {}.test'.format(name) print 'expected {} ({}), got {} ({})'.format( stacks_price, type(stacks_price), name_rec['token_fee'], type(name_rec['token_fee'])) return False if name_rec['op_fee'] != blockstack.lib.scripts.price_name( name, ns, state_engine.lastblock): print 'paid wrong BTC for baz.test ({})'.format(name_rec['op_fee']) return False return True
def scenario(wallets, **kw): # pass 100 stacks around in a circle. new_keys = [wallets[0].privkey] + [ virtualchain.lib.ecdsalib.ecdsa_private_key().to_hex() for i in range(0, 4) ] for k in range(0, 4): for j in range(0, len(new_keys)): i = j new_addr = virtualchain.get_privkey_address( new_keys[(i + 1) % len(new_keys)]) cur_addr = virtualchain.get_privkey_address( new_keys[i % len(new_keys)]) initial_new_balance_info = json.loads( testlib.nodejs_cli('balance', new_addr)) initial_cur_balance_info = json.loads( testlib.nodejs_cli('balance', cur_addr)) print '\n initial new balance info: {} \n'.format( initial_new_balance_info) if 'STACKS' not in initial_new_balance_info: initial_new_balance_info['STACKS'] = 0 if i > 0: testlib.send_funds(wallets[0].privkey, 500000, cur_addr) testlib.send_funds(wallets[0].privkey, 500000, new_addr) testlib.blockstack_send_tokens(new_addr, 'STACKS', 100, new_keys[i % len(new_keys)], safety_checks=False) # consolidate utxos = testlib.get_utxos(wallets[0].addr) if len(utxos) > 1: balance = testlib.get_balance(wallets[0].addr) testlib.send_funds(wallets[0].privkey, balance - 5500, wallets[0].addr, change=False) utxos = testlib.get_utxos(new_addr) if len(utxos) > 1: balance = testlib.get_balance(new_addr) testlib.send_funds(new_keys[(i + 1) % len(new_keys)], balance - 5500, new_addr, change=False) testlib.next_block(**kw) for j in range(0, len(new_keys)): i = j new_addr = virtualchain.get_privkey_address( new_keys[(i + 1) % len(new_keys)]) cur_addr = virtualchain.get_privkey_address( new_keys[i % len(new_keys)]) if j == len(new_keys) - 1: if (i + 1) % len(new_keys) != 0: # last address should have 100 stacks, unless new_addr is wallets[0] balance_info = json.loads( testlib.nodejs_cli('balance', new_addr)) assert int(balance_info['STACKS']) == 100 else: if i % len(new_keys) != 0: # every other address, except wallets[0], should have 0 balance balance_info = json.loads( testlib.nodejs_cli('balance', cur_addr)) assert int(balance_info['STACKS']) == 0 # consolidate for j in range(0, len(new_keys)): cur_addr = virtualchain.get_privkey_address( new_keys[i % len(new_keys)]) utxos = testlib.get_utxos(cur_addr) if len(utxos) > 1: balance = testlib.get_balance(cur_addr) testlib.send_funds(new_keys[i % len(new_keys)], balance - 5500, cur_addr, change=False) testlib.next_block(**kw) # each *new* address has 4 history items -- four spends, four receives for new_key in new_keys[1:]: new_addr = virtualchain.get_privkey_address(new_key) history = requests.get( 'http://localhost:16268/v1/accounts/{}/history?page=0'.format( new_addr)).json() # should have gotten 4 debits for 100, and 4 credits for 100 assert int(history[0]['credit_value']) == 400, history assert int(history[0]['debit_value']) == 400, history assert len(history) == 8, history
def make_wallet_keys(data_privkey=None, owner_privkey=None, payment_privkey=None): """ For testing. DO NOT USE """ ret = { 'owner_privkey': None, 'data_privkey': None, 'payment_privkey': None, } if data_privkey is not None: if not virtualchain.is_singlesig(data_privkey): raise ValueError('Invalid data key info') pk_data = ecdsa_private_key(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if virtualchain.is_multisig(owner_privkey): pks = owner_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( owner_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['owner_privkey'] = multisig_info ret['owner_addresses'] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(owner_privkey): pk_owner = ecdsa_private_key(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner ret['owner_addresses'] = [ virtualchain.get_privkey_address(pk_owner) ] else: raise ValueError('Invalid owner key info') if payment_privkey is None: return ret if virtualchain.is_multisig(payment_privkey): pks = payment_privkey['private_keys'] m, _ = virtualchain.parse_multisig_redeemscript( payment_privkey['redeem_script']) assert m <= len(pks) multisig_info = virtualchain.make_multisig_info(m, pks) ret['payment_privkey'] = multisig_info ret['payment_addresses'] = [ virtualchain.get_privkey_address(multisig_info) ] elif virtualchain.is_singlesig(payment_privkey): pk_payment = ecdsa_private_key(payment_privkey).to_hex() ret['payment_privkey'] = pk_payment ret['payment_addresses'] = [ virtualchain.get_privkey_address(pk_payment) ] else: raise ValueError('Invalid payment key info') ret['data_pubkey'] = ecdsa_private_key( ret['data_privkey']).public_key().to_hex() ret['data_pubkeys'] = [ret['data_pubkey']] return ret
def scenario(wallets, **kw): testlib.blockstack_namespace_preorder("id", wallets[1].addr, wallets[0].privkey) testlib.next_block(**kw) # names last for 10 blocks testlib.blockstack_namespace_reveal( "id", wallets[1].addr, 10, 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("id", wallets[1].privkey) testlib.next_block(**kw) names_to_preorder_and_renew = '/tmp/testdata.txt' if os.path.exists(names_to_preorder_and_renew): name_infos = [] with open(names_to_preorder_and_renew, 'r') as f: while True: name_line = f.readline() if len(name_line) == 0: break name_line = name_line.strip() m = re.match('^([^ ]+)[ ]+([^ ]+)[ ]+([^ ]+)$', name_line) if m: name, addr, pkey = m.groups() name_infos.append({ 'name': name.strip(), 'addr': addr.strip(), 'owner_privkey': pkey.strip() }) else: name, addr = name_line.split(' ', 1) name_infos.append({ 'name': name.strip(), 'addr': addr.strip() }) for i, name_info in enumerate(name_infos): owner_addr = None if name_info.has_key('owner_privkey'): owner_addr = virtualchain.get_privkey_address( name_info['owner_privkey']) else: print name_info['addr'] owner_addr = virtualchain.address_reencode(name_info['addr']) testlib.blockstack_name_preorder(name_info['name'], wallets[(i % 8) + 2].privkey, owner_addr, safety_checks=False, tx_fee=300 * 1000) testlib.next_block(**kw) for i, name_info in enumerate(name_infos): owner_addr = None if name_info.has_key('owner_privkey'): owner_addr = virtualchain.get_privkey_address( name_info['owner_privkey']) else: owner_addr = virtualchain.address_reencode(name_info['addr']) zonefile_hash = make_zonefile_hash(name_info['name'], owner_addr) testlib.blockstack_name_register(name_info['name'], wallets[(i % 8) + 2].privkey, owner_addr, zonefile_hash=zonefile_hash, safety_checks=False, tx_fee=300 * 1000) testlib.next_block(**kw) wallet = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[2].privkey, wallets[3].privkey, wallets[4].privkey) print >> sys.stderr, "We're a go!"