Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve KeyManager API import operation #968

Merged
merged 10 commits into from
Feb 16, 2024

Conversation

usmansaleem
Copy link
Collaborator

@usmansaleem usmansaleem commented Feb 6, 2024

PR Description

Improve KeyManager API import operation. Process new validator in parallel instead of serial stream

Fixed Issue(s)

Documentation

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.

Changelog

  • I thought about adding a changelog entry, and added one if I deemed necessary.

Testing

  • I thought about testing these changes in a realistic/non-local environment.

}
}

// process TO_BE_IMPORTED in parallel and add to validator manager. Change status to ERROR if
// any error happens.
final List<ImportKeystoreData> importedData =
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a shame we need to iterate over the list multiple times. You could probably do a zip using mapToObj and an IntStream to just over the stream once.

i.e. something like

IntStream.range(0, parsedBody.getKeystores().size())
            .mapToObj(i -> { create import keystore result; })

Comment on lines 149 to 196
final String jsonKeystoreData = parsedBody.getKeystores().get(i);
final String password = parsedBody.getPasswords().get(i);
final String pubkey = getNormalizedPubKey(jsonKeystoreData);
if (existingPubkeys.contains(pubkey)) {
// keystore already loaded
duplicateData.add(
new ImportKeystoreData(
i, pubkey, jsonKeystoreData, password, new ImportKeystoreResult(DUPLICATE)));
} else {
toBeImportedData.add(
new ImportKeystoreData(
i, pubkey, jsonKeystoreData, password, new ImportKeystoreResult(IMPORTED)));
}
}

// process TO_BE_IMPORTED in parallel and add to validator manager. Change status to ERROR if
// any error happens.
final List<ImportKeystoreData> importedData =
toBeImportedData.parallelStream()
.peek(
data -> {
try {
validatorManager.addValidator(
data.getPubKeyBytes(), data.getKeystoreJson(), data.getPassword());
} catch (final Exception e) {
data.setImportKeystoreResult(
new ImportKeystoreResult(
ImportKeystoreStatus.ERROR,
"Error importing keystore: " + e.getMessage()));
}
})
.toList();

// clean out any error results
removeSignersAndCleanupImportedKeystoreFiles(
importedData.stream()
.filter(
data -> data.getImportKeystoreResult().getStatus() == ImportKeystoreStatus.ERROR)
.map(ImportKeystoreData::getPubKey)
.toList());

// merge duplicate and imported data and sort
final List<ImportKeystoreResult> results =
Stream.concat(duplicateData.stream(), importedData.stream())
.sorted()
.map(ImportKeystoreData::getImportKeystoreResult)
.toList();

Copy link
Contributor

Choose a reason for hiding this comment

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

This block of code in the ImportKeystoresHandler.handle is getting very long and hard to read can this be broken up?

// process TO_BE_IMPORTED in parallel and add to validator manager. Change status to ERROR if
// any error happens.
final List<ImportKeystoreData> importedData =
toBeImportedData.parallelStream()
Copy link
Contributor

Choose a reason for hiding this comment

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

Will using the parallelStream impact the performance on signing requests?

import org.apache.tuweni.bytes.Bytes;

/** Keep the index of the keystore import json and password */
public class ImportKeystoreData implements Comparable<ImportKeystoreData> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be a record instead of a class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We might be able to convert this one to record as we need to modify the "ImportKeystoreResult" state to ERROR which is a member of this class.

CHANGELOG.md Outdated
@@ -4,6 +4,7 @@

### Bugs fixed
- Ensure that Web3Signer stops the http server when a sigterm is received
- Improve Key Manager API import operation to use parallel processing instead of serial processing.
Copy link
Contributor

Choose a reason for hiding this comment

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

think this is more of an improvement than a bug but you might disagree :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will move it to Improvements section

Copy link
Contributor

@jframe jframe left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@siladu siladu left a comment

Choose a reason for hiding this comment

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

Just a few minor comments, nothing blocking

.sorted()
.map(ImportKeystoreData::getImportKeystoreResult)
.toList();
// step 4: add validators to be imported in parallel stream
Copy link
Contributor

Choose a reason for hiding this comment

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

aren't we just importing here rather than "adding to be imported"?
Don't think we should mention the parallel stream implementation detail

context.fail(SERVER_ERROR, e);
}
}

private String getNormalizedPubKey(final String json) {
/**
* Import validators in parallel stream
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need javadoc for a private method? If parallel stream is important here maybe rename the method?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

you are right, good observation, lets me rename the method.

getKeystoreDataToProcess(parsedBody, activePubKeys);

// Step 3: import slashing protection data for all to-be-IMPORTED keys
final List<String> importedPubKeys = getToBeImportedPubKeys(importKeystoreDataList);
Copy link
Contributor

Choose a reason for hiding this comment

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

s/importedPubKeys/pubKeysToBeImported ?

CHANGELOG.md Outdated
@@ -4,6 +4,7 @@

### Bugs fixed
- Ensure that Web3Signer stops the http server when a sigterm is received
- Improve Key Manager API import operation to use parallel processing instead of serial processing.
Copy link
Contributor

Choose a reason for hiding this comment

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

// load existing keys
final Set<String> existingPubkeys =
// load "active" keys
final Set<String> activePubKeys =
Copy link
Contributor

Choose a reason for hiding this comment

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

Not clear to me what "active" means here and why that's significant for this code. Why is this better than "existingPubKeys"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The "active keys" term is used in the specs i.e https://github.com/ethereum/keymanager-APIs/tree/master/flows#import. For us, "active" means keys which are already loaded into memory before this API is called.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've renamed it back to prior one.

@usmansaleem usmansaleem requested a review from siladu February 15, 2024 10:55
@@ -103,26 +103,26 @@ public void handle(final RoutingContext context) {
return;
}

// load "active" keys
final Set<String> activePubKeys =
// "active" keys which are already loaded by Web3Signer before this import call.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
@usmansaleem usmansaleem enabled auto-merge (squash) February 16, 2024 04:15
@usmansaleem usmansaleem merged commit 371a5dd into Consensys:master Feb 16, 2024
7 checks passed
@usmansaleem usmansaleem deleted the import_api_fix branch March 18, 2024 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants