Skip to content

Instantly share code, notes, and snippets.

@fivepiece
Last active June 11, 2018 20:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fivepiece/d311b4621edb0149847d8c43e62fe52c to your computer and use it in GitHub Desktop.
Save fivepiece/d311b4621edb0149847d8c43e62fe52c to your computer and use it in GitHub Desktop.
# two participants, Signer and Blinder
# Blinder will own the secret
# Signer will blind-sign Blinder's backout transaction
# if Blinder ever uses Signer's blind signature,
# then Signer will learn the secret
# blind sig construction from :
# https://web.archive.org/web/20170203031622/http://oleganza.com/blind-ecdsa-draft-v2.pdf
# First, negotiate pubkeys.
# Blinder sends Signer two pubkeys, {BLN1, BLN2}
# Signer sends Blinder three pubkeys, {SGN1, SGN2, SGN3}
# notation of points alternate between capital letters for comments,
# and '_pt' or '_pt_*' for variables (sorry, bc can't do capitals :) )
# Then backout construction can begin :
## Signer :
# choose p, q
p1 = rand()
q1 = rand()
# compute P, Q
# P = (1/p)*G
# Q = (q/p)*G
p1_pt = ecmul( invmod(p1, curve_n) )
q1_pt = ecmul( q1 * invmod(p1, curve_n) )
# send P, Q to blinder
## Blinder :
# choose a, b, c, d
a1 = rand()
b1 = rand()
c1 = rand()
d1 = rand()
# compute R (public point of nonce to be used in validation)
# R = (a*c)*P
r1_pt = ecmul_pt( invmod( a1*c1, curve_n), p1_pt)
# r = R_x
uncompresspoint_api(r1_pt, r1_pt[])
r1 = r1_pt[0]
# compute T (pubkey to be used in signature validation)
# T = 1/(a*r) * (b*G + Q + (d/c)*P)
t1_pt_bln = ecmul_pt( invmod( a1*r1, curve_n), ecadd( ecadd( ecmul(b1), q1_pt ), ecmul_pt( d1*invmod(c1, curve_n), p1_pt) ))
# prepare script scr1
# if 2 <SGN1> <SGN2 + T> 2 checkmultisig else [L0] CLTV <BLN1> checksig endif
# prepare script scr2
# if 2 <BLN2> <T> 2 checkmultisig else [L1] CLTV <SGN3> checksig endif
# Blinder wants Signer to blind-sign hash h1
# this is the hash of backout transaction for Blinder from TX3, using scr2
# here set to a random number but really just a hash of payment to Blinder
h1 = rand()
# compute blinded hash h2
# h2 = a*h1 + b
h2 = mod( (a1*h1) + b1, curve_n)
# compute B, D
# B = b*G
# D = d*G
b1_pt = ecmul(b1)
d1_pt = ecmul(d1)
# send {a, c, h2, B, D, scr1, scr2} to Signer
# also send signatures from TX0, paying to TX2
## Signer :
# compute k (secret nonce, corresponding to R)
# k = 1/(c*a*p)
k1 = invmod( c1*a1*p1, curve_n )
# compute T from own values (same T that blinder is supposed to be using)
# T = k * ( (c*p)*B + (q*c)*G + D )
t1_pt_sgn = ecmul_pt( k1*invmod(r1, curve_n), ecadd( ecadd( ecmul_pt(c1*p1, b1_pt), ecmul( q1*c1 ) ), d1_pt ) )
# check that T is used in backout scriptpubkey scr2
t1_pt_bln == t1_pt_sgn
# check that pubkey (SGN2 + T) from scr1 equals is 'T == (SGN2 + T) - SGN2'
# check locktimes for correctness
# if checks are successful, continue
# compute blinded signature s value s1
# s1 = p*h2 + q
s1 = mod( (p1*h2) + q1, curve_n)
# compute h1*G from known values, without knowing h1
# h1*G = (h2/a)*G - (1/a)*B
# h1*G = (1/a) * (h2*G - B)
h1_pt = ecmul_pt( invmod(a1, curve_n), ecadd( ecmul(h2), -b1_pt) )
# check that our blinded signature verifies h1*G by checking the equality
# k*( (c*s)*G + D ) =? h1*G + r*T
ecmul_pt(k1, ecadd( ecmul( c1*s1 ), d1_pt) ) == ecadd( h1_pt, ecmul_pt( r1, t1_pt_sgn ) )
# if check is successful, continue
# send blinded s value s1 to Blinder
# also send signatures paying from TX0 to TX2, and TX1 to TX3
## Blinder :
# unblind s1, producing a valid signature sig1 over h1,
# blind signed by pubkey T and public nonce R, and verify
s2 = mod( (c1*s1) + d1, curve_n)
# verify unblinded signature
ecdsa_verify(h1, t1_pt_bln, r1, s2)
# if verify successful, send Signer signature for TX1 to TX3
# also sign and broadcast TX0
## Signer :
# broadcast TX1
# --------------
# if Blinder ever broadcasts the unblinded signature sig1,
# revealing {h1, s2}, then Blinder can solve for 't' (from t*G = T)
t1 = mod( ((s2*k1) - h1) * invmod(r1, curve_n), curve_n)
ecmul(t1) == t1_pt_sgn
# check correctness
# b =? h2 - a*h1
b1 == mod( h2 - a1*h1, curve_n )
# d =? s2 - c*s1
d1 == mod( s2 - c1*s1, curve_n )
# t =? ( b*c*p + q*c + d )/( a*r*c*p )
t1 == mod( (b1*c1*p1 + q1*c1 + d1) * invmod(a1*r1*c1*p1, curve_n), curve_n)

[ tweaking private keys \ nonces ahead, might be vulnerable ]

bob :

  • locate an alice utxo on chain, cadidate for coinswap
  • figure out alice's pubkey A0 from it (signature's r value)
  • compute secret nonce and its pubkey {b0, B0}
  • compute own privkey and pubkey pair {b2, B2}
  • secret preimage is B2 and the image X is HASH160(B2)
  • perform ecdh using b0 and A0, compute a shared secret e
  • tweak alice's A0 by e*G + A0 to produce A1
  • tweak own's (b0 + e)*G to produce B1
  • compute script scr1:
if hash160 X equalverify A1 else <locktime> CLTV B1 endif checksig
  • compute script scr2 :
if dup hash160 X equalverify else <locktime> CLTV A1 endif checksig
  • create a new transaction paying the amount to be swapped to scr1 and change to p2pkh(B2)
  • sign using b0 and relay the transaction
  • watch the p2sh address for scr2

alice :

  • scan the chain for transactions with two outputs p2sh and p2pkh
  • once a candidate is found, check if the amount paid to p2sh is slightly larger than an owned utxo
  • find the r value, call it A0, and secret nonce k, call it a0 that were used in the signature paying to it
  • find the image to be revealed X within the p2pkh output in the candidate transaction
  • fetch the r value, call it B0 from the signature in the candidate transaction
  • perform ecdh using a0 and B0, compute a shared secret e
  • tweak (a0 + e)*G to produce an A1
  • tweak B0 by e*G + B0 to produce B1
  • try computing script scr1' :
