比特币闪电网络代码解析

1. 引言

前序博客有:

根据BOLT #3: Bitcoin Transaction and Script Formats可知,闪电网络中的大多数交易output都是pay-to-witness-script-hash(P2WSH)output:为Segwit版本的P2SH。未来spend这些output,witness stack中的最后一项必须为用于生成所花费的P2WSH的实际脚本。

本文关注的代码库为:

重要参数:

--bitcoin.minhtlc= The smallest HTLC we are willing to forward on our channels, in millisatoshi (default: 1000)
--bitcoin.basefee= The base fee in millisatoshi we will charge for forwarding payments on our channels (default: 1000)
--bitcoin.feerate= The fee rate used when forwarding payments on our channels. The total fee charged is basefee + (amount * feerate / 1000000), where amount is the forwarded amount. (default: 1)
--bitcoin.timelockdelta= The CLTV delta we will subtract from a forwarded HTLC's timelock value (default: 144)

总体架构设计为:


                     -----------------
                     | KeysInterface |  --------------
                     -----------------  | UserConfig |
         --------------------       ^   --------------
   ------| MessageSendEvent |       |   ^     ----------------
  /      --------------------       |   |     | FeeEstimator | <-----------------------
 |   (as MessageSendEventsProvider) |   |     ----------------                         \
 |                         ^        |   |    ^                ------------------------  |
 |                          \       |   |   /      ---------> | BroadcasterInterface |  |
 |                           \      |   |  /      /           ------------------------  |
 |                            \     |   | /      /                          ^           |
 |    (as                      ------------------       ----------------    |           |
 |    ChannelMessageHandler)-> | ChannelManager | ----> | chain::Watch |    |           |
 v               /             ------------------       ----------------    |           |
--------------- /                  (as EventsProvider)         ^            |           |
| PeerManager |-                             \                 |            |           |
---------------                               \                | (is-a)     |           |
 |                    -----------------        \       _----------------   /           /
 |                    | chain::Access |         \     / | ChainMonitor |---------------
 |                    -----------------          \   /  ----------------
 |                            ^                   \ /          |
(as RoutingMessageHandler)    |                    v           v
  \                   ----------------------   ---------   -----------------
   -----------------> | NetGraphMsgHandler |   | Event |   | chain::Filter |
                      ----------------------   ---------   -----------------

核心组成主要有:

  • ChannelManager:用于维护多个通道,路由支付,提供发起支付和接收支付的API接口。
  • ChannelMonitor:监督通道的链上状态,惩罚错误操作的交易方,当有即将过期的unresolved HTLCs时强制关闭通道。
  • NetGraphMsgHandler:处理receiving channel和node announcements,可通过get_route计算路由路径。
  • PeerManager:处理认证和加密通讯协议,监督节点的活跃状态,与ChannelManager和NetGraphMsgHandler进行消息交互。

2. lightning模块

  • src/util/message_signing.rs:为闪电网络中消息的签名和验签机制。
  • src/util/zbase32.rs:为对human-oriented base-32 encoding 的实现。最终的消息签名经过了base32编码:signature = zbase32(SigRec(sha256d(("Lightning Signed Message:" + msg)))
  • src/util/byte_utils.rs:实现了big-endian、little-endian 与array、slice之间的转换。
  • src/util/chacha20.rs、chacha20poly1305rfc.rs、poly1305.rs:ChaCha20-Poly1305是由ChaCha20流密码和Poly1305消息认证码(MAC)结合的一种应用在互联网安全协议中的认证加密算法。
  • src/util/config.rs:包含了通道握手、通道配置、用户通道配置信息。
  • src/util/enforcing_trait_impls:可强制进行某种策略验证的强化签名。提供了sign_counterparty_commitment、sign_holder_commitment_and_htlcs等签名接口。
pub struct EnforcingSigner {
	pub inner: InMemorySigner,
	/// The last counterparty commitment number we signed, backwards counting
	pub last_commitment_number: Arc<Mutex<Option<u64>>>,
	/// The last holder commitment number we revoked, backwards counting
	pub revoked_commitment: Arc<Mutex<u64>>,
	pub disable_revocation_policy_check: bool,
}
  • src/util/errors.rs:定义了客户端常用错误码及描述。
  • src/util/events.rs:定义了Event(如FundingGenerationReady、PaymentReceived、PaymentSent、PaymentFailed、PendingHTLCsForwardable、SpendableOutputs),以及MessageSendEvent(由ChannelManager生成,有SendAcceptChannel、SendOpenChannel等等)
  • src/util/fuzz_wrappers.rs:定义了hash_to_message 宏。
  • src/util/logger.rs:实现了日志接口。
  • src/util/macro_logger.rs:定义了一些自定义结构体的日志打印规则。
  • src/util/scid_utils.rs:定义了short_channel_id 与 block、tx_index、vout_index之间的一些转换规则。
  • src/util/ser_macros.rs:定义了tlv、tlv_stream的一些编码解码宏。
  • src/util/ser.rs:定义一些结构体的read、write及序列化接口。
  • src/util/test_utils.rs:定义了一些chain、channel等接口。
  • src/util/transaction_utils.rs:定义了交易output相关接口。有一些SegWit Transaction input/output weight测试用例。
  • src/routing/network_graph.rs:维护通道和节点信息,可用于计算路由路径。
  • src/routing/router.rs:基于NetworkGraph和RoutingFees等计算路由路径,提供了get_route API。
  • src/chain/chaininterface.rs:定义了闪电网络与链的交互接口。
  • src/chain/chainmonitor.rs:用于连接链下通道和监控链上交易。
  • src/chain/channelmonitor.rs:用于监控链上交易并创建相应声明。
  • src/chain/keysinterface.rs:提供闪电网络中所需keys。
  • src/chain/onchaintx.rs:用于构建声明,并bump in-flight transactions until confirmations。
  • src/chain/package.rs:用于assemble claimable outpoints in package of one or more transactions。
  • src/chain/transaction.rs:定义闪电网络中交易的output。注意比特币上的OutPoint的index为u32,而闪电网络中交易OutPoint的index限定为u16。
/// A reference to a transaction output.
///
/// Differs from bitcoin::blockdata::transaction::OutPoint as the index is a u16 instead of u32
/// due to LN's restrictions on index values. Should reduce (possibly) unsafe conversions this way.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct OutPoint {
	/// The referenced transaction's txid.
	pub txid: Txid,
	/// The index of the referenced output in its transaction's vout.
	pub index: u16,
}
  • src/ln/chan_utils.rs:构建scripts和派生与通道相关的keys。

