Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Validation_service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Validation service

The validation service for Canis Major performs security checks to verify transaction integrity between the context broker and Canis Major blockchain transactions.

![Validation service](validation_service.png)


## Service Setup
The validation service implements a three-step verification protocol.

### Initial Validation Request
The client initiates the verification process by submitting a POST request to the Canis Major Validator service.
Comment thread
flopezag marked this conversation as resolved.
Outdated

### Dual-Source Data Retrieval
The Canis Major Validator executes two GET requests, based on the `entityId`, to:
- Canis Major
- The Context Broker

Both requests retrieve the entity information using the specified entity identifier as the query parameter.

### Data Integrity Analysis
The validator performs a systematic comparison of the entity representations obtained from both sources.
65 changes: 65 additions & 0 deletions Validation_service/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>validator</groupId>
<artifactId>validation-service</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<java.version>17</java.version> <!-- Specify your Java version -->
<jakarta.version>2.0.0</jakarta.version> <!-- Adjust as necessary -->
<jackson.version>2.13.0</jackson.version> <!-- Adjust as necessary -->
<slf4j.version>1.7.32</slf4j.version> <!-- Adjust as necessary -->
</properties>

<dependencies>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<version>3.5.0</version> <!-- Adjust as necessary -->
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<version>3.5.0</version> <!-- Adjust as necessary -->
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Comment thread
flopezag marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package validator.client;

// Import statements:
import jakarta.inject.Singleton; // For dependency injection, marks class as singleton
import java.net.URI; // For handling URIs
import java.net.http.HttpClient; // Core class for making HTTP requests
import java.net.http.HttpRequest; // For building HTTP requests
import java.net.http.HttpResponse; // For handling HTTP responses


// Marks this class as a singleton - only one instance will exist in the application
@Singleton
public class ValidationHttpClient {
// Creates a single HttpClient instance to be reused for all requests
private final HttpClient client = HttpClient.newHttpClient();

// Method to fetch data from Orion Context Broker
public String fetchOrionData(String entityId) throws Exception {
// Builds an HTTP request:
HttpRequest request = HttpRequest.newBuilder()
// Sets the URI for the request, querying for DLTtxReceipt entities with matching refEntity
.uri(new URI("http://localhost:1026/ngsi-ld/v1/entities/?type=DLTtxReceipt&q=refEntity==" + entityId))
// Adds Link header for JSON-LD context
.header("Link", "<https://raw.githubusercontent.com/smart-data-models/dataModel.DistributedLedgerTech/master/context.jsonld>; rel=\"http://www.w3.org/ns/json-ld#context\"; type=\"application/ld+json\"")
// Sets the NGSILD-Tenant header to "orion"
.header("NGSILD-Tenant", "orion")
Comment thread
flopezag marked this conversation as resolved.
Outdated
// Added Accept header for JSON format
.header("Accept", "application/json")
// Specifies this is a GET request
.GET()
// Finalizes the request building
.build();

// Sends the request and returns the response body as a String
return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
}

// Method to fetch data from a blockchain service
public String fetchBlockchainData(String entityId) throws Exception {
// Builds an HTTP request:
HttpRequest request = HttpRequest.newBuilder()
// Sets the URI for the blockchain service endpoint
.uri(new URI("http://localhost:4000/entity/" + entityId))
// Sets Accept header to receive JSONresponse
.header("Accept", "application/json")
// Specifies this is a GET request
.GET()
// Finalizes the request building
.build();

// Sends the request and returns the response body as a String
return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
}
}
Comment thread
flopezag marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package validator.controller;

import io.micronaut.http.annotation.*;
import io.micronaut.http.HttpResponse;
import validator.service.ValidationService;
import validator.model.ValidationResult;

