All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quorum.tessera.p2p.TransactionResource Maven / Gradle / Ivy

package com.quorum.tessera.p2p;

import static jakarta.ws.rs.core.MediaType.*;
import static java.util.Collections.emptyList;

import com.quorum.tessera.base64.Base64Codec;
import com.quorum.tessera.data.MessageHash;
import com.quorum.tessera.enclave.EncodedPayloadCodec;
import com.quorum.tessera.enclave.PayloadEncoder;
import com.quorum.tessera.encryption.PublicKey;
import com.quorum.tessera.p2p.recovery.ResendBatchRequest;
import com.quorum.tessera.p2p.resend.ResendRequest;
import com.quorum.tessera.recovery.resend.ResendBatchResponse;
import com.quorum.tessera.recovery.workflow.BatchResendManager;
import com.quorum.tessera.recovery.workflow.LegacyResendManager;
import com.quorum.tessera.shared.Constants;
import com.quorum.tessera.transaction.TransactionManager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
import java.util.*;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides endpoints for dealing with transactions, including:
 *
 * 

- creating new transactions and distributing them - deleting transactions - fetching * transactions - resending old transactions */ @Tag(name = "peer-to-peer") @Path("/") public class TransactionResource { private static final Logger LOGGER = LoggerFactory.getLogger(TransactionResource.class); private final TransactionManager transactionManager; private final BatchResendManager batchResendManager; private final LegacyResendManager legacyResendManager; public TransactionResource( final TransactionManager transactionManager, final BatchResendManager batchResendManager, final LegacyResendManager legacyResendManager) { this.transactionManager = Objects.requireNonNull(transactionManager); this.batchResendManager = Objects.requireNonNull(batchResendManager); this.legacyResendManager = Objects.requireNonNull(legacyResendManager); } @Operation( summary = "/resend", operationId = "requestPayloadResend", description = "initiate resend of either an INDIVIDUAL transaction or ALL transactions involving a given public key") @ApiResponse( responseCode = "200", description = "resent payload", content = @Content( array = @ArraySchema( schema = @Schema( description = "empty if request was for ALL; else the encoded INDIVIDUAL transaction", type = "string", format = "byte")))) @POST @Path("resend") @Consumes(APPLICATION_JSON) @Produces(TEXT_PLAIN) public Response resend(@Valid @NotNull final ResendRequest resendRequest) { LOGGER.debug("Received resend request"); final PayloadEncoder payloadEncoder = PayloadEncoder.create(EncodedPayloadCodec.LEGACY); final PublicKey recipient = Optional.of(resendRequest) .map(ResendRequest::getPublicKey) .map(Base64Codec.create()::decode) .map(PublicKey::from) .get(); final MessageHash transactionHash = Optional.of(resendRequest) .map(ResendRequest::getKey) .map(Base64.getDecoder()::decode) .map(MessageHash::new) .orElse(null); final com.quorum.tessera.recovery.resend.ResendRequest request = com.quorum.tessera.recovery.resend.ResendRequest.Builder.create() .withType( com.quorum.tessera.recovery.resend.ResendRequest.ResendRequestType.valueOf( resendRequest.getType())) .withRecipient(recipient) .withHash(transactionHash) .build(); final com.quorum.tessera.recovery.resend.ResendResponse response = legacyResendManager.resend(request); final Response.ResponseBuilder builder = Response.ok(); Optional.ofNullable(response.getPayload()) .map(payloadEncoder::encode) .ifPresent(builder::entity); return builder.build(); } @Operation( summary = "/resendBatch", operationId = "requestPayloadBatchResend", description = "initiate resend of all transactions for a given public key in batches") @ApiResponse( responseCode = "200", description = "count of total transactions being resent", content = @Content( schema = @Schema( implementation = com.quorum.tessera.p2p.recovery.ResendBatchResponse.class))) @POST @Path("resendBatch") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) public Response resendBatch(@Valid @NotNull final ResendBatchRequest resendBatchRequest) { LOGGER.debug("Received resend request"); final com.quorum.tessera.recovery.resend.ResendBatchRequest request = com.quorum.tessera.recovery.resend.ResendBatchRequest.Builder.create() .withPublicKey(resendBatchRequest.getPublicKey()) .withBatchSize(resendBatchRequest.getBatchSize()) .build(); final ResendBatchResponse response = batchResendManager.resendBatch(request); final com.quorum.tessera.p2p.recovery.ResendBatchResponse responseEntity = new com.quorum.tessera.p2p.recovery.ResendBatchResponse(); responseEntity.setTotal(response.getTotal()); final Response.ResponseBuilder builder = Response.status(Response.Status.OK); builder.entity(responseEntity); return builder.build(); } // path push is overloaded (RecoveryResource & TransactionResource); swagger cannot handle // situations like this so this operation documents both @Operation( summary = "/push", operationId = "pushPayload", description = "store encoded payload to the server's database") @ApiResponse( responseCode = "201", description = "hash of encoded payload", content = @Content( mediaType = TEXT_PLAIN, schema = @Schema( description = "hash of encrypted payload", type = "string", format = "base64"))) @ApiResponse( responseCode = "403", description = "server is in recovery mode and encoded payload is not a Standard Private transaction") @POST @Path("push") @Consumes(APPLICATION_OCTET_STREAM) public Response push( @Schema(description = "encoded payload") final byte[] payload, @HeaderParam(Constants.API_VERSION_HEADER) @Parameter( description = "client's supported API versions", array = @ArraySchema(schema = @Schema(type = "string"))) final List headers) { LOGGER.debug("Received push request"); final Set versions = Optional.ofNullable(headers).orElse(emptyList()).stream() .filter(Objects::nonNull) .flatMap(v -> Arrays.stream(v.split(","))) .collect(Collectors.toSet()); final EncodedPayloadCodec codec = EncodedPayloadCodec.getPreferredCodec(versions); final PayloadEncoder payloadEncoder = PayloadEncoder.create(codec); final MessageHash messageHash = transactionManager.storePayload(payloadEncoder.decode(payload)); LOGGER.debug("Push request generated hash {}", messageHash); return Response.status(Response.Status.CREATED).entity(Objects.toString(messageHash)).build(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy