[Lightning-dev] 2-of-3 Instant Escrow, or How to Do "2-of-3 Multisig Contract" Equivalent on Lightning

Joseph Poon joseph at lightning.network
Sun Jan 17 01:45:33 UTC 2016


TL;DR: It's possible to do 2-of-3 party escrow in Lightning,
functionally the same as the use case for on-chain "2-of-3 multisig"
escrow systems (without the 3rd party escrow being a custodian for any
funds! Or anyone else holding custodial ownership of coins for the
matter). As Lightning uses Bitcoin transactions and scripts (using
Bitcoins, not 3rdPartyCoins), it'll be possible to do nearly *instant*
*offchain* multisig "escrow" on decentralized Lightning! This is useful
for payments for goods and services which needs a 3rd party mediator.

By making multiple R-value hashes supported in HTLCs, you can do
something equivalent to "multisig" where payments are conditional upon
2-of-3 parties agreeing. This is to support conditional multiparty
payments, e.g. 2-of-3 "escrow", which is one of the biggest use cases of
bitcoin scripting today. An example use case is a 3rd party escrow
verifies whether a seller should be paid. This design is such that the
escrow is not a traditional custodial escrow, but instead determines who
should get the money in the event of non-cooperation. See the Bitcoin
Script below for details.

There isn't message integration for 2-of-3 yet, but can work with *very*
minimal changes. Arbitrary N-of-M can be supported with M values higher
than 3 and lower than max script size, but let's keep this simple for
now!

How it works: Require 2-of-3 R-value preimages (from 3 hashes) in order
for the HTLC to be fulfilled. For each hop in the payment, it requires
this 2-of-3 condition. The timeout minimum for each hop in the path is
at least the minimum agreed contractual escrow timeout. The timeouts
should be fairly long compared to straight payments for security and
giving enough time for the escrow service. This means each hop consumes
a higher amount of time-value (due to much longer timeouts along all
channels in the path, and possibly higher fees to pay for larger scripts
and greater time-sensitivity for transaction inclusion), which does have
greater pressure towards lower hop-distances, compared to straight
payments where it matters a whole lot less.

This is a slightly different way of thinking about things. It's not
signatures that the escrow produces (or for that matters any of the
3-parties in the 2-of-3 after the actual Commitment is signed). It's
some secret which is revealed to authorize payment. So if the escrow
wants the payment to go through without cooperation of the sender, they
disclose the secret (R-value) to the recipient. If the recipient is
unable to produce 2-of-3, after the agreed timeout, the sender will be
refunded. Sender and receiver can agree to authorize payment in most
cases where there is cooperation, escrow is only contacted if there is
non-cooperation.

Assume the order in the stack is Sender, Escrow, Recipient.

For PAID 2-of-3 Escrow+Recipient, the HTLC stack is:
        <BobSig> <0> <EscrowPreimageR> <RecipientPreimageR> <0>

If it's REFUND because 2-of-3 has not been redeemed in time:
        <AliceSig> <0> <1>

Bitcoin Script (Alice's, we use OP_1/OP_0 to distinctly show computed
true/false. 0/1 is for supplied data as part of the
sigScript/redeemScript stack):
------------------------------------------------------------------------

//Paid
OP_IF
        <CSVDelay> OP_DROP OP_CSV //under rusty's CSV style

        //Stack: <BobSig> <0> <EscrowPreimageR> <RecipientPreimageR>
        //Recipient must agree to receive funds.
        OP_HASH160 <RecipientHash> OP_EQUALVERIFY

        //Stack: <BobSig> <0> <EscrowPreimageR>
        //Either the Sender or Escrow must consent for payment
        OP_HASH160 <EscrowHash> OP_EQUAL
        //Stack: <BobSig> <0> <OP_1>
        OP_SWAP
        //Stack: <BobSig> <OP_1> <0>
        OP_HASH160 <SenderHash> OP_EQUAL
        //Stack: <BobSig> <OP_1> <OP_0>
        OP_BOOLOR
        //Stack: <BobSig> <OP_1>
        OP_VERIFY

        <BobPubKey>
        //Stack: <BobSig> <BobPubKey>
//Refund
OP_ELSE
        //Stack: <AliceSig> <0>
        OP_HASH160 OP_DUP
        <R-HASH> OP_EQUAL
        OP_NOTIF
                <CSVDelay> OP_DROP OP_CSV
        OP_ENDIF

        <HTLCTimeout> OP_DROP OP_CLTV

        //Stack: <AliceSig>
        <AlicePubKey>
        //Stack: <AliceSig> <AlicePubKey>
OP_ENDIF
OP_CHECKSIG
------------------------------------------------------------------------

Note: It is possible that Alice and Bob may not be Sender, Recipient,
nor Escrow! They could be nodes along the routing path. Script might
have a typo or two or might be able to be optimized a couple bytes
smaller maybe? Also, the script would be a bit different depending upon
is broadcasting due to the revocations/R-HASH.

The result? We can do 2-of-3 escrow payments which refund to the sender
after a timeout! The Sender and Recipient can agree to redeem and they
only need to go to the Escrow if there's a dispute. All nodes along the
path gets paid or refunded atomically, the same as a single-HTLC payment
on Lightning. Escrowed payments can be nearly instant and off-chain (but
enforced/net-settled ultimately using the blockchain).

Proposed HTLC wire message for the uint8 (two 4-bit N-of-M) determining
type:
0x11 (00010001): 1-of-1
0x22 (00100010): 2-of-2
0x23 (00100011): 2-of-3 [with Recipient being 1 of the two N parties]
0x33 (00110011): 3-of-3

I think the only ones that really matter are 1-of-1, 2-of-3, and 2-of-2.
1-of-2 and 1-of-3 doesn't make sense if the recipient must consent to
receiving funds anyway (pushing funds w/o consent is tricky due to
pay-to-contract-hash) so that's basically a 1-of-1.

Possible Resolution States:
* Recipient paid: Recipient and Sender provide R-values
* Recipient paid: Recipient and Escrow provide R-values
* Sender refunded via timeout: Sender is refunded if Recipient cannot
  convince Escrow or Sender to disclose their R-value before HTLC
  timeout
* Payment immediately cancelled and Sender gets refunded: Payment sent
  in the opposite direction enforced by same R-values (if there is
  sender & receiver consent/cooperation to cancel payment)

Sender+Escrow isn't going to want to push funds w/o cooperation of
Recipient. However, it's possible to construct a script that way.

Escrow is only contacted if the recipient needs to redeem and the sender
is uncooperative so this is still true to the "lazy escrow service" in
Bitcoin multisig.

Ta-da! "Smart Contract(TM)" meme on Lightning.

P.S. Tadge, Laolu, and I have also put up a repo of our in-progress
design/code for Lightning here: https://github.com/LightningNetwork/lnd

-- 
Joseph Poon


More information about the Lightning-dev mailing list