Add message structs required for dual-funded channels

This is the first of a set of PRs to enable the experimental dual-funded
channels feature using interactive transaction construction. This allows
both the channel initiator and channel acceptor to contribute funds
towards the channel.
This commit is contained in:
Duncan Dean 2022-10-20 22:56:37 +02:00 committed by Duncan Dean
parent 56b0c96838
commit adc1b55a6f
No known key found for this signature in database
6 changed files with 851 additions and 9 deletions

View file

@ -33,8 +33,8 @@ GEN_TEST lightning::ln::msgs::UpdateFailHTLC test_msg_simple ""
GEN_TEST lightning::ln::msgs::UpdateFailMalformedHTLC test_msg_simple ""
GEN_TEST lightning::ln::msgs::UpdateFee test_msg_simple ""
GEN_TEST lightning::ln::msgs::UpdateFulfillHTLC test_msg_simple ""
GEN_TEST lightning::ln::msgs::ChannelReestablish test_msg_simple ""
GEN_TEST lightning::ln::msgs::ChannelReestablish test_msg ""
GEN_TEST lightning::ln::msgs::DecodedOnionErrorPacket test_msg ""
GEN_TEST lightning::ln::msgs::ChannelAnnouncement test_msg_exact ""

View file

@ -15,11 +15,11 @@ use crate::utils::test_logger;
#[inline]
pub fn msg_channel_reestablish_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
test_msg!(lightning::ln::msgs::ChannelReestablish, data);
test_msg_simple!(lightning::ln::msgs::ChannelReestablish, data);
}
#[no_mangle]
pub extern "C" fn msg_channel_reestablish_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
test_msg!(lightning::ln::msgs::ChannelReestablish, data);
test_msg_simple!(lightning::ln::msgs::ChannelReestablish, data);
}

View file

@ -5695,6 +5695,9 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
next_remote_commitment_number: INITIAL_COMMITMENT_NUMBER - self.cur_counterparty_commitment_transaction_number - 1,
your_last_per_commitment_secret: remote_last_secret,
my_current_per_commitment_point: dummy_pubkey,
// TODO(dual_funding): If we've sent `commtiment_signed` for an interactive transaction construction but have not received `tx_signatures`
// we MUST set `next_funding_txid` to the txid of that interactive transaction, else we MUST NOT set it.
next_funding_txid: None,
}
}

View file

