Skip to content

Experimenting with DLC-style of contract on Rust-Lightning #605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ariard opened this issue Apr 23, 2020 · 20 comments
Open

Experimenting with DLC-style of contract on Rust-Lightning #605

ariard opened this issue Apr 23, 2020 · 20 comments

Comments

@ariard
Copy link

ariard commented Apr 23, 2020

@nkohen and Yancy Ribbens are interested to experiment offchain DLC. RL suits very well for this, I think ideally DLC extension should be downstream as another under rust-bitcoin org repository to make progress as its own rhythm but right now opening this issue to track what we should do.

  • what custom message ?
  • how to avoid weaking commitment transaction ?
  • what design of 2nd-stage transaction ?
  • maintaining a rust-secp256k-zkp in rust-bitcoin with ecdsa_adaptor module ?

@nkohen I'm willingly to start the rust-dlc skeleton compatible with Rust-Lighning and let you fulfill holes from then to learn Rust but I need a better description of the protocol do you have in one of your blog or in dlcspecs ?

cc @arik-so (you should be definitely interested, DLC where my main reason to experiment with PTLC a while ago :) )

@Christewart
Copy link

If anyone is interested in organizing a "formal" effort for this, it appears Fulmo is having another hackday over the weekend of May 9th-11th. It was pretty useful for us all to collaborate on the first version of PTLCs over that weekend and get something working.

@channel Save the date! The next #LightningHacksprint is coming up on the weekend of May 9th/10th 2020. Please use https://wiki.fulmo.org to update your challenges, bounties and self-organized sessions. Ping me or @rootzoll is you have any questions.

@LLFourn
Copy link

LLFourn commented Apr 23, 2020

