diff --git a/.aspell.en.pws b/.aspell.en.pws index c9e3a0206..d34122a8e 100644 --- a/.aspell.en.pws +++ b/.aspell.en.pws @@ -23,6 +23,7 @@ nhops retransmitted dev tradeoff +kiloweight mixHeader uint hopsData @@ -115,6 +116,17 @@ delayedsig hopDataSize I'th segwit +RBF +accepter +accepter's +subtype +redeemScript +scriptSig +utxo +scriptPubKey +scriptPubKeys +scriptlen +sats htlc htlcs ChaCha @@ -386,3 +398,12 @@ csv CHECKSIGVERIFY IFDUP sats +workflow +PUSHDATA +prev +vout +rbf +standardness +perkw +prevtx +ints diff --git a/01-messaging.md b/01-messaging.md index 678393e1b..ea928c20c 100644 --- a/01-messaging.md +++ b/01-messaging.md @@ -316,11 +316,11 @@ The 2-byte `len` field indicates the number of bytes in the immediately followin The channel is referred to by `channel_id`, unless `channel_id` is 0 (i.e. all bytes are 0), in which case it refers to all channels. -The funding node: +The funding node using channel establishment `open_channel`: - for all error messages sent before (and including) the `funding_created` message: - MUST use `temporary_channel_id` in lieu of `channel_id`. -The fundee node: +The fundee node using channel establishment v1 (`accept_channel`): - for all error messages sent before (and not including) the `funding_signed` message: - MUST use `temporary_channel_id` in lieu of `channel_id`. diff --git a/02-peer-protocol.md b/02-peer-protocol.md index f9dc39351..fbc56c82d 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -7,12 +7,29 @@ operation, and closing. * [Channel](#channel) * [Definition of `channel_id`](#definition-of-channel_id) + * [Interactive Transaction Construction](#interactive-transaction-construction) + * [Set-Up and Vocabulary](#set-up-and-vocabulary) + * [Fee Responsibility](#fee-responsibility) + * [Overview](#overview) + * [The `tx_add_input` Message](#the-tx_add_input-message) + * [The `tx_add_output` Message](#the-tx_add_output-message) + * [The `tx_remove_input` and `tx_remote_output` Messages](#the-tx_remove_input-and-tx_remove_output-messages) + * [The `tx_complete` Message](#the-tx_complete-message) + * [The `tx_signatures` Message](#the-tx_signatures-message) * [Channel Establishment](#channel-establishment) * [The `open_channel` Message](#the-open_channel-message) * [The `accept_channel` Message](#the-accept_channel-message) * [The `funding_created` Message](#the-funding_created-message) * [The `funding_signed` Message](#the-funding_signed-message) * [The `funding_locked` Message](#the-funding_locked-message) + * [Channel Establishment v2](#channel-establishment-v2) + * [The `open_channel2` Message](#the-open_channel2-message) + * [The `accept_channel2` Message](#the-accept_channel2-message) + * [Funding Composition](#funding-composition) + * [The `commitment_signed` Message](#the-commitment_signed-message) + * [Sharing funding signatures: `tx_signatures`](#sharing-funding-signatures-tx_signatures) + * [The `init_rbf` Message](#the-init_rbf-message) + * [The `ack_rbf` Message](#the-ack_rbf-message) * [Channel Close](#channel-close) * [Closing Initiation: `shutdown`](#closing-initiation-shutdown) * [Closing Negotiation: `closing_signed`](#closing-negotiation-closing_signed) @@ -50,10 +67,361 @@ pubkey corresponding to the funding output nothing prevents duplicative channel ids. +### `channel_id`, v2 + +For channels established using the v2 protocol, the `channel_id` is the +SHA256(lesser-revocation-basepoint || greater-revocation-basepoint), +where the lesser and greater is based off the order of the basepoint. +The basepoints are compact DER-encoded public keys. + +If the peer's revocation basepoint is unknown (e.g. `open_channel2`), +a temporary `channel_id` should be found by using a zeroed out basepoint for the unknown peer. + +#### Rationale +These values must be remembered by both peers for correct operation anyway. +They're known from the first exchange of messages, obviating the need for +a `temporary_channel_id`. Finally, by mixing information from both sides, +they avoid `channel_id` collisions. + + +## Interactive Transaction Construction + +Interactive transaction construction allows two peers to collaboratively +build a transaction for broadcast. This protocol is the foundation +for dual-funded channels establishment (v2). + +### Set-Up and Vocabulary + +There are two parties to a transaction construction: an *initiator* +and a *non-initiator*. + The *initiator* is the peer which initiates the protocol, e.g. +for channel establishment v2 the *initiator* would be the peer which +sends `open_channel2`; for a close, the *initiator* sends `shutdown`. + +The protocol makes the following assumptions: + +- The `feerate` for the transaction is known. +- The `dust_limit` for the transaction is known. +- The `nLocktime` for the transaction has been negotiated, or is established + via convention. +- The transaction version is 2. + +### Fee Responsibility + +The *initiator* is responsible for paying the fees for the following fields, +to be referred to as the `common fields`. + + - version + - segwit marker + flag + - input count + - output count + - locktime + +The rest of the transaction bytes' fees are the responsibility of +the peer who contributed that input or output via `tx_add_input` or +`tx_add_output`, at the agreed upon `feerate`. + + +### Overview + +The *initiator* initiates the interactive transaction construction +protocol with `tx_add_input`. The *non-initiator* responds with any +of `tx_add_input`, `tx_add_output`, `tx_rm_input`, `tx_rm_output`, or + `tx_complete`. The protocol continues with the synchronous exchange +of interactive transaction protocol messages until both nodes have sent +and received a consecutive `tx_complete`. + +Once peers have exchanged consecutive `tx_complete`s, the +interactive transaction construction protocol is considered concluded. +Both peers should construct the transaction and fail the negotiation +if an error is discovered. + +This protocol is expressly designed to allow for parallel, multi-party +sessions to collectively construct a single transaction. This preserves +the ability to open multiple channels in a single transaction. While +`serial_id`s are generally chosen randomly, to maintain consistent transaction +ordering across all peer sessions, it is simplest to invert the bottom-bit of +received `serial_id` before forwarding them to other peers. + +Here are a few example exchanges. + +#### *initiator* only + +A, *initiator* has two inputs and an output (the funding output). +B, the *non-initiator* has nothing to contribute. +n + + +-------+ +-------+ + | |--(1)- tx_add_input -->| | + | |<-(2)- tx_complete ----| | + | |--(3)- tx_add_input -->| | + | A |<-(4)- tx_complete ----| B | + | |--(5)- tx_add_output ->| | + | | | | + | |--(6)- tx_complete --->| | + | |<-(7)- tx_complete ----| | + +-------+ +-------+ + +#### *initiator* and *non-initiator* + +A the *initiator* contributes 2 inputs and an output that they +then remove. B, the *non-initiator*, contributes 1 input and an output, +but waits until A adds a second input before contributing. + +Note that if A does not send a second input, the negotiation will end without +B's contributions. + + +-------+ +-------+ + | |--(1)- tx_add_input -->| | + | |<-(2)- tx_complete ----| | + | |--(3)- tx_add_output ->| | + | |<-(4)- tx_complete ----| | + | |--(5)- tx_add_input -->| | + | A |<-(6)- tx_add_input ---| B | + | |<-(7)- tx_add_output --| | + | |--(8)- tx_rm_output -->| | + | |<-(9)- tx_complete ----| | + | |--(10) tx_complete --->| | + +-------+ +-------+ + + +### The `tx_add_input` Message + +This message contains a transaction input. + +1. type: 66 (`tx_add_input`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`serial_id`] + * [`u16`:`prevtx_len`] + * [`prevtx_len*byte`:`prevtx`] + * [`u32`:`prevtx_vout`] + * [`u32`:`sequence`] + * [`u16`:`script_sig_len`] + * [`script_sig_len*byte`:`script_sig`] + +#### Requirements + +The sending node: + - MUST add all sent inputs to the transaction + - MUST use a unique `serial_id` for each input currently added to the + transaction + - MUST NOT re-transmit inputs it has received from the peer + - if is the *initiator*: + - MUST send even `serial_id`s + - if is the *non-initiator*: + - MUST send odd `serial_id`s + +The receiving node: + - MUST add all received inputs to the transaction + - MUST fail the negotiation if: + - the `prevtx` and `prevtx_vout` are identical to a previously added + (and not removed) input's + - `prevtx` is not a valid transaction + - `prevtx_vout` is greater or equal to the number of outputs on `prevtx` + - the `prevtx_out` input of `prevtx` is not an `OP_0` to `OP_16` + followed by a single push + - the `serial_id` is already included in the transaction + - the `serial_id` has the wrong parity + - if has received 4096 `tx_add_input` messages during this negotiation + +#### Rationale +Each node must know the set of the transaction inputs. The *non-initiator* +MAY omit this message. + +`serial_id` is a randomly chosen number which uniquely identifies this input. +Inputs in the constructed transaction are sorted by `serial_id`. + +`prevtx_tx` is the serialized transaction that contains the output +this input spends. Used to verify that the input is non-malleable. + +`prevtx_vout` is the index of the output being spent. + +`sequence` is the sequence number of this input. Must be less than +4294967294 (0xFFFFFFFE). See BIP125. + +`script_sig` is the scriptSig for the input, with length omitted. +The `script_sig` for non-P2SH-wrapped inputs will be an empty byte. + +### The `tx_add_output` Message + +This message adds a transaction output. + +1. type: 67 (`tx_add_output`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`serial_id`] + * [`u64`:`sats`] + * [`u16`:`scriptlen`] + * [`scriptlen*byte`:`script`] + +#### Requirements + +Either node: + - MAY omit this message + +The sending node: + - MUST add all sent outputs to the transaction + - if is the *initiator*: + - MUST send even `serial_id`s + - if is the *non-initiator*: + - MUST send odd `serial_id`s + +The receiving node: + - MUST add the specified output to the transaction + - MUST accept P2SH, P2WSH, P2WPKH, P2PKH `script`s + - MAY fail the negotiation if `script` is non-standard + - MUST fail the negotiation if: + - the `serial_id` is already included in the transaction + - the `serial_id` has the wrong parity + - it has received 4096 `tx_add_output` messages during this negotiation + - the `sats` amount is less than or equal to the `dust_limit` + + +#### Rationale +Each node must know the set of the transaction outputs. + +`serial_id` is a randomly chosen number which uniquely identifies this output. +Outputs in the constructed transaction are sorted by `serial_id`. + +`sats` is the satoshi value of the output. + +`script` is the scriptPubKey for the output. The length is omitted. +It's left undefined if you accept other standard outputs such as `OP_RETURN` +or implement stricter checks on standardness. + + +### The `tx_remove_input` and `tx_remove_output` Messages +These message removes an input from the transaction. + +1. type: 68 (`tx_remove_input`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`serial_id`] + +This message removes an output from the transaction. + +1. type: 69 (`tx_remove_output`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`serial_id`] + +#### Requirements + +The sending node: + - MUST NOT send a `tx_remove` with a `serial_id` it did not add + to the transaction or has already removed + +The receiving node: + - MUST remove the indicated input or output from the transaction + - MUST fail the negotiation if: + - the input or output identified by the `serial_id` was not added by the + sender + - the `serial_id` does not correspond to a currently added input (or output) + + +### The `tx_complete` Message + +This message signals the conclusion of a peer's transaction +contributions. + +1. type: 70 (`tx_complete`) +2. data: + * [`channel_id`:`channel_id`] + + +#### Requirements + +The nodes: + - MUST send this message in succession to conclude this protocol + +The receiving node: + - MUST use the negotiated inputs and outputs to construct a transaction + - MUST fail the negotiation if: + - the peer's total input satoshis is less than their outputs + - the peer's paid feerate does not meet or exceed the agreed `feerate`, + (based on the `minimum fee`). + - if is the *non-initiator*: + - the *initiator*'s fees do not cover the `common` fields + - there are more than 252 inputs + - there are more than 252 outputs + +#### Rationale +To signal the conclusion of exchange of transaction inputs and outputs. + +Upon successful exchange of `tx_complete` messages, both nodes +should construct the transaction and proceed to the next portion of the +protocol. For channel establishment v2, exchanging commitment transactions. + +For the `minimum fee` calculation see [BOLT #3](03-transactions.md#calculating-fees-for-collaborative-transaction-construction). + +The maximum inputs and outputs are capped at 252. This effectively fixes +the byte size of the input and output counts on the transaction to one (1). + + +### The `tx_signatures` Message + +1. type: 71 (`tx_signatures`) +2. data: + * [`channel_id`:`channel_id`] + * [`sha256`:`txid`] + * [`u16`:`num_witnesses`] + * [`num_witnesses*witness_stack`:`witness_stack`] + +1. subtype: `witness_stack` +2. data: + * [`u16`:`num_input_witness`] + * [`num_input_witness*witness_element`:`witness_element`] + +1. subtype: `witness_element` +2. data: + * [`u16`:`len`] + * [`len*byte`:`witness`] + + +#### Requirements +The sending node: + - MUST order the `witness_stack`s by the `serial_id` of the input they + correspond to + - number of `witness_stack`s MUST equal the number of inputs they added + +The receiving node: + - MUST fail the negotiation if: + - the message contains an empty `witness_stack` + - the number of `witness_stack`s does not equal the number of inputs + added by the sending node + - the `txid` does not match the txid of the transaction + - MUST fail the channel if: + - the `witness_stack` weight lowers the effective `feerate` + below the agreed upon transaction `feerate` + - SHOULD apply the `witness`es to the transaction and broadcast it + - MUST reply with their `tx_signatures` if not already transmitted + + +#### Rationale +`witness` is the data for a witness element in a witness stack. +Witness elements should *not* include their length. + +Witness data must be sorted according to the `serial_id` of +the corresponding input. + +While the `minimum fee` is calculated and verified at `tx_complete` conclusion, +it is possible for the fee for the exchanged witness data to be underpaid. +It is the responsibility of the sending peer to correctly account for the +required fee, e.g. a multisig witness stack whose weight exceeds 110. +If the fees paid by the peer (inputs - outputs) does not meet or exceed +the pre-established `feerate`, the receiving peer SHOULD immediately +fail the channel by broadcasting their commitment transaction. + ## Channel Establishment After authenticating and initializing a connection ([BOLT #8](08-transport.md) and [BOLT #1](01-messaging.md#the-init-message), respectively), channel establishment may begin. + +There are two pathways for establishing a channel, a legacy version presented here, +and a second version ([below](#channel-establishment-v2)). Which channel +establishment protocols are available for use is negotiated in the `init` message. + This consists of the funding node (funder) sending an `open_channel` message, followed by the responding node (fundee) sending `accept_channel`. With the channel parameters locked in, the funder is able to create the funding @@ -230,6 +598,8 @@ The receiving node MUST: `open_channel`, BUT before receiving a `funding_created` message: - accept a new `open_channel` message. - discard the previous `open_channel` message. + - if `option_dual_fund` has been negotiated: + - fail the channel. The receiving node MAY fail the channel if: - `announce_channel` is `false` (`0`), yet it wishes to publicly announce the channel. @@ -445,6 +815,338 @@ to broadcast the commitment transaction to get his funds back and open a new channel. To avoid this, the funder should ensure the funding transaction confirms in the next 2016 blocks. +If an RBF negotiation is in progress when a `funding_locked` message is +exchanged, the negotiation must be abandoned. + +## Channel Establishment v2 + +This is a revision of the channel establishment protocol. +It changes the previous protocol to allow the `accept_channel2` peer +(the *accepter*/*non-initiator*) to contribute inputs to the funding +transaction, via the interactive transaction construction protocol. + +The protocol is also expanded to include a mechanism for initiating RBF. + + +-------+ +-------+ + | |--(1)--- open_channel2 ----->| | + | |<-(2)--- accept_channel2 -----| | + | | | | + --->| | | | + | | | | | + | | |--(3)-- commitment_signed -->| | + | | |<-(4)-- commitment_signed ---| | + | | A | | B | + | | |<-(5)-- tx_signatures -------| | + | | |--(6)-- tx_signatures ------>| | + | | | | | + | | |--(a)--- init_rbf ----------->| | + ----| |<-(b)--- ack_rbf ------------| | + | | | | + | |--(7)--- funding_locked ----->| | +n- + | |<-(8)--- funding_locked ------| | + +-------+ +-------+ + + - where node A is *opener*/*initiator* and node B is + *accepter*/*non-initiator* + +### The `open_channel2` Message + +This message initiates the v2 channel establishment workflow. + +1. type: 64 (`open_channel2`) +2. data: + * [`chain_hash`:`chain_hash`] + * [`channel_id`:`channel_id`] + * [`u32`:`funding_feerate_perkw`] + * [`u32`:`commitment_feerate_perkw`] + * [`u64`:`funding_satoshis`] + * [`u64`:`dust_limit_satoshis`] + * [`u64`:`max_htlc_value_in_flight_msat`] + * [`u64`:`htlc_minimum_msat`] + * [`u16`:`to_self_delay`] + * [`u16`:`max_accepted_htlcs`] + * [`u32`:`locktime`] + * [`point`:`funding_pubkey`] + * [`point`:`revocation_basepoint`] + * [`point`:`payment_basepoint`] + * [`point`:`delayed_payment_basepoint`] + * [`point`:`htlc_basepoint`] + * [`point`:`first_per_commitment_point`] + * [`byte`:`channel_flags`] + * [`opening_tlvs`:`tlvs`] + + +1. `tlv_stream`: `opening_tlvs` +2. types: + 1. type: 1 (`option_upfront_shutdown_script`) + 2. data: + * [`u16`:`shutdown_len`] + * [`shutdown_len*byte`:`shutdown_scriptpubkey`] + + +Rationale and Requirements are the same as for [`open_channel`](#the-open_channel-message), with the following additions: + +#### Requirements: + +If nodes have negotiated `option_dual_fund`: + - the opening node: + - MUST NOT send `open_channel` + +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. + +The receiving node: + - MAY fail the negotiation if: + - the `locktime` is unacceptable + - the `funding_feerate_per_kw` is unacceptable + +#### Rationale +`channel_id` for the `open_channel2` MUST be derived using a zero-d out +basepoint for the peer's revocation basepoint. This allows the peer to +return channel-assignable errors before the *accepter*'s revocation +basepoint is known. + +`funding_feerate_perkw` indicates the fee rate that the opening node will +pay for the funding transaction in satoshi per 1000-weight, as described +in [BOLT-3, Appendix F](03-transactions.md#appendix-f-dual-funded-transaction-test-vectors). + +`locktime` is the locktime for the funding transaction. + +The receiving node, if the `locktime` or `feerate_funding_perkw` is considered +out of an acceptable range, may fail the negotiation. However, it is +recommended that the *accepter* permits the channel open to proceed +without their participation in the channel's funding. + +Note that `open_channel`'s `channel_reserve_satoshi` has been omitted. +Instead, the channel reserve is fixed at 1% of the total channel balance +(`open_channel2`.`funding_satoshis` + `accept_channel2`.`funding_satoshis`) +rounded down to the nearest whole satoshi or the `dust_limit_satoshis`, +whichever is greater. + +Note that `push_msat` has been omitted. + +### The `accept_channel2` Message + +This message contains information about a node and indicates its +acceptance of the new channel. + +1. type: 65 (`accept_channel2`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`funding_satoshis`] + * [`u64`:`dust_limit_satoshis`] + * [`u64`:`max_htlc_value_in_flight_msat`] + * [`u64`:`htlc_minimum_msat`] + * [`u32`:`minimum_depth`] + * [`u16`:`to_self_delay`] + * [`u16`:`max_accepted_htlcs`] + * [`point`:`funding_pubkey`] + * [`point`:`revocation_basepoint`] + * [`point`:`payment_basepoint`] + * [`point`:`delayed_payment_basepoint`] + * [`point`:`htlc_basepoint`] + * [`point`:`first_per_commitment_point`] + * [`accept_tlvs`:`tlvs`] + +1. `tlv_stream`: `accept_tlvs` +2. types: + 1. type: 1 (`option_upfront_shutdown_script`) + 2. data: + * [`u16`:`shutdown_len`] + * [`shutdown_len*byte`:`shutdown_scriptpubkey`] + + +Rationale and Requirements are the same as listed above, +for [`accept_channel`](#the-accept_channel-message) with the following +additions. + +#### Requirements: + +The accepting node: + - MAY respond with a `funding_satoshis` value of zero. + +#### Rationale + +The `funding_satoshis` is the amount of bitcoin in satoshis +the *accepter* will be contributing to the channel's funding transaction. + +Note that `accept_channel`'s `channel_reserve_satoshi` has been omitted. +Instead, the channel reserve is fixed at 1% of the total channel balance +(`open_channel2`.`funding_satoshis` + `accept_channel2`.`funding_satoshis`) +rounded down to the nearest whole satoshi or the `dust_limit_satoshis`, +whichever is greater. + + +### Funding Composition +Funding composition for channel establishment v2 makes use of the +[Interactive Transaction Construction](#interactive-transaction-construction) +protocol, with the following additional caveats. + + +#### The `tx_add_input` Message + +No additional caveats or requirements. + + +#### The `tx_add_output` Message +##### Requirements +The sending node: + - if is the *opener*: + - MUST send at least one `tx_add_output`, which contains the + channel's funding output + +##### Rationale +The channel funding output must be added by the *opener*, who pays its fees. + + +#### The `tx_complete` Message + +Upon receipt of consecutive `tx_complete`s, the receiving node: + - if is the *accepter*: + - MUST fail the negotiation if: + - no funding output was received + - the value of the funding output is not equal to the sum of + `open_channel2`.`funding_satoshis` and `accept_channel2`. + `funding_satoshis` + - the value of the funding output is less than the `dust_limit` + - if is an RBF attempt: + - MUST fail the negotiation if: + - the transaction's total fees is less than the last + successfully negotiated transaction's fees + - the transaction does not share a common input with all previous + funding transactions + +### The `commitment_signed` Message + +This message is exchanged by both peers. It contains the signatures for +the first commitment transaction. + +Rationale and Requirements are the same as listed below, +for [`commitment_signed`](#commiting-updates-so-far-commitment_signed) with the following additions. + +#### Requirements + +The sending node: + - MUST send zero HTLC's. + +The receiving node: + - if the message has one or more HTLC's: + - MUST fail the negotiation + - if it has not already transmitted its `commitment_signed`: + - MUST send `commitment_signed` + - Otherwise: + - MUST send `tx_signatures` + +#### Rationale + +The first commitment transaction has no HTLC's. + + +### Sharing funding signatures: `tx_signatures` + +After a valid `commitment_signature` has been received +from the peer and a `commitment_signature` has been sent, a peer: + - MUST transmit a [`tx_signatures` message](#the-tx_signatures-message) with their signatures for + the funding transaction + + +#### Requirements +The sending node: + - MUST verify it has received a valid commitment signature from its peer + - MUST remember the details of this funding transaction + - if it has NOT received a valid `commitment_signed` message: + - MUST NOT send a `tx_signatures` message + +The receiving node: + - if the received `witness_stack` weight results in the peer's + paid feerate falling below the *opener*'s feerate for the funding + transaction: + - SHOULD broadcast their commitment transaction, closing the channel. + - SHOULD apply `witness`es to the funding transaction and broadcast it + - if has already sent or received a `funding_locked` message for this + channel: + - MUST ignore this message + +#### Rationale +A peer sends their `tx_signatures` as soon as they have received a valid +`commitments_signed` message. + +The channel should be preemptively closed in the case where a peer provides +valid witness data that causes their paid feerate to fall beneath the +`open_channel2.funding_feerate_perkw` rate. This penalizes the peer +for underpayment of fees. + + +### The `init_rbf` Message + +This message initiates a replacement of a broadcast funding transaction. + +1. type: 72 (`init_rbf`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`funding_satoshis`] + * [`u32`:`locktime`] + * [`byte`:`fee_step`] + +#### Requirements + +The sender: + - MUST have sent `open_channel2` + - MUST set `fee_step` greater than zero and greater than any prior `fee_step` + - MUST NOT have sent or received a `funding_locked` message. + +The recipient: + - MUST respond with either an error or an `ack_rbf` message. + - MUST fail the negotiation if: + - the `fee_step` is not greater than the last successfully negotiated + `init_rbf` attempt or `one` if no prior successful `init_rbf` has + been received + - they have already sent or received `funding_locked` + - MAY fail the negotiation for any reason + +#### Rationale +`fee_step` is an integer value, which specifies the `feerate` for this +funding transaction, as a rate of increase above the `open_channel2`. +`funding_feerate_perkw`. + +The effective `funding_feerate_perkw` for this RBF attempt +if calculated as 1.25^`fee_step` * `funding_feerate_perkw`. +E.g. if `feerate_per_kw_funding` is 512 and the `fee_step` is 1, +the effective `feerate` for this RBF attempt is 512 + 512 / 4 or 640 sat/kw. +A `fee_step` 2 would be `1.25^2 * 512` (or 640 + 640 / 4), 800 sat/kw. + +If a valid `funding_locked` message is received in the middle of an +RBF attempt, the attempt MUST be abandoned. + +### The `ack_rbf` Message + +1. type: 73 (`ack_rbf`) +2. data: + * [`channel_id`:`channel_id`] + * [`u64`:`funding_satoshis`] + +#### Requirements + +The sender: + - MUST NOT have sent or received a `funding_locked` message + +The recipient: + - MUST either fail the negotiation or transmit a `tx_add_input` message + +#### Rationale +`funding_satoshis` is the amount of satoshis that this peer will +contribute to the funding output. Note that this may be different than +the amount transmitted in either `accept_channel2`.`funding_satoshis` or +any previous `ack_rbf`.`funding_satoshis`, as the amount the *accepter* +wishes to commit to the funding output may change. + +It's recommended that a peer, rather than fail the RBF negotiation due to +a large feerate change, instead sets their `funding_satoshis` to zero, +and decline to participate further in the channel funding. + ## Channel Close Nodes can negotiate a mutual close of the connection, which unlike a diff --git a/03-transactions.md b/03-transactions.md index 43b7ed1cd..3e042d277 100644 --- a/03-transactions.md +++ b/03-transactions.md @@ -37,6 +37,7 @@ This details the exact format of on-chain transactions, which both sides need to * [Storage Tests](#storage-tests) * [Appendix E: Key Derivation Test Vectors](#appendix-e-key-derivation-test-vectors) * [Appendix F: Commitment and HTLC Transaction Test Vectors (anchors)](#appendix-f-commitment-and-htlc-transaction-test-vectors-anchors) + * [Appendix G: Dual Funded Transaction Test Vectors](#appendix-f-dual-funded-transaction-test-vectors) * [References](#references) * [Authors](#authors) @@ -477,6 +478,14 @@ A node: - if the resulting fee rate is too low: - MAY fail the channel. + +### Calculating Fees for Collaborative Transactions + +For transactions constructed using the [interactive protocol](02-peer-protocol.md#interactive-transaction-construction), +fees are paid by each party to the transaction, at `feerate` determined during the +initiation, with the initiator covering the fees for the common transaction fields. + + ## Commitment Transaction Construction This section ties the previous sections together to detail the @@ -710,6 +719,104 @@ at each bucket is a prefix of the desired index. # Appendix A: Expected Weights +## Expected Weight of the Funding Transaction (v2 Channel Establishment) + +The *expected weight* of a funding transaction is calculated as follows: + + inputs: 40 bytes + var_int + `scriptlen` + - previous_out_point: 36 bytes + - hash: 32 bytes + - index: 4 bytes + - var_int: ? bytes (dependent on `scriptlen`) + - script_sig: `scriptlen` + - witness <---- Cost for "witness" data calculated separately. + - sequence: 4 bytes + + non_funding_outputs: 8 bytes + var_int + `scriptlen` + - value: 8 bytes + - var_int: ? bytes (dependent on `scriptlen`) + - script_sig: `scriptlen` + + funding_output: 43 bytes + - value: 8 bytes + - var_int: 1 byte + - script: 34 bytes + - OP_0: 1 byte + - PUSHDATA(32-byte-hash): 33 bytes + +Multiplying non-witness data by 4 results in a weight of: + + // transaction_fields = 10 (version, input count, output count, locktime) + // segwit_fields = 2 (marker + flag) + // funding_transaction = 43 + num_inputs * 40 + num_outputs * 8 + // + sum(scriptlen) + sum(var_ints) + + funding_transaction_weight = 4 * (funding_transaction + transaction_fields) + segwit_fields + + witness_weight = sum(max_witness_len) + + overall_weight = funding_transaction_weight + witness_weight + +### Calculating Fees for Collaborative Transaction Construction + +Every participant in a collaborative transaction covers the fees for +their own inputs and outputs. The initiator also provides funds to +cover for the common transaction fields. + +Upon successful exchange of `tx_complete` messages, each peer must at +least cover a minimum estimated fee. Here is how to calculate the minimum +fee for each participant. + +The minimum witness weight for an input is 110. + +In the following, the initiator has provided one input (P2WPKH), one change output +(P2WPKH), and the funding output. The contributor has provided two +inputs (P2WPKH) and two change outputs (P2WPKH). + +Assuming a `feerate` of 253 per kiloweight, the initiator's minimum fee is +calculated as follows. Note that the feerate is rounded down to the nearest satoshi. + + initiator_weight = transaction_fields * 4 + + segwit_fields + + p2wpkh_input * 4 + + funding_output * 4 + + p2wpkh_output * 4 + + input_count * 110 (minimum witness weight) + + initiator_weight = 10 * 4 + + 2 + + 41 * 4 + + 43 * 4 + + 31 * 4 + + 107 + + initiator_weight = 612 + + initiator_fees = initiator_weight * feerate + initiator_fees = 612 * 253 / 1000 + initiator_fees = 154 sats + +The contributor's minimum fee is calculated as follows. + + contributor_weight = 2 * p2wpkh_input * 4 + + 2 * p2wpkh_output * 4 + + input_count * 110 (minimum witness weight) + + contributor_weight = 2 * 41 * 4 + + 2 * 31 * 4 + + 2 * 110 + + contributor_weight = 796 + + contributor_fees = contributor_weight * feerate + contributor_fees = 796 * 253 / 1000 + contributor_fees = 201 sats + + +This is an estimated fee. The peer MUST at least contribute the estimated rate, +and MUST exceed the minimum fee in the case that their witness weight is greater +than the estimated weight of 110 per input. + + ## Expected Weight of the Commitment Transaction The *expected weight* of a commitment transaction is calculated as follows: @@ -1006,7 +1113,7 @@ The resulting funding transaction is: In the following: - *local* transactions are considered, which implies that all payments to *local* are delayed. - - It's assumed that *local* is the funder. + - It's assumed that *local* is the opener. - Private keys are displayed as 32 bytes plus a trailing 1 (Bitcoin's convention for "compressed" private keys, i.e. keys for which the public key is compressed). - Transaction signatures are all deterministic, using RFC6979 (using HMAC-SHA256). @@ -1953,6 +2060,244 @@ before subtraction of: "RemoteSigHex": "30450221008fd5dbff02e4b59020d4cd23a3c30d3e287065fda75a0a09b402980adf68ccda022001e0b8b620cd915ddff11f1de32addf23d81d51b90e6841b2cb8dcaf3faa5ecf" } ] + +# Appendix G: Dual Funded Transaction Test Vectors + +## Funding Transaction Construction +### Preliminaries: + +``` +Genesis block 0: +0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000 + +Block 1:0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910ff86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de8be86815cffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a0100000017a914113ca7e584fe1575b6fc39abae991529f66eda58870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000 +Coinbase address pubkey: 2MtpN8zCxTp8AWSg7VBjBX7vU6x73bVCKP8 +Coinbase address privkey: cPxFtfE1w3ptFnsZvvFeWji21kTArYa9GXwMkYsoQHdaJKrjUTek + +Parent transaction (spends coinbase of block 1): +02000000000101f86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de800000000171600141fb9623ffd0d422eacc450fd1e967efc477b83ccffffffff0580b2e60e00000000220020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b94680b2e60e0000000017a9146a235d064786b49e7043e4a042d4cc429f7eb6948780b2e60e00000000160014fbb4db9d85fba5e301f4399e3038928e44e37d3280b2e60e0000000017a9147ecd1b519326bc13b0ec716e469b58ed02b112a087f0006bee0000000017a914f856a70093da3a5b5c4302ade033d4c2171705d387024730440220696f6cee2929f1feb3fd6adf024ca0f9aa2f4920ed6d35fb9ec5b78c8408475302201641afae11242160101c6f9932aeb4fcd1f13a9c6df5d1386def000ea259a35001210381d7d5b1bc0d7600565d827242576d9cb793bfe0754334af82289ee8b65d137600000000 +``` + +### Funding transaction (spends parent's outputs): + +Locktime: 618013 +Feerate: 253 sat/kiloweight +Opener's `funding_satoshi`: 4 0000 0000 sat +Accepter's `funding_satoshi`: 4 0000 0000 sat + +Inputs: +``` +4303ca8ff10c6c345b9299672a66f111c5b81ae027cc5b0d4d39d09c66b032b9 0 + witness_data: + preimage: 20 68656c6c6f2074686572652c2074686973206973206120626974636f6e212121 + witness_script: 27 82012088a820add57dfe5277079d069ca4ad4893c96de91f88ffb981fdc6a2a34d5336c66aff87 + scriptPubKey: 0020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b946 + address: bcrt1qlky6eaj5sh0cj7tanwnm573nvf9vg3f0qrdssyrlxsjh6vl9h9rql40v2g + +4303ca8ff10c6c345b9299672a66f111c5b81ae027cc5b0d4d39d09c66b032b9 1 + redeemScript: 0020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b946 + witness_data: + preimage: 20 68656c6c6f2074686572652c2074686973206973206120626974636f6e212121 + witness_script: 27 82012088a820add57dfe5277079d069ca4ad4893c96de91f88ffb981fdc6a2a34d5336c66aff87 + scriptPubKey: a9146a235d064786b49e7043e4a042d4cc429f7eb69487 + address: 2N2vRyi3cm5VYpw218MJJrJWK1Jd4qbaefW + +4303ca8ff10c6c345b9299672a66f111c5b81ae027cc5b0d4d39d09c66b032b9 2 + pubkey: 034695f5b7864c580bf11f9f8cb1a94eb336f2ce9ef872d2ae1a90ee276c772484 + privkey: cUM8Dr33wK4uFmw3Tz8sbQ7BiBNgX5BthRurU7RkgXVvNUPcWrJf + witness_program: fbb4db9d85fba5e301f4399e3038928e44e37d32 + scriptPubKey: 0014fbb4db9d85fba5e301f4399e3038928e44e37d32 + address: bcrt1qlw6dh8v9lwj7xq058x0rqwyj3ezwxlfjxsy7er + +4303ca8ff10c6c345b9299672a66f111c5b81ae027cc5b0d4d39d09c66b032b9 3 + pubkey: 034695f5b7864c580bf11f9f8cb1a94eb336f2ce9ef872d2ae1a90ee276c772484 + privkey: cUM8Dr33wK4uFmw3Tz8sbQ7BiBNgX5BthRurU7RkgXVvNUPcWrJf + redeemScript: 0014fbb4db9d85fba5e301f4399e3038928e44e37d32 + witness_program: fbb4db9d85fba5e301f4399e3038928e44e37d32 + scriptPubKey: a9147ecd1b519326bc13b0ec716e469b58ed02b112a087 + address: 2N4ogqX3kaLTT2jjxi18nAiyWdwKtE5PxLF +``` + +Expected Opener's `tx_add_input` (inputs 0+1 above): +``` + num_inputs: 2 + tx_add_input:[ + { + channel_id: xxx, + serial_id: 10, + prevtx_len: 353, + prevtx: 02000000000101f86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de800000000171600141fb9623ffd0d422eacc450fd1e967efc477b83ccffffffff0580b2e60e00000000220020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b94680b2e60e0000000017a9146a235d064786b49e7043e4a042d4cc429f7eb6948780b2e60e00000000160014fbb4db9d85fba5e301f4399e3038928e44e37d3280b2e60e0000000017a9147ecd1b519326bc13b0ec716e469b58ed02b112a087f0006bee0000000017a914f856a70093da3a5b5c4302ade033d4c2171705d387024730440220696f6cee2929f1feb3fd6adf024ca0f9aa2f4920ed6d35fb9ec5b78c8408475302201641afae11242160101c6f9932aeb4fcd1f13a9c6df5d1386def000ea259a35001210381d7d5b1bc0d7600565d827242576d9cb793bfe0754334af82289ee8b65d137600000000 +, + prev_vout: 0, + sequence: 4294967293, + script_sig_len: 0, + script_sig: null + },{ + channel_id: xxx, + serial_id: 20, + prevtx_len: 353, + prevtx: 02000000000101f86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de800000000171600141fb9623ffd0d422eacc450fd1e967efc477b83ccffffffff0580b2e60e00000000220020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b94680b2e60e0000000017a9146a235d064786b49e7043e4a042d4cc429f7eb6948780b2e60e00000000160014fbb4db9d85fba5e301f4399e3038928e44e37d3280b2e60e0000000017a9147ecd1b519326bc13b0ec716e469b58ed02b112a087f0006bee0000000017a914f856a70093da3a5b5c4302ade033d4c2171705d387024730440220696f6cee2929f1feb3fd6adf024ca0f9aa2f4920ed6d35fb9ec5b78c8408475302201641afae11242160101c6f9932aeb4fcd1f13a9c6df5d1386def000ea259a35001210381d7d5b1bc0d7600565d827242576d9cb793bfe0754334af82289ee8b65d137600000000 + prev_vout: 1, + sequence: 4294967293, + script_sig_len: 68, + script_sig: 0020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b946 + } + ] +``` + +Expected Accepter's `tx_add_input` (inputs 2+3 above): +``` + num_inputs: 2 + tx_add_input:[ + { + channel_id: xxx, + serial_id: 11, + prevtx_len: 353, + prevtx: 02000000000101f86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de800000000171600141fb9623ffd0d422eacc450fd1e967efc477b83ccffffffff0580b2e60e00000000220020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b94680b2e60e0000000017a9146a235d064786b49e7043e4a042d4cc429f7eb6948780b2e60e00000000160014fbb4db9d85fba5e301f4399e3038928e44e37d3280b2e60e0000000017a9147ecd1b519326bc13b0ec716e469b58ed02b112a087f0006bee0000000017a914f856a70093da3a5b5c4302ade033d4c2171705d387024730440220696f6cee2929f1feb3fd6adf024ca0f9aa2f4920ed6d35fb9ec5b78c8408475302201641afae11242160101c6f9932aeb4fcd1f13a9c6df5d1386def000ea259a35001210381d7d5b1bc0d7600565d827242576d9cb793bfe0754334af82289ee8b65d137600000000 + prev_vout: 2, + script_sig_len: 0, + script_sig: null + },{ + channel_id: xxx, + serial_id: 17, + prevtx_len: 353, + prevtx: 02000000000101f86fd1d0db3ac5a72df968622f31e6b5e6566a09e29206d7c7a55df90e181de800000000171600141fb9623ffd0d422eacc450fd1e967efc477b83ccffffffff0580b2e60e00000000220020fd89acf65485df89797d9ba7ba7a33624ac4452f00db08107f34257d33e5b94680b2e60e0000000017a9146a235d064786b49e7043e4a042d4cc429f7eb6948780b2e60e00000000160014fbb4db9d85fba5e301f4399e3038928e44e37d3280b2e60e0000000017a9147ecd1b519326bc13b0ec716e469b58ed02b112a087f0006bee0000000017a914f856a70093da3a5b5c4302ade033d4c2171705d387024730440220696f6cee2929f1feb3fd6adf024ca0f9aa2f4920ed6d35fb9ec5b78c8408475302201641afae11242160101c6f9932aeb4fcd1f13a9c6df5d1386def000ea259a35001210381d7d5b1bc0d7600565d827242576d9cb793bfe0754334af82289ee8b65d137600000000 + prev_vout: 3, + script_sig_len: 22, + script_sig: 0014fbb4db9d85fba5e301f4399e3038928e44e37d32 + } + ] +``` + +Outputs: (scriptPubKeys) +``` +# opener's change address +pubkey: 0206e626a4c6d4392d4030bc78bd93f728d1ba61214a77c63adc17d71e32ded3df +# privkey: cSpC1KYEV1vsUFBwTdcuRkncbwfipY1m5zuQ9CjgAYwiVvbQ4fc1 +scriptPubKey: 00141ca1cca8855bad6bc1ea5436edd8cff10b7e448b +address: bcrt1qrjsue2y9twkkhs022smwmkx07y9hu3ytshgjmj + +# accepter's change address +pubkey: 028f3978c211f4c0bf4d20674f345ae14e08871b25b2c957b4bdbd42e9726278fc +privkey: cQ1HXnbAE4wGhuB2b9rJEydV5ayeEmMqxf1dvHPZmyMTPkwvZJyg +scriptPubKey: 001444cb0c39f93ecc372b5851725bd29d865d333b10 +address: bcrt1qgn9scw0e8mxrw26c29e9h55asewnxwcsdxdp50 + +# the 2-of-2s +pubkey1: 0292edb5f7bbf9e900f7e024be1c1339c6d149c11930e613af3a983d2565f4e41e +pubkey2: 02e16172a41e928cbd78f761bd1c657c4afc7495a1244f7f30166b654fbf7661e3 +script_def: multi(2,0292edb5f7bbf9e900f7e024be1c1339c6d149c11930e613af3a983d2565f4e41e,02e16172a41e928cbd78f761bd1c657c4afc7495a1244f7f30166b654fbf7661e3) +script: 52210292edb5f7bbf9e900f7e024be1c1339c6d149c11930e613af3a983d2565f4e41e2102e16172a41e928cbd78f761bd1c657c4afc7495a1244f7f30166b654fbf7661e352ae +scriptPubKey: 0020297b92c238163e820b82486084634b4846b86a3c658d87b9384192e6bea98ec5 +address: bcrt1q99ae9s3czclgyzuzfpsggc6tfprts63uvkxc0wfcgxfwd04f3mzs3asq6l +``` + +Expected Opener's `tx_add_output`: + +``` + num_outputs: 2 + tx_add_output[ + { + channel_id: xxx, + serial_id: 30, + sats: 99999809 + scriptlen: 22 + script: 1600141ca1cca8855bad6bc1ea5436edd8cff10b7e448b + },{ + channel_id: xxx, + serial_id: 44, + sats: 800000000 + scriptlen: 34 + script: 220020297b92c238163e820b82486084634b4846b86a3c658d87b9384192e6bea98ec5 + } + ] +``` + +Expected Accepter's `tx_add_output`: + +``` + num_outputs: 1 + tx_add_output[ + { + channel_id: xxx, + serial_id: 33, + sats: 99999809 + scriptlen: 22 + script: 16001444cb0c39f93ecc372b5851725bd29d865d333b10 + } +``` + +Expected Fee Calculation: + +Opener's fees and change: +``` + initiator_weight = transaction_fields * 4 + + segwit_fields + + inputs * 4 + + funding_output * 4 + + change_output * 4 + + sum_max_witness_weight + + initiator_weight = 10 * 4 + + 2 + + (41 + 74) * 4 + + 43 * 4 + + 31 * 4 + + 148 + + initiator_weight = 946 + + initiator_fees = initiator_weight * feerate + initiator_fees = 946 * 253 / 1000 + initiator_fees = 239 sats + + change = total_funding + - funding_sats + - fees + + change = 5 0000 0000 + - 4 0000 0000 + - 239 + + change = 9999 9761 +``` + +Accepter's fees and change: +``` + contributor_weight = 2 * p2wpkh_input * 4 + + 2 * p2wpkh_output * 4 + + sum_max_witness_weight + + contributor_weight = (41 + 63) * 4 + + 31 * 4 + + 218 + + contributor_weight = 758 + + contributor_fees = contributor_weight * feerate + contributor_fees = 758 * 253 / 1000 + contributor_fees = 191 sats + + change = total_funding + - funding_sats + - fees + + change = 5 0000 0000 + - 4 0000 0000 + - 191 + + change = 9999 9809 + +``` + +Unsigned Funding Transaction: +``` +0200000004b932b0669cd0394d0d5bcc27e01ab8c511f1662a6799925b346c0cf18fca03430000000000ffffffffb932b0669cd0394d0d5bcc27e01ab8c511f1662a6799925b346c0cf18fca03430200000000ffffffffb932b0669cd0394d0d5bcc27e01ab8c511f1662a6799925b346c0cf18fca03430300000000ffffffffb932b0669cd0394d0d5bcc27e01ab8c511f1662a6799925b346c0cf18fca03430100000000ffffffff0341e0f505000000001600141ca1cca8855bad6bc1ea5436edd8cff10b7e448b11e0f5050000000016001444cb0c39f93ecc372b5851725bd29d865d333b100008af2f00000000220020297b92c238163e820b82486084634b4846b86a3c658d87b9384192e6bea98ec51d6e0900 +``` + +Signed Funding Transaction: +``` +TODO ``` # References diff --git a/09-features.md b/09-features.md index ea37b8b5e..0b321ebbf 100644 --- a/09-features.md +++ b/09-features.md @@ -40,6 +40,7 @@ The Context column decodes as follows: | 18/19 | `option_support_large_channel` | Can create large channels | IN | | [BOLT #2](02-peer-protocol.md#the-open_channel-message) | | 20/21 | `option_anchor_outputs` | Anchor outputs | IN | `option_static_remotekey` | [BOLT #3](03-transactions.md) | | 22/23 | `option_anchors_zero_fee_htlc_tx` | Anchor commitment type with zero fee HTLC transactions | IN | | [BOLT #3][bolt03-htlc-tx], [lightning-dev][ml-sighash-single-harmful]| +| 28/29 | `option_dual_fund` | Use v2 of channel open, enables dual funding | IN9 | `option_anchor_outputs`, `option_static_remotekey` | [BOLT #2](02-peer-protocol.md) | ## Requirements diff --git a/tools/spellcheck.sh b/tools/spellcheck.sh index fb309315f..709913db3 100755 --- a/tools/spellcheck.sh +++ b/tools/spellcheck.sh @@ -68,7 +68,8 @@ do -e 's/0x[a-fA-F0-9 ]\+//g' \ -e 's/[a-fA-F0-9]\{20,\}//g' \ -e 's/^ .*_htlcs//g' \ - -e 's/ \(bc\|tb\)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]\+//g' \ + -e 's/ ln\(bc\|tb\)[0-9munp]*1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]\+//g' \ + -e 's/ \(bc\|tb\|bcrt\)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]\+//g' \ -e 's/pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny//' \ -e 's/[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]\{20,\}//g' < $f | tee /tmp/`basename $f`.aspell | aspell -l en_US --home-dir ${homedir} list) if [ -n "$WORDS" ]; then