def decrypt_private_key_info( privkey_info, password ): """ 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. """ hex_password = hexlify(password) if is_encrypted_multisig( privkey_info ): ret = decrypt_multisig_info( privkey_info, password ) if 'error' in ret: return {'error': 'Failed to decrypt multisig wallet: %s' % ret['error']} # sanity check if 'redeem_script' not in ret: return {'error': 'Invalid multisig wallet: missing redeem_script'} if 'private_keys' not in ret: return {'error': 'Invalid multisig wallet: missing private_keys'} return {'address': virtualchain.make_p2sh_address(ret['redeem_script']), 'private_key_info': ret} elif type(privkey_info) in [str, unicode]: try: pk = aes_decrypt( privkey_info, hex_password ) virtualchain.BitcoinPrivateKey(pk) except: return {'error': 'Invalid password'} return {'address': virtualchain.BitcoinPrivateKey(pk).public_key().address(), 'private_key_info': pk} else: return {'error': 'Invalid encrypted private key info'}
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 is_singlesig(data_privkey): raise ValueError("Invalid data key info") pk_data = virtualchain.BitcoinPrivateKey(data_privkey).to_hex() ret['data_privkey'] = pk_data if owner_privkey is not None: if is_multisig(owner_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in owner_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( owner_privkey['redeem_script']) ret['owner_privkey'] = virtualchain.make_multisig_info(m, pks) elif is_singlesig(owner_privkey): pk_owner = virtualchain.BitcoinPrivateKey(owner_privkey).to_hex() ret['owner_privkey'] = pk_owner else: raise ValueError("Invalid owner key info") if payment_privkey is not None: if is_multisig(payment_privkey): pks = [ virtualchain.BitcoinPrivateKey(pk).to_hex() for pk in payment_privkey['private_keys'] ] m, pubs = virtualchain.parse_multisig_redeemscript( payment_privkey['redeem_script']) ret['payment_privkey'] = virtualchain.make_multisig_info(m, pks) elif is_singlesig(payment_privkey): pk_payment = virtualchain.BitcoinPrivateKey( payment_privkey).to_hex() ret['payment_privkey'] = pk_payment else: raise ValueError("Invalid payment key info") return ret
def get_uncompressed_private_and_public_keys(privkey_str): """ Get the private and public keys from a private key string. Make sure the both are *uncompressed* """ pk = virtualchain.BitcoinPrivateKey(str(privkey_str)) pk_hex = pk.to_hex() # force uncompressed if len(pk_hex) > 64: assert pk_hex[-2:] == '01' pk_hex = pk_hex[:64] pubk_hex = virtualchain.BitcoinPrivateKey(pk_hex).public_key().to_hex() return pk_hex, pubk_hex
def tx_sign_input(blockstack_tx, idx, private_key_info, hashcode=bitcoin.SIGHASH_ALL): """ Sign a particular input in the given transaction. @private_key_info can either be a private key, or it can be a dict with 'redeem_script' and 'private_keys' defined """ if type(private_key_info) in [str, unicode]: # single private key return bitcoin.sign(blockstack_tx, idx, virtualchain.BitcoinPrivateKey( str(private_key_info)).to_hex(), hashcode=hashcode) else: assert type(private_key_info) in [dict] assert "redeem_script" in private_key_info assert "private_keys" in private_key_info redeem_script = private_key_info['redeem_script'] private_keys = private_key_info['private_keys'] assert type(redeem_script) in [str, unicode] redeem_script = str(redeem_script) # multisig return tx_sign_multisig(blockstack_tx, idx, str(redeem_script), private_keys, hashcode=bitcoin.SIGHASH_ALL)
def decrypt_multisig_info( enc_multisig_info, password ): """ Given an encrypted multisig info dict, decrypt the sensitive fields. Returns {'private_keys': ..., 'redeem_script': ..., **other_fields} Return {'error': ...} on error """ multisig_info = {} hex_password = hexlify(password) if 'encrypted_private_keys' in enc_multisig_info.keys(): multisig_info['private_keys'] = [] for enc_pk in enc_multisig_info['encrypted_private_keys']: pk = None try: pk = aes_decrypt( enc_pk, hex_password ) virtualchain.BitcoinPrivateKey(pk) except Exception, e: if os.environ.get("BLOCKSTACK_TEST", None) == "1": log.exception(e) return {'error': 'Invalid password; failed to decrypt private key in multisig wallet'} multisig_info['private_keys'].append( pk )
def encrypt_private_key_info(privkey_info, password): """ Encrypt private key info. Return {'status': True, 'encrypted_private_key_info': {'address': ..., 'private_key_info': ...}} on success Returns {'error': ...} on error """ ret = {} hex_password = hexlify(password) if is_multisig(privkey_info): ret['address'] = virtualchain.make_multisig_address( privkey_info['redeem_script']) ret['private_key_info'] = encrypt_multisig_info(privkey_info, password) return {'status': True, 'encrypted_private_key_info': ret} elif is_singlesig(privkey_info): ret['address'] = virtualchain.BitcoinPrivateKey( privkey_info).public_key().address() ret['private_key_info'] = aes_encrypt(privkey_info, hex_password) return {'status': True, 'encrypted_private_key_info': ret} else: return {'error': 'Invalid private key info'}
def tx_make_input_signature(tx, idx, script, privkey_str, hashcode): """ Sign a single input of a transaction, given the serialized tx, the input index, the output's scriptPubkey, and the hashcode. privkey_str must be a hex-encoded private key TODO: move to virtualchain Return the hex signature. """ pk = virtualchain.BitcoinPrivateKey(str(privkey_str)) pubk = pk.public_key() priv = pk.to_hex() pub = pubk.to_hex() addr = pubk.address() signing_tx = bitcoin.signature_form(tx, idx, script, hashcode) txhash = bitcoin.bin_txhash(signing_tx, hashcode) # sign using uncompressed private key pk_uncompressed_hex, pubk_uncompressed_hex = get_uncompressed_private_and_public_keys( priv) sigb64 = sign_digest(txhash.encode('hex'), priv) # sanity check assert verify_digest(txhash.encode('hex'), pubk_uncompressed_hex, sigb64) sig_r, sig_s = decode_signature(sigb64) sig_bin = ecdsa.util.sigencode_der(sig_r, sig_s, ecdsa.SECP256k1.order) sig = sig_bin.encode('hex') + bitcoin.encode(hashcode, 16, 2) return sig
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 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) # derive importer keys and do imports # NOTE: breaks consensus trace from 0.14.0 private_keychain = keychain.PrivateKeychain.from_private_key( wallets[1].privkey) private_keys = [ wallets[1].privkey ] # NOTE: always start with the reveal key, then use children for i in xrange(0, 3): import_key = private_keychain.child(i).private_key() print "fund {} (child {})".format(import_key, i) res = testlib.send_funds( wallets[1].privkey, 100000000, virtualchain.BitcoinPrivateKey(import_key).public_key().address()) if 'error' in res: print json.dumps(res, indent=4, sort_keys=True) return False testlib.next_block(**kw) private_keys.append(import_key) resp = testlib.blockstack_name_import( "foo.test", addr_reencode("1BKufFedDrueBBFBXtiATB2PSdsBGZxf3N"), "11" * 20, wallets[1].privkey) # master if 'error' in resp: print json.dumps(resp, indent=4) testlib.next_block(**kw) resp = testlib.blockstack_name_import( "foo.test", addr_reencode("1ARVjrtKnUVWt2GNrpuFLnNCL2WGUhKdkW"), "33" * 20, private_keys[2]) # derived child 2 if 'error' in resp: print json.dumps(resp, indent=4) testlib.next_block(**kw) resp = testlib.blockstack_name_import( "foo.test", addr_reencode("1PYu4vKB3g2QLDFdurxqYSJ9aJSed7tne1"), "22" * 20, private_keys[1]) # derived child 1 if 'error' in resp: print json.dumps(resp, indent=4) testlib.next_block(**kw) testlib.blockstack_namespace_ready("test", wallets[1].privkey) testlib.next_block(**kw)
def tx_sign_multisig(blockstack_tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction """ # sign in the right order privs = dict([ (virtualchain.BitcoinPrivateKey(str(pk_str)).public_key().to_hex(), str(pk_str)) for pk_str in private_keys ]) m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys = [] sigs = [] for ki in xrange(0, len(public_keys)): if not privs.has_key(public_keys[ki]): continue if len(used_keys) == m: break assert public_keys[ ki] not in used_keys, "Tried to reuse key %s" % public_keys[ki] pk_str = privs[public_keys[ki]] used_keys.append(public_keys[ki]) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = bitcoin.multisign(blockstack_tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, "Missing private keys" return bitcoin.apply_multisignatures(blockstack_tx, idx, str(redeem_script), sigs)
def multisig_privkey_to_string(privkey_info): """ Convert multisig keys to string """ return ",".join([ virtualchain.BitcoinPrivateKey(pk).to_wif() for pk in privkey_info['private_keys'] ])
def decrypt_multisig_info(enc_multisig_info, password): """ Given an encrypted multisig info dict, decrypt the sensitive fields. Returns {'private_keys': ..., 'redeem_script': ..., **other_fields} Return {'error': ...} on error """ multisig_info = { 'private_keys': None, 'redeem_script': None, } hex_password = hexlify(password) assert is_encrypted_multisig( enc_multisig_info), 'Invalid encrypted multisig keys' multisig_info['private_keys'] = [] for enc_pk in enc_multisig_info['encrypted_private_keys']: pk = None try: pk = aes_decrypt(enc_pk, hex_password) virtualchain.BitcoinPrivateKey(pk) except Exception as e: if BLOCKSTACK_TEST: log.exception(e) return { 'error': 'Invalid password; failed to decrypt private key in multisig wallet' } multisig_info['private_keys'].append(ECPrivateKey(pk).to_hex()) redeem_script = None enc_redeem_script = enc_multisig_info['encrypted_redeem_script'] try: redeem_script = aes_decrypt(enc_redeem_script, hex_password) except Exception as e: if BLOCKSTACK_TEST: log.exception(e) return { 'error': 'Invalid password; failed to decrypt redeem script in multisig wallet' } multisig_info['redeem_script'] = redeem_script # preserve any other information in the multisig info for k, v in enc_multisig_info.items(): if k not in ['encrypted_private_keys', 'encrypted_redeem_script']: multisig_info[k] = v return multisig_info
def get_privkey_info_address(privkey_info): if privkey_info is None: return None if is_singlesig(privkey_info): return virtualchain.BitcoinPrivateKey( privkey_info).public_key().address() else: raise ValueError("Invalid private key info")
def make_transaction(name, payment_privkey_info, consensus_hash, payment_addr, zonefilemanage_client): data = build(name) tx = make_op_return_tx( data, virtualchain.BitcoinPrivateKey(payment_privkey_info), zonefilemanage_client, fee=100000, format='bin') return tx
def tx_sign_multisig(tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction TODO: move to virtualchain """ # sign in the right order privs = { virtualchain.BitcoinPrivateKey(str(pk)).public_key().to_hex(): str(pk) for pk in private_keys } m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys, sigs = [], [] for public_key in public_keys: if public_key not in privs: continue if len(used_keys) == m: break assert public_key not in used_keys, 'Tried to reuse key {}'.format( public_key) pk_str = privs[public_key] used_keys.append(public_key) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode) # sig = bitcoin.multisign(tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, 'Missing private keys' return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
def is_singlesig(privkey_info): """ Does the given private key info represent a single signature bundle? (i.e. one private key)? """ if type(privkey_info) not in [str, unicode]: return False try: virtualchain.BitcoinPrivateKey(privkey_info) return True except: return False
def fill_wallet(bitcoind, wallet, value): """ Fill a test wallet on regtet bitcoind Return True on success Raise an error """ if type(wallet.privkey) in [str, unicode]: #single private key testnet_wif = wallet.privkey if not testnet_wif.startswith("c"): testnet_wif = virtualchain.BitcoinPrivateKey(testnet_wif).to_wif() bitcoind.importprivkey(testnet_wif, "") addr = virtualchain.BitcoinPublicKey(wallet.pubkey_hex).address() log.info("Fill %s with %s " % (addr, value)) bitcoind.sendtoaddress(addr, value) else: # multisig address testnet_wifs = [] testnet_pubks = [] for pk in wallet.privkey['private_keys']: if not pk.startswith("c"): pk = virtualchain.BitcoinPrivateKey(pk).to_wif() testnet_wifs.append(pk) testnet_pubks.append( virtualchain.BitcoinPrivateKey(pk).public_key().to_hex()) multisig_info = virtualchain.make_multisig_info(wallet.m, testnet_wifs) bitcoind.addmultisigaddress(wallet.m, testnet_pubks) bitcoind.importaddress(multisig_info['address']) log.debug("Fill %s with %s" % (multisig_info['address'], value)) bitcoind.sendtoaddress(multisig_info['address'], value) return True
def get_data_or_owner_privkey(user_zonefile, owner_address, wallet_keys=None, config_path=CONFIG_PATH): """ Get the data private key if it is set in the zonefile, or if not, fall back to the owner private key. Due to legacy compatibility Useful for signing mutable data when no explicit data key is set. Returns {'status': True, 'privatekey': ...} on success Returns {'error': ...} on error """ # generate the mutable zonefile data_privkey = get_data_privkey(user_zonefile, wallet_keys=wallet_keys, config_path=config_path) if data_privkey is None: # This is legacy code here. The only time this should happen is # when the user has a single owner key, and does not have a # separate data key. log.warn("No data private key set. Falling back to owner keypair.") owner_privkey_info = get_owner_privkey_info(wallet_keys=wallet_keys, config_path=config_path) if owner_privkey_info is None: raise Exception("No owner private key info") return {'error': 'No usable private signing key found'} # sanity check: must be a single private key if not is_singlesig(owner_privkey_info): raise Exception("Owner private key info must be a single key") return {'error': 'No usable private signing key found'} # sanity check: must match profile address owner_pubkey = virtualchain.BitcoinPrivateKey( owner_privkey_info).public_key().to_hex() compressed_addr, uncompressed_addr = get_pubkey_addresses(owner_pubkey) if owner_address not in [compressed_addr, uncompressed_addr]: raise Exception( "%s not in [%s,%s]" % (owner_address, compressed_addr, uncompressed_addr)) return {'error': 'No usable public key'} data_privkey = owner_privkey_info return {'status': True, 'privatekey': data_privkey}
def decrypt_private_key_info(privkey_info, password): """ 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. """ hex_password = hexlify(password) 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']) } # sanity check if 'redeem_script' not in ret: return {'error': 'Invalid multisig wallet: missing redeem_script'} if 'private_keys' not in ret: return {'error': 'Invalid multisig wallet: missing private_keys'} return { 'address': virtualchain.make_p2sh_address(ret['redeem_script']), 'private_key_info': ret } if is_encrypted_singlesig(privkey_info): try: pk = aes_decrypt(privkey_info, hex_password) pk = ECPrivateKey(pk).to_hex() except Exception as e: if BLOCKSTACK_TEST: log.exception(e) return {'error': 'Invalid password'} return { 'address': virtualchain.BitcoinPrivateKey(pk).public_key().address(), 'private_key_info': pk } return {'error': 'Invalid encrypted private key info'}
def __init__(self, pk_wif, ignored): pk = virtualchain.BitcoinPrivateKey(pk_wif) self._pk = pk if pk_wif.startswith("c"): #already a privkey self.privkey = pk_wif else: self.privkey = pk.to_wif() self.pubkey_hex = pk.public_key().to_hex() self.addr = pk.public_key().address() log.info("Wallet %s(%s)" % (self.privkey, self.addr))
def tx_make_input_signature(tx, idx, script, privkey_str, hashcode): """ Sign a single input of a transaction, given the serialized tx, the input index, the output's scriptPubkey, and the hashcode. TODO: move to virtualchain Return the hex signature. """ pk = virtualchain.BitcoinPrivateKey(str(privkey_str)) pubk = pk.public_key() priv = pk.to_hex() pub = pubk.to_hex() addr = pubk.address() signing_tx = bitcoin.signature_form(tx, idx, script, hashcode) txhash = bitcoin.bin_txhash(signing_tx, hashcode) # sign using uncompressed private key pk_uncompressed_hex, pubk_uncompressed_hex = get_uncompressed_private_and_public_keys( priv) sk = ecdsa.SigningKey.from_string(pk_uncompressed_hex.decode('hex'), curve=ecdsa.SECP256k1) sig_bin = sk.sign_digest(txhash, sigencode=ecdsa.util.sigencode_der) # enforce low-s sig_r, sig_s = ecdsa.util.sigdecode_der(sig_bin, ecdsa.SECP256k1.order) if sig_s * 2 >= ecdsa.SECP256k1.order: log.debug("High-S to low-S") sig_s = ecdsa.SECP256k1.order - sig_s sig_bin = ecdsa.util.sigencode_der(sig_r, sig_s, ecdsa.SECP256k1.order) # sanity check vk = ecdsa.VerifyingKey.from_string( pubk_uncompressed_hex[2:].decode('hex'), curve=ecdsa.SECP256k1) assert vk.verify_digest(sig_bin, txhash, sigdecode=ecdsa.util.sigdecode_der ), "Failed to verify signature ({}, {})".format( sig_r, sig_s) sig = sig_bin.encode('hex') + bitcoin.encode(hashcode, 16, 2) return sig
def get_privkey_info_address( privkey_info ): """ Get the address of private key information: * if it's a single private key, then calculate the address. * if it's a multisig info dict, then get the p2sh address """ if privkey_info is None: return None if is_singlesig(privkey_info): return virtualchain.BitcoinPrivateKey(privkey_info).public_key().address() elif is_multisig(privkey_info): return virtualchain.make_multisig_address( privkey_info['redeem_script'] ) else: raise ValueError("Invalid private key info")
def do_name_register(name, payment_privkey_info, utxo_client, tx_broadcaster, consensus_hash=None, proxy=None, safety_check=None): db = get_default_db_inst() records = db.get_name(name) if records is not None: log.error("The name %s has been registered" % name) return {"error": "Name %s has already exist" % name} try: payment_address = virtualchain.BitcoinPrivateKey( payment_privkey_info).public_key().address() except Exception, e: log.error("Invalid private key info") return { 'error': 'Name register can only use a single private key with a P2PKH script' }
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 make_transaction(name, payment_privkey_info, owner_address, zonefilemanage_client): data = build(name) private_key_obj, from_address, inputs = analyze_private_key( virtualchain.BitcoinPrivateKey(payment_privkey_info), zonefilemanage_client) outputs = make_op_return_outputs(data, inputs, from_address, owner_address, fee=100000) # serialize the transaction unsigned_tx = serialize_transaction(inputs, outputs) # generate a scriptSig for each input for i in xrange(0, len(inputs)): signed_tx = sign_transaction(unsigned_tx, i, private_key_obj.to_hex()) unsigned_tx = signed_tx # return the signed tx return signed_tx
def scenario(wallets, **kw): # make a test namespace resp = testlib.blockstack_namespace_preorder("test", wallets[1].addr, wallets[0].privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) resp = 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) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # import 3 names in the same block: foo.test, bar.test, baz.test 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]] # derive importer keys and do imports # NOTE: breaks consensus trace from 0.14.0 private_keychain = keychain.PrivateKeychain.from_private_key( wallets[1].privkey) private_keys = [ wallets[1].privkey ] # NOTE: always start with the reveal key, then use children for i in xrange(0, len(names) - 1): import_key = private_keychain.child(i).private_key() print "fund {} (child {})".format(import_key, i) res = testlib.send_funds( wallets[1].privkey, 100000000, virtualchain.BitcoinPrivateKey(import_key).public_key().address()) if 'error' in res: print json.dumps(res, indent=4, sort_keys=True) return False testlib.next_block(**kw) private_keys.append(import_key) for i in xrange(0, len(names)): name = names[i] register_wallet = name_register_wallets[i] import_key = private_keys[i] resp = testlib.blockstack_name_import(name, register_wallet.addr, str(9 - i) * 40, import_key) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # namespace ready... resp = testlib.blockstack_namespace_ready("test", wallets[1].privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # update 3 names in the same block for i in xrange(0, len(names)): name = names[i] register_wallet = name_register_wallets[i] resp = testlib.blockstack_name_update(name, str(i + 2) * 40, register_wallet.privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # update 3 names in the same block, again for i in xrange(0, len(names)): name = names[i] register_wallet = name_register_wallets[i] resp = testlib.blockstack_name_update(name, str(i + 1) * 40, register_wallet.privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # transfer 3 names in the same block for i in xrange(0, len(names)): name = names[i] register_wallet = name_register_wallets[i] transfer_wallet = name_transfer_wallets[i] resp = testlib.blockstack_name_transfer(name, transfer_wallet.addr, True, register_wallet.privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False testlib.next_block(**kw) # exchange after transfer... tmp = name_register_wallets name_register_wallets = name_transfer_wallets name_transfer_wallets = tmp # revoke 3 names in the same block for i in xrange(0, len(names)): name = names[i] register_wallet = name_register_wallets[i] resp = testlib.blockstack_name_revoke(name, register_wallet.privkey) if 'error' in resp: print json.dumps(resp, indent=4) return False # iterate the blocks a few times for i in xrange(0, 5): testlib.next_block(**kw)
def scenario(wallets, **kw): global synchronized import blockstack_integration_tests.atlas_network as atlas_network 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) # set up RPC daemon test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy(test_proxy) wallet_keys = blockstack_client.make_wallet_keys( owner_privkey=wallets[3].privkey, data_privkey=wallets[4].privkey, payment_privkey=wallets[5].privkey) testlib.blockstack_client_set_wallet("0123456789abcdef", wallet_keys['payment_privkey'], wallet_keys['owner_privkey'], wallet_keys['data_privkey']) # register 10 names for i in xrange(0, 10): res = testlib.blockstack_name_preorder("foo_{}.test".format(i), wallets[2].privkey, wallets[3].addr) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) for i in xrange(0, 10): res = testlib.blockstack_name_register("foo_{}.test".format(i), wallets[2].privkey, wallets[3].addr) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) # start up a simple Atlas test network with two nodes: the main one doing the test, and a subordinate one that treats it as a seed peer. network_des = atlas_network.atlas_network_build( [17000], {17000: [16264]}, {}, os.path.join(testlib.working_dir(**kw), "atlas_network")) atlas_network.atlas_network_start(network_des) time.sleep(5.0) # make 10 empty zonefiles and propagate them for i in xrange(0, 10): data_pubkey = virtualchain.BitcoinPrivateKey( wallet_keys['data_privkey']).public_key().to_hex() empty_zonefile = blockstack_client.zonefile.make_empty_zonefile( "foo_{}.test".format(i), data_pubkey, urls=["file:///tmp/foo_{}.test".format(i)]) empty_zonefile_str = blockstack_zones.make_zone_file(empty_zonefile) value_hash = blockstack_client.hash_zonefile(empty_zonefile) res = testlib.blockstack_name_update("foo_{}.test".format(i), value_hash, wallets[3].privkey) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) # propagate res = testlib.blockstack_cli_sync_zonefile( 'foo_{}.test'.format(i), zonefile_string=empty_zonefile_str) if 'error' in res: print json.dumps(res) return False # wait at most 10 seconds for atlas network to converge synchronized = False for i in xrange(0, 10): atlas_network.atlas_print_network_state(network_des) if atlas_network.atlas_network_is_synchronized( network_des, testlib.last_block(**kw) - 1, 1): print "Synchronized!" synchronized = True break else: time.sleep(1.0) # shut down atlas_network.atlas_network_stop(network_des) return synchronized
def scenario(wallets, **kw): print '\nactivating segwit\n' virtualchain.set_features("segwit", True) print '\nsegwit state: {}\n'.format(virtualchain.get_features('segwit')) testlib.blockstack_namespace_preorder("test", wallets[1].addr, wallets[5].privkey) testlib.blockstack_namespace_preorder("mult", wallets[2].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[5].privkey) testlib.blockstack_namespace_reveal( "mult", wallets[2].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=2) testlib.next_block(**kw) private_keychain = keychain.PrivateKeychain.from_private_key( wallets[2].privkey) private_keys = [ wallets[2].privkey ] # NOTE: always start with the reveal key, then use children for i in xrange(0, 4): import_key = private_keychain.child(i).private_key() print "fund {} (child {})".format(import_key, i) res = testlib.send_funds( wallets[1].privkey, 100000000, virtualchain.BitcoinPrivateKey(import_key).public_key().address()) if 'error' in res: print json.dumps(res, indent=4, sort_keys=True) return False testlib.next_block(**kw) private_keys.append(import_key) # should succeed resp = testlib.blockstack_name_import("foo.mult", wallets[2].addr, '00' * 20, private_keys[0]) if 'error' in resp: print json.dumps(resp, indent=4, sort_keys=True) return False testlib.next_block(**kw) # should succeed resp = testlib.blockstack_name_import("bar.mult", wallets[3].addr, "11" * 20, private_keys[1]) if 'error' in resp: print json.dumps(resp, indent=4, sort_keys=True) return False # should succeed resp = testlib.blockstack_name_import("baz.mult", wallets[4].addr, "22" * 20, private_keys[2]) if 'error' in resp: print json.dumps(resp, indent=4, sort_keys=True) return False # should succeed resp = testlib.blockstack_name_import("goo.mult", wallets[5].addr, "33" * 20, private_keys[3]) if 'error' in resp: print json.dumps(resp, indent=4, sort_keys=True) return False testlib.next_block(**kw) testlib.blockstack_namespace_ready("test", wallets[1].privkey) testlib.blockstack_namespace_ready("mult", wallets[2].privkey) testlib.next_block(**kw) namespace_balance = testlib.get_balance(wallets[0].addr) # get prices hello_cost = testlib.blockstack_get_name_cost('hello.mult') world_cost = testlib.blockstack_get_name_cost('world.mult') foo_cost = testlib.blockstack_get_name_cost('foo.mult') # register/renew res = testlib.blockstack_name_preorder("hello.mult", wallets[1].privkey, wallets[2].addr) if 'error' in res: print res return False res = testlib.blockstack_name_preorder('world.mult', wallets[6].privkey, wallets[7].addr, wallet=wallets[7]) if 'error' in res: print res return False res = testlib.blockstack_name_renew('foo.mult', wallets[2].privkey) if 'error' in res: print res return False testlib.next_block(**kw) new_namespace_balance = testlib.get_balance(wallets[0].addr) if new_namespace_balance != namespace_balance + hello_cost + world_cost + foo_cost: print 'wrong balance' print new_namespace_balance print namespace_balance print hello_cost print foo_cost return False res = testlib.blockstack_name_register("hello.mult", wallets[1].privkey, wallets[2].addr) if 'error' in res: print res return False res = testlib.blockstack_name_register('world.mult', wallets[6].privkey, wallets[7].addr, wallet=wallets[7], zonefile_hash='44' * 20) if 'error' in res: print res return False testlib.next_block(**kw)
def singlesig_privkey_to_string( privkey_info ): """ Convert private key to string """ return virtualchain.BitcoinPrivateKey(privkey_info).to_wif()
def scenario(wallets, **kw): global synchronized, value_hash import blockstack_integration_tests.atlas_network as atlas_network 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) # set up RPC daemon test_proxy = testlib.TestAPIProxy() blockstack_client.set_default_proxy(test_proxy) wallet_keys = blockstack_client.make_wallet_keys( owner_privkey=wallets[3].privkey, data_privkey=wallets[4].privkey, payment_privkey=wallets[5].privkey) testlib.blockstack_client_set_wallet("0123456789abcdef", wallet_keys['payment_privkey'], wallet_keys['owner_privkey'], wallet_keys['data_privkey']) # register 10 names for i in xrange(0, 10): res = testlib.blockstack_name_preorder("foo_{}.test".format(i), wallets[2].privkey, wallets[3].addr) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) for i in xrange(0, 10): res = testlib.blockstack_name_register("foo_{}.test".format(i), wallets[2].privkey, wallets[3].addr) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) # start up an Atlas test network with 9 nodes: the main one doing the test, and 8 subordinate ones that treat it as a seed peer # organize nodes into a linear chain: node n is neighbor to n-1 and n+1, with the seed at one end. # nodes cannot talk to anyone else. atlas_nodes = [17000, 17001, 17002, 17003, 17004, 17005, 17006, 17007] atlas_topology = {} atlas_topology[17000] = [16264, 17001] atlas_topology[17007] = [17006] for i in xrange(1, len(atlas_nodes) - 1): atlas_topology[atlas_nodes[i]] = [ atlas_nodes[i - 1], atlas_nodes[i + 1] ] def chain_drop(src_hostport, dest_hostport): if src_hostport is None: return 0.0 src_host, src_port = blockstack_client.config.url_to_host_port( src_hostport) dest_host, dest_port = blockstack_client.config.url_to_host_port( dest_hostport) if (src_port == 16264 and dest_port == 17000) or (src_port == 17000 and dest_port == 16264): # seed end of the chain return 0.0 if abs(src_port - dest_port) <= 1: # chain link return 0.0 # drop otherwise return 1.0 network_des = atlas_network.atlas_network_build( atlas_nodes, atlas_topology, {}, os.path.join(testlib.working_dir(**kw), "atlas_network")) atlas_network.atlas_network_start(network_des, drop_probability=chain_drop) print "Waiting 25 seconds for the altas peers to catch up" time.sleep(25.0) # make 10 empty zonefiles and propagate them for i in xrange(0, 10): data_pubkey = virtualchain.BitcoinPrivateKey( wallet_keys['data_privkey']).public_key().to_hex() empty_zonefile = blockstack_client.zonefile.make_empty_zonefile( "foo_{}.test".format(i), data_pubkey, urls=["file:///tmp/foo_{}.test".format(i)]) empty_zonefile_str = blockstack_zones.make_zone_file(empty_zonefile) value_hash = blockstack_client.hash_zonefile(empty_zonefile) res = testlib.blockstack_name_update("foo_{}.test".format(i), value_hash, wallets[3].privkey) if 'error' in res: print json.dumps(res) return False testlib.next_block(**kw) # propagate res = testlib.blockstack_cli_sync_zonefile( 'foo_{}.test'.format(i), zonefile_string=empty_zonefile_str) if 'error' in res: print json.dumps(res) return False # wait at most 30 seconds for atlas network to converge synchronized = False for i in xrange(0, 30): atlas_network.atlas_print_network_state(network_des) if atlas_network.atlas_network_is_synchronized( network_des, testlib.last_block(**kw) - 1, 1): print "Synchronized!" sys.stdout.flush() synchronized = True break else: time.sleep(1.0) # shut down atlas_network.atlas_network_stop(network_des) if not synchronized: print "Not synchronized" sys.stdout.flush() return synchronized