@@ -26,6 +26,7 @@ import (
2626)
2727
2828var UtxoValidationRules = []common.UtxoValidationRuleFunc {
29+ UtxoValidateMetadata ,
2930 UtxoValidateOutsideValidityIntervalUtxo ,
3031 UtxoValidateInputSetEmptyUtxo ,
3132 UtxoValidateFeeTooSmallUtxo ,
@@ -499,3 +500,46 @@ func MinCoinTxOut(
499500 minCoinTxOut := tmpPparams .AdaPerUtxoByte * uint64 (len (txOutBytes ))
500501 return minCoinTxOut , nil
501502}
503+
504+ // UtxoValidateMetadata validates that auxiliary data (metadata) matches the hash in transaction body
505+ // This is a cheap structural check that should run before expensive ledger lookups
506+ func UtxoValidateMetadata (
507+ tx common.Transaction ,
508+ slot uint64 ,
509+ ls common.LedgerState ,
510+ pp common.ProtocolParameters ,
511+ ) error {
512+ bodyAuxDataHash := tx .AuxDataHash ()
513+ txAuxData := tx .Metadata ()
514+
515+ // Case 1: Neither body hash nor aux data present - OK
516+ if bodyAuxDataHash == nil && txAuxData == nil {
517+ return nil
518+ }
519+
520+ // Case 2: Body has hash but no aux data provided - error
521+ if bodyAuxDataHash != nil && txAuxData == nil {
522+ return MissingTransactionMetadataError {
523+ Hash : * bodyAuxDataHash ,
524+ }
525+ }
526+
527+ // Case 3: Aux data provided but body has no hash - error
528+ if bodyAuxDataHash == nil && txAuxData != nil {
529+ actualHash := common .Blake2b256 (txAuxData .Cbor ())
530+ return MissingTransactionAuxiliaryDataHashError {
531+ Hash : actualHash ,
532+ }
533+ }
534+
535+ // Case 4: Both present - verify hash matches
536+ actualHash := common .Blake2b256 (txAuxData .Cbor ())
537+ if * bodyAuxDataHash != actualHash {
538+ return ConflictingMetadataHashError {
539+ Supplied : * bodyAuxDataHash ,
540+ Expected : actualHash ,
541+ }
542+ }
543+
544+ return nil
545+ }
0 commit comments