use of com.hedera.mirror.common.domain.addressbook.AddressBook in project hedera-mirror-node by hashgraph.
the class AddressBookRepositoryTest method findLatestTimestamp.
@Test
void findLatestTimestamp() {
EntityId fileId = EntityId.of(101L, EntityType.FILE);
assertThat(addressBookRepository.findLatestTimestamp(fileId.getId())).isEmpty();
domainBuilder.addressBook().customize(a -> a.fileId(EntityId.of(999L, EntityType.FILE))).persist();
assertThat(addressBookRepository.findLatestTimestamp(fileId.getId())).isEmpty();
AddressBook addressBook2 = domainBuilder.addressBook().customize(a -> a.fileId(fileId)).persist();
assertThat(addressBookRepository.findLatestTimestamp(fileId.getId())).get().isEqualTo(addressBook2.getStartConsensusTimestamp());
AddressBook addressBook3 = domainBuilder.addressBook().customize(a -> a.fileId(fileId)).persist();
assertThat(addressBookRepository.findLatestTimestamp(fileId.getId())).get().isEqualTo(addressBook3.getStartConsensusTimestamp());
}
use of com.hedera.mirror.common.domain.addressbook.AddressBook in project hedera-mirror-node by hashgraph.
the class Downloader method downloadAndParseSigFiles.
/**
* Download and parse all signature files with a timestamp later than the last valid file. Put signature files into
* a multi-map sorted and grouped by the timestamp.
*
* @param addressBook the current address book
* @return a multi-map of signature file objects from different nodes, grouped by filename
*/
private Multimap<String, FileStreamSignature> downloadAndParseSigFiles(AddressBook addressBook) throws InterruptedException {
String startAfterFilename = getStartAfterFilename();
Multimap<String, FileStreamSignature> sigFilesMap = Multimaps.synchronizedSortedSetMultimap(TreeMultimap.create());
Set<EntityId> nodeAccountIds = addressBook.getNodeSet();
List<Callable<Object>> tasks = new ArrayList<>(nodeAccountIds.size());
AtomicInteger totalDownloads = new AtomicInteger();
log.info("Downloading signature files created after file: {}", startAfterFilename);
/*
* For each node, create a thread that will make S3 ListObject requests as many times as necessary to
* start maxDownloads download operations.
*/
for (EntityId nodeAccountId : nodeAccountIds) {
tasks.add(Executors.callable(() -> {
String nodeAccountIdStr = nodeAccountId.entityIdToString();
Stopwatch stopwatch = Stopwatch.createStarted();
try {
List<S3Object> s3Objects = listFiles(startAfterFilename, nodeAccountIdStr);
List<PendingDownload> pendingDownloads = downloadSignatureFiles(nodeAccountIdStr, s3Objects);
AtomicInteger count = new AtomicInteger();
pendingDownloads.forEach(pendingDownload -> {
try {
parseSignatureFile(pendingDownload, nodeAccountId).ifPresent(fileStreamSignature -> {
sigFilesMap.put(fileStreamSignature.getFilename(), fileStreamSignature);
count.incrementAndGet();
totalDownloads.incrementAndGet();
});
} catch (InterruptedException ex) {
log.warn("Failed downloading {} in {}", pendingDownload.getS3key(), pendingDownload.getStopwatch(), ex);
Thread.currentThread().interrupt();
} catch (Exception ex) {
log.warn("Failed to parse signature file {}: {}", pendingDownload.getS3key(), ex);
}
});
if (count.get() > 0) {
log.info("Downloaded {} signatures for node {} in {}", count.get(), nodeAccountIdStr, stopwatch);
}
} catch (InterruptedException e) {
log.error("Error downloading signature files for node {} after {}", nodeAccountIdStr, stopwatch, e);
Thread.currentThread().interrupt();
} catch (Exception e) {
log.error("Error downloading signature files for node {} after {}", nodeAccountIdStr, stopwatch, e);
}
}));
}
// Wait for all tasks to complete.
// invokeAll() does return Futures, but it waits for all to complete (so they're returned in a completed state).
Stopwatch stopwatch = Stopwatch.createStarted();
signatureDownloadThreadPool.invokeAll(tasks);
if (totalDownloads.get() > 0) {
var rate = (int) (1000000.0 * totalDownloads.get() / stopwatch.elapsed(TimeUnit.MICROSECONDS));
log.info("Downloaded {} signatures in {} ({}/s)", totalDownloads, stopwatch, rate);
}
return sigFilesMap;
}
use of com.hedera.mirror.common.domain.addressbook.AddressBook 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));
}
use of com.hedera.mirror.common.domain.addressbook.AddressBook in project hedera-mirror-node by hashgraph.
the class NetworkControllerTest method nullFields.
@Test
void nullFields() {
AddressBook addressBook = addressBook();
AddressBookEntry addressBookEntry = domainBuilder.addressBookEntry().customize(a -> a.consensusTimestamp(CONSENSUS_TIMESTAMP).description(null).memo(null).nodeCertHash(null).publicKey(null).stake(null)).persist();
AddressBookQuery query = AddressBookQuery.newBuilder().setFileId(FileID.newBuilder().setFileNum(addressBook.getFileId().getEntityNum()).build()).build();
reactiveService.getNodes(Mono.just(query)).as(StepVerifier::create).thenAwait(Duration.ofMillis(50)).consumeNextWith(n -> assertThat(n).isNotNull().returns("", NodeAddress::getDescription).returns(ByteString.EMPTY, NodeAddress::getMemo).returns(addressBookEntry.getNodeAccountId(), t -> EntityId.of(n.getNodeAccountId())).returns(ByteString.EMPTY, NodeAddress::getNodeCertHash).returns(addressBookEntry.getNodeId(), NodeAddress::getNodeId).returns("", NodeAddress::getRSAPubKey).returns(0L, NodeAddress::getStake)).expectComplete().verify(Duration.ofSeconds(1L));
}
use of com.hedera.mirror.common.domain.addressbook.AddressBook in project hedera-mirror-node by hashgraph.
the class NetworkControllerTest method noLimit.
@Test
void noLimit() {
AddressBook addressBook = addressBook();
AddressBookEntry addressBookEntry1 = addressBookEntry();
AddressBookEntry addressBookEntry2 = addressBookEntry();
AddressBookQuery query = AddressBookQuery.newBuilder().setFileId(FileID.newBuilder().setFileNum(addressBook.getFileId().getEntityNum()).build()).build();
reactiveService.getNodes(Mono.just(query)).as(StepVerifier::create).thenAwait(Duration.ofMillis(50)).consumeNextWith(n -> assertEntry(addressBookEntry1, n)).consumeNextWith(n -> assertEntry(addressBookEntry2, n)).expectComplete().verify(Duration.ofSeconds(1L));
}
Aggregations