@@ -465,6 +465,119 @@ describe('Verify Transaction', function () {
465465 coinMock . restore ( ) ;
466466 } ) ;
467467
468+ describe ( 'quantum-resistant sweep (qr: true)' , function ( ) {
469+ it ( 'should reject when explicit external outputs are present' , async ( ) => {
470+ const coinMock = sinon . stub ( coin , 'parseTransaction' ) . resolves ( {
471+ keychains : { } as any ,
472+ keySignatures : { } ,
473+ outputs : [ ] ,
474+ missingOutputs : [ ] ,
475+ explicitExternalOutputs : [ { address : 'external_addr' , amount : '5000' } ] ,
476+ implicitExternalOutputs : [ ] ,
477+ changeOutputs : [ { address : 'change_addr' , amount : '4000' } ] ,
478+ explicitExternalSpendAmount : 5000 ,
479+ implicitExternalSpendAmount : 0 ,
480+ needsCustomChangeKeySignatureVerification : false ,
481+ } ) ;
482+
483+ await assert . rejects (
484+ coin . verifyTransaction ( {
485+ txParams : { walletPassphrase : passphrase , qr : true } ,
486+ txPrebuild : { } ,
487+ wallet : unsignedSendingWallet as any ,
488+ verification : { } ,
489+ } ) ,
490+ / q u a n t u m - r e s i s t a n t s w e e p t r a n s a c t i o n s m u s t o n l y c o n t a i n w a l l e t - i n t e r n a l o u t p u t s /
491+ ) ;
492+
493+ coinMock . restore ( ) ;
494+ } ) ;
495+
496+ it ( 'should reject when implicit external outputs are present' , async ( ) => {
497+ const coinMock = sinon . stub ( coin , 'parseTransaction' ) . resolves ( {
498+ keychains : { } as any ,
499+ keySignatures : { } ,
500+ outputs : [ ] ,
501+ missingOutputs : [ ] ,
502+ explicitExternalOutputs : [ ] ,
503+ implicitExternalOutputs : [ { address : 'paygo_addr' , amount : '100' } ] ,
504+ changeOutputs : [ { address : 'change_addr' , amount : '9900' } ] ,
505+ explicitExternalSpendAmount : 0 ,
506+ implicitExternalSpendAmount : 100 ,
507+ needsCustomChangeKeySignatureVerification : false ,
508+ } ) ;
509+
510+ await assert . rejects (
511+ coin . verifyTransaction ( {
512+ txParams : { walletPassphrase : passphrase , qr : true } ,
513+ txPrebuild : { } ,
514+ wallet : unsignedSendingWallet as any ,
515+ verification : { } ,
516+ } ) ,
517+ / q u a n t u m - r e s i s t a n t s w e e p t r a n s a c t i o n s m u s t o n l y c o n t a i n w a l l e t - i n t e r n a l o u t p u t s /
518+ ) ;
519+
520+ coinMock . restore ( ) ;
521+ } ) ;
522+
523+ it ( 'should pass when all outputs are internal (change only)' , async ( ) => {
524+ const coinMock = sinon . stub ( coin , 'parseTransaction' ) . resolves ( {
525+ keychains : { } as any ,
526+ keySignatures : { } ,
527+ outputs : [ { address : 'change_addr' , amount : '10000' } ] ,
528+ missingOutputs : [ ] ,
529+ explicitExternalOutputs : [ ] ,
530+ implicitExternalOutputs : [ ] ,
531+ changeOutputs : [ { address : 'change_addr' , amount : '10000' } ] ,
532+ explicitExternalSpendAmount : 0 ,
533+ implicitExternalSpendAmount : 0 ,
534+ needsCustomChangeKeySignatureVerification : false ,
535+ } ) ;
536+
537+ const result = await coin . verifyTransaction ( {
538+ txParams : { walletPassphrase : passphrase , qr : true } ,
539+ txPrebuild : { } ,
540+ wallet : unsignedSendingWallet as any ,
541+ verification : { } ,
542+ } ) ;
543+
544+ assert . strictEqual ( result , true ) ;
545+
546+ coinMock . restore ( ) ;
547+ } ) ;
548+
549+ it ( 'should not apply qr check when qr is not set' , async ( ) => {
550+ const coinMock = sinon . stub ( coin , 'parseTransaction' ) . resolves ( {
551+ keychains : { } as any ,
552+ keySignatures : { } ,
553+ outputs : [ ] ,
554+ missingOutputs : [ ] ,
555+ explicitExternalOutputs : [ { address : 'external_addr' , amount : '5000' } ] ,
556+ implicitExternalOutputs : [ ] ,
557+ changeOutputs : [ ] ,
558+ explicitExternalSpendAmount : 5000 ,
559+ implicitExternalSpendAmount : 0 ,
560+ needsCustomChangeKeySignatureVerification : false ,
561+ } ) ;
562+
563+ const bitcoinMock = sinon
564+ . stub ( coin , 'createTransactionFromHex' )
565+ . returns ( { ins : [ ] } as unknown as utxolib . bitgo . UtxoTransaction ) ;
566+
567+ const result = await coin . verifyTransaction ( {
568+ txParams : { walletPassphrase : passphrase } ,
569+ txPrebuild : { txHex : '00' } ,
570+ wallet : unsignedSendingWallet as any ,
571+ verification : { } ,
572+ } ) ;
573+
574+ assert . strictEqual ( result , true ) ;
575+
576+ coinMock . restore ( ) ;
577+ bitcoinMock . restore ( ) ;
578+ } ) ;
579+ } ) ;
580+
468581 it ( 'should work with bigint amounts' , async ( ) => {
469582 // need a coin that uses bigint
470583 const bigintCoin = getUtxoCoin ( 'tdoge' ) ;
0 commit comments