2.1 TLV (Type-Length-Value Format)中的BigSize编解码

		uint8(x)                if x < 0xfd
        0xfd + be16(uint16(x))  if x < 0x10000
        0xfe + be32(uint32(x))  if x < 0x100000000
        0xff + be64(x)          otherwise.

对应的代码实现为:

/// Lightning TLV uses a custom variable-length integer called BigSize. It is similar to Bitcoin's
/// variable-length integers except that it is serialized in big-endian instead of little-endian.
///
/// Like Bitcoin's variable-length integer, it exhibits ambiguity in that certain values can be
/// encoded in several different ways, which we must check for at deserialization-time. Thus, if
/// you're looking for an example of a variable-length integer to use for your own project, move
/// along, this is a rather poor design.
pub(crate) struct BigSize(pub u64);
impl Writeable for BigSize {
	#[inline]
	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
		match self.0 {
			0...0xFC => {
				(self.0 as u8).write(writer)
			},
			0xFD...0xFFFF => {
				0xFDu8.write(writer)?;
				(self.0 as u16).write(writer)
			},
			0x10000...0xFFFFFFFF => {
				0xFEu8.write(writer)?;
				(self.0 as u32).write(writer)
			},
			_ => {
				0xFFu8.write(writer)?;
				(self.0 as u64).write(writer)
			},
		}
	}
}
impl Readable for BigSize {
	#[inline]
	fn read<R: Read>(reader: &mut R) -> Result<BigSize, DecodeError> {
		let n: u8 = Readable::read(reader)?;
		match n {
			0xFF => {
				let x: u64 = Readable::read(reader)?;
				if x < 0x100000000 {
					Err(DecodeError::InvalidValue)
				} else {
					Ok(BigSize(x))
				}
			}
			0xFE => {
				let x: u32 = Readable::read(reader)?;
				if x < 0x10000 {
					Err(DecodeError::InvalidValue)
				} else {
					Ok(BigSize(x as u64))
				}
			}
			0xFD => {
				let x: u16 = Readable::read(reader)?;
				if x < 0xFD {
					Err(DecodeError::InvalidValue)
				} else {
					Ok(BigSize(x as u64))
				}
			}
			n => Ok(BigSize(n as u64))
		}
	}
}

3. channel通道

/// One counterparty's public keys which do not change over the life of a channel.
#[derive(Clone, PartialEq)]
pub struct ChannelPublicKeys {
	/// The public key which is used to sign all commitment transactions, as it appears in the
	/// on-chain channel lock-in 2-of-2 multisig output.
	pub funding_pubkey: PublicKey,
	/// The base point which is used (with derive_public_revocation_key) to derive per-commitment
	/// revocation keys. This is combined with the per-commitment-secret generated by the
	/// counterparty to create a secret which the counterparty can reveal to revoke previous
	/// states.
	pub revocation_basepoint: PublicKey,
	/// The public key on which the non-broadcaster (ie the countersignatory) receives an immediately
	/// spendable primary channel balance on the broadcaster's commitment transaction. This key is
	/// static across every commitment transaction.
	pub payment_point: PublicKey,
	/// The base point which is used (with derive_public_key) to derive a per-commitment payment
	/// public key which receives non-HTLC-encumbered funds which are only available for spending
	/// after some delay (or can be claimed via the revocation path).
	pub delayed_payment_basepoint: PublicKey,
	/// The base point which is used (with derive_public_key) to derive a per-commitment public key
	/// which is used to encumber HTLC-in-flight outputs.
	pub htlc_basepoint: PublicKey,
}
pub(super) struct Channel<Signer: Sign> {
	config: ChannelConfig,

	user_id: u64,

	channel_id: [u8; 32],
	channel_state: u32,
	secp_ctx: Secp256k1<secp256k1::All>,
	channel_value_satoshis: u64,

	latest_monitor_update_id: u64,

	holder_signer: Signer,
	shutdown_pubkey: PublicKey,
	destination_script: Script,

	// Our commitment numbers start at 2^48-1 and count down, whereas the ones used in transaction
	// generation start at 0 and count up...this simplifies some parts of implementation at the
	// cost of others, but should really just be changed.

