Skip to content

Commit

Permalink
upd
Browse files Browse the repository at this point in the history
  • Loading branch information
kroist committed Jan 22, 2024
1 parent 68f77b8 commit 1247180
Show file tree
Hide file tree
Showing 4 changed files with 13,210 additions and 118 deletions.
107 changes: 50 additions & 57 deletions contracts/FHEordle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ contract FHEordle is EIP712WithModifier {

euint16 private word1Id;
euint16[5] private word1Letters;
euint32 private word1LettersMask;
uint32 public word1;

uint8 public nGuesses;
euint32[5] private letterMaskGuess;
euint16[5][5] private letterMaskGuessHist;
uint32[5] public letterMaskGuess;
uint32[5] public guessHist;

bytes32 private root;
bytes32 private rootAllowed;
uint16 wordSetSz;

bool public wordSubmitted;
Expand All @@ -32,6 +34,7 @@ contract FHEordle is EIP712WithModifier {
address _relayerAddr,
uint16 _word1Id,
bytes32 _root,
bytes32 _rootAllowed,
uint16 _wordSetSz
) EIP712WithModifier("Authorization token", "1") {
relayerAddr = _relayerAddr;
Expand All @@ -42,15 +45,18 @@ contract FHEordle is EIP712WithModifier {
word1IdSet = true;
word1Id = TFHE.asEuint16(_word1Id);
}
word1LettersMask = TFHE.asEuint32(0);
for (uint8 i = 0; i < 5; i++) {
letterMaskGuess[i] = TFHE.asEuint32(0);
letterMaskGuess[i] = 0;
guessHist[i] = 0;
}
nGuesses = 0;
wordSubmitted = false;
gameStarted = false;
playerWon = false;
proofChecked = false;
root = _root;
rootAllowed = _rootAllowed;
word1 = 0;
}

Expand Down Expand Up @@ -91,60 +97,60 @@ contract FHEordle is EIP712WithModifier {
word1Letters[2] = l2;
word1Letters[3] = l3;
word1Letters[4] = l4;
wordSubmitted = true;
gameStarted = true;
}

function guessWord1(
bytes calldata el0,
bytes calldata el1,
bytes calldata el2,
bytes calldata el3,
bytes calldata el4,
bytes calldata eMask
) public {
guessWord1(
TFHE.asEuint16(el0),
TFHE.asEuint16(el1),
TFHE.asEuint16(el2),
TFHE.asEuint16(el3),
TFHE.asEuint16(el4),
TFHE.asEuint32(eMask)
);
}

function guessWord1(euint16 l0, euint16 l1, euint16 l2, euint16 l3, euint16 l4, euint32 letterMask) public onlyPlayer {
require(gameStarted, "game not started");
require(nGuesses < 5, "cannot exceed five guesses!");
euint32 word1LettersMask =
word1LettersMask =

Check failure on line 100 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Delete ⏎············
TFHE.or(
TFHE.shl(1, TFHE.asEuint32(word1Letters[0])),

Check failure on line 102 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Replace ················ with ············
TFHE.or(

Check failure on line 103 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Delete ····
TFHE.shl(1, TFHE.asEuint32(word1Letters[1])),

Check failure on line 104 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Replace ···················· with ················
TFHE.or(

Check failure on line 105 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Delete ····
TFHE.shl(1, TFHE.asEuint32(word1Letters[2])),

Check failure on line 106 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Replace ························ with ····················
TFHE.or(TFHE.shl(1, TFHE.asEuint32(word1Letters[3])), TFHE.shl(1, TFHE.asEuint32(word1Letters[4])))
TFHE.or(

Check failure on line 107 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Replace ····TFHE.or(⏎····························TFHE.shl(1,·TFHE.asEuint32(word1Letters[3])),·⏎····························TFHE.shl(1,·TFHE.asEuint32(word1Letters[4]))⏎························ with TFHE.or(TFHE.shl(1,·TFHE.asEuint32(word1Letters[3])),·TFHE.shl(1,·TFHE.asEuint32(word1Letters[4]))
TFHE.shl(1, TFHE.asEuint32(word1Letters[3])),
TFHE.shl(1, TFHE.asEuint32(word1Letters[4]))
)
)

Check failure on line 111 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Replace ···················· with ················
)

Check failure on line 112 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Delete ····
);

Check failure on line 113 in contracts/FHEordle.sol

View workflow job for this annotation

GitHub Actions / ci

Delete ····
letterMaskGuess[nGuesses] = TFHE.and(word1LettersMask, letterMask);
letterMaskGuessHist[nGuesses][0] = l0;
letterMaskGuessHist[nGuesses][1] = l1;
letterMaskGuessHist[nGuesses][2] = l2;
letterMaskGuessHist[nGuesses][3] = l3;
letterMaskGuessHist[nGuesses][4] = l4;
wordSubmitted = true;
gameStarted = true;
}

function guessWord1(
uint32 word,
bytes32[] calldata proof
) public onlyPlayer {
require(gameStarted, "game not started");
require(nGuesses < 5, "cannot exceed five guesses!");

uint8 zeroIndex = 0;
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(zeroIndex, word))));
require(MerkleProof.verify(proof, rootAllowed, leaf), "Invalid word");
uint32 l0 = (word) % 26;
uint32 l1 = (word/26) % 26;
uint32 l2 = (word/26/26) % 26;
uint32 l3 = (word/26/26/26) % 26;
uint32 l4 = (word/26/26/26/26) % 26;
uint32 base = 1;
uint32 letterMask = (base<<l0)|(base<<l1)|(base<<l2)|(base<<l3)|(base<<l4);
letterMaskGuess[nGuesses] = TFHE.decrypt(TFHE.and(word1LettersMask, TFHE.asEuint32(letterMask)));
guessHist[nGuesses] = word;
nGuesses += 1;
}