@Controller("/service/v1/validation")
public class ValidationController {
private final ValidationService validationService;

public ValidationController(ValidationService validationService) {
this.validationService = validationService;
}

@Post("/compare")
public HttpResponse<ValidationResult> compareResponses(@Body CompareRequest request) {
// Call the validation service to perform the validation
ValidationResult result = validationService.validateEntity(request.getEntityId());
return HttpResponse.ok(result);
Comment thread
flopezag marked this conversation as resolved.
Outdated
}

// Inner class to represent the request body
public static class CompareRequest {
private String entityId;

// Getter and Setter
public String getEntityId() {
return entityId;
}

public void setEntityId(String entityId) {
this.entityId = entityId;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package validator.model;

import java.util.List;

//import java.util.List;

/*public record ValidationResult(boolean isValid, List<String> errors) {
public String getErrorMessage() {
return String.join(", ", errors);
}
} */

public class ValidationResult {
private final boolean valid;
private final List<String> messages;


public ValidationResult(boolean valid, List<String> messages){
this.valid = valid;
this.messages = messages;
}

public boolean isValid() { //getter
return valid;
}

public List<String> getMessages() {
return messages;
}
}
Comment thread
flopezag marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
This implementation of the validation service:
- Compares all relevant fields between Orion and blockchain responses
- Provides detailed validation errors for each mismatched field
- Handles JSON parsing and null checks
- Uses logging for error tracking
- Returns structured validation results
*/

package validator.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import validator.model.ValidationResult;
import jakarta.inject.Singleton;
import validator.client.ValidationHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;

@Singleton
public class ValidationService {
private static final Logger logger = LoggerFactory.getLogger(ValidationService.class);
private final ValidationHttpClient validationHttpClient;
private final ObjectMapper mapper = new ObjectMapper();

public ValidationService(ValidationHttpClient validationHttpClient) {
this.validationHttpClient = validationHttpClient;
}

public ValidationResult validateEntity(String entityId) {
try {
String orionData = validationHttpClient.fetchOrionData(entityId);
String blockchainData = validationHttpClient.fetchBlockchainData(entityId);

if (orionData == null || orionData.isEmpty()) {
logger.error("No data found from Orion for entityId: {}", entityId);
return new ValidationResult(false, List.of("No data found from Orion"));
}

if (blockchainData == null || blockchainData.isEmpty()) {
logger.error("No data found from Canis Major for entityId: {}", entityId);
return new ValidationResult(false, List.of("No data found from Canis Major"));
}

List<String> validationErrors = compareData(orionData, blockchainData);
return new ValidationResult(validationErrors.isEmpty(), validationErrors);
} catch (Exception e) {
logger.error("Error validating entity: {}", entityId, e);
return new ValidationResult(false, List.of("Validation error: " + e.getMessage()));
}
}

private List<String> compareData(String orionData, String blockchainData) {
List<String> errors = new ArrayList<>();
try {
JsonNode orionNode = mapper.readTree(orionData);
JsonNode blockchainNode = mapper.readTree(blockchainData);

JsonNode txReceipts = orionNode.get("TxReceipts").get("value");

validateField(txReceipts, blockchainNode, "status", "Status", errors);
validateField(txReceipts, blockchainNode, "blockNumber", "Block Number", errors);
validateField(txReceipts, blockchainNode, "transactionHash", "Transaction Hash", errors);
validateField(txReceipts, blockchainNode, "blockHash", "Block Hash", errors);
validateField(txReceipts, blockchainNode, "from", "From Address", errors);
validateField(txReceipts, blockchainNode, "to", "To Address", errors);

return errors;
} catch (JsonProcessingException e) {
logger.error("Error parsing JSON data", e);
errors.add("Error parsing JSON data: " + e.getMessage());
return errors;
}
}

private void validateField(JsonNode orion, JsonNode blockchain, String fieldName, String displayName, List<String> errors) {
JsonNode orionValue = orion.get(fieldName);
JsonNode blockchainValue = blockchain.get(fieldName);

if (orionValue == null || blockchainValue == null) {
errors.add(displayName + " field missing in one or both responses");
return;
}

if (!orionValue.equals(blockchainValue)) {
errors.add(displayName + " mismatch: Orion=" + orionValue + ", Blockchain=" + blockchainValue);
}
}
}

Comment thread
flopezag marked this conversation as resolved.
Outdated
Binary file added Validation_service/validation_service.png

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is missing in this diagram the message 4) with the communication of the results.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the diagram you sent me in the chat. I can make a new diagram and add this step

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions it/docker-compose/clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#Stop and remove all Docker containers:
Comment thread
flopezag marked this conversation as resolved.
Outdated
docker stop $(sudo docker ps -aq)
docker rm $(sudo docker ps -aq)
docker container prune

#Remove all created volumes:
docker volume rm $(sudo docker volume ls -q)
docker volume prune

#Remove all Docker custom networks:
docker network prune -f

Empty file added it/src/test/java/it/README.MD
Empty file.