Skip to content
chidubestevePublic

About

Proof-of-concept: oracle-mediated pre-ingestion validation for blockchain-integrated digital twin systems. TypeScript · MQTT · Chainlink Functions · Base Sepolia · Staffordshire University FYP.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

TrustFlow-DT

Oracle-mediated validation and blockchain anchoring for a simulated Digital Twin

Final Year Project — BSc (Hons) Computer Science, Staffordshire University Author: Chidube Steve Anike

TrustFlow-DT is a proof-of-concept system that validates signed IoT telemetry before it updates a Digital Twin or is committed to a blockchain. It combines a simulated temperature sensor, MQTT messaging, a fail-closed local oracle, Digital Twin command logic, deterministic batching, IPFS storage, Chainlink Runtime Environment (CRE), and Solidity contracts deployed on Polygon Amoy.

The central design principle is that blockchain immutability protects data after commitment; it does not establish whether the original sensor reading was authentic, current, unique, or physically plausible. TrustFlow-DT therefore places an explicit validation boundary before both Digital Twin consumption and blockchain anchoring.

This repository is an academic proof of concept. It is not production-ready infrastructure and must not be used to control real equipment.

Architecture

TrustFlow-DT six-layer bidirectional architecture

Layer Implemented component Main location
1. Physical simulation Temperature generator, ECDSA secp256k1 signing, and controlled anomaly injection src/simulation/, src/mqtt/publisher.ts
2. Messaging MQTT raw, validated, anomaly, and command streams src/config/mqttClient.ts, src/mqtt/
3. Local validation Registry status, signature, timestamp, nonce, threshold, and rate-of-change checks src/validation/
4. Relay and CRE Per-device batching, deterministic hashing, IPFS pinning, HTTP relay, and CRE field consensus src/relay/, cre/trustflow-anchor-workflow/
5. Blockchain Device lifecycle, role-based anchoring, CRE receiver, events, and public hash verification src/blockchain/
6. Digital Twin Live accepted-state processing, anomaly responses, and QoS 2 command publication src/scripts/runDTCore.ts

The latency-sensitive Digital Twin path is independent of blockchain confirmation. Validated telemetry can update the twin immediately, while the same validated stream is batched and anchored asynchronously.

MQTT topics

Topic QoS Purpose
trustflow/raw/{deviceId} 1 Signed telemetry from the simulated device
trustflow/validated/{deviceId} 1 Readings accepted by the oracle
trustflow/anomaly/{deviceId} 1 Rejected readings and reason codes
trustflow/commands/{deviceId} 2 Commands issued by the Digital Twin Core

Technology

  • Node.js and TypeScript
  • Mosquitto and MQTT.js
  • Node.js crypto with ECDSA on secp256k1
  • Express
  • Pinata and IPFS
  • Chainlink Runtime Environment
  • Solidity 0.8.28, Hardhat 3, Ethers.js, Mocha, and Chai
  • Polygon Amoy testnet

Prerequisites

For the local telemetry and evaluation path:

  • Node.js 20 or later
  • npm
  • Mosquitto broker and command-line clients

For the complete anchoring path:

  • a Pinata account and JWT
  • a Polygon Amoy RPC endpoint
  • funded Polygon Amoy accounts
  • the Chainlink CRE CLI
  • Bun for the CRE workflow dependencies
  • a public HTTPS tunnel, such as ngrok, when CRE must reach the local relay

Quick Start: Local Validated Telemetry

1. Clone and install

git clone https://github.com/chidubesteve/FYP.git
cd FYP
npm install

2. Create the environment file

macOS or Linux:

cp .env.example .env

Windows PowerShell:

Copy-Item .env.example .env

For the local path, confirm these values in .env:

TRUSTFLOW_BROKER_URL=mqtt://localhost:1883
TRUSTFLOW_ANCHOR_DEVICE_ID=device-001
TRUSTFLOW_KEY_PATH=keys/device-001.json
TRUSTFLOW_POLICY=normal
TRUSTFLOW_REGISTRY_PATH=config/devices.json
TRUSTFLOW_INTERVAL_MS=1000
TRUSTFLOW_DURATION_MS=0
TRUSTFLOW_ENABLE_REGISTRY_SYNC=false

Do not commit .env, wallet private keys, Pinata credentials, or files in keys/.

3. Start Mosquitto

mosquitto

4. Start the publisher once to provision its key

In a second terminal:

npx tsx src/mqtt/publisher.ts

On its first run, the publisher creates keys/device-001.json. Stop it with Ctrl+C.

The oracle verifies the publisher against config/devices.json. If a new key was generated, copy the generated public key only into the publicKey field for device-001 in that registry. Never copy the private key into the registry.