@Christewart I'll join again :). Another approach we could take would just to implement a normal PTLC payment without randomization at each hop. i.e. just making the one-way function for the secret a point multiplication rather than a hash. This seems like a pre-requisite to doing DLCs anyway. Here's what i think are the most minimal script changes to do a PTLC (fyi I'm a script noob):

Offered HTLC

Before

OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_NOTIF
        # To local node via HTLC-timeout transaction (timelocked).
        OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    OP_ELSE
        # To remote node with preimage.
        OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

After

OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    # Goes to either PTLC-success or  PTLC-timeout transaction
    2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
OP_ENDIF

Note that for this case we need a new "PTLC-success" transaction for an offered HTLC which isn't necessary in the original (the output can be spent with any transaction if secret is known).

Received HTLC

Before

OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_IF
        # To local node via HTLC-success transaction.
        OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    OP_ELSE
        # To remote node after timeout.
        OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
        OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

After

OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_IF
        # To local node via PTLC-success transaction.
        2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    OP_ELSE
        # To remote node after timeout.
        OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
        OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

@nkohen
Copy link

nkohen commented Apr 23, 2020

Just a quick note that @ariard made in slack, if we really wanted to go for minimal script changes, we could actually not change any of the above scripts and instead just use adaptor sigs directly where some fixed "payment hash" with known pre-image is used

@LLFourn
Copy link

LLFourn commented Apr 24, 2020

@ariard That's clever but I don't think that works on the offered HTLC side:

# To remote node with preimage.
OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
OP_CHECKSIG

That OP_CHECKSIG checks against the remote_htlcpubkey but it adaptor sig here needs to be given under local_htlcpubkey. It seems like we need to modify script to get this to work.

@ariard
Copy link
Author

ariard commented Apr 24, 2020

@LLFourn scalar release is going from Bob to Alice so it's remote_htlcpubkey we're interested with here ?

I think I don't talk with your latest protocol in mind I'm still on https://github.com/ElementsProject/scriptless-scripts/blob/master/md/multi-hop-locks.md

Can you describe or points me towards the latest PTLC flow exchange ? Like Alice send offer to Bob, ... You may be right but we have way to hack on current scripts.

The good news is we have an external signer in RL since soon, API is still WIP, but what we can do is swap commitment transaction HTLC scripts, by PTLC at signing (see ChannelsKeys::sign_local_commitment). It will of course fail verification right now at commitment_signed reception but that's few lines to move behind something ChannelsKeys::verify_remote_commitment.

Anyway we'll need our own set of messages, what I want to avoid is touching the state machine, but round-trip isn't the same ?

@ariard
Copy link
Author

ariard commented Apr 24, 2020

On the dependency-tree, is this something like this ?


                                         rust-dlc
                                           /
                                         /
                                    rust-ptlc
                                       /
                                     /
                             rust-lightning 
                                  /
                                /
                     rust-secp256k1 (another fork or Yancy fork?)
                            /
                          /
             secpk256k1 (nickler fork)

We should aim to keep dependency hell manageable. I think rust-ptlc would be an overlay of rust-lightning, until PTLC have formalized in BOLTs (surely gonna take forever...) and we can host them in-tree (aka rust-lighhtning). Having a seperate repo means we can go our own pace.

It should be rebased on downstream but if we do it right I can maintain it. Also making rust-lightning more flexible is something which can be done in-tree.

Thoughts ?

@ariard
Copy link
Author

ariard commented Apr 24, 2020

Note that for this case we need a new "PTLC-success" transaction for an offered HTLC which isn't necessary in the original (the output can be spent with any transaction if secret is known).

If you're interested you can go through whole discussion https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-April/002639.html but tl;dr likely we're going to lockdown remote 2nd-stage txn on local commitment to make LN just-secure

Another approach we could take would just to implement a normal PTLC payment without randomization at each hop. i.e. just making the one-way function for the secret a point multiplication rather than a hash.

For a PoC, we should do first a one-hop, so need to touch onions. But yeah the meanwhile, we can think about other data structs we need to touch for multi-hop (onion, invoices)

Fyi, there is idea to move towards a bidirectional communication channel for LN (https://www.scion-architecture.net/pdf/2015-HORNET.pdf), it's something PTLC would benefit

@ariard
Copy link
Author

ariard commented Apr 24, 2020

If anyone is interested in organizing a "formal" effort for this, it appears Fulmo is having another hackday over the weekend of May 9th-11th.

@Christewart, thanks for dates I will definitely participate this time :)

@ariard
Copy link
Author

ariard commented Apr 24, 2020

@Tibo-lg, how your rust-dlc repo can be combined with PTLCs or are you using DLCs 2nd-stage transaction pluggable on actual HTLC output scripts ?

@LLFourn
Copy link

LLFourn commented Apr 24, 2020

@LLFourn scalar release is going from Bob to Alice so it's remote_htlcpubkey we're interested with here ?

Alice and Bob is too much for me atm so I'll just use "local" and "remote". The payment is going from the local party to the remote party and the scalar release is going from the remote party to the local right? In order for the local party to ensure that there is no way for the remote party to claim the coins other than completing an adaptor signature with the scalar the CHECKSIG that releases the scalar needs to be on a key only the local party knows. I chose local_htlcpubkey for this. Of course, you need another CHECKSIG to make sure that the condition that is spent is completes_adaptor_on_local_key AND is_remote_party. So we use OP_CHECKMULTISIG.

I think I don't talk with your latest protocol in mind I'm still on https://github.com/ElementsProject/scriptless-scripts/blob/master/md/multi-hop-locks.md
Anyway we'll need our own set of messages, what I want to avoid is touching the state machine, but round-trip isn't the same ?

I think @jonasnick mentioned he might update that diagram for the OP_CMS adaptor lock. With OP_CMS adaptors round trips should be the same as now -- just the payment hash replaced with 33 byte point (I think xonly here just makes things confusing) + adaptor signatures. Note that when you create a PTLC, two ECDSA adaptor signatures needs to be sent by the party offering the PTLC. One of them is just replacing what used to be a normal signature here:

# To local node via PTLC-success transaction.
# OP_HASH160 <RIPEMD160(payment_hash)> 
# OP_EQUALVERIFY <----- this check is removed and instead of the signatures below becomes adaptor.
2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG

Then the other is a new adaptor for the new "PTLC-success" transaction on the offered side.

If you're interested you can go through whole discussion https://lists.linuxfoundation.org/pipermail/lightjust a new transaction and an adaptor on it.ning-dev/2020-April/002639.html but tl;dr likely we're going to lockdown remote 2nd-stage txn on local commitment to make LN just-secure

Thanks. I only vaguely understand all the RBF discussion here. It's true if this change were implemented now then we could use the dummy payment hash trick but since it probably won't be done I guess we should prepare ourselves to change script and do it properly :P

@Tibo-lg
Copy link
Contributor

Tibo-lg commented Apr 24, 2020

@ariard Right now our goal was to get a basic DLC implementation, with the classical construction (so without PTLC or adaptor sigs). It's still WIP, and right now @yancyribbens was working on forking rust-secp256k1 to point to a branch where we can have the latest schnorr module. But I think there can be synergy at the rust-secp256k1 level so that we could maintain a single fork providing functions for both "classical" DLC and "fancy" ones.

@yancyribbens
Copy link

@ariard I can share a Repo that's open source that contains my current progress on the "classical" DLC construction (without adapter-sigs and PTLC). The CET (as @Tibo-lg mentioned) and the bindings are still a work in progress at the moment but I think it can help the discussion about how to move forward using either a different repo or adding on to the current construction. Regarding the dependencies, I'm currently using a secp256k1-zkp fork but @nkohen recommended using a schnorr fork of secp256k1.

current "Classic" construction:

                                         rust-dlc
                                           /
                                         /
                             rust-lightning 
                                  /
                                /
                     rust-secp256k1 (jonasnick/schnorrsig?)

I'm not sure how to combine with PTLCs or adapter-sigs at the moment.

@ariard
Copy link
Author

ariard commented Apr 26, 2020

@LLFourn Okay gotcha, it's either "adaptor_sig for Alice_adaptor_key + Bob_pubkey" || "Alice_pubkey + timelock". First branch you need a sig from Bob to be sure that Alice doesn't malleate Bob's redeem once seeing scalar. Second you can't reuse same key for Alice because she needs to timeout without cooperation.

I think you can't still reuse offered output redeemscript. Just inverse 2nd-stage transaction. HTLC-timeout becomes Bob''s redeem and HTLC-preimage becomes Alice's timeout. Bob signs Alice's timeout and can so enforces timelock as he is the one interested with this semantic enforcement. Alice key is replaced by the hash, for which she knows preimage.

It's a bit hacky but that may work assuming we have few new messages?:



                                     update_add_htlc

                                ------------------------->

                                     commitment_signed         Bob[Alice_adaptor_sig] // assuming external signer swap on Alice-side

                                ------------------------->

                                       revoke_ack
                                <-------------------------

                                      NEW_send_back_sig

                                <-------------------------

                                     commitment_signed

   Alice[Bob_timeout_sig]       <-------------------------

                                      NEW_release_scalar
                                <-------------------------

                                     commitment_signed

                                <-------------------------

Like I said previously we have an external signer in RL, so you can reimplement the trait with a new one, which does some key swap to get semantic you want. Verification needs also to be abstracted.

Note that when you create a PTLC, two ECDSA adaptor signatures needs to be sent by the party offering the PTLC

I see, in this case, update_add_htlc becomes update_add_ptlc and provides adaptor_sig for spending on Alice commitment transaction? Okay likely it's better to have clean scripts from scratch, but like I said we can swap them against HTLC scripts in the external signer to minimize disruption :)

Thanks. I only vaguely understand all the RBF discussion here. It's true if this change were implemented now then we could use the dummy payment hash trick but since it probably won't be done I guess we should prepare ourselves to change script and do it properly :P

Until we do so, it's not that hard to steal HTLC on the network, so going to happen ;)

To move forward, if nickler or you have a more tied-up description, we can see how to integrate this with DLC?

@ariard
Copy link
Author

ariard commented Apr 26, 2020

@Tibo-lg , @yancyribbens @nkohen Yes I think it would be better to focus on Yancy rust-dlc now, to get as soon as we can an offchain-DLC PoC with which to play.

In the meanwhile we can keep thinking on 1p-ECDSA PTLC, but AFAICT the good thing it's not exactly the same component, DLC are different 2nd-stage transaction while PTLC different commitment output . So it should be hacky but far possible to recombine them latter.

If you have a link to your repo, I can start to dig in. I don't have an opinion on either rust-secpk256-zkp or rust-secp256k1

@yancyribbens
Copy link

yancyribbens commented Apr 26, 2020

Here's the current state of the Rust DLC implementation I've recently started. There's not much here yet, although hopefully this can help continue the discussion around a Rust implementation we can play with as @ariard mentioned. The next major obstacle (I think) is adding a Secp256k1 fork that includes shnorr sigs with DLC specific functions. Ideally we would incorporate the branch @nkohen is also working on nkohen/schnorrsig-sigpoint and keep that work common among all implementations as @Tibo-lg mentioned. I've researched adding nkohen/schnorrsig-sigpoint into a Rust wrapper but it's a bit of a lift.. Suggestions welcome.

@yancyribbens
Copy link

PRs/commits and comments also welcome :)

