Try the Call Decryption Oracle Locally


What you’ll do

  • Start a local test chain (anvil).
  • Deploy the on-chain Call Decryption Oracle Contract.
  • Start the off-chain Call Decryption Oracle.
  • Deploy the on-chain demo contract that talks to the call decryption oracle contract.
  • Check the results stored in the demo contract.

Required Installations

You need foundry (for the chain tooling) and jbang (to start a web service with the renderer). For installation instructions see the setup page. Apart from this we assume that openssl, cat, tr, xxd 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

After this switch to a new shell.

2. Generate the private and public key (used by the decryption oracle)

2.1. Download the script that will generate a new private key for the decryption oracle.

REPO=https://gitlab.com/finmath/finmath-decryption-oracle/-/raw/main

curl --create-dirs -L $REPO/scripts/generate-keys.sh -o generate-keys.sh
chmod 755 generate-keys.sh

2.1. Generate the private key for the key decryption oracle

export SECRETS_DIR=$(pwd)/secrets

./generate-keys.sh

2.2. Extract the public key (used to encrypt the arguments of function calls)

CERT="$SECRETS_DIR/oracle.crt.pem"
export PUBLIC_KEY="$(openssl x509 -in "$CERT" -noout -pubkey | openssl pkey -pubin -outform DER | xxd -p -c 1000000 | tr -d '\r\n' | sed 's/^/0x/')"
echo "$PUBLIC_KEY"

3. Deploy the Key Decryption Oracle Contract and the Demo Contract

3.1. Download CallDecryptionOracle.sol and CallDecryptionOracleRouter.sol

Run:

REPO=https://gitlab.com/finmath/finmath-decryption-oracle/-/raw/main/oracle-execution

curl --create-dirs -L $REPO/src/main/solidity/ICallDecryptionOracle.sol -o ICallDecryptionOracle.sol
curl --create-dirs -L $REPO/src/main/solidity/ICallDecryptionOracleRouterDemo.sol -o ICallDecryptionOracleRouterDemo.sol
curl --create-dirs -L $REPO/src/main/solidity/CallDecryptionOracle.sol -o CallDecryptionOracle.sol
curl --create-dirs -L $REPO/src/main/solidity/CallDecryptionOracleRouter.sol -o CallDecryptionOracleRouter.sol

3.2. Build and deploy the contract CallDecryptionOracle

Run:

export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"

OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
CONTRACT_PATH=.
CONTRACT_NAME=CallDecryptionOracle

ORACLE_CONTRACT=$(
  forge create --use 0.8.23 --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:$CONTRACT_NAME" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
    --constructor-args "$OWNER" "$PUBLIC_KEY" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

echo "Contract deployed at $ORACLE_CONTRACT"

3.3. Build and deploy the contract CallDecryptionOracleRouter

Run:

export RPC_URL="http://127.0.0.1:8545"
export PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"

OWNER=$(cast wallet address --private-key "$PRIVATE_KEY")
CONTRACT_PATH=.
CONTRACT_NAME=CallDecryptionOracleRouter

ROUTER_CONTRACT=$(
  forge create --use 0.8.23 --via-ir --broadcast --root . --json "$CONTRACT_PATH/$CONTRACT_NAME.sol:CallDecryptionOracleRouter" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" \
    --constructor-args "$ORACLE_CONTRACT" \
  | tr -d '\r\n' \
  | sed -nE 's/.*"deployedTo"[[:space:]]*:[[:space:]]*"(0x[0-9a-fA-F]{40})".*/\1/p'
)

echo "Contract deployed at $ROUTER_CONTRACT"

4. Launch the CallDecryptionOracle service (locally)

4.1. Launch the CallDecryptionOracle service (locally)

Run (in the same shell that deployed the contract and the same folder as $SECRETS_DIR):

jbang --java 21 -Dethereum.rpcUrl=http://127.0.0.1:8545 -Dethereum.chainId=1337 -Dethereum.calldecryptionoracle.contract=$ORACLE_CONTRACT -Dethereum.calldecryptionoracle.stateFile=oracle-state.txt net.finmath:finmath-decryption-oracle-encrypted-hashed-execution:2.5.5 > jbang.log 2>&1 </dev/null &

4.2. Show the log of the KeyDecryptionOracle

(Optional): In a new Window / shell, in the same directory, run:

tail -f jbang.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 $ORACLE_CONTRACT and $ROUTER_CONTRACT).


The decryption oracle is up and running now, we now perform a test, generating encrypted arguments, storing them, requesting decryption and execution of a function call.

5. Encrypted Arguments Generation

Pasting the following lines in your shell (e.g. bash) will generate the encrypted arguments for (uint256) 20, (string) “Peach” (uint256) 990. It will construct a call descriptor for a function that has that signature.

ARGS_PLAIN=$(cast abi-encode "func(uint256,string,uint256)" 20 "Peach" 990)