5. Start the oracle

npx tsx src/scripts/runOracle.ts

The oracle subscribes to trustflow/raw/#. A valid reading is republished to trustflow/validated/{deviceId}; a rejected reading is published to trustflow/anomaly/{deviceId} with its rejection reason.

6. Start the Digital Twin Core

In another terminal:

npx tsx src/scripts/runDTCore.ts

The Digital Twin Core listens to the validated and anomaly streams, maintains the latest accepted state in memory, and publishes operational or trust-response commands to trustflow/commands/{deviceId} at QoS 2.

To inspect those commands:

mosquitto_sub -h localhost -p 1883 -t "trustflow/commands/#" -v

7. Start normal telemetry

npx tsx src/mqtt/publisher.ts

Stop each long-running service with Ctrl+C.

Running Evaluation Experiments

Keep Mosquitto and the oracle running. Do not start a separate publisher or attacker: runEvaluation.ts starts and stops the required traffic generators. Running another publisher at the same time contaminates the experiment counts.

macOS or Linux:

TRUSTFLOW_EXPERIMENT=mixed-100 npx tsx src/scripts/runEvaluation.ts

Windows PowerShell:

$env:TRUSTFLOW_EXPERIMENT="mixed-100"
npx tsx src/scripts/runEvaluation.ts

Available experiments are:

Experiment Purpose
baseline Normal signed telemetry with no deliberate anomaly
sensor-spike Out-of-range sensor spikes
sensor-drift Gradually drifting sensor readings
sensor-frozen Repeated constant values; a documented negative case
sensor-rate-spike Excessive rate-of-change events
attack-replay Reuse of a previously accepted signed reading
attack-forged-sig Telemetry carrying an invalid signature
attack-unknown-device Telemetry from an unregistered device
attack-stale Correctly formed but stale telemetry
mixed-100 Combined sensor anomalies and attacker actions

The evaluator correlates injected readings and oracle decisions by nonce and reports true positives, false negatives, false positives, wrong-reason classifications, precision, recall, F1 score, and publish-to-validation latency.

Current evaluation-harness caveat: runEvaluation.ts does not yet pass its generated TRUSTFLOW_RUN_ID to runAttacker.ts, and the sensor-spike injector records spike while the oracle emits out_of_range. Until those two mappings are aligned, attacker rejections can appear as false positives and detected spikes can appear under “wrong reason�. The oracle decisions remain visible, but those affected aggregate metrics should not be treated as final results.

Injection logs are written under keys/ and are excluded from version control.

Fuzz testing

With Mosquitto and the oracle running:

npx tsx src/scripts/runFuzz.ts

Override the default number of cases per category if required:

TRUSTFLOW_FUZZ_ITERATIONS=100 npx tsx src/scripts/runFuzz.ts

PowerShell:

$env:TRUSTFLOW_FUZZ_ITERATIONS="100"
npx tsx src/scripts/runFuzz.ts

Relay, IPFS, and Batch Finalisation

Add the following to .env:

PINATA_JWT=your_pinata_jwt
TRUSTFLOW_RELAY_PORT=4000

Start Mosquitto and the oracle, then start the relay:

npx tsx src/relay/relay.ts

The relay subscribes only to trustflow/validated/# and maintains a separate pending batch for each device. After validated readings have accumulated, finalise and pin the current batch:

curl -X POST http://localhost:4000/batch/device-001/finalise

Useful endpoints:

Method and path Purpose
GET /health Relay status and batch counts
POST /batch/:deviceId/finalise Freeze, hash, and pin the pending batch
GET /batch/:deviceId/latest Latest finalised commitment for a device
GET /batch/id/:batchId Retrieve a finalised batch by ID
GET /batch/hash/:hash Retrieve a finalised batch by composite hash

Batch finalisation is currently triggered manually. Automatic size-based or time-based finalisation is a future extension.

Smart Contracts

Compile, type-check, and test

npm run typecheck
npm run typecheck:hardhat
npm run compile:contracts
npm run test:contracts

The contract suite covers access control, device registration and lifecycle changes, anchoring validation, events, and public batch-hash verification.

Polygon Amoy configuration

Hardhat secrets should be stored in its encrypted keystore rather than committed to .env:

npx hardhat keystore set ALCHEMY_POLYGON_AMOY_RPC_URL
npx hardhat keystore set POLYGON_AMOY_PRIVATE_KEY
npx hardhat keystore set ORACLE_PRIVATE_KEY
npx hardhat keystore set ETHERSCAN_API_KEY

Deploy the main contract:

npx hardhat run src/blockchain/scripts/deploy.ts --network polygonAmoy

The script writes deployment metadata to deployments/.

Deploy the CRE receiver after setting the forwarder address:

