@@ -31,21 +31,40 @@ tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
3131/// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
3232/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
3333#[ derive( Clone , Debug , PartialEq ) ]
34- pub struct TaggedHash ( Message ) ;
34+ pub struct TaggedHash {
35+ tag : String ,
36+ merkle_root : sha256:: Hash ,
37+ digest : Message ,
38+ }
3539
3640impl TaggedHash {
3741 /// Creates a tagged hash with the given parameters.
3842 ///
3943 /// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
4044 pub ( super ) fn new ( tag : & str , tlv_stream : & [ u8 ] ) -> Self {
41- let tag = sha256:: Hash :: hash ( tag. as_bytes ( ) ) ;
45+ let tag_hash = sha256:: Hash :: hash ( tag. as_bytes ( ) ) ;
4246 let merkle_root = root_hash ( tlv_stream) ;
43- Self ( Message :: from_slice ( & tagged_hash ( tag, merkle_root) ) . unwrap ( ) )
47+ let digest = Message :: from_slice ( & tagged_hash ( tag_hash, merkle_root) ) . unwrap ( ) ;
48+ Self {
49+ tag : tag. to_owned ( ) ,
50+ merkle_root,
51+ digest,
52+ }
4453 }
4554
4655 /// Returns the digest to sign.
4756 pub fn as_digest ( & self ) -> & Message {
48- & self . 0
57+ & self . digest
58+ }
59+
60+ /// Returns the tag used in the tagged hash.
61+ pub fn tag ( & self ) -> & str {
62+ & self . tag
63+ }
64+
65+ /// Returns the merkle root used in the tagged hash.
66+ pub fn merkle_root ( & self ) -> sha256:: Hash {
67+ self . merkle_root
4968 }
5069}
5170
@@ -254,12 +273,13 @@ mod tests {
254273 use super :: { SIGNATURE_TYPES , TlvStream , WithoutSignatures } ;
255274
256275 use bitcoin:: hashes:: { Hash , sha256} ;
257- use bitcoin:: secp256k1:: { KeyPair , Secp256k1 , SecretKey } ;
276+ use bitcoin:: secp256k1:: { KeyPair , Message , Secp256k1 , SecretKey } ;
258277 use bitcoin:: secp256k1:: schnorr:: Signature ;
259278 use core:: convert:: Infallible ;
260279 use crate :: offers:: offer:: { Amount , OfferBuilder } ;
261280 use crate :: offers:: invoice_request:: InvoiceRequest ;
262281 use crate :: offers:: parse:: Bech32Encode ;
282+ use crate :: offers:: test_utils:: { payer_pubkey, recipient_pubkey} ;
263283 use crate :: util:: ser:: Writeable ;
264284
265285 #[ test]
@@ -318,6 +338,25 @@ mod tests {
318338 ) ;
319339 }
320340
341+ #[ test]
342+ fn compute_tagged_hash ( ) {
343+ let unsigned_invoice_request = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
344+ . amount_msats ( 1000 )
345+ . build ( ) . unwrap ( )
346+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
347+ . payer_note ( "bar" . into ( ) )
348+ . build ( ) . unwrap ( ) ;
349+
350+ // Simply test that we can grab the tag and merkle root exposed by the accessor
351+ // functions, then use them to succesfully compute a tagged hash.
352+ let tagged_hash = unsigned_invoice_request. as_ref ( ) ;
353+ let expected_digest = unsigned_invoice_request. as_ref ( ) . as_digest ( ) ;
354+ let tag = sha256:: Hash :: hash ( tagged_hash. tag ( ) . as_bytes ( ) ) ;
355+ let actual_digest = Message :: from_slice ( & super :: tagged_hash ( tag, tagged_hash. merkle_root ( ) ) )
356+ . unwrap ( ) ;
357+ assert_eq ! ( * expected_digest, actual_digest) ;
358+ }
359+
321360 #[ test]
322361 fn skips_encoding_signature_tlv_records ( ) {
323362 let secp_ctx = Secp256k1 :: new ( ) ;
0 commit comments