@ariard
Copy link
Author

ariard commented May 8, 2020

Fulmo Hackathon 05/09-05/10

The PoC

This hackathon is the occasion to assert the Simple-DLC-Channel design, as
roughly laid out in discreetlogcontracts/dlcspecs#3

This design enable single-hop DLC where at every maturation you need to
resign the whole range of CET transaction.

CET output are directly bind on the commitment transaction and replace HTLC
output. N CET-transaction may replace them, themselves spendable by closing/
penalty transaction.

We don't aim to cover the onchain monitoring during this hackhathon.

PoC deliverable is :

$> ./rust-lightning-bitcoincoreprc generate dlc-offer
$> ./rust-lightning-bitcoincoreprc accept dlc-offer
$> ./rust-lightning-bitcoincoreprc settle dlc

The Hacks

On the sender-side, starting at accept dlc-offer, we keep the current entry point ChannelManager::send_payment, until we get in return MessageSendEvent::UpdateHTLCs. Internally, at Channel::commitment_signedwe abstract transaction building by some TxBuilder trait. Behind this trait we may userust-dlc to build transaction accordingly and pass them to ChanSigner::sign_remote_commitment. ChanSigner is
already its own interface so we just have to implement a DLC-signing one. After
commitment_signed we get a Signature that we return as a MessageSendEvent::UpdateHTLCs
for now. Instead of calling directly PeerHandler::process_events, we extract
signature and we switch it by MessageSendEvents::UpdateDLCs. We may encapsulate in
some generic enum and pass it to PeerHandler::process_event.

