Skip to content

Commit

Permalink
add multicall test and update script for timelock request tests
Browse files Browse the repository at this point in the history
  • Loading branch information
najienka committed Feb 6, 2025
1 parent d7d8547 commit 2b30bb3
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 50 deletions.
106 changes: 66 additions & 40 deletions scripts/mocks/create-timelock-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { keccak_256 } from "@noble/hashes/sha3";
// yarn ts-node scripts/mocks/create-timelock-request.ts

const RPC_URL = process.env.CALIBRATIONNET_RPC_URL;
const walletAddr = "0x5d84b82b750B996BFC1FA7985D90Ae8Fbe773364"
const walletAddr = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
const blocklockSenderAddr = "0xfF66908E1d7d23ff62791505b2eC120128918F44"
const decryptionSenderAddr = "0x9297Bb1d423ef7386C8b2e6B7BdE377977FBedd3";
const mockBlocklockReceiverAddr = "0x6f637EcB3Eaf8bEd0fc597Dc54F477a33BBCA72B";
Expand Down Expand Up @@ -55,6 +55,12 @@ function encodeCiphertextToSolidity(ciphertext: Ciphertext): BlocklockTypes.Ciph
};
}

// Create a provider using the RPC URL
const provider = new ethers.JsonRpcProvider(RPC_URL);

// Create a signer using the private key
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);

