@@ -78,6 +78,7 @@ public static void destroy() {
7878 VMConfig .initAllowTvmIstanbul (0 );
7979 VMConfig .initAllowTvmLondon (0 );
8080 VMConfig .initAllowTvmCompatibleEvm (0 );
81+ VMConfig .initAllowTvmOsaka (0 );
8182 }
8283
8384 @ Test
@@ -905,6 +906,128 @@ public void testPush0() throws ContractValidateException {
905906 VMConfig .initAllowTvmShangHai (0 );
906907 }
907908
909+ @ Test
910+ public void testCLZ () throws ContractValidateException {
911+ VMConfig .initAllowTvmOsaka (1 );
912+
913+ try {
914+ invoke = new ProgramInvokeMockImpl ();
915+ Protocol .Transaction trx = Protocol .Transaction .getDefaultInstance ();
916+ InternalTransaction interTrx =
917+ new InternalTransaction (trx , InternalTransaction .TrxType .TRX_UNKNOWN_TYPE );
918+
919+ // CLZ(0) = 256
920+ byte [] op = buildCLZBytecode (new byte [32 ]);
921+ program = new Program (op , op , invoke , interTrx );
922+ testOperations (program );
923+ Assert .assertEquals (new DataWord (256 ), program .getStack ().pop ());
924+
925+ // CLZ(0x80...00) = 0 (highest bit set)
926+ byte [] val = new byte [32 ];
927+ val [0 ] = (byte ) 0x80 ;
928+ op = buildCLZBytecode (val );
929+ program = new Program (op , op , invoke , interTrx );
930+ testOperations (program );
931+ Assert .assertEquals (new DataWord (0 ), program .getStack ().pop ());
932+
933+ // CLZ(0xFF...FF) = 0
934+ val = new byte [32 ];
935+ for (int i = 0 ; i < 32 ; i ++) {
936+ val [i ] = (byte ) 0xFF ;
937+ }
938+ op = buildCLZBytecode (val );
939+ program = new Program (op , op , invoke , interTrx );
940+ testOperations (program );
941+ Assert .assertEquals (new DataWord (0 ), program .getStack ().pop ());
942+
943+ // CLZ(0x40...00) = 1
944+ val = new byte [32 ];
945+ val [0 ] = (byte ) 0x40 ;
946+ op = buildCLZBytecode (val );
947+ program = new Program (op , op , invoke , interTrx );
948+ testOperations (program );
949+ Assert .assertEquals (new DataWord (1 ), program .getStack ().pop ());
950+
951+ // CLZ(0x7F...FF) = 1
952+ val = new byte [32 ];
953+ for (int i = 0 ; i < 32 ; i ++) {
954+ val [i ] = (byte ) 0xFF ;
955+ }
956+ val [0 ] = (byte ) 0x7F ;
957+ op = buildCLZBytecode (val );
958+ program = new Program (op , op , invoke , interTrx );
959+ testOperations (program );
960+ Assert .assertEquals (new DataWord (1 ), program .getStack ().pop ());
961+
962+ // CLZ(1) = 255
963+ val = new byte [32 ];
964+ val [31 ] = 0x01 ;
965+ op = buildCLZBytecode (val );
966+ program = new Program (op , op , invoke , interTrx );
967+ testOperations (program );
968+ Assert .assertEquals (new DataWord (255 ), program .getStack ().pop ());
969+
970+ // Vectors with CLZ in [128, 254] — exercise the (byte) cast path in
971+ // DataWord.of(byte) where the unsigned int would otherwise become a
972+ // negative byte. Read-back goes through new BigInteger(1, data), so the
973+ // bit pattern must round-trip as unsigned.
974+ // CLZ = 128 (boundary): byte[16] high bit set
975+ val = new byte [32 ];
976+ val [16 ] = (byte ) 0x80 ;
977+ op = buildCLZBytecode (val );
978+ program = new Program (op , op , invoke , interTrx );
979+ testOperations (program );
980+ Assert .assertEquals (new DataWord (128 ), program .getStack ().pop ());
981+
982+ // CLZ = 192 (mid-range): byte[24] high bit set
983+ val = new byte [32 ];
984+ val [24 ] = (byte ) 0x80 ;
985+ op = buildCLZBytecode (val );
986+ program = new Program (op , op , invoke , interTrx );
987+ testOperations (program );
988+ Assert .assertEquals (new DataWord (192 ), program .getStack ().pop ());
989+
990+ // CLZ = 247 (near-upper): 30 zero bytes, then 0x01
991+ val = new byte [32 ];
992+ val [30 ] = 0x01 ;
993+ op = buildCLZBytecode (val );
994+ program = new Program (op , op , invoke , interTrx );
995+ testOperations (program );
996+ Assert .assertEquals (new DataWord (247 ), program .getStack ().pop ());
997+
998+ // Verify energy cost = LOW_TIER(5) + PUSH32 cost(3) = 8
999+ Assert .assertEquals (8 , program .getResult ().getEnergyUsed ());
1000+ } finally {
1001+ VMConfig .initAllowTvmOsaka (0 );
1002+ }
1003+ }
1004+
1005+ @ Test
1006+ public void testCLZRejectedWhenOsakaDisabled () throws ContractValidateException {
1007+ VMConfig .initAllowTvmOsaka (0 );
1008+
1009+ invoke = new ProgramInvokeMockImpl ();
1010+ Protocol .Transaction trx = Protocol .Transaction .getDefaultInstance ();
1011+ InternalTransaction interTrx =
1012+ new InternalTransaction (trx , InternalTransaction .TrxType .TRX_UNKNOWN_TYPE );
1013+
1014+ byte [] op = buildCLZBytecode (new byte [32 ]);
1015+ program = new Program (op , op , invoke , interTrx );
1016+ testOperations (program );
1017+
1018+ Assert .assertTrue (program .getResult ().getException ()
1019+ instanceof Program .IllegalOperationException );
1020+ }
1021+
1022+ // Build bytecode: PUSH32 <value> CLZ
1023+ private byte [] buildCLZBytecode (byte [] value ) {
1024+ byte [] op = new byte [34 ];
1025+ op [0 ] = 0x7f ; // PUSH32
1026+ System .arraycopy (value , 0 , op , 1 , 32 );
1027+ op [33 ] = Op .CLZ ;
1028+ return op ;
1029+ }
1030+
9081031 @ Test
9091032 public void testSuicideCost () throws ContractValidateException {
9101033 invoke = new ProgramInvokeMockImpl (StoreFactory .getInstance (), new byte [0 ], new byte [21 ]);
0 commit comments