Inline predicates to simplify HashCashService & FilterManager

Remove all the 'challengeValidation', 'difficultyValidation' and
'testDifficulty' BiPredicate method params from 'HashCashService' &
'ProofOfWorkService', to simplify the API. These were originally
included to aid testing, but turned out to be unnecessary.

Patches committed on behalf of @chimp1984.
This commit is contained in:
Steven Barclay 2021-11-27 05:59:41 +00:00 committed by Christoph Atteneder
parent 772cd74ab2
commit 92209c3ed7
No known key found for this signature in database
GPG Key ID: CD5DC1C529CDFD3B
4 changed files with 21 additions and 109 deletions

View File

@ -22,24 +22,18 @@ import com.google.common.primitives.Longs;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import lombok.extern.slf4j.Slf4j;
/**
* HashCash implementation for proof of work
* It doubles required work by difficulty increase (adding one leading zero).
* It doubles required work by log2Difficulty increase (adding one leading zero).
*
* See https://www.hashcash.org/papers/hashcash.pdf
*/
@Slf4j
public class HashCashService extends ProofOfWorkService {
// Default validations. Custom implementations might use tolerance.
private static final BiPredicate<byte[], byte[]> isChallengeValid = Arrays::equals;
private static final BiPredicate<Integer, Integer> isDifficultyValid = Integer::equals;
HashCashService() {
super(0);
}
@ -50,105 +44,35 @@ public class HashCashService extends ProofOfWorkService {
return mint(payload, challenge, difficulty);
}
@Override
public byte[] getChallenge(String itemId, String ownerId) {
return getBytes(itemId + ownerId);
}
static CompletableFuture<ProofOfWork> mint(byte[] payload,
public CompletableFuture<ProofOfWork> mint(byte[] payload,
byte[] challenge,
double difficulty) {
return HashCashService.mint(payload,
challenge,
difficulty,
HashCashService::testDifficulty);
}
@Override
boolean verify(ProofOfWork proofOfWork) {
return verify(proofOfWork,
proofOfWork.getChallenge(),
toNumLeadingZeros(proofOfWork.getDifficulty()));
}
static boolean verify(ProofOfWork proofOfWork,
byte[] controlChallenge,
int controlLog2Difficulty) {
return HashCashService.verify(proofOfWork,
controlChallenge,
controlLog2Difficulty,
HashCashService::testDifficulty);
}
static boolean verify(ProofOfWork proofOfWork,
byte[] controlChallenge,
int controlLog2Difficulty,
BiPredicate<byte[], byte[]> challengeValidation,
BiPredicate<Integer, Integer> difficultyValidation) {
return HashCashService.verify(proofOfWork,
controlChallenge,
controlLog2Difficulty,
challengeValidation,
difficultyValidation,
HashCashService::testDifficulty);
}
private static boolean testDifficulty(byte[] result, int log2Difficulty) {
return HashCashService.numberOfLeadingZeros(result) > log2Difficulty;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Generic
///////////////////////////////////////////////////////////////////////////////////////////
static CompletableFuture<ProofOfWork> mint(byte[] payload,
byte[] challenge,
double difficulty,
BiPredicate<byte[], Integer> testDifficulty) {
return CompletableFuture.supplyAsync(() -> {
long ts = System.currentTimeMillis();
int log2Difficulty = toNumLeadingZeros(difficulty);
byte[] result;
byte[] hash;
long counter = 0;
do {
result = toSha256Hash(payload, challenge, ++counter);
hash = toSha256Hash(payload, challenge, ++counter);
}
while (!testDifficulty.test(result, log2Difficulty));
while (numberOfLeadingZeros(hash) <= log2Difficulty);
ProofOfWork proofOfWork = new ProofOfWork(payload, counter, challenge, difficulty, System.currentTimeMillis() - ts, 0);
log.info("Completed minting proofOfWork: {}", proofOfWork);
return proofOfWork;
});
}
static boolean verify(ProofOfWork proofOfWork,
byte[] controlChallenge,
int controlLog2Difficulty,
BiPredicate<byte[], Integer> testDifficulty) {
return verify(proofOfWork,
controlChallenge,
controlLog2Difficulty,
HashCashService.isChallengeValid,
HashCashService.isDifficultyValid,
testDifficulty);
}
static boolean verify(ProofOfWork proofOfWork,
byte[] controlChallenge,
int controlLog2Difficulty,
BiPredicate<byte[], byte[]> challengeValidation,
BiPredicate<Integer, Integer> difficultyValidation,
BiPredicate<byte[], Integer> testDifficulty) {
return challengeValidation.test(proofOfWork.getChallenge(), controlChallenge) &&
difficultyValidation.test(toNumLeadingZeros(proofOfWork.getDifficulty()), controlLog2Difficulty) &&
verify(proofOfWork, testDifficulty);
}
private static boolean verify(ProofOfWork proofOfWork, BiPredicate<byte[], Integer> testDifficulty) {
byte[] hash = HashCashService.toSha256Hash(proofOfWork.getPayload(),
@Override
boolean verify(ProofOfWork proofOfWork) {
byte[] hash = toSha256Hash(proofOfWork.getPayload(),
proofOfWork.getChallenge(),
proofOfWork.getCounter());
return testDifficulty.test(hash, toNumLeadingZeros(proofOfWork.getDifficulty()));
return numberOfLeadingZeros(hash) > toNumLeadingZeros(proofOfWork.getDifficulty());
}
@Override
public byte[] getChallenge(String itemId, String ownerId) {
return getBytes(itemId + ownerId);
}

View File

@ -19,9 +19,9 @@ package bisq.common.crypto;
import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import lombok.Getter;
@ -58,15 +58,13 @@ public abstract class ProofOfWorkService {
public boolean verify(ProofOfWork proofOfWork,
String itemId,
String ownerId,
double controlDifficulty,
BiPredicate<byte[], byte[]> challengeValidation,
BiPredicate<Double, Double> difficultyValidation) {
double controlDifficulty) {
Preconditions.checkArgument(proofOfWork.getVersion() == version);
byte[] controlChallenge = getChallenge(itemId, ownerId);
return challengeValidation.test(proofOfWork.getChallenge(), controlChallenge) &&
difficultyValidation.test(proofOfWork.getDifficulty(), controlDifficulty) &&
return Arrays.equals(proofOfWork.getChallenge(), controlChallenge) &&
proofOfWork.getDifficulty() >= controlDifficulty &&
verify(proofOfWork);
}
}

View File

@ -77,7 +77,7 @@ public class HashCashServiceTest {
List<ProofOfWork> tokens = new ArrayList<>();
for (int i = 0; i < numTokens; i++) {
byte[] challenge = UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8);
tokens.add(HashCashService.mint(payload, challenge, difficulty).get());
tokens.add(new HashCashService().mint(payload, challenge, difficulty).get());
}
double size = tokens.size();
long ts2 = System.currentTimeMillis();

View File

@ -37,9 +37,9 @@ import bisq.common.app.DevEnv;
import bisq.common.app.Version;
import bisq.common.config.Config;
import bisq.common.config.ConfigFileEditor;
import bisq.common.crypto.ProofOfWorkService;
import bisq.common.crypto.KeyRing;
import bisq.common.crypto.ProofOfWork;
import bisq.common.crypto.ProofOfWorkService;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
@ -58,7 +58,6 @@ import java.nio.charset.StandardCharsets;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@ -66,7 +65,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.lang.reflect.Method;
@ -88,12 +86,6 @@ public class FilterManager {
private static final String BANNED_SEED_NODES = "bannedSeedNodes";
private static final String BANNED_BTC_NODES = "bannedBtcNodes";
private final BiPredicate<byte[], byte[]> challengeValidation = Arrays::equals;
// We only require a new pow if difficulty has increased
private final BiPredicate<Double, Double> difficultyValidation =
(value, controlValue) -> value - controlValue >= 0;
///////////////////////////////////////////////////////////////////////////////////////////
// Listener
///////////////////////////////////////////////////////////////////////////////////////////
@ -506,9 +498,7 @@ public class FilterManager {
}
return service.get().verify(offer.getBsqSwapOfferPayload().get().getProofOfWork(),
offer.getId(), offer.getOwnerNodeAddress().toString(),
filter.getPowDifficulty(),
challengeValidation,
difficultyValidation);
filter.getPowDifficulty());
}
public List<Integer> getEnabledPowVersions() {