diff --git a/solana/modules/nft_bridge/Cargo.lock b/solana/modules/nft_bridge/Cargo.lock index 6044bc905..afa9e34c0 100644 --- a/solana/modules/nft_bridge/Cargo.lock +++ b/solana/modules/nft_bridge/Cargo.lock @@ -264,6 +264,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" +[[package]] +name = "bstr" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + [[package]] name = "bumpalo" version = "3.7.0" @@ -1645,6 +1656,7 @@ version = "0.1.0" dependencies = [ "borsh", "bridge", + "bstr", "byteorder", "hex", "hex-literal", @@ -2199,6 +2211,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" diff --git a/solana/modules/nft_bridge/program/Cargo.toml b/solana/modules/nft_bridge/program/Cargo.toml index 7b9cc3197..eeabb533a 100644 --- a/solana/modules/nft_bridge/program/Cargo.toml +++ b/solana/modules/nft_bridge/program/Cargo.toml @@ -19,6 +19,7 @@ default = [] [dependencies] bridge = { path = "../../../bridge/program", features = ["no-entrypoint", "cpi"] } borsh = "0.8.1" +bstr = "0.2.16" byteorder = "1.4.3" rocksalt = { path = "../../../solitaire/rocksalt" } solitaire = { path = "../../../solitaire/program" } diff --git a/solana/modules/nft_bridge/program/src/api/complete_transfer.rs b/solana/modules/nft_bridge/program/src/api/complete_transfer.rs index cc69b5036..6a66bafff 100644 --- a/solana/modules/nft_bridge/program/src/api/complete_transfer.rs +++ b/solana/modules/nft_bridge/program/src/api/complete_transfer.rs @@ -215,6 +215,8 @@ pub fn complete_wrapped( accs: &mut CompleteWrapped, data: CompleteWrappedData, ) -> Result<()> { + use bstr::ByteSlice; + // Verify the chain registration let derivation_data: EndpointDerivationData = (&*accs).into(); accs.chain_registration @@ -261,10 +263,12 @@ pub fn complete_wrapped( }, )?; - let mut name = accs.vaa.name.clone(); - name.truncate(32); - let mut symbol = accs.vaa.symbol.clone(); + let name = accs.vaa.name.clone(); + let mut symbol: Vec = accs.vaa.symbol.clone().as_bytes().to_vec(); symbol.truncate(10); + let mut symbol: Vec = symbol.chars().collect(); + symbol.retain(|&c| c != '\u{FFFD}'); + let symbol: String = symbol.iter().collect(); let spl_token_metadata_ix = spl_token_metadata::instruction::create_metadata_accounts( spl_token_metadata::id(), diff --git a/solana/modules/nft_bridge/program/src/messages.rs b/solana/modules/nft_bridge/program/src/messages.rs index 1fa7def90..fe6290964 100644 --- a/solana/modules/nft_bridge/program/src/messages.rs +++ b/solana/modules/nft_bridge/program/src/messages.rs @@ -67,6 +67,7 @@ pub struct PayloadTransfer { impl DeserializePayload for PayloadTransfer { fn deserialize(buf: &mut &[u8]) -> Result { + use bstr::ByteSlice; let mut v = Cursor::new(buf); if v.read_u8()? != 1 { @@ -78,17 +79,23 @@ impl DeserializePayload for PayloadTransfer { let token_chain = v.read_u16::()?; - let mut symbol_data: [u8; 32] = [0; 32]; + // We may receive invalid UTF-8 over the bridge, especially if truncated. To compensate for + // this we rely on the bstr libraries ability to parse invalid UTF-8, and strip out the + // "Invalid Unicode Codepoint" (FFFD) characters. This becomes the canonical representation + // on Solana. + let mut symbol_data = vec![0u8; 32]; v.read_exact(&mut symbol_data)?; - let mut symbol = String::from_utf8(symbol_data.to_vec()) - .map_err::(|_| TokenBridgeError::InvalidUTF8String.into())?; - symbol = symbol.chars().filter(|c| c != &'\0').collect(); + symbol_data.retain(|&c| c != 0); + let mut symbol: Vec = symbol_data.chars().collect(); + symbol.retain(|&c| c != '\u{FFFD}'); + let symbol: String = symbol.iter().collect(); - let mut name_data: [u8; 32] = [0; 32]; + let mut name_data = vec![0u8; 32]; v.read_exact(&mut name_data)?; - let mut name = String::from_utf8(name_data.to_vec()) - .map_err::(|_| TokenBridgeError::InvalidUTF8String.into())?; - name = name.chars().filter(|c| c != &'\0').collect(); + name_data.retain(|&c| c != 0); + let mut name: Vec = name_data.chars().collect(); + name.retain(|&c| c != '\u{FFFD}'); + let name: String = name.iter().collect(); let mut id_data: [u8; 32] = [0; 32]; v.read_exact(&mut id_data)?;