Skip to content

Commit 49f4ccf

Browse files
committed
bootstrap streamlining
1 parent 639eb78 commit 49f4ccf

File tree

8 files changed

+500
-308
lines changed

8 files changed

+500
-308
lines changed

infra-apps/vmagent.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
scrape_configs:
2+
- job_name: "switchboard-pods"
3+
scrape_interval: 60s
4+
kubernetes_sd_configs:
5+
- role: pod
6+
scheme: http
7+
metrics_path: /metrics
8+
relabel_configs:
9+
- source_labels: [__meta_kubernetes_pod_label_should_scrape]
10+
regex: scrape
11+
action: keep
12+
- target_label: operator
13+
replacement: "$EXAMPLE_OPERATOR"
14+
15+
remoteWriteUrls:
16+
- "http://remote-write.switchboard.xyz/insert/0/prometheus"

infra-apps/vmetrics-remotewrite.yaml

Lines changed: 0 additions & 7 deletions
This file was deleted.

operator-guide.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,7 @@ also want to set up prometheus or vmetrics to scrape
7979
metrics from the oracles, secrets management like
8080
infisical, or a log aggregator like loki.
8181

82-
Exposing metrics and logs are strongly encouraged for all oracle operators. To do so, first you must register a domain/subdomain to use for your metrics and logs endpoints respectively. If you are using k3s, then you can find the external ip address by running this command:
83-
84-
```bash
85-
kubectl -n kube-system get svc traefik
86-
```
87-
88-
If you are using a different kubernetes distribution and/or ingress controller, you can find that wit this command:
82+
Exposing metrics and logs are strongly encouraged for all oracle operators. To do so, first you must register a domain/subdomain to use for your metrics and logs endpoints respectively. You can find the external ip address by running this command once your ingress controller is installed:
8983

9084
```bash
9185
kubectl get svc --all-namespaces
@@ -107,13 +101,23 @@ helm install \
107101
--version v1.12.3 \
108102
--set installCRDs=true \
109103
--set global.leaderElection.namespace=cert-manager
104+
helm install ingress-nginx ingress-nginx/ingress-nginx \
105+
--namespace ingress-nginx \
106+
--create-namespace \
107+
--timeout 600s \
108+
--debug \
110109

111110
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
112111
helm install secrets-operator infisical-helm-charts/secrets-operator
113112
```
114113

