expose more granular data in TaggedHash struct
authorOrbital <orbitalturtle@protonmail.com>
Thu, 26 Oct 2023 03:10:35 +0000 (22:10 -0500)
committerOrbital <orbitalturtle@protonmail.com>
Tue, 7 Nov 2023 03:36:54 +0000 (21:36 -0600)
Expose tag and merkle root fields in the TaggedHash struct.

lightning/src/offers/merkle.rs

index cf9a2eff4626168982648b99a9dabec1a824d11c..a86f0b4e6fbff57c4b7ac59403bb357d894c3b45 100644 (file)
@@ -31,21 +31,40 @@ 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(Clone, Debug, PartialEq)]
-pub struct TaggedHash(Message);
+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 {
-               let tag = sha256::Hash::hash(tag.as_bytes());
+               let tag_hash = sha256::Hash::hash(tag.as_bytes());
                let merkle_root = root_hash(tlv_stream);
-               Self(Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap())
+               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
        }
 }
 
@@ -254,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]
@@ -318,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();