@1upmonster/duel 0.1.8 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -59
- package/dist/admin.d.ts +25 -40
- package/dist/admin.js +93 -114
- package/dist/encryption.js +1 -2
- package/dist/generated/duel/accounts/index.d.ts +10 -0
- package/dist/generated/duel/accounts/index.js +11 -0
- package/dist/generated/duel/accounts/matchTicket.d.ts +38 -0
- package/dist/generated/duel/accounts/matchTicket.js +45 -0
- package/dist/generated/duel/accounts/queue.d.ts +42 -0
- package/dist/generated/duel/accounts/queue.js +45 -0
- package/dist/generated/duel/accounts/tenant.d.ts +41 -0
- package/dist/generated/duel/accounts/tenant.js +44 -0
- package/dist/generated/duel/errors/duel.d.ts +31 -0
- package/dist/generated/duel/errors/duel.js +35 -0
- package/dist/generated/duel/errors/index.d.ts +8 -0
- package/dist/generated/duel/errors/index.js +9 -0
- package/dist/generated/duel/index.d.ts +12 -0
- package/dist/generated/duel/index.js +13 -0
- package/dist/generated/duel/instructions/cancelTicket.d.ts +45 -0
- package/dist/generated/duel/instructions/cancelTicket.js +56 -0
- package/dist/generated/duel/instructions/closeTicket.d.ts +45 -0
- package/dist/generated/duel/instructions/closeTicket.js +56 -0
- package/dist/generated/duel/instructions/commitTickets.d.ts +39 -0
- package/dist/generated/duel/instructions/commitTickets.js +50 -0
- package/dist/generated/duel/instructions/createTicket.d.ts +48 -0
- package/dist/generated/duel/instructions/createTicket.js +63 -0
- package/dist/generated/duel/instructions/delegateQueue.d.ts +69 -0
- package/dist/generated/duel/instructions/delegateQueue.js +90 -0
- package/dist/generated/duel/instructions/delegateTicket.d.ts +69 -0
- package/dist/generated/duel/instructions/delegateTicket.js +90 -0
- package/dist/generated/duel/instructions/flushMatches.d.ts +37 -0
- package/dist/generated/duel/instructions/flushMatches.js +43 -0
- package/dist/generated/duel/instructions/index.d.ts +19 -0
- package/dist/generated/duel/instructions/index.js +20 -0
- package/dist/generated/duel/instructions/initializeQueue.d.ts +48 -0
- package/dist/generated/duel/instructions/initializeQueue.js +63 -0
- package/dist/generated/duel/instructions/initializeTenant.d.ts +70 -0
- package/dist/generated/duel/instructions/initializeTenant.js +67 -0
- package/dist/generated/duel/instructions/joinQueue.d.ts +51 -0
- package/dist/generated/duel/instructions/joinQueue.js +56 -0
- package/dist/generated/duel/instructions/processUndelegation.d.ts +43 -0
- package/dist/generated/duel/instructions/processUndelegation.js +49 -0
- package/dist/generated/duel/instructions/setupTicketPermission.d.ts +54 -0
- package/dist/generated/duel/instructions/setupTicketPermission.js +63 -0
- package/dist/generated/duel/programs/duel.d.ts +92 -0
- package/dist/generated/duel/programs/duel.js +146 -0
- package/dist/generated/duel/programs/index.d.ts +8 -0
- package/dist/generated/duel/programs/index.js +9 -0
- package/dist/generated/duel/types/accountType.d.ts +25 -0
- package/dist/generated/duel/types/accountType.js +25 -0
- package/dist/generated/duel/types/index.d.ts +13 -0
- package/dist/generated/duel/types/index.js +14 -0
- package/dist/generated/duel/types/matchEntry.d.ts +23 -0
- package/dist/generated/duel/types/matchEntry.js +18 -0
- package/dist/generated/duel/types/matchFound.d.ts +23 -0
- package/dist/generated/duel/types/matchFound.js +18 -0
- package/dist/generated/duel/types/pendingMatch.d.ts +21 -0
- package/dist/generated/duel/types/pendingMatch.js +18 -0
- package/dist/generated/duel/types/queueEntry.d.ts +19 -0
- package/dist/generated/duel/types/queueEntry.js +18 -0
- package/dist/generated/duel/types/ticketStatus.d.ts +40 -0
- package/dist/generated/duel/types/ticketStatus.js +25 -0
- package/dist/generated/rps-game/accounts/gameSession.d.ts +40 -0
- package/dist/generated/rps-game/accounts/gameSession.js +45 -0
- package/dist/generated/rps-game/accounts/index.d.ts +9 -0
- package/dist/generated/rps-game/accounts/index.js +10 -0
- package/dist/generated/rps-game/accounts/playerProfile.d.ts +36 -0
- package/dist/generated/rps-game/accounts/playerProfile.js +47 -0
- package/dist/generated/rps-game/errors/index.d.ts +8 -0
- package/dist/generated/rps-game/errors/index.js +9 -0
- package/dist/generated/rps-game/errors/rpsGame.d.ts +25 -0
- package/dist/generated/rps-game/errors/rpsGame.js +29 -0
- package/dist/generated/rps-game/index.d.ts +12 -0
- package/dist/generated/rps-game/index.js +13 -0
- package/dist/generated/rps-game/instructions/closePlayer.d.ts +45 -0
- package/dist/generated/rps-game/instructions/closePlayer.js +56 -0
- package/dist/generated/rps-game/instructions/delegatePda.d.ts +69 -0
- package/dist/generated/rps-game/instructions/delegatePda.js +90 -0
- package/dist/generated/rps-game/instructions/index.d.ts +16 -0
- package/dist/generated/rps-game/instructions/index.js +17 -0
- package/dist/generated/rps-game/instructions/initializePlayer.d.ts +48 -0
- package/dist/generated/rps-game/instructions/initializePlayer.js +63 -0
- package/dist/generated/rps-game/instructions/makeChoice.d.ts +44 -0
- package/dist/generated/rps-game/instructions/makeChoice.js +46 -0
- package/dist/generated/rps-game/instructions/onMatchFound.d.ts +43 -0
- package/dist/generated/rps-game/instructions/onMatchFound.js +45 -0
- package/dist/generated/rps-game/instructions/persistResults.d.ts +43 -0
- package/dist/generated/rps-game/instructions/persistResults.js +50 -0
- package/dist/generated/rps-game/instructions/processUndelegation.d.ts +43 -0
- package/dist/generated/rps-game/instructions/processUndelegation.js +49 -0
- package/dist/generated/rps-game/instructions/startGame.d.ts +54 -0
- package/dist/generated/rps-game/instructions/startGame.js +67 -0
- package/dist/generated/rps-game/instructions/startGameWithTicket.d.ts +57 -0
- package/dist/generated/rps-game/instructions/startGameWithTicket.js +67 -0
- package/dist/generated/rps-game/programs/index.d.ts +8 -0
- package/dist/generated/rps-game/programs/index.js +9 -0
- package/dist/generated/rps-game/programs/rpsGame.d.ts +78 -0
- package/dist/generated/rps-game/programs/rpsGame.js +118 -0
- package/dist/generated/rps-game/types/accountType.d.ts +34 -0
- package/dist/generated/rps-game/types/accountType.js +25 -0
- package/dist/generated/rps-game/types/choice.d.ts +17 -0
- package/dist/generated/rps-game/types/choice.js +25 -0
- package/dist/generated/rps-game/types/gameResult.d.ts +26 -0
- package/dist/generated/rps-game/types/gameResult.js +25 -0
- package/dist/generated/rps-game/types/index.d.ts +10 -0
- package/dist/generated/rps-game/types/index.js +11 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/player.d.ts +25 -46
- package/dist/player.js +81 -140
- package/dist/tee.d.ts +14 -0
- package/dist/tee.js +62 -0
- package/dist/transaction.d.ts +11 -0
- package/dist/transaction.js +50 -0
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +23 -8
- package/package.json +3 -6
- package/src/admin.ts +151 -161
- package/src/duel.json +66 -7
- package/src/encryption.ts +0 -3
- package/src/generated/duel/accounts/index.ts +11 -0
- package/src/generated/duel/accounts/matchTicket.ts +77 -0
- package/src/generated/duel/accounts/queue.ts +77 -0
- package/src/generated/duel/accounts/tenant.ts +76 -0
- package/src/generated/duel/errors/duel.ts +46 -0
- package/src/generated/duel/errors/index.ts +9 -0
- package/src/generated/duel/index.ts +13 -0
- package/src/generated/duel/instructions/cancelTicket.ts +100 -0
- package/src/generated/duel/instructions/closeTicket.ts +100 -0
- package/src/generated/duel/instructions/commitTickets.ts +84 -0
- package/src/generated/duel/instructions/createTicket.ts +109 -0
- package/src/generated/duel/instructions/delegateQueue.ts +157 -0
- package/src/generated/duel/instructions/delegateTicket.ts +157 -0
- package/src/generated/duel/instructions/flushMatches.ts +76 -0
- package/src/generated/duel/instructions/index.ts +20 -0
- package/src/generated/duel/instructions/initializeQueue.ts +109 -0
- package/src/generated/duel/instructions/initializeTenant.ts +126 -0
- package/src/generated/duel/instructions/joinQueue.ts +106 -0
- package/src/generated/duel/instructions/processUndelegation.ts +86 -0
- package/src/generated/duel/instructions/setupTicketPermission.ts +115 -0
- package/src/generated/duel/programs/duel.ts +108 -0
- package/src/generated/duel/programs/index.ts +9 -0
- package/src/generated/duel/types/accountType.ts +36 -0
- package/src/generated/duel/types/index.ts +14 -0
- package/src/generated/duel/types/matchEntry.ts +25 -0
- package/src/generated/duel/types/matchFound.ts +25 -0
- package/src/generated/duel/types/pendingMatch.ts +25 -0
- package/src/generated/duel/types/queueEntry.ts +25 -0
- package/src/generated/duel/types/ticketStatus.ts +38 -0
- package/src/generated/rps-game/accounts/gameSession.ts +77 -0
- package/src/generated/rps-game/accounts/index.ts +10 -0
- package/src/generated/rps-game/accounts/playerProfile.ts +80 -0
- package/src/generated/rps-game/errors/index.ts +9 -0
- package/src/generated/rps-game/errors/rpsGame.ts +40 -0
- package/src/generated/rps-game/index.ts +13 -0
- package/src/generated/rps-game/instructions/closePlayer.ts +100 -0
- package/src/generated/rps-game/instructions/delegatePda.ts +157 -0
- package/src/generated/rps-game/instructions/index.ts +17 -0
- package/src/generated/rps-game/instructions/initializePlayer.ts +109 -0
- package/src/generated/rps-game/instructions/makeChoice.ts +84 -0
- package/src/generated/rps-game/instructions/onMatchFound.ts +79 -0
- package/src/generated/rps-game/instructions/persistResults.ts +88 -0
- package/src/generated/rps-game/instructions/processUndelegation.ts +86 -0
- package/src/generated/rps-game/instructions/startGame.ts +118 -0
- package/src/generated/rps-game/instructions/startGameWithTicket.ts +121 -0
- package/src/generated/rps-game/programs/index.ts +9 -0
- package/src/generated/rps-game/programs/rpsGame.ts +95 -0
- package/src/generated/rps-game/types/accountType.ts +36 -0
- package/src/generated/rps-game/types/choice.ts +25 -0
- package/src/generated/rps-game/types/gameResult.ts +37 -0
- package/src/generated/rps-game/types/index.ts +11 -0
- package/src/index.ts +2 -1
- package/src/player.ts +129 -192
- package/src/{idl/private_matchmaking.json → rps_game.json} +547 -583
- package/src/tee.ts +79 -0
- package/src/transaction.ts +90 -0
- package/src/utils.ts +35 -20
- package/tsconfig.json +2 -2
- package/dist/client.d.ts +0 -54
- package/dist/client.js +0 -265
- package/dist/duel.json +0 -1207
- package/dist/private_matchmaking.json +0 -534
- package/dist/types.d.ts +0 -635
- package/dist/types.js +0 -2
- package/src/idl/private_matchmaking.ts +0 -1033
- package/src/types.ts +0 -300
package/src/tee.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBase58Decoder,
|
|
3
|
+
createSignableMessage,
|
|
4
|
+
type Address,
|
|
5
|
+
type MessagePartialSigner,
|
|
6
|
+
} from "@solana/kit";
|
|
7
|
+
|
|
8
|
+
export type { MessagePartialSigner as MessageSigner };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Authenticate with the MagicBlock TEE via challenge-sign flow.
|
|
12
|
+
*/
|
|
13
|
+
export async function getAuthToken(
|
|
14
|
+
rpcUrl: string,
|
|
15
|
+
signer: MessagePartialSigner,
|
|
16
|
+
): Promise<{ token: string; expiresAt: number }> {
|
|
17
|
+
const challengeRes = await fetch(
|
|
18
|
+
`${rpcUrl}/auth/challenge?pubkey=${signer.address}`
|
|
19
|
+
);
|
|
20
|
+
if (!challengeRes.ok) {
|
|
21
|
+
throw new Error(`TEE challenge failed: ${challengeRes.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const { challenge } = (await challengeRes.json()) as { challenge: string };
|
|
24
|
+
|
|
25
|
+
const challengeBytes = new TextEncoder().encode(challenge);
|
|
26
|
+
const [sigDict] = await signer.signMessages([createSignableMessage(challengeBytes)]);
|
|
27
|
+
const signature = sigDict[signer.address as Address];
|
|
28
|
+
const signatureString = getBase58Decoder().decode(signature);
|
|
29
|
+
|
|
30
|
+
const tokenRes = await fetch(`${rpcUrl}/auth/login`, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
pubkey: signer.address,
|
|
35
|
+
challenge,
|
|
36
|
+
signature: signatureString,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
const authJson = (await tokenRes.json()) as { token: string; expiresAt?: number; error?: string };
|
|
40
|
+
if (tokenRes.status !== 200) {
|
|
41
|
+
throw new Error(`Failed to authenticate: ${authJson.error}`);
|
|
42
|
+
}
|
|
43
|
+
const expiresAt = authJson.expiresAt ?? Date.now() + 1000 * 60 * 60 * 24 * 30;
|
|
44
|
+
return { token: authJson.token, expiresAt };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Poll the TEE /permission endpoint until the given PDA has authorized users,
|
|
49
|
+
* indicating delegation is active. Returns false on timeout (does not throw).
|
|
50
|
+
*/
|
|
51
|
+
export async function waitUntilPermissionActive(
|
|
52
|
+
teeUrlWithToken: string,
|
|
53
|
+
pda: Address,
|
|
54
|
+
timeoutMs = 30000,
|
|
55
|
+
): Promise<boolean> {
|
|
56
|
+
// Parse URL: "https://host/path?token=xxx" -> baseUrl="https://host/path", tokenParam="token=xxx"
|
|
57
|
+
const [baseUrl, tokenParam] = teeUrlWithToken.replace("/?", "?").split("?");
|
|
58
|
+
let permissionUrl: string;
|
|
59
|
+
if (tokenParam) {
|
|
60
|
+
permissionUrl = `${baseUrl}/permission?${tokenParam}&pubkey=${pda}`;
|
|
61
|
+
} else {
|
|
62
|
+
permissionUrl = `${baseUrl}/permission?pubkey=${pda}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
while (Date.now() - start < timeoutMs) {
|
|
67
|
+
try {
|
|
68
|
+
const res = await fetch(permissionUrl);
|
|
69
|
+
if (res.ok) {
|
|
70
|
+
const { authorizedUsers } = (await res.json()) as { authorizedUsers?: unknown[] };
|
|
71
|
+
if (authorizedUsers && authorizedUsers.length > 0) return true;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// ignore transient errors, keep polling
|
|
75
|
+
}
|
|
76
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTransactionMessage,
|
|
3
|
+
setTransactionMessageFeePayerSigner,
|
|
4
|
+
setTransactionMessageLifetimeUsingBlockhash,
|
|
5
|
+
appendTransactionMessageInstruction,
|
|
6
|
+
appendTransactionMessageInstructions,
|
|
7
|
+
signTransactionMessageWithSigners,
|
|
8
|
+
getBase64EncodedWireTransaction,
|
|
9
|
+
pipe,
|
|
10
|
+
type Instruction,
|
|
11
|
+
type SolanaRpcApi,
|
|
12
|
+
type Rpc,
|
|
13
|
+
type TransactionSigner,
|
|
14
|
+
} from "@solana/kit";
|
|
15
|
+
|
|
16
|
+
type SolanaRpc = Rpc<SolanaRpcApi>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build, sign with a Kit keypair signer, and send a single instruction.
|
|
20
|
+
*/
|
|
21
|
+
export async function sendInstruction(
|
|
22
|
+
rpc: SolanaRpc,
|
|
23
|
+
instruction: Instruction,
|
|
24
|
+
signer: TransactionSigner,
|
|
25
|
+
): Promise<string> {
|
|
26
|
+
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
const tx: any = pipe(
|
|
30
|
+
createTransactionMessage({ version: 0 as const }),
|
|
31
|
+
(m) => setTransactionMessageFeePayerSigner(signer, m),
|
|
32
|
+
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
|
|
33
|
+
(m) => appendTransactionMessageInstruction(instruction, m),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const signedTx = await signTransactionMessageWithSigners(tx);
|
|
37
|
+
const encoded = getBase64EncodedWireTransaction(signedTx);
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
const sig = await (rpc.sendTransaction(encoded as any, {
|
|
41
|
+
encoding: "base64",
|
|
42
|
+
skipPreflight: true,
|
|
43
|
+
}).send() as Promise<string>);
|
|
44
|
+
|
|
45
|
+
// Poll for status to detect runtime errors
|
|
46
|
+
for (let i = 0; i < 8; i++) {
|
|
47
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
const statuses = await (rpc as any).getSignatureStatuses([sig], { searchTransactionHistory: false }).send().catch(() => null);
|
|
50
|
+
const status = statuses?.value?.[0];
|
|
51
|
+
if (status) {
|
|
52
|
+
if (status.err) {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
console.error(`[TX] ${sig.slice(0, 16)}... FAILED:`, JSON.stringify(status.err, (_, v) => typeof v === 'bigint' ? v.toString() : v));
|
|
55
|
+
} else if (status.confirmationStatus) {
|
|
56
|
+
console.log(`[TX] ${sig.slice(0, 16)}... ${status.confirmationStatus}`);
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return sig;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build, sign, and send multiple instructions in a single transaction.
|
|
66
|
+
*/
|
|
67
|
+
export async function sendInstructions(
|
|
68
|
+
rpc: SolanaRpc,
|
|
69
|
+
instructions: Instruction[],
|
|
70
|
+
signer: TransactionSigner,
|
|
71
|
+
): Promise<string> {
|
|
72
|
+
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
const tx: any = pipe(
|
|
76
|
+
createTransactionMessage({ version: 0 as const }),
|
|
77
|
+
(m) => setTransactionMessageFeePayerSigner(signer, m),
|
|
78
|
+
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
|
|
79
|
+
(m) => appendTransactionMessageInstructions(instructions, m),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const signedTx = await signTransactionMessageWithSigners(tx);
|
|
83
|
+
const encoded = getBase64EncodedWireTransaction(signedTx);
|
|
84
|
+
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
return rpc.sendTransaction(encoded as any, {
|
|
87
|
+
encoding: "base64",
|
|
88
|
+
skipPreflight: true,
|
|
89
|
+
}).send() as Promise<string>;
|
|
90
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
getProgramDerivedAddress,
|
|
3
|
+
getAddressEncoder,
|
|
4
|
+
getUtf8Encoder,
|
|
5
|
+
type Address,
|
|
6
|
+
} from "@solana/kit";
|
|
7
|
+
|
|
8
|
+
const addressEncoder = getAddressEncoder();
|
|
9
|
+
const utf8Encoder = getUtf8Encoder();
|
|
3
10
|
|
|
4
11
|
export const QUEUE_SEED = "queue";
|
|
5
12
|
export const TENANT_SEED = "tenant";
|
|
6
13
|
export const TICKET_SEED = "ticket";
|
|
7
14
|
|
|
8
|
-
export function deriveQueuePda(programId:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
export async function deriveQueuePda(programId: Address, authority: Address): Promise<Address> {
|
|
16
|
+
const [pda] = await getProgramDerivedAddress({
|
|
17
|
+
programAddress: programId,
|
|
18
|
+
seeds: [utf8Encoder.encode(QUEUE_SEED), addressEncoder.encode(authority)],
|
|
19
|
+
});
|
|
20
|
+
return pda;
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
export function deriveTenantPda(programId:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
export async function deriveTenantPda(programId: Address, authority: Address): Promise<Address> {
|
|
24
|
+
const [pda] = await getProgramDerivedAddress({
|
|
25
|
+
programAddress: programId,
|
|
26
|
+
seeds: [utf8Encoder.encode(TENANT_SEED), addressEncoder.encode(authority)],
|
|
27
|
+
});
|
|
28
|
+
return pda;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
export function deriveTicketPda(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
export async function deriveTicketPda(
|
|
32
|
+
programId: Address,
|
|
33
|
+
player: Address,
|
|
34
|
+
tenant: Address
|
|
35
|
+
): Promise<Address> {
|
|
36
|
+
const [pda] = await getProgramDerivedAddress({
|
|
37
|
+
programAddress: programId,
|
|
38
|
+
seeds: [
|
|
39
|
+
utf8Encoder.encode(TICKET_SEED),
|
|
40
|
+
addressEncoder.encode(player),
|
|
41
|
+
addressEncoder.encode(tenant),
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
return pda;
|
|
30
45
|
}
|
package/tsconfig.json
CHANGED
package/dist/client.d.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Program, AnchorProvider } from "@coral-xyz/anchor";
|
|
2
|
-
import { PublicKey, Keypair, TransactionSignature } from "@solana/web3.js";
|
|
3
|
-
import { PrivateMatchmaking } from "./idl/private_matchmaking";
|
|
4
|
-
import { MatchmakingClientConfig, QueueHead, QueuePage, PlayerStatus, JoinQueueResult } from "./types";
|
|
5
|
-
import { EncryptionProvider } from "./encryption";
|
|
6
|
-
export declare class MatchmakingClient {
|
|
7
|
-
program: Program<PrivateMatchmaking>;
|
|
8
|
-
provider: AnchorProvider;
|
|
9
|
-
config: MatchmakingClientConfig;
|
|
10
|
-
encryption: EncryptionProvider;
|
|
11
|
-
constructor(provider: AnchorProvider, programId: PublicKey, config?: MatchmakingClientConfig);
|
|
12
|
-
/**
|
|
13
|
-
* Initialize a new matchmaking queue.
|
|
14
|
-
*/
|
|
15
|
-
initializeQueue(queueId: string, config: any, capacity: number, pageSize?: number): Promise<PublicKey>;
|
|
16
|
-
/**
|
|
17
|
-
* Initialize a specific page for the queue.
|
|
18
|
-
*/
|
|
19
|
-
initializePage(queue: PublicKey, index: number): Promise<PublicKey>;
|
|
20
|
-
/**
|
|
21
|
-
* Delegate the queue to the Privacy Layer (Ephemeral Rollup).
|
|
22
|
-
*/
|
|
23
|
-
delegateQueue(queueId: string, validatorOverride?: PublicKey): Promise<TransactionSignature>;
|
|
24
|
-
/**
|
|
25
|
-
* Join the queue.
|
|
26
|
-
*/
|
|
27
|
-
joinQueue(queue: PublicKey, playerGameAccount: PublicKey, tenantProgramId: PublicKey): Promise<JoinQueueResult>;
|
|
28
|
-
/**
|
|
29
|
-
* Unlock a player manually (refund rent).
|
|
30
|
-
*/
|
|
31
|
-
unlockPlayer(playerGameAccount: PublicKey, playerWallet: PublicKey): Promise<TransactionSignature>;
|
|
32
|
-
/**
|
|
33
|
-
* Process matches on a specific page.
|
|
34
|
-
* Usually called by the off-chain worker or manually for testing.
|
|
35
|
-
*/
|
|
36
|
-
processMatch(queue: PublicKey, pageIndex: number): Promise<TransactionSignature>;
|
|
37
|
-
/**
|
|
38
|
-
* Resize the queue capacity.
|
|
39
|
-
*/
|
|
40
|
-
resizeQueue(queue: PublicKey, newCapacity: number): Promise<TransactionSignature>;
|
|
41
|
-
getQueue(queuePda: PublicKey): Promise<QueueHead>;
|
|
42
|
-
getPage(pagePda: PublicKey): Promise<QueuePage>;
|
|
43
|
-
getPlayerStatus(statusPda: PublicKey): Promise<PlayerStatus>;
|
|
44
|
-
getPlayerStatusForGameAccount(playerGameAccount: PublicKey): Promise<PlayerStatus>;
|
|
45
|
-
createMockPlayer(playerAccount: Keypair, elo: number): Promise<TransactionSignature>;
|
|
46
|
-
/**
|
|
47
|
-
* Close a queue page to reclaim rent.
|
|
48
|
-
*/
|
|
49
|
-
closePage(queue: PublicKey, index: number): Promise<TransactionSignature>;
|
|
50
|
-
/**
|
|
51
|
-
* Close a queue head to reclaim rent.
|
|
52
|
-
*/
|
|
53
|
-
closeQueue(queueId: string): Promise<TransactionSignature>;
|
|
54
|
-
}
|
package/dist/client.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.MatchmakingClient = void 0;
|
|
27
|
-
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
28
|
-
const anchor_1 = require("@coral-xyz/anchor");
|
|
29
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
30
|
-
const utils_1 = require("./utils");
|
|
31
|
-
const encryption_1 = require("./encryption");
|
|
32
|
-
// Default Validator for Devnet (MagicBlock)
|
|
33
|
-
const DEFAULT_VALIDATOR = "FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA";
|
|
34
|
-
class MatchmakingClient {
|
|
35
|
-
constructor(provider, programId, config = {}) {
|
|
36
|
-
this.provider = provider;
|
|
37
|
-
this.config = config;
|
|
38
|
-
this.encryption = new encryption_1.EncryptionProvider();
|
|
39
|
-
// Load the IDL (we assume it's bundled or we can require it if running in node)
|
|
40
|
-
// For SDK purity, we should import the JSON.
|
|
41
|
-
const idl = require("./idl/private_matchmaking.json");
|
|
42
|
-
idl.address = programId.toBase58();
|
|
43
|
-
this.program = new anchor_1.Program(idl, provider);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Initialize a new matchmaking queue.
|
|
47
|
-
*/
|
|
48
|
-
async initializeQueue(queueId, config, capacity, pageSize = 50) {
|
|
49
|
-
const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
|
|
50
|
-
// Ensure tenant ID is set
|
|
51
|
-
const tenantProgramId = config.tenantProgramId || this.provider.wallet.publicKey;
|
|
52
|
-
const tx = await this.program.methods
|
|
53
|
-
.initializeQueue(queueId, config, capacity, pageSize)
|
|
54
|
-
.accounts({
|
|
55
|
-
// queue: queuePda,
|
|
56
|
-
// authority: this.provider.wallet.publicKey,
|
|
57
|
-
tenantProgramId: tenantProgramId,
|
|
58
|
-
// systemProgram: SystemProgram.programId,
|
|
59
|
-
})
|
|
60
|
-
.rpc(this.config.confirmOptions);
|
|
61
|
-
console.log(`Initialized Queue: ${queuePda.toBase58()} in tx: ${tx}`);
|
|
62
|
-
// Initialize Pages (Ring Buffer)
|
|
63
|
-
for (let i = 0; i < capacity; i++) {
|
|
64
|
-
await this.initializePage(queuePda, i);
|
|
65
|
-
}
|
|
66
|
-
return queuePda;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Initialize a specific page for the queue.
|
|
70
|
-
*/
|
|
71
|
-
async initializePage(queue, index) {
|
|
72
|
-
const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, index);
|
|
73
|
-
const tx = await this.program.methods
|
|
74
|
-
.initializePage(new anchor.BN(index))
|
|
75
|
-
.accounts({
|
|
76
|
-
queue: queue,
|
|
77
|
-
// page: pagePda,
|
|
78
|
-
// authority: this.provider.wallet.publicKey,
|
|
79
|
-
// systemProgram: SystemProgram.programId,
|
|
80
|
-
})
|
|
81
|
-
.rpc(this.config.confirmOptions);
|
|
82
|
-
console.log(`Initialized Page ${index}: ${pagePda.toBase58()}`);
|
|
83
|
-
return pagePda;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Delegate the queue to the Privacy Layer (Ephemeral Rollup).
|
|
87
|
-
*/
|
|
88
|
-
async delegateQueue(queueId, validatorOverride) {
|
|
89
|
-
const validator = validatorOverride || new web3_js_1.PublicKey(DEFAULT_VALIDATOR);
|
|
90
|
-
const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
|
|
91
|
-
const tx = await this.program.methods
|
|
92
|
-
.delegateQueue(queueId)
|
|
93
|
-
.preInstructions([
|
|
94
|
-
web3_js_1.ComputeBudgetProgram.requestHeapFrame({ bytes: 256 * 1024 })
|
|
95
|
-
])
|
|
96
|
-
.accounts({
|
|
97
|
-
// pda: queuePda,
|
|
98
|
-
// authority: this.provider.wallet.publicKey,
|
|
99
|
-
// payer: this.provider.wallet.publicKey,
|
|
100
|
-
validator: validator,
|
|
101
|
-
})
|
|
102
|
-
.rpc(this.config.confirmOptions);
|
|
103
|
-
console.log(`Delegated Queue ${queueId}: ${tx}`);
|
|
104
|
-
return tx;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Join the queue.
|
|
108
|
-
*/
|
|
109
|
-
async joinQueue(queue, playerGameAccount, tenantProgramId) {
|
|
110
|
-
// Fetch queue to determine current WRITE index
|
|
111
|
-
const queueAccount = await this.getQueue(queue);
|
|
112
|
-
const capacity = queueAccount.capacity;
|
|
113
|
-
const writeIndex = queueAccount.writePageIndex.toNumber();
|
|
114
|
-
const currentIndex = writeIndex % capacity;
|
|
115
|
-
const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, currentIndex);
|
|
116
|
-
const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
|
|
117
|
-
// Encryption Handshake Integration
|
|
118
|
-
let instructionData;
|
|
119
|
-
if (this.config.encrypted) {
|
|
120
|
-
console.log("🔒 Encrypted Queue Join Init...");
|
|
121
|
-
// TODO: Fetch Validator Key from on-chain Registry or Delegate Account
|
|
122
|
-
// For now, using a mock dummy key for demonstration of the flow
|
|
123
|
-
// In PROD: const validatorKey = await this.getValidatorKey(queue);
|
|
124
|
-
const mockValidatorKey = await this.encryption.createMockValidatorKey(); // Valid P-256 Public Key
|
|
125
|
-
// Perform Encryption
|
|
126
|
-
// Real payload would vary (e.g. Rock/Paper/Scissor choice)
|
|
127
|
-
const payload = Buffer.from("PLAYER_CHOICE_ROCK");
|
|
128
|
-
const { encrypted, clientPublicKey } = await this.encryption.encryptPayload(payload, mockValidatorKey);
|
|
129
|
-
console.log("🔒 Payload Encrypted. Client PubKey:", Buffer.from(clientPublicKey).toString('hex'));
|
|
130
|
-
// In a real implementation, 'encrypted' and 'clientPublicKey' would be passed
|
|
131
|
-
// as arguments to the 'joinQueue' instruction modification.
|
|
132
|
-
// Since the IDL isn't updated for arguments yet, we just log it.
|
|
133
|
-
}
|
|
134
|
-
const tx = await this.program.methods
|
|
135
|
-
.joinQueue()
|
|
136
|
-
.accounts({
|
|
137
|
-
queue: queue,
|
|
138
|
-
// page: pagePda,
|
|
139
|
-
page: pagePda,
|
|
140
|
-
// playerStatus: statusPda,
|
|
141
|
-
// playerAuthority: this.provider.wallet.publicKey,
|
|
142
|
-
playerGameAccount: playerGameAccount,
|
|
143
|
-
tenantProgram: tenantProgramId, // Added for Matchable Interface CPI
|
|
144
|
-
// systemProgram: SystemProgram.programId,
|
|
145
|
-
})
|
|
146
|
-
.rpc(this.config.confirmOptions);
|
|
147
|
-
console.log(`Joined Queue at Page ${currentIndex}: ${tx} (Lock: ${statusPda.toBase58()})`);
|
|
148
|
-
// Future Logic: If SDK receives a return log/event indicating "Instant Match", parse it.
|
|
149
|
-
// For now, default to "Queued".
|
|
150
|
-
return {
|
|
151
|
-
status: "Queued",
|
|
152
|
-
tx,
|
|
153
|
-
statusPda
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Unlock a player manually (refund rent).
|
|
158
|
-
*/
|
|
159
|
-
async unlockPlayer(playerGameAccount, playerWallet) {
|
|
160
|
-
const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
|
|
161
|
-
const statusAccount = await this.getPlayerStatus(statusPda);
|
|
162
|
-
const queueAddress = statusAccount.queue;
|
|
163
|
-
const tx = await this.program.methods
|
|
164
|
-
.unlockPlayer()
|
|
165
|
-
.accounts({
|
|
166
|
-
queue: queueAddress,
|
|
167
|
-
// authority: this.provider.wallet.publicKey,
|
|
168
|
-
// playerStatus: statusPda,
|
|
169
|
-
player: playerWallet,
|
|
170
|
-
playerGameAccount: playerGameAccount
|
|
171
|
-
})
|
|
172
|
-
.rpc(this.config.confirmOptions);
|
|
173
|
-
console.log(`Unlocked Player: ${tx}`);
|
|
174
|
-
return tx;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Process matches on a specific page.
|
|
178
|
-
* Usually called by the off-chain worker or manually for testing.
|
|
179
|
-
*/
|
|
180
|
-
async processMatch(queue, pageIndex) {
|
|
181
|
-
const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, pageIndex);
|
|
182
|
-
const tx = await this.program.methods
|
|
183
|
-
.processMatch(new anchor.BN(pageIndex))
|
|
184
|
-
.accounts({
|
|
185
|
-
queueAccount: queue,
|
|
186
|
-
// page: pagePda,
|
|
187
|
-
})
|
|
188
|
-
.rpc(this.config.confirmOptions);
|
|
189
|
-
return tx;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Resize the queue capacity.
|
|
193
|
-
*/
|
|
194
|
-
async resizeQueue(queue, newCapacity) {
|
|
195
|
-
const tx = await this.program.methods
|
|
196
|
-
.resizeQueue(newCapacity)
|
|
197
|
-
.accounts({
|
|
198
|
-
queue: queue,
|
|
199
|
-
// authority: this.provider.wallet.publicKey,
|
|
200
|
-
})
|
|
201
|
-
.rpc(this.config.confirmOptions);
|
|
202
|
-
console.log(`Resized Queue to ${newCapacity}: ${tx}`);
|
|
203
|
-
return tx;
|
|
204
|
-
}
|
|
205
|
-
// --- State Fetchers ---
|
|
206
|
-
async getQueue(queuePda) {
|
|
207
|
-
return await this.program.account.queueHead.fetch(queuePda);
|
|
208
|
-
}
|
|
209
|
-
async getPage(pagePda) {
|
|
210
|
-
return await this.program.account.queuePage.fetch(pagePda);
|
|
211
|
-
}
|
|
212
|
-
async getPlayerStatus(statusPda) {
|
|
213
|
-
return await this.program.account.playerStatus.fetch(statusPda);
|
|
214
|
-
}
|
|
215
|
-
async getPlayerStatusForGameAccount(playerGameAccount) {
|
|
216
|
-
const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
|
|
217
|
-
return await this.getPlayerStatus(statusPda);
|
|
218
|
-
}
|
|
219
|
-
// --- Dev Helpers ---
|
|
220
|
-
async createMockPlayer(playerAccount, elo) {
|
|
221
|
-
const tx = await this.program.methods
|
|
222
|
-
.createMockPlayer(new anchor.BN(elo))
|
|
223
|
-
.accounts({
|
|
224
|
-
playerAccount: playerAccount.publicKey,
|
|
225
|
-
authority: this.provider.wallet.publicKey,
|
|
226
|
-
// systemProgram: SystemProgram.programId,
|
|
227
|
-
})
|
|
228
|
-
.signers([playerAccount])
|
|
229
|
-
.rpc(this.config.confirmOptions);
|
|
230
|
-
return tx;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Close a queue page to reclaim rent.
|
|
234
|
-
*/
|
|
235
|
-
async closePage(queue, index) {
|
|
236
|
-
const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, index);
|
|
237
|
-
const tx = await this.program.methods
|
|
238
|
-
.closePage(new anchor.BN(index))
|
|
239
|
-
.accounts({
|
|
240
|
-
queue: queue,
|
|
241
|
-
// page: pagePda, // Auto-resolved
|
|
242
|
-
authority: this.provider.wallet.publicKey,
|
|
243
|
-
})
|
|
244
|
-
.rpc(this.config.confirmOptions);
|
|
245
|
-
console.log(`Closed Page ${index}: ${pagePda.toBase58()}`);
|
|
246
|
-
return tx;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Close a queue head to reclaim rent.
|
|
250
|
-
*/
|
|
251
|
-
async closeQueue(queueId) {
|
|
252
|
-
const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
|
|
253
|
-
const tx = await this.program.methods
|
|
254
|
-
.closeQueue(queueId)
|
|
255
|
-
.accounts({
|
|
256
|
-
// queue: queuePda, // Auto-resolved
|
|
257
|
-
authority: this.provider.wallet.publicKey,
|
|
258
|
-
})
|
|
259
|
-
.rpc(this.config.confirmOptions);
|
|
260
|
-
console.log(`Closed Queue: ${queuePda.toBase58()}`);
|
|
261
|
-
return tx;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
exports.MatchmakingClient = MatchmakingClient;
|
|
265
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDBEQUE0QztBQUM1Qyw4Q0FBaUU7QUFDakUsNkNBTXlCO0FBR3pCLG1DQUErRTtBQUMvRSw2Q0FBa0Q7QUFFbEQsNENBQTRDO0FBQzVDLE1BQU0saUJBQWlCLEdBQUcsOENBQThDLENBQUM7QUFFekUsTUFBYSxpQkFBaUI7SUFNMUIsWUFDSSxRQUF3QixFQUN4QixTQUFvQixFQUNwQixTQUFrQyxFQUFFO1FBRXBDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSwrQkFBa0IsRUFBRSxDQUFDO1FBRTNDLGdGQUFnRjtRQUNoRiw2Q0FBNkM7UUFDN0MsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLGdDQUFnQyxDQUFDLENBQUM7UUFDdEQsR0FBRyxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDbkMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLGdCQUFPLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxlQUFlLENBQ2pCLE9BQWUsRUFDZixNQUFXLEVBQ1gsUUFBZ0IsRUFDaEIsV0FBbUIsRUFBRTtRQUVyQixNQUFNLFFBQVEsR0FBRyxJQUFBLHNCQUFjLEVBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRWpHLDBCQUEwQjtRQUMxQixNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQyxRQUFRLENB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QyxjQUFjLENBQUMsQ0FBQztRQUNyQyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixRQUFRLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3BELE9BQU8sRUFBRSxDQUFDO0lBQ2QsQ0FBQztDQUNKO0FBclNELDhDQXFTQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGFuY2hvciBmcm9tIFwiQGNvcmFsLXh5ei9hbmNob3JcIjtcbmltcG9ydCB7IFByb2dyYW0sIElkbCwgQW5jaG9yUHJvdmlkZXIgfSBmcm9tIFwiQGNvcmFsLXh5ei9hbmNob3JcIjtcbmltcG9ydCB7IFxuICAgIFB1YmxpY0tleSwgXG4gICAgU3lzdGVtUHJvZ3JhbSwgXG4gICAgS2V5cGFpciwgXG4gICAgVHJhbnNhY3Rpb25TaWduYXR1cmUsXG4gICAgQ29tcHV0ZUJ1ZGdldFByb2dyYW1cbn0gZnJvbSBcIkBzb2xhbmEvd2ViMy5qc1wiO1xuaW1wb3J0IHsgUHJpdmF0ZU1hdGNobWFraW5nIH0gZnJvbSBcIi4vaWRsL3ByaXZhdGVfbWF0Y2htYWtpbmdcIjtcbmltcG9ydCB7IE1hdGNobWFraW5nQ2xpZW50Q29uZmlnLCBRdWV1ZUhlYWQsIFF1ZXVlUGFnZSwgUGxheWVyU3RhdHVzLCBKb2luUXVldWVSZXN1bHQgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgZGVyaXZlUGFnZVBkYSwgZGVyaXZlUGxheWVyU3RhdHVzUGRhLCBkZXJpdmVRdWV1ZVBkYSB9IGZyb20gXCIuL3V0aWxzXCI7XG5pbXBvcnQgeyBFbmNyeXB0aW9uUHJvdmlkZXIgfSBmcm9tIFwiLi9lbmNyeXB0aW9uXCI7XG5cbi8vIERlZmF1bHQgVmFsaWRhdG9yIGZvciBEZXZuZXQgKE1hZ2ljQmxvY2spXG5jb25zdCBERUZBVUxUX1ZBTElEQVRPUiA9IFwiRm5FNlZKVDVRTlpkZWRaUG5Db0xzQVJnQndvRTZEZUpOakJzMkgxZ3lTWEFcIjtcblxuZXhwb3J0IGNsYXNzIE1hdGNobWFraW5nQ2xpZW50IHtcbiAgICBwcm9ncmFtOiBQcm9ncmFtPFByaXZhdGVNYXRjaG1ha2luZz47XG4gICAgcHJvdmlkZXI6IEFuY2hvclByb3ZpZGVyO1xuICAgIGNvbmZpZzogTWF0Y2htYWtpbmdDbGllbnRDb25maWc7XG4gICAgZW5jcnlwdGlvbjogRW5jcnlwdGlvblByb3ZpZGVyO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByb3ZpZGVyOiBBbmNob3JQcm92aWRlciwgXG4gICAgICAgIHByb2dyYW1JZDogUHVibGljS2V5LFxuICAgICAgICBjb25maWc6IE1hdGNobWFraW5nQ2xpZW50Q29uZmlnID0ge31cbiAgICApIHtcbiAgICAgICAgdGhpcy5wcm92aWRlciA9IHByb3ZpZGVyO1xuICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgdGhpcy5lbmNyeXB0aW9uID0gbmV3IEVuY3J5cHRpb25Qcm92aWRlcigpO1xuICAgICAgICBcbiAgICAgICAgLy8gTG9hZCB0aGUgSURMICh3ZSBhc3N1bWUgaXQncyBidW5kbGVkIG9yIHdlIGNhbiByZXF1aXJlIGl0IGlmIHJ1bm5pbmcgaW4gbm9kZSlcbiAgICAgICAgLy8gRm9yIFNESyBwdXJpdHksIHdlIHNob3VsZCBpbXBvcnQgdGhlIEpTT04uXG4gICAgICAgIGNvbnN0IGlkbCA9IHJlcXVpcmUoXCIuL2lkbC9wcml2YXRlX21hdGNobWFraW5nLmpzb25cIik7XG4gICAgICAgIGlkbC5hZGRyZXNzID0gcHJvZ3JhbUlkLnRvQmFzZTU4KCk7XG4gICAgICAgIHRoaXMucHJvZ3JhbSA9IG5ldyBQcm9ncmFtKGlkbCwgcHJvdmlkZXIpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgYSBuZXcgbWF0Y2htYWtpbmcgcXVldWUuXG4gICAgICovXG4gICAgYXN5bmMgaW5pdGlhbGl6ZVF1ZXVlKFxuICAgICAgICBxdWV1ZUlkOiBzdHJpbmcsIFxuICAgICAgICBjb25maWc6IGFueSwgXG4gICAgICAgIGNhcGFjaXR5OiBudW1iZXIsXG4gICAgICAgIHBhZ2VTaXplOiBudW1iZXIgPSA1MFxuICAgICk6IFByb21pc2U8UHVibGljS2V5PiB7XG4gICAgICAgIGNvbnN0IHF1ZXVlUGRhID0gZGVyaXZlUXVldWVQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LCBxdWV1ZUlkKTtcblxuICAgICAgICAvLyBFbnN1cmUgdGVuYW50IElEIGlzIHNldFxuICAgICAgICBjb25zdCB0ZW5hbnRQcm9ncmFtSWQgPSBjb25maWcudGVuYW50UHJvZ3JhbUlkIHx8IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleTtcblxuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMucHJvZ3JhbS5tZXRob2RzXG4gICAgICAgICAgICAuaW5pdGlhbGl6ZVF1ZXVlKHF1ZXVlSWQsIGNvbmZpZywgY2FwYWNpdHksIHBhZ2VTaXplKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICAvLyBxdWV1ZTogcXVldWVQZGEsXG4gICAgICAgICAgICAgICAgLy8gYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICAgICAgdGVuYW50UHJvZ3JhbUlkOiB0ZW5hbnRQcm9ncmFtSWQsXG4gICAgICAgICAgICAgICAgLy8gc3lzdGVtUHJvZ3JhbTogU3lzdGVtUHJvZ3JhbS5wcm9ncmFtSWQsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIFxuICAgICAgICBjb25zb2xlLmxvZyhgSW5pdGlhbGl6ZWQgUXVldWU6ICR7cXVldWVQZGEudG9CYXNlNTgoKX0gaW4gdHg6ICR7dHh9YCk7XG4gICAgICAgIFxuICAgICAgICAvLyBJbml0aWFsaXplIFBhZ2VzIChSaW5nIEJ1ZmZlcilcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjYXBhY2l0eTsgaSsrKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVQYWdlKHF1ZXVlUGRhLCBpKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHF1ZXVlUGRhO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgYSBzcGVjaWZpYyBwYWdlIGZvciB0aGUgcXVldWUuXG4gICAgICovXG4gICAgYXN5bmMgaW5pdGlhbGl6ZVBhZ2UocXVldWU6IFB1YmxpY0tleSwgaW5kZXg6IG51bWJlcik6IFByb21pc2U8UHVibGljS2V5PiB7XG4gICAgICAgIGNvbnN0IHBhZ2VQZGEgPSBkZXJpdmVQYWdlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHF1ZXVlLCBpbmRleCk7XG4gICAgICAgIFxuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMucHJvZ3JhbS5tZXRob2RzXG4gICAgICAgICAgICAuaW5pdGlhbGl6ZVBhZ2UobmV3IGFuY2hvci5CTihpbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLFxuICAgICAgICAgICAgICAgIC8vIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIC8vIHN5c3RlbVByb2dyYW06IFN5c3RlbVByb2dyYW0ucHJvZ3JhbUlkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICAgICAgXG4gICAgICAgIGNvbnNvbGUubG9nKGBJbml0aWFsaXplZCBQYWdlICR7aW5kZXh9OiAke3BhZ2VQZGEudG9CYXNlNTgoKX1gKTtcbiAgICAgICAgcmV0dXJuIHBhZ2VQZGE7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGVsZWdhdGUgdGhlIHF1ZXVlIHRvIHRoZSBQcml2YWN5IExheWVyIChFcGhlbWVyYWwgUm9sbHVwKS5cbiAgICAgKi9cbiAgICBhc3luYyBkZWxlZ2F0ZVF1ZXVlKHF1ZXVlSWQ6IHN0cmluZywgdmFsaWRhdG9yT3ZlcnJpZGU/OiBQdWJsaWNLZXkpOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHZhbGlkYXRvciA9IHZhbGlkYXRvck92ZXJyaWRlIHx8IG5ldyBQdWJsaWNLZXkoREVGQVVMVF9WQUxJREFUT1IpO1xuICAgICAgICBjb25zdCBxdWV1ZVBkYSA9IGRlcml2ZVF1ZXVlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSwgcXVldWVJZCk7XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmRlbGVnYXRlUXVldWUocXVldWVJZClcbiAgICAgICAgICAgIC5wcmVJbnN0cnVjdGlvbnMoW1xuICAgICAgICAgICAgICAgIENvbXB1dGVCdWRnZXRQcm9ncmFtLnJlcXVlc3RIZWFwRnJhbWUoeyBieXRlczogMjU2ICogMTAyNCB9KVxuICAgICAgICAgICAgXSlcbiAgICAgICAgICAgIC5hY2NvdW50cyh7XG4gICAgICAgICAgICAgICAgLy8gcGRhOiBxdWV1ZVBkYSxcbiAgICAgICAgICAgICAgICAvLyBhdXRob3JpdHk6IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICAvLyBwYXllcjogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIHZhbGlkYXRvcjogdmFsaWRhdG9yLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICAgICAgXG4gICAgICAgIGNvbnNvbGUubG9nKGBEZWxlZ2F0ZWQgUXVldWUgJHtxdWV1ZUlkfTogJHt0eH1gKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEpvaW4gdGhlIHF1ZXVlLlxuICAgICAqL1xuICAgIGFzeW5jIGpvaW5RdWV1ZShcbiAgICAgICAgcXVldWU6IFB1YmxpY0tleSwgXG4gICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBQdWJsaWNLZXksIFxuICAgICAgICB0ZW5hbnRQcm9ncmFtSWQ6IFB1YmxpY0tleVxuICAgICk6IFByb21pc2U8Sm9pblF1ZXVlUmVzdWx0PiB7XG4gICAgICAgIC8vIEZldGNoIHF1ZXVlIHRvIGRldGVybWluZSBjdXJyZW50IFdSSVRFIGluZGV4XG4gICAgICAgIGNvbnN0IHF1ZXVlQWNjb3VudCA9IGF3YWl0IHRoaXMuZ2V0UXVldWUocXVldWUpO1xuICAgICAgICBjb25zdCBjYXBhY2l0eSA9IHF1ZXVlQWNjb3VudC5jYXBhY2l0eTtcbiAgICAgICAgY29uc3Qgd3JpdGVJbmRleCA9IHF1ZXVlQWNjb3VudC53cml0ZVBhZ2VJbmRleC50b051bWJlcigpO1xuICAgICAgICBjb25zdCBjdXJyZW50SW5kZXggPSB3cml0ZUluZGV4ICUgY2FwYWNpdHk7XG5cbiAgICAgICAgY29uc3QgcGFnZVBkYSA9IGRlcml2ZVBhZ2VQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgcXVldWUsIGN1cnJlbnRJbmRleCk7XG4gICAgICAgIGNvbnN0IHN0YXR1c1BkYSA9IGRlcml2ZVBsYXllclN0YXR1c1BkYSh0aGlzLnByb2dyYW0ucHJvZ3JhbUlkLCBwbGF5ZXJHYW1lQWNjb3VudCk7XG5cbiAgICAgICAgLy8gRW5jcnlwdGlvbiBIYW5kc2hha2UgSW50ZWdyYXRpb25cbiAgICAgICAgbGV0IGluc3RydWN0aW9uRGF0YTogQnVmZmVyIHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAodGhpcy5jb25maWcuZW5jcnlwdGVkKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIvCflJIgRW5jcnlwdGVkIFF1ZXVlIEpvaW4gSW5pdC4uLlwiKTtcbiAgICAgICAgICAgIC8vIFRPRE86IEZldGNoIFZhbGlkYXRvciBLZXkgZnJvbSBvbi1jaGFpbiBSZWdpc3RyeSBvciBEZWxlZ2F0ZSBBY2NvdW50XG4gICAgICAgICAgICAvLyBGb3Igbm93LCB1c2luZyBhIG1vY2sgZHVtbXkga2V5IGZvciBkZW1vbnN0cmF0aW9uIG9mIHRoZSBmbG93XG4gICAgICAgICAgICAvLyBJbiBQUk9EOiBjb25zdCB2YWxpZGF0b3JLZXkgPSBhd2FpdCB0aGlzLmdldFZhbGlkYXRvcktleShxdWV1ZSk7XG4gICAgICAgICAgICBjb25zdCBtb2NrVmFsaWRhdG9yS2V5ID0gYXdhaXQgdGhpcy5lbmNyeXB0aW9uLmNyZWF0ZU1vY2tWYWxpZGF0b3JLZXkoKTsgLy8gVmFsaWQgUC0yNTYgUHVibGljIEtleVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBQZXJmb3JtIEVuY3J5cHRpb25cbiAgICAgICAgICAgIC8vIFJlYWwgcGF5bG9hZCB3b3VsZCB2YXJ5IChlLmcuIFJvY2svUGFwZXIvU2Npc3NvciBjaG9pY2UpXG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gQnVmZmVyLmZyb20oXCJQTEFZRVJfQ0hPSUNFX1JPQ0tcIik7XG4gICAgICAgICAgICBjb25zdCB7IGVuY3J5cHRlZCwgY2xpZW50UHVibGljS2V5IH0gPSBhd2FpdCB0aGlzLmVuY3J5cHRpb24uZW5jcnlwdFBheWxvYWQocGF5bG9hZCwgbW9ja1ZhbGlkYXRvcktleSk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIvCflJIgUGF5bG9hZCBFbmNyeXB0ZWQuIENsaWVudCBQdWJLZXk6XCIsIEJ1ZmZlci5mcm9tKGNsaWVudFB1YmxpY0tleSkudG9TdHJpbmcoJ2hleCcpKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gSW4gYSByZWFsIGltcGxlbWVudGF0aW9uLCAnZW5jcnlwdGVkJyBhbmQgJ2NsaWVudFB1YmxpY0tleScgd291bGQgYmUgcGFzc2VkIFxuICAgICAgICAgICAgLy8gYXMgYXJndW1lbnRzIHRvIHRoZSAnam9pblF1ZXVlJyBpbnN0cnVjdGlvbiBtb2RpZmljYXRpb24uXG4gICAgICAgICAgICAvLyBTaW5jZSB0aGUgSURMIGlzbid0IHVwZGF0ZWQgZm9yIGFyZ3VtZW50cyB5ZXQsIHdlIGp1c3QgbG9nIGl0LlxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmpvaW5RdWV1ZSgpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLFxuICAgICAgICAgICAgICAgIHBhZ2U6IHBhZ2VQZGEsIFxuICAgICAgICAgICAgICAgIC8vIHBsYXllclN0YXR1czogc3RhdHVzUGRhLFxuICAgICAgICAgICAgICAgIC8vIHBsYXllckF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBwbGF5ZXJHYW1lQWNjb3VudCxcbiAgICAgICAgICAgICAgICB0ZW5hbnRQcm9ncmFtOiB0ZW5hbnRQcm9ncmFtSWQsIC8vIEFkZGVkIGZvciBNYXRjaGFibGUgSW50ZXJmYWNlIENQSVxuICAgICAgICAgICAgICAgIC8vIHN5c3RlbVByb2dyYW06IFN5c3RlbVByb2dyYW0ucHJvZ3JhbUlkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuXG4gICAgICAgIGNvbnNvbGUubG9nKGBKb2luZWQgUXVldWUgYXQgUGFnZSAke2N1cnJlbnRJbmRleH06ICR7dHh9IChMb2NrOiAke3N0YXR1c1BkYS50b0Jhc2U1OCgpfSlgKTtcbiAgICAgICAgXG4gICAgICAgIC8vIEZ1dHVyZSBMb2dpYzogSWYgU0RLIHJlY2VpdmVzIGEgcmV0dXJuIGxvZy9ldmVudCBpbmRpY2F0aW5nIFwiSW5zdGFudCBNYXRjaFwiLCBwYXJzZSBpdC5cbiAgICAgICAgLy8gRm9yIG5vdywgZGVmYXVsdCB0byBcIlF1ZXVlZFwiLlxuICAgICAgICByZXR1cm4geyBcbiAgICAgICAgICAgIHN0YXR1czogXCJRdWV1ZWRcIiwgXG4gICAgICAgICAgICB0eCwgXG4gICAgICAgICAgICBzdGF0dXNQZGEgXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVW5sb2NrIGEgcGxheWVyIG1hbnVhbGx5IChyZWZ1bmQgcmVudCkuXG4gICAgICovXG4gICAgYXN5bmMgdW5sb2NrUGxheWVyKFxuICAgICAgICBwbGF5ZXJHYW1lQWNjb3VudDogUHVibGljS2V5LCBcbiAgICAgICAgcGxheWVyV2FsbGV0OiBQdWJsaWNLZXlcbiAgICApOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHN0YXR1c1BkYSA9IGRlcml2ZVBsYXllclN0YXR1c1BkYSh0aGlzLnByb2dyYW0ucHJvZ3JhbUlkLCBwbGF5ZXJHYW1lQWNjb3VudCk7XG4gICAgICAgIGNvbnN0IHN0YXR1c0FjY291bnQgPSBhd2FpdCB0aGlzLmdldFBsYXllclN0YXR1cyhzdGF0dXNQZGEpO1xuICAgICAgICBjb25zdCBxdWV1ZUFkZHJlc3MgPSBzdGF0dXNBY2NvdW50LnF1ZXVlO1xuXG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC51bmxvY2tQbGF5ZXIoKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogcXVldWVBZGRyZXNzLFxuICAgICAgICAgICAgICAgIC8vIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIC8vIHBsYXllclN0YXR1czogc3RhdHVzUGRhLFxuICAgICAgICAgICAgICAgIHBsYXllcjogcGxheWVyV2FsbGV0LFxuICAgICAgICAgICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBwbGF5ZXJHYW1lQWNjb3VudFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICBcbiAgICAgICAgY29uc29sZS5sb2coYFVubG9ja2VkIFBsYXllcjogJHt0eH1gKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFByb2Nlc3MgbWF0Y2hlcyBvbiBhIHNwZWNpZmljIHBhZ2UuXG4gICAgICogVXN1YWxseSBjYWxsZWQgYnkgdGhlIG9mZi1jaGFpbiB3b3JrZXIgb3IgbWFudWFsbHkgZm9yIHRlc3RpbmcuXG4gICAgICovXG4gICAgYXN5bmMgcHJvY2Vzc01hdGNoKFxuICAgICAgICBxdWV1ZTogUHVibGljS2V5LFxuICAgICAgICBwYWdlSW5kZXg6IG51bWJlclxuICAgICk6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgcGFnZVBkYSA9IGRlcml2ZVBhZ2VQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgcXVldWUsIHBhZ2VJbmRleCk7XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLnByb2Nlc3NNYXRjaChuZXcgYW5jaG9yLkJOKHBhZ2VJbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlQWNjb3VudDogcXVldWUsXG4gICAgICAgICAgICAgICAgLy8gcGFnZTogcGFnZVBkYSxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAucnBjKHRoaXMuY29uZmlnLmNvbmZpcm1PcHRpb25zKTtcblxuICAgICAgICByZXR1cm4gdHg7XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFJlc2l6ZSB0aGUgcXVldWUgY2FwYWNpdHkuXG4gICAgICovXG4gICAgYXN5bmMgcmVzaXplUXVldWUocXVldWU6IFB1YmxpY0tleSwgbmV3Q2FwYWNpdHk6IG51bWJlcik6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLnJlc2l6ZVF1ZXVlKG5ld0NhcGFjaXR5KVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogcXVldWUsXG4gICAgICAgICAgICAgICAgLy8gYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBSZXNpemVkIFF1ZXVlIHRvICR7bmV3Q2FwYWNpdHl9OiAke3R4fWApO1xuICAgICAgICByZXR1cm4gdHg7XG4gICAgfVxuXG4gICAgLy8gLS0tIFN0YXRlIEZldGNoZXJzIC0tLVxuXG4gICAgYXN5bmMgZ2V0UXVldWUocXVldWVQZGE6IFB1YmxpY0tleSk6IFByb21pc2U8UXVldWVIZWFkPiB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnByb2dyYW0uYWNjb3VudC5xdWV1ZUhlYWQuZmV0Y2gocXVldWVQZGEpO1xuICAgIH1cblxuICAgIGFzeW5jIGdldFBhZ2UocGFnZVBkYTogUHVibGljS2V5KTogUHJvbWlzZTxRdWV1ZVBhZ2U+IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucHJvZ3JhbS5hY2NvdW50LnF1ZXVlUGFnZS5mZXRjaChwYWdlUGRhKTtcbiAgICB9XG5cbiAgICBhc3luYyBnZXRQbGF5ZXJTdGF0dXMoc3RhdHVzUGRhOiBQdWJsaWNLZXkpOiBQcm9taXNlPFBsYXllclN0YXR1cz4ge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5wcm9ncmFtLmFjY291bnQucGxheWVyU3RhdHVzLmZldGNoKHN0YXR1c1BkYSk7XG4gICAgfVxuICAgIFxuICAgIGFzeW5jIGdldFBsYXllclN0YXR1c0ZvckdhbWVBY2NvdW50KHBsYXllckdhbWVBY2NvdW50OiBQdWJsaWNLZXkpOiBQcm9taXNlPFBsYXllclN0YXR1cz4ge1xuICAgICAgICAgY29uc3Qgc3RhdHVzUGRhID0gZGVyaXZlUGxheWVyU3RhdHVzUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHBsYXllckdhbWVBY2NvdW50KTtcbiAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldFBsYXllclN0YXR1cyhzdGF0dXNQZGEpO1xuICAgIH1cblxuICAgIC8vIC0tLSBEZXYgSGVscGVycyAtLS1cblxuICAgIGFzeW5jIGNyZWF0ZU1vY2tQbGF5ZXIoXG4gICAgICAgIHBsYXllckFjY291bnQ6IEtleXBhaXIsIFxuICAgICAgICBlbG86IG51bWJlclxuICAgICk6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmNyZWF0ZU1vY2tQbGF5ZXIobmV3IGFuY2hvci5CTihlbG8pKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBwbGF5ZXJBY2NvdW50OiBwbGF5ZXJBY2NvdW50LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICBhdXRob3JpdHk6IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICAvLyBzeXN0ZW1Qcm9ncmFtOiBTeXN0ZW1Qcm9ncmFtLnByb2dyYW1JZCxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuc2lnbmVycyhbcGxheWVyQWNjb3VudF0pXG4gICAgICAgICAgICAucnBjKHRoaXMuY29uZmlnLmNvbmZpcm1PcHRpb25zKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENsb3NlIGEgcXVldWUgcGFnZSB0byByZWNsYWltIHJlbnQuXG4gICAgICovXG4gICAgYXN5bmMgY2xvc2VQYWdlKHF1ZXVlOiBQdWJsaWNLZXksIGluZGV4OiBudW1iZXIpOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHBhZ2VQZGEgPSBkZXJpdmVQYWdlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHF1ZXVlLCBpbmRleCk7XG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC5jbG9zZVBhZ2UobmV3IGFuY2hvci5CTihpbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLCAvLyBBdXRvLXJlc29sdmVkXG4gICAgICAgICAgICAgICAgYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDbG9zZWQgUGFnZSAke2luZGV4fTogJHtwYWdlUGRhLnRvQmFzZTU4KCl9YCk7XG4gICAgICAgIHJldHVybiB0eDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZSBhIHF1ZXVlIGhlYWQgdG8gcmVjbGFpbSByZW50LlxuICAgICAqL1xuICAgIGFzeW5jIGNsb3NlUXVldWUocXVldWVJZDogc3RyaW5nKTogUHJvbWlzZTxUcmFuc2FjdGlvblNpZ25hdHVyZT4ge1xuICAgICAgICBjb25zdCBxdWV1ZVBkYSA9IGRlcml2ZVF1ZXVlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSwgcXVldWVJZCk7XG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC5jbG9zZVF1ZXVlKHF1ZXVlSWQpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIC8vIHF1ZXVlOiBxdWV1ZVBkYSwgLy8gQXV0by1yZXNvbHZlZFxuICAgICAgICAgICAgICAgIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICBjb25zb2xlLmxvZyhgQ2xvc2VkIFF1ZXVlOiAke3F1ZXVlUGRhLnRvQmFzZTU4KCl9YCk7XG4gICAgICAgIHJldHVybiB0eDtcbiAgICB9XG59XG4iXX0=
|