Deployment and Configuration


⚠️ Before you continue: Please read the Disclaimer.
By using this site, software, or contracts, you acknowledge that you have read and accepted it.


Run Model

IMPORTANT: The event listener may perform a hard System.exit(1) (e.g. upon lost web socket connection). In that case, the container should schedule a restart (Docker: restart: unless-stopped). This is by design: It is much harder to recover a lost ws connection than to schedule a clean restart. The oracle is implemented in a way, that it will pick-up the unprocessed events.

Setup

If you like to set up a key decryption oracle from scratch:

Once (Initial Setup)

  • Owner EOA: You need an off-chain private key and address that functions as the owner of the on-chain contract and can deploy the on-chain oracle contract.

  • Oracle EOA: You need an off-chain private key and address that functions as the signer of the fulfillments performed by the off-chain oracle.

  • Deploy the on-chain oracle procy contract (src/main/solidity/KeyDecryptionOracle.sol) using the off-chain oracle address (signer of the fulfillments) in the constructor. Note the address of the on-chain oracle contract.

  • Generate a private-public-key pair for the “key decryption” using the script scripts/generate-keys.sh in the parent project. This will create a directory secrets and the following files

    secrets/oracle.crt.pem
    secrets/oracle.p12
    secrets/oracle.pw
    secrets/oracle-public-key.der
    secrets/oracle-public-key.pem
    

    Note: This is the private-public-key that is used for encryption and decryption of the key provided by the decryption oracle. You reference these files in the application.properties. Put them in a safe place and make them available to the off-chain oracle. See docker-compose.yml and application.properties.

  • Adapt the application.properties (see below)

    • Add private key of the off-chain oracle EOA (needed to sign the fulfill*). Set ethereum.sender.privateKey
    • Add the address of the off-chain oracle EOA (needed to sign the fulfill*). Set ethereum.sender.address
    • Add the address of the on-chain oracle contract (needed to for the event listening to the request*). Set ethereum.keydecryptionoracle.contract
    • If the secrets folder is mounted in the docker-compose.yml you do not adjust the setting of oracle.keystore.*.
  • Adapt the docker-compose.yml (see below)

    • Adjust PATH-TO-CONFIG in /PATH-TO-CONFIG/secrets:/app/secrets:ro to the location of the generated secrets (needs to be secure).
    • Adjust PATH-TO-CONFIG in /PATH-TO-CONFIG/state:/app/state:rw to the location where the off-chain oracle can persist state.
    • Adjust PATH-TO-CONFIG in /PATH-TO-CONFIG/config:/app/config:ro to the location where the application.propierties resides (needs to be secure, because it contains the private key for EOA).

Start and Stop

For the folder containing the docker-compose.yml (see below):

  • Use docker-compose up -d to start the off-chain oracle.
  • Use docker-compose down to stop the off-chain oracle.
  • Use docker-compose log -f to view the logs in the console.

Configuration: Application Properties

Configure the off-chain oracle via command-line or application.properties:

Important: if you use listenerMode to be stream then the rpcUrl must use wss://.

# RPC URL (use either ws or http - example: wss://eth-mainnet.g.alchemy.com/v2/... or https://eth-mainnet.g.alchemy.com/v2/...). If streaming, use ws.
ethereum.rpcUrl = <url>
# Numeric chain id (ethereum mainnet: 1, ethereum testnet: 11155111, polygon mainnet: 137, polygon testnet: 80002, local (besu/anvil): 1337)
ethereum.chainId = <id>
# Poll interval if the listener is http (not used in ws-mode)
ethereum.listener.pollIntervalMs = 5000
# Poll chunk size (maximum number of blocks that are requested in a single call to the RPC)
ethereum.listener.pollingChunkSizeBlocks = 500

# ReST Service port
server.port=8080

# Chain Event Listener (required)
ethereum.listener.enabled=true
ethereum.tx.enabled=true

# Sender of the fulfill-ment of request-s
ethereum.sender.privateKey = <private key hex 0x...>
# Address of sender (this needs to be passed to the on-chain oracles constructor)
ethereum.sender.address = <address hex of the sender 0x..>

#
# Listener Configuration
#

# Listener mode: may be poll (polling of ethGetLogs), steam-http (ehtLogFlowable on http (will poll)), stream-ws (ethLogFlowable on ws), stream (sames as stream-ws). Note: stream: cheap with low latency on small number of events; poll: usually cheaper with high number of events.
ethereum.calldecryptionoracle.listenerMode = stream

