1515//! # Transactions Blinding
1616//!
1717
18- use internals:: array:: ArrayExt as _;
1918use internals:: slice:: SliceExt ;
2019use std:: { self , collections:: BTreeMap , fmt} ;
2120
@@ -201,26 +200,144 @@ impl From<secp256k1_zkp::Error> for ConfidentialTxOutError {
201200 ConfidentialTxOutError :: Upstream ( from)
202201 }
203202}
204- /// The Rangeproof message
205- #[ derive( Debug , Clone , Eq , PartialEq , Hash , Ord , PartialOrd ) ]
206- pub struct RangeProofMessage {
207- /// The asset id
208- pub asset : AssetId ,
209- /// The asset blinding factor
210- pub bf : AssetBlindingFactor ,
211- }
212203
213- impl RangeProofMessage {
214- /// Converts the message to bytes
215- pub fn to_bytes ( & self ) -> [ u8 ; 64 ] {
216- let mut message = [ 0u8 ; 64 ] ;
204+ mod range_proof_message {
205+ use core:: fmt;
206+ use internals:: array:: ArrayExt ;
207+ use secp256k1_zkp:: { Generator , Signing , Secp256k1 } ;
208+ use super :: { Asset , AssetId , AssetBlindingFactor } ;
209+
210+ /// The Rangeproof message
211+ #[ derive( Debug , Clone , Eq , PartialEq , Hash , Ord , PartialOrd ) ]
212+ pub struct RangeProofMessage {
213+ /// The asset id
214+ asset_id : AssetId ,
215+ /// The asset blinding factor
216+ asset_bf : AssetBlindingFactor ,
217+ }
218+
219+ impl RangeProofMessage {
220+ /// Constructs a [`RangeProofMessage`] from an asset ID and blinding factor.
221+ pub fn new ( asset_id : AssetId , asset_bf : AssetBlindingFactor ) -> Self {
222+ Self { asset_id, asset_bf }
223+ }
224+
225+ /// The asset ID embedded in the rangeproof message.
226+ pub fn asset_id ( & self ) -> & AssetId {
227+ & self . asset_id
228+ }
229+
230+ /// The asset blinding factor embedded in the rangeproof message.
231+ pub fn blinding_factor ( & self ) -> & AssetBlindingFactor {
232+ & self . asset_bf
233+ }
217234
218- message[ ..32 ] . copy_from_slice ( self . asset . into_tag ( ) . as_ref ( ) ) ;
219- message[ 32 ..] . copy_from_slice ( self . bf . into_inner ( ) . as_ref ( ) ) ;
235+ /// Computes the commmitment of the rangeproof message.
236+ pub fn commitment < C : Signing > ( & self , secp : & Secp256k1 < C > ) -> Generator {
237+ Generator :: new_blinded ( secp, self . asset_id . into_tag ( ) , self . asset_bf . into_inner ( ) )
238+ }
220239
221- message
240+ /// Parses a message from bytes
241+ pub fn from_byte_array < C : Signing > (
242+ secp : & Secp256k1 < C > ,
243+ inner : [ u8 ; 64 ] ,
244+ expected_asset : & Asset ,
245+ ) -> Result < Self , RangeProofMessageError > {
246+ let ( asset_id, asset_bf) = inner. split_array :: < 32 , 32 > ( ) ;
247+ let ret = Self {
248+ asset_id : AssetId :: from_byte_array ( * asset_id) ,
249+ asset_bf : AssetBlindingFactor :: from_byte_array ( * asset_bf)
250+ . map_err ( RangeProofMessageError :: BlindingFactorOutOfRange ) ?,
251+ } ;
252+
253+ match expected_asset {
254+ Asset :: Null => return Err ( RangeProofMessageError :: NullExpectedAsset ) ,
255+ Asset :: Explicit ( asset_id) => {
256+ if ret. asset_id != * asset_id {
257+ return Err ( RangeProofMessageError :: ExplicitAssetMismatch {
258+ in_txout : * asset_id,
259+ in_message : ret. asset_id ,
260+ } )
261+ }
262+ if ret. asset_bf != AssetBlindingFactor :: zero ( ) {
263+ return Err ( RangeProofMessageError :: ExplicitAssetNonzeroBf {
264+ blinding_factor : ret. asset_bf ,
265+ } ) ;
266+ }
267+ }
268+ Asset :: Confidential ( commitment) => {
269+ let ret_commitment = ret. commitment ( secp) ;
270+ if ret_commitment != * commitment {
271+ return Err ( RangeProofMessageError :: ConfidentialAssetMismatch {
272+ in_txout : * commitment,
273+ in_message : ret_commitment,
274+ } )
275+ }
276+ }
277+ }
278+
279+ Ok ( ret)
280+ }
281+
282+ /// Converts the message to bytes
283+ pub fn to_byte_array ( & self ) -> [ u8 ; 64 ] {
284+ let mut message = [ 0u8 ; 64 ] ;
285+ message[ ..32 ] . copy_from_slice ( self . asset_id . into_tag ( ) . as_ref ( ) ) ;
286+ message[ 32 ..] . copy_from_slice ( self . asset_bf . into_inner ( ) . as_ref ( ) ) ;
287+ message
288+ }
289+ }
290+
291+ #[ non_exhaustive]
292+ #[ derive( PartialEq , Eq , Clone , Debug ) ]
293+ pub enum RangeProofMessageError {
294+ NullExpectedAsset ,
295+ ExplicitAssetMismatch {
296+ in_txout : AssetId ,
297+ in_message : AssetId ,
298+ } ,
299+ ExplicitAssetNonzeroBf {
300+ blinding_factor : AssetBlindingFactor ,
301+ } ,
302+ ConfidentialAssetMismatch {
303+ in_txout : Generator ,
304+ in_message : Generator ,
305+ } ,
306+ BlindingFactorOutOfRange ( secp256k1_zkp:: Error ) ,
307+ }
308+
309+ impl fmt:: Display for RangeProofMessageError {
310+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
311+ match * self {
312+ Self :: NullExpectedAsset => f. write_str ( "rangeproof associated with null asset" ) ,
313+ Self :: ExplicitAssetMismatch { in_txout, in_message } => {
314+ write ! ( f, "txout had explicit asset ID {in_txout}, but rangeproof encoded asset ID {in_message}" )
315+ }
316+ Self :: ExplicitAssetNonzeroBf { blinding_factor } => {
317+ write ! ( f, "txout had explicit asset ID, but rangeproof encoded a nonzero asset blinding factor {blinding_factor}" )
318+ }
319+ Self :: ConfidentialAssetMismatch { in_txout, in_message } => {
320+ write ! ( f, "txout had asset commitment {in_txout}, but rangeproof encoded asset commitment {in_message}" )
321+ }
322+ Self :: BlindingFactorOutOfRange ( ref e) => e. fmt ( f) ,
323+ }
324+ }
325+ }
326+
327+ impl std:: error:: Error for RangeProofMessageError {
328+ fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
329+ match * self {
330+ Self :: NullExpectedAsset => None ,
331+ Self :: ExplicitAssetMismatch { .. } => None ,
332+ Self :: ExplicitAssetNonzeroBf { .. } => None ,
333+ Self :: ConfidentialAssetMismatch { .. } => None ,
334+ Self :: BlindingFactorOutOfRange ( ref e) => Some ( e) ,
335+ }
336+
337+ }
222338 }
223339}
340+ pub use self :: range_proof_message:: { RangeProofMessage , RangeProofMessageError } ;
224341
225342/// Information about Transaction Input Asset
226343#[ cfg_attr(
@@ -432,8 +549,7 @@ impl Value {
432549 let value = self
433550 . explicit ( )
434551 . ok_or ( ConfidentialTxOutError :: ExpectedExplicitValue ) ?;
435- let out_asset_commitment =
436- Generator :: new_blinded ( secp, msg. asset . into_tag ( ) , msg. bf . into_inner ( ) ) ;
552+ let out_asset_commitment = msg. commitment ( secp) ;
437553 let value_commitment = Value :: new_confidential ( secp, value, out_asset_commitment, vbf) ;
438554
439555 let rangeproof = RangeProof :: new (
@@ -442,7 +558,7 @@ impl Value {
442558 value_commitment. commitment ( ) . expect ( "confidential value" ) ,
443559 value,
444560 vbf. into_inner ( ) ,
445- & msg. to_bytes ( ) ,
561+ & msg. to_byte_array ( ) ,
446562 spk. as_bytes ( ) ,
447563 shared_secret,
448564 TxOut :: RANGEPROOF_EXP_SHIFT ,
@@ -536,10 +652,10 @@ impl TxOut {
536652 let ( out_asset, surjection_proof) =
537653 exp_asset. blind ( rng, secp, out_secrets. asset_bf , spent_utxo_secrets) ?;
538654
539- let msg = RangeProofMessage {
540- asset : out_secrets. asset ,
541- bf : out_secrets. asset_bf ,
542- } ;
655+ let msg = RangeProofMessage :: new (
656+ out_secrets. asset ,
657+ out_secrets. asset_bf ,
658+ ) ;
543659 let exp_value = Value :: Explicit ( out_secrets. value ) ;
544660 let ( out_value, nonce, range_proof) = exp_value. blind (
545661 secp,
@@ -735,7 +851,7 @@ impl TxOut {
735851 /// Unblinds a transaction output, if it is confidential.
736852 ///
737853 /// It returns the secret elements of the value and asset Pedersen commitments.
738- pub fn unblind < C : Verification > (
854+ pub fn unblind < C : Signing + Verification > (
739855 & self ,
740856 secp : & Secp256k1 < C > ,
741857 blinding_key : SecretKey ,
@@ -760,34 +876,22 @@ impl TxOut {
760876 shared_secret,
761877 self . script_pubkey . as_bytes ( ) ,
762878 additional_generator,
763- ) ?;
879+ ) . map_err ( UnblindError :: Rewind ) ?;
764880
765- // Use `MissingRangeproof` error because it's available so does not require
766- // API breaks. In a later PR we should extend that enum and add #[non_exhaustive]
767- // to it. The maybe-better `MalformedAssetId` error requires we start with a
768- // std `FromSliceError` which we don't have.
881+ let value = opening. value ;
882+ let value_bf = ValueBlindingFactor ( opening. blinding_factor ) ;
769883 let asset_and_bf = SliceExt :: split_first_chunk :: < 64 > ( opening. message . as_ref ( ) )
770884 . ok_or ( UnblindError :: MissingRangeproof ) ?
771885 . 0 ;
772- let ( asset_id, asset_bf) = asset_and_bf. split_array ( ) ;
773-
774- let asset_id = AssetId :: from_byte_array ( * asset_id) ;
775- let asset_bf = AssetBlindingFactor :: from_byte_array ( * asset_bf) ?;
776- if let Asset :: Confidential ( own_asset) = self . asset {
777- let secp = Secp256k1 :: signing_only ( ) ; // needed to avoid API break
778- let asset = Generator :: new_blinded ( & secp, asset_id. into_tag ( ) , asset_bf. into_inner ( ) ) ;
779- if asset != own_asset {
780- // See above about use of MissingRangeproof.
781- return Err ( UnblindError :: MissingRangeproof ) ;
782- }
783- }
784-
785- let value = opening. value ;
786- let value_bf = ValueBlindingFactor ( opening. blinding_factor ) ;
886+ let message = RangeProofMessage :: from_byte_array (
887+ secp,
888+ * asset_and_bf,
889+ & self . asset ,
890+ ) . map_err ( UnblindError :: RangeProofMessage ) ?;
787891
788892 Ok ( TxOutSecrets {
789- asset : asset_id,
790- asset_bf,
893+ asset : * message . asset_id ( ) ,
894+ asset_bf : * message . blinding_factor ( ) ,
791895 value,
792896 value_bf,
793897 } )
@@ -796,25 +900,26 @@ impl TxOut {
796900
797901/// Errors encountered when unblinding `TxOut`s.
798902#[ derive( Debug ) ]
903+ #[ non_exhaustive]
799904pub enum UnblindError {
800905 /// The `TxOut` is not fully confidential.
801906 NotConfidential ,
802907 /// Transaction output does not have a nonce commitment.
803908 MissingNonce ,
804909 /// Transaction output does not have a rangeproof.
805910 MissingRangeproof ,
806- /// Malformed asset ID .
807- MalformedAssetId ( core :: array :: TryFromSliceError ) ,
911+ /// Malformed rangeproof message .
912+ RangeProofMessage ( RangeProofMessageError ) ,
808913 /// Error originated in `secp256k1_zkp`.
809- Upstream ( secp256k1_zkp:: Error ) ,
914+ Rewind ( secp256k1_zkp:: Error ) ,
810915}
811916
812917impl fmt:: Display for UnblindError {
813918 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> Result < ( ) , fmt:: Error > {
814919 match self {
815920 UnblindError :: MissingNonce => write ! ( f, "missing nonce in txout" ) ,
816- UnblindError :: MalformedAssetId ( _) => write ! ( f , "malformed asset id ") ,
817- UnblindError :: Upstream ( e ) => write ! ( f , "{}" , e ) ,
921+ UnblindError :: RangeProofMessage ( _) => f . write_str ( "failed to parse message embedded in rangeproof ") ,
922+ UnblindError :: Rewind ( _ ) => f . write_str ( "failed to rewind rangeproof" ) ,
818923 UnblindError :: NotConfidential => write ! ( f, "cannot unblind non-confidential txout" ) ,
819924 UnblindError :: MissingRangeproof => write ! ( f, "missing rangeproof in txout" ) ,
820925 }
@@ -825,20 +930,14 @@ impl std::error::Error for UnblindError {
825930 fn cause ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
826931 match self {
827932 UnblindError :: MissingNonce => None ,
828- UnblindError :: MalformedAssetId ( e) => Some ( e) ,
829- UnblindError :: Upstream ( e) => Some ( e) ,
933+ UnblindError :: RangeProofMessage ( e) => Some ( e) ,
934+ UnblindError :: Rewind ( e) => Some ( e) ,
830935 UnblindError :: NotConfidential => None ,
831936 UnblindError :: MissingRangeproof => None ,
832937 }
833938 }
834939}
835940
836- impl From < secp256k1_zkp:: Error > for UnblindError {
837- fn from ( from : secp256k1_zkp:: Error ) -> Self {
838- UnblindError :: Upstream ( from)
839- }
840- }
841-
842941impl TxIn {
843942 /// Blind issuances for this [`TxIn`]. Asset amount and token amount must be
844943 /// set in [`AssetIssuance`](crate::AssetIssuance) field for this input
@@ -871,10 +970,10 @@ impl TxIn {
871970 Value :: Explicit ( v) => Value :: Explicit ( v) ,
872971 } ;
873972 let spk = Script :: new ( ) ;
874- let msg = RangeProofMessage {
973+ let msg = RangeProofMessage :: new (
875974 asset,
876- bf : AssetBlindingFactor :: zero ( ) ,
877- } ;
975+ AssetBlindingFactor :: zero ( ) ,
976+ ) ;
878977 let ( comm, prf) = v. blind_with_shared_secret ( secp, bf, blind_sk, & spk, & msg) ?;
879978 if i == 0 {
880979 self . asset_issuance . amount = comm;
0 commit comments