def get_public_key_hex_from_tx(inputs, address): """ Given a list of inputs and the address of one of the inputs, find the public key. This only works for p2pkh scripts. We only really need this for NAMESPACE_REVEAL, but we included it in other transactions' consensus data for legacy reasons that now have to be supported forever :( """ ret = None for inp in inputs: input_scriptsig = inp['script'] input_script_code = virtualchain.btc_script_deserialize( input_scriptsig) if len(input_script_code) == 2: # signature pubkey pubkey_candidate = input_script_code[1] pubkey = None try: pubkey = virtualchain.BitcoinPublicKey(pubkey_candidate) except Exception as e: traceback.print_exc() log.warn("Invalid public key {}".format(pubkey_candidate)) continue if address != pubkey.address(): continue # success! return pubkey_candidate return None
def get_wallet_addr( wallet ): """ Get a wallet's address """ if type(wallet.privkey) in [str, unicode]: return virtualchain.BitcoinPublicKey(wallet.pubkey_hex).address() else: return wallet.addr
def get_public_key_hex_from_tx( inputs, address ): """ Given a list of inputs and the address of one of the inputs, find the public key. This only works for p2pkh scripts. We only really need this for NAMESPACE_REVEAL, but we included it in other transactions' consensus data for legacy reasons that now have to be supported forever :( """ ret = None for inp in inputs: input_scriptsig = inp.get('scriptSig', None ) if input_scriptsig is None: continue input_asm = input_scriptsig.get("asm") if len(input_asm.split(" ")) >= 2: # public key is the second hex string. verify it matches the address pubkey_hex = input_asm.split(" ")[1] pubkey = None try: pubkey = virtualchain.BitcoinPublicKey( str(pubkey_hex) ) except Exception, e: traceback.print_exc() log.warning("Invalid public key '%s'" % pubkey_hex) continue if address != pubkey.address(): continue ret = pubkey_hex break
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_public_key_hex_from_tx( inputs, address ): """ Given a list of inputs and the address of one of the inputs, find the public key. This only works for p2pkh scripts. """ ret = None for inp in inputs: input_scriptsig = inp.get('scriptSig', None ) if input_scriptsig is None: continue input_asm = input_scriptsig.get("asm") if len(input_asm.split(" ")) >= 2: # public key is the second hex string. verify it matches the address pubkey_hex = input_asm.split(" ")[1] pubkey = None try: pubkey = virtualchain.BitcoinPublicKey( str(pubkey_hex) ) except Exception, e: traceback.print_exc() log.warning("Invalid public key '%s'" % pubkey_hex) continue if address != pubkey.address(): continue ret = pubkey_hex break
def get_name_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, create_if_absent=False, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, decode_profile=True): """ Given the name of the user, look up the user's record hash, and then get the record itself from storage. If the user's zonefile is really a legacy profile, then the profile will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. Returns (profile, zonefile) on success. If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information Returns (None, {'error': ...}) on failure """ if proxy is None: proxy = get_default_proxy() raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, create_if_absent=create_if_absent, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile) if user_zonefile is None: return (None, {'error': 'No user zonefile'}) if 'error' in user_zonefile: return (None, user_zonefile) name_record = user_zonefile['name_record'] del user_zonefile['name_record'] raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile['raw_zonefile'] del user_zonefile['raw_zonefile'] user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): # convert it log.debug("Converting legacy profile to modern profile") user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): # not a legacy profile, but a custom profile log.debug("Using custom legacy profile") user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key user_address = None old_address = None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) user_address = virtualchain.BitcoinPublicKey( user_data_pubkey).address() except ValueError: # user decided to put multiple keys under the same name into the zonefile. # so don't use them. user_data_pubkey = None # convert to address if name_record is None: name_record = proxy.get_name_blockchain_record(name) if name_record is None or 'error' in name_record: log.error("Failed to look up name record for '%s'" % name) return (None, {'error': 'Failed to look up name record'}) old_address = name_record['address'] if user_address is None: # cut to the chase user_address = old_address user_profile = load_name_profile( name, user_zonefile, user_address, old_address, use_zonefile_urls=use_zonefile_urls, storage_drivers=profile_storage_drivers, decode=decode_profile) if user_profile is not None: if decode_profile: assert isinstance(user_profile, dict) else: assert type(user_profile) in [str, unicode] else: log.debug("WARN: no user profile for %s" % name) if create_if_absent: user_profile = user_db.make_empty_user_profile() else: return (None, {'error': 'Failed to load user profile'}) # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. if include_name_record: if name_record is None: name_record = proxy.get_name_blockchain_record(name) if name_record is None or 'error' in name_record: log.error("Failed to look up name record for '%s'" % name) return (None, {'error': 'Failed to look up name record'}) user_zonefile['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: user_zonefile['raw_zonefile'] = raw_zonefile return (user_profile, user_zonefile)
def parse_nameop( opcode, payload, fake_pubkey, recipient=None, recipient_address=None, import_update_hash=None, burn_address=None, reveal_address=None ): opcode_name = OPCODE_NAMES[opcode] pubk = virtualchain.BitcoinPublicKey(fake_pubkey) address = pubk.address() script_pubkey = virtualchain.make_payment_script( address ) senders = [{ "script_pubkey": script_pubkey, "script_type": "pubkeyhash", "addresses": [ address ] }] # just enough to get the public key inputs = [{ 'script': 'ignored {}'.format(fake_pubkey).encode('hex') }] script = "OP_RETURN %s" % payload try: scripthex = virtualchain.make_data_script(binascii.hexlify(payload)) except: if len(payload) == 0: scripthex = "6a" else: print 'failed on {}'.format(payload) raise outputs = [{ 'script': scripthex, 'value': 0 }] if recipient_address is not None: script = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" % binascii.hexlify( virtualchain.lib.hashing.bin_double_sha256( fake_pubkey ) ) scripthex = virtualchain.make_payment_script( recipient_address ) outputs.append( { 'script': scripthex, "value": 10000000 }) if import_update_hash is not None: script = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" % import_update_hash scripthex = virtualchain.make_payment_script( virtualchain.hex_hash160_to_address( import_update_hash ) ) outputs.append( { "script": scripthex, "value": 10000000 }) elif burn_address is not None: scripthex = virtualchain.make_payment_script( burn_address ) outputs.append( { "script": scripthex, "value": 10000000 }) elif reveal_address is not None: scripthex = virtualchain.make_payment_script( reveal_address ) outputs.append( { "script": scripthex, "value": 10000000 }) try: op = op_extract( opcode_name, payload, senders, inputs, outputs, 488501, 0, "00" * 64 ) except AssertionError, ae: # failed to parse return None
def run_zonefilemanage(): working_dir = "/tmp/zonefilemanage" # errase prior state if os.path.exists(working_dir): log.debug("Remove %s " % working_dir) shutil.rmtree(working_dir) if not os.path.exists(working_dir): os.makedirs(working_dir) # export to test os.environ["VIRTUALCHAIN_WORKING_DIR"] = working_dir # set up bitcoind bitcoind_regtest_reset() virtualchain_working_dir = os.environ["VIRTUALCHAIN_WORKING_DIR"] spv_header_path = os.path.join(virtualchain_working_dir, "spv_headers.dat") virtualchain.setup_virtualchain(state_engine) # db = state_engine.get_db_state(disposition=state_engine.DISPOSITION_RW) log.info("Connect to the bitcoind ") # Start the pinger pinger = Pinger() pinger.start() bitcoind = bitcoin_regtest_connect(bitcoin_regtest_opts()) if is_main_worker(): log.info("fill up the default wallet") # set up the default payment wallet default_payment_wallet = MultisigWallet(2, '5JYAj69z2GuFAZHrkhRuBKoCmKh6GcPXgcw9pbH8e8J2pu2RU9z', '5Kfg4xkZ1gGN5ozgDZ37Mn3EH9pXSuWZnQt1pzax4cLax8PetNs', '5JXB7rNxZa8yQtpuKtwy1nWUUTgdDEYTDmaEqQvKKC8HCWs64bL') # load wallets bitcoion_regtest_fill_wallets(wallets, default_payment_wallet=default_payment_wallet) else: # Watch out for wallets for wallet in wallets: 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("Watch out for %s" % (addr)) for wallet in wallets: addr = get_wallet_addr(wallet) unspents = bitcoind.listunspent(0, 200000, [addr]) SATOSHIS_PER_COIN = 10 ** 8 value = sum([int(round(s["amount"] * SATOSHIS_PER_COIN)) for s in unspents]) print >> sys.stderr, "Address %s loaded with %s satoshis" % (addr, value) db = state_engine.get_db_state(disposition=state_engine.DISPOSITION_RW) # Kill the pid for use this port return_code, output = commands.getstatusoutput("netstat -apn | grep %s | grep python| awk '{print $7}'" % RPC_SERVER_PORT) if 'python' in output: import re pattern = re.compile("(\d+)/python") match = pattern.search(output) if match: port = match.group(1) rc = os.system("kill -9 %s" % port) if rc != 0: log.exception("force kill failed") os.abort() # Start up the rpc server server = VoteServer() server.start() set_global_server(server) while True: height = bitcoind.getblockcount() log.info("Sync virtualchain up to %s " % height) virtualchain.sync_virtualchain(bitcoin_regtest_opts(), height, db) # wait for the next block deadline = time.time() + REINDEX_FREQUENCY while time.time() < deadline: try: time.sleep(1) except: break
def get_profile(name, zonefile_storage_drivers=None, profile_storage_drivers=None, proxy=None, user_zonefile=None, name_record=None, include_name_record=False, include_raw_zonefile=False, use_zonefile_urls=True, use_legacy=False, use_legacy_zonefile=True, decode_profile=True): """ Given a name, look up an associated profile. Do so by first looking up the zonefile the name points to, and then loading the profile from that zonefile's public key. Notes on backwards compatibility (activated if use_legacy=True and use_legacy_zonefile=True): * (use_legacy=True) If the user's zonefile is really a legacy profile from Onename, then the profile returned will be the converted legacy profile. The returned zonefile will still be a legacy profile, however. The caller can check this and perform the conversion automatically. * (use_legacy_zonefile=True) If the name points to a current zonefile that does not have a data public key, then the owner address of the name will be used to verify the profile's authenticity. Returns (profile, zonefile) on success. If include_name_record is True, then zonefile['name_record'] will be defined and will contain the user's blockchain information Returns (None, {'error': ...}) on failure """ proxy = get_default_proxy() if proxy is None else proxy raw_zonefile = None if user_zonefile is None: user_zonefile = get_name_zonefile( name, proxy=proxy, name_record=name_record, include_name_record=True, storage_drivers=zonefile_storage_drivers, include_raw_zonefile=include_raw_zonefile, allow_legacy=True) if 'error' in user_zonefile: return None, user_zonefile raw_zonefile = None if include_raw_zonefile: raw_zonefile = user_zonefile.pop('raw_zonefile') user_zonefile = user_zonefile['zonefile'] # is this really a legacy profile? if blockstack_profiles.is_profile_in_legacy_format(user_zonefile): if not use_legacy: return (None, {'error': 'Profile is in legacy format'}) # convert it log.debug('Converting legacy profile to modern profile') user_profile = blockstack_profiles.get_person_from_legacy_format( user_zonefile) elif not user_db.is_user_zonefile(user_zonefile): if not use_legacy: return (None, {'error': 'Name zonefile is non-standard'}) # not a legacy profile, but a custom profile log.debug('Using custom legacy profile') user_profile = copy.deepcopy(user_zonefile) else: # get user's data public key data_address, owner_address = None, None try: user_data_pubkey = user_db.user_zonefile_data_pubkey(user_zonefile) if user_data_pubkey is not None: user_data_pubkey = str(user_data_pubkey) data_address = virtualchain.BitcoinPublicKey( user_data_pubkey).address() except ValueError: # multiple keys defined; we don't know which one to use user_data_pubkey = None if not use_legacy_zonefile and user_data_pubkey is None: # legacy zonefile without a data public key return (None, {'error': 'Name zonefile is missing a public key'}) # find owner address if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error( 'Failed to look up name record for "{}"'.format(name)) return None, {'error': 'Failed to look up name record'} assert 'address' in name_record.keys(), json.dumps(name_record, indent=4, sort_keys=True) owner_address = name_record['address'] # get user's data public key from the zonefile urls = None if use_zonefile_urls and user_zonefile is not None: urls = user_db.user_zonefile_urls(user_zonefile) user_profile = storage.get_mutable_data( name, user_data_pubkey, data_address=data_address, owner_address=owner_address, urls=urls, drivers=profile_storage_drivers, decode=decode_profile, ) if user_profile is None or json_is_error(user_profile): if user_profile is None: log.debug('WARN: no user profile for {}'.format(name)) else: log.debug('WARN: failed to load profile for {}: {}'.format( name, user_profile['error'])) return None, {'error': 'Failed to load user profile'} # finally, if the caller asked for the name record, and we didn't get a chance to look it up, # then go get it. if include_name_record: if name_record is None: name_record = get_name_blockchain_record(name, proxy=proxy) if name_record is None or 'error' in name_record: log.error('Failed to look up name record for "{}"'.format(name)) return None, {'error': 'Failed to look up name record'} user_zonefile['name_record'] = name_record if include_raw_zonefile: if raw_zonefile is not None: user_zonefile['raw_zonefile'] = raw_zonefile return user_profile, user_zonefile