Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,27 @@ FILECOIN_MAINNET_RPC_URL=wss://wss.node.glif.io/apigw/lotus/rpc/v1
# dcipher furnace
FURNACE_PRIVATE_KEY=somehexstringwithoutthe0xprefix
FURNACE_RPC_URL=https://api.furnace.dcipher.network

# base sepolia
BASE_PRIVATE_KEY=somehexstringwithoutthe0xprefix
BASE_RPC_URL=https://sepolia.base.org

# polygon PoS
POLYGON_PRIVATE_KEY=somehexstringwithoutthe0xprefix
POLYGON_RPC_URL=wss://cantuseoursbecauseitsprivate.quiknode.pro

# avalanche c-chain
AVALANCHE_C_CHAIN_PRIVATE_KEY=somehexstringwithoutthe0xprefix
AVALANCHE_C_CHAIN_RPC_URL=https://avalanche-c-chain-rpc.publicnode.com

# optimism sepolia
OPTIMISM_SEPOLIA_PRIVATE_KEY=somehexstringwithoutthe0xprefix
OPTIMISM_SEPOLIA_RPC_URL=https://sepolia.optimism.io

# arbitrum sepolia
ARBITRUM_SEPOLIA_PRIVATE_KEY=somehexstringwithoutthe0xprefix
ARBITRUM_SEPOLIA_RPC_URL=https://api.zan.top/arb-sepolia

# Sei testnet
SEI_TESTNET_PRIVATE_KEY=somehexstringwithoutthe0xprefix
SEI_TESTNET_RPC_URL=wss://sei-testnet.drpc.org
16 changes: 12 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ on:

env:
BLS_KEY: ${{ secrets.BLS_KEY }}
FILECOIN_PRIVATE_KEY: ${{ secrets.FILECOIN_PRIVATE_KEY }}
FILECOIN_RPC_URL: ${{ secrets.FILECOIN_RPC_URL }}
ARBITRUM_SEPOLIA_PRIVATE_KEY: ${{ secrets.ARBITRUM_SEPOLIA_PRIVATE_KEY }}
ARBITRUM_SEPOLIA_RPC_URL: ${{ secrets.ARBITRUM_SEPOLIA_RPC_URL }}
AVALANCHE_C_CHAIN_PRIVATE_KEY: ${{ secrets.AVALANCHE_C_CHAIN_PRIVATE_KEY }}
AVALANCHE_C_CHAIN_RPC_URL: ${{ secrets.AVALANCHE_C_CHAIN_RPC_URL }}
BASE_PRIVATE_KEY: ${{ secrets.BASE_PRIVATE_KEY }}
BASE_RPC_URL: ${{ secrets.BASE_RPC_URL }}
FILECOIN_MAINNET_PRIVATE_KEY: ${{ secrets.FILECOIN_MAINNET_PRIVATE_KEY }}
FILECOIN_MAINNET_RPC_URL: ${{ secrets.FILECOIN_MAINNET_RPC_URL }}
FILECOIN_PRIVATE_KEY: ${{ secrets.FILECOIN_PRIVATE_KEY }}
FILECOIN_RPC_URL: ${{ secrets.FILECOIN_RPC_URL }}
FURNACE_PRIVATE_KEY: ${{ secrets.FURNACE_PRIVATE_KEY }}
FURNACE_RPC_URL: ${{ secrets.FURNACE_RPC_URL }}
BASE_PRIVATE_KEY: ${{ secrets.BASE_PRIVATE_KEY }}
BASE_RPC_URL: ${{ secrets.BASE_RPC_URL }}
POLYGON_PRIVATE_KEY: ${{ secrets.POLYGON_PRIVATE_KEY }}
POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }}
OPTIMISM_SEPOLIA_PRIVATE_KEY: ${{ secrets.OPTIMISM_SEPOLIA_PRIVATE_KEY }}
OPTIMISM_SEPOLIA_RPC_URL: ${{ secrets.OPTIMISM_SEPOLIA_RPC_URL }}
SEI_TESTNET_PRIVATE_KEY: ${{ secrets.SEI_TESTNET_PRIVATE_KEY }}
SEI_TESTNET_RPC_URL: ${{ secrets.SEI_TESTNET_RPC_URL }}

jobs:
build:
Expand Down
153 changes: 99 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
## blocklock-js

blocklock-js is a TypeScript library designed to simplify the process of generating encrypted data off-chain for the dcrypt 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 receive the decryption keys automatically.


### Key Capabilities

Using this library, developers can:

* Encode and encrypt various Solidity-compatible data types.
* Encrypt the encoded data off-chain using a public key, which can then be integrated into smart contracts for timelock encryption requests.
`blocklock-js` is a TypeScript library that simplifies generating encrypted data off-chain for use with the dcrypt network. It allows developers to securely encrypt data tied to a user-defined condition, e.g., a future block height. This encrypted payload can then be referenced in on-chain timelock encryption requests via smart contracts. Once the specified block is mined, the decryption key is automatically delivered to the smart contract via a callback, enabling conditional data access on-chain.

The library also enables developers to track the status of a conditional encryption request.

### On-Chain Integration

Solidity interfaces and associated documentation for them can be found in the [blocklock-solidity](https://github.com/randa-mu/blocklock-solidity.git) repository.
Solidity interfaces and associated documentation can be found in the [blocklock-solidity](https://github.com/randa-mu/blocklock-solidity.git) repository.

#### Smart Contract Addresses

Expand All @@ -27,8 +20,13 @@ A lightweight proxy contract that enables upgradeability for the `BlocklockSende
| Filecoin Calibration Testnet | [0xF00aB3B64c81b6Ce51f8220EB2bFaa2D469cf702](https://calibration.filfox.info/en/address/0xF00aB3B64c81b6Ce51f8220EB2bFaa2D469cf702) |
| Base Sepolia | [0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e](https://sepolia.basescan.org/address/0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e) |
| Polygon PoS | [0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e](https://polygonscan.com/address/0x82Fed730CbdeC5A2D8724F2e3b316a70A565e27e) |
| Optimism Sepolia | [0xd22302849a87d5B00f13e504581BC086300DA080](https://sepolia-optimism.etherscan.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) |
| Arbitrum Sepolia | [0xd22302849a87d5B00f13e504581BC086300DA080](https://sepolia.arbiscan.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) |
| Avalanche (C-Chain) Testnet | [0xd22302849a87d5B00f13e504581BC086300DA080](https://testnet.snowtrace.io/address/0xd22302849a87d5B00f13e504581BC086300DA080) |
| Sei Testnet | [0xd22302849a87d5B00f13e504581BC086300DA080](https://seitrace.com/address/0xd22302849a87d5B00f13e504581BC086300DA080?chain=atlantic-2) |


**Others**
**Other contract addresses**
You should only need the `BlocklockSender` proxy above, but a full list of contract addresses can be found in the [solidity repo's README](https://github.com/randa-mu/blocklock-solidity).


Expand All @@ -41,53 +39,68 @@ npm install blocklock-js
```



### Usage Example

#### Prerequisites

* [ethers](https://www.npmjs.com/package/ethers) for wallet setup and message encoding.
* Node.js v22+

#### Setup

1. Create a `.env` file or set the following environment variables, e.g., for Filecoin mainnet:

Here’s how to use BlocklockJS to encrypt data and create an on-chain timelock encryption request.
```bash
RPC_URL=https://your-rpc-url
PRIVATE_KEY=your_private_key
```

#### Example: Encrypting a uint256 (4 ETH) for Decryption 2 Blocks Later
2. Install dependencies:

```bash
npm install
```


#### Usage example

This example demonstrates encrypting a `uint256` value and using the Ciphertext and condition bytes in a user smart contract that implements the `createTimelockRequestWithDirectFunding` function to create a timelock encryption request on-chain. An example use case is a sealed-bid auction where bid amounts are encrypted and only decrypted at the auction ending block number.

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](https://github.com/randa-mu/blocklock-solidity/blob/main/src/mocks/MockBlocklockReceiver.sol).

```js
import { ethers, getBytes } from "ethers";
import { Blocklock, SolidityEncoder, encodeCiphertextToSolidity, encodeCondition } from "blocklock-js";
import { MockBlocklockReceiver__factory } from "../types"; // Users' solidity contract TypeScript binding
This script shows how to encrypt data with a future block as a condition

async function main() {
// User wallet
const wallet = new ethers.Wallet("your-private-key", ethers.provider);
// User contract
const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect("user blocklcok receiver contract address", wallet);

// Ensure plainTextValue is initially 0
console.log("Initial plainTextValue:", (await mockBlocklockReceiver.plainTextValue()).toString());
```ts
import { createProvider, Blocklock, encodeCiphertextToSolidity, encodeCondition } from "blocklock-js"
import { Wallet, Provider, NonceManager, ethers, getBytes } from "ethers"
import { MockBlocklockReceiver__factory } from "../types"; // Users' solidity contract TypeScript binding

// Set block height (current block + 2)
const blockHeight = BigInt(await ethers.provider.getBlockNumber() + 2);
async function main() {
const rpc = createProvider(process.env.RPC_URL || "")
const wallet = new NonceManager(new Wallet(process.env.PRIVATE_KEY || "", rpc))
// Create a Blocklock instance for the Filecoin mainnet
const blocklock = Blocklock.createFilecoinMainnet(wallet)

// Value to encrypt (4 ETH as uint256)
const msg = ethers.utils.parseEther("4");
const plaintext = Buffer.from(msg)
const currentBlock = await rpc.getBlockNumber()
const targetBlock = BigInt(currentBlock + 5)

// Encode the uint256 value
const encoder = new SolidityEncoder();
const msgBytes = encoder.encodeUint256(msg);
const encodedMessage = getBytes(msgBytes);
console.log(`Encrypting for block ${targetBlock} (current: ${currentBlock})`)
const ciphertext = await blocklock.encrypt(plaintext, targetBlock)

// Encrypt the encoded message
const blocklockjs = new Blocklock(wallet, "blocklockSender contract address");
const ciphertext = blocklockjs.encrypt(encodedMessage, blockHeight);
// User contract
const mockBlocklockReceiver = MockBlocklockReceiver__factory.connect("user blocklcok receiver contract address", wallet);

// Generate the timelock encryption condition bytes string
const conditionBytes = encodeCondition(blockHeight);
const callbackGasLimit = 400_00;
const conditionBytes = encodeCondition(targetBlock);
// Amount of gas users callback function is expected to consume, i.e., the function that will be called with the decryption key
const callbackGasLimit = 500_000;

// Compute the request price to pay for the direct funding request
const requestPrice = await blocklock.calculateRequestPriceNative(callbackGasLimit);

// Call `createTimelockRequestWithDirectFunding` on the user's contract
// for a direct or ad hoc funding request with the following parameters:
Expand All @@ -96,7 +109,11 @@ async function main() {
// TypesLib.Ciphertext calldata encryptedData
const tx = await mockBlocklockReceiver
.connect(wallet)
.createTimelockRequestWithDirectFunding(callbackGasLimit, conditionBytes, encodeCiphertextToSolidity(ciphertext));
.createTimelockRequestWithDirectFunding(callbackGasLimit, conditionBytes, encodeCiphertextToSolidity(ciphertext),
{value: requestPrice});
// Note: for subscription-based funding,
// use createTimelockRequestWithSubscription instead.

const receipt = await tx.wait(1);

if (!receipt) {
Expand All @@ -114,30 +131,58 @@ main().catch((error) => {
#### How It Works
1. Encoding and Encryption:

* Use the SolidityEncoder to encode Solidity-compatible data types.
* Encrypt the encoded message and specify the decryption chain height.
* Generate the condition bytes string
* Use blocklock-js to:

* Create a provider and signer using Wallet and NonceManager.

* Initialize a Blocklock instance for your target network (e.g., Filecoin mainnet).

* Prepare the message to encrypt (e.g., 4 ETH encoded as a uint256).

* Select a future block height as the decryption condition.

```ts
const msg = ethers.utils.parseEther("4");
const plaintext = Buffer.from(msg);
const currentBlock = await rpc.getBlockNumber();
const targetBlock = BigInt(currentBlock + 5);

const ciphertext = await blocklock.encrypt(plaintext, targetBlock);
const conditionBytes = encodeCondition(targetBlock);
```

2. On-Chain Interaction:

* Use your own smart contract (e.g., `MockBlocklockReceiver`) to create a timelock encryption request.
* Call `createTimelockRequestWithDirectFunding`, which:

* Stores the encrypted data (Ciphertext) and decryption condition on-chain via the `BlocklockSender` contract.

2. On-Chain Interaction:
* Funds the request by paying the `requestPrice` via the transaction.

* Call the appropriate function in the user contract with the encrypted data and the chain height used during off-chain encryption. In this example, the `createTimelockRequestWithDirectFunding` function is called, which calls the [BlocklockSender](https://github.com/randa-mu/blocklock-solidity/blob/main/src/blocklock/BlocklockSender.sol) contract to create an on-chain timelock request with the encrypted data and condition (represented as bytes to support different condition types) for decryption, using the direct funding method. The `BlocklockSender` contract then stores the encrypted data, and generates a unique request ID. The `BlocklockSender` contract also supports a subscription funding method. To make a request via that is paid for via a funded subscription account, the `createTimelockRequestWithSubscription` function in the [example](https://github.com/randa-mu/blocklock-solidity/blob/main/src/mocks/MockBlocklockReceiver.sol) smart contract code can be called.
* Generates a unique request ID.Call the appropriate function in the user contract with the encrypted data and the chain height used during off-chain encryption.

3. Decryption:
```ts
const callbackGasLimit = 500_000;

const requestPrice = await blocklock.calculateRequestPriceNative(callbackGasLimit);

* After the specified chain height, the on-chain timelock contract triggers a callback to the user's contract, providing the decryption key. The user's contract can then call the `decrypt` function in the `BlocklockSender` contract to perform on-chain decryption using the provided decryption key.
await mockBlocklockReceiver.createTimelockRequestWithDirectFunding(
callbackGasLimit,
conditionBytes,
encodeCiphertextToSolidity(ciphertext),
{value: requestPrice}
);
```

3. Automatic Decryption and Callback:
* Once the specified condition is met (e.g., target block is mined):

#### Supported Data Types
The library supports encoding and encryption of the following Solidity-compatible data types:
* The `BlocklockSender` contract receives the decryption key from the dcipher Threshold Network.

* uint256
* int256
* address
* string
* bytes
* bytes32
* It automatically triggers a callback to the users contract with the key.

Use the `SolidityEncoder` to encode any of these types before encryption.
* The users contract can optionally call decrypt to recover the original message on-chain.


### Common Errors
Expand Down
2 changes: 1 addition & 1 deletion blocklock-solidity
Submodule blocklock-solidity updated 35 files
+44 −15 README.md
+2 −2 package-lock.json
+1 −1 package.json
+67 −0 script/DeployAllContracts.s.sol
+23 −0 script/DeployFactory.s.sol
+96 −0 script/README.md
+14 −0 script/json/README.md
+9 −0 script/libraries/Constants.sol
+46 −0 script/single-deployment/DeployBlocklockReceiver.s.sol
+145 −0 script/single-deployment/DeployBlocklockSender.s.sol
+54 −0 script/single-deployment/DeployBlocklockSignatureScheme.s.sol
+90 −0 script/single-deployment/DeployDecryptionSender.s.sol
+48 −0 script/single-deployment/DeploySignatureSchemeAddressProvider.s.sol
+31 −0 script/utils/EnvReader.sol
+33 −0 script/utils/JsonUtils.sol
+33 −44 src/AbstractBlocklockReceiver.sol
+34 −34 src/blocklock/BlocklockSender.sol
+7 −7 src/decryption-requests/DecryptionReceiverBase.sol
+42 −42 src/decryption-requests/DecryptionSender.sol
+2 −2 src/interfaces/IBlocklockReceiver.sol
+4 −6 src/interfaces/IBlocklockSender.sol
+2 −2 src/interfaces/IDecryptionReceiver.sol
+8 −8 src/interfaces/IDecryptionSender.sol
+3 −3 src/libraries/BLS.sol
+1 −1 src/libraries/TypesLib.sol
+8 −8 src/mocks/MockBlocklockReceiver.sol
+8 −8 src/mocks/MockBlocklockRevertingReceiver.sol
+8 −8 src/mocks/MockBlocklockStringReceiver.sol
+105 −21 test/forge/DirectFunding.t.sol
+23 −23 test/forge/SubscriptionFunding.t.sol
+0 −23 test/forge/base/Blocklock.t.sol
+4 −1 test/forge/base/Deployment.t.sol
+14 −12 test/hardhat/blocklock.test.ts
+0 −274 utils/mocks/create-blocklock-request.ts
+0 −103 utils/mocks/fetch-contract-data.ts
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blocklock-js",
"version": "0.0.8-rc4",
"version": "0.0.8-rc5",
"description": "A library for encrypting and decrypting data for the future",
"source": "src/index.ts",
"main": "./dist/cjs/index.cjs",
Expand Down Expand Up @@ -32,7 +32,7 @@
"clean": "rm -rf dist",
"lint": "eslint src",
"lint:fix": "eslint --fix",
"test": "jest ./test/*.test.ts"
"test": "jest ./test/*.test.ts --forceExit"
},
"keywords": [
"conditional",
Expand Down
Loading
Loading