	cur_holder_commitment_transaction_number: u64,
	cur_counterparty_commitment_transaction_number: u64,
	value_to_self_msat: u64, // Excluding all pending_htlcs, excluding fees
	pending_inbound_htlcs: Vec<InboundHTLCOutput>,
	pending_outbound_htlcs: Vec<OutboundHTLCOutput>,
	holding_cell_htlc_updates: Vec<HTLCUpdateAwaitingACK>,

	/// When resending CS/RAA messages on channel monitor restoration or on reconnect, we always
	/// need to ensure we resend them in the order we originally generated them. Note that because
	/// there can only ever be one in-flight CS and/or one in-flight RAA at any time, it is
	/// sufficient to simply set this to the opposite of any message we are generating as we
	/// generate it. ie when we generate a CS, we set this to RAAFirst as, if there is a pending
	/// in-flight RAA to resend, it will have been the first thing we generated, and thus we should
	/// send it first.
	resend_order: RAACommitmentOrder,

	monitor_pending_funding_locked: bool,
	monitor_pending_revoke_and_ack: bool,
	monitor_pending_commitment_signed: bool,
	monitor_pending_forwards: Vec<(PendingHTLCInfo, u64)>,
	monitor_pending_failures: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,

	// pending_update_fee is filled when sending and receiving update_fee
	// For outbound channel, feerate_per_kw is updated with the value from
	// pending_update_fee when revoke_and_ack is received
	//
	// For inbound channel, feerate_per_kw is updated when it receives
	// commitment_signed and revoke_and_ack is generated
	// The pending value is kept when another pair of update_fee and commitment_signed
	// is received during AwaitingRemoteRevoke and relieved when the expected
	// revoke_and_ack is received and new commitment_signed is generated to be
	// sent to the funder. Otherwise, the pending value is removed when receiving
	// commitment_signed.
	pending_update_fee: Option<u32>,
	// update_fee() during ChannelState::AwaitingRemoteRevoke is hold in
	// holdina_cell_update_fee then moved to pending_udpate_fee when revoke_and_ack
	// is received. holding_cell_update_fee is updated when there are additional
	// update_fee() during ChannelState::AwaitingRemoteRevoke.
	holding_cell_update_fee: Option<u32>,
	next_holder_htlc_id: u64,
	next_counterparty_htlc_id: u64,
	update_time_counter: u32,
	feerate_per_kw: u32,

	#[cfg(debug_assertions)]
	/// Max to_local and to_remote outputs in a locally-generated commitment transaction
	holder_max_commitment_tx_output: ::std::sync::Mutex<(u64, u64)>,
	#[cfg(debug_assertions)]
	/// Max to_local and to_remote outputs in a remote-generated commitment transaction
	counterparty_max_commitment_tx_output: ::std::sync::Mutex<(u64, u64)>,

	last_sent_closing_fee: Option<(u32, u64, Signature)>, // (feerate, fee, holder_sig)

	/// The hash of the block in which the funding transaction was included.
	funding_tx_confirmed_in: Option<BlockHash>,
	funding_tx_confirmation_height: u32,
	short_channel_id: Option<u64>,

	counterparty_dust_limit_satoshis: u64,
	#[cfg(test)]
	pub(super) holder_dust_limit_satoshis: u64,
	#[cfg(not(test))]
	holder_dust_limit_satoshis: u64,
	#[cfg(test)]
	pub(super) counterparty_max_htlc_value_in_flight_msat: u64,
	#[cfg(not(test))]
	counterparty_max_htlc_value_in_flight_msat: u64,
	//get_holder_max_htlc_value_in_flight_msat(): u64,
	/// minimum channel reserve for self to maintain - set by them.
	counterparty_selected_channel_reserve_satoshis: Option<u64>,
	// get_holder_selected_channel_reserve_satoshis(channel_value_sats: u64): u64
	counterparty_htlc_minimum_msat: u64,
	holder_htlc_minimum_msat: u64,
	#[cfg(test)]
	pub counterparty_max_accepted_htlcs: u16,
	#[cfg(not(test))]
	counterparty_max_accepted_htlcs: u16,
	//implied by OUR_MAX_HTLCS: max_accepted_htlcs: u16,
	minimum_depth: Option<u32>,

	counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,

	pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
	funding_transaction: Option<Transaction>,

	counterparty_cur_commitment_point: Option<PublicKey>,
	counterparty_prev_commitment_point: Option<PublicKey>,
	counterparty_node_id: PublicKey,

	counterparty_shutdown_scriptpubkey: Option<Script>,

	commitment_secrets: CounterpartyCommitmentSecrets,

	channel_update_status: ChannelUpdateStatus,

	/// Our counterparty's channel_announcement signatures provided in announcement_signatures.
	/// This can be used to rebroadcast the channel_announcement message later.
	announcement_sigs: Option<(Signature, Signature)>,

	// We save these values so we can make sure `next_local_commit_tx_fee_msat` and
	// `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will
	// be, by comparing the cached values to the fee of the tranaction generated by
	// `build_commitment_transaction`.
	#[cfg(any(test, feature = "fuzztarget"))]
	next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
	#[cfg(any(test, feature = "fuzztarget"))]
	next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,

	/// lnd has a long-standing bug where, upon reconnection, if the channel is not yet confirmed
	/// they will not send a channel_reestablish until the channel locks in. Then, they will send a
	/// funding_locked *before* sending the channel_reestablish (which is clearly a violation of
	/// the BOLT specs). We copy c-lightning's workaround here and simply store the funding_locked
	/// message until we receive a channel_reestablish.
	///
	/// See-also <https://github.com/lightningnetwork/lnd/issues/4006>
	pub workaround_lnd_bug_4006: Option<msgs::FundingLocked>,
}

