Skip to content

Commit

Permalink
Merge pull request #5047 from chimp1984/optimize-signed-witness-domain
Browse files Browse the repository at this point in the history
Add cache for signature verification results and a lookup map by ownerPubKey
  • Loading branch information
sqrrm authored Jan 3, 2021
2 parents d79799a + 0ce9324 commit f6efded
Showing 1 changed file with 46 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ public class SignedWitnessService {
// This avoids iterations over the signedWitnessMap for getting the set of such SignedWitnesses.
private final Map<P2PDataStorage.ByteArray, Set<SignedWitness>> signedWitnessSetByAccountAgeWitnessHash = new HashMap<>();

// Iterating over all SignedWitnesses and do a byte array comparison is a bit expensive and
// it is called at filtering the offer book many times, so we use a lookup map for fast
// access to the set of SignedWitness which match the ownerPubKey.
private final Map<P2PDataStorage.ByteArray, Set<SignedWitness>> signedWitnessSetByOwnerPubKey = new HashMap<>();

// The signature verification calls are rather expensive and called at filtering the offer book many times,
// so we cache the results using the hash as key. The hash is created from the accountAgeWitnessHash and the
// signature.
private final Map<P2PDataStorage.ByteArray, Boolean> verifySignatureWithDSAKeyResultCache = new HashMap<>();
private final Map<P2PDataStorage.ByteArray, Boolean> verifySignatureWithECKeyResultCache = new HashMap<>();


///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
Expand Down Expand Up @@ -322,32 +333,45 @@ public boolean verifySignature(SignedWitness signedWitness) {
}

private boolean verifySignatureWithECKey(SignedWitness signedWitness) {
P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(signedWitness.getHash());
if (verifySignatureWithECKeyResultCache.containsKey(hash)) {
return verifySignatureWithECKeyResultCache.get(hash);
}
try {
String message = Utilities.encodeToHex(signedWitness.getAccountAgeWitnessHash());
String signatureBase64 = new String(signedWitness.getSignature(), Charsets.UTF_8);
ECKey key = ECKey.fromPublicOnly(signedWitness.getSignerPubKey());
if (arbitratorManager.isPublicKeyInList(Utilities.encodeToHex(key.getPubKey()))) {
key.verifyMessage(message, signatureBase64);
verifySignatureWithECKeyResultCache.put(hash, true);
return true;
} else {
log.warn("Provided EC key is not in list of valid arbitrators.");
verifySignatureWithECKeyResultCache.put(hash, false);
return false;
}
} catch (SignatureException e) {
log.warn("verifySignature signedWitness failed. signedWitness={}", signedWitness);
log.warn("Caused by ", e);
verifySignatureWithECKeyResultCache.put(hash, false);
return false;
}
}

private boolean verifySignatureWithDSAKey(SignedWitness signedWitness) {
P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(signedWitness.getHash());
if (verifySignatureWithDSAKeyResultCache.containsKey(hash)) {
return verifySignatureWithDSAKeyResultCache.get(hash);
}
try {
PublicKey signaturePubKey = Sig.getPublicKeyFromBytes(signedWitness.getSignerPubKey());
Sig.verify(signaturePubKey, signedWitness.getAccountAgeWitnessHash(), signedWitness.getSignature());
verifySignatureWithDSAKeyResultCache.put(hash, true);
return true;
} catch (CryptoException e) {
log.warn("verifySignature signedWitness failed. signedWitness={}", signedWitness);
log.warn("Caused by ", e);
verifySignatureWithDSAKeyResultCache.put(hash, false);
return false;
}
}
Expand Down Expand Up @@ -393,10 +417,15 @@ public Set<SignedWitness> getUnsignedSignerPubKeys() {
// witnessOwnerPubKey
private Set<SignedWitness> getSignedWitnessSetByOwnerPubKey(byte[] ownerPubKey,
Stack<P2PDataStorage.ByteArray> excluded) {
return getSignedWitnessMapValues().stream()
.filter(e -> Arrays.equals(e.getWitnessOwnerPubKey(), ownerPubKey))
.filter(e -> !excluded.contains(new P2PDataStorage.ByteArray(e.getSignerPubKey())))
.collect(Collectors.toSet());
P2PDataStorage.ByteArray key = new P2PDataStorage.ByteArray(ownerPubKey);
if (signedWitnessSetByOwnerPubKey.containsKey(key)) {
return signedWitnessSetByOwnerPubKey.get(key).stream()
.filter(e -> !excluded.contains(new P2PDataStorage.ByteArray(e.getSignerPubKey())))
.collect(Collectors.toSet());

} else {
return new HashSet<>();
}
}

public boolean isSignedAccountAgeWitness(AccountAgeWitness accountAgeWitness) {
Expand Down Expand Up @@ -498,6 +527,10 @@ public void addToMap(SignedWitness signedWitness) {
P2PDataStorage.ByteArray accountAgeWitnessHash = new P2PDataStorage.ByteArray(signedWitness.getAccountAgeWitnessHash());
signedWitnessSetByAccountAgeWitnessHash.putIfAbsent(accountAgeWitnessHash, new HashSet<>());
signedWitnessSetByAccountAgeWitnessHash.get(accountAgeWitnessHash).add(signedWitness);

P2PDataStorage.ByteArray ownerPubKey = new P2PDataStorage.ByteArray(signedWitness.getWitnessOwnerPubKey());
signedWitnessSetByOwnerPubKey.putIfAbsent(ownerPubKey, new HashSet<>());
signedWitnessSetByOwnerPubKey.get(ownerPubKey).add(signedWitness);
}

private void publishSignedWitness(SignedWitness signedWitness) {
Expand Down Expand Up @@ -526,6 +559,15 @@ public void removeSignedWitness(SignedWitness signedWitness) {
signedWitnessSetByAccountAgeWitnessHash.remove(accountAgeWitnessHash);
}
}

P2PDataStorage.ByteArray ownerPubKey = new P2PDataStorage.ByteArray(signedWitness.getWitnessOwnerPubKey());
if (signedWitnessSetByOwnerPubKey.containsKey(ownerPubKey)) {
Set<SignedWitness> set = signedWitnessSetByOwnerPubKey.get(ownerPubKey);
set.remove(signedWitness);
if (set.isEmpty()) {
signedWitnessSetByOwnerPubKey.remove(ownerPubKey);
}
}
}

// Remove SignedWitnesses that are signed by TRADE that also have an ARBITRATOR signature
Expand Down

0 comments on commit f6efded

Please sign in to comment.