Skip to content

Commit

Permalink
feat: keri voting support (#555)
Browse files Browse the repository at this point in the history
* feat: keri voting support

* fix: fixes more tests.

* fix: leaderboard fixes.

* fix: account endpoint fixes.

* fix: KERI tests fix

* fix: KERI tests fix

* feat: keri wallet support in vote commitment service.

* fix: keri web 3 filter added to spring security

* chore(ledger-follower): add flyway-database-postgresql depend

* chore(ledger-follower): remove unneeded flyway plugin

* refactor: changing http header names

* fix: removal of lombok getter and setter annotation.

---------

Co-authored-by: Mateusz Czeladka <[email protected]>
Co-authored-by: Roberto C. Morano <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2024
1 parent 68126c0 commit eb3d576
Show file tree
Hide file tree
Showing 122 changed files with 4,520 additions and 1,637 deletions.
20 changes: 10 additions & 10 deletions backend-services/user-verification-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
java
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.3"
id("org.graalvm.buildtools.native") version "0.9.27"
id("org.graalvm.buildtools.native") version "0.9.27"
id("org.flywaydb.flyway") version "9.22.1"
id("cz.habarta.typescript-generator") version "3.2.1263"
id("com.github.ben-manes.versions") version "0.48.0"
Expand Down Expand Up @@ -36,8 +36,8 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
testImplementation("io.rest-assured:rest-assured:5.3.2")
testImplementation("org.wiremock:wiremock-standalone:3.3.1")
testImplementation("io.rest-assured:rest-assured:5.4.0")
testImplementation("org.wiremock:wiremock-standalone:3.9.1")

testCompileOnly("org.springframework.boot:spring-boot-starter-test")
implementation("org.springframework.boot:spring-boot-starter-actuator")
Expand All @@ -53,22 +53,22 @@ dependencies {

implementation("org.zalando:problem-spring-web-starter:0.29.1")

compileOnly("org.projectlombok:lombok:1.18.30")
annotationProcessor("org.projectlombok:lombok:1.18.30")
compileOnly("org.projectlombok:lombok:1.18.34")
annotationProcessor("org.projectlombok:lombok:1.18.34")

testCompileOnly("org.projectlombok:lombok:1.18.30")
testAnnotationProcessor("org.projectlombok:lombok:1.18.30")
testCompileOnly("org.projectlombok:lombok:1.18.34")
testAnnotationProcessor("org.projectlombok:lombok:1.18.34")

implementation("org.flywaydb:flyway-core")

implementation("com.googlecode.libphonenumber:libphonenumber:8.13.21")
implementation("com.googlecode.libphonenumber:libphonenumber:8.13.42")

implementation("com.querydsl:querydsl-jpa")
annotationProcessor("com.querydsl:querydsl-apt")

implementation("com.bloxbean.cardano:cardano-client-address:0.5.0")
implementation("com.bloxbean.cardano:cardano-client-address:0.5.1")

implementation("software.amazon.awssdk:sns:2.20.153")
implementation("software.amazon.awssdk:sns:2.26.25")

implementation("io.vavr:vavr:0.10.4")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.cardano.foundation.voting.client;

import io.vavr.control.Either;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.val;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
Expand All @@ -17,44 +17,49 @@
import java.util.HashMap;
import java.util.Map;

import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpStatus.BAD_REQUEST;

@Component
@Slf4j
@RequiredArgsConstructor
public class KeriVerificationClient {

@Autowired
private RestTemplate restTemplate;
private final RestTemplate restTemplate;

@Value("${keri.ballot.verifier.base.url}")
private String keriVerifierBaseUrl;

public Either<Problem, Boolean> verifySignature(String aid, String signature, String payload) {
String url = String.format("%s/verify", keriVerifierBaseUrl);
public Either<Problem, Boolean> verifySignature(String aid,
String signature,
String payload) {
val url = String.format("%s/verify", keriVerifierBaseUrl);

HttpHeaders headers = new HttpHeaders();
val headers = new HttpHeaders();
headers.add("Content-Type", "application/json");

Map<String, String> requestBody = new HashMap<>();
val requestBody = new HashMap<String, String>();
requestBody.put("pre", aid);
requestBody.put("signature", signature);
requestBody.put("payload", payload);

HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody, headers);
val entity = new HttpEntity<Map<String, String>>(requestBody, headers);

try {
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
val response = restTemplate.exchange(url, POST, entity, String.class);

if (response.getStatusCode().is2xxSuccessful()) {
return Either.right(true);
} else {
return Either.left(Problem.builder()
.withTitle("KERI_VERIFICATION_FAILED")
.withDetail("The Keri-specific condition was not met.")
.withStatus(new HttpStatusAdapter(response.getStatusCode()))
.build());
}

return Either.left(Problem.builder()
.withTitle("KERI_VERIFICATION_FAILED")
.withDetail("The Keri-specific condition was not met.")
.withStatus(new HttpStatusAdapter(response.getStatusCode()))
.build());
} catch (HttpClientErrorException e) {
log.error("Unable to verify signature, reason: {}", e.getMessage());

return Either.left(Problem.builder()
.withTitle("KERI_VERIFICATION_ERROR")
Expand All @@ -65,28 +70,27 @@ public Either<Problem, Boolean> verifySignature(String aid, String signature, St
}

public Either<Problem, Boolean> registerOOBI(String oobi) {
String url = String.format("%s/oobi", keriVerifierBaseUrl);
val url = String.format("%s/oobi", keriVerifierBaseUrl);

HttpHeaders headers = new HttpHeaders();
val headers = new HttpHeaders();
headers.add("Content-Type", "application/json");

Map<String, String> requestBody = new HashMap<>();
val requestBody = new HashMap<String, String>();
requestBody.put("oobi", oobi);

HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody, headers);

val entity = new HttpEntity<Map<String, String>>(requestBody, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
val response = restTemplate.exchange(url, POST, entity, String.class);

if (response.getStatusCode().is2xxSuccessful()) {
return Either.right(true);
} else {
return Either.left(Problem.builder()
.withTitle("OOBI_REGISTRATION_FAILED")
.withDetail("Failed to register OOBI.")
.withStatus(new HttpStatusAdapter(response.getStatusCode()))
.build());
}

return Either.left(Problem.builder()
.withTitle("OOBI_REGISTRATION_FAILED")
.withDetail("Failed to register OOBI.")
.withStatus(new HttpStatusAdapter(response.getStatusCode()))
.build());
} catch (HttpClientErrorException e) {
return Either.left(Problem.builder()
.withTitle("OOBI_REGISTRATION_ERROR")
Expand All @@ -97,19 +101,19 @@ public Either<Problem, Boolean> registerOOBI(String oobi) {
}

public Either<Problem, String> getOOBI(String oobi, Integer maxAttempts) {
String url = String.format("%s/oobi?url=%s", keriVerifierBaseUrl, oobi);
val url = String.format("%s/oobi?url=%s", keriVerifierBaseUrl, oobi);

HttpHeaders headers = new HttpHeaders();
val headers = new HttpHeaders();
headers.add("Content-Type", "application/json");

HttpEntity<Void> entity = new HttpEntity<>(headers);
val entity = new HttpEntity<Void>(headers);

int attempts = (maxAttempts == null) ? 1 : maxAttempts;
int attempt = 0;

while (attempt < attempts) {
try {
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
val response = restTemplate.exchange(url, GET, entity, String.class);

if (response.getStatusCode().is2xxSuccessful()) {
return Either.right(response.getBody());
Expand Down Expand Up @@ -145,4 +149,5 @@ public Either<Problem, String> getOOBI(String oobi, Integer maxAttempts) {
.withStatus(new HttpStatusAdapter(BAD_REQUEST))
.build());
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.cardano.foundation.voting.config;

import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.CardanoNetwork;
import org.cardano.foundation.voting.domain.ChainNetwork;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -11,7 +11,7 @@
public class CardanoConfig {

@Bean
public CardanoNetwork network(@Value("${cardano.network:main}") CardanoNetwork network) {
public ChainNetwork network(@Value("${cardano.network:main}") ChainNetwork network) {
log.info("Configured backend network:{}", network);

return network;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import java.util.Arrays;
import java.util.List;

public enum CardanoNetwork {
public enum ChainNetwork {

MAIN, // main-net
PREPROD, // preprod-net
PREVIEW, // preview-net
DEV; // e.g. local Yaci-Dev net

public static List<String> supportedNetworks() {
return Arrays.stream(CardanoNetwork.values()).map(network -> network.name().toLowerCase()).toList();
return Arrays.stream(ChainNetwork.values()).map(network -> network.name().toLowerCase()).toList();
}

public boolean isTestnet() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cardano.foundation.voting.domain;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.*;

@Getter
Expand All @@ -13,6 +14,9 @@ public class IsVerifiedRequest {
@NotBlank
private String eventId;

@NotNull
private WalletType walletType;

@NotBlank
private String walletId;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.cardano.foundation.voting.domain;

public enum VerificationStatus {

PENDING,
VERIFIED,

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.cardano.foundation.voting.domain;

public enum WalletType {
CARDANO, KERI
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.cardano.foundation.voting.domain.discord;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.cardano.foundation.voting.domain.WalletType;

import java.util.Optional;
import org.cardano.foundation.voting.utils.WalletType;

@Getter
@Builder
Expand All @@ -21,23 +22,20 @@ public class DiscordCheckVerificationRequest {
@NotBlank
private String walletId;

@Builder.Default
private Optional<WalletType> walletType = Optional.of(WalletType.CARDANO);
@NotNull
private WalletType walletType;

@NotBlank
private String secret;

@Builder.Default
protected Optional<String> coseSignature = Optional.empty();

@Builder.Default
protected Optional<String> cosePublicKey = Optional.empty();
protected Optional<String> signature = Optional.empty();

@Builder.Default
protected Optional<String> keriSignedMessage = Optional.empty();
protected Optional<String> payload = Optional.empty();

@Builder.Default
protected Optional<String> keriPayload = Optional.empty();
protected Optional<String> publicKey = Optional.empty(); // StakeAddress (Cardano) or AID (KERI)

@Builder.Default
protected Optional<String> oobi = Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.VerificationStatus;
import org.cardano.foundation.voting.utils.WalletType;
import org.cardano.foundation.voting.domain.WalletType;

import javax.annotation.Nullable;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -38,8 +38,9 @@ public class DiscordUserVerification extends AbstractTimestampEntity {
private String walletId;

@Column(name = "wallet_type")
@Nullable
@Enumerated(EnumType.STRING)
@Getter
@Setter
private WalletType walletType;

@Column(name = "secret_code", nullable = false)
Expand All @@ -59,22 +60,6 @@ public class DiscordUserVerification extends AbstractTimestampEntity {
@Setter
private LocalDateTime expiresAt;

public Optional<String> getWalletId() {
return Optional.ofNullable(walletId);
}

public Optional<WalletType> getWalletType() {
return Optional.ofNullable(walletType);
}

public void setWalletId(Optional<String> walletId) {
this.walletId = walletId.orElse(null);
}

public void setWalletType(Optional<WalletType> walletType) {
this.walletType = walletType.orElse(null);
}

@Override
public String toString() {
return "DiscordUserVerification{" +
Expand All @@ -90,4 +75,12 @@ public String toString() {
'}';
}

public Optional<String> getWalletId() {
return Optional.ofNullable(walletId);
}

public void setWalletId(Optional<String> walletId) {
this.walletId = walletId.orElse(null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.VerificationStatus;
import org.cardano.foundation.voting.utils.WalletType;
import org.cardano.foundation.voting.domain.WalletType;

import java.time.LocalDateTime;

Expand Down
Loading

0 comments on commit eb3d576

Please sign in to comment.