@@ -25,28 +25,44 @@ use validation::{
2525use x509_chunks:: X509Chunks ;
2626
2727use super :: transaction:: witness:: TxWitness ;
28- use crate :: utils:: {
29- decode_helper:: { decode_bytes, decode_helper, decode_map_len} ,
30- general:: { decode_utf8, decremented_index} ,
31- hashing:: { blake2b_128, blake2b_256} ,
28+ use crate :: {
29+ cardano:: cip509:: rbac:: Cip509RbacMetadata ,
30+ utils:: {
31+ decode_helper:: { decode_bytes, decode_helper, decode_map_len} ,
32+ general:: decremented_index,
33+ hashing:: { blake2b_128, blake2b_256} ,
34+ } ,
3235} ;
3336
3437/// CIP509 label.
3538pub const LABEL : u64 = 509 ;
3639
37- /// CIP509.
38- #[ derive( Debug , PartialEq , Clone , Default ) ]
40+ /// A x509 metadata envelope.
41+ ///
42+ /// The envelope is required to prevent replayability attacks. See [this document] for
43+ /// more details.
44+ ///
45+ /// [this document]: https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md
46+ #[ derive( Debug , PartialEq , Clone ) ]
3947pub struct Cip509 {
40- /// `UUIDv4` Purpose .
41- pub purpose : Uuid , // (bytes .size 16)
48+ /// A registration purpose (`UUIDv4`).
49+ ///
50+ /// The purpose is defined by the consuming dApp.
51+ pub purpose : Uuid ,
4252 /// Transaction inputs hash.
43- pub txn_inputs_hash : TxInputHash , // bytes .size 16
44- /// Optional previous transaction ID.
45- pub prv_tx_id : Option < Hash < 32 > > , // bytes .size 32
46- /// x509 chunks.
47- pub x509_chunks : X509Chunks , // chunk_type => [ + x509_chunk ]
48- /// Validation signature.
49- pub validation_signature : Vec < u8 > , // bytes size (1..64)
53+ pub txn_inputs_hash : TxInputHash ,
54+ /// An optional hash of the previous transaction.
55+ ///
56+ /// The hash must always be present except for the first registration transaction.
57+ // TODO: Use the `Blake2b256Hash` type from the `cardano-blockchain-types` crate.
58+ pub prv_tx_id : Option < Hash < 32 > > ,
59+ /// Metadata.
60+ ///
61+ /// This field encoded in chunks. See [`X509Chunks`] for more details.
62+ pub metadata : Cip509RbacMetadata ,
63+ /// Validation signature. Must be at least 1 byte and at most 64 bytes long (1..64).
64+ // TODO: Create a type for the signature.
65+ pub validation_signature : Vec < u8 > ,
5066}
5167
5268/// Validation value for CIP509 metadatum.
@@ -92,7 +108,13 @@ pub(crate) enum Cip509IntIdentifier {
92108impl Decode < ' _ , ( ) > for Cip509 {
93109 fn decode ( d : & mut Decoder , ctx : & mut ( ) ) -> Result < Self , decode:: Error > {
94110 let map_len = decode_map_len ( d, "CIP509" ) ?;
95- let mut cip509_metadatum = Cip509 :: default ( ) ;
111+
112+ let mut purpose = Uuid :: default ( ) ;
113+ let mut txn_inputs_hash = TxInputHash :: default ( ) ;
114+ let mut prv_tx_id = None ;
115+ let mut metadata = None ;
116+ let mut validation_signature = Vec :: new ( ) ;
117+
96118 for _ in 0 ..map_len {
97119 // Use probe to peak
98120 let key = d. probe ( ) . u8 ( ) ?;
@@ -101,43 +123,59 @@ impl Decode<'_, ()> for Cip509 {
101123 let _: u8 = decode_helper ( d, "CIP509" , ctx) ?;
102124 match key {
103125 Cip509IntIdentifier :: Purpose => {
104- cip509_metadatum. purpose =
105- Uuid :: try_from ( decode_bytes ( d, "CIP509 purpose" ) ?) . map_err ( |_| {
106- decode:: Error :: message ( "Invalid data size of Purpose" )
107- } ) ?;
126+ purpose = Uuid :: try_from ( decode_bytes ( d, "CIP509 purpose" ) ?)
127+ . map_err ( |_| decode:: Error :: message ( "Invalid data size of Purpose" ) ) ?;
108128 } ,
109129 Cip509IntIdentifier :: TxInputsHash => {
110- cip509_metadatum . txn_inputs_hash =
130+ txn_inputs_hash =
111131 TxInputHash :: try_from ( decode_bytes ( d, "CIP509 txn inputs hash" ) ?)
112132 . map_err ( |_| {
113133 decode:: Error :: message ( "Invalid data size of TxInputsHash" )
114134 } ) ?;
115135 } ,
116136 Cip509IntIdentifier :: PreviousTxId => {
117- let prv_tx_hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
137+ let hash : [ u8 ; 32 ] = decode_bytes ( d, "CIP509 previous tx ID" ) ?
118138 . try_into ( )
119139 . map_err ( |_| {
120- decode:: Error :: message ( "Invalid data size of PreviousTxId" )
121- } ) ?;
122- cip509_metadatum . prv_tx_id = Some ( Hash :: from ( prv_tx_hash ) ) ;
140+ decode:: Error :: message ( "Invalid data size of PreviousTxId" )
141+ } ) ?;
142+ prv_tx_id = Some ( Hash :: from ( hash ) ) ;
123143 } ,
124144 Cip509IntIdentifier :: ValidationSignature => {
125- let validation_signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
126- if validation_signature . is_empty ( ) || validation_signature . len ( ) > 64 {
145+ let signature = decode_bytes ( d, "CIP509 validation signature" ) ?;
146+ if signature . is_empty ( ) || signature . len ( ) > 64 {
127147 return Err ( decode:: Error :: message (
128148 "Invalid data size of ValidationSignature" ,
129149 ) ) ;
130150 }
131- cip509_metadatum . validation_signature = validation_signature ;
151+ validation_signature = signature ;
132152 } ,
133153 }
134154 } else {
135155 // Handle the x509 chunks 10 11 12
136156 let x509_chunks = X509Chunks :: decode ( d, ctx) ?;
137- cip509_metadatum. x509_chunks = x509_chunks;
157+ // Technically it is possible to store multiple copies (or different instances) of
158+ // metadata, but it isn't allowed. See this link for more details:
159+ // https://github.com/input-output-hk/catalyst-CIPs/blob/x509-envelope-metadata/CIP-XXXX/README.md#keys-10-11-or-12---x509-chunked-data
160+ if metadata. is_some ( ) {
161+ return Err ( decode:: Error :: message (
162+ "Only one instance of the chunked metadata should be present" ,
163+ ) ) ;
164+ }
165+ metadata = Some ( x509_chunks. into ( ) ) ;
138166 }
139167 }
140- Ok ( cip509_metadatum)
168+
169+ let metadata =
170+ metadata. ok_or_else ( || decode:: Error :: message ( "Missing metadata in CIP509" ) ) ?;
171+
172+ Ok ( Self {
173+ purpose,
174+ txn_inputs_hash,
175+ prv_tx_id,
176+ metadata,
177+ validation_signature,
178+ } )
141179 }
142180}
143181
@@ -179,16 +217,14 @@ impl Cip509 {
179217 let mut is_valid_stake_public_key = true ;
180218 let mut is_valid_payment_key = true ;
181219 let mut is_valid_signing_key = true ;
182- if let Some ( role_set) = & self . x509_chunks . 0 . role_set {
183- // Validate only role 0
184- for role in role_set {
185- if role. role_number == 0 {
186- is_valid_stake_public_key =
187- validate_stake_public_key ( self , txn, validation_report) . unwrap_or ( false ) ;
188- is_valid_payment_key =
189- validate_payment_key ( txn, role, validation_report) . unwrap_or ( false ) ;
190- is_valid_signing_key = validate_role_singing_key ( role, validation_report) ;
191- }
220+ // Validate only role 0
221+ for role in & self . metadata . role_set {
222+ if role. role_number == 0 {
223+ is_valid_stake_public_key =
224+ validate_stake_public_key ( self , txn, validation_report) . unwrap_or ( false ) ;
225+ is_valid_payment_key =
226+ validate_payment_key ( txn, role, validation_report) . unwrap_or ( false ) ;
227+ is_valid_signing_key = validate_role_singing_key ( role, validation_report) ;
192228 }
193229 }
194230 Cip509Validation {
0 commit comments