@@ -1253,11 +1253,60 @@ impl HostExports {
12531253 }
12541254}
12551255
1256- fn string_to_h160 ( string : & str ) -> Result < H160 , DeterministicHostError > {
1256+ pub fn string_to_h160 ( string : & str ) -> Result < H160 , DeterministicHostError > {
1257+ // Enhanced validation and error reporting for issue #6067
1258+ if string. is_empty ( ) {
1259+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1260+ "Address string is empty - this indicates a string corruption bug. Please report this issue."
1261+ ) ) ) ;
1262+ }
1263+
1264+ if string == "0x" {
1265+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1266+ "Address string is just '0x' - this indicates a string corruption bug. Please report this issue."
1267+ ) ) ) ;
1268+ }
1269+
1270+ if string. len ( ) < 3 {
1271+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1272+ "Address string too short: '{}' (length: {}, bytes: {:?}) - this may indicate a string corruption bug." ,
1273+ string, string. len( ) , string. as_bytes( )
1274+ ) ) ) ;
1275+ }
1276+
12571277 // `H160::from_str` takes a hex string with no leading `0x`.
12581278 let s = string. trim_start_matches ( "0x" ) ;
1279+
1280+ // Additional validation
1281+ if s. is_empty ( ) {
1282+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1283+ "Empty address string after trimming 0x: original='{}' (length: {}) - this indicates a string corruption bug." ,
1284+ string, string. len( )
1285+ ) ) ) ;
1286+ }
1287+
1288+ if s. len ( ) != 40 {
1289+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1290+ "Invalid address length: expected 40 hex chars, got {} chars: '{}' (original: '{}' length: {}). Valid Ethereum addresses must be exactly 40 hex characters after the 0x prefix." ,
1291+ s. len( ) , s, string, string. len( )
1292+ ) ) ) ;
1293+ }
1294+
1295+ // Validate hex characters
1296+ if !s. chars ( ) . all ( |c| c. is_ascii_hexdigit ( ) ) {
1297+ return Err ( DeterministicHostError :: from ( anyhow:: anyhow!(
1298+ "Address contains invalid hex characters: '{}' (original: '{}'). Only 0-9 and a-f (case insensitive) are allowed." ,
1299+ s, string
1300+ ) ) ) ;
1301+ }
1302+
12591303 H160 :: from_str ( s)
1260- . with_context ( || format ! ( "Failed to convert string to Address/H160: '{}'" , s) )
1304+ . with_context ( || {
1305+ format ! (
1306+ "Failed to convert string to Address/H160: '{}' (original: '{}')" ,
1307+ s, string
1308+ )
1309+ } )
12611310 . map_err ( DeterministicHostError :: from)
12621311}
12631312
@@ -1358,6 +1407,118 @@ fn test_string_to_h160_with_0x() {
13581407 )
13591408}
13601409
1410+ #[ test]
1411+ fn test_problematic_address_from_issue_6067 ( ) {
1412+ // This is the address reported in issue #6067 that causes string corruption
1413+ let address_str = "0x3b44b2a187a7b3824131f8db5a74194d0a42fc15" ;
1414+
1415+ // Test the string_to_h160 function directly
1416+ let result = string_to_h160 ( address_str) ;
1417+
1418+ // Should successfully parse
1419+ assert ! ( result. is_ok( ) , "Failed to parse address: {:?}" , result) ;
1420+
1421+ let h160 = result. unwrap ( ) ;
1422+
1423+ // The expected address
1424+ let expected = H160 :: from_str ( "3b44b2a187a7b3824131f8db5a74194d0a42fc15" ) . unwrap ( ) ;
1425+ assert_eq ! ( h160, expected, "Address mismatch" ) ;
1426+ }
1427+
1428+ #[ test]
1429+ fn test_address_with_nulls ( ) {
1430+ // Test what happens if we have null characters in the address string
1431+ let address_with_nulls = "0x3b44b2a187a7b3824131f8db5a\0 \0 194d0a42fc15" ;
1432+
1433+ // First simulate what the FromAscObj does - remove nulls
1434+ let processed = address_with_nulls. replace ( '\u{0000}' , "" ) ;
1435+ println ! (
1436+ "After null removal: '{}' (length: {})" ,
1437+ processed,
1438+ processed. len( )
1439+ ) ;
1440+
1441+ let result = string_to_h160 ( & processed) ;
1442+
1443+ // This should work if nulls are just removed cleanly
1444+ if result. is_ok ( ) {
1445+ println ! ( "Unexpectedly succeeded after null removal" ) ;
1446+ } else {
1447+ println ! ( "Failed after null removal: {}" , result. unwrap_err( ) ) ;
1448+ }
1449+ }
1450+
1451+ #[ test]
1452+ fn test_empty_after_null_removal ( ) {
1453+ // Test what happens if string becomes empty after null removal
1454+ // This simulates what might happen in the FromAscObj conversion
1455+ let empty_str = "" ;
1456+ let result = string_to_h160 ( empty_str) ;
1457+
1458+ assert ! ( result. is_err( ) , "Should fail with empty string" ) ;
1459+ let error = result. unwrap_err ( ) ;
1460+ assert ! (
1461+ error. to_string( ) . contains( "string corruption bug" ) ,
1462+ "Error should mention string corruption"
1463+ ) ;
1464+
1465+ // Test with just "0x"
1466+ let just_0x = "0x" ;
1467+ let result2 = string_to_h160 ( just_0x) ;
1468+ assert ! ( result2. is_err( ) , "Should fail with just 0x" ) ;
1469+ assert ! (
1470+ result2
1471+ . unwrap_err( )
1472+ . to_string( )
1473+ . contains( "string corruption bug" ) ,
1474+ "Error should mention string corruption"
1475+ ) ;
1476+ }
1477+
1478+ #[ test]
1479+ fn test_comprehensive_address_scenarios ( ) {
1480+ // Test all the scenarios that could lead to the reported bug
1481+
1482+ // 1. Valid address - should work
1483+ let valid = "0x3b44b2a187a7b3824131f8db5a74194d0a42fc15" ;
1484+ assert ! ( string_to_h160( valid) . is_ok( ) , "Valid address should work" ) ;
1485+
1486+ // 2. Valid address without 0x - should work
1487+ let no_prefix = "3b44b2a187a7b3824131f8db5a74194d0a42fc15" ;
1488+ assert ! (
1489+ string_to_h160( no_prefix) . is_ok( ) ,
1490+ "Address without 0x should work"
1491+ ) ;
1492+
1493+ // 3. Address with wrong length - should fail with helpful message
1494+ let too_short = "0x3b44b2a187a7b3824131f8db5a" ;
1495+ let result = string_to_h160 ( too_short) ;
1496+ assert ! ( result. is_err( ) , "Too short address should fail" ) ;
1497+ assert ! (
1498+ result
1499+ . unwrap_err( )
1500+ . to_string( )
1501+ . contains( "expected 40 hex chars" ) ,
1502+ "Should mention expected length"
1503+ ) ;
1504+
1505+ // 4. Address with invalid characters - should fail
1506+ let invalid_chars = "0x3b44b2a187a7b3824131f8db5a74194d0a42fZZZ" ;
1507+ let result2 = string_to_h160 ( invalid_chars) ;
1508+ assert ! ( result2. is_err( ) , "Invalid hex chars should fail" ) ;
1509+ assert ! (
1510+ result2
1511+ . unwrap_err( )
1512+ . to_string( )
1513+ . contains( "invalid hex characters" ) ,
1514+ "Should mention invalid hex"
1515+ ) ;
1516+
1517+ // 5. Mixed case - should work
1518+ let mixed_case = "0x3B44B2A187a7b3824131f8db5a74194d0a42fc15" ;
1519+ assert ! ( string_to_h160( mixed_case) . is_ok( ) , "Mixed case should work" ) ;
1520+ }
1521+
13611522#[ test]
13621523fn bytes_to_string_is_lossy ( ) {
13631524 assert_eq ! (
0 commit comments