We need well-defined update_add_dlc, update_fail_dlc, update_fulfill_dlc in msgs.rs.

On the receiver-side, we add custom message support in PeerHandler::read_event, using
a new trait DLCChannelMessageHandler with handle_update_*_dlc. Same, this one call the
same workflow, calling Channel::update_add_dlc and committing output in the channel
object.

Then we need to reverse the workflow for implementing settle dlc-offer.

Listing tasks to be done:

  • rust-lightning-bitcoincorerpc cmds "get/add/settle cet"
  • update_add_cet/update_fulfill_cet, `update_fail_cet messages (msg.rs)
  • MessageSendEvent::UpdateCET
  • custom messages sending/receiving (peer_handler.rs)
  • DLCChannelMessageHandler (msg.rs)
  • exposing Channel publicly (channel.rs) @ariard
  • abstraction of HTLC in Channel (channel.rs) @ariard
  • TxBuilder trait (channel.rs) @ariard

@ariard
Copy link
Author

ariard commented May 9, 2020

I'm fine with this blockchain rpc api, I'm not proposing myself to work on the rpc part so it's up to whom will hack on it :)

@LLFourn
Copy link

LLFourn commented May 9, 2020

To avoid doing ffi stuff for different forks etc here's the code that let's you do the very basic schnorr stuff you'll need for this:

https://gist.github.com/LLFourn/ee73d67b3f4645e4c19bd853e1b17062

Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants