Merge pull request #2687 from orbitalturtle/signature-data-enum
[rust-lightning] / lightning / src / offers / merkle.rs
index 7390b58fef8ef780a68f58aa50438dcbe1979d71..a86f0b4e6fbff57c4b7ac59403bb357d894c3b45 100644 (file)
@@ -30,20 +30,41 @@ tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
 ///
 /// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
 /// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
-#[derive(Debug, PartialEq)]
-pub struct TaggedHash(Message);
+#[derive(Clone, Debug, PartialEq)]
+pub struct TaggedHash {
+       tag: String,
+       merkle_root: sha256::Hash,
+       digest: Message,
+}
 
 impl TaggedHash {
        /// Creates a tagged hash with the given parameters.
        ///
        /// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
        pub(super) fn new(tag: &str, tlv_stream: &[u8]) -> Self {
-               Self(message_digest(tag, tlv_stream))
+               let tag_hash = sha256::Hash::hash(tag.as_bytes());
+               let merkle_root = root_hash(tlv_stream);
+               let digest = Message::from_slice(&tagged_hash(tag_hash, merkle_root)).unwrap();
+               Self {
+                       tag: tag.to_owned(),
+                       merkle_root,
+                       digest,
+               }
        }
 
        /// Returns the digest to sign.
        pub fn as_digest(&self) -> &Message {
-               &self.0
+               &self.digest
+       }
+
+       /// Returns the tag used in the tagged hash.
+       pub fn tag(&self) -> &str {
+               &self.tag
+       }
+
+       /// Returns the merkle root used in the tagged hash.
+       pub fn merkle_root(&self) -> sha256::Hash {
+               self.merkle_root
        }
 }
 
@@ -91,7 +112,7 @@ where
 /// Verifies the signature with a pubkey over the given message using a tagged hash as the message
 /// digest.
 pub(super) fn verify_signature(
-       signature: &Signature, message: TaggedHash, pubkey: PublicKey,
+       signature: &Signature, message: &TaggedHash, pubkey: PublicKey,
 ) -> Result<(), secp256k1::Error> {
        let digest = message.as_digest();
        let pubkey = pubkey.into();
@@ -99,12 +120,6 @@ pub(super) fn verify_signature(
        secp_ctx.verify_schnorr(signature, digest, &pubkey)
 }
 
-pub(super) fn message_digest(tag: &str, bytes: &[u8]) -> Message {
-       let tag = sha256::Hash::hash(tag.as_bytes());
-       let merkle_root = root_hash(bytes);
-       Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()
-}
-
 /// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
 /// containing at least one TLV record.
 fn root_hash(data: &[u8]) -> sha256::Hash {
@@ -258,12 +273,13 @@ mod tests {
        use super::{SIGNATURE_TYPES, TlvStream, WithoutSignatures};
 
        use bitcoin::hashes::{Hash, sha256};
-       use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
+       use bitcoin::secp256k1::{KeyPair, Message, Secp256k1, SecretKey};
        use bitcoin::secp256k1::schnorr::Signature;
        use core::convert::Infallible;
        use crate::offers::offer::{Amount, OfferBuilder};
        use crate::offers::invoice_request::InvoiceRequest;
        use crate::offers::parse::Bech32Encode;
+       use crate::offers::test_utils::{payer_pubkey, recipient_pubkey};
        use crate::util::ser::Writeable;
 
        #[test]
@@ -322,6 +338,25 @@ mod tests {
                );
        }
 
+        #[test]
+        fn compute_tagged_hash() {
+                let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                        .amount_msats(1000)
+                        .build().unwrap()
+                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                        .payer_note("bar".into())
+                        .build().unwrap();
+
+                // Simply test that we can grab the tag and merkle root exposed by the accessor
+                // functions, then use them to succesfully compute a tagged hash.
+                let tagged_hash = unsigned_invoice_request.as_ref();
+                let expected_digest = unsigned_invoice_request.as_ref().as_digest();
+                let tag = sha256::Hash::hash(tagged_hash.tag().as_bytes());
+                let actual_digest = Message::from_slice(&super::tagged_hash(tag, tagged_hash.merkle_root()))
+                        .unwrap();
+                assert_eq!(*expected_digest, actual_digest);
+        }
+
        #[test]
        fn skips_encoding_signature_tlv_records() {
                let secp_ctx = Secp256k1::new();