@ -26,7 +26,7 @@
use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::secp256k1;
use bitcoin::{secp256k1, Witness, Transaction};
use bitcoin::blockdata::script::Script;
use bitcoin::hash_types::{Txid, BlockHash};
@ -158,6 +158,8 @@ pub struct Pong {
/// An [`open_channel`] message to be sent to or received from a peer.
///
/// Used in V1 channel establishment
///
/// [`open_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannel {
@ -208,8 +210,69 @@ pub struct OpenChannel {
pub channel_type: Option<ChannelTypeFeatures>,
}
/// An open_channel2 message to be sent by or received from the channel initiator.
///
/// Used in V2 channel establishment
///
// TODO(dual_funding): Add spec link for `open_channel2`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannelV2 {
/// The genesis hash of the blockchain where the channel is to be opened
pub chain_hash: BlockHash,
/// A temporary channel ID derived using a zeroed out value for the channel acceptor's revocation basepoint
pub temporary_channel_id: [u8; 32],
/// The feerate for the funding transaction set by the channel initiator
pub funding_feerate_sat_per_1000_weight: u32,
/// The feerate for the commitment transaction set by the channel initiator
pub commitment_feerate_sat_per_1000_weight: u32,
/// Part of the channel value contributed by the channel initiator
pub funding_satoshis: u64,
/// The threshold below which outputs on transactions broadcast by the channel initiator will be
/// omitted
pub dust_limit_satoshis: u64,
/// The maximum inbound HTLC value in flight towards channel initiator, in milli-satoshi
pub max_htlc_value_in_flight_msat: u64,
/// The minimum HTLC size incoming to channel initiator, in milli-satoshi
pub htlc_minimum_msat: u64,
/// 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 channel initiator
pub max_accepted_htlcs: u16,
/// The locktime for the funding transaction
pub locktime: u32,
/// The channel initiator'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 channel initiator for transactions broadcast by counterparty
pub payment_basepoint: PublicKey,
/// Used to derive a payment key to channel initiator for transactions broadcast by channel
/// initiator
pub delayed_payment_basepoint: PublicKey,
/// Used to derive an HTLC payment key to channel initiator
pub htlc_basepoint: PublicKey,
/// The first to-be-broadcast-by-channel-initiator transaction's per commitment point
pub first_per_commitment_point: PublicKey,
/// The second to-be-broadcast-by-channel-initiator transaction's per commitment point
pub second_per_commitment_point: PublicKey,
/// Channel flags
pub channel_flags: u8,
/// Optionally, a request to pre-set the to-channel-initiator output's scriptPubkey for when we
/// collaboratively close
pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent. If none is set, we derive the channel
/// type from the intersection of our feature bits with our counterparty's feature bits from
/// the Init message.
pub channel_type: Option<ChannelTypeFeatures>,
/// Optionally, a requirement that only confirmed inputs can be added
pub require_confirmed_inputs: Option<()>,
}
/// An [`accept_channel`] message to be sent to or received from a peer.
///
/// Used in V1 channel establishment
///
/// [`accept_channel`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-accept_channel-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AcceptChannel {
@ -254,8 +317,63 @@ pub struct AcceptChannel {
pub next_local_nonce: Option<musig2::types::PublicNonce>,
}
/// An accept_channel2 message to be sent by or received from the channel accepter.
///
/// Used in V2 channel establishment
///
// TODO(dual_funding): Add spec link for `accept_channel2`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AcceptChannelV2 {
/// The same `temporary_channel_id` received from the initiator's `open_channel2` message.
pub temporary_channel_id: [u8; 32],
/// Part of the channel value contributed by the channel acceptor
pub funding_satoshis: u64,
/// The threshold below which outputs on transactions broadcast by the channel acceptor will be
/// omitted
pub dust_limit_satoshis: u64,
/// The maximum inbound HTLC value in flight towards channel acceptor, in milli-satoshi
pub max_htlc_value_in_flight_msat: u64,
/// The minimum HTLC size incoming to channel acceptor, in milli-satoshi
pub htlc_minimum_msat: u64,
/// Minimum depth of the funding transaction before the channel is considered open
pub minimum_depth: 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 channel acceptor
pub max_accepted_htlcs: u16,
/// The channel acceptor'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 channel acceptor for transactions broadcast by counterparty
pub payment_basepoint: PublicKey,
/// Used to derive a payment key to channel acceptor for transactions broadcast by channel
/// acceptor
pub delayed_payment_basepoint: PublicKey,
/// Used to derive an HTLC payment key to channel acceptor for transactions broadcast by counterparty
pub htlc_basepoint: PublicKey,
/// The first to-be-broadcast-by-channel-acceptor transaction's per commitment point
pub first_per_commitment_point: PublicKey,
/// The second to-be-broadcast-by-channel-acceptor transaction's per commitment point
pub second_per_commitment_point: PublicKey,
/// Optionally, a request to pre-set the to-channel-acceptor output's scriptPubkey for when we
/// collaboratively close
pub shutdown_scriptpubkey: Option<Script>,
/// The channel type that this channel will represent. If none is set, we derive the channel
/// type from the intersection of our feature bits with our counterparty's feature bits from
/// the Init message.
///
/// This is required to match the equivalent field in [`OpenChannelV2::channel_type`].
pub channel_type: Option<ChannelTypeFeatures>,
/// Optionally, a requirement that only confirmed inputs can be added
pub require_confirmed_inputs: Option<()>,
}
/// A [`funding_created`] message to be sent to or received from a peer.
///
/// Used in V1 channel establishment
///
/// [`funding_created`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_created-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingCreated {
@ -277,6 +395,8 @@ pub struct FundingCreated {
/// A [`funding_signed`] message to be sent to or received from a peer.
///
/// Used in V1 channel establishment
///
/// [`funding_signed`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-funding_signed-message
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FundingSigned {
@ -305,6 +425,167 @@ pub struct ChannelReady {
pub short_channel_id_alias: Option<u64>,
}
/// A wrapper for a `Transaction` which can only be constructed with [`TransactionU16LenLimited::new`]
/// if the `Transaction`'s consensus-serialized length is <= u16::MAX.
///
/// Use [`TransactionU16LenLimited::into_transaction`] to convert into the contained `Transaction`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransactionU16LenLimited(Transaction);
impl TransactionU16LenLimited {
/// Constructs a new `TransactionU16LenLimited` from a `Transaction` only if it's consensus-
/// serialized length is <= u16::MAX.
pub fn new(transaction: Transaction) -> Result<Self, ()> {
if transaction.serialized_length() > (u16::MAX as usize) {
Err(())
} else {
Ok(Self(transaction))
}
}
/// Consumes this `TransactionU16LenLimited` and returns its contained `Transaction`.
pub fn into_transaction(self) -> Transaction {
self.0
}
}
impl Writeable for TransactionU16LenLimited {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.0.serialized_length() as u16).write(w)?;
self.0.write(w)
}
}
impl Readable for TransactionU16LenLimited {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let len = <u16 as Readable>::read(r)?;
let mut tx_reader = FixedLengthReader::new(r, len as u64);
Ok(Self(Readable::read(&mut tx_reader)?))
}
}
/// A tx_add_input message for adding an input during interactive transaction construction
///
// TODO(dual_funding): Add spec link for `tx_add_input`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxAddInput {
/// The channel ID
pub channel_id: [u8; 32],
/// A randomly chosen unique identifier for this input, which is even for initiators and odd for
/// non-initiators.
pub serial_id: u64,
/// Serialized transaction that contains the output this input spends to verify that it is non
/// malleable.
pub prevtx: TransactionU16LenLimited,
/// The index of the output being spent
pub prevtx_out: u32,
/// The sequence number of this input
pub sequence: u32,
}
/// A tx_add_output message for adding an output during interactive transaction construction.
///
// TODO(dual_funding): Add spec link for `tx_add_output`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxAddOutput {
/// The channel ID
pub channel_id: [u8; 32],
/// A randomly chosen unique identifier for this output, which is even for initiators and odd for
/// non-initiators.
pub serial_id: u64,
/// The satoshi value of the output
pub sats: u64,
/// The scriptPubKey for the output
pub script: Script,
}
/// A tx_remove_input message for removing an input during interactive transaction construction.
///
// TODO(dual_funding): Add spec link for `tx_remove_input`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxRemoveInput {
/// The channel ID
pub channel_id: [u8; 32],
/// The serial ID of the input to be removed
pub serial_id: u64,
}
/// A tx_remove_output message for removing an output during interactive transaction construction.
///
// TODO(dual_funding): Add spec link for `tx_remove_output`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxRemoveOutput {
/// The channel ID
pub channel_id: [u8; 32],
/// The serial ID of the output to be removed
pub serial_id: u64,
}
/// A tx_complete message signalling the conclusion of a peer's transaction contributions during
/// interactive transaction construction.
///
// TODO(dual_funding): Add spec link for `tx_complete`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxComplete {
/// The channel ID
pub channel_id: [u8; 32],
}
/// A tx_signatures message containing the sender's signatures for a transaction constructed with
/// interactive transaction construction.
///
// TODO(dual_funding): Add spec link for `tx_signatures`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxSignatures {
/// The channel ID
pub channel_id: [u8; 32],
/// The TXID
pub tx_hash: Txid,
/// The list of witnesses
pub witnesses: Vec<Witness>,
}
/// A tx_init_rbf message which initiates a replacement of the transaction after it's been
/// completed.
///
// TODO(dual_funding): Add spec link for `tx_init_rbf`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxInitRbf {
/// The channel ID
pub channel_id: [u8; 32],
/// The locktime of the transaction
pub locktime: u32,
/// The feerate of the transaction
pub feerate_sat_per_1000_weight: u32,
/// The number of satoshis the sender will contribute to or, if negative, remove from
/// (e.g. splice-out) the funding output of the transaction
pub funding_output_contribution: Option<i64>,
}
/// A tx_ack_rbf message which acknowledges replacement of the transaction after it's been
/// completed.
///
// TODO(dual_funding): Add spec link for `tx_ack_rbf`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxAckRbf {
/// The channel ID
pub channel_id: [u8; 32],
/// The number of satoshis the sender will contribute to or, if negative, remove from
/// (e.g. splice-out) the funding output of the transaction
pub funding_output_contribution: Option<i64>,
}
/// A tx_abort message which signals the cancellation of an in-progress transaction negotiation.
///
// TODO(dual_funding): Add spec link for `tx_abort`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxAbort {
/// The channel ID
pub channel_id: [u8; 32],
/// Message data
pub data: Vec<u8>,
}
/// A [`shutdown`] message to be sent to or received from a peer.
///
/// [`shutdown`]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#closing-initiation-shutdown
@ -474,6 +755,8 @@ pub struct ChannelReestablish {
pub your_last_per_commitment_secret: [u8; 32],
/// The sender's per-commitment point for their current commitment transaction
pub my_current_per_commitment_point: PublicKey,
/// The next funding transaction ID
pub next_funding_txid: Option<Txid>,
}
/// An [`announcement_signatures`] message to be sent to or received from a peer.
@ -567,7 +850,7 @@ impl NetAddress {
}
impl Writeable for NetAddress {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
match self {
&NetAddress::IPv4 { ref addr, ref port } => {
1u8.write(writer)?;
@ -1284,6 +1567,82 @@ impl_writeable_msg!(AcceptChannel, {
(4, next_local_nonce, option),
});
impl_writeable_msg!(AcceptChannelV2, {
temporary_channel_id,
funding_satoshis,
dust_limit_satoshis,
max_htlc_value_in_flight_msat,
htlc_minimum_msat,
minimum_depth,
to_self_delay,
max_accepted_htlcs,
funding_pubkey,
revocation_basepoint,
payment_basepoint,
delayed_payment_basepoint,
htlc_basepoint,
first_per_commitment_point,
second_per_commitment_point,
}, {
(0, shutdown_scriptpubkey, option),
(1, channel_type, option),
(2, require_confirmed_inputs, option),
});
impl_writeable_msg!(TxAddInput, {
channel_id,
serial_id,
prevtx,
prevtx_out,
sequence,
}, {});
impl_writeable_msg!(TxAddOutput, {
channel_id,
serial_id,
sats,
script,
}, {});
impl_writeable_msg!(TxRemoveInput, {
channel_id,
serial_id,
}, {});
impl_writeable_msg!(TxRemoveOutput, {
channel_id,
serial_id,
}, {});
impl_writeable_msg!(TxComplete, {
channel_id,
}, {});
impl_writeable_msg!(TxSignatures, {
channel_id,
tx_hash,
witnesses,
}, {});
impl_writeable_msg!(TxInitRbf, {
channel_id,
locktime,
feerate_sat_per_1000_weight,
}, {
(0, funding_output_contribution, option),
});
impl_writeable_msg!(TxAckRbf, {
channel_id,
}, {
(0, funding_output_contribution, option),
});
impl_writeable_msg!(TxAbort, {
channel_id,
data,
}, {});
impl_writeable_msg!(AnnouncementSignatures, {
channel_id,
short_channel_id,
@ -1297,7 +1656,9 @@ impl_writeable_msg!(ChannelReestablish, {
next_remote_commitment_number,
your_last_per_commitment_secret,
my_current_per_commitment_point,
}, {});
}, {
(0, next_funding_txid, option),
});
impl_writeable_msg!(ClosingSigned,
{ channel_id, fee_satoshis, signature },
@ -1422,6 +1783,32 @@ impl_writeable_msg!(OpenChannel, {
(1, channel_type, option),
});
impl_writeable_msg!(OpenChannelV2, {
chain_hash,
temporary_channel_id,
funding_feerate_sat_per_1000_weight,
commitment_feerate_sat_per_1000_weight,
funding_satoshis,
dust_limit_satoshis,
max_htlc_value_in_flight_msat,
htlc_minimum_msat,
to_self_delay,
max_accepted_htlcs,
locktime,
funding_pubkey,
revocation_basepoint,
payment_basepoint,
delayed_payment_basepoint,
htlc_basepoint,
first_per_commitment_point,
second_per_commitment_point,
channel_flags,
}, {
(0, shutdown_scriptpubkey, option),
(1, channel_type, option),
(2, require_confirmed_inputs, option),
});
#[cfg(not(taproot))]
impl_writeable_msg!(RevokeAndACK, {
channel_id,
@ -2039,10 +2426,11 @@ impl_writeable_msg!(GossipTimestampFilter, {
#[cfg(test)]
mod tests {
use bitcoin::{Transaction, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut};
use hex;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{self, TransactionU16LenLimited};
use crate::ln::msgs::{FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
use crate::routing::gossip::{NodeAlias, NodeId};
use crate::util::ser::{Writeable, Readable, Hostname};
@ -2060,6 +2448,9 @@ mod tests {
use crate::io::{self, Cursor};
use crate::prelude::*;
use core::convert::TryFrom;
use core::str::FromStr;
use crate::chain::transaction::OutPoint;
#[test]
fn encoding_channel_reestablish() {
@ -2074,12 +2465,53 @@ mod tests {
next_remote_commitment_number: 4,
your_last_per_commitment_secret: [9;32],
my_current_per_commitment_point: public_key,
next_funding_txid: None,
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
vec![4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143]
vec![
4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
]
);
}
#[test]
fn encoding_channel_reestablish_with_next_funding_txid() {
let public_key = {
let secp_ctx = Secp256k1::new();
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap())
};
let cr = msgs::ChannelReestablish {
channel_id: [4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0],
next_local_commitment_number: 3,
next_remote_commitment_number: 4,
your_last_per_commitment_secret: [9;32],
my_current_per_commitment_point: public_key,
next_funding_txid: Some(Txid::from_hash(bitcoin::hashes::Hash::from_slice(&[
48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124,
]).unwrap())),
};
let encoded_value = cr.encode();
assert_eq!(
encoded_value,
vec![
4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, // channel_id
0, 0, 0, 0, 0, 0, 0, 3, // next_local_commitment_number
0, 0, 0, 0, 0, 0, 0, 4, // next_remote_commitment_number
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // your_last_per_commitment_secret
3, 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, // my_current_per_commitment_point
0, // Type (next_funding_txid)
32, // Length
48, 167, 250, 69, 152, 48, 103, 172, 164, 99, 59, 19, 23, 11, 92, 84, 15, 80, 4, 12, 98, 82, 75, 31, 201, 11, 91, 23, 98, 23, 53, 124, // Value
]
);
}
@ -2395,6 +2827,98 @@ mod tests {
do_encoding_open_channel(true, true, true);
}
fn do_encoding_open_channelv2(random_bit: bool, shutdown: bool, incl_chan_type: bool, require_confirmed_inputs: bool) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
let open_channelv2 = msgs::OpenChannelV2 {
chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
temporary_channel_id: [2; 32],
funding_feerate_sat_per_1000_weight: 821716,
commitment_feerate_sat_per_1000_weight: 821716,
funding_satoshis: 1311768467284833366,
dust_limit_satoshis: 3608586615801332854,
max_htlc_value_in_flight_msat: 8517154655701053848,
htlc_minimum_msat: 2316138423780173,
to_self_delay: 49340,
max_accepted_htlcs: 49340,
locktime: 305419896,
funding_pubkey: pubkey_1,
revocation_basepoint: pubkey_2,
payment_basepoint: pubkey_3,
delayed_payment_basepoint: pubkey_4,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
second_per_commitment_point: pubkey_7,
channel_flags: if random_bit { 1 << 5 } else { 0 },
shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
require_confirmed_inputs: if require_confirmed_inputs { Some(()) } else { None },
};
let encoded_value = open_channelv2.encode();
let mut target_value = Vec::new();
target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
target_value.append(&mut hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap());
target_value.append(&mut hex::decode("000c89d4").unwrap());
target_value.append(&mut hex::decode("000c89d4").unwrap());
target_value.append(&mut hex::decode("1234567890123456").unwrap());
target_value.append(&mut hex::decode("3214466870114476").unwrap());
target_value.append(&mut hex::decode("7633030896203198").unwrap());
target_value.append(&mut hex::decode("00083a840000034d").unwrap());
target_value.append(&mut hex::decode("c0bc").unwrap());
target_value.append(&mut hex::decode("c0bc").unwrap());
target_value.append(&mut hex::decode("12345678").unwrap());
target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap());
target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap());
target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap());
target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap());
target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap());
target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap());
target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap());
if random_bit {
target_value.append(&mut hex::decode("20").unwrap());
} else {
target_value.append(&mut hex::decode("00").unwrap());
}
if shutdown {
target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
}
if incl_chan_type {
target_value.append(&mut hex::decode("0100").unwrap());
}
if require_confirmed_inputs {
target_value.append(&mut hex::decode("0200").unwrap());
}
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_open_channelv2() {
do_encoding_open_channelv2(false, false, false, false);
do_encoding_open_channelv2(false, false, false, true);
do_encoding_open_channelv2(false, false, true, false);
do_encoding_open_channelv2(false, false, true, true);
do_encoding_open_channelv2(false, true, false, false);
do_encoding_open_channelv2(false, true, false, true);
do_encoding_open_channelv2(false, true, true, false);
do_encoding_open_channelv2(false, true, true, true);
do_encoding_open_channelv2(true, false, false, false);
do_encoding_open_channelv2(true, false, false, true);
do_encoding_open_channelv2(true, false, true, false);
do_encoding_open_channelv2(true, false, true, true);
do_encoding_open_channelv2(true, true, false, false);
do_encoding_open_channelv2(true, true, false, true);
do_encoding_open_channelv2(true, true, true, false);
do_encoding_open_channelv2(true, true, true, true);
}
fn do_encoding_accept_channel(shutdown: bool) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
@ -2437,6 +2961,64 @@ mod tests {
do_encoding_accept_channel(true);
}
fn do_encoding_accept_channelv2(shutdown: bool) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
let (_, pubkey_3) = get_keys_from!("0303030303030303030303030303030303030303030303030303030303030303", secp_ctx);
let (_, pubkey_4) = get_keys_from!("0404040404040404040404040404040404040404040404040404040404040404", secp_ctx);
let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
let accept_channelv2 = msgs::AcceptChannelV2 {
temporary_channel_id: [2; 32],
funding_satoshis: 1311768467284833366,
dust_limit_satoshis: 1311768467284833366,
max_htlc_value_in_flight_msat: 2536655962884945560,
htlc_minimum_msat: 2316138423780173,
minimum_depth: 821716,
to_self_delay: 49340,
max_accepted_htlcs: 49340,
funding_pubkey: pubkey_1,
revocation_basepoint: pubkey_2,
payment_basepoint: pubkey_3,
delayed_payment_basepoint: pubkey_4,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
second_per_commitment_point: pubkey_7,
shutdown_scriptpubkey: if shutdown { Some(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: pubkey_1}, Network::Testnet).script_pubkey()) } else { None },
channel_type: None,
require_confirmed_inputs: None,
};
let encoded_value = accept_channelv2.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // temporary_channel_id
target_value.append(&mut hex::decode("1234567890123456").unwrap()); // funding_satoshis
target_value.append(&mut hex::decode("1234567890123456").unwrap()); // dust_limit_satoshis
target_value.append(&mut hex::decode("2334032891223698").unwrap()); // max_htlc_value_in_flight_msat
target_value.append(&mut hex::decode("00083a840000034d").unwrap()); // htlc_minimum_msat
target_value.append(&mut hex::decode("000c89d4").unwrap()); // minimum_depth
target_value.append(&mut hex::decode("c0bc").unwrap()); // to_self_delay
target_value.append(&mut hex::decode("c0bc").unwrap()); // max_accepted_htlcs
target_value.append(&mut hex::decode("031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f").unwrap()); // funding_pubkey
target_value.append(&mut hex::decode("024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766").unwrap()); // revocation_basepoint
target_value.append(&mut hex::decode("02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337").unwrap()); // payment_basepoint
target_value.append(&mut hex::decode("03462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap()); // delayed_payment_basepoint
target_value.append(&mut hex::decode("0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f7").unwrap()); // htlc_basepoint
target_value.append(&mut hex::decode("03f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap()); // first_per_commitment_point
target_value.append(&mut hex::decode("02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f").unwrap()); // second_per_commitment_point
if shutdown {
target_value.append(&mut hex::decode("001b").unwrap()); // Type 0 + Length 27
target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
}
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_accept_channelv2() {
do_encoding_accept_channelv2(false);
do_encoding_accept_channelv2(true);
}
#[test]
fn encoding_funding_created() {
let secp_ctx = Secp256k1::new();
@ -2487,6 +3069,180 @@ mod tests {
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_add_input() {
let tx_add_input = msgs::TxAddInput {
channel_id: [2; 32],
serial_id: 4886718345,
prevtx: TransactionU16LenLimited::new(Transaction {
version: 2,
lock_time: PackedLockTime(0),
input: vec![TxIn {
previous_output: OutPoint { txid: Txid::from_hex("305bab643ee297b8b6b76b320792c8223d55082122cb606bf89382146ced9c77").unwrap(), index: 2 }.into_bitcoin_outpoint(),
script_sig: Script::new(),
sequence: Sequence(0xfffffffd),
witness: Witness::from_vec(vec![
hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
}],
output: vec![
TxOut {
value: 12704566,
script_pubkey: Address::from_str("bc1qzlffunw52jav8vwdu5x3jfk6sr8u22rmq3xzw2").unwrap().script_pubkey(),
},
TxOut {
value: 245148,
script_pubkey: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
},
],
}).unwrap(),
prevtx_out: 305419896,
sequence: 305419896,
};
let encoded_value = tx_add_input.encode();
let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900de02000000000101779ced6c148293f86b60cb222108553d22c89207326bb7b6b897e23e64ab5b300200000000fdffffff0236dbc1000000000016001417d29e4dd454bac3b1cde50d1926da80cfc5287b9cbd03000000000016001436ec78d514df462da95e6a00c24daa8915362d420247304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701210301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944000000001234567812345678").unwrap();
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_add_output() {
let tx_add_output = msgs::TxAddOutput {
channel_id: [2; 32],
serial_id: 4886718345,
sats: 4886718345,
script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().script_pubkey(),
};
let encoded_value = tx_add_output.encode();
let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202000000012345678900000001234567890016001436ec78d514df462da95e6a00c24daa8915362d42").unwrap();
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_remove_input() {
let tx_remove_input = msgs::TxRemoveInput {
channel_id: [2; 32],
serial_id: 4886718345,
};
let encoded_value = tx_remove_input.encode();
let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_remove_output() {
let tx_remove_output = msgs::TxRemoveOutput {
channel_id: [2; 32],
serial_id: 4886718345,
};
let encoded_value = tx_remove_output.encode();
let target_value = hex::decode("02020202020202020202020202020202020202020202020202020202020202020000000123456789").unwrap();
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_complete() {
let tx_complete = msgs::TxComplete {
channel_id: [2; 32],
};
let encoded_value = tx_complete.encode();
let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_signatures() {
let tx_signatures = msgs::TxSignatures {
channel_id: [2; 32],
tx_hash: Txid::from_hex("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
witnesses: vec![
Witness::from_vec(vec![
hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap(),
hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap()]),
Witness::from_vec(vec![
hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap(),
hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap()]),
],
};
let encoded_value = tx_signatures.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
target_value.append(&mut hex::decode("6e96fe9f8b0ddcd729ba03cfafa5a27b050b39d354dd980814268dfa9a44d4c2").unwrap()); // tx_hash (sha256) (big endian byte order)
target_value.append(&mut hex::decode("0002").unwrap()); // num_witnesses (u16)
// Witness 1
target_value.append(&mut hex::decode("006b").unwrap()); // len of witness_data
target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
target_value.append(&mut hex::decode("47").unwrap()); // len of witness element data (VarInt)
target_value.append(&mut hex::decode("304402206af85b7dd67450ad12c979302fac49dfacbc6a8620f49c5da2b5721cf9565ca502207002b32fed9ce1bf095f57aeb10c36928ac60b12e723d97d2964a54640ceefa701").unwrap());
target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
target_value.append(&mut hex::decode("0301ab7dc16488303549bfcdd80f6ae5ee4c20bf97ab5410bbd6b1bfa85dcd6944").unwrap());
// Witness 2
target_value.append(&mut hex::decode("006c").unwrap()); // len of witness_data
target_value.append(&mut hex::decode("02").unwrap()); // num_witness_elements (VarInt)
target_value.append(&mut hex::decode("48").unwrap()); // len of witness element data (VarInt)
target_value.append(&mut hex::decode("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap());
target_value.append(&mut hex::decode("21").unwrap()); // len of witness element data (VarInt)
target_value.append(&mut hex::decode("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap());
assert_eq!(encoded_value, target_value);
}
fn do_encoding_tx_init_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
let tx_init_rbf = msgs::TxInitRbf {
channel_id: [2; 32],
locktime: 305419896,
feerate_sat_per_1000_weight: 20190119,
funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
};
let encoded_value = tx_init_rbf.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
target_value.append(&mut hex::decode("12345678").unwrap()); // locktime
target_value.append(&mut hex::decode("013413a7").unwrap()); // feerate_sat_per_1000_weight
if let Some((_, target)) = funding_value_with_hex_target {
target_value.push(0x00); // Type
target_value.push(target.len() as u8 / 2); // Length
target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
}
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_init_rbf() {
do_encoding_tx_init_rbf(Some((1311768467284833366, "1234567890123456")));
do_encoding_tx_init_rbf(Some((13117684672, "000000030DDFFBC0")));
do_encoding_tx_init_rbf(None);
}
fn do_encoding_tx_ack_rbf(funding_value_with_hex_target: Option<(i64, &str)>) {
let tx_ack_rbf = msgs::TxAckRbf {
channel_id: [2; 32],
funding_output_contribution: if let Some((value, _)) = funding_value_with_hex_target { Some(value) } else { None },
};
let encoded_value = tx_ack_rbf.encode();
let mut target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap();
if let Some((_, target)) = funding_value_with_hex_target {
target_value.push(0x00); // Type
target_value.push(target.len() as u8 / 2); // Length
target_value.append(&mut hex::decode(target).unwrap()); // Value (i64)
}
assert_eq!(encoded_value, target_value);
}
#[test]
fn encoding_tx_ack_rbf() {
do_encoding_tx_ack_rbf(Some((1311768467284833366, "1234567890123456")));
do_encoding_tx_ack_rbf(Some((13117684672, "000000030DDFFBC0")));
do_encoding_tx_ack_rbf(None);
}
#[test]
fn encoding_tx_abort() {
let tx_abort = msgs::TxAbort {
channel_id: [2; 32],
data: hex::decode("54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap(),
};
let encoded_value = tx_abort.encode();
let target_value = hex::decode("0202020202020202020202020202020202020202020202020202020202020202002C54686520717569636B2062726F776E20666F78206A756D7073206F76657220746865206C617A7920646F672E").unwrap();
assert_eq!(encoded_value, target_value);
}
fn do_encoding_shutdown(script_type: u8) {
let secp_ctx = Secp256k1::new();
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);

View file

@ -349,6 +349,50 @@ impl Encode for msgs::ClosingSigned {
const TYPE: u16 = 39;
}
impl Encode for msgs::OpenChannelV2 {
const TYPE: u16 = 64;
}
impl Encode for msgs::AcceptChannelV2 {
const TYPE: u16 = 65;
}
impl Encode for msgs::TxAddInput {
const TYPE: u16 = 66;
}
impl Encode for msgs::TxAddOutput {
const TYPE: u16 = 67;
}
impl Encode for msgs::TxRemoveInput {
const TYPE: u16 = 68;
}
impl Encode for msgs::TxRemoveOutput {
const TYPE: u16 = 69;
}
impl Encode for msgs::TxComplete {
const TYPE: u16 = 70;
}
impl Encode for msgs::TxSignatures {
const TYPE: u16 = 71;
}
impl Encode for msgs::TxInitRbf {
const TYPE: u16 = 72;
}
impl Encode for msgs::TxAckRbf {
const TYPE: u16 = 73;
}
impl Encode for msgs::TxAbort {
const TYPE: u16 = 74;
}
impl Encode for msgs::OnionMessage {
const TYPE: u16 = 513;
}

View file

@ -31,7 +31,7 @@ use bitcoin::secp256k1::schnorr;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::{self, Script};
use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
use bitcoin::consensus;
use bitcoin::{consensus, Witness};
use bitcoin::consensus::Encodable;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{Txid, BlockHash};
@ -512,6 +512,10 @@ impl_writeable_primitive!(u128, 16);
impl_writeable_primitive!(u64, 8);
impl_writeable_primitive!(u32, 4);
impl_writeable_primitive!(u16, 2);
impl_writeable_primitive!(i64, 8);
impl_writeable_primitive!(i32, 4);
impl_writeable_primitive!(i16, 2);
impl_writeable_primitive!(i8, 1);
impl Writeable for u8 {
#[inline]
@ -832,6 +836,40 @@ impl_for_vec!((A, B), A, B);
impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
impl_readable_for_vec!(crate::routing::router::BlindedTail);
impl Writeable for Vec<Witness> {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.len() as u16).write(w)?;
for witness in self {
(witness.serialized_len() as u16).write(w)?;
witness.write(w)?;
}
Ok(())
}
}
impl Readable for Vec<Witness> {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let num_witnesses = <u16 as Readable>::read(r)? as usize;
let mut witnesses = Vec::with_capacity(num_witnesses);
for _ in 0..num_witnesses {
// Even though the length of each witness can be inferred in its consensus-encoded form,
// the spec includes a length prefix so that implementations don't have to deserialize
// each initially. We do that here anyway as in general we'll need to be able to make
// assertions on some properties of the witnesses when receiving a message providing a list
// of witnesses. We'll just do a sanity check for the lengths and error if there is a mismatch.
let witness_len = <u16 as Readable>::read(r)? as usize;
let witness = <Witness as Readable>::read(r)?;
if witness.serialized_len() != witness_len {
return Err(DecodeError::BadLengthDescriptor);
}
witnesses.push(witness);
}
Ok(witnesses)
}
}
impl Writeable for Script {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.len() as u16).write(w)?;
@ -1135,6 +1173,7 @@ macro_rules! impl_consensus_ser {
}
impl_consensus_ser!(Transaction);
impl_consensus_ser!(TxOut);
impl_consensus_ser!(Witness);
impl<T: Readable> Readable for Mutex<T> {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {