Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solana-core: export secp256k1, add optional guardianSetData param #742

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
- run: npm ci
- run: npm run build --if-present
- run: npm test
env:
ETH_RPC: ${{ secrets.ETH_RPC }}
- run: |
version="$GITHUB_REF_NAME"

Expand Down
112 changes: 75 additions & 37 deletions core/base/src/constants/guardians.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,78 @@
import type { MapLevels} from './../utils/index.js';
import { constMap, filterIndexes, zip, cartesianRightRecursive } from './../utils/index.js';
import type { Network } from './networks.js';

// prettier-ignore
const guardianKeyAndNameEntries = [[
"Mainnet", [
["0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5", "JumpCrypto" ],
["0xfF6CB952589BDE862c25Ef4392132fb9D4A42157", "Staked" ],
["0x114De8460193bdf3A2fCf81f86a09765F4762fD1", "Figment" ],
["0x107A0086b32d7A0977926A205131d8731D39cbEB", "ChainodeTech" ],
["0x8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", "Inotel" ],
["0x11b39756C042441BE6D8650b69b54EbE715E2343", "HashQuark" ],
["0x54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", "Chainlayer" ],
["0x15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20", "xLabs" ],
["0x74a3bf913953D695260D88BC1aA25A4eeE363ef0", "Forbole" ],
["0x000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", "StakingFund" ],
["0xAF45Ced136b9D9e24903464AE889F5C8a723FC14", "MoonletWallet" ],
["0xf93124b7c738843CBB89E864c862c38cddCccF95", "P2PValidator" ],
["0xD2CC37A4dc036a8D232b48f62cDD4731412f4890", "01Node" ],
["0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811", "MCF" ],
["0x71AA1BE1D36CaFE3867910F99C09e347899C19C3", "Everstake" ],
["0x8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", "ChorusOne" ],
["0x178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", "Syncnode" ],
["0x5E1487F35515d02A92753504a8D75471b9f49EdB", "Triton" ],
["0x6FbEBc898F403E4773E95feB15E80C9A99c8348d", "StakingFacilities"],
]], [
"Testnet", [
["0x13947Bd48b18E53fdAeEe77F3473391aC727C638", "Testnet guardian"]
]]
] as const satisfies MapLevels<[Network, string, string]>;

export const [guardianKeys, guardianNames] =
filterIndexes(zip(cartesianRightRecursive(guardianKeyAndNameEntries)), [1, 2]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we should keep these and create them using just the latest guardian set


export const guardianNameToKey = constMap(guardianKeyAndNameEntries, [[0, 2], 1]);
export const guardianKeyToName = constMap(guardianKeyAndNameEntries, [1, [0, 2]]);
import { replaceElement } from "./../utils/index.js";

// Mainnet guardian sets
const guardianSet1 = [
["0x58CC3AE5C097b213cE3c81979e1B9f9570746AA5", "Certus One"],
["0xfF6CB952589BDE862c25Ef4392132fb9D4A42157", "Staked"],
["0x114De8460193bdf3A2fCf81f86a09765F4762fD1", "Figment"],
["0x107A0086b32d7A0977926A205131d8731D39cbEB", "ChainodeTech"],
["0x8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", "Inotel"],
["0x11b39756C042441BE6D8650b69b54EbE715E2343", "HashQuark"],
["0x54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", "ChainLayer"],
["0xeB5F7389Fa26941519f0863349C223b73a6DDEE7", "DokiaCapital"],
["0x74a3bf913953D695260D88BC1aA25A4eeE363ef0", "Forbole"],
["0x000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", "Staking Fund"],
["0xAF45Ced136b9D9e24903464AE889F5C8a723FC14", "Moonlet"],
["0xf93124b7c738843CBB89E864c862c38cddCccF95", "P2P Validator"],
["0xD2CC37A4dc036a8D232b48f62cDD4731412f4890", "01node"],
["0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811", "MCF"],
["0x71AA1BE1D36CaFE3867910F99C09e347899C19C3", "Everstake"],
["0x8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", "Chorus One"],
["0x178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", "syncnode"],
["0x5E1487F35515d02A92753504a8D75471b9f49EdB", "Triton"],
["0x6FbEBc898F403E4773E95feB15E80C9A99c8348d", "Staking Facilities"],
] as const;

const guardianSet2 = replaceElement(guardianSet1, 7, [
"0x66B9590e1c41e0B226937bf9217D1d67Fd4E91F5",
"FTX",
] as const);

const guardianSet3 = replaceElement(guardianSet2, 7, [
"0x15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20",
"xLabs",
] as const);

const guardianSet4 = replaceElement(guardianSet3, 0, [
"0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3",
"RockawayX",
] as const);

// Testnet guardian sets
const testnetGuardianSet1 = [
["0x13947Bd48b18E53fdAeEe77F3473391aC727C638", "Testnet guardian"],
] as const;

// TODO: Attempting to use `constMap` results in a type instantiation too deep error
const guardianSetsMap = {
Mainnet: {
1: guardianSet1,
2: guardianSet2,
3: guardianSet3,
4: guardianSet4,
},
Testnet: {
1: testnetGuardianSet1,
},
} as const;

type GuardianSetsMap = typeof guardianSetsMap;

type GuardianInfo = {
address: string;
name: string;
};

export function getGuardianSet<N extends keyof GuardianSetsMap, I extends keyof GuardianSetsMap[N]>(
network: N,
index: I,
): GuardianInfo[] {
const guardianSet = guardianSetsMap[network][index] as readonly [string, string][];
return guardianSet.map(([address, name]) => ({
address,
name,
}));
}

export const devnetGuardianPrivateKey =
"cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
Expand Down
15 changes: 15 additions & 0 deletions core/base/src/utils/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,18 @@ export type Cartesian<L, R> =
: R extends RoArray
? [...{ [K in keyof R]: K extends `${number}` ? [L, R[K]] : never }]
: [L, R];

export type ReplaceElement<A extends RoArray, I extends number, NE> = {
readonly [K in keyof A]: K extends `${I}` ? NE : A[K];
};

export const replaceElement = <
const A extends RoArray,
const I extends number,
const NE
>(arr: A, index: I, newElement: NE,
) => {
const newArr = [...arr];
newArr[index] = newElement;
return newArr as ReplaceElement<A, I, NE>;
}
33 changes: 33 additions & 0 deletions platforms/evm/__tests__/unit/core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, test } from '@jest/globals';
import { ethers } from 'ethers';
import { EvmWormholeCore } from '@wormhole-foundation/sdk-evm-core';
import { contracts } from '@wormhole-foundation/sdk-connect';
import { guardians } from '@wormhole-foundation/sdk-base';

