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.