if hash160 X equalverify A1 else <locktime> CLTV B1 endif checksig
  • if HASH160(scr1') is the same as in the p2sh output from the candidate transaction, we have a match, continue
  • compute script scr2 :
if dup hash160 X equalverify else <locktime> CLTV A1 endif checksig
  • create a transaction paying to scr2, sign and relay

bob :

  • if p2sh address matching HASH160(scr2) is seen on the network, redeem scr2 by providing scriptsig :
<sig for B2> B2
  • if not, redeem scr1 after timeout using a signature for B1

alice :

  • if scr2 is redeemed, fetch B2 from its scriptsig and redeem scr1 with scriptsig
B2 <sig for A1>
  • if not, redeem scr2 after timeout using a signature for A1
  1. alice has d1 - private key, k0 - secret nonce, b1 - a tweak for d1

  2. alice computes proof of d1 such that P1 = d1G using s1R = z1G + r1P1,
    where z1 is just a representation of P1

  3. alice sends b1 to bob

  4. alice sends proof for P1 to bob - ((r1,s1), z1, P1)

  5. bob sends tweak t1, and point T2 for which he knows the private key t2 for to alice

  6. alice computes z2, a sighash_noinput for redeeming her backout transaction to an address of hers

  7. alice computes her signature of the message z2
    using (k0+t1) as nonce and (d1+b1)*G as pubkey :
    s2 = 1/(k0+t1) * (z2 + r2*(d1+b1))

  8. alice sends her signature for z2 (but not z2 itself)
    using ((r2,s2), P2) where R2 = (k0+t1)*G and P2 = (d1+b1)*G to bob

  9. bob can check :

    a. R2 = t1*G + R1

    b. P2 = b1*G + P1

  10. bob signs alice's payment to her backout where the scriptpubkey is

<P2> <(r2,s2)>[noinput] checksig
  1. alice computes P3 = T2 + P2 where T2 was the point given to her by bob <- needs to be done securely
  2. alice signs bob's backout where the scriptpubkey is
if <P3> else CLTV <bob pubkey> endif checksig

if alice redeems her backout transaction, bob learns z2 and can then compute :

d1 = (z2/s2 + r2*b1/s2 - t1 - z1/s1) / (r1/s1 - r2/s2)

meaning he can complete the 2-of-2 path without waiting for the locktime to pass by computing P3 = (d1 + b1 + t2)*G to an outside observer, P2 and P3 are unrelated. it might be possible to construct bob's backout to look like a somewhat normal script that a non-coinswap application might use.

algebra :

1. s1 = 1/(k0) * (z1 + r1*d1)
2. s2 = 1/(k0+t1) * (z2 + r2*(d1+b1))

=>

1. k0 = 1/s1 * (z1 + r1*d1)
2. k0 = 1/s2 * (z2 + r2*(d1+b1)) - t1

=>

compare (1) and (2)

1/s1 * (z1 + r1*d1) = 1/s2 * (z2 + r2*(d1+b1)) - t1

=>

move stuff around

z1/s1 + r1*d1/s1 = z2/s2 + r2*d1/s2 + r2*b1/s2 - t1

r1*d1/s1 - r2*d1/s2 = z2/s2 + r2*b1/s2 - t1 - z1/s1

d1 * (r1/s1 - r2/s2) = z2/s2 + r2*b1/s2 - t1 - z1/s1

d1 = (z2/s2 + r2*b1/s2 - t1 - z1/s1) / (r1/s1 - r2/s2)
STLC - sign-time-locked-contracts
In here we use the coinswap scheme from :
https://joinmarket.me/blog/blog/coinswaps | Coinswap - with privacy
And math from :
https://github.com/oleganza/bitcoin-papers/blob/master/BitcoinBlindSignatures.md | Bitcoin Blind Signatures
Two participants in this scheme. They'd like to perform an atomic swap of their coins :
Bob - Blinder
Sarah - Signer
Setup stage
-----------
Bob and Sarah exchange pubkeys. Sarah's pubkeys are labeld as S, Bob's pubkeys are labeled as B.
Bob sends three pubkeys to Sarah, (B1, B2, B3)
Sarah sends three pubkeys to Bob, (S1, S2, S3)
They also agree on locktimes to be used for backouts, (L0, L1), where L1 is closer than L0.
Bob's locktime is L0 and Sarah's is L1, which means she gets to back out earlier.
Sarah generates secret values (p, q) at random and computes two pubkeys :
P = (1/p) * G
Q = (q/p) * G
# provide PoDLE here?
Bob generates secret values (b, d) at random and computes two pubkeys :
B = b * G
D = d * G
Bob and Sarah derive two shared secrets (a, c) together
# ECDH?
Bob and Sarah exchange the values (P, Q) and (B, D)
Backout stage
-------------
Bob and Sarah will each compute the necessary values to for producing an ECDSA signature over a yet unspecified hash.
Bob computes the point R, the public point of the nonce where the discrete log would be the value k :
R = ( 1/(a*c) ) * P
r = R_x
Bob also computes the point T, which will be the pubkey used in the verification of the signature :
T = ( 1/(a*c) ) * ( b*G + Q + (d/c)*P )
Sarah on her end computes the discrete log of R, the point R itself and the x coordinate of it, r :
k = 1/(a*c*p)
R = k * G
r = R_x
Sarah also computes the point T from her own values :
T = ( k * (1/r) ) * ( (c*p)*B + (q*c)*G + D )
# Neither party does not know the discrete log 't' for T at this stage.
They are both able to compute the following two scriptpubkeys :
scr1 : 2 <B1> <S1> <S2 + T> 3 checkmultisig
scr2 : 3 <B2> <B3> <S3> <T> 4 checkmultisig
# only Sarah knows the private key for S2, her pubkey.
Bob computes a backout address 'B_backout'
Bob creates funding tx TX0 that pays scr1 but does not broadcast it, and sends Sarah the txid:index
Bob creates a backout transaction TX2, 'TX0->B_backout', which redeems TX0 by solving scr1
Bob then asks Sarah for a signature by S1 for payment from TX2, redeeming scr1, nlocktime'd to L0
Sarah computes a backout address 'S_backout'
Sarah creates funding tx TX1 that pays scr2 but does not broadcast it
Sarah creates a backout transaction TX3, 'TX1->S_backout' which redeems TX1 by solving scr2
Sarah sends Bob a signature by S1 for TX2, nlocktime'd to L0
Also sends Bob txid:index of TX1
Sarah then asks Bob for a signature by (B2, B3) for payment from TX3, redeeming scr2, nlocktime'd to L1
Bob verifies the signature by S1. If valid..
Bob sends Sarah a signature by (B2, B3), for TX3, nlocktime'd to L1
Sarah verifies the signature by (B2, B3). If valid..
Sarah starts watching for TX0
Sarah broadcasts TX1
Bob watches the script TX1, if TX1 is spotted..
Bob broadcast TX0
If Sarah fails to see TX0, she waits for L1 to pass, signs TX3 with S3 and broadcasts
If Sarah spots TX0, continue..
Blinding stage
--------------
# At this stage, at any point, each party is able wait out their respective locktimes and back out if needed.
Bob computes a swap address 'B_swap'
Bob creates a swap transaction TX4 that pays him from Sarah's funding, 'TX1->B_swap' by solving scr2.
Bob computes the sighash h1 :
h1 = sighash(TX4)
Bob then blinds the value h1 as h2 :
h2 = a*h1 + h1
Bob then sends sarah h2
Sarah computes a value 's1', which is the blinded 's' value for a signature '(r, s)' over TX4 :
s1 = p*h2 + q
Sarah computes the point (h1*G) from known values, without knowledge of 'h1' itself :
h1 * G = (1/a) * (h2*g - B)
Sarah attempts to check that her blinded signature '(r, s1)' could be used to verify the message h1, using T as the pubkey :
k * ( (c*s1)*G + D ) =? h1*G + r*T
# At this stage Sarah basically signs "anything".
# This previous check is designed to assure her that the point T,
# the public nonce R, with 'r' and a message that would make for a point 'h1*G'
# can make for a valid signature by a yet unknwon value 's2', where 's2*R',
# which she simulates using 'k * ( (c*s1)*G + D )'
If this check fails or Bob becomes unresponsive, she backs out using TX3 (same as above)
If the check passes, Sarah continues..
Sarah creates a swap transaction TX5 that pays her from Bob's finding, 'TX0->S_swap' by solving scr1
Sarah then sends Bob the value s1
Swap stage
----------
Bob unblinds s1 to produce a value s2 for a valid signature '(r, s2)' over TX4 :
s2 = (c*s1) + d
Bob then validates the signature using his known values ((r, s2), h1, T) :
s2*R = h1*G + r*T
If this check fails, he waits for L0 to pass, signs TX2 with B1 and broadcasts
If the check passes, Bob signs TX4 with B2 and broadcasts it immediatelly
Sarah is already watching her own TX1. As it is redeemed, she learns (s2, h1)
Sarah then computes the discrete log 't' for the point T :
t = (1/r) * ( s2*k - h1 )
Sarah then signs TX5 with (S1, (S2 + T)) and broadcasts it immediatelly
# Sarah is unable to redeem her own TX1 at this point even by knowing t,
# because scr2 requires 3 signatures of 4 pubkeys.

alice :

  1. has private key a1, point P1 = a1*G, private nonce k1, corresponding point R1 = k1*G
  2. sends bob a signature sig1 for P1 : ((r1,s1), P1, z1=<message containing P1>
  3. sends bob her backout scriptpubkey scr1 for TX2 to be paid for from her TX0 :
<P1> <(r1,s2)>[noinput] checksig

where <(r1,s2)>[noinput] is a sighash_noinput type signature sig2 using the same r1 from sig1 and s2 is different

bob :
4. checks validity of sig1
5. checks sig1_r1 == sig2_r1, and sig1_s1 != sig2_s2, and P1 == P1 (the same P1 from sig1)
(checks that sig2, P1 is not valid for z1 as message? probably overkill)
6. signs a spend sig3 from TX0 to TX2 where the scriptpubkey is scr1
7. chooses private value t1, computes T1 = t1*G
8. chooses private key b1, computes B1 = b1*G
9. computes point P2 = P1 + T1
10. sets up his own backout scriptpubkey scr2 for TX3 to be paid for him from his TX1 :

if <P2> else <time delay> CSV drop <B1> endif checksig

where <time delay> might be the same as a standard LN to local output:
https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#to_local-output

  1. sends sig3 and T1 to alice

alice :

  1. checks that T1 does not negate her P1 ( <- probably tricky... )
  2. checks that sig3 is valid for the spend TX0 -> TX2
  3. signs sig4 bob's spend from TX1 -> TX3
  4. send bob sig4
  5. broadcast TX0

bob :

  1. verify sig4 is valid for spend TX1 -> TX3
  2. broadcast TX1

both :

  1. wait for TX0 and TX1 to confirm

now what?


if alice redeems her backout, she has to re-use k1, bob learns a1 and can figure out privkey for P2

traditional coinswap with non-apparent connection using sighash_noinput

alice :

  1. choose a1, compute A1 = a1*G
  2. choose a2, compute A2 = a2*G
  3. choose k1, compute R1 = k1*G
  4. compute a signature sig1 = (r1,s1) using k1 as nonce and A1 as pubkey over a message z1 which contains A1
  5. choose her destination address dest_a for the swap
  6. compute z2, a sighash_noinput for her dest_a
  7. compute a signature sig2 = (r1,s2) using k1 as nonce and A1 as pubkey over the message z2
  8. send (sig1, A1, z1), sig2 to bob

bob :

  1. validate (sig1, A1, z1) for A1
  2. check sig1_r == sig2_r, sig1_s != sig2_s
  3. choose b1, compute B1 = b1*G
  4. choose b2, compute B2 = b2*G
  5. send B1 to alice along with a signature for it (just an unrelated signature to prove knowledge of b1)

alice :

  1. validate bob's siganture for B1
  2. compute B3 = B1 + A1
  3. send to scr1:
    if <B3> else <L0> CLTV <A2> endif checksig

bob :

  1. check B3 - B1 == A1
  2. send to scr2:
    if (r1, s2)[noinput] <A1> else <L1> CLTV <B2> endif checksig

alice :

  1. redeem IF branch of scr2, paying to dest_a, revealing z2

bob :

  1. compute k1 = (z1 - z2) / (s1 - s2) ( really four candidates for each [+-]s{1,2} )
  2. compute a1 = ((s1 * k1) - z1) / r1 ( really four candidates for each k1_{1,2,3,4} )
  3. compute B3 == (b1 + a1)*G
  4. redeem IF branch of scr1, able to sign for B3

alice considerations :

  1. if at step (18) L1 is too close, don't redeem scr2. instead wait for L0 to pass and backout scr1
  2. coins from scr2 are obviously coinswap

bob considerations :

  1. L1 must be considerably less than L0. if not - do not broadcast scr2
  2. if alice does not redeem scr2, backing out from it taints coins with obvious coinswap attempt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment