def send_to_stealth_address(self, stealth_addr, ephemeral_private=None): if isinstance(stealth_addr, str): stealth_addr = bc.StealthAddress.from_string(stealth_addr) assert stealth_addr is not None # Sender generates a new ephemeral key. if ephemeral_private is None: ephemeral_private = StealthSender._random_ephemeral_secret() ephemeral_public = ephemeral_private.to_public() spend_keys = stealth_addr.spend_keys() assert spend_keys # Sender derives stealth public, requiring ephemeral private. self.sender_public = bc.uncover_stealth(stealth_addr.scan_key(), ephemeral_private, spend_keys[0]) self._send_address = bc.PaymentAddress.from_point( self.sender_public, self._version) metadata = ephemeral_public.data[1:] assert len(metadata) == 32 meta_script = bc.Script.from_ops( [bc.Opcode.return_, metadata + StealthSender._random_data(8)]) return meta_script, self._send_address
def stealth_round_trip(): expected_stealth_private = bc.EcSecret.from_bytes( bytes.fromhex(STEALTH_PRIVATE)) # Receiver generates a new scan private. scan_private = bc.EcSecret.from_bytes(bytes.fromhex(SCAN_PRIVATE)) scan_public = scan_private.to_public() assert scan_public.data.hex() == SCAN_PUBLIC # Receiver generates a new spend private. spend_private = bc.EcSecret.from_bytes(bytes.fromhex(SPEND_PRIVATE)) spend_public = spend_private.to_public() assert spend_public.data.hex() == SPEND_PUBLIC # Sender generates a new ephemeral key. ephemeral_private = bc.EcSecret.from_bytes( bytes.fromhex(EPHEMERAL_PRIVATE)) ephemeral_public = ephemeral_private.to_public() assert ephemeral_public.data.hex() == EPHEMERAL_PUBLIC # Sender derives stealth public, requiring ephemeral private. sender_public = bc.uncover_stealth(scan_public, ephemeral_private, spend_public) assert sender_public.data.hex() == STEALTH_PUBLIC # Receiver derives stealth public, requiring scan private. receiver_public = bc.uncover_stealth(ephemeral_public, scan_private, spend_public) assert receiver_public.data.hex() # Only reciever can derive stealth private, as it requires both scan # and spend private. stealth_private = bc.uncover_stealth(ephemeral_public, scan_private, spend_private) # This shows that both parties have actually generated stealth public. stealth_public = stealth_private.to_public() assert stealth_public.data.hex() == STEALTH_PUBLIC # Both parties therefore have the ability to generate the p2pkh address. # versioning: stealth_address::main corresponds to payment_address::main_p2pkh public = bc.EcPublic.from_compressed(stealth_public) address = bc.PaymentAddress.from_point(public, bc.PaymentAddress.mainnet_p2kh) assert str(address) == P2PKH_ADDRESS
def derive_address(self, ephemeral_public): spend_public = self.spend_private.to_public() self.receiver_public = bc.uncover_stealth(ephemeral_public, self.scan_private, spend_public) self.derived_address = bc.PaymentAddress.from_point( self.receiver_public, self._version) return self.derived_address
def derive_private(self, ephemeral_public): receiver_private = bc.uncover_stealth(ephemeral_public, self.scan_private, self.spend_private) return receiver_private