3.1 通道状态

支付通道状态有:

/// There are a few "states" and then a number of flags which can be applied:
/// We first move through init with OurInitSent -> TheirInitSent -> FundingCreated -> FundingSent.
/// TheirFundingLocked and OurFundingLocked then get set on FundingSent, and when both are set we
/// move on to ChannelFunded.
/// Note that PeerDisconnected can be set on both ChannelFunded and FundingSent.
/// ChannelFunded can then get all remaining flags set on it, until we finish shutdown, then we
/// move on to ShutdownComplete, at which point most calls into this channel are disallowed.
enum ChannelState {
	/// Implies we have (or are prepared to) send our open_channel/accept_channel message
	OurInitSent = 1 << 0,
	/// Implies we have received their open_channel/accept_channel message
	TheirInitSent = 1 << 1,
	/// We have sent funding_created and are awaiting a funding_signed to advance to FundingSent.
	/// Note that this is nonsense for an inbound channel as we immediately generate funding_signed
	/// upon receipt of funding_created, so simply skip this state.
	FundingCreated = 4,
	/// Set when we have received/sent funding_created and funding_signed and are thus now waiting
	/// on the funding transaction to confirm. The FundingLocked flags are set to indicate when we
	/// and our counterparty consider the funding transaction confirmed.
	FundingSent = 8,
	/// Flag which can be set on FundingSent to indicate they sent us a funding_locked message.
	/// Once both TheirFundingLocked and OurFundingLocked are set, state moves on to ChannelFunded.
	TheirFundingLocked = 1 << 4,
	/// Flag which can be set on FundingSent to indicate we sent them a funding_locked message.
	/// Once both TheirFundingLocked and OurFundingLocked are set, state moves on to ChannelFunded.
	OurFundingLocked = 1 << 5,
	ChannelFunded = 64,
	/// Flag which is set on ChannelFunded and FundingSent indicating remote side is considered
	/// "disconnected" and no updates are allowed until after we've done a channel_reestablish
	/// dance.
	PeerDisconnected = 1 << 7,
	/// Flag which is set on ChannelFunded, FundingCreated, and FundingSent indicating the user has
	/// told us they failed to update our ChannelMonitor somewhere and we should pause sending any
	/// outbound messages until they've managed to do so.
	MonitorUpdateFailed = 1 << 8,
	/// Flag which implies that we have sent a commitment_signed but are awaiting the responding
	/// revoke_and_ack message. During this time period, we can't generate new commitment_signed
	/// messages as then we will be unable to determine which HTLCs they included in their
	/// revoke_and_ack implicit ACK, so instead we have to hold them away temporarily to be sent
	/// later.
	/// Flag is set on ChannelFunded.
	AwaitingRemoteRevoke = 1 << 9,
	/// Flag which is set on ChannelFunded or FundingSent after receiving a shutdown message from
	/// the remote end. If set, they may not add any new HTLCs to the channel, and we are expected
	/// to respond with our own shutdown message when possible.
	RemoteShutdownSent = 1 << 10,
	/// Flag which is set on ChannelFunded or FundingSent after sending a shutdown message. At this
	/// point, we may not add any new HTLCs to the channel.
	/// TODO: Investigate some kind of timeout mechanism by which point the remote end must provide
	/// us their shutdown.
	LocalShutdownSent = 1 << 11,
	/// We've successfully negotiated a closing_signed dance. At this point ChannelManager is about
	/// to drop us, but we store this anyway.
	ShutdownComplete = 4096,
}

/// An open_channel message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct OpenChannel {
	/// The genesis hash of the blockchain where the channel is to be opened
	pub chain_hash: BlockHash,
	/// A temporary channel ID, until the funding outpoint is announced
	pub temporary_channel_id: [u8; 32],
	/// The channel value
	pub funding_satoshis: u64,
	/// The amount to push to the counterparty as part of the open, in milli-satoshi
	pub push_msat: u64,
	/// The threshold below which outputs on transactions broadcast by sender will be omitted
	pub dust_limit_satoshis: u64,
	/// The maximum inbound HTLC value in flight towards sender, in milli-satoshi
	pub max_htlc_value_in_flight_msat: u64,
	/// The minimum value unencumbered by HTLCs for the counterparty to keep in the channel
	pub channel_reserve_satoshis: u64,
	/// The minimum HTLC size incoming to sender, in milli-satoshi
	pub htlc_minimum_msat: u64,
	/// The feerate per 1000-weight of sender generated transactions, until updated by update_fee
	pub feerate_per_kw: u32,
	/// The number of blocks which the counterparty will have to wait to claim on-chain funds if they broadcast a commitment transaction
	pub to_self_delay: u16,
	/// The maximum number of inbound HTLCs towards sender
	pub max_accepted_htlcs: u16,
	/// The sender's key controlling the funding transaction
	pub funding_pubkey: PublicKey,
	/// Used to derive a revocation key for transactions broadcast by counterparty
	pub revocation_basepoint: PublicKey,
	/// A payment key to sender for transactions broadcast by counterparty
	pub payment_point: PublicKey,
	/// Used to derive a payment key to sender for transactions broadcast by sender
	pub delayed_payment_basepoint: PublicKey,
	/// Used to derive an HTLC payment key to sender
	pub htlc_basepoint: PublicKey,
	/// The first to-be-broadcast-by-sender transaction's per commitment point
	pub first_per_commitment_point: PublicKey,
	/// Channel flags
	pub channel_flags: u8,
	/// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
	pub shutdown_scriptpubkey: OptionalField<Script>,
}

