}
/// A semantically valid [`InvoiceRequest`] that hasn't been signed.
+///
+/// # Serialization
+///
+/// This is serialized as a TLV stream, which includes TLV records from the originating message. As
+/// such, it may include unknown, odd TLV records.
pub struct UnsignedInvoiceRequest {
bytes: Vec<u8>,
- invoice_request: InvoiceRequestContents,
+ contents: InvoiceRequestContents,
tagged_hash: TaggedHash,
}
impl UnsignedInvoiceRequest {
- fn new(offer: &Offer, invoice_request: InvoiceRequestContents) -> Self {
+ fn new(offer: &Offer, contents: InvoiceRequestContents) -> Self {
// Use the offer bytes instead of the offer TLV stream as the offer may have contained
// unknown TLV records, which are not stored in `OfferContents`.
let (payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) =
- invoice_request.as_tlv_stream();
+ contents.as_tlv_stream();
let offer_bytes = WithoutLength(&offer.bytes);
let unsigned_tlv_stream = (payer_tlv_stream, offer_bytes, invoice_request_tlv_stream);
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
- Self { bytes, invoice_request, tagged_hash }
+ Self { bytes, contents, tagged_hash }
}
- /// Signs the invoice request using the given function.
+ /// Signs the [`TaggedHash`] of the invoice request using the given function.
+ ///
+ /// Note: The hash computation may have included unknown, odd TLV records.
///
/// This is not exported to bindings users as functions are not yet mapped.
pub fn sign<F, E>(mut self, sign: F) -> Result<InvoiceRequest, SignError<E>>
where
F: FnOnce(&Self) -> Result<Signature, E>
{
- let pubkey = self.invoice_request.payer_id;
+ let pubkey = self.contents.payer_id;
let signature = merkle::sign_message(sign, &self, pubkey)?;
// Append the signature TLV record to the bytes.
Ok(InvoiceRequest {
bytes: self.bytes,
- contents: self.invoice_request,
+ contents: self.contents,
signature,
})
}
}
}
+impl Writeable for UnsignedInvoiceRequest {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ WithoutLength(&self.bytes).write(writer)
+ }
+}
+
impl Writeable for InvoiceRequest {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
WithoutLength(&self.bytes).write(writer)
InvoiceRequestTlvStreamRef<'a>,
);
+impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
+ type Error = Bolt12ParseError;
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ let invoice_request = ParsedMessage::<PartialInvoiceRequestTlvStream>::try_from(bytes)?;
+ let ParsedMessage { bytes, tlv_stream } = invoice_request;
+ let (
+ payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+ ) = tlv_stream;
+ let contents = InvoiceRequestContents::try_from(
+ (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+ )?;
+
+ let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+
+ Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
+ }
+}
+
impl TryFrom<Vec<u8>> for InvoiceRequest {
type Error = Bolt12ParseError;
#[cfg(test)]
mod tests {
- use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG};
+ use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
#[test]
fn builds_invoice_request_with_defaults() {
- let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+ let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
- .build().unwrap()
- .sign(payer_sign).unwrap();
+ .build().unwrap();
+
+ let mut buffer = Vec::new();
+ unsigned_invoice_request.write(&mut buffer).unwrap();
+
+ match UnsignedInvoiceRequest::try_from(buffer) {
+ Err(e) => panic!("error parsing unsigned invoice request: {:?}", e),
+ Ok(parsed) => {
+ assert_eq!(parsed.bytes, unsigned_invoice_request.bytes);
+ assert_eq!(parsed.tagged_hash, unsigned_invoice_request.tagged_hash);
+ },
+ }
+
+ let invoice_request = unsigned_invoice_request.sign(payer_sign).unwrap();
let mut buffer = Vec::new();
invoice_request.write(&mut buffer).unwrap();
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.0.metadata = None;
let mut buffer = Vec::new();
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.2.payer_id = None;
let mut buffer = Vec::new();
.build().unwrap();
let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap();
- let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream();
+ let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream();
tlv_stream.1.node_id = None;
let mut buffer = Vec::new();
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
- .invoice_request
+ .contents
.write(&mut buffer).unwrap();
match InvoiceRequest::try_from(buffer) {