ARGS_HASH=$(cast keccak "$ARGS_PLAIN")
export ARGS_DESCRIPTOR=$(cast abi-encode "wrap(address[],bytes)" [] $ARGS_PLAIN)
export ORACLE_PUBLIC_KEY=$(cast call "$ORACLE_CONTRACT" "getPublicKey()(bytes,bytes32)" --rpc-url "$RPC_URL" | head -n 1)
ENCRYPTED_ARGS=$(jbang net.finmath:finmath-decryption-oracle-base:2.5.5:cli)

FEE_CALL_DECRYPT=$(cast call "$ORACLE_CONTRACT" "feeCall()(uint256)" --rpc-url "$RPC_URL")
ECHO $FEE_CALL_DECRYPT

DEMO_TARGET_CONTRACT=$ROUTER_CONTRACT
DEMO_TARGET_METHOD="demoTargetFunction"
DEMO_TARGET_VALID_UNTIL_BLOCK=0
DEMO_SELECTOR=$(cast sig "$DEMO_TARGET_METHOD(uint256,string,uint256)")
DEMO_CALL_DESCRIPTOR=$(cast abi-encode "CallDescriptor(address,bytes4,uint256)" $DEMO_TARGET_CONTRACT $DEMO_SELECTOR $DEMO_TARGET_VALID_UNTIL_BLOCK)

printf "\n\n\e[1mDemo Call Descriptor:\e[0m\n$DEMO_CALL_DESCRIPTOR\n\n\e[1mDemo Encrypted Arguments:\e[0m\n$ENCRYPTED_ARGS\n\n\e[1mArgs Hash:\e[0m\n$ARGS_HASH\n\n\e[1mFee (for Decrypt) in Wei (1E18):\e[0m\n$FEE_CALL_DECRYPT\n\n"

6. Use the encrypted arguments in the demo contract

6.1 Store the encrypted arguments for later use in a function call

Run:

ID=5

cast send "$ROUTER_CONTRACT" \
  "store(uint256,bytes,bytes,bytes32)" $ID $DEMO_CALL_DESCRIPTOR $ENCRYPTED_ARGS $ARGS_HASH \
  --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 1000000

6.2 Define a Helper Function to Fetch the Result form the Demo 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)(uint256,address,address,bytes4,bytes,bytes32,uint256,bytes,bytes32,bool)'
    
    # temp file to avoid subshell issues
    local tmpfile
    tmpfile="$(mktemp)"
    
    cast call "$ROUTER_CONTRACT" "$sig" "$id" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" > "$tmpfile" || {
        rm -f "$tmpfile"
        echo "cast call failed" >&2
        return 1
    }
    vars=(phase caller targetContract targetSelector argsEncrypted argsHash requestId argsPlain argsHashComputed routingSuccess)
    {
      for v in "${vars[@]}"; do
        IFS= read -r "$v" || break
      done
    } < "$tmpfile"

    rm -f "$tmpfile"
    
# Optional pretty print
cat <<RESULT
    phase.............: $phase (0=new, 1=stored, 2=requested, 3=callback, 4=routed, 5=final)
    caller............: $caller
    targetContract....: $targetContract
    targetSelector....: $targetSelector
    argsEncrypted.....: $argsEncrypted
    argsHash..........: $argsHash
    requestId.........: $requestId
    argsPlain.........: $argsPlain
    argsHashComputed..: $argsHashComputed
    routingSuccess....: $routingSuccess
RESULT
}

6.3 Fetch the Result

Run:

oracle_get_result $ID

This will fetch the rsult. The encrypted key will be stored under $encryptedKey.

7. Decryption of the Arguments

7.1 Trigger decryption of the arguments (through the demo contract)

Run:

cast send "$ROUTER_CONTRACT" \
  "decrypt(uint256)" $ID \
  --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 300000

7.2 Fetch the Result

Run:

oracle_get_result $ID

This will fetch and display the result.

Repeating this call, you may first observe that “phase 2 (=requested)”, then a few seconds later “phase 3 (=callback)”. In the log of the decryption oracle service you will see the request and the fulfillment.

8. Execution of a Function Call

8.1 Trigger execution of a function call with the decrypted arguments (through the demo contract)

Run:

cast send "$ROUTER_CONTRACT" \
  "execute(uint256)" $ID \
  --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY" --legacy --gas-price 1gwei --gas-limit 300000

8.2 Fetch the Result

Run:

oracle_get_result $ID

This will fetch and display the result.

9. Routing

9.1 Fetch the Results of the Routing

The demo contract automatically triggers a rounting a function with typed arguments (uint256,string,uint256) where we should recover the arguments that were encrypted above.

Run:

cast call "$ROUTER_CONTRACT" "getDemoTargetValues(uint256)(uint256,string,uint256)" "$ID" --rpc-url "$RPC_URL" --private-key "$PRIVATE_KEY"

Last updated: 2025‑12‑24