3.2 创建通道

    +-------+                              +-------+
    |       |--(1)---  open_channel  ----->|       |
    |       |<-(2)--  accept_channel  -----|       |
    |       |                              |       |
    |   A   |--(3)--  funding_created  --->|   B   |
    |       |<-(4)--  funding_signed  -----|       |
    |       |                              |       |
    |       |--(5)--- funding_locked  ---->|       |
    |       |<-(6)--- funding_locked  -----|       |
    +-------+                              +-------+

    - where node A is 'funder' and node B is 'fundee'

make_channel创建通道的流程为:

  • funder端(source)create_channel:new_outbound -> get_open_channel
  • fundee端(dest)handle_open_channel:internal_open_channel
  • funder端(source)handle_accept_channel:internal_accept_channel
  • funder端(source)funding_transaction_generated:get_funding_redeemscript().to_v0_p2wsh()->funding_transaction_generated_intern
  • fundee端(dest)handle_funding_created:internal_funding_created
  • funder端(source)handle_funding_signed:internal_funding_signed

各函数详细功能为:

  • new_outbound(source):构建Channel,其中返回channel_state为OurInitSent。
  • get_open_channel(source):指定LN所锚定的区块链主网genesis_hash(如比特币主网的genesis_hash)。其中会派生第一次commitment_point的public key以及其他channel public keys。返回的为OpenChannel消息,然后发送SenOpenChannel事件。
  • internal_open_channel(dest):dest端会验证source端发来的OpenChannel消息所锚定的链与自身的链是一致的。维护source端的channel public keys信息,并构建dest端的Channel信息 及 AcceptChannel消息,然后发送SendAcceptChannel事件。
  • internal_accept_channel(source):要求所收到的AcceptChannel信息在本地之前已成功构建过。发送FundingGenerationReady消息。
  • get_funding_redeemscript().to_v0_p2wsh()(source):基于source的funding_pubkey和dest的funding_pubkey来构建redeemscript for the funding transaction output。
    Funding Transaction Output 为P2WSH to :(其中pubkey1为词典序更小的funding_pubkey,pubkey2为词典序更大的。)
2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG
  • funding_transaction_generated_intern(source):source 验证所创建的Funding Transaction Output,get_outbound_funding_created中调用build_commitment_transaction来构建commitment transaction并签名,然后发送SendFundingCreated事件。
  • internal_funding_created(dest):在funding_created_signature中,对收到的source已签名的commitment transaction进行签名,然后发送SendFundingSigned事件。同时,ChainMonitor会监听锚定链上状态。
  • internal_funding_signed:ChainMonitor会监听锚定链上状态的同时,通过tx_broadcaster将funding_tx广播到锚定链上。
  • 当source和dest都监听到funding_tx已固化在锚定链后,会发出SendFundingLocked事件,当双方都收到对对方发出的事件后,则通道创建完成。

4. invoice

详细可参见:

闪电网络中采用invoice来发起支付请求,而不是使用Bitcoin-style addresses

Invoice通常以alphanumerical strings 或 QR codes形式来展示,可借助如下decoding工具来解析:

每个Lightning invocie中需包含如下valid信息:

  • destination:The public key of the person receiving the LN payment。
  • payment_hash:The hash of the preimage that is used to lock the payment. You can only redeem a locked payment with the corresponding preimage to the payment hash. This enables routing on the Lightning Network without trusted third parties.
  • num_statoshis:The amount of satoshis to be paid.
  • expiry:Time when your node marks the invoice as invalid. 默认为1小时或3600秒。
  • timestamp:创建invoice的时间,以自1970开始的秒计数。可借助https://www.epochconverter.com/ 将其转换为regular human time。
  • cltv_expiry:Delta to use for time-lock of CLTV extended to the final hop。

invoice举例为:
在这里插入图片描述

lnbc4u1psk2kq2pp5mact67ftvyshejg6fzhaxze94j0ya4rmjzagjszdtgwem2n8f3jsdqc8gszqumsd35hggrrdanxvet9sp5ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3qxq9p5hsqcqp79qtzqqqqqqysgqxqmx5hetqqm2u2sljjl8agq00hw8mkyze7n09pj6ssvx3d6k8x8n8gydysacmc6fm9ckl2s5k0zngvgdqz2wm94j2uz3qacpjuefzygql2t07v

解析结果为:

Chainbitcoin
Amount (Millisatoshis)400000
Payee Pub Key03ee58475055820fbfa52e356a8920f62f8316129c39369dbdde3e5d0198a9e315
Invoicelnbc4u1psk2kq2pp5mact67ftvyshejg6fzhaxze94j0ya4rmjzagjszdtgwem2n8f3jsdqc8gszqumsd35hggrrdanxvet9sp5ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3qxq9p5hsqcqp79qtzqqqqqqysgqxqmx5hetqqm2u2sljjl8agq00hw8mkyze7n09pj6ssvx3d6k8x8n8gydysacmc6fm9ckl2s5k0zngvgdqz2wm94j2uz3qacpjuefzygql2t07v
Prefixlnbc4u
Recovery Flag0
Amount (Satoshis)400
Transaction Signature30366a5f2b0036ae2a1f94be7ea00f7ddc7dd882cfa6f2865a841868b756398f33a08d243b8de349d9716faa14b3c534310d0094ed96b2570510770197329111
Payment Hashdf70bd792b61217cc91a48afd30b25ac9e4ed47b90ba89404d5a1d9daa674c65
Description: split coffee
Unknown Tag
Tag Code16
Tag Wordsunknown1mvdjms8aa6xgs3ne8s8rwl36952eupl2qt93z72hj6mw5kknsq8s4a8y0l
Unknown Tag
Tag Code16
Tag Wordsunknown1ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3q5m7c7t
Expire Time1728000
Minimum Final CLTV Expiry30
Unknown Tag
Tag Code5
Tag Wordsunknown1zqqqqqqysgqemre3m
Time Expire Date1635759626
Time Expire Date String2021-11-01T09:40:26.000Z
Timestamp1634031626
Timestamp String2021-10-12T09:40:26.000Z
Words Temptemp1psk2kq2pp5mact67ftvyshejg6fzhaxze94j0ya4rmjzagjszdtgwem2n8f3jsdqc8gszqumsd35hggrrdanxvet9sp5ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3qxq9p5hsqcqp79qtzqqqqqqysgqxqmx5hetqqm2u2sljjl8agq00hw8mkyze7n09pj6ssvx3d6k8x8n8gydysacmc6fm9ckl2s5k0zngvgdqz2wm94j2uz3qacpjuefzygqp7625l
在这里插入图片描述
invoice的字符串长度要比比特币地址长,且仅能使用一次,因为不能在LN中对多笔交易使用相同的hash preimage。
/// Represents an syntactically correct Invoice for a payment on the lightning network,
/// but without the signature information.
/// De- and encoding should not lead to information loss but may lead to different hashes.
///
/// For methods without docs see the corresponding methods in `Invoice`.
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RawInvoice {
	/// human readable part
	pub hrp: RawHrp,

	/// data part
	pub data: RawDataPart,
}

/// Data of the `RawInvoice` that is encoded in the human readable part
///
/// (C-not exported) As we don't yet support Option<Enum>
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RawHrp {
	/// The currency deferred from the 3rd and 4th character of the bech32 transaction
	pub currency: Currency, // 当前可配置支持 比特币主网、比特币testnet、比特币regtest、比特币simnet以及比特币signet

	/// The amount that, multiplied by the SI prefix, has to be payed
	pub raw_amount: Option<u64>, // raw_amount * si_prefix 即为支付金额

	/// SI prefix that gets multiplied with the `raw_amount`
	pub si_prefix: Option<SiPrefix>, //支持Milli (10^-3), Micro (10^-6), Nano (10^-9), Pico (10^-12)
}

/// Data of the `RawInvoice` that is encoded in the data part
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RawDataPart {
	/// generation time of the invoice
	pub timestamp: PositiveTimestamp, // invoice的创建时间

	/// tagged fields of the payment request
	pub tagged_fields: Vec<RawTaggedField>,
}

/// Tagged field which may have an unknown tag
///
/// (C-not exported) as we don't currently support TaggedField
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum RawTaggedField {
	/// Parsed tagged field with known tag
	KnownSemantics(TaggedField),
	/// tagged field which was not parsed due to an unknown tag or undefined field semantics
	UnknownSemantics(Vec<u5>),
}

/// Tagged field with known tag
///
/// For descriptions of the enum values please refer to the enclosed type's docs.
///
/// (C-not exported) As we don't yet support enum variants with the same name the struct contained
/// in the variant.
#[allow(missing_docs)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum TaggedField {
	PaymentHash(Sha256),
	Description(Description),
	PayeePubKey(PayeePubKey),
	DescriptionHash(Sha256),
	ExpiryTime(ExpiryTime),
	MinFinalCltvExpiry(MinFinalCltvExpiry),
	Fallback(Fallback),
	PrivateRoute(PrivateRoute),
	PaymentSecret(PaymentSecret),
	Features(InvoiceFeatures),
}

5. HTLC

参考资料:

HTLC (Hashed TimeLock Contract),允许交易 to be sent between parties who do not have a direct channel on the Lightning Network。
主要包括:

  • Hashlock:只有正确的收款方知道该hash值的preimage,可保证正确的收款方可收到款项。
  • Timelock:time lock应根据路由时间递减设置,一方面可保证中间的路由方可在其上游time lock到期前收到垫付的资金;另一方面,当无人可在设置的timelock期限内提供正确的preimage,则可将其支付出去的金额收回。
/// Simple structure sent back by `chain::Watch` when an HTLC from a forward channel is detected on
/// chain. Used to update the corresponding HTLC in the backward channel. Failing to pass the
/// preimage claim backward will lead to loss of funds.
#[derive(Clone, PartialEq)]
pub struct HTLCUpdate {
	pub(crate) payment_hash: PaymentHash,
	pub(crate) payment_preimage: Option<PaymentPreimage>,
	pub(crate) source: HTLCSource
}

