Search in sources :

Example 1 with SignatureVerificationException

use of com.hedera.mirror.importer.exception.SignatureVerificationException in project hedera-mirror-node by hashgraph.

the class Downloader method verifySigsAndDownloadDataFiles.

/**
 * For each group of signature files with the same file name: (1) verify that the signature files are signed by
 * corresponding node's PublicKey; (2) For valid signature files, we compare their Hashes to see if at least 1/3 of
 * hashes match. If they do, we download the corresponding data file from a node folder which has valid signature
 * file. (3) compare the hash of data file with Hash which has been agreed on by valid signatures, if match, move
 * the data file into `valid` directory; else download the data file from other valid node folder and compare the
 * hash until we find a match.
 *
 * @param sigFilesMap signature files grouped by filename
 */
private void verifySigsAndDownloadDataFiles(Multimap<String, FileStreamSignature> sigFilesMap) {
    Instant endDate = mirrorProperties.getEndDate();
    for (var sigFilenameIter = sigFilesMap.keySet().iterator(); sigFilenameIter.hasNext(); ) {
        if (ShutdownHelper.isStopping()) {
            return;
        }
        Instant startTime = Instant.now();
        String sigFilename = sigFilenameIter.next();
        Collection<FileStreamSignature> signatures = sigFilesMap.get(sigFilename);
        boolean valid = false;
        try {
            nodeSignatureVerifier.verify(signatures);
        } catch (SignatureVerificationException ex) {
            if (sigFilenameIter.hasNext()) {
                log.warn("Signature verification failed but still have files in the batch, try to process the " + "next group: {}", ex.getMessage());
                continue;
            }
            throw ex;
        }
        for (FileStreamSignature signature : signatures) {
            if (ShutdownHelper.isStopping()) {
                return;
            }
            // Ignore signatures that didn't validate or weren't in the majority
            if (signature.getStatus() != FileStreamSignature.SignatureStatus.CONSENSUS_REACHED) {
                continue;
            }
            try {
                PendingDownload pendingDownload = downloadSignedDataFile(signature);
                if (!pendingDownload.waitForCompletion()) {
                    continue;
                }
                StreamFilename dataFilename = pendingDownload.getStreamFilename();
                StreamFileData streamFileData = new StreamFileData(dataFilename, pendingDownload.getBytes());
                T streamFile = streamFileReader.read(streamFileData);
                streamFile.setNodeAccountId(signature.getNodeAccountId());
                verify(streamFile, signature);
                if (downloaderProperties.isWriteFiles()) {
                    Utility.archiveFile(streamFile.getName(), streamFile.getBytes(), downloaderProperties.getNodeStreamPath(signature.getNodeAccountIdString()));
                }
                if (downloaderProperties.isWriteSignatures()) {
                    signatures.forEach(s -> {
                        Path destination = downloaderProperties.getNodeStreamPath(s.getNodeAccountIdString());
                        Utility.archiveFile(s.getFilename(), s.getBytes(), destination);
                    });
                }
                if (!downloaderProperties.isPersistBytes()) {
                    streamFile.setBytes(null);
                }
                if (dataFilename.getInstant().isAfter(endDate)) {
                    downloaderProperties.setEnabled(false);
                    log.warn("Disabled polling after downloading all files <= endDate ({})", endDate);
                    return;
                }
                onVerified(pendingDownload, streamFile);
                valid = true;
                break;
            } catch (HashMismatchException e) {
                log.warn("Failed to verify data file from node {} corresponding to {}. Will retry another node", signature.getNodeAccountIdString(), sigFilename, e);
            } catch (InterruptedException e) {
                log.warn("Failed to download data file from node {} corresponding to {}", signature.getNodeAccountIdString(), sigFilename, e);
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                log.error("Error downloading data file from node {} corresponding to {}. Will retry another node", signature.getNodeAccountIdString(), sigFilename, e);
            }
        }
        if (!valid) {
            log.error("None of the data files could be verified, signatures: {}", signatures);
        }
        streamVerificationMetric.tag("success", String.valueOf(valid)).register(meterRegistry).record(Duration.between(startTime, Instant.now()));
    }
}
Also used : Path(java.nio.file.Path) StreamFilename(com.hedera.mirror.importer.domain.StreamFilename) Instant(java.time.Instant) FileStreamSignature(com.hedera.mirror.importer.domain.FileStreamSignature) HashMismatchException(com.hedera.mirror.importer.exception.HashMismatchException) HashMismatchException(com.hedera.mirror.importer.exception.HashMismatchException) InvalidStreamFileException(com.hedera.mirror.importer.exception.InvalidStreamFileException) SignatureVerificationException(com.hedera.mirror.importer.exception.SignatureVerificationException) ExecutionException(java.util.concurrent.ExecutionException) StreamFileData(com.hedera.mirror.importer.domain.StreamFileData) SignatureVerificationException(com.hedera.mirror.importer.exception.SignatureVerificationException)

Example 2 with SignatureVerificationException

use of com.hedera.mirror.importer.exception.SignatureVerificationException in project hedera-mirror-node by hashgraph.

the class Downloader method downloadNextBatch.

