Hypr Cards SDK Usage Example
Learn how to use the Hypr Cards SDK
Install modules
Begin by installing and importing typescript packages hypr-poker-export
and ethers
yarn add hypr-poker-export
yarn add ethers
Import modules
import * as precompile from 'hypr-poker-export/precompile';
import * as wasm from 'hypr-poker-export/wasm';
import { Contract, JsonRpcProvider, Wallet, getBytes } from 'ethers';
const IMentalPokerExecAbi = require('hypr-poker-export/abi/IMentalPokerExec.json');
const IMentalPokerVerifyAbi = require('hypr-poker-export/abi/IMentalPokerVerify.json');
Contract Objects
Next, you need to create a contract object of Poker Exec and Poker Verify precompile contracts.
You will need to update the provider RPC url and a wallet key.
Never hardcode your wallet private key.
const provider = new JsonRpcProvider('http://testnet-proposer0.hypr.network:8545');
const signer = new Wallet('<your secret key>', provider);
const pokerExec = new Contract(
'0x0000000000000000000000000000000000000040',
IMentalPokerExecAbi,
signer,
) as unknown as precompile.IMentalPokerExec;
const pokerVerify = new Contract(
'0x0000000000000000000000000000000000000030',
IMentalPokerVerifyAbi,
signer,
) as unknown as precompile.IMentalPokerVerify;
Game Parameters
Before beginning the game, you must generate game parameters and cards for on-chain contract computation, player key, and keyproof for off-chain player computation.
// Generator game parameter.
const m = 4;
const n = 13;
const rand = wasm.CardRand.buildRand();
const parameter = wasm.setup(rand, m, n);
// Generate origin cards and encoded origin cards.
const cards = wasm.encodeCards(rand, m * n);
const cardsEncoded: wasm.Card[] = [];
const cards_len = cards.len();
for (let i = 0; i < cards_len; i++) {
cardsEncoded.push(wasm.Card.deserial(cards.pop().serial()));
}
// Generate game keys and keyproof for player Alice.
const Alice = wasm.PlayerName.NewPlayerName('Alice');
const gameKeyAndProofAlice = wasm.keygen(rand, parameter, Alice);
const pubKeyAlice = gameKeyAndProofAlice.getPubKey();
const secKeyAlice = gameKeyAndProofAlice.getSecKey();
const keyownershipProofAlice = gameKeyAndProofAlice.getProof();
// Generate game key and proof for player Peter.
const Peter = wasm.PlayerName.NewPlayerName('Peter');
const gameKeyAndProofPeter = wasm.keygen(rand, parameter, Peter);
const pubKeyPeter = gameKeyAndProofPeter.getPubKey();
const secKeyPeter = gameKeyAndProofPeter.getSecKey();
const keyownershipProofPeter = gameKeyAndProofPeter.getProof();
Integers n*m
must equal to the number of cards. e.g. 4 * 13 = 52.
The need for two variables is due to wasm execution performance optimization.
Shuffle Cards
If you want to shuffle cards on-chain safely, you are able to call function verifyKeyOwnership()
-> computeAggregateKey()
-> mask()
-> shuffleAndRemask()
-> verifyShuffle()
// Verify player Alice keyproof.
if(!await pokerVerify.verifyKeyOwnership(
parameter.serial(),
pubKeyAlice.serial(),
Alice.serial(),
keyownershipProofAlice.serial()
)) {
throw 'poker verify ownership is failed';
}
// Verify player Peter keyproof.
if(!await pokerVerify.verifyKeyOwnership(
parameter.serial(),
pubKeyPeter.serial(),
Peter.serial(),
keyownershipProofPeter.serial()
)) {
throw 'poker verify ownership is failed';
}
// Compute aggregate key.
const pubKeys : Uint8Array[] = [];
pubKeys.push(pubKeyAlice.serial());
pubKeys.push(pubKeyPeter.serial());
const aggregatedKey = wasm.AggregatePublicKey.deserial(
getBytes(await pokerExec.computeAggregateKey(pubKeys)),
);
const uint8ArrayEquals = (a: Uint8Array, b: Uint8Array) =>
a.length === b.length && a.every((v, i) => v === b[i]);
// Mask cards.
const maskedCards = wasm.VMaskedCard.newVMaskedCard();
for (let i = 0; i < cardsEncoded.length; i++) {
// mask() through precompile on-chain.
const maskedCardOnChain = getBytes(await pokerExec.mask(
parameter.serial(),
aggregatedKey.serial(),
cardsEncoded[i].serial(),
));
// mask() through wasm off-chain.
const maskedCardOffChain = wasm
.mask(parameter, aggregatedKey, cardsEncoded[i]).serial();
if(!uint8ArrayEquals(maskedCardOnChain, maskedCardOffChain)) {
throw 'poker mask error';
}
maskedCards.push(wasm.MaskedCard.deserial(maskedCardOnChain));
}
// Player Alice shuffle cards and verify shuffled cards.
const randAlice = wasm.CardRand.buildRand();
const permutationAlice = wasm.Permutation.newPermutation(randAlice, m * n);
const shuffledCardsAndShuffleProofAlice = wasm.shuffleAndRemask(
randAlice,
parameter,
aggregatedKey,
maskedCards,
permutationAlice,
);
const maskedCardsSerialed: Uint8Array[] = [];
const maskedCards_len = maskedCards.len();
for (let i = 0; i < maskedCards_len; i++) {
maskedCardsSerialed.push(maskedCards.pop().serial());
}
let shuffledCardsAlice = shuffledCardsAndShuffleProofAlice.getMaskedCards();
const shuffleProofAlice = shuffledCardsAndShuffleProofAlice.getShuffleProof();
const shuffledCardsAliceSerial: Uint8Array[] = [];
const shuffledCardsAlice_len = shuffledCardsAlice.len();
for (let i = 0; i < shuffledCardsAlice_len; i++) {
shuffledCardsAliceSerial.push(shuffledCardsAlice.pop().serial());
}
shuffledCardsAlice = shuffledCardsAndShuffleProofAlice.getMaskedCards();
if(!await pokerVerify.verifyShuffle(
parameter.serial(),
aggregatedKey.serial(),
maskedCardsSerialed,
shuffledCardsAliceSerial,
shuffleProofAlice.serial(),
)) {
throw 'poker verify shuffle failed';
}
// Player Peter shuffle cards and verify shuffled cards.
const randPeter = wasm.CardRand.buildRand();
const permutationPeter = wasm.Permutation.newPermutation(randPeter, m * n);
const shuffledCardsAndShuffleProofPeter = wasm.shuffleAndRemask(
randPeter,
parameter,
aggregatedKey,
shuffledCardsAlice,
permutationPeter,
);
let shuffledCardsPeter = shuffledCardsAndShuffleProofPeter.getMaskedCards();
const shuffleProofPeter = shuffledCardsAndShuffleProofPeter.getShuffleProof();
const shuffledCardsPeterSerial: Uint8Array[] = [];
const shuffledCardsPeter_len = shuffledCardsPeter.len();
for (let i = 0; i < shuffledCardsPeter_len; i++) {
shuffledCardsPeterSerial.push(shuffledCardsPeter.pop().serial());
}
shuffledCardsPeter = shuffledCardsAndShuffleProofPeter.getMaskedCards();
if(!await pokerVerify.verifyShuffle(
parameter.serial(),
aggregatedKey.serial(),
shuffledCardsAliceSerial,
shuffledCardsPeterSerial,
shuffleProofPeter.serial(),
)) {
throw 'poker verify shuffle failed';
}
Reaveal Cards
If you want to reveal cards from mask-shuffled cards on-chain safely, you are able to call function computeRevealToken()
-> verifyReveal()
-> reveal()
// Get one of mask-shuffled cards, used for reveal.
const shuffledCard = shuffledCardsPeter.pop();
// Compute reveal token for Alice.
const randRevealAlice = wasm.CardRand.buildRand();
const revealTokenAndProofAlice = wasm.computeRevealToken(
randRevealAlice,
parameter,
secKeyAlice,
pubKeyAlice,
shuffledCard,
);
const revealTokenAlice = revealTokenAndProofAlice.getRevealToken();
const revealProofAlice = revealTokenAndProofAlice.getRevealProof();
// Compute reveal token for Peter
const randRevealPeter = wasm.CardRand.buildRand();
const revealTokenAndProofPeter = wasm.computeRevealToken(
randRevealPeter,
parameter,
secKeyPeter,
pubKeyPeter,
shuffledCard,
);
const revealTokenPeter = revealTokenAndProofPeter.getRevealToken();
const revealProofPeter = revealTokenAndProofPeter.getRevealProof();
// Verify reveal token of Alice can reveal specified mask-shuffled card.
if(!await pokerVerify.verifyReveal(
parameter.serial(),
pubKeyAlice.serial(),
revealTokenAlice.serial(),
shuffledCard.serial(),
revealProofAlice.serial(),
)) {
throw 'poker verify reveal failed';
}
// Verify reveal token of Peter can reveal specified mask-shuffled card.
if(!await pokerVerify.verifyReveal(
parameter.serial(),
pubKeyPeter.serial(),
revealTokenPeter.serial(),
shuffledCard.serial(),
revealProofPeter.serial(),
)) {
throw 'poker verify reveal failed';
}
// Reveal mask-shuffled card with reveal tokens.
const revealTokens = wasm.VRevealToken.newVRevealToken();
revealTokens.push(revealTokenAlice);
revealTokens.push(revealTokenPeter);
const revealTokensSerial = [];
revealTokensSerial.push(revealTokenAlice.serial());
revealTokensSerial.push(revealTokenPeter.serial());
const cards1 = wasm.reveal(revealTokens, shuffledCard).serial();
const cards2 = getBytes(
await pokerExec.reveal(
revealTokensSerial,
shuffledCard.serial()
));
if(!uint8ArrayEquals(cards1, cards2)) {
throw 'poker reveal error';
}
Note: The function shuffleAndRemask()
has to be called after mask()
.
Last updated