From 0031842ff7a9602ec91150a36516748f248bc2ed Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Thu, 26 Jun 2025 13:40:25 -0700 Subject: [PATCH 1/2] cli: set wormhole address in solana binary by patching it The problem: solana binaries contain program addresses embedded into them. Specifically, NTT includes the address of the Wormhole program it's linked against. Which Wormhole address to include is controlled via feature flags in the wormhole-anchor-sdk crate, which supports one of mainnet, devnet, and localhost. For other SVM chains, like Fogo, we can't build this binary correctly, because there is no way to tell it which core address to use. One solution is to roll out an updated version of the wormhole-anchor-sdk, which allows setting the Wormhole address via an environment variable. That is likely the prudent solution, but it will need to be backported to older NTT versions (in case a Fogo deployer wants to deploy, say, 3.0.0, instead of the main HEAD). Another solution, which we adopt in this commit, is simply patching the binary. In other words: compile the binary with the solana flag, then replace the solana address with the desired address. This seems to work just fine, there is no checksum signing on the binaries or anything. It also makes compilation a bit faster, because we don't need to recompile anything between different targets. Not sure if we should keep this, but will do for now. --- cli/src/index.ts | 74 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index 631a54c8d..6dfdd0baf 100755 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -1995,7 +1995,15 @@ async function deploySolana( } - await checkSolanaBinary(binary, wormhole, providedProgramId, version ?? undefined) + const wh = new Wormhole(ch.network, [solana.Platform], overrides); + const sol = wh.getChain("Solana"); + const solanaAddress = sol.config.contracts.coreBridge; + if (!solanaAddress) { + console.error("Core bridge address not found in Solana config"); + process.exit(1); + } + await patchSolanaBinary(binary, wormhole, solanaAddress); + await checkSolanaBinary(binary, wormhole, providedProgramId, version ?? undefined); // if buffer.json doesn't exist, create it if (!fs.existsSync(`buffer.json`)) { @@ -2557,41 +2565,75 @@ async function pullInboundLimits(ntts: Partial<{ [C in Chain]: Ntt } } } +async function patchSolanaBinary(binary: string, wormhole: string, solanaAddress: string) { + // Ensure binary path exists + if (!fs.existsSync(binary)) { + console.error(`.so file not found: ${binary}`); + process.exit(1); + } + + // Convert addresses from base58 to Buffer + const wormholeBuffer = new PublicKey(wormhole).toBuffer(); + const solanaAddressBuffer = new PublicKey(solanaAddress).toBuffer(); + + // Read the binary file + let binaryData = fs.readFileSync(binary); + + // Find and count occurrences of core bridge address + let occurrences = 0; + let searchIndex = 0; + + // Replace all occurrences of core bridge with wormhole + searchIndex = 0; + while (true) { + const index = binaryData.indexOf(solanaAddressBuffer, searchIndex); + if (index === -1) break; + occurrences++; + + // Replace the bytes at this position + wormholeBuffer.copy(binaryData, index); + searchIndex = index + solanaAddressBuffer.length; + } + + // Write the patched binary back to file + fs.writeFileSync(binary, binaryData); + + if (occurrences > 0) { + console.log(`Patched binary, replacing ${solanaAddress} with ${wormhole} in ${occurrences} places.`); + } +} + async function checkSolanaBinary(binary: string, wormhole: string, providedProgramId: string, version?: string) { // ensure binary path exists if (!fs.existsSync(binary)) { console.error(`.so file not found: ${binary}`); process.exit(1); } - // console.log(`Checking binary ${binary} for wormhole and provided program ID`); - // convert wormhole and providedProgramId from base58 to hex - const wormholeHex = new PublicKey(wormhole).toBuffer().toString("hex"); - const providedProgramIdHex = new PublicKey(providedProgramId).toBuffer().toString("hex"); - const versionHex = version ? Buffer.from(version).toString("hex") : undefined; + // convert addresses from base58 to Buffer + const wormholeBuffer = new PublicKey(wormhole).toBuffer(); + const providedProgramIdBuffer = new PublicKey(providedProgramId).toBuffer(); + const versionBuffer = version ? Buffer.from(version, 'utf8') : undefined; - if (!searchHexInBinary(binary, wormholeHex)) { + if (!searchBufferInBinary(binary, wormholeBuffer)) { console.error(`Wormhole address not found in binary: ${wormhole}`); process.exit(1); } - if (!searchHexInBinary(binary, providedProgramIdHex)) { + if (!searchBufferInBinary(binary, providedProgramIdBuffer)) { console.error(`Provided program ID not found in binary: ${providedProgramId}`); process.exit(1); } - if (versionHex && !searchHexInBinary(binary, versionHex)) { + if (versionBuffer && !searchBufferInBinary(binary, versionBuffer)) { // TODO: figure out how to search for the version string in the binary // console.error(`Version string not found in binary: ${version}`); // process.exit(1); } } -// not the most efficient, but at least it's definitely portable -function searchHexInBinary(binaryPath: string, searchHex: string) { - const buffer = fs.readFileSync(binaryPath); - const hexString = buffer.toString('hex'); - const found = hexString.includes(searchHex); - - return found; +// Search for a buffer pattern within a binary file using direct buffer operations +function searchBufferInBinary(binaryPath: string, searchBuffer: Buffer): boolean { + const binaryData = fs.readFileSync(binaryPath); + return binaryData.indexOf(searchBuffer) !== -1; } function getSlowFlag(chain: Chain): string { From d0d93cbacab10d0d66371f8d9968f361482a4948 Mon Sep 17 00:00:00 2001 From: evgenidefi <97796468+evgeniko@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:42:07 +0200 Subject: [PATCH 2/2] only apply patch if the used chain is not solana --- cli/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index 6dfdd0baf..32f5268d6 100755 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -2002,7 +2002,10 @@ async function deploySolana( console.error("Core bridge address not found in Solana config"); process.exit(1); } - await patchSolanaBinary(binary, wormhole, solanaAddress); + + if (ch.chain !== "Solana") { + await patchSolanaBinary(binary, wormhole, solanaAddress); + } await checkSolanaBinary(binary, wormhole, providedProgramId, version ?? undefined); // if buffer.json doesn't exist, create it