115114
in order to use the infisical secrets operator, you must first create an infisical account and follow [the setup guide](https://infisical.com/docs/integrations/platforms/kubernetes) and upload secrets with a name/slug that aligns with the infisicalSecretKey and infisicalSecretSlug in your values yaml.
116115

116+
If you are a switchboard partner, you are expected to export your metric data to a centralized instance to better monitor network health.
117+
```bash
118+
helm upgrade -i vmagent vm/victoria-metrics-agent -f vmagent.yaml
119+
```
120+
117121
### Step 3: Install the Kubernetes SGX Plugin
118122

119123
Before installing the SGX plugin, check the latest

scripts/src/bootstrap.ts

Lines changed: 151 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -2,154 +2,173 @@ import * as anchor from "@coral-xyz/anchor";
22
import * as spl from "@solana/spl-token";
33
import type { AccountInfo, AccountMeta } from "@solana/web3.js";
44
import {
5-
AddressLookupTableProgram,
6-
Connection,
7-
Keypair,
8-
MessageV0,
9-
PublicKey,
10-
sendAndConfirmTransaction,
11-
SystemProgram,
12-
Transaction,
13-
TransactionInstruction,
14-
TransactionMessage,
15-
VersionedTransaction,
5+
AddressLookupTableProgram,
6+
Connection,
7+
Keypair,
8+
MessageV0,
9+
PublicKey,
10+
sendAndConfirmTransaction,
11+
SystemProgram,
12+
Transaction,
13+
TransactionInstruction,
14+
TransactionMessage,
15+
VersionedTransaction,
16+
AddressLookupTableAccount,
1617
} from "@solana/web3.js";
18+
var resolve = require("resolve-dir");
1719
import { Big, BigUtils, bs58 } from "@switchboard-xyz/common";
1820
import { OracleJob } from "@switchboard-xyz/common";
19-
import * as sb from "@switchboard-xyz/solana.js";
21+
import * as sb from "@switchboard-xyz/on-demand";
2022
import { toBufferLE } from "bigint-buffer";
2123
import * as crypto from "crypto";
2224
import * as fs from "fs";
2325
const assert = require("assert");
24-
25-
const walletFile = "/home/scottk/workspace/creds/devnet-wallet";
26-
// example "/Users/mgild/switchboard_environments_v2/devnet/upgrade_authority/test.json"
27-
const payerFile = "/home/scottk/workspace/creds/devnet-wallet"
28-
let PID = new PublicKey("sbattyXrzedoNATfc4L31wC9Mhxsi1BmFhTiN8gDshx");
29-
// PID = new PublicKey("CR1hCrkKveeWrYYs5kk7rasRM2AH1vZy8s8fn42NBwkq");
30-
const RPC_URL = "https://api.devnet.solana.com";
31-
32-
33-
34-
async function fetchLatestSlotHash(
35-
connection: Connection
36-
): Promise<[bigint, string]> {
37-
const slotHashesSysvarKey = new PublicKey(
38-
"SysvarS1otHashes111111111111111111111111111"
39-
);
40-
const accountInfo = await connection.getAccountInfo(slotHashesSysvarKey, {
41-
commitment: "confirmed",
42-
dataSlice: { length: 40, offset: 8 },
43-
});
44-
let buffer = accountInfo!.data;
45-
const slotNumber = buffer.readBigUInt64LE();
46-
buffer = buffer.slice(8);
47-
return [slotNumber, bs58.encode(buffer)];
48-
}
49-
50-
async function initWalletFromFile(filePath: string): Promise<anchor.Wallet> {
51-
// Read the file
52-
const secretKeyString: string = fs.readFileSync(filePath, {
53-
encoding: "utf8",
54-
});
55-
const secretKey: Uint8Array = Uint8Array.from(JSON.parse(secretKeyString));
56-
57-
// Create a keypair from the secret key
58-
const keypair: Keypair = Keypair.fromSecretKey(secretKey);
59-
60-
// Create a wallet
61-
const wallet: anchor.Wallet = new anchor.Wallet(keypair);
62-
63-
return wallet;
64-
}
65-
66-
async function initKeypairFromFile(filePath: string): Promise<Keypair> {
67-
// Read the file
68-
const secretKeyString: string = fs.readFileSync(filePath, {
69-
encoding: "utf8",
70-
});
71-
const secretKey: Uint8Array = Uint8Array.from(JSON.parse(secretKeyString));
72-
73-
// Create a keypair from the secret key
74-
const keypair: Keypair = Keypair.fromSecretKey(secretKey);
75-
76-
return keypair;
26+
const yargs = require("yargs/yargs");
27+
import {
28+
InstructionUtils,
29+
PullFeed,
30+
Queue,
31+
RecentSlotHashes,
32+
} from "@switchboard-xyz/on-demand";
33+
34+
// ts-node bootstrap.ts --queueKey=7n7CSKBhqxM9m9YyLPn7cF6vXvxtZAWMBW42ior8qode --guardianQueue=F4pXZNjaaNmGwXzBhoxs5yaH6f6RKKkj5qgCZKPF9rjg --payerPath=/home/scottk/workspace/creds/devops-keypair.json
35+
36+
let argv = yargs(process.argv).options({
37+
queueKey: {
38+
type: "string",
39+
describe: "Queue to put pull oracle on",
40+
demand: false,
41+
default: "",
42+
},
43+
guardianQueue: {
44+
type: "string",
45+
describe: "Queue to put guardian oracle on",
46+
demand: false,
47+
default: "",
48+
},
49+
initQueues: {
50+
type: "boolean",
51+
describe: "Initialize new queues",
52+
demand: false,
53+
default: false,
54+
},
55+
payerPath: {
56+
type: "string",
57+
describe: "Path to payer keypair",
58+
demand: true,
59+
},
60+
}).argv;
61+
62+
async function sendIx(
63+
program: anchor.Program,
64+
ix: TransactionInstruction,
65+
signers: Array<Keypair>
66+
) {
67+
const tx = await InstructionUtils.asV0Tx(program, [ix], []);
68+
for (const signer of signers) {
69+
tx.sign([signer]);
70+
}
71+
const sig = await program.provider.connection.sendTransaction(tx);
72+
console.log(`signature: ${sig}`);
7773
}
7874

79-
async function keypairFromJson(secretKeyString: string): Promise<Keypair> {
80-
const secretKey: Uint8Array = Uint8Array.from(JSON.parse(secretKeyString));
81-
82-
// Create a keypair from the secret key
83-
return Keypair.fromSecretKey(secretKey);
75+
function keypairFromJson(secretKeyString: string): Keypair {
76+
const secretKey: Uint8Array = Uint8Array.from(JSON.parse(secretKeyString));
77+
return Keypair.fromSecretKey(secretKey);
8478
}
8579

86-
export function logEnvVariables(
87-
env: Array<[string, string | anchor.web3.PublicKey]>,
88-
pre = "Make sure to add the following to your .env file:"
89-
) {
90-
console.log(
91-
`\n${pre}\n\t${env
92-
.map(([key, value]) => `${key.toUpperCase()}=${value}`)
93-
.join("\n\t")}\n`
94-
);
80+
function initKeypairFromFile(filePath: string): Keypair {
81+
const secretKeyString = fs.readFileSync(filePath, { encoding: "utf8" });
82+
return keypairFromJson(secretKeyString);
9583
}
9684

9785
(async () => {
98-
const ORACLE_IP = "127.0.0.1";
99-
100-
let PID = new PublicKey("sbattyXrzedoNATfc4L31wC9Mhxsi1BmFhTiN8gDshx");
101-
PID = sb.SB_ON_DEMAND_PID;
102-
const connection = new Connection(
103-
RPC_URL,
104-
"confirmed"
105-
);
106-
107-
const wallet = await initWalletFromFile(
108-
walletFile
109-
);
110-
const devnetPayer = await initKeypairFromFile(
111-
payerFile
86+
let PID;
87+
PID = sb.SB_ON_DEMAND_PID;
88+
const connection = new Connection(
89+
"https://api.devnet.solana.com",
90+
"confirmed"
91+
);
92+
const devnetPayer = initKeypairFromFile(
93+
resolve(argv.payerPath || "./payer.json")
94+
);
95+
const wallet = new anchor.Wallet(devnetPayer);
96+
const provider = new anchor.AnchorProvider(connection, wallet, {});
97+
const idl = await anchor.Program.fetchIdl(PID, provider);
98+
const program = new anchor.Program(idl!, PID, provider);
99+
let queueKey = argv.queueKey;
100+
let guardianQueueKey = argv.guardianQueue;
101+
console.log(`Queue: ${queueKey}`);
102+
console.log(`GuardianQueue: ${guardianQueueKey}`);
103+
if (argv.initQueues) {
104+
try {
105+
const [state, stateInitSig] = await sb.State.create(program);
106+
console.log(`State: ${state.pubkey.toBase58()} Sig: ${stateInitSig}`);
107+
} catch (e) {}
108+
const state = new sb.State(program);
109+
const [queue, queueCreateSig] = await sb.Queue.create(program, {});
110+
console.log(`Queue: ${queue.pubkey.toBase58()} Sig: ${queueCreateSig}`);
111+
queueKey = queue.pubkey.toBase58();
112+
const [guardianQueue, guardianQueueCreateSig] = await sb.Queue.create(
113+
program,
114+
{}
112115
);
113-
const provider = new anchor.AnchorProvider(connection, wallet, {});
114-
const idl = await anchor.Program.fetchIdl(PID, provider);
115-
const program = new anchor.Program(idl!, PID, provider);
116-
const switchboardProgram = sb.SwitchboardProgram.from(
117-
connection,
118-
devnetPayer,
119-
sb.SB_V2_PID,
120-
PID
116+
guardianQueueKey = guardianQueue.pubkey.toBase58();
117+
console.log(
118+
`GuardianQueue: ${queue.pubkey.toBase58()} Sig: ${guardianQueueCreateSig}`
121119
);
122-
123-
const [slotNumber, slotHash] = await fetchLatestSlotHash(connection);
124-
const bootstrappedQueue = (await sb.AttestationQueueAccount.bootstrapNewQueue(
125-
switchboardProgram
126-
)) as any;
127-
console.log(bootstrappedQueue);
128-
129-
const attestationQueueAccount = bootstrappedQueue.attestationQueue.account;
130-
const verifierOracleAccount = bootstrappedQueue.verifier.account;
131-
const quoteKeypair2 = Keypair.generate();
132-
133-
const [verifier2, signature] = await attestationQueueAccount.createVerifier({
134-
createPermissions: true,
135-
keypair: quoteKeypair2,
120+
const setConfigIx = await state.setConfigsIx({
121+
queue: guardianQueue.pubkey,
122+
newAuthority: devnetPayer.publicKey,
123+
minQuoteVerifyVotes: new anchor.BN(1),
124+
});
125+
const setConfigTx = await InstructionUtils.asV0Tx(program, [setConfigIx]);
126+
setConfigTx.sign([devnetPayer]);
127+
const setConfigSig = await connection.sendTransaction(setConfigTx);
128+
console.log(`Set Config Sig: ${setConfigSig}`);
129+
}
130+
const queue = new sb.Queue(program, new PublicKey(queueKey));
131+
const guardianQueue = new sb.Queue(program, new PublicKey(guardianQueueKey));
132+
const stateAccount = new sb.State(program);
133+
const state = new sb.State(program);
134+
const [oracle1, oracleCreateSig1] = await sb.Oracle.create(program, {
135+
queue: queue.pubkey,
136+
});
137+
const [guardianOracle1, goracleCreateSig1] = await sb.Oracle.create(program, {
138+
queue: guardianQueue.pubkey,
139+
});
140+
if (argv.initQueues) {
141+
await sendIx(
142+
program,
143+
await sb.Permission.setIx(program, {
144+
authority: devnetPayer.publicKey,
145+
grantee: oracle1.pubkey,
146+
granter: queue.pubkey,
136147
enable: true,
137-
queueAuthorityPubkey: devnetPayer.publicKey,
148+
permission: sb.SwitchboardPermission.PermitOracleHeartbeat,
149+
}),
150+
[devnetPayer]
151+
);
152+
await sendIx(
153+
program,
154+
await sb.Permission.setIx(program, {
138155
authority: devnetPayer.publicKey,
139-
queueAccount: attestationQueueAccount.publicKey,
140-
registryKey: new Uint8Array(64).fill(0),
141-
});
142-
console.log(verifier2.publicKey);
143-
144-
logEnvVariables([
145-
["SWITCHBOARD_ATTESTATION_QUEUE_KEY", attestationQueueAccount.publicKey],
146-
["SWITCHBOARD_VERIFIER_ORACLE_KEY", verifierOracleAccount.publicKey],
147-
["SWITCHBOARD_VERIFIER_ORACLE_KEY2", verifier2.publicKey.toString()],
148-
]);
149-
150-
const y = bootstrappedQueue.signatures.map((s: any, i: any): any => {
151-
return { name: `bootstrap_queue #${i + 1}`, tx: s };
152-
});
153-
console.log(y);
154-
return;
156+
granter: guardianQueue.pubkey,
157+
grantee: guardianOracle1.pubkey,
158+
enable: true,
159+
permission: sb.SwitchboardPermission.PermitOracleHeartbeat,
160+
}),
161+
[devnetPayer]
162+
);
163+
}
164+
165+
console.log(`
166+
export QUEUE=${queue.pubkey.toBase58()}
167+
export GUARDIAN_QUEUE=${guardianQueue.pubkey.toBase58()}
168+
export STATE=${state.pubkey.toBase58()}
169+
export PID=${PID.toBase58()}
170+
export ORACLE1=${oracle1.pubkey.toBase58()}
171+
export GUARDIAN_ORACLE1=${guardianOracle1.pubkey.toBase58()}
172+
`);
173+
return;
155174
})();

0 commit comments

Comments
 (0)