@@ -306,7 +306,11 @@ func TestConwayTransactionBodyRejectsDuplicateTaggedSetFields(t *testing.T) {
306306 }
307307}
308308
309- func TestConwayWitnessSetRejectsDuplicateTaggedVkeyWitness (t * testing.T ) {
309+ // Conway (protocol versions 9-11) tolerates duplicate vkey witnesses:
310+ // cardano-ledger decodes them via Set.fromList and only begins rejecting
311+ // duplicates at protocol version 12 (Dijkstra). Rejecting at decode in Conway
312+ // wrongly rejects valid historical blocks (issue #1853).
313+ func TestConwayWitnessSetToleratesDuplicateTaggedVkeyWitness (t * testing.T ) {
310314 dupCbor := []byte {
311315 0xa1 , // map(1)
312316 0x00 , // key: 0 (VkeyWitnesses field)
@@ -316,11 +320,100 @@ func TestConwayWitnessSetRejectsDuplicateTaggedVkeyWitness(t *testing.T) {
316320 0x82 , 0x41 , 0x01 , 0x41 , 0x02 , // duplicate
317321 }
318322
323+ var ws ConwayTransactionWitnessSet
324+ err := ws .UnmarshalCBOR (dupCbor )
325+ assert .NoError (t , err ,
326+ "Conway must tolerate duplicate vkey witnesses (dedup at decode, matching cardano-ledger pv 9-11)" )
327+ }
328+
329+ func TestConwayWitnessSetToleratesDuplicateTaggedWitnessSetFields (t * testing.T ) {
330+ tests := []struct {
331+ name string
332+ field byte
333+ member []byte
334+ }{
335+ {
336+ name : "bootstrap witnesses" ,
337+ field : 0x02 ,
338+ member : []byte {
339+ 0x84 , // BootstrapWitness
340+ 0x41 , 0x01 , 0x41 , 0x02 , // public key, signature
341+ 0x41 , 0x03 , 0x41 , 0x04 , // chain code, attributes
342+ },
343+ },
344+ {
345+ name : "native scripts" ,
346+ field : 0x01 ,
347+ member : []byte {0x82 , 0x00 , 0x41 , 0x01 }, // pubkey script
348+ },
349+ {
350+ name : "plutus data" ,
351+ field : 0x04 ,
352+ member : []byte {0x00 }, // integer datum
353+ },
354+ }
355+ for _ , tt := range tests {
356+ t .Run (tt .name , func (t * testing.T ) {
357+ var ws ConwayTransactionWitnessSet
358+ err := ws .UnmarshalCBOR (
359+ duplicateTaggedWitnessSetFieldCbor (tt .field , tt .member ),
360+ )
361+ assert .NoError (t , err )
362+ })
363+ }
364+ }
365+
366+ // Plutus script sets reject duplicates from protocol version 9 (cardano-ledger
367+ // scriptDecoderV9), so Conway must still reject them at decode.
368+ func TestConwayWitnessSetRejectsDuplicateTaggedPlutusV1Script (t * testing.T ) {
369+ dupCbor := []byte {
370+ 0xa1 , // map(1)
371+ 0x03 , // key: 3 (WsPlutusV1Scripts field)
372+ 0xd9 , 0x01 , 0x02 , // tag(258) - CBOR set
373+ 0x82 , // array(2)
374+ 0x41 , 0x01 , // PlutusV1Script [0x01]
375+ 0x41 , 0x01 , // duplicate
376+ }
377+
319378 var ws ConwayTransactionWitnessSet
320379 err := ws .UnmarshalCBOR (dupCbor )
321380 assert .ErrorContains (t , err , "duplicate member in set" )
322381}
323382
383+ func TestConwayWitnessSetRejectsDuplicateTaggedPlutusV2AndV3Scripts (t * testing.T ) {
384+ tests := []struct {
385+ name string
386+ field byte
387+ }{
388+ {name : "Plutus V2" , field : 0x06 },
389+ {name : "Plutus V3" , field : 0x07 },
390+ }
391+ for _ , tt := range tests {
392+ t .Run (tt .name , func (t * testing.T ) {
393+ var ws ConwayTransactionWitnessSet
394+ err := ws .UnmarshalCBOR (
395+ duplicateTaggedWitnessSetFieldCbor (
396+ tt .field ,
397+ []byte {0x41 , 0x01 },
398+ ),
399+ )
400+ assert .ErrorContains (t , err , "duplicate member in set" )
401+ })
402+ }
403+ }
404+
405+ func duplicateTaggedWitnessSetFieldCbor (field byte , member []byte ) []byte {
406+ ret := []byte {
407+ 0xa1 , // map(1)
408+ field , // witness set field key
409+ 0xd9 , 0x01 , 0x02 , // tag(258) - CBOR set
410+ 0x82 , // array(2)
411+ }
412+ ret = append (ret , member ... )
413+ ret = append (ret , member ... )
414+ return ret
415+ }
416+
324417func testConwayShelleyInput () shelley.ShelleyTransactionInput {
325418 var txId common.Blake2b256
326419 txId [0 ] = 1
0 commit comments