Hi,

There have been attempts to create static payment codes that function as a way 
for transacting parties to create "private" addresses, where private stands for 
"known only to transacting parties". BIP47 was one such standard.

The standard suffered from a number of problems:

1. The standard promised extensibility through versioning but it never used 
that capability to follow innovations in the Bitcoin protocol. It was designed 
around the idea that legacy p2pkh addresses would always be the primary and 
only way to transact. As new standard script types started to emerge (Segwit 
v0, Taproot), the creators dealt with the problem by stating that implementing 
wallets should scan for all existing standard scripts. The inability of payment 
codes to explicitly state which address types they derive places a burden on 
more resource constrained wallets.

2. The standard relied on a notification mechanism in order to connect a sender 
with a recipient, which included either offchain technology (Bitmessage), or so 
called "notification addresses" which a) left a footprint b) created toxic 
change. That type of footprint is particularly harmful because it makes it 
obvious that a particular recipient is going to receive private transactions. 
If the notifying party performs this process with coins linked to its identity 
(i.e. tainted or non-anonymized inputs), it forever becomes visible that Alice 
connected with Bob despite the fact that her payment code was blinded. While 
future transactions and their amounts aren't visible, this metadata makes it 
possible to build a social graph.

3. The standard was implemented only by an entity that disavowed the BIP 
process and didn't wish to use it to keep the standard up to date. Further 
updates did take place but only outside the BIP process, creating a lack of 
clarity as to what the real specification is. Ultimately the standard was 
abandoned.

I propose to build on the idea of payment codes under a new BIP with the 
following principal differences:

1. The new standard will allocate a 2-byte bitflag array that will signal 
address/script types that the receiver is deriving. Since the vast majority of 
scripts are p2pkh (47.3%) and p2wpkh (26.2%), bits 0 and 1 will be used for 
those respectively. Bit 2 will be p2tr. The remaining 13 bits are reserved for 
future standard script types.

2. Notification transactions still exist but no longer leave a privacy 
footprint on the blockchain. Instead, a notification transaction is simply a 
single OP_RETURN containing a value that only Alice and Bob can calculate. If 
Alice's notification transaction uses UTXOs not associated with her identity, 
there is never a footprint showing that either her or Bob are using private 
payments. If Alice uses tainted coins, only she is exposed as a user of Private 
Payments but Bob still isn't.

3. Payment code versioning is no longer done because it creates the potential 
for fragmentation and disparate standard updates by different parties that 
don't follow the BIP process (BIP47 is a good example of that).

4. Relying on static compressed pubkeys as opposed to extended keys means 
shorter payment codes.

==Proposed Payment Code Structure==

bytes 0-1: - enabled (watched) address types (16 possible address types)
bytes 2-35: - compressed public key P

==Encoding==

A payment code is encoded in base58check and the version byte produces "S" for 
the first character. A code might look like 
"SwLUHs3UfMUXq956aXLTUPPfow7a8gDfSUUAtafwqHcobf6mKcMbJk".

==Pubkey Derivation==

Recipient's payment code pubkey `P` is derived from a master key using the 
following path: `m/purpose'/coin_type'/account'`. `purpose` will be defined 
once a BIP number is assigned. Its corresponding private key is `p`.

Notifier/sender's pubkey `N` is derived using the following derivation path: 
`m/purpose'/coin_type'/account'/*`, where each recipient gets a new index. This 
way send-side privacy is always preserved. Its corresponding private key is `n`.

==Notifications==

Alice wants to notify Bob that he will receive future payments from her. Alice 
selects any UTXO in her wallet (preferably not associated with her) and 
`n_Alice`. Alice selects the public key contained in Bob's payment code 
`P_Bob`. Alice performs the following process (`*` and `+` are EC operations):

notification = SHA256(n_Alice * P_Bob)

Alice then constructs a 72-byte OP_RETURN output whose value is set to `BIPXXXX 
+ notification + N_Alice` (`+` is concat) and sends it in a transaction 
containing no other outputs (XXXX to be replaced once a BIP number is 
assigned). Alice MUST now keep track of `n_Alice` or its derivation path as it 
will be used in future transactions exclusively with Bob (not for spending but 
to calculate secret addresses).

Bob's wallet receives whole blocks but doesn't need to waste resources on 
decoding them if the environment is resource constrained. Bob simply needs find 
the string BIPXXXX in the binary blob that represents an undecoded block. Once 
found, Bob extracts the subsequent 32 bytes (`notification`) and the subsequent 
33 bytes (`N_Alice`). The benefit of this approach is that Bob doesn't have to 
decode blocks and extract pubkeys from scriptsigs.

Since ECDH dictates that SHA256(n_Alice * P_Bob) == SHA256(N_Alice * p_Bob), 
Bob calculates the expected notification value and checks if it matches the 
first value in the payload. If so, Bob found a notification transaction 
addressed to himself and stores `N_Alice` in order to be able to detect and 
spend future payments from Alice. The added benefit of this approach over BIP47 
is that Bob doesn't learn Alice's payment code, so Alice can pay Bob without 
revealing her identity. To take advantage of these privacy benefits, Alice 
simply has to engage in coin control on her end. A real world scenario where 
this might be useful is anonymous donations to a party whose wallet may be 
seized in the future. Seizing such a wallet won't reveal who Alice is (as long 
as she engages in coin control), whereas BIP47 would by default leak her 
identity even if her coins as anonymized.

If this process fails for any reason, Bob assumes a spurious notification or 
one not addressed to himself and gives up.

==Transacting==

Now that they are connected, Alice can send transactions to Bob. Alice needs to 
keep track of her transaction count toward Bob; let's name that counter `X`. 
This process is similar to what BIP47 does.

Alice calculates a secret point:

S = n_Alice * P_Bob

Alice calculates a shared secret:

s = SHA256(S, X)

Alice calculates Bob's ephemeral public key and its associated address where 
the funds will be sent:

P_Bob' = P_Bob + s*G

When Bob detects a payment to `P_Bob'`, he can spend such coins by calculating 
the shared secret `s` in the same manner using `N_Alice` and `p_Bob` and 
performing:

p_bob' = p_bob + s

The fact that Alice and Bob are using a shared counter means we can do away 
with chain codes and make payment codes much smaller. Bob simply needs to 
derive a number of addresses to watch with respect to some gap limit (which can 
be as low as 1 in practice).

==Anti-spam==

While DoS hasn't been a problem with BIP47, it is possible to build anti-spam 
measures into payment codes. The owner of a code could simply demand that a 
notification transaction meets some minimum miner fee or a multiple of some 
trailing average. This would help prevent spam notifications that might 
otherwise overwhelm a payment code with addresses to watch. But that is purely 
optional.

Looking forward to hearing thoughts and ideas.

Alfred

_______________________________________________
bitcoin-dev mailing list
bitcoin-dev@lists.linuxfoundation.org
https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev

Reply via email to