function blockHeightToBEBytes(blockHeight: bigint) {
const buffer = new ArrayBuffer(32)
const dataView = new DataView(buffer)
Expand Down Expand Up @@ -87,53 +93,73 @@ async function latestBlockNumber(provider: JsonRpcProvider) {
console.log(`Latest Block Number: ${latestBlockNumber}`);
}

async function main() {
try {
// Create a provider using the RPC URL
const provider = new ethers.JsonRpcProvider(RPC_URL);
async function createTimelockRequest() {
// Create blocklockSender instance with proxy contract address
const blocklockSender = new ethers.Contract(blocklockSenderAddr, BlocklockSender__factory.abi, provider);
// cast call 0xfF66908E1d7d23ff62791505b2eC120128918F44 "version()(string)" --rpc-url https://rpc.ankr.com/filecoin_testnet
console.log("decryptionSender address from blocklockSender proxy", await blocklockSender.decryptionSender());

// Create decryptionSender instance with proxy contract address
const decryptionSender = new ethers.Contract(decryptionSenderAddr, DecryptionSender__factory.abi, provider);
console.log("Version number from decryptionSender proxy", await decryptionSender.version());

// Create mockBlocklockReceiver instance with implementation contract address
const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect(mockBlocklockReceiverAddr, signer);

// create a timelock request from mockBlocklockReceiver contract and check it is fulfilled by blocklock agent
const blockHeight = BigInt(await provider.getBlockNumber() + 6);
const msg = ethers.parseEther("3");
const abiCoder = AbiCoder.defaultAbiCoder();
const msgBytes = abiCoder.encode(["uint256"], [msg]);
const encodedMessage = getBytes(msgBytes);
const conditions = {
blockHeight: blockHeight
}
const encodedConditions = blockHeightToBEBytes(conditions.blockHeight);
const ct = encrypt_towards_identity_g1(encodedMessage, encodedConditions, BLOCKLOCK_DEFAULT_PUBLIC_KEY, BLOCKLOCK_IBE_OPTS);

let tx = await mockBlocklockReceiver.connect(signer).createTimelockRequest(blockHeight, encodeCiphertextToSolidity(ct));
let receipt = await tx.wait(1);
if (!receipt) {
throw new Error("transaction has not been mined");
}
const reqId = await mockBlocklockReceiver.requestId();
console.log("Created request id on filecoin testnet:", reqId);
console.log("Decryption block number:", blockHeight);
console.log("Request creation block number:", await provider.getBlockNumber())
console.log("is created blocklock request id inFlight?:", await decryptionSender.isInFlight(reqId));

}

async function replacePendingTransaction() {
let txData = {
to: "0x5d84b82b750B996BFC1FA7985D90Ae8Fbe773364",
value: "0",
chainId: 314159,
nonce: 1415,
gasLimit: 10000000000,
gasPrice: 2000000000
}
// let estimate = await provider.estimateGas(tx)
// tx.gasLimit = estimate;
// tx.gasPrice = ethers.parseUnits("0.14085197", "gwei");
let tx = await signer.sendTransaction(txData)
let receipt = await tx.wait(1)
console.log(receipt)
}

// Create a signer using the private key
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
async function getTransactionCount() {
return await provider.getTransactionCount(walletAddr)
}

async function main() {
try {
// Get latest block number
await latestBlockNumber(provider);

// Get wallet ETH balance
await getWalletBalance(RPC_URL!, walletAddr);

// Create blocklockSender instance with proxy contract address
const blocklockSender = new ethers.Contract(blocklockSenderAddr, BlocklockSender__factory.abi, provider);
// cast call 0xfF66908E1d7d23ff62791505b2eC120128918F44 "version()(string)" --rpc-url https://rpc.ankr.com/filecoin_testnet
console.log("decryptionSender address from blocklockSender proxy", await blocklockSender.decryptionSender());

// Create decryptionSender instance with proxy contract address
const decryptionSender = new ethers.Contract(decryptionSenderAddr, DecryptionSender__factory.abi, provider);
console.log("Version number from decryptionSender proxy", await decryptionSender.version());

// Create mockBlocklockReceiver instance with implementation contract address
const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect(mockBlocklockReceiverAddr, signer);

// create a timelock request from mockBlocklockReceiver contract and check it is fulfilled by blocklock agent
const blockHeight = BigInt(await provider.getBlockNumber() + 6);
const msg = ethers.parseEther("4");
const abiCoder = AbiCoder.defaultAbiCoder();
const msgBytes = abiCoder.encode(["uint256"], [msg]);
const encodedMessage = getBytes(msgBytes);
const conditions = {
blockHeight: blockHeight
}
const encodedConditions = blockHeightToBEBytes(conditions.blockHeight);
const ct = encrypt_towards_identity_g1(encodedMessage, encodedConditions, BLOCKLOCK_DEFAULT_PUBLIC_KEY, BLOCKLOCK_IBE_OPTS);

let tx = await mockBlocklockReceiver.connect(signer).createTimelockRequest(blockHeight, encodeCiphertextToSolidity(ct));
let receipt = await tx.wait(1);
if (!receipt) {
throw new Error("transaction has not been mined");
}
const reqId = await mockBlocklockReceiver.requestId();
console.log("Created blocklock request id on filecoin testnet", reqId);
console.log("is created blocklock request id inFlight?", await decryptionSender.isInFlight(reqId));

} catch (error) {
console.error("Error fetching latest block number:", error);
}
Expand Down
165 changes: 156 additions & 9 deletions test/hardhat/Blocklock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ describe("BlocklockSender", function () {
decryptionSenderIface.getEvent("DecryptionRequested"),
);

console.log(`received decryption request ${requestID}`);
console.log(`call back address ${callback}, scheme id ${schemeID}`);
// console.log(`received decryption request ${requestID}`);
// console.log(`call back address ${callback}, scheme id ${schemeID}`);

const bls = await BlsBn254.create();
const { pubKey, secretKey } = bls.createKeyPair(blsKey as `0x${string}`);
Expand Down Expand Up @@ -346,13 +346,13 @@ describe("BlocklockSender", function () {
decryptionSenderIface.getEvent("DecryptionRequested"),
);

console.log("callback and blocklock address", callback, await blocklock.getAddress());
// console.log("callback and blocklock address", callback, await blocklock.getAddress());

let req = await blocklock.getRequest(BigInt(requestID));
expect(req.blockHeight).to.be.equal(BigInt(blockHeight + 2));

console.log(`received decryption request ${requestID}`);
console.log(`call back address ${callback}, scheme id ${schemeID}`);
// console.log(`received decryption request ${requestID}`);
// console.log(`call back address ${callback}, scheme id ${schemeID}`);

const bls = await BlsBn254.create();
const { pubKey, secretKey } = bls.createKeyPair(blsKey as `0x${string}`);
Expand Down Expand Up @@ -505,13 +505,13 @@ describe("BlocklockSender", function () {

expect(pendingRequestIds[0]).to.be.equal(1);

console.log("callback and blocklock address", callback, await blocklock.getAddress());
// console.log("callback and blocklock address", callback, await blocklock.getAddress());

let req = await blocklock.getRequest(BigInt(requestID));
expect(req.blockHeight).to.be.equal(BigInt(blockHeight + 2));

console.log(`received decryption request ${requestID}`);
console.log(`call back address ${callback}, scheme id ${schemeID}`);
// console.log(`received decryption request ${requestID}`);
// console.log(`call back address ${callback}, scheme id ${schemeID}`);

const bls = await BlsBn254.create();
const { pubKey, secretKey } = bls.createKeyPair(blsKey as `0x${string}`);
Expand Down Expand Up @@ -572,6 +572,153 @@ describe("BlocklockSender", function () {
expect(Array.from(getBytes(encodedMessage))).to.have.members(Array.from(decryptedM2));

expect(await blocklockStringReceiver.plainTextValue()).to.be.equal(msg);
console.log(await blocklockStringReceiver.plainTextValue(), msg);
// console.log(await blocklockStringReceiver.plainTextValue(), msg);
});

it("can process multiple requests for the same decrtyption block from user contracts for string and uint256", async function () {
// expected request ids
const requestIDs = [1, 2];

let numberOfPendingRequests = await decryptionSender.getCountOfUnfulfilledRequestIds();
let pendingRequestIds = await decryptionSender.getAllUnfulfilledRequestIds();
let nonPendingRequestIds = await decryptionSender.getAllFulfilledRequestIds();

expect(numberOfPendingRequests).to.be.equal(0);
expect(pendingRequestIds.length).to.be.equal(0);
expect(nonPendingRequestIds.length).to.be.equal(0);

let blockHeight = await ethers.provider.getBlockNumber();
let decryptionBlockHeight = BigInt(blockHeight + 5);
// console.log("current block height", blockHeight);

// string message
const stringmsg = "mainnet launch soon";
const stringmsgBytes = AbiCoder.defaultAbiCoder().encode(["string"], [stringmsg]);
const stringencodedMessage = getBytes(stringmsgBytes);

// uint256 message
const uintmsg = ethers.parseEther("4");
const uintmsgBytes = AbiCoder.defaultAbiCoder().encode(["uint256"], [uintmsg]);
const uintencodedMessage = getBytes(uintmsgBytes);

// ciphertexts
const stringct = encrypt(stringencodedMessage, decryptionBlockHeight, BLOCKLOCK_DEFAULT_PUBLIC_KEY);
const uintct = encrypt(uintencodedMessage, decryptionBlockHeight, BLOCKLOCK_DEFAULT_PUBLIC_KEY);

// on-chain timelock request transactions
let tx1 = await blocklockStringReceiver
.connect(owner)
.createTimelockRequest(decryptionBlockHeight, encodeCiphertextToSolidity(stringct));

let tx2 = await blocklockReceiver
.connect(owner)
.createTimelockRequest(decryptionBlockHeight, encodeCiphertextToSolidity(uintct));

let receipt1 = await tx1.wait(1);
if (!receipt1) {
throw new Error("transaction has not been mined");
}

let receipt2 = await tx2.wait(1);
if (!receipt2) {
throw new Error("transaction has not been mined");
}

// decryption block number checks from blocklock contract events
let req1 = await blocklock.getRequest(BigInt(1));
expect(req1.blockHeight).to.be.equal(BigInt(blockHeight + 5));

let req2 = await blocklock.getRequest(BigInt(2));
expect(req2.blockHeight).to.be.equal(BigInt(blockHeight + 5));

// fetch requests
const firstRequest = await decryptionSender.getRequest(requestIDs[0]);
const secondRequest = await decryptionSender.getRequest(requestIDs[1]);

numberOfPendingRequests = await decryptionSender.getCountOfUnfulfilledRequestIds();
pendingRequestIds = await decryptionSender.getAllUnfulfilledRequestIds();
nonPendingRequestIds = await decryptionSender.getAllFulfilledRequestIds();

expect(numberOfPendingRequests).to.be.equal(2);
expect(pendingRequestIds.length).to.be.equal(2);
expect(nonPendingRequestIds.length).to.be.equal(0);

// generate signatures and decryption keys
const bls = await BlsBn254.create();
const { pubKey, secretKey } = bls.createKeyPair(blsKey as `0x${string}`);

const conditionBytes1 = isHexString(firstRequest.condition)
? getBytes(firstRequest.condition)
: toUtf8Bytes(firstRequest.condition);
const m1 = bls.hashToPoint(BLOCKLOCK_IBE_OPTS.dsts.H1_G1, conditionBytes1);

const hexCondition1 = Buffer.from(conditionBytes1).toString("hex");
blockHeight = BigInt("0x" + hexCondition1);

const parsedCiphertext1 = parseSolidityCiphertextString(firstRequest.ciphertext);

const conditionBytes2 = isHexString(secondRequest.condition)
? getBytes(secondRequest.condition)
: toUtf8Bytes(secondRequest.condition);
const m2 = bls.hashToPoint(BLOCKLOCK_IBE_OPTS.dsts.H1_G1, conditionBytes2);

const hexCondition2 = Buffer.from(conditionBytes2).toString("hex");
blockHeight = BigInt("0x" + hexCondition2);

const parsedCiphertext2 = parseSolidityCiphertextString(secondRequest.ciphertext);

const signature1 = bls.sign(m1, secretKey).signature;
const sig1 = bls.serialiseG1Point(signature1);
const sigBytes1 = AbiCoder.defaultAbiCoder().encode(["uint256", "uint256"], [sig1[0], sig1[1]]);

const decryption_key1 = preprocess_decryption_key_g1(
parsedCiphertext1,
{ x: sig1[0], y: sig1[1] },
BLOCKLOCK_IBE_OPTS,
);

const signature2 = bls.sign(m2, secretKey).signature;
const sig2 = bls.serialiseG1Point(signature2);
const sigBytes2 = AbiCoder.defaultAbiCoder().encode(["uint256", "uint256"], [sig2[0], sig2[1]]);

const decryption_key2 = preprocess_decryption_key_g1(
parsedCiphertext2,
{ x: sig2[0], y: sig2[1] },
BLOCKLOCK_IBE_OPTS,
);

// use the signatures and decryption keys to generate a multicall transaction
// to fulfill both requests in a single transaction
const decryptionKeys = [decryption_key1, decryption_key2];
const signatures = [sigBytes1, sigBytes2];

const encodedCalls = requestIDs.map((id, index) =>
decryptionSender.interface.encodeFunctionData("fulfilDecryptionRequest", [
id,
decryptionKeys[index],
signatures[index],
]),
);

try {
// Send multicall
const tx = await decryptionSender.multicall(encodedCalls);
// console.log("Transaction sent:", tx.hash);

// Wait for confirmation
const receipt = await tx.wait(1);
// console.log("Transaction confirmed:", receipt);
} catch (error) {
console.error("Error executing multicall:", error);
}

// verify that both reqests have been fulfilled
numberOfPendingRequests = await decryptionSender.getCountOfUnfulfilledRequestIds();
pendingRequestIds = await decryptionSender.getAllUnfulfilledRequestIds();
nonPendingRequestIds = await decryptionSender.getAllFulfilledRequestIds();

expect(numberOfPendingRequests).to.be.equal(0);
expect(pendingRequestIds.length).to.be.equal(0);
expect(nonPendingRequestIds.length).to.be.equal(2);
});
});
2 changes: 1 addition & 1 deletion test/hardhat/crypto/ibe_bn254.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe("ibe bn254", () => {
const ct = await encrypt_towards_identity_g1(m, identity, X_G2);
const decryption_key = await preprocess_decryption_key_g1(ct, sig);

console.log(decryption_key);
// console.log(decryption_key);
const m2 = decrypt_g1_with_preprocess(ct, decryption_key);

expect(m).to.deep.equal(m2);
Expand Down

0 comments on commit 2b30bb3

Please sign in to comment.