protected void downloadNextBatch() {
    if (!downloaderProperties.isEnabled()) {
        return;
    }
    if (ShutdownHelper.isStopping()) {
        return;
    }
    try {
        AddressBook addressBook = addressBookService.getCurrent();
        var sigFilesMap = downloadAndParseSigFiles(addressBook);
        // Following is a cost optimization to not unnecessarily list the public demo bucket once complete
        if (sigFilesMap.isEmpty() && mirrorProperties.getNetwork() == MirrorProperties.HederaNetwork.DEMO) {
            downloaderProperties.setEnabled(false);
            log.warn("Disabled polling after downloading all files in demo bucket");
        }
        // Verify signature files and download corresponding files of valid signature files
        verifySigsAndDownloadDataFiles(sigFilesMap);
    } catch (SignatureVerificationException e) {
        log.warn(e.getMessage());
    } catch (InterruptedException e) {
        log.error("Error downloading files", e);
        Thread.currentThread().interrupt();
    } catch (Exception e) {
        log.error("Error downloading files", e);
    }
}
Also used : AddressBook(com.hedera.mirror.common.domain.addressbook.AddressBook) SignatureVerificationException(com.hedera.mirror.importer.exception.SignatureVerificationException) HashMismatchException(com.hedera.mirror.importer.exception.HashMismatchException) InvalidStreamFileException(com.hedera.mirror.importer.exception.InvalidStreamFileException) SignatureVerificationException(com.hedera.mirror.importer.exception.SignatureVerificationException) ExecutionException(java.util.concurrent.ExecutionException)

Example 3 with SignatureVerificationException

use of com.hedera.mirror.importer.exception.SignatureVerificationException in project hedera-mirror-node by hashgraph.

the class NodeSignatureVerifier method verify.

/**
 * Verifies that the signature files satisfy the consensus requirement:
 * <ol>
 *  <li>At least 1/3 signature files are present</li>
 *  <li>For a signature file, we validate it by checking if it's signed by corresponding node's PublicKey. For valid
 *      signature files, we compare their hashes to see if at least 1/3 have hashes that match. If a signature is
 *      valid, we put the hash in its content and its file to the map, to see if at least 1/3 valid signatures have
 *      the same hash</li>
 * </ol>
 *
 * @param signatures a list of signature files which have the same filename
 * @throws SignatureVerificationException
 */
public void verify(Collection<FileStreamSignature> signatures) throws SignatureVerificationException {
    AddressBook currentAddressBook = addressBookService.getCurrent();
    Map<String, PublicKey> nodeAccountIDPubKeyMap = currentAddressBook.getNodeAccountIDPubKeyMap();
    Multimap<String, FileStreamSignature> signatureHashMap = HashMultimap.create();
    String filename = signatures.stream().map(FileStreamSignature::getFilename).findFirst().orElse("unknown");
    int consensusCount = 0;
    long sigFileCount = signatures.size();
    long nodeCount = nodeAccountIDPubKeyMap.size();
    if (!canReachConsensus(sigFileCount, nodeCount)) {
        throw new SignatureVerificationException(String.format("Insufficient downloaded signature file count, requires at least %.03f to reach consensus, got %d" + " out of %d for file %s: %s", commonDownloaderProperties.getConsensusRatio(), sigFileCount, nodeCount, filename, statusMap(signatures, nodeAccountIDPubKeyMap)));
    }
    for (FileStreamSignature fileStreamSignature : signatures) {
        if (verifySignature(fileStreamSignature, nodeAccountIDPubKeyMap)) {
            fileStreamSignature.setStatus(SignatureStatus.VERIFIED);
            signatureHashMap.put(fileStreamSignature.getFileHashAsHex(), fileStreamSignature);
        }
    }
    if (commonDownloaderProperties.getConsensusRatio() == 0 && signatureHashMap.size() > 0) {
        log.debug("Signature file {} does not require consensus, skipping consensus check", filename);
        return;
    }
    for (String key : signatureHashMap.keySet()) {
        Collection<FileStreamSignature> validatedSignatures = signatureHashMap.get(key);
        if (canReachConsensus(validatedSignatures.size(), nodeCount)) {
            consensusCount += validatedSignatures.size();
            validatedSignatures.forEach(s -> s.setStatus(SignatureStatus.CONSENSUS_REACHED));
        }
    }
    if (consensusCount == nodeCount) {
        log.debug("Verified signature file {} reached consensus", filename);
        return;
    } else if (consensusCount > 0) {
        log.warn("Verified signature file {} reached consensus but with some errors: {}", filename, statusMap(signatures, nodeAccountIDPubKeyMap));
        return;
    }
    throw new SignatureVerificationException("Signature verification failed for file " + filename + ": " + statusMap(signatures, nodeAccountIDPubKeyMap));
}
Also used : AddressBook(com.hedera.mirror.common.domain.addressbook.AddressBook) PublicKey(java.security.PublicKey) SignatureVerificationException(com.hedera.mirror.importer.exception.SignatureVerificationException) FileStreamSignature(com.hedera.mirror.importer.domain.FileStreamSignature)

Aggregations

SignatureVerificationException (com.hedera.mirror.importer.exception.SignatureVerificationException)3 AddressBook (com.hedera.mirror.common.domain.addressbook.AddressBook)2 FileStreamSignature (com.hedera.mirror.importer.domain.FileStreamSignature)2 HashMismatchException (com.hedera.mirror.importer.exception.HashMismatchException)2 InvalidStreamFileException (com.hedera.mirror.importer.exception.InvalidStreamFileException)2 ExecutionException (java.util.concurrent.ExecutionException)2 StreamFileData (com.hedera.mirror.importer.domain.StreamFileData)1 StreamFilename (com.hedera.mirror.importer.domain.StreamFilename)1 Path (java.nio.file.Path)1 PublicKey (java.security.PublicKey)1 Instant (java.time.Instant)1