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.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* HashCash implementation for proof of work * 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 * See https://www.hashcash.org/papers/hashcash.pdf
*/ */
@Slf4j @Slf4j
public class HashCashService extends ProofOfWorkService { 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() { HashCashService() {
super(0); super(0);
} }
@ -50,105 +44,35 @@ public class HashCashService extends ProofOfWorkService {
return mint(payload, challenge, difficulty); return mint(payload, challenge, difficulty);
} }
@Override public CompletableFuture<ProofOfWork> mint(byte[] payload,
public byte[] getChallenge(String itemId, String ownerId) {
return getBytes(itemId + ownerId);
}
static CompletableFuture<ProofOfWork> mint(byte[] payload,
byte[] challenge, byte[] challenge,
double difficulty) { 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(() -> { return CompletableFuture.supplyAsync(() -> {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
int log2Difficulty = toNumLeadingZeros(difficulty); int log2Difficulty = toNumLeadingZeros(difficulty);
byte[] result; byte[] hash;
long counter = 0; long counter = 0;
do { 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); ProofOfWork proofOfWork = new ProofOfWork(payload, counter, challenge, difficulty, System.currentTimeMillis() - ts, 0);
log.info("Completed minting proofOfWork: {}", proofOfWork); log.info("Completed minting proofOfWork: {}", proofOfWork);
return proofOfWork; return proofOfWork;
}); });
} }
static boolean verify(ProofOfWork proofOfWork, @Override
byte[] controlChallenge, boolean verify(ProofOfWork proofOfWork) {
int controlLog2Difficulty, byte[] hash = toSha256Hash(proofOfWork.getPayload(),
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(),
proofOfWork.getChallenge(), proofOfWork.getChallenge(),
proofOfWork.getCounter()); 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 com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import lombok.Getter; import lombok.Getter;
@ -58,15 +58,13 @@ public abstract class ProofOfWorkService {
public boolean verify(ProofOfWork proofOfWork, public boolean verify(ProofOfWork proofOfWork,
String itemId, String itemId,
String ownerId, String ownerId,
double controlDifficulty, double controlDifficulty) {
BiPredicate<byte[], byte[]> challengeValidation,
BiPredicate<Double, Double> difficultyValidation) {
Preconditions.checkArgument(proofOfWork.getVersion() == version); Preconditions.checkArgument(proofOfWork.getVersion() == version);
byte[] controlChallenge = getChallenge(itemId, ownerId); byte[] controlChallenge = getChallenge(itemId, ownerId);
return challengeValidation.test(proofOfWork.getChallenge(), controlChallenge) && return Arrays.equals(proofOfWork.getChallenge(), controlChallenge) &&
difficultyValidation.test(proofOfWork.getDifficulty(), controlDifficulty) && proofOfWork.getDifficulty() >= controlDifficulty &&
verify(proofOfWork); verify(proofOfWork);
} }
} }

View File

@ -77,7 +77,7 @@ public class HashCashServiceTest {
List<ProofOfWork> tokens = new ArrayList<>(); List<ProofOfWork> tokens = new ArrayList<>();
for (int i = 0; i < numTokens; i++) { for (int i = 0; i < numTokens; i++) {
byte[] challenge = UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8); 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(); double size = tokens.size();
long ts2 = System.currentTimeMillis(); long ts2 = System.currentTimeMillis();

View File

@ -37,9 +37,9 @@ import bisq.common.app.DevEnv;
import bisq.common.app.Version; import bisq.common.app.Version;
import bisq.common.config.Config; import bisq.common.config.Config;
import bisq.common.config.ConfigFileEditor; import bisq.common.config.ConfigFileEditor;
import bisq.common.crypto.ProofOfWorkService;
import bisq.common.crypto.KeyRing; import bisq.common.crypto.KeyRing;
import bisq.common.crypto.ProofOfWork; import bisq.common.crypto.ProofOfWork;
import bisq.common.crypto.ProofOfWorkService;
import org.bitcoinj.core.ECKey; import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Sha256Hash;
@ -58,7 +58,6 @@ import java.nio.charset.StandardCharsets;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -66,7 +65,6 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiPredicate;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.lang.reflect.Method; 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_SEED_NODES = "bannedSeedNodes";
private static final String BANNED_BTC_NODES = "bannedBtcNodes"; 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 // Listener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -506,9 +498,7 @@ public class FilterManager {
} }
return service.get().verify(offer.getBsqSwapOfferPayload().get().getProofOfWork(), return service.get().verify(offer.getBsqSwapOfferPayload().get().getProofOfWork(),
offer.getId(), offer.getOwnerNodeAddress().toString(), offer.getId(), offer.getOwnerNodeAddress().toString(),
filter.getPowDifficulty(), filter.getPowDifficulty());
challengeValidation,
difficultyValidation);
} }
public List<Integer> getEnabledPowVersions() { public List<Integer> getEnabledPowVersions() {