Subject: Re: [Bitcoin-development] Stealth Addresses

From: Peter Todd <[email-masked]>
Date: Tue, 14 Jan 2014 15:48:06 -0500

On Tue, Jan 14, 2014 at 11:12:40AM -0800, Jeremy Spilman wrote:
> Maybe you are saying:
> 
>   byte[] S = EC.DH(e, Q2);
>   byte[] q1New = EC.PointAdd(Q1, Util.SingleSHA256(S));
>   byte[] q2New = EC.PointAdd(Q2, Util.SingleSHA256(S));
> 
> But the payment would have (q2New - q1New) == (Q2 - Q1), so I think
> not entirely stealth? OK, let's fix that by adding a counter to the
> hash function...

Good catch, yeah, use the master shared secret to derive per-pubkey
secrets.

>   byte[] S = EC.DH(e, Q2);
>   byte[] q1New = EC.PointAdd(Q1, Util.SingleSHA256(S || 1));
>   byte[] q2New = EC.PointAdd(Q2, Util.SingleSHA256(S || 2));
>   stealthTx.Vout.Add(TxOut.PayToMultiSig(Util.Amount(".995"), 2, 2,
> q1New, q2New));
>   stealthTx.Vout.Add(TxOut.OpReturn(P));
> 
> This is assuming we want to put q2New somewhere into the
> transaction, which, is it even required?
> 
>   byte[] S = EC.DH(e, Q2);
>   byte[] q1New = EC.PointAdd(Q1, Util.SingleSHA256(S));
>   stealthTx.Vout.Add(TxOut.PayToPubKeyHash(Util.Amount(".995"), q1New);
>   stealthTx.Vout.Add(TxOut.OpReturn(P));

Well like I said, you shouldn't force the txout to be exactly a 2-of-2
multisig - the recipient might be using a multi-factor wallet for
instance. So, if I understand your code, what you want is the following:

byte[] Q = <payee root pubkeys>;
byte[] Q_Scan = <may or may not be provided in Q>
int m = <# of pubkeys required to redeem>;
byte[] S = EC.DH(e, Q_Scan);

byte[] qDerived[];
for (int = 0; i < len(Q); i++){
    qDerived[i] = EC.PointAdd(Q[i], Util.SingleSHA256(S || i));
}

// Best to have a single canonical order re: anonymity set.
qDerived = sorted(qDerived);

if (len(Q) > 1){
    stealthTx.Vout.Add(TxOut.PayToMultiSig(amount, m, len(Q), qDerived));
} else {
    stealthTx.Vout.Add(TxOut.PayToPubKeyHash(amount, qDerived[0]);
}
stealthTx.Vout.Add(TxOut.OpReturn(P));

Finally, it would probably be better if the multisig output was wrapped
in a P2SH output to better match the behavior of other wallets for the
sake of a bigger anonymity set - seems that stuff that is implementing
multifactor wallets and escrow is using P2SH to do it rather than bare
multisig. Also there's quite a bit of support for making bare multisig
not IsStandard() to discourage data-storage applications.

-- 
'peter'[:-1]@petertodd.org
00000000000000010c474cd4e25913535ec1c166b6d43fbdd9a5f2726711ced7