Skip to content

Commit ada041a

Browse files
evgenikokcsongor
andauthored
set wormhole address in solana binary by patching it, to enable other SVM chains to work with the CLI (#658)
* 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. * only apply patch if the used chain is not solana --------- Co-authored-by: Csongor Kiss <kiss.csongor.kiss@gmail.com>
1 parent 2b736b6 commit ada041a

1 file changed

Lines changed: 61 additions & 16 deletions

File tree

cli/src/index.ts

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,7 +1995,18 @@ async function deploySolana<N extends Network, C extends SolanaChains>(
19951995
}
19961996

19971997

1998-
await checkSolanaBinary(binary, wormhole, providedProgramId, version ?? undefined)
1998+
const wh = new Wormhole(ch.network, [solana.Platform], overrides);
1999+
const sol = wh.getChain("Solana");
2000+
const solanaAddress = sol.config.contracts.coreBridge;
2001+
if (!solanaAddress) {
2002+
console.error("Core bridge address not found in Solana config");
2003+
process.exit(1);
2004+
}
2005+
2006+
if (ch.chain !== "Solana") {
2007+
await patchSolanaBinary(binary, wormhole, solanaAddress);
2008+
}
2009+
await checkSolanaBinary(binary, wormhole, providedProgramId, version ?? undefined);
19992010

20002011
// if buffer.json doesn't exist, create it
20012012
if (!fs.existsSync(`buffer.json`)) {
@@ -2557,41 +2568,75 @@ async function pullInboundLimits(ntts: Partial<{ [C in Chain]: Ntt<Network, C> }
25572568
}
25582569
}
25592570

2571+
async function patchSolanaBinary(binary: string, wormhole: string, solanaAddress: string) {
2572+
// Ensure binary path exists
2573+
if (!fs.existsSync(binary)) {
2574+
console.error(`.so file not found: ${binary}`);
2575+
process.exit(1);
2576+
}
2577+
2578+
// Convert addresses from base58 to Buffer
2579+
const wormholeBuffer = new PublicKey(wormhole).toBuffer();
2580+
const solanaAddressBuffer = new PublicKey(solanaAddress).toBuffer();
2581+
2582+
// Read the binary file
2583+
let binaryData = fs.readFileSync(binary);
2584+
2585+
// Find and count occurrences of core bridge address
2586+
let occurrences = 0;
2587+
let searchIndex = 0;
2588+
2589+
// Replace all occurrences of core bridge with wormhole
2590+
searchIndex = 0;
2591+
while (true) {
2592+
const index = binaryData.indexOf(solanaAddressBuffer, searchIndex);
2593+
if (index === -1) break;
2594+
occurrences++;
2595+
2596+
// Replace the bytes at this position
2597+
wormholeBuffer.copy(binaryData, index);
2598+
searchIndex = index + solanaAddressBuffer.length;
2599+
}
2600+
2601+
// Write the patched binary back to file
2602+
fs.writeFileSync(binary, binaryData);
2603+
2604+
if (occurrences > 0) {
2605+
console.log(`Patched binary, replacing ${solanaAddress} with ${wormhole} in ${occurrences} places.`);
2606+
}
2607+
}
2608+
25602609
async function checkSolanaBinary(binary: string, wormhole: string, providedProgramId: string, version?: string) {
25612610
// ensure binary path exists
25622611
if (!fs.existsSync(binary)) {
25632612
console.error(`.so file not found: ${binary}`);
25642613
process.exit(1);
25652614
}
2566-
// console.log(`Checking binary ${binary} for wormhole and provided program ID`);
25672615

2568-
// convert wormhole and providedProgramId from base58 to hex
2569-
const wormholeHex = new PublicKey(wormhole).toBuffer().toString("hex");
2570-
const providedProgramIdHex = new PublicKey(providedProgramId).toBuffer().toString("hex");
2571-
const versionHex = version ? Buffer.from(version).toString("hex") : undefined;
2616+
// convert addresses from base58 to Buffer
2617+
const wormholeBuffer = new PublicKey(wormhole).toBuffer();
2618+
const providedProgramIdBuffer = new PublicKey(providedProgramId).toBuffer();
2619+
const versionBuffer = version ? Buffer.from(version, 'utf8') : undefined;
25722620

2573-
if (!searchHexInBinary(binary, wormholeHex)) {
2621+
if (!searchBufferInBinary(binary, wormholeBuffer)) {
25742622
console.error(`Wormhole address not found in binary: ${wormhole}`);
25752623
process.exit(1);
25762624
}
2577-
if (!searchHexInBinary(binary, providedProgramIdHex)) {
2625+
if (!searchBufferInBinary(binary, providedProgramIdBuffer)) {
25782626
console.error(`Provided program ID not found in binary: ${providedProgramId}`);
25792627
process.exit(1);
25802628
}
2581-
if (versionHex && !searchHexInBinary(binary, versionHex)) {
2629+
if (versionBuffer && !searchBufferInBinary(binary, versionBuffer)) {
25822630
// TODO: figure out how to search for the version string in the binary
25832631
// console.error(`Version string not found in binary: ${version}`);
25842632
// process.exit(1);
25852633
}
25862634
}
25872635

2588-
// not the most efficient, but at least it's definitely portable
2589-
function searchHexInBinary(binaryPath: string, searchHex: string) {
2590-
const buffer = fs.readFileSync(binaryPath);
2591-
const hexString = buffer.toString('hex');
2592-
const found = hexString.includes(searchHex);
2593-
2594-
return found;
2636+
// Search for a buffer pattern within a binary file using direct buffer operations
2637+
function searchBufferInBinary(binaryPath: string, searchBuffer: Buffer): boolean {
2638+
const binaryData = fs.readFileSync(binaryPath);
2639+
return binaryData.indexOf(searchBuffer) !== -1;
25952640
}
25962641

25972642
function getSlowFlag(chain: Chain): string {

0 commit comments

Comments
 (0)