def test_sec(self): pair_blob = h2b('0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8') public_pair = encoding.sec_to_public_pair(pair_blob, strict=False) try: public_pair = encoding.sec_to_public_pair(pair_blob, strict=True) self.fail("sec_to_public_pair unexpectedly succeeded") except encoding.EncodingError: pass
def test_sec(self): pair_blob = h2b( "0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" ) public_pair = encoding.sec_to_public_pair(pair_blob, strict=False) try: public_pair = encoding.sec_to_public_pair(pair_blob, strict=True) self.fail("sec_to_public_pair unexpectedly succeeded") except encoding.EncodingError: pass
def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = h2b(public_pair_sec) c_sec = h2b(c_public_pair_sec) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual( secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = public_pair_for_secret_exponent( generator_secp256k1, secret_exponent) pk_public_pair = sec_to_public_pair(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = sec_to_public_pair(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual( public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual( bitcoin_address_to_hash160_sec(c_address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual( bitcoin_address_to_hash160_sec(address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=False))
def pcode_to_public_node(pcode): pcode_bytes = b58_to_pcode(pcode) sec = pcode_bytes[2:35] public_pair = encoding.sec_to_public_pair(sec) chain_code = pcode_bytes[36:68] node = BIP32Node( netcode=NETCODE, chain_code=chain_code, public_pair=public_pair ) return node
def solve_finalize_commit(self, **kwargs): hash160_lookup = kwargs.get("hash160_lookup") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") existing_script = kwargs.get("existing_script") # FIXME validate on receiving the commit # validate payer sig opcode, data, pc = tools.get_opcode(existing_script, 0) # OP_0 opcode, payer_sig, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(payer_sig) try: public_pair = encoding.sec_to_public_pair(self.payer_sec) sig_pair, signature_type = parse_signature_blob(payer_sig) valid = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if not valid: raise Exception("Invalid payer public_pair!") except (encoding.EncodingError, UnexpectedDER): raise Exception("Invalid payer public_pair!") # sign private_key = hash160_lookup.get(encoding.hash160(self.payee_sec)) secret_exponent, public_pair, compressed = private_key payee_sig = self._create_script_signature( secret_exponent, sign_value, signature_type ) script_text = "OP_0 {payer_sig} {payee_sig} OP_1".format( payer_sig=b2h(payer_sig), payee_sig=b2h(payee_sig) ) return tools.compile(script_text)
def from_sec(class_, sec): """ Create a key from an sec bytestream (which is an encoding of a public pair). """ public_pair = sec_to_public_pair(sec) return Key(public_pair=public_pair, prefer_uncompressed=not is_sec_compressed(sec))
def is_pubkey_valid(pubkey): try: sec=encoding.binascii.unhexlify(pubkey) public_pair=encoding.sec_to_public_pair(sec) return curves.ecdsa.point_is_valid(ecdsa.generator_secp256k1, public_pair[0], public_pair[1]) except TypeError: return False
def pubkey_to_address(pubkey_hex): sec = binascii.unhexlify(pubkey_hex) compressed = encoding.is_sec_compressed(sec) public_pair = encoding.sec_to_public_pair(sec) address_prefix = b'\x6f' if config.TESTNET else b'\x00' return encoding.public_pair_to_bitcoin_address( public_pair, compressed=compressed, address_prefix=address_prefix)
def decrypt(self, raw, mac=16): """Decrypt a message sent to the holder of this key. """ # get the nonce-point x, y = sec_to_public_pair(raw[:33]) # validation that this point lies on the curve happens in # initialization nonce_point = PubKey(BasePoint.curve(), x, y) # message gives us the ciphered message message = raw[33:-mac] # checksum makes sure everything is transmitted properly checksum = raw[-mac:] # calculate the shared secret tmp = nonce_point * self.exponent shared_secret = PubKey(tmp.curve(), tmp.x(), tmp.y()) # derive keys key = nonce_point.kdf(shared_secret) cipher = AES.new(key[:32]) # verify the checksum checksum_maker = hmac.new(key[32:], digestmod=hashlib.sha256) checksum_maker.update(message) if checksum_maker.digest()[:mac] != checksum: raise RuntimeError("Invalid checksum on encoded message string") return cipher.decrypt(message)
def encrypt_message(address, message): """Encrypts a message to an address. """ hex_public_key = get_public_key_from_address(address) x, y = sec_to_public_pair(hex_public_key.decode('hex')) public_key = PubKey(BasePoint.curve(), x, y) encrypted = b64encode(public_key.encrypt(message)) return encrypted
def from_sec(class_, sec, netcode=None): """ Create a key from an sec bytestream (which is an encoding of a public pair). """ public_pair = sec_to_public_pair(sec) return class_(public_pair=public_pair, is_compressed=is_sec_compressed(sec), netcode=netcode)
def add_sec_annotations(a1, data, address_prefix): pair = sec_to_public_pair(data) is_compressed = is_sec_compressed(data) a1.append( "SEC for %scompressed %s" % ("" if is_compressed else "un", public_pair_to_bitcoin_address( pair, compressed=is_compressed, address_prefix=address_prefix)))
def do_test(as_public_pair, as_sec, is_compressed, as_hash160_sec, as_bitcoin_address): self.assertEqual(encoding.sec_to_public_pair(as_sec), as_public_pair) self.assertEqual(encoding.public_pair_to_sec(as_public_pair, compressed=is_compressed), as_sec) self.assertEqual(encoding.is_sec_compressed(as_sec), is_compressed) self.assertEqual(encoding.public_pair_to_hash160_sec(as_public_pair, compressed=is_compressed), as_hash160_sec) self.assertEqual(encoding.hash160_sec_to_bitcoin_address(as_hash160_sec), as_bitcoin_address) self.assertEqual(encoding.public_pair_to_bitcoin_address(as_public_pair, compressed=is_compressed), as_bitcoin_address) self.assertTrue(encoding.is_valid_bitcoin_address(as_bitcoin_address)) bad_address = as_bitcoin_address[:17] + chr(ord(as_bitcoin_address[17]) + 1) + as_bitcoin_address[18:] self.assertFalse(encoding.is_valid_bitcoin_address(bad_address))
def compress_pubkey(uncompressed_pubkey): """ Convert uncompressed public key to compressed public key. Args: pubkey (str): Hex encoded 65Byte uncompressed public key Return: str: Hex encoded 33Byte compressed public key """ public_pair = encoding.sec_to_public_pair(h2b(uncompressed_pubkey)) return b2h(encoding.public_pair_to_sec(public_pair, compressed=True))
def parse_sig(script_hex): """Takes a hex string of the script signature and returns the corresponding user obj Its in the format: [3a05e435... 027efd7...] where: [Signature, Sec_pubkey] """ pubkey_hex = script_hex.split(' ')[1] sec = sec_to_public_pair(a2b_hex(pubkey_hex)) addr = public_pair_to_bitcoin_address(sec, address_prefix=NETWORK) return Username(addr)
def uncompress_pubkey(pubkey): """ Convert compressed public key to uncompressed public key. Args: pubkey (str): Hex encoded 33Byte compressed public key Return: str: Hex encoded uncompressed 65byte public key (4 + x + y). """ public_pair = encoding.sec_to_public_pair(h2b(pubkey)) return b2h(encoding.public_pair_to_sec(public_pair, compressed=False))
def solve_finalize_commit(self, **kwargs): hash160_lookup = kwargs.get("hash160_lookup") signature_type = kwargs.get("signature_type") existing_script = kwargs.get("existing_script") # validate existing script reference_script_hex = _compile_commit_scriptsig( "deadbeef", "deadbeef", b2h(self.script)) _validate(reference_script_hex, b2h(existing_script)) # check provided payer signature try: opcode, data, pc = tools.get_opcode(existing_script, 0) # OP_0 opcode, payer_sig, pc = tools.get_opcode(existing_script, pc) # verify signature type sig_r_s, actual_signature_type = parse_signature_blob(payer_sig) assert (signature_type == actual_signature_type) # verify payer signature public_pair = encoding.sec_to_public_pair(self.payer_sec) sign_value = kwargs.get("sign_value") public_pair = encoding.sec_to_public_pair(self.payer_sec) sign_value = kwargs["signature_for_hash_type_f"]( signature_type, kwargs["script_to_hash"]) if not ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_r_s): raise InvalidPayerSignature("invalid r s values") except UnexpectedDER: raise InvalidPayerSignature("not in DER format") # sign private_key = hash160_lookup.get(encoding.hash160(self.payee_sec)) secret_exponent, public_pair, compressed = private_key payee_sig = self._create_sig(secret_exponent, **kwargs) script_asm = COMMIT_SCRIPTSIG.format(payer_sig=b2h(payer_sig), payee_sig=b2h(payee_sig)) return tools.compile(script_asm)
def do_test(exp_hex, wif, c_wif, public_pair_sec, c_public_pair_sec, address_b58, c_address_b58): secret_exponent = int(exp_hex, 16) sec = h2b(public_pair_sec) c_sec = h2b(c_public_pair_sec) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=False), wif) self.assertEqual(secret_exponent_to_wif(secret_exponent, compressed=True), c_wif) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(wif) self.assertEqual(exponent, secret_exponent) self.assertFalse(compressed) exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(c_wif) self.assertEqual(exponent, secret_exponent) self.assertTrue(compressed) public_pair = secret_exponent * secp256k1_generator pk_public_pair = sec_to_public_pair(sec) compressed = is_sec_compressed(sec) self.assertEqual(pk_public_pair, public_pair) self.assertFalse(is_sec_compressed(sec)) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=False), sec) pk_public_pair = sec_to_public_pair(c_sec) compressed = is_sec_compressed(c_sec) self.assertEqual(pk_public_pair, public_pair) self.assertTrue(compressed) self.assertEqual(public_pair_to_sec(pk_public_pair, compressed=True), c_sec) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=True) self.assertEqual(bca, c_address_b58) self.assertEqual(bitcoin_address_to_hash160_sec(c_address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=True)) bca = public_pair_to_bitcoin_address(pk_public_pair, compressed=False) self.assertEqual(bca, address_b58) self.assertEqual(bitcoin_address_to_hash160_sec(address_b58), public_pair_to_hash160_sec(pk_public_pair, compressed=False))
def address_from_pubkey(pubkey, netcode="BTC"): """ Get bitcoin address from given public key. Args: pubkey (str): Hex encoded 33Byte compressed public key netcode (str): Netcode for resulting bitcoin address. Return: str: Bitcoin address """ prefix = networks.address_prefix_for_netcode(netcode) public_pair = encoding.sec_to_public_pair(h2b(pubkey)) return encoding.public_pair_to_bitcoin_address(public_pair, address_prefix=prefix)
def verify(pubkey, signature, data): """ Verify data is signed by private key. Args: pubkey (str): Hex encoded 33Byte compressed public key signature (str): Hex encoded signature in DER format. Return: bool: True if signature is valid. """ public_pair = encoding.sec_to_public_pair(h2b(pubkey)) val = util.bytestoint(h2b(data)) sig = ecdsa.util.sigdecode_der(h2b(signature), G.order()) return ecdsa_verify(G, public_pair, val, sig)
def address_h160_from_script (script): s = disassemble(script).split(' ') if 'OP_HASH160' in s: p = s.index('OP_HASH160') if len(s) > p+1: return h2b(s[p+1]) elif 'OP_CHECKSIG' in s: p = s.index('OP_CHECKSIG') if len(s[p-1]) in (66, 130): # public key sec = h2b(s[p-1]) return public_pair_to_hash160_sec(sec_to_public_pair(sec), is_sec_compressed(sec)) else: logger.warn("Can't parse address from script: %s" % (s)) return None
def bitcoin_address_for_script(self, is_test=False): try: r = self.match_script_to_templates() if len(r) != 1 or len(r[0]) != 2: return None if r[0][0] == opcodes.OP_PUBKEYHASH: return hash160_sec_to_bitcoin_address(r[0][1], is_test=is_test) if r[0][0] == opcodes.OP_PUBKEY: sec = r[0][1] return public_pair_to_bitcoin_address( sec_to_public_pair(sec), compressed=is_sec_compressed(sec), is_test=is_test) except SolvingError: pass return None
def __call__(self, tx_out_script, signature_hash, signature_type): """Figure out how to create a signature for the incoming transaction, and sign it. tx_out_script: the tx_out script that needs to be "solved" signature_hash: the bignum hash value of the new transaction reassigning the coins signature_type: always SIGHASH_ALL (1) """ if signature_hash == 0: raise SolvingError("signature_hash can't be 0") tx_script = TxScript(tx_out_script) opcode_value_list = tx_script.match_script_to_templates() ba = bytearray() compressed = True for opcode, v in opcode_value_list: if opcode == opcodes.OP_PUBKEY: public_pair = sec_to_public_pair(v) bitcoin_address = public_pair_to_bitcoin_address( public_pair, compressed=compressed) elif opcode == opcodes.OP_PUBKEYHASH: bitcoin_address = hash160_sec_to_bitcoin_address(v) else: raise SolvingError("can't determine how to sign this script") secret_exponent = self.wallet.getsecretexponent(bitcoin_address) r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent, signature_hash) sig = der.sigencode_der(r, s) + bytes_from_int(signature_type) ba += tools.compile(binascii.hexlify(sig).decode("utf8")) if opcode == opcodes.OP_PUBKEYHASH: public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, secret_exponent) ba += tools.compile( binascii.hexlify( public_pair_to_sec( public_pair, compressed=compressed)).decode("utf8")) return bytes(ba)
def parse_as_public_pair(s): try: if s[:2] in (["02", "03", "04"]): return encoding.sec_to_public_pair(encoding.h2b(s)) except (encoding.EncodingError, binascii.Error): pass for c in ",/": if c in s: s0, s1 = s.split(c, 1) v0 = parse_as_number(s0) if v0: if s1 in ("even", "odd"): return ecdsa.public_pair_for_x(ecdsa.generator_secp256k1, v0, is_even=(s1=='even')) v1 = parse_as_number(s1) if v1: if not ecdsa.is_public_pair_valid(ecdsa.generator_secp256k1, (v0, v1)): sys.stderr.write("invalid (x, y) pair\n") sys.exit(1) return (v0, v1)
def __call__(self, tx_out_script, signature_hash, signature_type): """Figure out how to create a signature for the incoming transaction, and sign it. tx_out_script: the tx_out script that needs to be "solved" signature_hash: the bignum hash value of the new transaction reassigning the coins signature_type: always SIGHASH_ALL (1) """ if signature_hash == 0: raise SolvingError("signature_hash can't be 0") tx_script = TxScript(tx_out_script) opcode_value_list = tx_script.match_script_to_templates() ba = bytearray() compressed = True for opcode, v in opcode_value_list: if opcode == opcodes.OP_PUBKEY: public_pair = sec_to_public_pair(v) bitcoin_address = public_pair_to_bitcoin_address(public_pair, compressed=compressed) elif opcode == opcodes.OP_PUBKEYHASH: bitcoin_address = hash160_sec_to_bitcoin_address(v) else: raise SolvingError("can't determine how to sign this script") secret_exponent = self.wallet.getsecretexponent(bitcoin_address) r, s = ecdsa.sign(ecdsa.generator_secp256k1, secret_exponent, signature_hash) sig = der.sigencode_der(r, s) + bytes_from_int(signature_type) ba += tools.compile(binascii.hexlify(sig).decode("utf8")) if opcode == opcodes.OP_PUBKEYHASH: public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, secret_exponent) ba += tools.compile( binascii.hexlify(public_pair_to_sec(public_pair, compressed=compressed)).decode("utf8") ) return bytes(ba)
def pubkey_to_address(pubkey_hex): sec = binascii.unhexlify(pubkey_hex) compressed = encoding.is_sec_compressed(sec) public_pair = encoding.sec_to_public_pair(sec) address_prefix = b'\x6f' if config.TESTNET else b'\x00' return encoding.public_pair_to_bitcoin_address(public_pair, compressed=compressed, address_prefix=address_prefix)
cec_key.set_secretbytes(bitcoin.core.x(my_secret_exp)) print("Private Key dec: ", random_nbr) print("Private Key hex: ", my_secret_exp) privkey_wif = encoding.secret_exponent_to_wif(eval('0x' + my_secret_exp), wif_prefix=my_privkey_prefix) print("Private key WIF: ", privkey_wif) privkey_wif = encoding.secret_exponent_to_wif(eval('0x' + my_secret_exp), False, wif_prefix=my_privkey_prefix) print(" uncompressed: ", privkey_wif) cec_key.set_compressed(True) print() pubkey_pair = encoding.sec_to_public_pair(cec_key.get_pubkey()) print("Public key pair: ", pubkey_pair) (pub_key_x, pub_key_y) = pubkey_pair compressed_indicator = True if (pub_key_y % 2) == 0 else False print("Public key y parity? ", 'even' if compressed_indicator else 'odd') #print("Private key hexlify: ", hexlify(cec_key.get_privkey())) print("Public key hex: ", bitcoin.core.b2x(cec_key.get_pubkey())) cec_key.set_compressed(False) print(" uncompressed: ", bitcoin.core.b2x(cec_key.get_pubkey())) addr_compressed = encoding.public_pair_to_bitcoin_address( pubkey_pair, True, address_prefix=my_pubaddr_prefix) addr_uncompressed = encoding.public_pair_to_bitcoin_address( pubkey_pair, False, address_prefix=my_pubaddr_prefix)
def onJoin(self, data): print('Welcome to GreenAddress mnemonic login example') print('\nThis script will login to GA in full mode') self.mnemonic_phrase = getpass('Please write your mnemonic phrase and press enter: ') self.mnemonic_phrase = "hotel helmet envelope amazing often proud scorpion myth shaft differ put expand equal scout piece million hair crater annual echo net eye middle replace" #Generate GA-side wallet path and key info GA_pubkey = binascii.unhexlify("036307e560072ed6ce0aa5465534fb5c258a2ccfbc257f369e8e7a181b16d897b3") GA_pair = sec_to_public_pair(GA_pubkey) GA_chaincode = binascii.unhexlify("b60befcc619bb1c212732770fe181f2f1aa824ab89f8aab49f2e13e3a56f0f04") gawallet = BIP32Node.BIP32Node('XTN', GA_chaincode, public_pair=GA_pair) if sys.version_info.major < 3: m = hmac.new(bytearray(self.mnemonic_phrase), bytearray('GreenAddress!!!'), hashlib.sha512) else: m = hmac.new(bytearray(self.mnemonic_phrase, 'utf-8'), bytearray('GreenAddress!!!', 'utf-8'), hashlib.sha512) gawalletpath = binascii.hexlify(m.digest()) gawalletpath_bin = binascii.unhexlify(gawalletpath) gawalletpath_str = '/'.join(str(struct.unpack('!H', gawalletpath_bin[i*2:(i+1)*2])[0]) for i in range(32)) # 1. Master wallet seed = mnemonic.Mnemonic.to_seed(self.mnemonic_phrase) self.wallet = BIP32Node.BIP32Node.from_master_secret(seed, 'XTN') # Change 'BTC' to 'XTN' for Testnet # 2. Login wallet path = '%X' % random.randint(0, 2**64-1) while len(path) < 16: path = '0' + path path_bin = binascii.unhexlify(path) path_str = '/'.join(str(struct.unpack('!H', path_bin[i*2:(i+1)*2])[0]) for i in range(4)) wallet_login = self.wallet.subkey_for_path(path_str) # 3. Get challenge print('\nLogging in with mnemonic passphrase, requesting challenge') challenge = yield self.call( 'com.greenaddress.login.get_challenge', self.wallet.bitcoin_address(), # disclose_me is required for authentication options=CallOptions(disclose_me=True) ) # 4. Login signature = pycoin_ecdsa.sign(pycoin_ecdsa.generator_secp256k1, wallet_login.secret_exponent(), int(challenge)) if signature[1]+signature[1] > pycoin_ecdsa.generator_secp256k1.order(): signature = (signature[0], pycoin_ecdsa.generator_secp256k1.order() - signature[1]) login_data = yield self.call( "com.greenaddress.login.authenticate", list(map(str, signature)), False, path, options=CallOptions(disclose_me=True) ) if data and login_data: print(login_data) last_login = (login_data['last_login']['at'], login_data['last_login']['country'], login_data['last_login']['ip']) print('\nLogin successful! Last login on %s, from country %s, ip address: %s' % last_login) else: print('\nLogin failed') p2sh_addr = yield self.call( "com.greenaddress.vault.fund", return_pointer=True, options=CallOptions(disclose_me=True)) print(p2sh_addr) validateGAAddr(p2sh_addr, self.wallet) syncWallet(p2sh_addr, self.wallet, gawallet, gawalletpath_str) ''' balance = yield self.call( "com.greenaddress.txs.get_balance", options=CallOptions(disclose_me=True)) print(balance) prep_tx = yield self.call( "com.greenaddress.vault.prepare_tx", rcpt_ad="2MtXwJyVCWLUmNeeNsQt958sV9658ZEpAdn", value="10000", add_fee='sender', priv_data={}, options=CallOptions(disclose_me=True)) print(prep_tx) ''' reactor.stop()
def pubkey_to_address(pubkey_hex): sec = binascii.unhexlify(pubkey_hex) compressed = encoding.is_sec_compressed(sec) public_pair = encoding.sec_to_public_pair(sec) return encoding.public_pair_to_bitcoin_address(public_pair, compressed=compressed)
def solve(self, **kwargs): """ The kwargs required depend upon the script type. hash160_lookup: dict-like structure that returns a secret exponent for a hash160 existing_script: existing solution to improve upon (optional) sign_value: the integer value to sign (derived from the transaction hash) signature_type: usually SIGHASH_ALL (1) """ # we need a hash160 => secret_exponent lookup db = kwargs.get("hash160_lookup") if db is None: raise SolvingError("missing hash160_lookup parameter") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") secs_solved = set() existing_signatures = [] existing_script = kwargs.get("existing_script") if existing_script: pc = 0 opcode, data, pc = tools.get_opcode(existing_script, pc) # ignore the first opcode while pc < len(existing_script): opcode, data, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(data) for sec_key in self.sec_keys: try: public_pair = encoding.sec_to_public_pair(sec_key) sig_pair, signature_type = parse_signature_blob(data) v = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if v: existing_signatures.append(data) secs_solved.add(sec_key) break except encoding.EncodingError: # if public_pair is invalid, we just ignore it pass for sec_key in self.sec_keys: if sec_key in secs_solved: continue if len(existing_signatures) >= self.n: break hash160 = encoding.hash160(sec_key) result = db.get(hash160) if result is None: continue secret_exponent, public_pair, compressed = result binary_signature = self._create_script_signature(secret_exponent, sign_value, signature_type) existing_signatures.append(b2h(binary_signature)) DUMMY_SIGNATURE = "OP_0" while len(existing_signatures) < self.n: existing_signatures.append(DUMMY_SIGNATURE) script = "OP_0 %s" % " ".join(s for s in existing_signatures) solution = tools.compile(script) return solution
def solve(self, **kwargs): """ The kwargs required depend upon the script type. hash160_lookup: dict-like structure that returns a secret exponent for a hash160 existing_script: existing solution to improve upon (optional) sign_value: the integer value to sign (derived from the transaction hash) signature_type: usually SIGHASH_ALL (1) """ # we need a hash160 => secret_exponent lookup db = kwargs.get("hash160_lookup") if db is None: raise SolvingError("missing hash160_lookup parameter") sign_value = kwargs.get("sign_value") signature_type = kwargs.get("signature_type") secs_solved = set() existing_signatures = [] existing_script = kwargs.get("existing_script") if existing_script: pc = 0 opcode, data, pc = tools.get_opcode(existing_script, pc) # ignore the first opcode while pc < len(existing_script): opcode, data, pc = tools.get_opcode(existing_script, pc) sig_pair, actual_signature_type = parse_signature_blob(data) for sec_key in self.sec_keys: try: public_pair = encoding.sec_to_public_pair(sec_key) sig_pair, signature_type = parse_signature_blob(data) v = ecdsa.verify(ecdsa.generator_secp256k1, public_pair, sign_value, sig_pair) if v: existing_signatures.append(data) secs_solved.add(sec_key) break except encoding.EncodingError: # if public_pair is invalid, we just ignore it pass for sec_key in self.sec_keys: if sec_key in secs_solved: continue if len(existing_signatures) >= self.n: break hash160 = encoding.hash160(sec_key) result = db.get(hash160) if result is None: continue secret_exponent, public_pair, compressed = result binary_signature = self._create_script_signature( secret_exponent, sign_value, signature_type) existing_signatures.append(b2h(binary_signature)) DUMMY_SIGNATURE = "OP_0" while len(existing_signatures) < self.n: existing_signatures.append(DUMMY_SIGNATURE) script = "OP_0 %s" % " ".join(s for s in existing_signatures) solution = tools.compile(script) return solution
def get_compressed_pubkey_format(pubkey): public_pair=encoding.sec_to_public_pair(encoding.binascii.unhexlify(pubkey)) return encoding.binascii.hexlify(encoding.public_pair_to_sec(public_pair))
def get_address_of_pubkey(pubkey): public_pair=encoding.sec_to_public_pair(encoding.binascii.unhexlify(pubkey)) return encoding.public_pair_to_bitcoin_address(public_pair)
def _is_valid_pubkey(self, k): try: sec_to_public_pair(binascii.unhexlify(k.encode("ascii"))) return True except EncodingError: return False
def test_bitcoind_cosigning(dev, bitcoind, start_sign, end_sign, import_ms_wallet, clear_ms, explora, try_sign, need_keypress, addr_style): # Make a P2SH wallet with local bitcoind as a co-signer (and simulator) # - send an receive various # - following text of <https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md> # - the constructed multisig walelt will only work for a single pubkey on core side # - before starting this test, have some funds already deposited to bitcoind testnet wallet from pycoin.encoding import sec_to_public_pair from binascii import a2b_hex import re if addr_style == 'legacy': addr_fmt = AF_P2SH elif addr_style == 'p2sh-segwit': addr_fmt = AF_P2WSH_P2SH elif addr_style == 'bech32': addr_fmt = AF_P2WSH try: addr, = bitcoind.getaddressesbylabel("sim-cosign").keys() except: addr = bitcoind.getnewaddress("sim-cosign") info = bitcoind.getaddressinfo(addr) #pprint(info) assert info['address'] == addr bc_xfp = swab32(int(info['hdmasterfingerprint'], 16)) bc_deriv = info['hdkeypath'] # example: "m/0'/0'/3'" bc_pubkey = info['pubkey'] # 02f75ae81199559c4aa... pp = sec_to_public_pair(a2b_hex(bc_pubkey)) # No means to export XPUB from bitcoind! Still. In 2019. # - this fake will only work for for one pubkey value, the first/topmost node = BIP32Node('XTN', b'\x23'*32, depth=len(bc_deriv.split('/'))-1, parent_fingerprint=a2b_hex('%08x' % bc_xfp), public_pair=pp) keys = [ (bc_xfp, None, node), (1130956047, None, BIP32Node.from_hwif('tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')), # simulator: m/45' ] M,N=2,2 clear_ms() import_ms_wallet(M, N, keys=keys, accept=1, name="core-cosign") cc_deriv = "m/45'/55" cc_pubkey = B2A(BIP32Node.from_hwif(simulator_fixed_xprv).subkey_for_path(cc_deriv[2:]).sec()) # NOTE: bitcoind doesn't seem to implement pubkey sorting. We have to do it. resp = bitcoind.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])), 'shared-addr-'+addr_style, addr_style) ms_addr = resp['address'] bc_redeem = a2b_hex(resp['redeemScript']) assert bc_redeem[0] == 0x52 def mapper(cosigner_idx): return list(str2ipath(cc_deriv if cosigner_idx else bc_deriv)) scr, pubkeys, xfp_paths = make_redeem(M, keys, mapper) assert scr == bc_redeem # check Coldcard calcs right address to match got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address( M, xfp_paths, scr, addr_fmt=addr_fmt), timeout=None) assert got_addr == ms_addr time.sleep(.1) need_keypress('x') # clear screen / start over print(f"Will be signing an input from {ms_addr}") if xfp2str(bc_xfp) in ('5380D0ED', 'EDD08053'): # my own expected values assert ms_addr in ( '2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4', '2N1hZJ5mazTX524GQTPKkCT4UFZn5Fqwdz6', 'tb1qpcv2rkc003p5v8lrglrr6lhz2jg8g4qa9vgtrgkt0p5rteae5xtqn6njw9') # Need some UTXO to sign # # - but bitcoind can't give me that (using listunspent) because it's only a watched addr?? # did_fund = False while 1: rr = explora('address', ms_addr, 'utxo') pprint(rr) avail = [] amt = 0 for i in rr: txn = i['txid'] vout = i['vout'] avail.append( (txn, vout) ) amt += i['value'] # just use first UTXO available; save other for later tests break else: # doesn't need to confirm, but does need to reach public testnet/blockstream assert not amt and not avail if not did_fund: print(f"Sending some XTN to {ms_addr} (wait)") bitcoind.sendtoaddress(ms_addr, 0.0001, 'fund testing') did_fund = True else: print(f"Still waiting ...") time.sleep(2) if amt: break ret_addr = bitcoind.getrawchangeaddress() ''' If you get insufficent funds, even tho we provide the UTXO (!!), do this: bitcoin-cli importaddress "2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4" true true Better method: always fund addresses for testing here from same wallet (ie. got from non-multisig to multisig on same bitcoin-qt instance). -> Now doing that, automated, above. ''' resp = bitcoind.walletcreatefundedpsbt([dict(txid=t, vout=o) for t,o in avail], [{ret_addr: amt/1E8}], 0, {'subtractFeeFromOutputs': [0], 'includeWatching': True}, True) assert resp['changepos'] == -1 psbt = b64decode(resp['psbt']) open('debug/funded.psbt', 'wb').write(psbt) # patch up the PSBT a little ... bitcoind doesn't know the path for the CC's key ex = BasicPSBT().parse(psbt) cxpk = a2b_hex(cc_pubkey) for i in ex.inputs: assert cxpk in i.bip32_paths, 'input not to be signed by CC?' i.bip32_paths[cxpk] = pack('<3I', keys[1][0], *str2ipath(cc_deriv)) psbt = ex.as_bytes() open('debug/patched.psbt', 'wb').write(psbt) _, updated = try_sign(psbt, finalize=False) open('debug/cc-updated.psbt', 'wb').write(updated) # have bitcoind do the rest of the signing rr = bitcoind.walletprocesspsbt(b64encode(updated).decode('ascii')) pprint(rr) open('debug/bc-processed.psbt', 'wt').write(rr['psbt']) assert rr['complete'] # finalize and send rr = bitcoind.finalizepsbt(rr['psbt'], True) open('debug/bc-final-txn.txn', 'wt').write(rr['hex']) assert rr['complete'] txn_id = bitcoind.sendrawtransaction(rr['hex']) print(txn_id)
def add_sec_annotations(a1, data, address_prefix): pair = sec_to_public_pair(data) is_compressed = is_sec_compressed(data) a1.append("SEC for %scompressed %s" % ( "" if is_compressed else "un", public_pair_to_bitcoin_address( pair, compressed=is_compressed, address_prefix=address_prefix)))
def from_sec(class_, sec, netcode="BTC"): """ Create a key from an sec bytestream (which is an encoding of a public pair). """ public_pair = sec_to_public_pair(sec) return class_(public_pair=public_pair, is_compressed=is_sec_compressed(sec), netcode=netcode)