Rusty Russell [ARCHIVE] on Nostr: 📅 Original date posted:2021-04-20 📝 Original message: Lloyd Fournier ...
📅 Original date posted:2021-04-20
📝 Original message:
Lloyd Fournier <lloyd.fourn at gmail.com> writes:
> Hi Rusty,
>
> On Tue, 20 Apr 2021 at 10:55, Rusty Russell <rusty at rustcorp.com.au> wrote:
>
>> Lloyd Fournier <lloyd.fourn at gmail.com> writes:
>> > On Wed, Dec 9, 2020 at 4:26 PM Rusty Russell <rusty at rustcorp.com.au>
>> wrote:
>> >
>> >>
>> >> Say r1=SHA256(ss || counter || 0), r2 = SHA256(ss || counter || 1)?
>> >>
>> >> Nice work. This would be a definite recovery win. We should add this
>> >> to the DF spec, because Lisa was almost finished implmenting it, so it's
>> >> clearly due for a change!
>> >>
>> >
>> > Yes that's certainly a fine way to do it.
>> > I was also thinking you could eliminate all "basepoints" (not just
>> funding
>> > pubkey) using something like this. i.e. just use the node pubkey as the
>> > "basepoint" for everything and randomize it using the shared secret for
>> > each purpose.
>>
>> OK, I tried to spec this out, to implement it. One issue is that you
>> now can't sign the commitment_tx (or htlc_tx) without knowing the node's
>> secret key (or, equivalently, knowing the tweaked key and being able to
>> use the derivation scheme to untweak it).
>>
>
> Using node secret key to sign the commitment_tx seems like something you
> will have to accept to introduce this feature. For the idea to work it has
> to be some public key that is known by others and gossiped through the
> network. Of course you could extend the information that is gossiped about
> a node to include a "commit_tx_point" but the nodeid seems the more natural
> choice.
Duh, yes, of course you need the funding_key secret to sign the
commitment tx.
But you really don't want to access the `remote_pubkey` (which in a
modern option_static_remotekey world is simply the payment_basepoint).
It's generally considered good practice *not* to have this accessible to
your lightning node at all.
>> c-lightning currently does a round-trip to the signing daemon for this
>> already, but it'd be nice to avoid requiring it.
>>
>> So I somewhat reluctantly added `commit_basepoint` from which the others
>> are derived: an implementation can use some hardened derivation from its
>> privkey (e.g. SHA256(node_privkey || ss || counter)) to create
>> this in a deterministic but still private manner.
>>
>> Or we could just leave all the other points in and just replace
>> funding_pubkey.
>>
>
> Another approach is to do things in "soft-fork" like manner.
> Each node that wants to offer this feature sets their funding_pubkey to a
> specified DH tweak of the nodeid. Nodes that want backup-free channel
> recovery can just refuse to carry on the funding protocol if the
> funding_pubkey is not set the way it wanted.
Yeah, you can totally do this in an opt-in manner, except it doesn't
work unless your peer does it too. Since we expect everyone to want to
do this, it's clearer to force everyone to calculate this and not have
redundant and confusing fields in the message.
>>From my pruisit crypto point of view having only one public key is nice but
> I'm not sure how it impacts things architecturally and other protocols like
> watchtowers.
They can operate exactly like the existing scheme, AFAICT.
Here's the spec diff (based on dual-funding, since it's easier to simply
hard change). Please check my EC math! :)
diff --git a/02-peer-protocol.md b/02-peer-protocol.md
index fbc56c8..1114068 100644
--- a/02-peer-protocol.md
+++ b/02-peer-protocol.md
@@ -867,11 +867,9 @@ This message initiates the v2 channel establishment workflow.
* [`u16`:`to_self_delay`]
* [`u16`:`max_accepted_htlcs`]
* [`u32`:`locktime`]
- * [`point`:`funding_pubkey`]
+ * [`u64`:`generation`]
* [`point`:`revocation_basepoint`]
* [`point`:`payment_basepoint`]
- * [`point`:`delayed_payment_basepoint`]
- * [`point`:`htlc_basepoint`]
* [`point`:`first_per_commitment_point`]
* [`byte`:`channel_flags`]
* [`opening_tlvs`:`tlvs`]
@@ -895,13 +893,16 @@ If nodes have negotiated `option_dual_fund`:
The sending node:
- MUST set `funding_feerate_perkw` to the feerate for this transaction
- - MUST ensure `temporary_channel_id` is unique from any
- other channel ID with the same peer.
+ - MUST set `generation` to a number greater than any previous
+ `generation` it has sent to this receiving node which has reached
+ `commitment_signed`.
+ - SHOULD set `generation` to the lowest number which meets this requirement.
The receiving node:
- MAY fail the negotiation if:
- the `locktime` is unacceptable
- the `funding_feerate_per_kw` is unacceptable
+ - the `generation` exceeds expectation by more than the maximum it would scan for recovery.
#### Rationale
`channel_id` for the `open_channel2` MUST be derived using a zero-d out
@@ -926,6 +927,13 @@ Instead, the channel reserve is fixed at 1% of the total channel balance
rounded down to the nearest whole satoshi or the `dust_limit_satoshis`,
whichever is greater.
+`generation` is a number which is used to generate the points used for
+this pair of peers, with the aim of allowing automatic onchain
+scanning for channels if all other information is lost. Since this
+scan would presumably only try a limited number of generations, it is
+best if this number is low, but it also needs to change for each
+successive channel between the peers, to avoid obvious fingerprinting.
+
Note that `push_msat` has been omitted.
### The `accept_channel2` Message
@@ -943,11 +951,9 @@ acceptance of the new channel.
* [`u32`:`minimum_depth`]
* [`u16`:`to_self_delay`]
* [`u16`:`max_accepted_htlcs`]
- * [`point`:`funding_pubkey`]
+ * [`u64`:`generation`]
* [`point`:`revocation_basepoint`]
* [`point`:`payment_basepoint`]
- * [`point`:`delayed_payment_basepoint`]
- * [`point`:`htlc_basepoint`]
* [`point`:`first_per_commitment_point`]
* [`accept_tlvs`:`tlvs`]
@@ -967,6 +973,10 @@ additions.
The accepting node:
- MAY respond with a `funding_satoshis` value of zero.
+ - MUST set `generation` to a number greater than any previous
+ `generation` it has sent to this receiving node which has reached
+ `commitment_signed`.
+ - SHOULD set `generation` to the lowest number which meets this requirement.
#### Rationale
@@ -985,6 +995,31 @@ Funding composition for channel establishment v2 makes use of the
[Interactive Transaction Construction](#interactive-transaction-construction)
protocol, with the following additional caveats.
+#### Point Derivation
+
+The `funding_pubkey` and basepoints are derived from the two
+`node_id`s and the higher of the two `generation` values; the
+`payment_basepoint` is supplied directly.
+
+Derivation is done as follows:
+
+1. Start with two node ids, `N1` and `N2` (`N1` is the lesser of the
+ two SEC1-encoded compressed public keys, `N2` the greater).
+2. Derive a shared secret, `SS`, using ECDH on `N1` and `N2`.
+3. Define tweaks `T` for each peer, using `SHA256(SS || generation || node_id || name)`, where:
+ 1. `generation` is the `u64` larger of the two `generation` fields from `open_channel2` and `accept_channel2`.
+ 2. `node_id` is the SEC1-encoded compressed public key of the peer.
+ 3. `name` is a non-terminated ASCII string, e.g. `htlc` is the four bytes
+ `0x68 0x74 0x6C 0x63`
+4. The `funding_pubkey` is defined as the `node_id` + G*T(`funding`).
+5. The `delayed_payment_basepoint` is defined as `node_id` + G*T(`delayed_payment`).
+6. The `htlc_basepoint` is defined as the `node_id` + G*T(`htlc`).
+
+If the secret for `payment_basepoint` is derived in a similar manner,
+it too can be easily recovered from just the `generation`, node key
+and peer `node_id`. However, it may also point to an address for a
+completely separate system (e.g. cold storage), so it is specified
+explicitly in the protocol.
#### The `tx_add_input` Message
Published at
2023-06-09 13:02:15Event JSON
{
"id": "92f94bd82bcd0f88e2c5b1cdca01188368174336c69238b69d344ad11f21a37b",
"pubkey": "13bd8c1c5e3b3508a07c92598647160b11ab0deef4c452098e223e443c1ca425",
"created_at": 1686315735,
"kind": 1,
"tags": [
[
"e",
"9eb4c7be3b2b54415d1ca222464abf2f2c946db286a895ee30cb6529f9bcf7b5",
"",
"root"
],
[
"e",
"8477d2cd0b33d8a4dc6daca1f66f42539b1d8a8ef804e3a478e81f8156ec8992",
"",
"reply"
],
[
"p",
"b5ff7c704f90e4eebfa414c0a017a84544c32586a1bd2fc86c74c2914d03c25e"
]
],
"content": "📅 Original date posted:2021-04-20\n📝 Original message:\nLloyd Fournier \u003clloyd.fourn at gmail.com\u003e writes:\n\u003e Hi Rusty,\n\u003e\n\u003e On Tue, 20 Apr 2021 at 10:55, Rusty Russell \u003crusty at rustcorp.com.au\u003e wrote:\n\u003e\n\u003e\u003e Lloyd Fournier \u003clloyd.fourn at gmail.com\u003e writes:\n\u003e\u003e \u003e On Wed, Dec 9, 2020 at 4:26 PM Rusty Russell \u003crusty at rustcorp.com.au\u003e\n\u003e\u003e wrote:\n\u003e\u003e \u003e\n\u003e\u003e \u003e\u003e\n\u003e\u003e \u003e\u003e Say r1=SHA256(ss || counter || 0), r2 = SHA256(ss || counter || 1)?\n\u003e\u003e \u003e\u003e\n\u003e\u003e \u003e\u003e Nice work. This would be a definite recovery win. We should add this\n\u003e\u003e \u003e\u003e to the DF spec, because Lisa was almost finished implmenting it, so it's\n\u003e\u003e \u003e\u003e clearly due for a change!\n\u003e\u003e \u003e\u003e\n\u003e\u003e \u003e\n\u003e\u003e \u003e Yes that's certainly a fine way to do it.\n\u003e\u003e \u003e I was also thinking you could eliminate all \"basepoints\" (not just\n\u003e\u003e funding\n\u003e\u003e \u003e pubkey) using something like this. i.e. just use the node pubkey as the\n\u003e\u003e \u003e \"basepoint\" for everything and randomize it using the shared secret for\n\u003e\u003e \u003e each purpose.\n\u003e\u003e\n\u003e\u003e OK, I tried to spec this out, to implement it. One issue is that you\n\u003e\u003e now can't sign the commitment_tx (or htlc_tx) without knowing the node's\n\u003e\u003e secret key (or, equivalently, knowing the tweaked key and being able to\n\u003e\u003e use the derivation scheme to untweak it).\n\u003e\u003e\n\u003e\n\u003e Using node secret key to sign the commitment_tx seems like something you\n\u003e will have to accept to introduce this feature. For the idea to work it has\n\u003e to be some public key that is known by others and gossiped through the\n\u003e network. Of course you could extend the information that is gossiped about\n\u003e a node to include a \"commit_tx_point\" but the nodeid seems the more natural\n\u003e choice.\n\nDuh, yes, of course you need the funding_key secret to sign the\ncommitment tx.\n\nBut you really don't want to access the `remote_pubkey` (which in a\nmodern option_static_remotekey world is simply the payment_basepoint).\nIt's generally considered good practice *not* to have this accessible to\nyour lightning node at all.\n\n\u003e\u003e c-lightning currently does a round-trip to the signing daemon for this\n\u003e\u003e already, but it'd be nice to avoid requiring it.\n\u003e\u003e\n\u003e\u003e So I somewhat reluctantly added `commit_basepoint` from which the others\n\u003e\u003e are derived: an implementation can use some hardened derivation from its\n\u003e\u003e privkey (e.g. SHA256(node_privkey || ss || counter)) to create\n\u003e\u003e this in a deterministic but still private manner.\n\u003e\u003e\n\u003e\u003e Or we could just leave all the other points in and just replace\n\u003e\u003e funding_pubkey.\n\u003e\u003e\n\u003e\n\u003e Another approach is to do things in \"soft-fork\" like manner.\n\u003e Each node that wants to offer this feature sets their funding_pubkey to a\n\u003e specified DH tweak of the nodeid. Nodes that want backup-free channel\n\u003e recovery can just refuse to carry on the funding protocol if the\n\u003e funding_pubkey is not set the way it wanted.\n\nYeah, you can totally do this in an opt-in manner, except it doesn't\nwork unless your peer does it too. Since we expect everyone to want to\ndo this, it's clearer to force everyone to calculate this and not have\nredundant and confusing fields in the message.\n\n\u003e\u003eFrom my pruisit crypto point of view having only one public key is nice but\n\u003e I'm not sure how it impacts things architecturally and other protocols like\n\u003e watchtowers.\n\nThey can operate exactly like the existing scheme, AFAICT.\n\nHere's the spec diff (based on dual-funding, since it's easier to simply\nhard change). Please check my EC math! :)\n\ndiff --git a/02-peer-protocol.md b/02-peer-protocol.md\nindex fbc56c8..1114068 100644\n--- a/02-peer-protocol.md\n+++ b/02-peer-protocol.md\n@@ -867,11 +867,9 @@ This message initiates the v2 channel establishment workflow.\n * [`u16`:`to_self_delay`]\n * [`u16`:`max_accepted_htlcs`]\n * [`u32`:`locktime`]\n- * [`point`:`funding_pubkey`]\n+ * [`u64`:`generation`]\n * [`point`:`revocation_basepoint`]\n * [`point`:`payment_basepoint`]\n- * [`point`:`delayed_payment_basepoint`]\n- * [`point`:`htlc_basepoint`]\n * [`point`:`first_per_commitment_point`]\n * [`byte`:`channel_flags`]\n * [`opening_tlvs`:`tlvs`]\n@@ -895,13 +893,16 @@ If nodes have negotiated `option_dual_fund`:\n \n The sending node:\n - MUST set `funding_feerate_perkw` to the feerate for this transaction\n- - MUST ensure `temporary_channel_id` is unique from any\n- other channel ID with the same peer.\n+ - MUST set `generation` to a number greater than any previous\n+ `generation` it has sent to this receiving node which has reached\n+ `commitment_signed`.\n+ - SHOULD set `generation` to the lowest number which meets this requirement.\n \n The receiving node:\n - MAY fail the negotiation if:\n - the `locktime` is unacceptable\n - the `funding_feerate_per_kw` is unacceptable\n+ - the `generation` exceeds expectation by more than the maximum it would scan for recovery.\n \n #### Rationale\n `channel_id` for the `open_channel2` MUST be derived using a zero-d out\n@@ -926,6 +927,13 @@ Instead, the channel reserve is fixed at 1% of the total channel balance\n rounded down to the nearest whole satoshi or the `dust_limit_satoshis`,\n whichever is greater.\n \n+`generation` is a number which is used to generate the points used for\n+this pair of peers, with the aim of allowing automatic onchain\n+scanning for channels if all other information is lost. Since this\n+scan would presumably only try a limited number of generations, it is\n+best if this number is low, but it also needs to change for each\n+successive channel between the peers, to avoid obvious fingerprinting.\n+\n Note that `push_msat` has been omitted.\n \n ### The `accept_channel2` Message\n@@ -943,11 +951,9 @@ acceptance of the new channel.\n * [`u32`:`minimum_depth`]\n * [`u16`:`to_self_delay`]\n * [`u16`:`max_accepted_htlcs`]\n- * [`point`:`funding_pubkey`]\n+ * [`u64`:`generation`]\n * [`point`:`revocation_basepoint`]\n * [`point`:`payment_basepoint`]\n- * [`point`:`delayed_payment_basepoint`]\n- * [`point`:`htlc_basepoint`]\n * [`point`:`first_per_commitment_point`]\n * [`accept_tlvs`:`tlvs`]\n \n@@ -967,6 +973,10 @@ additions.\n \n The accepting node:\n - MAY respond with a `funding_satoshis` value of zero.\n+ - MUST set `generation` to a number greater than any previous\n+ `generation` it has sent to this receiving node which has reached\n+ `commitment_signed`.\n+ - SHOULD set `generation` to the lowest number which meets this requirement.\n \n #### Rationale\n \n@@ -985,6 +995,31 @@ Funding composition for channel establishment v2 makes use of the\n [Interactive Transaction Construction](#interactive-transaction-construction)\n protocol, with the following additional caveats.\n \n+#### Point Derivation\n+\n+The `funding_pubkey` and basepoints are derived from the two\n+`node_id`s and the higher of the two `generation` values; the\n+`payment_basepoint` is supplied directly.\n+\n+Derivation is done as follows:\n+\n+1. Start with two node ids, `N1` and `N2` (`N1` is the lesser of the\n+ two SEC1-encoded compressed public keys, `N2` the greater).\n+2. Derive a shared secret, `SS`, using ECDH on `N1` and `N2`.\n+3. Define tweaks `T` for each peer, using `SHA256(SS || generation || node_id || name)`, where:\n+ 1. `generation` is the `u64` larger of the two `generation` fields from `open_channel2` and `accept_channel2`.\n+ 2. `node_id` is the SEC1-encoded compressed public key of the peer.\n+ 3. `name` is a non-terminated ASCII string, e.g. `htlc` is the four bytes\n+ `0x68 0x74 0x6C 0x63`\n+4. The `funding_pubkey` is defined as the `node_id` + G*T(`funding`).\n+5. The `delayed_payment_basepoint` is defined as `node_id` + G*T(`delayed_payment`).\n+6. The `htlc_basepoint` is defined as the `node_id` + G*T(`htlc`).\n+\n+If the secret for `payment_basepoint` is derived in a similar manner,\n+it too can be easily recovered from just the `generation`, node key\n+and peer `node_id`. However, it may also point to an address for a\n+completely separate system (e.g. cold storage), so it is specified\n+explicitly in the protocol.\n \n #### The `tx_add_input` Message",
"sig": "57e28120528e1b9734432e1823a23c9697bd425d35a0140b664873ea2a015ed5ba122d74509b1b33f0a27c5de9aaeaeba6ffbd96c08bf9d3d2bd9d79858518a8"
}