Repository: stellar/rs-soroban-env
Pull request #1273 (2023-12-01 17:14:44)

Change `string_new_from_slice` to use `&[u8]` instead of `&str`.
### What

Change `string_new_from_slice` to use `&[u8]` instead of `&str`.

Resolves https://github.com/stellar/rs-soroban-env/issues/1188

### Why

`String` host type doesn't provide encoding guarantees and usually is encoded is `&[u8]`; this function is inconsistent with the rest of the API.

### Known limitations

N/A



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 87c395a9..bee3ab4c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -461,6 +461,12 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "escape-bytes"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3871d161fec5b6fade5fe7afe2e196b86839c5526a8d256c7b1a04dbbe5241a4"
+
 [[package]]
 name = "ethnum"
 version = "1.5.0"
@@ -1504,11 +1510,12 @@ dependencies = [
 [[package]]
 name = "stellar-xdr"
 version = "20.0.0-rc1"
-source = "git+https://github.com/stellar/rs-stellar-xdr?rev=d6f8ece2c89809d5e2800b9df64ae60787ee492b#d6f8ece2c89809d5e2800b9df64ae60787ee492b"
+source = "git+https://github.com/stellar/rs-stellar-xdr?rev=889d433c6069c7cdd63a9e4f391de06f5d8018ac#889d433c6069c7cdd63a9e4f391de06f5d8018ac"
 dependencies = [
  "arbitrary",
  "base64 0.13.1",
  "crate-git-revision",
+ "escape-bytes",
  "hex",
  "serde",
  "serde_with",
diff --git a/Cargo.toml b/Cargo.toml
index 1a14f689..cc21b6d9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,7 +29,7 @@ soroban-builtin-sdk-macros = { version = "=20.0.0-rc2", path = "soroban-builtin-
 [workspace.dependencies.stellar-xdr]
 version = "=20.0.0-rc1"
 git = "https://github.com/stellar/rs-stellar-xdr"
-rev = "d6f8ece2c89809d5e2800b9df64ae60787ee492b"
+rev = "889d433c6069c7cdd63a9e4f391de06f5d8018ac"
 default-features = false
 
 [workspace.dependencies.wasmi]
diff --git a/soroban-env-common/src/env.rs b/soroban-env-common/src/env.rs
index f7ccadd0..e9fdac42 100644
--- a/soroban-env-common/src/env.rs
+++ b/soroban-env-common/src/env.rs
@@ -165,7 +165,7 @@ pub trait EnvBase: Sized + Clone {
     fn bytes_new_from_slice(&self, slice: &[u8]) -> Result<BytesObject, Self::Error>;
 
     /// Form a new `String` host object from a slice of client memory.
-    fn string_new_from_slice(&self, slice: &str) -> Result<StringObject, Self::Error>;
+    fn string_new_from_slice(&self, slice: &[u8]) -> Result<StringObject, Self::Error>;
 
     /// Form a new `Symbol` host object from a slice of client memory.
     fn symbol_new_from_slice(&self, slice: &str) -> Result<SymbolObject, Self::Error>;
diff --git a/soroban-env-common/src/string.rs b/soroban-env-common/src/string.rs
index bc333b20..4ae3dc1f 100644
--- a/soroban-env-common/src/string.rs
+++ b/soroban-env-common/src/string.rs
@@ -39,7 +39,8 @@ impl<E: Env> TryFromVal<E, &str> for StringObject {
     type Error = crate::Error;
     #[inline(always)]
     fn try_from_val(env: &E, val: &&str) -> Result<StringObject, Self::Error> {
-        env.string_new_from_slice(val).map_err(Into::into)
+        env.string_new_from_slice(val.as_bytes())
+            .map_err(Into::into)
     }
 }
 
diff --git a/soroban-env-guest/src/guest.rs b/soroban-env-guest/src/guest.rs
index b9b7226a..79a57883 100644
--- a/soroban-env-guest/src/guest.rs
+++ b/soroban-env-guest/src/guest.rs
@@ -107,7 +107,7 @@ impl EnvBase for Guest {
         self.bytes_new_from_linear_memory(lm_pos, len)
     }
 
-    fn string_new_from_slice(&self, slice: &str) -> Result<StringObject, Self::Error> {
+    fn string_new_from_slice(&self, slice: &[u8]) -> Result<StringObject, Self::Error> {
         sa::assert_eq_size!(u32, *const u8);
         sa::assert_eq_size!(u32, usize);
         let lm_pos: U32Val = Val::from_u32(slice.as_ptr() as u32);
diff --git a/soroban-env-host/Cargo.toml b/soroban-env-host/Cargo.toml
index 36508d1e..2b3399bc 100644
--- a/soroban-env-host/Cargo.toml
+++ b/soroban-env-host/Cargo.toml
@@ -65,7 +65,7 @@ arbitrary = "=1.3.2"
 [dev-dependencies.stellar-xdr]
 version = "=20.0.0-rc1"
 git = "https://github.com/stellar/rs-stellar-xdr"
-rev = "d6f8ece2c89809d5e2800b9df64ae60787ee492b"
+rev = "889d433c6069c7cdd63a9e4f391de06f5d8018ac"
 default-features = false
 features = ["arbitrary"]
 
diff --git a/soroban-env-host/src/builtin_contracts/stellar_asset_contract/contract.rs b/soroban-env-host/src/builtin_contracts/stellar_asset_contract/contract.rs
index fc887da7..5a49c9c0 100644
--- a/soroban-env-host/src/builtin_contracts/stellar_asset_contract/contract.rs
+++ b/soroban-env-host/src/builtin_contracts/stellar_asset_contract/contract.rs
@@ -18,7 +18,7 @@ use crate::{err, HostError};
 
 use soroban_builtin_sdk_macros::contractimpl;
 use soroban_env_common::xdr::Asset;
-use soroban_env_common::{Compare, ConversionError, Env, EnvBase, TryFromVal, TryIntoVal};
+use soroban_env_common::{Compare, Env, EnvBase, TryFromVal, TryIntoVal};
 
 use super::admin::{read_administrator, write_administrator};
 use super::asset_info::read_asset_info;
@@ -112,10 +112,7 @@ impl StellarAssetContract {
                     AssetInfo::AlphaNum4(AlphaNum4AssetInfo {
                         asset_code: String::try_from_val(
                             e,
-                            &e.string_new_from_slice(
-                                core::str::from_utf8(&asset4.asset_code.0)
-                                    .map_err(|_| ConversionError)?,
-                            )?,
+                            &e.string_new_from_slice(&asset4.asset_code.0)?,
                         )?,
                         issuer: BytesN::<32>::try_from_val(
                             e,
@@ -131,10 +128,7 @@ impl StellarAssetContract {
                     AssetInfo::AlphaNum12(AlphaNum12AssetInfo {
                         asset_code: String::try_from_val(
                             e,
-                            &e.string_new_from_slice(
-                                core::str::from_utf8(&asset12.asset_code.0)
-                                    .map_err(|_| ConversionError)?,
-                            )?,
+                            &e.string_new_from_slice(&asset12.asset_code.0)?,
                         )?,
                         issuer: BytesN::<32>::try_from_val(
                             e,
diff --git a/soroban-env-host/src/builtin_contracts/stellar_asset_contract/metadata.rs b/soroban-env-host/src/builtin_contracts/stellar_asset_contract/metadata.rs
index 5fba79e0..569daf9e 100644
--- a/soroban-env-host/src/builtin_contracts/stellar_asset_contract/metadata.rs
+++ b/soroban-env-host/src/builtin_contracts/stellar_asset_contract/metadata.rs
@@ -148,7 +148,7 @@ fn render_sep0011_asset<const N: usize>(
     s.push(':');
     s.push_str(&ed25519::PublicKey(issuer.to_array()?).to_string());
     Ok((
-        String::try_from_val(e, &e.string_new_from_slice(s.as_str())?)?,
+        String::try_from_val(e, &e.string_new_from_slice(s.as_bytes())?)?,
         symbol,
     ))
 }
@@ -156,7 +156,7 @@ fn render_sep0011_asset<const N: usize>(
 pub fn set_metadata(e: &Host) -> Result<(), HostError> {
     let name_and_symbol: (String, String) = match read_asset_info(e)? {
         AssetInfo::Native => {
-            let n = String::try_from_val(e, &e.string_new_from_slice("native")?)?;
+            let n = String::try_from_val(e, &e.string_new_from_slice(b"native")?)?;
             (n.clone(), n)
         }
         AssetInfo::AlphaNum4(asset) => {
diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs
index 2e006ae5..9d014321 100644
--- a/soroban-env-host/src/host.rs
+++ b/soroban-env-host/src/host.rs
@@ -779,11 +779,9 @@ impl EnvBase for Host {
         res
     }
 
-    fn string_new_from_slice(&self, s: &str) -> Result<StringObject, HostError> {
+    fn string_new_from_slice(&self, s: &[u8]) -> Result<StringObject, HostError> {
         call_env_call_hook!(self, s.len());
-        let res = self.add_host_object(ScString(
-            self.metered_slice_to_vec(s.as_bytes())?.try_into()?,
-        ));
+        let res = self.add_host_object(ScString(self.metered_slice_to_vec(s)?.try_into()?));
         call_env_ret_hook!(self, res);
         res
     }
diff --git a/soroban-env-host/src/host/error.rs b/soroban-env-host/src/host/error.rs
index fde0c408..ebe0a65f 100644
--- a/soroban-env-host/src/host/error.rs
+++ b/soroban-env-host/src/host/error.rs
@@ -501,7 +501,7 @@ impl DebugArg for xdr::Hash {
 
 impl DebugArg for str {
     fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result<Val, HostError> {
-        host.string_new_from_slice(arg).map(|s| s.into())
+        host.string_new_from_slice(arg.as_bytes()).map(|s| s.into())
     }
 }
 


Test patch:
diff --git a/soroban-env-host/src/test/address.rs b/soroban-env-host/src/test/address.rs
index fd33509a..ad324162 100644
--- a/soroban-env-host/src/test/address.rs
+++ b/soroban-env-host/src/test/address.rs
@@ -5,7 +5,7 @@ use soroban_env_common::{
 };
 
 fn extract_string(host: &Host, s: StringObject) -> String {
-    host.visit_obj(s, |st: &ScString| Ok(st.0.to_string_lossy()))
+    host.visit_obj(s, |st: &ScString| Ok(st.0.to_string()))
         .unwrap()
 }
 
diff --git a/soroban-env-host/src/test/str.rs b/soroban-env-host/src/test/str.rs
index 0740c494..f07d5867 100644
--- a/soroban-env-host/src/test/str.rs
+++ b/soroban-env-host/src/test/str.rs
@@ -12,7 +12,7 @@ fn str_conversions() -> Result<(), HostError> {
         obj = host.bytes_push(obj, (c as u32).into())?;
     }
     let ss = "abcdefghijklmnopqrstuvwxyz";
-    let so = host.string_new_from_slice(ss)?;
+    let so = host.string_new_from_slice(ss.as_bytes())?;
     let val = so.to_val();
     let s: String = val.try_into_val(&*host)?;
     assert_eq!(s, ss);