function getEqMask(
uint8 guessN
) internal view returns (euint8) {
euint8 g0 = TFHE.asEuint8(TFHE.eq(word1Letters[0], letterMaskGuessHist[guessN][0]));
euint8 g1 = TFHE.asEuint8(TFHE.eq(word1Letters[1], letterMaskGuessHist[guessN][1]));
euint8 g2 = TFHE.asEuint8(TFHE.eq(word1Letters[2], letterMaskGuessHist[guessN][2]));
euint8 g3 = TFHE.asEuint8(TFHE.eq(word1Letters[3], letterMaskGuessHist[guessN][3]));
euint8 g4 = TFHE.asEuint8(TFHE.eq(word1Letters[4], letterMaskGuessHist[guessN][4]));
uint32 word = guessHist[guessN];
uint16 l0 = uint16((word) % 26);
uint16 l1 = uint16((word/26) % 26);
uint16 l2 = uint16((word/26/26) % 26);
uint16 l3 = uint16((word/26/26/26) % 26);
uint16 l4 = uint16((word/26/26/26/26) % 26);
euint8 g0 = TFHE.asEuint8(TFHE.eq(word1Letters[0], l0));
euint8 g1 = TFHE.asEuint8(TFHE.eq(word1Letters[1], l1));
euint8 g2 = TFHE.asEuint8(TFHE.eq(word1Letters[2], l2));
euint8 g3 = TFHE.asEuint8(TFHE.eq(word1Letters[3], l3));
euint8 g4 = TFHE.asEuint8(TFHE.eq(word1Letters[4], l4));
euint8 eqMask = TFHE.or(
TFHE.shl(g0, 0),
TFHE.or(TFHE.shl(g1, 1), TFHE.or(TFHE.shl(g2, 2), TFHE.or(TFHE.shl(g3, 3), TFHE.shl(g4, 4))))
Expand All @@ -153,24 +159,11 @@ contract FHEordle is EIP712WithModifier {
}

function getGuess(
uint8 guessN,
bytes32 publicKey,
bytes calldata signature
) public view onlySignedPublicKey(publicKey, signature) onlyPlayer returns (bytes memory, bytes memory) {
uint8 guessN
) public view onlyPlayer returns (uint8) {
require(guessN < nGuesses, "canno exceed nGuesses");
euint8 eqMask = getEqMask(guessN);
return (TFHE.reencrypt(eqMask, publicKey), TFHE.reencrypt(letterMaskGuess[guessN], publicKey));
}

function getLetterGuess(
uint8 guessN,
uint8 letterN,
bytes32 publicKey,
bytes calldata signature
) public view onlySignedPublicKey(publicKey, signature) onlyPlayer returns (bytes memory) {
require(guessN < nGuesses, "canno exceed nGuesses");
require(letterN < 5, "cannot exceed 5");
return (TFHE.reencrypt(letterMaskGuessHist[guessN][letterN], publicKey));
return (TFHE.decrypt(eqMask));
}

function claimWin(uint8 guessN) public onlyPlayer {
Expand Down
2 changes: 2 additions & 0 deletions contracts/FHEordleFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ contract FHEordleFactory is EIP712WithModifier {
_relayerAddr,
0,
0x918fd5f641d6c8bb0c5e07a42f975969c2575250dc3fb743346d1a3c11728bdd,
0xd3e7a12d252dcf5de57a406f0bd646217ec1f340bad869182e5b2bfadd086993,
5757
)
);
Expand All @@ -45,6 +46,7 @@ contract FHEordleFactory is EIP712WithModifier {
_relayerAddr,
3,
0x918fd5f641d6c8bb0c5e07a42f975969c2575250dc3fb743346d1a3c11728bdd,
0xd3e7a12d252dcf5de57a406f0bd646217ec1f340bad869182e5b2bfadd086993,
5757
)
);
Expand Down
105 changes: 44 additions & 61 deletions test/fheordle/FHEordle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ import { createInstances } from "../instance";
import { getSigners } from "../signers";
import { createTransaction } from "../utils";
import { WORDS } from "./wordslist";
import { VALID_WORDS } from "./validWordsList";

export function genProofAndRoot(values: any, key: any): [string, string[]] {
const tree = StandardMerkleTree.of(values, ["uint16", "uint32"]);
export function genProofAndRoot(values: any, key: any, encoding: string[]): [string, string[]] {
const tree = StandardMerkleTree.of(values, encoding);
const root = tree.root;
for (const [i, v] of tree.entries()) {
if (v[0] == key) {
if (v[1] == key[1]) {
const proof = tree.getProof(i);
return [root, proof];
}
}
return ["", []];
}

export const wordAtIdToNumber = (id: number) => {
const word = WORDS[id];
export const wordToNumber = (word: string) => {
return (
word.charCodeAt(0) -
97 +
Expand All @@ -31,7 +31,6 @@ export const wordAtIdToNumber = (id: number) => {
(word.charCodeAt(4) - 97) * 26 * 26 * 26 * 26
);
};
console.log(WORDS[3]);

describe("FHEordle", function () {
before(async function () {
Expand All @@ -46,18 +45,22 @@ describe("FHEordle", function () {
// id = 3

const wordsList = [];
const wordsSz = WORDS.length;
for (let i = 0; i < wordsSz; i++) {
wordsList.push([i, wordAtIdToNumber(i)]);
for (let i = 0; i < WORDS.length; i++) {
wordsList.push([i, wordToNumber(WORDS[i])]);
}
// console.log(wordsList);
// while (true) {
// }
const ourWord = wordsList[3][1];

const [_root, proof] = genProofAndRoot(wordsList, 3);
const [_root, proof] = genProofAndRoot(wordsList, [3, wordToNumber(WORDS[3])], ["uint16", "uint32"]);
expect(StandardMerkleTree.verify(_root, ["uint16", "uint32"], [3, wordToNumber(WORDS[3])], proof)).to.equal(true);
console.log(_root);
console.log(wordsSz);
const ourWord = wordsList[3][1]; // "about"

const validWordsList = [];
for (let i = 0; i < VALID_WORDS.length; i++) {
validWordsList.push([0, wordToNumber(VALID_WORDS[i])]);
}
const [_validRoot, proofValid] = genProofAndRoot(validWordsList, [0, wordToNumber(VALID_WORDS[1])], ["uint8", "uint32"]);
expect(StandardMerkleTree.verify(_validRoot, ["uint8", "uint32"], [0, wordToNumber(VALID_WORDS[1])], proofValid)).to.equal(true);
console.log(_validRoot);


const fheordleFactoryFactory = await ethers.getContractFactory("FHEordleFactory");
const factoryContract: FHEordleFactory = await fheordleFactoryFactory.connect(this.signers.alice).deploy();
Expand Down Expand Up @@ -95,11 +98,11 @@ describe("FHEordle", function () {
{
const bobContract = contract.connect(this.signers.bob);
const l0 = ourWord % 26;
const l1 = (ourWord / 26) % 26;
const l2 = (ourWord / 26 / 26) % 26;
const l3 = (ourWord / 26 / 26 / 26) % 26;
const l4 = (ourWord / 26 / 26 / 26 / 26) % 26;
const mask = (1 << l0) | (1 << l1) | (1 << l2) | (1 << l3) | (1 << l4);
const l1 = Math.floor(ourWord / 26) % 26;
const l2 = Math.floor(ourWord / 26 / 26) % 26;
const l3 = Math.floor(ourWord / 26 / 26 / 26) % 26;
const l4 = Math.floor(ourWord / 26 / 26 / 26 / 26) % 26;
console.log(l0, l1, l2, l3, l4);
const encl0 = this.instances.bob.encrypt16(l0);
const encl1 = this.instances.bob.encrypt16(l1);
const encl2 = this.instances.bob.encrypt16(l2);
Expand Down Expand Up @@ -130,26 +133,18 @@ describe("FHEordle", function () {

//guess n.1
{
const l0 = 5;
const l1 = 5;
const l2 = 5;
// "rerun"
const l0 = 17;
const l1 = 4;
const l2 = 17;
const l3 = 20;
const l4 = 5;
const mask = (1 << l0) | (1 << l1) | (1 << l2) | (1 << l3) | (1 << l4);
const encl0 = this.instances.alice.encrypt16(l0);
const encl1 = this.instances.alice.encrypt16(l1);
const encl2 = this.instances.alice.encrypt16(l2);
const encl3 = this.instances.alice.encrypt16(l3);
const encl4 = this.instances.alice.encrypt16(l4);
const encMask = this.instances.alice.encrypt32(mask);
const l4 = 13;
const word = l0 + 26*(l1+26*(l2+26*(l3+26*l4)));
const [_vR, proof] = genProofAndRoot(validWordsList, [0, word], ["uint8", "uint32"]);
const tx1 = await createTransaction(
contract["guessWord1(bytes,bytes,bytes,bytes,bytes,bytes)"],
encl0,
encl1,
encl2,
encl3,
encl4,
encMask,
contract.guessWord1,
word,
proof
);
await tx1.wait();
}
Expand All @@ -162,37 +157,27 @@ describe("FHEordle", function () {

//check guess
{
const token = this.instances.alice.getTokenSignature(this.contractAddress)!;
const tx1 = await contract.getGuess(0, token.publicKey, token.signature);
const eqMask = this.instances.alice.decrypt(this.contractAddress, tx1[0]);
const letterMask = this.instances.alice.decrypt(this.contractAddress, tx1[1]);
const eqMask = await contract.getGuess(0);
const letterMask = await contract.letterMaskGuess(0);
expect(eqMask).to.equal(8);
expect(letterMask).to.equal(1 << 20);
}

console.log("guess 2");
// guess 2
{
// "about"
const l0 = 0;
const l1 = 1;
const l2 = 14;
const l3 = 20;
const l4 = 19;
const mask = (1 << l0) | (1 << l1) | (1 << l2) | (1 << l3) | (1 << l4);
const encl0 = this.instances.alice.encrypt16(l0);
const encl1 = this.instances.alice.encrypt16(l1);
const encl2 = this.instances.alice.encrypt16(l2);
const encl3 = this.instances.alice.encrypt16(l3);
const encl4 = this.instances.alice.encrypt16(l4);
const encMask = this.instances.alice.encrypt32(mask);
const word = l0 + 26*(l1+26*(l2+26*(l3+26*l4)));
const [_validRoot, proof] = genProofAndRoot(validWordsList, [0, word], ["uint8", "uint32"]);
const tx1 = await createTransaction(
contract["guessWord1(bytes,bytes,bytes,bytes,bytes,bytes)"],
encl0,
encl1,
encl2,
encl3,
encl4,
encMask,
contract.guessWord1,
word,
proof
);
await tx1.wait();
}
Expand All @@ -205,10 +190,8 @@ describe("FHEordle", function () {

// get guess
{
const token = this.instances.alice.getTokenSignature(this.contractAddress)!;
const tx1 = await contract.getGuess(1, token.publicKey, token.signature);
const eqMask = this.instances.alice.decrypt(this.contractAddress, tx1[0]);
const letterMask = this.instances.alice.decrypt(this.contractAddress, tx1[1]);
const eqMask = await contract.getGuess(1);
const letterMask = await contract.letterMaskGuess(1);
expect(eqMask).to.equal(31);
expect(letterMask).to.equal(1589251);
}
Expand Down
Loading

0 comments on commit 1247180

Please sign in to comment.