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
解析结果为:
Chain | bitcoin |
---|---|
Amount (Millisatoshis) | 400000 |
Payee Pub Key | 03ee58475055820fbfa52e356a8920f62f8316129c39369dbdde3e5d0198a9e315 |
Invoice | lnbc4u1psk2kq2pp5mact67ftvyshejg6fzhaxze94j0ya4rmjzagjszdtgwem2n8f3jsdqc8gszqumsd35hggrrdanxvet9sp5ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3qxq9p5hsqcqp79qtzqqqqqqysgqxqmx5hetqqm2u2sljjl8agq00hw8mkyze7n09pj6ssvx3d6k8x8n8gydysacmc6fm9ckl2s5k0zngvgdqz2wm94j2uz3qacpjuefzygql2t07v |
Prefix | lnbc4u |
Recovery Flag | 0 |
Amount (Satoshis) | 400 |
Transaction Signature | 30366a5f2b0036ae2a1f94be7ea00f7ddc7dd882cfa6f2865a841868b756398f33a08d243b8de349d9716faa14b3c534310d0094ed96b2570510770197329111 |
Payment Hash | df70bd792b61217cc91a48afd30b25ac9e4ed47b90ba89404d5a1d9daa674c65 |
Description | : split coffee |
Unknown Tag | |
Tag Code | 16 |
Tag Words | unknown1mvdjms8aa6xgs3ne8s8rwl36952eupl2qt93z72hj6mw5kknsq8s4a8y0l |
Unknown Tag | |
Tag Code | 16 |
Tag Words | unknown1ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3q5m7c7t |
Expire Time | 1728000 |
Minimum Final CLTV Expiry | 30 |
Unknown Tag | |
Tag Code | 5 |
Tag Words | unknown1zqqqqqqysgqemre3m |
Time Expire Date | 1635759626 |
Time Expire Date String | 2021-11-01T09:40:26.000Z |
Timestamp | 1634031626 |
Timestamp String | 2021-10-12T09:40:26.000Z |
Words Temp | temp1psk2kq2pp5mact67ftvyshejg6fzhaxze94j0ya4rmjzagjszdtgwem2n8f3jsdqc8gszqumsd35hggrrdanxvet9sp5ckwfp7z7r4hhefeakuftqrauq9ralegahq0qsdljflyjdh62jz3qxq9p5hsqcqp79qtzqqqqqqysgqxqmx5hetqqm2u2sljjl8agq00hw8mkyze7n09pj6ssvx3d6k8x8n8gydysacmc6fm9ckl2s5k0zngvgdqz2wm94j2uz3qacpjuefzygqp7625l |
![]() | |
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]>);