mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Add separate ProofOfWork.solution proto field for Equihash
Avoid repurposing the 'ProofOfWork.payload' field for Equihash puzzle solutions, as that may be of later use in interactive PoW schemes such as P2P network DoS protection (where the challenge may be a random nonce instead of derived from the offer ID). Instead, make the payload the UTF-8 bytes of the offer ID, just as with Hashcash. Also, make the puzzle seed the SHA-256 hash of the payload concatenated with the challenge, instead of just the 256-bit challenge on its own, so that the PoW is tied to a particular payload and cannot be reused for other payloads in the case of future randomly chosen challenges.
This commit is contained in:
parent
24d2a7f222
commit
db6025a08d
@ -17,6 +17,7 @@
|
||||
|
||||
package bisq.common.crypto;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -34,21 +35,26 @@ public class EquihashProofOfWorkService extends ProofOfWorkService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ProofOfWork> mint(String itemId, byte[] challenge, double difficulty) {
|
||||
public CompletableFuture<ProofOfWork> mint(byte[] payload, byte[] challenge, double difficulty) {
|
||||
double scaledDifficulty = scaledDifficulty(difficulty);
|
||||
log.info("Got scaled & adjusted difficulty: {}", scaledDifficulty);
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
long ts = System.currentTimeMillis();
|
||||
byte[] solution = new Equihash(90, 5, scaledDifficulty).puzzle(challenge).findSolution().serialize();
|
||||
byte[] seed = getSeed(payload, challenge);
|
||||
byte[] solution = new Equihash(90, 5, scaledDifficulty).puzzle(seed).findSolution().serialize();
|
||||
long counter = Longs.fromByteArray(Arrays.copyOf(solution, 8));
|
||||
var proofOfWork = new ProofOfWork(solution, counter, challenge, difficulty,
|
||||
System.currentTimeMillis() - ts, getVersion());
|
||||
var proofOfWork = new ProofOfWork(payload, counter, challenge, difficulty,
|
||||
System.currentTimeMillis() - ts, solution, getVersion());
|
||||
log.info("Completed minting proofOfWork: {}", proofOfWork);
|
||||
return proofOfWork;
|
||||
});
|
||||
}
|
||||
|
||||
private byte[] getSeed(byte[] payload, byte[] challenge) {
|
||||
return Hash.getSha256Hash(Bytes.concat(payload, challenge));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getChallenge(String itemId, String ownerId) {
|
||||
String escapedItemId = itemId.replace(" ", " ");
|
||||
@ -60,8 +66,9 @@ public class EquihashProofOfWorkService extends ProofOfWorkService {
|
||||
boolean verify(ProofOfWork proofOfWork) {
|
||||
double scaledDifficulty = scaledDifficulty(proofOfWork.getDifficulty());
|
||||
|
||||
var puzzle = new Equihash(90, 5, scaledDifficulty).puzzle(proofOfWork.getChallenge());
|
||||
return puzzle.deserializeSolution(proofOfWork.getPayload()).verify();
|
||||
byte[] seed = getSeed(proofOfWork.getPayload(), proofOfWork.getChallenge());
|
||||
var puzzle = new Equihash(90, 5, scaledDifficulty).puzzle(seed);
|
||||
return puzzle.deserializeSolution(proofOfWork.getSolution()).verify();
|
||||
}
|
||||
|
||||
private static double scaledDifficulty(double difficulty) {
|
||||
|
@ -39,11 +39,6 @@ public class HashCashService extends ProofOfWorkService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ProofOfWork> mint(String itemId, byte[] challenge, double difficulty) {
|
||||
byte[] payload = getBytes(itemId);
|
||||
return mint(payload, challenge, difficulty);
|
||||
}
|
||||
|
||||
public CompletableFuture<ProofOfWork> mint(byte[] payload,
|
||||
byte[] challenge,
|
||||
double difficulty) {
|
||||
@ -56,7 +51,9 @@ public class HashCashService extends ProofOfWorkService {
|
||||
hash = toSha256Hash(payload, challenge, ++counter);
|
||||
}
|
||||
while (numberOfLeadingZeros(hash) <= log2Difficulty);
|
||||
ProofOfWork proofOfWork = new ProofOfWork(payload, counter, challenge, difficulty, System.currentTimeMillis() - ts, 0);
|
||||
byte[] solution = Longs.toByteArray(counter);
|
||||
ProofOfWork proofOfWork = new ProofOfWork(payload, counter, challenge, difficulty,
|
||||
System.currentTimeMillis() - ts, solution, 0);
|
||||
log.info("Completed minting proofOfWork: {}", proofOfWork);
|
||||
return proofOfWork;
|
||||
});
|
||||
|
@ -37,6 +37,8 @@ public final class ProofOfWork implements NetworkPayload {
|
||||
@Getter
|
||||
private final long duration;
|
||||
@Getter
|
||||
private final byte[] solution;
|
||||
@Getter
|
||||
private final int version;
|
||||
|
||||
public ProofOfWork(byte[] payload,
|
||||
@ -44,12 +46,14 @@ public final class ProofOfWork implements NetworkPayload {
|
||||
byte[] challenge,
|
||||
double difficulty,
|
||||
long duration,
|
||||
byte[] solution,
|
||||
int version) {
|
||||
this.payload = payload;
|
||||
this.counter = counter;
|
||||
this.challenge = challenge;
|
||||
this.difficulty = difficulty;
|
||||
this.duration = duration;
|
||||
this.solution = solution;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@ -66,6 +70,7 @@ public final class ProofOfWork implements NetworkPayload {
|
||||
.setChallenge(ByteString.copyFrom(challenge))
|
||||
.setDifficulty(difficulty)
|
||||
.setDuration(duration)
|
||||
.setSolution(ByteString.copyFrom(solution))
|
||||
.setVersion(version)
|
||||
.build();
|
||||
}
|
||||
@ -77,6 +82,7 @@ public final class ProofOfWork implements NetworkPayload {
|
||||
proto.getChallenge().toByteArray(),
|
||||
proto.getDifficulty(),
|
||||
proto.getDuration(),
|
||||
proto.getSolution().toByteArray(),
|
||||
proto.getVersion()
|
||||
);
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package bisq.common.crypto;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -45,14 +47,18 @@ public abstract class ProofOfWorkService {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<ProofOfWork> mint(String itemId, byte[] challenge, double difficulty);
|
||||
|
||||
public abstract byte[] getChallenge(String itemId, String ownerId);
|
||||
public abstract CompletableFuture<ProofOfWork> mint(byte[] payload, byte[] challenge, double difficulty);
|
||||
|
||||
abstract boolean verify(ProofOfWork proofOfWork);
|
||||
|
||||
public byte[] getPayload(String itemId) {
|
||||
return itemId.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public abstract byte[] getChallenge(String itemId, String ownerId);
|
||||
|
||||
public CompletableFuture<ProofOfWork> mint(String itemId, String ownerId, double difficulty) {
|
||||
return mint(itemId, getChallenge(itemId, ownerId), difficulty);
|
||||
return mint(getPayload(itemId), getChallenge(itemId, ownerId), difficulty);
|
||||
}
|
||||
|
||||
public boolean verify(ProofOfWork proofOfWork,
|
||||
|
@ -884,7 +884,8 @@ message ProofOfWork {
|
||||
bytes challenge = 3;
|
||||
double difficulty = 4;
|
||||
int64 duration = 5;
|
||||
int32 version = 6;
|
||||
bytes solution = 6;
|
||||
int32 version = 7;
|
||||
}
|
||||
|
||||
message AccountAgeWitness {
|
||||
|
Loading…
Reference in New Issue
Block a user