/// Information about an HTLC as it appears in a commitment transaction
pub struct HTLCOutputInCommitment {
	/// Whether the HTLC was "offered" (ie outbound in relation to this commitment transaction).
	/// Note that this is not the same as whether it is ountbound *from us*. To determine that you
	/// need to compare this value to whether the commitment transaction in question is that of
	/// the counterparty or our own.
	pub offered: bool,
	/// The value, in msat, of the HTLC. The value as it appears in the commitment transaction is
	/// this divided by 1000.
	pub amount_msat: u64,
	/// The CLTV lock-time at which this HTLC expires.
	pub cltv_expiry: u32,
	/// The hash of the preimage which unlocks this HTLC.
	pub payment_hash: PaymentHash,
	/// The position within the commitment transactions' outputs. This may be None if the value is
	/// below the dust limit (in which case no output appears in the commitment transaction and the
	/// value is spent to additional transaction fees).
	pub transaction_output_index: Option<u32>,
}

/// An enum indicating whether the local or remote side offered a given HTLC.
enum HTLCInitiator {
	LocalOffered,
	RemoteOffered,
}

/// Used when calculating whether we or the remote can afford an additional HTLC.
struct HTLCCandidate {
	amount_msat: u64,
	origin: HTLCInitiator,
}

/// Tracks the inbound corresponding to an outbound HTLC
#[derive(Clone, PartialEq)]
pub(crate) struct HTLCPreviousHopData {
	short_channel_id: u64,
	htlc_id: u64,
	incoming_packet_shared_secret: [u8; 32],

	// This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards
	// channel with a preimage provided by the forward channel.
	outpoint: OutPoint, // 唯一标识unspent transaction output的transaction hash和output index。需作为输入来组成一笔新的交易。
}

struct ClaimableHTLC {
	prev_hop: HTLCPreviousHopData,
	value: u64,
	/// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
	/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
	/// are part of the same payment.
	payment_data: msgs::FinalOnionHopData,
	cltv_expiry: u32,
}

/// An update_add_htlc message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateAddHTLC {
	/// The channel ID
	pub channel_id: [u8; 32],
	/// The HTLC ID
	pub htlc_id: u64,
	/// The HTLC value in milli-satoshi
	pub amount_msat: u64,
	/// The payment hash, the pre-image of which controls HTLC redemption
	pub payment_hash: PaymentHash,
	/// The expiry height of the HTLC
	pub cltv_expiry: u32,
	pub(crate) onion_routing_packet: OnionPacket,
}

/// An update_fulfill_htlc message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateFulfillHTLC {
	/// The channel ID
	pub channel_id: [u8; 32],
	/// The HTLC ID
	pub htlc_id: u64,
	/// The pre-image of the payment hash, allowing HTLC redemption
	pub payment_preimage: PaymentPreimage,
}

/// An update_fail_htlc message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateFailHTLC {
	/// The channel ID
	pub channel_id: [u8; 32],
	/// The HTLC ID
	pub htlc_id: u64,
	pub(crate) reason: OnionErrorPacket,
}

/// An update_fail_malformed_htlc message to be sent or received from a peer
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateFailMalformedHTLC {
	/// The channel ID
	pub channel_id: [u8; 32],
	/// The HTLC ID
	pub htlc_id: u64,
	pub(crate) sha256_of_onion: [u8; 32],
	/// The failure code
	pub failure_code: u16,
}

struct ClaimableHTLC {
	prev_hop: HTLCPreviousHopData,
	value: u64,
	/// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
	/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
	/// are part of the same payment.
	payment_data: msgs::FinalOnionHopData,
	cltv_expiry: u32,
}

6. commitment transaction

根据BIP-0112 CHECKSEQUENCEVERIFY中,以Bitcoin script脚本来表示:

  • Alice端的commitment transaction表示为:【其中CHECKSEQUENCEVERIFY为相对锁定时间,CHECKLOCKTIMEVERIFY为绝对锁定时间。】
//Alice向链上提交其commitment transaction,有:
//【需等待才可生效】若Alice可提供secret R,且过24小时后,对Alice的签名验签通过后即可。。。
//【可立即生效】若Bob可提供secret R,以及revocation secret,对Bob的签名验签通过后即可。。。
 	HASH160 DUP <R-HASH> EQUAL
    IF
        "24h" CHECKSEQUENCEVERIFY
        2DROP
        <Alice's pubkey>
    ELSE
        <Commit-Revocation-Hash> EQUAL
        NOTIF
            "2015/10/20 10:33" CHECKLOCKTIMEVERIFY DROP
        ENDIF
        <Bob's pubkey>
    ENDIF
    CHECKSIG
  • Bob端的commitment transaction表示为:
//Bob向链上提交其commitment transaction,有:
//【可立即生效】若Alice可提供secret R以及revocation secret,,对Alice的签名验签通过后即可。。。
//【需等待才可生效】若Bob可提供secret R,需等待过了2015/10/20 10:33 且 过24小时后,对Bob的签名验签通过后即可。。。
   HASH160 DUP <R-HASH> EQUAL
   SWAP <Commit-Revocation-Hash> EQUAL ADD
   IF
       <Alice's pubkey>
   ELSE
       "2015/10/20 10:33" CHECKLOCKTIMEVERIFY
       "24h" CHECKSEQUENCEVERIFY
       2DROP
       <Bob's pubkey>
   ENDIF
   CHECKSIG
