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.keydecryptionoracle.listenerMode = stream

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

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

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

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

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

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

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

#
# 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

# key format (xml or hex)
oracle.key-format=hex
# hashing method (keccak256 or sha256)
oracle.hash-method=sha256

Configuration of the Encryption/Decryption and Hashing Method

Key Format

The key K is a byte-sequence (Java: byte[], Solidity:bytes), but one may specify what the sequence represents if interpreted as a string. This can be UTF-8 XML or a Hex-String.

oracle.key-format

possible values are

  • xml - the byte sequence IS an UTF-8 string of an XML file. This is the default.
  • hex - the byte seqeunce IS an ASCII string of a HEX sequence (only characters 0-9A-F).

Hashing Method

The hashing method that is used in the generation / verification steps can be configured via

oracle.hash-method

possible values are

  • keccak256 32 bytes keccak-256. This is the default.
  • sha256 32 bytes sha-256.
  • ripemd160 20 bytes ripemd-160.

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

A docker image is available at

docker.io/finmath/finmath-decryption-oracle-encrypted-hashed-keys:2.5.5

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-keys:2.5.5
    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