@@ -117,14 +117,13 @@ pub fn validate_aux(
117117 }
118118}
119119
120- /// Checks that all public keys extracted from x509 and c509 certificates are present in
121- /// the witness set of the transaction.
122- pub fn validate_stake_public_key (
120+ /// Check certificate URI addresses, if any, must be witnessed in the transaction.
121+ pub fn validate_cert_addrs (
123122 transaction : & conway:: Tx ,
124123 uris : Option < & Cip0134UriSet > ,
125124 report : & ProblemReport ,
126125) {
127- let context = "Cip509 stake public key validation" ;
126+ let context = "Cip509 certificate URI address validation" ;
128127
129128 let transaction = MultiEraTx :: Conway ( Box :: new ( Cow :: Borrowed ( transaction) ) ) ;
130129 let witness = match TxnWitness :: new ( std:: slice:: from_ref ( & transaction) ) {
@@ -135,23 +134,17 @@ pub fn validate_stake_public_key(
135134 } ,
136135 } ;
137136
138- let pk_addrs = extract_stake_addresses ( uris) ;
139- if pk_addrs. is_empty ( ) {
140- report. other (
141- "Unable to find stake addresses in Cip509 certificates" ,
142- context,
143- ) ;
144- return ;
145- }
137+ let stake_addrs = extract_stake_addresses ( uris) ;
138+ let payment_addrs = extract_payment_addresses ( uris) ;
146139
147- for ( hash, address) in pk_addrs {
140+ for ( hash, address) in stake_addrs . into_iter ( ) . chain ( payment_addrs ) {
148141 if !witness. check_witness_in_tx ( & hash, 0 . into ( ) ) {
149142 report. other (
150- & format ! (
151- "Payment Key '{address}' (0x{hash}) is not present in the transaction witness set, and can not be verified as owned and spendable. "
152- ) ,
153- context,
154- ) ;
143+ & format ! (
144+ "Address '{address}', key hash (0x{hash}) is not present in the transaction witness set, and can not be verified as owned and spendable"
145+ ) ,
146+ context,
147+ ) ;
155148 }
156149 }
157150}
@@ -184,6 +177,41 @@ fn extract_stake_addresses(uris: Option<&Cip0134UriSet>) -> Vec<(VKeyHash, Strin
184177 . collect ( )
185178}
186179
180+ /// Extracts all payment addresses from both X509 and C509 certificates containing in the
181+ /// given `Cip509`. Returns a list of pairs containing verifying public key hash (only the
182+ /// payment part) and `bech32` string representation of address.
183+ fn extract_payment_addresses ( uris : Option < & Cip0134UriSet > ) -> Vec < ( VKeyHash , String ) > {
184+ let Some ( uris) = uris else {
185+ return Vec :: new ( ) ;
186+ } ;
187+
188+ uris. x_uris ( )
189+ . iter ( )
190+ . chain ( uris. c_uris ( ) )
191+ . flat_map ( |( _index, uris) | uris. iter ( ) )
192+ . filter_map ( |uri| {
193+ if let Address :: Shelley ( a) = uri. address ( ) {
194+ match a. payment ( ) {
195+ // Shelley payment part is used to sign the transaction
196+ cardano_blockchain_types:: pallas_addresses:: ShelleyPaymentPart :: Key ( hash) => {
197+ match a. to_bech32 ( ) {
198+ Ok ( bech32) => {
199+ hash. as_slice ( ) . try_into ( ) . ok ( ) . map ( |hash| ( hash, bech32) )
200+ } ,
201+ Err ( _) => None ,
202+ }
203+ } ,
204+ cardano_blockchain_types:: pallas_addresses:: ShelleyPaymentPart :: Script ( _) => {
205+ None
206+ } ,
207+ }
208+ } else {
209+ None
210+ }
211+ } )
212+ . collect ( )
213+ }
214+
187215/// Validate self-signed certificates.
188216/// All certificates should be self-signed and support only ED25519 signature.
189217pub fn validate_self_sign_cert (
@@ -358,6 +386,15 @@ pub fn validate_role_data(
358386 {
359387 report. other ( "The role 0 certificate must be present" , context) ;
360388 }
389+
390+ // Can contain different kind of address URIs, but for role 0
391+ // there should be at least 1 stake address
392+ if metadata. certificate_uris . role_stake_addresses ( 0 ) . is_empty ( ) {
393+ report. missing_field (
394+ "The role 0 certificate must have at least one stake address" ,
395+ context,
396+ ) ;
397+ }
361398 } else {
362399 // For other roles there still must be exactly one certificate at 0 index, but it must
363400 // have the `undefined` value.
0 commit comments