@@ -651,7 +651,176 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
651651 args : & [ BasicMetadataValueEnum < ' a > ] ,
652652 first_arg_type : Option < BasicTypeEnum > ,
653653 ) -> Option < BasicValueEnum < ' a > > {
654- unimplemented ! ( )
654+ emit_context ! ( bin) ;
655+
656+ assert_eq ! ( 5 , args. len( ) ) ;
657+
658+ let [ hash, v, r, s, res] = args else {
659+ panic ! ( ) ;
660+ } ;
661+
662+ // Construct the precompile address (0x0000000000000000000000000000000000000001)
663+ // Address is stored in big-endian, so 0x01 goes in the last byte
664+ let contract = bin
665+ . builder
666+ . build_alloca ( bin. value_type ( ) , "contract" )
667+ . unwrap ( ) ;
668+ // Initialize address to zero
669+ bin. builder
670+ . build_store ( contract, bin. address_type ( ) . const_zero ( ) )
671+ . unwrap ( ) ;
672+ // Set last byte to 0x01 by casting to byte pointer
673+ let contract_byte_ptr = bin
674+ . builder
675+ . build_pointer_cast (
676+ contract,
677+ bin. context . ptr_type ( AddressSpace :: default ( ) ) ,
678+ "contract_byte_ptr" ,
679+ )
680+ . unwrap ( ) ;
681+ bin. builder
682+ . build_store (
683+ ptr_plus_offset (
684+ bin,
685+ contract_byte_ptr,
686+ bin. context . i32_type ( ) . const_int ( 19 as u64 , false ) ,
687+ ) ,
688+ bin. context . i8_type ( ) . const_int ( 0x01 , false ) ,
689+ )
690+ . unwrap ( ) ;
691+
692+ // Construct calldata: hash (32 bytes) + v (1 byte encoded as 32 bytes) + r (32 bytes) + s (32 bytes)
693+ // ecrecover expects big-endian format, so we need to byte-swap hash, v, r, and s
694+ let calldata = bin
695+ . builder
696+ . build_array_alloca (
697+ bin. context . i8_type ( ) ,
698+ i32_const ! ( 32 + 32 + 32 + 32 as u64 ) ,
699+ "buffer" ,
700+ )
701+ . unwrap ( ) ;
702+
703+ // Convert hash from little-endian to big-endian
704+ let hash_be = bin
705+ . builder
706+ . build_alloca ( bin. value_type ( ) , "hash_be" )
707+ . unwrap ( ) ;
708+ let hash_ptr = bin
709+ . builder
710+ . build_alloca ( bin. value_type ( ) , "hash_ptr" )
711+ . unwrap ( ) ;
712+ bin. builder
713+ . build_store ( hash_ptr, hash. into_int_value ( ) )
714+ . unwrap ( ) ;
715+ call ! (
716+ "__leNtobeN" ,
717+ & [ hash_ptr. into( ) , hash_be. into( ) , i32_const!( 32 ) . into( ) ]
718+ ) ;
719+ call ! (
720+ "__memcpy" ,
721+ & [ calldata. into( ) , hash_be. into( ) , i32_const!( 32 ) . into( ) ]
722+ ) ;
723+
724+ // Convert v from little-endian to big-endian
725+ let v_be = bin. builder . build_alloca ( bin. value_type ( ) , "v_be" ) . unwrap ( ) ;
726+ let v_ptr = bin. builder . build_alloca ( bin. value_type ( ) , "v_ptr" ) . unwrap ( ) ;
727+ bin. builder . build_store ( v_ptr, v. into_int_value ( ) ) . unwrap ( ) ;
728+ call ! (
729+ "__leNtobeN" ,
730+ & [ v_ptr. into( ) , v_be. into( ) , i32_const!( 32 ) . into( ) ]
731+ ) ;
732+ call ! (
733+ "__memcpy" ,
734+ & [
735+ ptr_plus_offset( bin, calldata, i32_const!( 32 ) ) . into( ) ,
736+ v_be. into( ) ,
737+ i32_const!( 32 ) . into( )
738+ ]
739+ ) ;
740+
741+ // Convert r from little-endian to big-endian
742+ let r_be = bin. builder . build_alloca ( bin. value_type ( ) , "r_be" ) . unwrap ( ) ;
743+ let r_ptr = bin. builder . build_alloca ( bin. value_type ( ) , "r_ptr" ) . unwrap ( ) ;
744+ bin. builder . build_store ( r_ptr, r. into_int_value ( ) ) . unwrap ( ) ;
745+ call ! (
746+ "__leNtobeN" ,
747+ & [ r_ptr. into( ) , r_be. into( ) , i32_const!( 32 ) . into( ) ]
748+ ) ;
749+ call ! (
750+ "__memcpy" ,
751+ & [
752+ ptr_plus_offset( bin, calldata, i32_const!( 64 ) ) . into( ) ,
753+ r_be. into( ) ,
754+ i32_const!( 32 ) . into( )
755+ ]
756+ ) ;
757+
758+ // Convert s from little-endian to big-endian
759+ let s_be = bin. builder . build_alloca ( bin. value_type ( ) , "s_be" ) . unwrap ( ) ;
760+ let s_ptr = bin. builder . build_alloca ( bin. value_type ( ) , "s_ptr" ) . unwrap ( ) ;
761+ bin. builder . build_store ( s_ptr, s. into_int_value ( ) ) . unwrap ( ) ;
762+ call ! (
763+ "__leNtobeN" ,
764+ & [ s_ptr. into( ) , s_be. into( ) , i32_const!( 32 ) . into( ) ]
765+ ) ;
766+ call ! (
767+ "__memcpy" ,
768+ & [
769+ ptr_plus_offset( bin, calldata, i32_const!( 96 ) ) . into( ) ,
770+ s_be. into( ) ,
771+ i32_const!( 32 ) . into( )
772+ ]
773+ ) ;
774+
775+ let calldata_len = i32_const ! ( 32 + 32 + 32 + 32 ) ;
776+
777+ let gas = i64_const ! ( i32 :: MAX as u64 ) ;
778+
779+ let return_data_len = bin
780+ . builder
781+ . build_alloca ( bin. llvm_type ( & Type :: Uint ( 32 ) ) , "return_data_len" )
782+ . unwrap ( ) ;
783+
784+ call ! (
785+ "static_call_contract" ,
786+ & [
787+ contract. into( ) ,
788+ calldata. into( ) ,
789+ calldata_len. into( ) ,
790+ gas. into( ) ,
791+ return_data_len. into( )
792+ ] ,
793+ "static_call_contract"
794+ ) ;
795+
796+ // ecrecover returns 32 bytes with the address right-aligned (last 20 bytes)
797+ let return_data_buffer = bin
798+ . builder
799+ . build_array_alloca ( bin. context . i8_type ( ) , i32_const ! ( 32 ) , "return_data_buffer" )
800+ . unwrap ( ) ;
801+
802+ call ! (
803+ "read_return_data" ,
804+ & [
805+ return_data_buffer. into( ) ,
806+ i32_zero!( ) . into( ) ,
807+ i32_const!( 32 ) . into( )
808+ ] ,
809+ "read_return_data"
810+ ) ;
811+
812+ // Skip the first 12 bytes of padding to get the 20-byte address
813+ let address_ptr = ptr_plus_offset ( bin, return_data_buffer, i32_const ! ( 12 ) ) ;
814+ let signer = bin
815+ . builder
816+ . build_load ( bin. address_type ( ) , address_ptr, "signer" )
817+ . unwrap ( ) ;
818+
819+ bin. builder
820+ . build_store ( res. into_pointer_value ( ) , signer)
821+ . unwrap ( ) ;
822+
823+ None
655824 }
656825
657826 /// Calls constructor
@@ -1545,3 +1714,28 @@ mod local {
15451714 . unwrap ( )
15461715 }
15471716}
1717+
1718+ // smoelius: `dump_array` is useful for debugging.
1719+ #[ allow( dead_code) ]
1720+ fn dump_array < ' a , T : TargetRuntime < ' a > > (
1721+ target : & T ,
1722+ bin : & Binary < ' a > ,
1723+ array : BasicValueEnum < ' a > ,
1724+ array_len : u64 ,
1725+ function : FunctionValue < ' a > ,
1726+ ) {
1727+ emit_context ! ( bin) ;
1728+ let p = array. into_pointer_value ( ) ;
1729+ for i in 0 ..array_len {
1730+ let x = bin
1731+ . builder
1732+ . build_load (
1733+ bin. llvm_type ( & ast:: Type :: Uint ( 8 ) ) ,
1734+ ptr_plus_offset ( bin, p, i32_const ! ( i) ) ,
1735+ "x" ,
1736+ )
1737+ . unwrap ( )
1738+ . into_int_value ( ) ;
1739+ crate :: emit:: debug_value!( target, bin, Type :: Uint ( 8 ) , x, function) ;
1740+ }
1741+ }
0 commit comments