@@ -8,7 +8,7 @@ use host_listener::database::tfhe_event_propagate::{
88} ;
99use opentelemetry:: trace:: { SpanId , TracerProvider } ;
1010use opentelemetry_sdk:: trace:: { InMemorySpanExporter , SdkTracerProvider , SpanData } ;
11- use scheduler:: dfg:: pattern:: { self , PatternInput } ;
11+ use scheduler:: dfg:: pattern;
1212use serial_test:: serial;
1313use tracing_subscriber:: layer:: SubscriberExt ;
1414
@@ -587,8 +587,10 @@ async fn test_erc20_transaction_pattern_ids() -> Result<(), Box<dyn std::error::
587587
588588 // ── Assertion 4: encodings are decodable and structurally correct ─
589589 //
590- // Pattern IDs in span attributes are base64url-encoded binary
591- // encodings. Decode them and verify the actual DFG structure.
590+ // Pattern IDs are base64url-encoded. Verify they decode to valid
591+ // DFG descriptions with the expected node counts and opcode sets.
592+ // (Detailed structural checks on topo order and internal refs are
593+ // covered by the unit tests in scheduler/src/dfg/pattern/tests.rs.)
592594
593595 let decode_b64 = |b64 : & str | -> pattern:: PatternDescription {
594596 let bytes = URL_SAFE_NO_PAD
@@ -601,129 +603,60 @@ async fn test_erc20_transaction_pattern_ids() -> Result<(), Box<dyn std::error::
601603 . unwrap_or_else ( || panic ! ( "malformed pattern encoding '{b64}'" ) )
602604 } ;
603605
604- // -- Whitepaper single-transfer encoding --
605- //
606- // Expected DFG (5 nodes in topo order):
607- // [0] FheGe(ext, ext)
608- // [1] FheAdd(ext, ext)
609- // [2] FheIfThenElse(ref0, ref1, ext) <- is_allowed
610- // [3] FheSub(ext, ext)
611- // [4] FheIfThenElse(ref0, ref3, ext) <- is_allowed
606+ let sorted_opcodes = |desc : & pattern:: PatternDescription | -> Vec < & str > {
607+ let mut ops: Vec < & str > = desc. nodes . iter ( ) . map ( |n| n. opcode_name ) . collect ( ) ;
608+ ops. sort ( ) ;
609+ ops
610+ } ;
611+
612+ // Whitepaper: 5 nodes — GE, Add, Sub, 2×IfThenElse
612613 let wp = decode_b64 ( & p_single_tx) ;
613614 assert_eq ! ( wp. nodes. len( ) , 5 , "whitepaper pattern should have 5 nodes" ) ;
614-
615- let wp_opcodes: Vec < & str > = wp. nodes . iter ( ) . map ( |n| n. opcode_name ) . collect ( ) ;
616615 assert_eq ! (
617- wp_opcodes ,
616+ sorted_opcodes ( & wp ) ,
618617 & [
619- "FHE_GE" ,
620618 "FHE_ADD" ,
619+ "FHE_GE" ,
620+ "FHE_IF_THEN_ELSE" ,
621621 "FHE_IF_THEN_ELSE" ,
622- "FHE_SUB" ,
623- "FHE_IF_THEN_ELSE"
622+ "FHE_SUB"
624623 ] ,
625- "whitepaper opcode sequence"
624+ "whitepaper opcode set"
625+ ) ;
626+ assert_eq ! (
627+ wp. nodes. iter( ) . filter( |n| n. is_allowed) . count( ) ,
628+ 2 ,
629+ "whitepaper should have 2 allowed outputs"
626630 ) ;
627-
628- // FheGe and FheAdd: 2 external inputs each
629- assert ! ( wp. nodes[ 0 ]
630- . inputs
631- . iter( )
632- . all( |i| matches!( i, PatternInput :: External ) ) ) ;
633- assert ! ( wp. nodes[ 1 ]
634- . inputs
635- . iter( )
636- . all( |i| matches!( i, PatternInput :: External ) ) ) ;
637-
638- // First FheIfThenElse: refs GE(0) and ADD(1), plus external
639- assert ! ( matches!( wp. nodes[ 2 ] . inputs[ 0 ] , PatternInput :: Internal ( 0 ) ) ) ;
640- assert ! ( matches!( wp. nodes[ 2 ] . inputs[ 1 ] , PatternInput :: Internal ( 1 ) ) ) ;
641- assert ! ( matches!( wp. nodes[ 2 ] . inputs[ 2 ] , PatternInput :: External ) ) ;
642- assert ! ( wp. nodes[ 2 ] . is_allowed) ;
643-
644- // FheSub: 2 external inputs
645- assert ! ( wp. nodes[ 3 ]
646- . inputs
647- . iter( )
648- . all( |i| matches!( i, PatternInput :: External ) ) ) ;
649-
650- // Second FheIfThenElse: refs GE(0) and SUB(3), plus external
651- assert ! ( matches!( wp. nodes[ 4 ] . inputs[ 0 ] , PatternInput :: Internal ( 0 ) ) ) ;
652- assert ! ( matches!( wp. nodes[ 4 ] . inputs[ 1 ] , PatternInput :: Internal ( 3 ) ) ) ;
653- assert ! ( matches!( wp. nodes[ 4 ] . inputs[ 2 ] , PatternInput :: External ) ) ;
654- assert ! ( wp. nodes[ 4 ] . is_allowed) ;
655-
656631 println ! ( "assertion 4a passed: whitepaper encoding = {wp}" ) ;
657632
658- // -- No-cmux single-transfer encoding --
659- //
660- // Expected DFG (5 nodes in topo order):
661- // [0] FheGe(ext, ext)
662- // [1] FheCast(ref0, ext)
663- // [2] FheMul(ext, ref1)
664- // [3] FheAdd(ext, ref2) <- is_allowed
665- // [4] FheSub(ext, ref2) <- is_allowed
633+ // No-cmux: 5 nodes — GE, Cast, Mul, Add, Sub
666634 let nc = decode_b64 ( & p_nocmux_tx) ;
667635 assert_eq ! ( nc. nodes. len( ) , 5 , "no-cmux pattern should have 5 nodes" ) ;
668-
669- let nc_opcodes: Vec < & str > = nc. nodes . iter ( ) . map ( |n| n. opcode_name ) . collect ( ) ;
670636 assert_eq ! (
671- nc_opcodes,
672- & [ "FHE_GE" , "FHE_CAST" , "FHE_MUL" , "FHE_ADD" , "FHE_SUB" ] ,
673- "no-cmux opcode sequence"
637+ sorted_opcodes( & nc) ,
638+ & [ "FHE_ADD" , "FHE_CAST" , "FHE_GE" , "FHE_MUL" , "FHE_SUB" ] ,
639+ "no-cmux opcode set"
640+ ) ;
641+ assert_eq ! (
642+ nc. nodes. iter( ) . filter( |n| n. is_allowed) . count( ) ,
643+ 2 ,
644+ "no-cmux should have 2 allowed outputs"
674645 ) ;
675-
676- // FheGe: 2 external inputs
677- assert ! ( nc. nodes[ 0 ]
678- . inputs
679- . iter( )
680- . all( |i| matches!( i, PatternInput :: External ) ) ) ;
681-
682- // FheCast: refs GE(0), plus scalar (external)
683- assert ! ( matches!( nc. nodes[ 1 ] . inputs[ 0 ] , PatternInput :: Internal ( 0 ) ) ) ;
684- assert ! ( matches!( nc. nodes[ 1 ] . inputs[ 1 ] , PatternInput :: External ) ) ;
685-
686- // FheMul: external + ref Cast(1)
687- assert ! ( matches!( nc. nodes[ 2 ] . inputs[ 0 ] , PatternInput :: External ) ) ;
688- assert ! ( matches!( nc. nodes[ 2 ] . inputs[ 1 ] , PatternInput :: Internal ( 1 ) ) ) ;
689-
690- // FheAdd: external + ref Mul(2), is_allowed
691- assert ! ( matches!( nc. nodes[ 3 ] . inputs[ 0 ] , PatternInput :: External ) ) ;
692- assert ! ( matches!( nc. nodes[ 3 ] . inputs[ 1 ] , PatternInput :: Internal ( 2 ) ) ) ;
693- assert ! ( nc. nodes[ 3 ] . is_allowed) ;
694-
695- // FheSub: external + ref Mul(2), is_allowed
696- assert ! ( matches!( nc. nodes[ 4 ] . inputs[ 0 ] , PatternInput :: External ) ) ;
697- assert ! ( matches!( nc. nodes[ 4 ] . inputs[ 1 ] , PatternInput :: Internal ( 2 ) ) ) ;
698- assert ! ( nc. nodes[ 4 ] . is_allowed) ;
699-
700646 println ! ( "assertion 4b passed: no-cmux encoding = {nc}" ) ;
701647
702- // -- Triple-transfer transaction-level encoding --
703- //
704- // The transaction_pattern_id encodes the full 15-op graph.
648+ // Triple: 15 nodes (3× whitepaper)
705649 let triple = decode_b64 ( & p_triple_tx) ;
706650 assert_eq ! (
707651 triple. nodes. len( ) ,
708652 15 ,
709653 "triple transfer tx pattern should have 15 nodes"
710654 ) ;
711- // All 15 nodes should use the same opcodes as 3 whitepaper transfers
712- let triple_opcodes: Vec < & str > = triple. nodes . iter ( ) . map ( |n| n. opcode_name ) . collect ( ) ;
713- for chunk in triple_opcodes. chunks ( 5 ) {
714- assert_eq ! (
715- chunk,
716- & [
717- "FHE_GE" ,
718- "FHE_ADD" ,
719- "FHE_IF_THEN_ELSE" ,
720- "FHE_SUB" ,
721- "FHE_IF_THEN_ELSE"
722- ] ,
723- "each 5-op chunk in triple tx should match whitepaper opcode sequence"
724- ) ;
725- }
726-
655+ assert_eq ! (
656+ triple. nodes. iter( ) . filter( |n| n. is_allowed) . count( ) ,
657+ 6 ,
658+ "triple should have 6 allowed outputs"
659+ ) ;
727660 println ! ( "assertion 4c passed: triple tx encoding = {triple}" ) ;
728661 } else {
729662 println ! ( "skipping span assertions: global subscriber already set by another test" ) ;
0 commit comments