import '@wormhole-foundation/sdk-evm-core';

describe('Core tests', function () {
test('Check latest mainnet guardian set', async () => {
const rpc = process.env.ETH_RPC
? new ethers.JsonRpcProvider(process.env.ETH_RPC)
: ethers.getDefaultProvider('mainnet');

const core = new EvmWormholeCore('Mainnet', 'Ethereum', rpc, {
coreBridge: contracts.coreBridge.get('Mainnet', 'Ethereum'),
});

const index = await core.getGuardianSetIndex();
// If this test fails, the guardian set index may have been updated
expect(index).toBe(4);

const guardianSet = await core.getGuardianSet(index);
expect(guardianSet.index).toBe(index);

const localGuardianSet = [...guardians.getGuardianSet('Mainnet', 4)];
expect(localGuardianSet.length).toBe(guardianSet.keys.length);

for (let i = 0; i < guardianSet.keys.length; i++) {
expect(guardianSet.keys[i]).toBe(localGuardianSet[i]?.address);
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './governance.js';
export * from './initialize.js';
export * from './postMessage.js';
export * from './postVaa.js';
export * from './secp256k1.js';
export * from './verifySignature.js';
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
} from '@solana/web3.js';
import type { VAA } from '@wormhole-foundation/sdk-connect';
import { createReadOnlyWormholeProgramInterface } from '../program.js';
import { deriveGuardianSetKey, getGuardianSet } from './../accounts/index.js';
import {
GuardianSetData,
deriveGuardianSetKey,
getGuardianSet,
} from './../accounts/index.js';
import { createSecp256k1Instruction } from './secp256k1.js';

const MAX_LEN_GUARDIAN_KEYS = 19;
Expand All @@ -35,6 +39,7 @@ const MAX_LEN_GUARDIAN_KEYS = 19;
* @param {SignedVaa | ParsedVaa} vaa - either signed VAA bytes or parsed VAA
* @param {PublicKeyInitData} signatureSet - address to account of verified signatures
* @param {web3.ConfirmOptions} [options] - Solana confirmation options
* @param {GuardianSetData} [guardianSetData] - guardian set data
*/
export async function createVerifySignaturesInstructions(
connection: Connection,
Expand All @@ -43,14 +48,17 @@ export async function createVerifySignaturesInstructions(
vaa: VAA<any>,
signatureSet: PublicKeyInitData,
commitment?: Commitment,
guardianSetData?: GuardianSetData,
): Promise<TransactionInstruction[]> {
const guardianSetIndex = vaa.guardianSet;
const guardianSetData = await getGuardianSet(
connection,
wormholeProgramId,
guardianSetIndex,
commitment,
);
if (guardianSetData === undefined) {
guardianSetData = await getGuardianSet(
connection,
wormholeProgramId,
guardianSetIndex,
commitment,
);
}

const guardianSignatures = vaa.signatures;
const guardianKeys = guardianSetData.keys;
Expand Down
Loading