Key Decryption Oracle
What you’ll do
- Start a local test chain (anvil).
- Deploy the on-chain Key Decryption Oracle Contract.
- Start the off-chain Key Decryption Oracle.
- Deploy the on-chain demo contract that talks to the key decryption oracle contract.
- Check the results stored in the demo contract.
Required Installations
You need foundry (for the chain tooling), Git, and Java 17 or newer. The repository's
Maven wrapper builds the matching off-chain service.
For installation instructions see the setup page.
Apart from this we assume that cat, tr and sed are present.
The following commands should be run in a shell (macOS Terminal, Windows GitBash). You may use the copy button and just paste them into the shell.
Step-by-Step
1. Launch a local chain for testing
Run:
anvil --chain-id 1337
2. Deploy the Key Decryption Oracle Contract and the Demo Contract
2.1. Check out the 4.0.1 release sources
Run:
RELEASE_TAG=finmath-decryption-oracle-parent-4.0.1
SOURCE_DIR=finmath-decryption-oracle-4.0.1
git clone --branch "$RELEASE_TAG" --depth 1 https://gitlab.com/finmath/finmath-decryption-oracle.git "$SOURCE_DIR"
CONTRACT_PATH="$SOURCE_DIR/oracle-keys/src/main/solidity"
2.2. Build and deploy the contract KeyDecryptionOracle
Run:
export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
CONTRACT_NAME=KeyDecryptionOracle
FULFILLMENTMODE=0
PERMISSIONLESS=true # LOCAL DEMO ONLY; production deployments must use an allowlist
CONTRACT_ADDRESS=$(
forge create --use 0.8.20 --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:$CONTRACT_NAME" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
--constructor-args "$OWNER" "$FULFILLMENTMODE" "$PERMISSIONLESS" \
| tr -d '\r\n' \
| sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)
echo "Contract deployed at $CONTRACT_ADDRESS"
2.3. Build and deploy the contract DemoContract
Run:
export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
CONTRACT_NAME=DemoContract
DEMO_CONTRACT_ADDRESS=$(
forge create --use 0.8.20 --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:$CONTRACT_NAME" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
--constructor-args "$CONTRACT_ADDRESS" \
| tr -d '\r\n' \
| sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)
echo "Contract deployed at $DEMO_CONTRACT_ADDRESS"
3. Launch the KeyDecryptionOracle service (locally)
3.1. Use the matching current sources
The on-chain contract and off-chain service must come from the same 4.0.1 release tag. The tagged 3.0.0 service still implements scalar verification and is not compatible with the batch-verification/request-ID ABI used in this walkthrough. The source checkout from step 2.1 is therefore also used to build the service.
3.2. Generate the private key for the key decryption oracle
export SECRETS_DIR=$(pwd)/secrets
"$SOURCE_DIR/scripts/generate-keys.sh"
3.3. Launch the KeyDecryptionOracle service (locally)
Build and run the service from that checkout (in the same shell that deployed the contract):
"$SOURCE_DIR/mvnw" -f "$SOURCE_DIR/pom.xml" -pl oracle-keys -am -DskipTests package
java \
-Dethereum.rpcUrl=http://127.0.0.1:8545 \
-Dethereum.chainId=1337 \
-Dethereum.keydecryptionoracle.contract="$CONTRACT_ADDRESS" \
-Dethereum.keydecryptionoracle.stateFile=oracle-state.txt \
-Doracle.keystore.path="$SECRETS_DIR/oracle.p12" \
-Doracle.keystore.password-file="$SECRETS_DIR/oracle.pw" \
-jar "$SOURCE_DIR/oracle-keys/target/finmath-decryption-oracle-encrypted-hashed-keys-4.0.1.jar" \
> oracle.log 2>&1 </dev/null &
3.4. Show the log of the KeyDecryptionOracle
(Optional): In a new Window / shell, in the same directory, run:
tail -f oracle.log
This allows you to observe the log of the decryption oracle service.
Then return to the previous shell that deployed the contract (this is important, because we will use the environment variables $CONTRACT_ADDRESS and $DEMO_CONTRACT_ADDRESS).
4. Key Generation
4.1 Trigger a key generation (through the demo contract)
Run:
ID=1
TRANSACTION=0x0002
cast send "$DEMO_CONTRACT_ADDRESS" \
"requestGenerate(uint256,bytes)" $ID $TRANSACTION \
--rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 500000
The demo contract will trigger a one-element key-generation batch and will await the
complete result. The result will be stored under result id $ID.
4.2 Define a Helper Function to Fetch the Result form the Contract
We define a function that fetches the result from the demo contract.
After this function has been defined we may fetch a result from the contract via oracle_get_result <id>.
Run:
oracle_get_result () {
local id="$1"
local sig='getResult(uint256)(uint8,uint8,address,bytes,bytes,bytes,bool,address,bytes,uint256,uint256)'
# temp file to avoid subshell issues
local tmpfile
tmpfile="$(mktemp)"
cast call "$DEMO_CONTRACT_ADDRESS" "$sig" "$id" --rpc-url "$RPC_URL" > "$tmpfile" || {
rm -f "$tmpfile"
echo "cast call failed" >&2
return 1
}
vars=(status kind requester key encryptedKey hashedKey verified receiverContract transaction createdAt updatedAt)
{
for v in "${vars[@]}"; do
IFS= read -r "$v" || break
done
} < "$tmpfile"
rm -f "$tmpfile"
# Optional pretty print
cat <<RESULT
kind..............: $kind (1=generate, 2=verify, 3=decrypt)
status............: $status (0=None, 1=Pending, 2=Generated, 3=Verified, 4=Released, 5=Denied, 6=VerificationFailed)
requester.........: $requester
key...............: $key
encryptedKey......: $encryptedKey
hashedKey.........: $hashedKey
verified..........: $verified
receiverContract..: $receiverContract
transaction.......: $transaction
createdAt.........: $createdAt
updatedAt.........: $updatedAt
RESULT
}
4.3 Fetch the Result
Run:
oracle_get_result $ID
This will fetch the rsult. The encrypted key will be stored under $encryptedKey.
5. Key Verification
5.1. Request a Key Verification
We take the previously generated $encryptedKey and request an atomic one-element
verification batch. The oracle returns the hash and bound receiver/transaction
context without exposing the decrypted key. The result is accepted only when the explicit
$verified value is true.
Run:
ID=2
ENC_KEY=$encryptedKey
cast send "$DEMO_CONTRACT_ADDRESS" \
"requestVerify(uint256,bytes)" $ID $ENC_KEY \
--rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 300000
5.2 Fetch the Result
Run:
oracle_get_result $ID
This will fetch and display the result.
6. Decryption
6.1. Request a Key Decryption
We take the previously generated $encryptedKey and request a decryption. The decryption can only be requested to be submitted to
an eligible contract (information that is contained in the key) - in our case the demo contract is eligible.
Run:
ID=3
ENC_KEY=$encryptedKey
cast send "$DEMO_CONTRACT_ADDRESS" \
"requestDecrypt(uint256,bytes,bytes)" $ID $ENC_KEY $TRANSACTION \
--rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 300000
6.2 Fetch the Result
Run:
oracle_get_result $ID
This will fetch and display the result.
Created: 2025‑12‑24. Last updated: 2026‑08‑02.