/// This class tracks the per-transaction information needed to build a commitment transaction and to
/// actually build it and sign.  It is used for holder transactions that we sign only when needed
/// and for transactions we sign for the counterparty.
///
/// This class can be used inside a signer implementation to generate a signature given the relevant
/// secret key.
#[derive(Clone)]
pub struct CommitmentTransaction {
	commitment_number: u64,
	to_broadcaster_value_sat: u64,
	to_countersignatory_value_sat: u64,
	feerate_per_kw: u32,
	htlcs: Vec<HTLCOutputInCommitment>,
	// A cache of the parties' pubkeys required to construct the transaction, see doc for trust()
	keys: TxCreationKeys,
	// For access to the pre-built transaction, see doc for trust()
	built: BuiltCommitmentTransaction,
}

/// The set of public keys which are used in the creation of one commitment transaction.
/// These are derived from the channel base keys and per-commitment data.
///
/// A broadcaster key is provided from potential broadcaster of the computed transaction.
/// A countersignatory key is coming from a protocol participant unable to broadcast the
/// transaction.
///
/// These keys are assumed to be good, either because the code derived them from
/// channel basepoints via the new function, or they were obtained via
/// CommitmentTransaction.trust().keys() because we trusted the source of the
/// pre-calculated keys.
#[derive(PartialEq, Clone)]
pub struct TxCreationKeys {
	/// The broadcaster's per-commitment public key which was used to derive the other keys.
	pub per_commitment_point: PublicKey,
	/// The revocation key which is used to allow the broadcaster of the commitment
	/// transaction to provide their counterparty the ability to punish them if they broadcast
	/// an old state.
	pub revocation_key: PublicKey,
	/// Broadcaster's HTLC Key
	pub broadcaster_htlc_key: PublicKey,
	/// Countersignatory's HTLC Key
	pub countersignatory_htlc_key: PublicKey,
	/// Broadcaster's Payment Key (which isn't allowed to be spent from for some delay)
	pub broadcaster_delayed_payment_key: PublicKey,
}

/// A pre-built Bitcoin commitment transaction and its txid.
#[derive(Clone)]
pub struct BuiltCommitmentTransaction {
	/// The commitment transaction
	pub transaction: Transaction,
	/// The txid for the commitment transaction.
	///
	/// This is provided as a performance optimization, instead of calling transaction.txid()
	/// multiple times.
	pub txid: Txid,
}

/// A Bitcoin transaction, which describes an authenticated movement of coins.
///
/// If any inputs have nonempty witnesses, the entire transaction is serialized
/// in the post-BIP141 Segwit format which includes a list of witnesses. If all
/// inputs have empty witnesses, the transaction is serialized in the pre-BIP141
/// format.
///
/// There is one major exception to this: to avoid deserialization ambiguity,
/// if the transaction has no inputs, it is serialized in the BIP141 style. Be
/// aware that this differs from the transaction format in PSBT, which _never_
/// uses BIP141. (Ordinarily there is no conflict, since in PSBT transactions
/// are always unsigned and therefore their inputs have empty witnesses.)
///
/// The specific ambiguity is that Segwit uses the flag bytes `0001` where an old
/// serializer would read the number of transaction inputs. The old serializer
/// would interpret this as "no inputs, one output", which means the transaction
/// is invalid, and simply reject it. Segwit further specifies that this encoding
/// should *only* be used when some input has a nonempty witness; that is,
/// witness-less transactions should be encoded in the traditional format.
///
/// However, in protocols where transactions may legitimately have 0 inputs, e.g.
/// when parties are cooperatively funding a transaction, the "00 means Segwit"
/// heuristic does not work. Since Segwit requires such a transaction be encoded
/// in the original transaction format (since it has no inputs and therefore
/// no input witnesses), a traditionally encoded transaction may have the `0001`
/// Segwit flag in it, which confuses most Segwit parsers including the one in
/// Bitcoin Core.
///
/// We therefore deviate from the spec by always using the Segwit witness encoding
/// for 0-input transactions, which results in unambiguously parseable transactions.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Transaction {
    /// The protocol version, is currently expected to be 1 or 2 (BIP 68).
    pub version: i32,
    /// Block number before which this transaction is valid, or 0 for
    /// valid immediately.
    pub lock_time: u32,
    /// List of inputs
    pub input: Vec<TxIn>,
    /// List of outputs
    pub output: Vec<TxOut>,
}
/// A transaction input, which defines old coins to be consumed
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TxIn {
    /// The reference to the previous output that is being used an an input
    pub previous_output: OutPoint, //唯一标识unspent transaction output的transaction hash和output index。需作为输入来组成一笔新的交易。
    /// The script which pushes values on the stack which will cause
    /// the referenced output's script to accept
    pub script_sig: Script,
    /// The sequence number, which suggests to miners which of two
    /// conflicting transactions should be preferred, or 0xFFFFFFFF
    /// to ignore this feature. This is generally never used since
    /// the miner behaviour cannot be enforced.
    pub sequence: u32,
    /// Witness data: an array of byte-arrays.
    /// Note that this field is *not* (de)serialized with the rest of the TxIn in
    /// Encodable/Decodable, as it is (de)serialized at the end of the full
    /// Transaction. It *is* (de)serialized with the rest of the TxIn in other
    /// (de)serialization routines.
    pub witness: Vec<Vec<u8>>
}
/// A transaction output, which defines new coins to be created from old ones.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TxOut {
    /// The value of the output, in satoshis
    pub value: u64,
    /// The script which must satisfy for the output to be spent
    pub script_pubkey: Script
}
/// A Bitcoin script
pub struct Script(Box<[u8]>);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值