Timelock Encryption
Quickstartβ
Installationβ
Solidityβ
We recommend foundry for managing your Solidity dependencies.
Install the latest timelock encryption Solidity library by running forge install blocklock-solidity
.
If you're using Hardhat, you can also install it by running npm install blocklock-solidity
.
Additional documentation for blocklock-solidity
including supported blockchain networks and the proxy smart contract address for the blocklockSender.sol
contract to call for on-chain timelock encryption requests can be found within the GitHub repository.
Typescriptβ
Install the latest timelock encryption library by running npm install blocklock-js
.
This comes with the smart contract Typescript bindings out of the box and features which designed to simplify the process of generating encrypted data off-chain for the dcipher network.
It enables developers to securely encrypt data tied to a user-specified future chain height. The encrypted data can then be used to create on-chain timelock encryption requests in smart contracts. Once the specified chain height is mined, the userβs smart contract will automatically receive the decryption keys used for performing on-chain decryption.
Additional documentation for blocklock-js
can be found within the GitHub repository.
Ciphertext creation off-chain and on-chain timelock encryption requestβ
Example: Encrypting a uint256 (4 ETH) for Decryption 2 Blocks Laterβ
This example demonstrates encrypting a uint256 value and sending it to a user smart contract that implements the createTimelockRequest function. In a different use case, e.g. sealed bid auction, this could be refactored into a sealedBid
function.
The example user smart contract source code can be found here.
First we connect to an RPC with a wallet, and create an instance of Blocklock and the user's smart contract through which the on-chain timelock encryption request will be made. This is so that the user's contract address receives the decryption key in a callback from the decryptionSender
contract when the decryption block number has been mined.
import { ethers, getBytes } from "ethers";
import { Blocklock, SolidityEncoder, encodeCiphertextToSolidity } from "blocklock-js";
import { MockBlocklockReceiver__factory } from "../types"; // Users' solidity contract TypeScript binding
async function main() {
const rpc = new ethers.JsonRpcProvider(rpcAddr)
// User wallet
const wallet = new ethers.Wallet("your-private-key", rpc);
// Blocklockjs instance
const blocklockjs = new Blocklock(wallet, "blocklockSender contract proxy address");
// Initialise user contract
const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect("user blocklcok receiver contract address", wallet);
Next step will be to perform the encryption and call the function in the user's smart contract (e.g., a sealed bid auction contract) that will make the on-chain timelock encryption request.
// Set block height (current block + 2)
const blockHeight = BigInt(await ethers.provider.getBlockNumber() + 2);
// Value to encrypt (4 ETH as uint256)
const msg = ethers.utils.parseEther("4");
// Encode the uint256 value
const encoder = new SolidityEncoder();
const msgBytes = encoder.encodeUint256(msg);
const encodedMessage = getBytes(msgBytes);
// Encrypt the encoded message
const ciphertext = blocklockjs.encrypt(encodedMessage, blockHeight);
// Call `createTimelockRequest` on the user's contract
const tx = await mockBlocklockReceiver
.connect(wallet)
.createTimelockRequest(blockHeight, encodeCiphertextToSolidity(ciphertext));
const receipt = await tx.wait(1);
if (!receipt) {
throw new Error("Transaction has not been mined");
}
console.log("Timelock request created!");
}
main().catch((error) => {
console.error("Error:", error);
});
The blocklock-js
library also supports encoding of other Solidity data types, e.g., string, bytes, bytes32, int256, address, array and struct.
If you're encrypting something that will be decrypted in a smart contract, you should ensure its type matches a solidity ABI, and can be reconstructed using
abi.decode()
.
We recommend using ethers.js to manage this, see their docs around the abi-coder.
Decryption on-chainβ
By leveraging the blocklock-solidity
library, developers can implement time-based data unlocking mechanisms securely within their smart contracts.
Importingβ
To use this library in your project, import the required files into your contract and use the proxy contract address for BlocklockSender in the constructor as the blocklockContract parameter.
All the smart contract addresses for supported blockchain networks can be found in the blocklock-solidity
GitHub repository README file.
// Import the Types library for managing ciphertexts
import {TypesLib} from "blocklock-solidity/src/libraries/TypesLib.sol";
// Import the AbstractBlocklockReceiver for handling timelock decryption callbacks
import {AbstractBlocklockReceiver} from "blocklock-solidity/src/AbstractBlocklockReceiver.sol";
Example Usage of blocklock-solidity On-Chain for uint256 Ciphertext Decryptionβ
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {TypesLib} from "blocklock-solidity/src/libraries/TypesLib.sol";
import {AbstractBlocklockReceiver} from "blocklock-solidity/src/AbstractBlocklockReceiver.sol";
contract MockBlocklockReceiver is AbstractBlocklockReceiver {
uint256 public requestId;
TypesLib.Ciphertext public encryptedValue;
uint256 public plainTextValue;
constructor(address blocklockContract) AbstractBlocklockReceiver(blocklockContract) {}
function createTimelockRequest(uint256 decryptionBlockNumber, TypesLib.Ciphertext calldata encryptedData)
external
returns (uint256)
{
// Create timelock request
requestId = blocklock.requestBlocklock(decryptionBlockNumber, encryptedData);
// Store the Ciphertext
encryptedValue = encryptedData;
return requestId;
}
function receiveBlocklock(uint256 requestID, bytes calldata decryptionKey)
external
override
onlyBlocklockContract
{
require(requestID == requestId, "Invalid request id");
// Decrypt stored Ciphertext with the decryption key
plainTextValue = abi.decode(blocklock.decrypt(encryptedValue, decryptionKey), (uint256));
}
}
By default, blocklock provides a decryption key to you/your contract to perform the on-chain decryption yourself.
To decrypt in Typescript, you can perform the following:
const plaintext = await blocklockjs.decryptWithId(id)
where id
is the requestID
returned from the on-chain timelock encryption call blocklock.requestBlocklock(decryptionBlockNumber, encryptedData);
in the user smart contract above.
Another Example: Write a smart contract that stores and decrypts encrypted moves for players of an on-chain gameβ
First define a game object, such as a player move, that you wish to encrypt:
enum Move {
UP,
DOWN,
LEFT,
RIGHT
}
then create a contract that will store encrypted moves that have been uploaded by players:
import {TypesLib} from "blocklock-solidity/src/libraries/TypesLib.sol";
import {AbstractBlocklockReceiver} from "blocklock-solidity/src/AbstractBlocklockReceiver.sol";
contract MyGame {
mapping(address => TypesLib.Ciphertext) playerMoves;
mapping(uint256 => address) requestIDs;
function move(TypesLib.Ciphertext calldata encryptedMove) external {
require(this.playerMoves[msg.sender].v.length == 0, "player can only move once!");
// this move will be encrypted for the next 10 blocks
uint256 requestID = Blocklock.requestBlocklock(block.number + 10, encryptedMove);
this.requestIDs[requestID] = msg.sender;
this.playerMoves[msg.sender] = encryptedMove;
}
function receiveBlocklock(uint256 requestID, bytes calldata decryptionKey) external {
require(this.requestIDs[requestID] != address(0), "such a request was never made");
require(this.playerMoves[this.requestIDs[requestID]].v.length > 0, "no such move");
bytes encryptedMove = this.playerMoves[requestIDs[requestID]];
bytes plaintextMove = Blocklock.decrypt(encryptedMove, decryptionKey);
Move playerMove = abi.decode(plaintextMove, (Move));
// perform further game logic
}
}
Using Blocklock.decrypt
automatically verifies the decrypt key for you, so you don't have to! (it's in fact a signature - learn more about the power of threshold signatures in a presentation from our CSO).