# Address of the on chain CallDecryptionOracle (to listen to)
ethereum.calldecryptionoracle.contract = <address hex of the on-chain oracle 0x..>

# Block to start listening, given that no state file is found
ethereum.calldecryptionoracle.fromBlock = latest

# finalityBlocks: block to wait (and to check finality thereafter). default 0 = process immediately; >0 waits N confirmations
ethereum.calldecryptionoracle.finalityBlocks = 3

# replayBlocks: block to replay upon restart. default 0 = no replay; if 0, uses finalityBlocks
ethereum.calldecryptionoracle.replayBlocks = 0

# finalityPollIntervalMs: head polling interval; only calls eth_blockNumber while pending queue is non-empty
ethereum.calldecryptionoracle.finalityPollIntervalMs = 5000

# reconciliationIntervalMs: if > 0 and streaming is used, an optional reconciliation polling is done periodically
ethereum.calldecryptionoracle.reconciliationIntervalMs = 90000

# reconciliationChunkSizeBlocks: Poll chunk size (maximum number of blocks that are requested in a single call to the RPC)
ethereum.calldecryptionoracle.reconciliationChunkSizeBlocks = 2000

# stateFile: Location of state file (cursor position). If empty or missing /app/state/<contract>-lastBlock will be used.
ethereum.calldecryptionoracle.stateFile

# stateWriteMode: either atomic-move ore direct. The atomic-move will first create a tmp file, the try an atomic move.
ethereum.calldecryptionoracle.stateWriteMode = atomic-move

#
# EIP-1559 fee policy (values in wei)
#

# Tip high enough to get picked quickly under load.
# Suggestion: ethereum 20000000 (0.02 gwei), polygon 40000000000 (40 gwei)
ethereum.gas.min-priority-fee-wei=40000000000

# Cap high so base-fee spikes don't stall you. We pay (base + priority) and not this cap
# Suggestions:
#   tight..: ethereum 1000000000 (1 gwei), polygon 500000000000 (500 gwei)
#   relaxed: ethereum 5000000000 (5 gwei), polygon 1000000000000 (1000 gwei)
ethereum.gas.max-fee-wei=1000000000000

# Gas limits (units) - do NOT affect speed, just OOG safety
# Suggestion: 1000000
ethereum.gas.limit-default=1000000

#
# keystore for the decryption oracle
#
oracle.keystore.type=PKCS12
oracle.keystore.path=secrets/oracle.p12
oracle.keystore.password-file=secrets/oracle.pw
oracle.key.alias=oracle-rsa
# optional if different from keystore password:
# oracle.key.password-file=/run/secrets/oracle.pw

Remarks

Gas

Ensure that off-chain oracle is funded and that the ethereum.gas.* parameters are set appropriately.

Fees

The off-chain oracle (ethereum.sender.privateKey) needs to be funded to pay the gas for a fulfillment. The on-chain oracle receives the fee at (ethereum.keydecryptionoracle.contract). The oracle implementation should allow to withdraw the fees. The reference implementation provides a method that transfers the accumulated on-chain fees to the off-chain oracle sender.

Private Key for the Decryption Oracle

The decryption oracle needs a public/private key for the generation/decryption of the encrypted key. You can generate the required files using the script generate-keys.sh.

Docker image

The latest published image is

docker.io/finmath/finmath-decryption-oracle-encrypted-hashed-execution:3.0.0

It predates the 4.0.1 Maven reactor release. For exact source/runtime parity, build the execution image from the 4.0.1 release tag and publish it only after verifying the Maven 4.0.1 artifacts. The compose example below deliberately shows the currently published 3.0.0 image.

Configuration: Docker Compose

Example docker-compose.yml

Given that the secrets are in /PATH-TO-CONFIG/secrets and the application.properties is in /PATH-TO-CONFIG/config a sample docker compose yml may look as follows:

name: decryption-oracle

services:
  key_decryption_oracle:
    image: docker.io/finmath/finmath-decryption-oracle-encrypted-hashed-execution:3.0.0
    container_name: key-decryption-oracle
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      SPRING_CONFIG_LOCATION: "file:/app/config/application.properties"
    volumes:
      - /PATH-TO-CONFIG/secrets:/app/secrets:ro
      - /PATH-TO-CONFIG/state:/app/state:rw
      - /PATH-TO-CONFIG/config:/app/config:ro