macOS or Linux:

TRUSTFLOW_CRE_FORWARDER_ADDRESS=0x... \
npx hardhat run src/blockchain/scripts/deployCREReceiver.ts --network polygonAmoy

Windows PowerShell:

$env:TRUSTFLOW_CRE_FORWARDER_ADDRESS="0x..."
npx hardhat run src/blockchain/scripts/deployCREReceiver.ts --network polygonAmoy

Grant the deployed receiver the main contract's oracle role:

npx hardhat run src/blockchain/scripts/assignOracleRoleToCREReceiver.ts --network polygonAmoy

The repository also contains scripts to update device status, repair the configured forwarder address, generate an oracle wallet, and exercise the earlier direct anchoring route. See src/blockchain/scripts/.

Optional on-chain registry synchronisation

To reconcile the local oracle registry with the current deployed contract at startup and listen for later lifecycle events:

TRUSTFLOW_ENABLE_REGISTRY_SYNC=true
ALCHEMY_POLYGON_AMOY_RPC_URL=https://polygon-amoy.g.alchemy.com/v2/...

Then start the oracle normally:

npx tsx src/scripts/runOracle.ts

The synchroniser resolves on-chain device hashes only for devices already known in config/devices.json.

Chainlink CRE Workflow

The original proposal used Chainlink Functions. The implemented version uses the Chainlink Runtime Environment.

  1. Start the relay and collect validated readings.

  2. Finalise the device batch.

  3. Expose the relay through a public HTTPS URL.

  4. Update relayUrl, receiverAddress, and other settings in cre/trustflow-anchor-workflow/config.staging.json.

  5. Install the workflow dependencies:

    cd cre/trustflow-anchor-workflow
    bun install
    cd ../..
  6. From the repository root, simulate the workflow:

    cre workflow simulate ./cre/trustflow-anchor-workflow --target=staging-settings

For a broadcast simulation, provide a funded CRE_ETH_PRIVATE_KEY through the CRE-supported secret or environment configuration and enable broadcasting when prompted by the CLI.

The workflow fetches the latest finalised commitment, applies identical field-level consensus to the device ID, batch hash, reading count, and IPFS CID, creates a signed report, and submits it through TrustFlowCREReceiver.

The checked-in CRE configuration contains demonstration endpoints and addresses. Verify them before each run; public tunnel URLs and deployments may change.

Project Structure

.
├── config/
│   └── devices.json                    Local device registry
├── cre/
│   └── trustflow-anchor-workflow/      CRE workflow and target configuration
├── deployments/                        Polygon Amoy deployment metadata
├── images/                             Implementation and evaluation evidence
├── src/
│   ├── blockchain/
│   │   ├── contracts/                  TrustFlowDT and CRE receiver contracts
│   │   ├── scripts/                    Deployment and administration scripts
│   │   └── tests/                      Hardhat contract tests
│   ├── config/                         MQTT and oracle policies
│   ├── mqtt/                           Publisher and subscriber infrastructure
│   ├── relay/                          Batch relay and Pinata integration
│   ├── scripts/                        Oracle, DT, attacker, fuzz, and evaluator
│   ├── simulation/                     Generator, experiments, and injectors
│   └── validation/                     Oracle and registry synchronisation
├── .env.example
├── hardhat.config.ts
├── logbook.md
├── package.json
└── tsconfig.json

Validation Decisions

The oracle rejects a reading using one of these structured reasons:

  • unknown_device
  • device_revoked
  • device_compromised
  • invalid_signature
  • stale_timestamp
  • future_timestamp
  • replay_detected
  • out_of_range
  • rate_anomaly
  • unexpected_error

Validation is fail-closed: a reading reaches the validated MQTT namespace only after all applicable checks pass.

Current Scope and Limitations

  • Device identities and private keys are software-generated for simulation; production use would require hardware-backed key storage and attestation.
  • Relay batches are stored in memory and are lost when the relay restarts.
  • Batch finalisation is manual.
  • The frozen-sensor case is not detected by pointwise threshold and rate-of-change rules; window-based stagnation detection is future work.
  • The CRE path has been demonstrated through workflow simulation with broadcasting. It is not presented as a production DON deployment.
  • Polygon Amoy is a public test network. Testnet confirmation time is separate from the real-time MQTT validation-latency measurement.
  • The Digital Twin state model is in memory and command execution by a physical actuator is simulated rather than verified.

Licence

Academic project — all rights reserved. Not licensed for commercial use.

✕Merlin

About

Proof-of-concept: oracle-mediated pre-ingestion validation for blockchain-integrated digital twin systems. TypeScript · MQTT · Chainlink Functions · Base Sepolia · Staffordshire University FYP.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages