mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-09-28 01:56:38 -04:00
All ints in quality calculation, quality to quality string
This commit is contained in:
@@ -74,11 +74,10 @@ PYBIND11_MODULE(chiapos, m) {
|
||||
const uint8_t* challenge_ptr = reinterpret_cast<const uint8_t*>(challenge_str.data());
|
||||
std::vector<LargeBits> qualities = dp.GetQualitiesForChallenge(challenge_ptr);
|
||||
std::vector<py::bytes> ret;
|
||||
uint8_t* quality_buf = new uint8_t[Util::ByteAlign(2 * dp.GetSize()) / 8];
|
||||
uint8_t* quality_buf = new uint8_t[32];
|
||||
for (LargeBits quality : qualities) {
|
||||
quality.ToBytes(quality_buf);
|
||||
py::bytes quality_py = py::bytes(reinterpret_cast<char*>(quality_buf),
|
||||
Util::ByteAlign(2 * dp.GetSize()) / 8);
|
||||
py::bytes quality_py = py::bytes(reinterpret_cast<char*>(quality_buf), 32);
|
||||
ret.push_back(quality_py);
|
||||
}
|
||||
delete[] quality_buf;
|
||||
@@ -113,10 +112,9 @@ PYBIND11_MODULE(chiapos, m) {
|
||||
if (quality.GetSize() == 0) {
|
||||
return stdx::optional<py::bytes>();
|
||||
}
|
||||
uint8_t* quality_buf = new uint8_t[Util::ByteAlign(2 * k) / 8];
|
||||
uint8_t* quality_buf = new uint8_t[32];
|
||||
quality.ToBytes(quality_buf);
|
||||
py::bytes quality_py = py::bytes(reinterpret_cast<char*>(quality_buf),
|
||||
Util::ByteAlign(2 * k) / 8);
|
||||
py::bytes quality_py = py::bytes(reinterpret_cast<char*>(quality_buf), 32);
|
||||
delete[] quality_buf;
|
||||
return stdx::optional<py::bytes>(quality_py);
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ int main(int argc, char *argv[]) {
|
||||
DiskProver prover(filename);
|
||||
vector<LargeBits> qualities = prover.GetQualitiesForChallenge(challenge_bytes);
|
||||
for (uint32_t i = 0; i < qualities.size(); i++) {
|
||||
k = qualities[i].GetSize() / 2;
|
||||
k = prover.GetSize() / 2;
|
||||
uint8_t proof_data[8 * k];
|
||||
LargeBits proof = prover.GetFullProof(challenge_bytes, i);
|
||||
proof.ToBytes(proof_data);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "encoding.hpp"
|
||||
#include "calculate_bucket.hpp"
|
||||
#include "plotter_disk.hpp"
|
||||
#include "../lib/include/picosha2.hpp"
|
||||
|
||||
// The DiskProver, given a correctly formatted plot file, can efficiently generate valid proofs
|
||||
// of space, for a given challenge.
|
||||
@@ -127,7 +128,7 @@ class DiskProver {
|
||||
return k;
|
||||
}
|
||||
|
||||
// Given a challenge, returns a quality string, which is 2 adjecent x values,
|
||||
// Given a challenge, returns a quality string, which is sha256(challenge + 2 adjecent x values),
|
||||
// from the 64 value proof. Note that this is more efficient than fetching all
|
||||
// 64 x values, which are in different parts of the disk.
|
||||
std::vector<LargeBits> GetQualitiesForChallenge(const uint8_t* challenge) {
|
||||
@@ -165,8 +166,13 @@ class DiskProver {
|
||||
uint128_t new_line_point = ReadLinePoint(1, position);
|
||||
auto x1x2 = Encoding::LinePointToSquare(new_line_point);
|
||||
|
||||
// The final two x values (which are stored in the same location) are returned.
|
||||
qualities.push_back(LargeBits(x1x2.second, k) + LargeBits(x1x2.first, k));
|
||||
// The final two x values (which are stored in the same location) are hashed
|
||||
vector<unsigned char> hash_input(32 + Util::ByteAlign(2 * k) / 8, 0);
|
||||
memcpy(hash_input.data(), challenge, 32);
|
||||
(LargeBits(x1x2.second, k) + LargeBits(x1x2.first, k)).ToBytes(hash_input.data() + 32);
|
||||
vector<unsigned char> hash(picosha2::k_digest_size);
|
||||
picosha2::hash256(hash_input.begin(), hash_input.end(), hash.begin(), hash.end());
|
||||
qualities.push_back(LargeBits(hash.data(), 32, 256));
|
||||
}
|
||||
disk_file.clear();
|
||||
disk_file.sync();
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
class Verifier {
|
||||
public:
|
||||
// Gets the quality string from a proof in proof ordering. The quality string is two
|
||||
// adjacent values, determined by the quality index (1-32), and the proof in plot
|
||||
// ordering.
|
||||
LargeBits GetQualityString(uint8_t k, LargeBits proof, uint16_t quality_index) {
|
||||
// Gets the quality string from a proof in proof ordering. The quality string is sha256 of
|
||||
// the challenge + two adjacent values, determined by the quality index (1-32),
|
||||
// and the proof in plot ordering.
|
||||
LargeBits GetQualityString(uint8_t k, LargeBits proof, uint16_t quality_index, const uint8_t* challenge) {
|
||||
// Converts the proof from proof ordering to plot ordering
|
||||
for (uint8_t table_index = 1; table_index < 7; table_index++) {
|
||||
LargeBits new_proof;
|
||||
@@ -40,8 +40,13 @@ class Verifier {
|
||||
}
|
||||
proof = new_proof;
|
||||
}
|
||||
// Returns two of the x values, based on the quality index
|
||||
return proof.Slice(k * quality_index, k * (quality_index + 2));
|
||||
// Hashes two of the x values, based on the quality index
|
||||
vector<unsigned char> hash_input(32 + Util::ByteAlign(2 * k) / 8, 0);
|
||||
memcpy(hash_input.data(), challenge, 32);
|
||||
proof.Slice(k * quality_index, k * (quality_index + 2)).ToBytes(hash_input.data() + 32);
|
||||
vector<unsigned char> hash(picosha2::k_digest_size);
|
||||
picosha2::hash256(hash_input.begin(), hash_input.end(), hash.begin(), hash.end());
|
||||
return LargeBits(hash.data(), 32, 256);
|
||||
}
|
||||
|
||||
// Validates a proof of space, and returns the quality string if the proof is valid for the given
|
||||
@@ -107,7 +112,7 @@ class Verifier {
|
||||
// Makes sure the output is equal to the first k bits of the challenge
|
||||
if (challenge_bits.Slice(0, k) == ys[0].Slice(0, k)) {
|
||||
// Returns quality string, which requires changing proof to plot ordering
|
||||
return GetQualityString(k, proof_bits, quality_index);
|
||||
return GetQualityString(k, proof_bits, quality_index, challenge);
|
||||
} else {
|
||||
return LargeBits();
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ void TestProofOfSpace(std::string filename, uint32_t iterations, uint8_t k, uint
|
||||
proof.ToBytes(proof_data);
|
||||
|
||||
LargeBits quality = verifier.ValidateProof(plot_id, k, hash.data(), proof_data, k*8);
|
||||
REQUIRE(quality.GetSize() == 2 * k);
|
||||
REQUIRE(quality.GetSize() == 256);
|
||||
REQUIRE(quality == qualities[index]);
|
||||
success += 1;
|
||||
|
||||
|
||||
@@ -50,15 +50,15 @@ def main():
|
||||
try:
|
||||
for i in range(args.num):
|
||||
challenge = sha256(i.to_bytes(32, "big")).digest()
|
||||
for index, quality in enumerate(
|
||||
for index, quality_str in enumerate(
|
||||
pr.get_qualities_for_challenge(challenge)
|
||||
):
|
||||
proof = pr.get_full_proof(challenge, index)
|
||||
total_proofs += 1
|
||||
ver_quality = v.validate_proof(
|
||||
ver_quality_str = v.validate_proof(
|
||||
plot_seed, pr.get_size(), challenge, proof
|
||||
)
|
||||
assert quality == ver_quality
|
||||
assert quality_str == ver_quality_str
|
||||
except BaseException as e:
|
||||
print(
|
||||
f"{type(e)}: {e} error in proving/verifying for plot {plot_filename}"
|
||||
|
||||
+18
-16
@@ -485,7 +485,7 @@ class Blockchain:
|
||||
self,
|
||||
block: FullBlock,
|
||||
pre_validated: bool = False,
|
||||
pos_quality: bytes32 = None,
|
||||
pos_quality_string: bytes32 = None,
|
||||
) -> Tuple[ReceiveBlockResult, Optional[Header]]:
|
||||
"""
|
||||
Adds a new block into the blockchain, if it's valid and connected to the current
|
||||
@@ -499,7 +499,9 @@ class Blockchain:
|
||||
if block.prev_header_hash not in self.headers and not genesis:
|
||||
return ReceiveBlockResult.DISCONNECTED_BLOCK, None
|
||||
|
||||
if not await self.validate_block(block, genesis, pre_validated, pos_quality):
|
||||
if not await self.validate_block(
|
||||
block, genesis, pre_validated, pos_quality_string
|
||||
):
|
||||
return ReceiveBlockResult.INVALID_BLOCK, None
|
||||
|
||||
# Cache header in memory
|
||||
@@ -518,7 +520,7 @@ class Blockchain:
|
||||
block: FullBlock,
|
||||
prev_full_block: Optional[FullBlock],
|
||||
pre_validated: bool = True,
|
||||
pos_quality: bytes32 = None,
|
||||
pos_quality_string: bytes32 = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Block validation algorithm. Returns true if the candidate block is fully valid
|
||||
@@ -535,7 +537,7 @@ class Blockchain:
|
||||
return False
|
||||
|
||||
# 3. Check coinbase signature with pool pk
|
||||
pair = block.body.coinbase_signature.AGGSIGPair(
|
||||
pair = block.body.coinbase_signature.PkMessagePair(
|
||||
block.proof_of_space.pool_pubkey, block.body.coinbase.name(),
|
||||
)
|
||||
|
||||
@@ -600,9 +602,9 @@ class Blockchain:
|
||||
return False
|
||||
|
||||
# 10. Check proof of space based on challenge
|
||||
if pos_quality is None:
|
||||
pos_quality = block.proof_of_space.verify_and_get_quality()
|
||||
if not pos_quality:
|
||||
if pos_quality_string is None:
|
||||
pos_quality_string = block.proof_of_space.verify_and_get_quality_string()
|
||||
if not pos_quality_string:
|
||||
return False
|
||||
|
||||
# 11. Check block height = prev height + 1
|
||||
@@ -620,7 +622,7 @@ class Blockchain:
|
||||
block: FullBlock,
|
||||
genesis: bool = False,
|
||||
pre_validated: bool = False,
|
||||
pos_quality: bytes32 = None,
|
||||
pos_quality_string: bytes32 = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Block validation algorithm. Returns true iff the candidate block is fully valid,
|
||||
@@ -636,7 +638,7 @@ class Blockchain:
|
||||
# 1. Validate unfinished block (check the rest of the conditions)
|
||||
if not (
|
||||
await self.validate_unfinished_block(
|
||||
block, prev_full_block, pre_validated, pos_quality
|
||||
block, prev_full_block, pre_validated, pos_quality_string
|
||||
)
|
||||
):
|
||||
return False
|
||||
@@ -652,14 +654,14 @@ class Blockchain:
|
||||
ips = uint64(self.constants["VDF_IPS_STARTING"])
|
||||
|
||||
# 3. Check number of iterations on PoT is correct, based on prev block and PoS
|
||||
if pos_quality is None:
|
||||
pos_quality = block.proof_of_space.verify_and_get_quality()
|
||||
if pos_quality_string is None:
|
||||
pos_quality_string = block.proof_of_space.verify_and_get_quality_string()
|
||||
|
||||
if pos_quality is None:
|
||||
if pos_quality_string is None:
|
||||
return False
|
||||
|
||||
number_of_iters: uint64 = calculate_iterations_quality(
|
||||
pos_quality,
|
||||
pos_quality_string,
|
||||
block.proof_of_space.size,
|
||||
difficulty,
|
||||
ips,
|
||||
@@ -788,12 +790,12 @@ class Blockchain:
|
||||
return False, None
|
||||
|
||||
# 10. Check proof of space based on challenge
|
||||
pos_quality = block.proof_of_space.verify_and_get_quality()
|
||||
pos_quality_string = block.proof_of_space.verify_and_get_quality_string()
|
||||
|
||||
if not pos_quality:
|
||||
if not pos_quality_string:
|
||||
return False, None
|
||||
|
||||
return True, bytes(pos_quality)
|
||||
return True, bytes(pos_quality_string)
|
||||
|
||||
def _reconsider_heights(self, old_lca: Optional[Header], new_lca: Header):
|
||||
"""
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
from src.util.ints import uint8, uint64
|
||||
from src.types.sized_bytes import bytes32
|
||||
|
||||
|
||||
def _expected_plot_size(k: uint8) -> uint64:
|
||||
"""
|
||||
Given the plot size parameter k (which is between 30 and 59), computes the
|
||||
expected size of the plot in bytes. This is based on efficient encoding
|
||||
of the plot, and aims to be scale agnostic, so larger plots don't
|
||||
necessarily get more rewards per byte.
|
||||
"""
|
||||
# The following line is the formula for total number of bytes. Instead we can use a formula
|
||||
# for number of kilobytes, to reduce decimal usage.
|
||||
# return 0.762 * k * pow(2, k)
|
||||
|
||||
return 780 * k * pow(2, k - 10)
|
||||
|
||||
|
||||
def quality_str_to_quality(quality_str: bytes32, k: uint8) -> uint64:
|
||||
"""
|
||||
Takes a 256 bit quality, converts it to an integer between 0 and 2**256,
|
||||
representing a decimal d=0.xxxxx..., where x are the bits of the quality.
|
||||
Then we perform -log(d), using a Pade approximation for log:
|
||||
log(1+x) = x(6+x)/(6+4x)
|
||||
This is a very good approximation for x when x is close to 1. However, we only
|
||||
work with big ints, to avoid using decimals. Finally, we divide by the plot size,
|
||||
to make bigger plots have a proportionally
|
||||
"""
|
||||
t = pow(2, 256)
|
||||
|
||||
# xt is (dec(quality_str) - 1) * 2^256. That is, the 0.xxxxx representation of the hash,
|
||||
# minus 1 (so that we can input it into the approximation for log(1+x)), times 2^256, since
|
||||
# we want to work with big ints and not decimals
|
||||
xt = int.from_bytes(quality_str, "big") - t
|
||||
numerator = xt * xt + 6 * (xt) * t
|
||||
denominator = 6 * t + 4 * (xt)
|
||||
|
||||
# To get the output of log(x), you would do the following
|
||||
# log(1+x) = - (numerator / denominator / pow(2, 256)
|
||||
|
||||
# Instead, here we rearrange the terms to only have one division, which we can use bigints for
|
||||
return -(pow(2, 256) * _expected_plot_size(k) * denominator) // numerator
|
||||
@@ -1,39 +1,7 @@
|
||||
from decimal import ROUND_UP, Decimal, getcontext
|
||||
|
||||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.ints import uint8, uint64
|
||||
|
||||
# Sets a high precision so we can convert a 256 bit has to a decimal, and
|
||||
# divide by a large number, while not losing any bits of precision.
|
||||
getcontext().prec = 600
|
||||
|
||||
|
||||
def _expected_plot_size(k: uint8) -> Decimal:
|
||||
"""
|
||||
Given the plot size parameter k (which is between 30 and 59), computes the
|
||||
expected size of the plot in bytes. This is based on efficient encoding
|
||||
of the plot, and aims to be scale agnostic, so larger plots don't
|
||||
necessarily get more rewards per byte.
|
||||
"""
|
||||
return Decimal(Decimal(0.762) * k * pow(2, k))
|
||||
|
||||
|
||||
def _quality_to_decimal(quality: bytes32) -> Decimal:
|
||||
"""
|
||||
Takes a 256 bit quality, converts it to an integer between 0 and 2**256,
|
||||
representing a decimal d=0.xxxxx..., where x are the bits of the quality.
|
||||
Then we perform -log(d), using a Pade approximation for log:
|
||||
log(1+x) = x(6+x)/(6+4x)
|
||||
This is a very good approximation for x when x is close to 1. However, we only
|
||||
work with big ints, to avoid using decimals.
|
||||
"""
|
||||
t = pow(2, 256)
|
||||
xt = int.from_bytes(quality, "big") - t
|
||||
numerator = xt * xt + 6 * (xt) * t
|
||||
denominator = 6 * t + 4 * (xt)
|
||||
# Performs big integer division, and then turns it into a decimal
|
||||
return -Decimal(numerator // denominator) / Decimal(t)
|
||||
from src.consensus.pos_quality import quality_str_to_quality
|
||||
|
||||
|
||||
def calculate_iterations_quality(
|
||||
@@ -49,12 +17,9 @@ def calculate_iterations_quality(
|
||||
difficulty.
|
||||
"""
|
||||
min_iterations = min_block_time * vdf_ips
|
||||
dec_iters = Decimal(int(difficulty) << 32) * (
|
||||
_quality_to_decimal(quality) / _expected_plot_size(size)
|
||||
)
|
||||
iters_final = uint64(
|
||||
int(min_iterations + dec_iters.to_integral_exact(rounding=ROUND_UP))
|
||||
)
|
||||
iters_rounded = (int(difficulty) << 32) // quality_str_to_quality(quality, size)
|
||||
|
||||
iters_final = uint64(min_iterations + iters_rounded)
|
||||
assert iters_final >= 1
|
||||
return iters_final
|
||||
|
||||
@@ -69,7 +34,7 @@ def calculate_iterations(
|
||||
Convenience function to calculate the number of iterations using the proof instead
|
||||
of the quality. The quality must be retrieved from the proof.
|
||||
"""
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality()
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality_string()
|
||||
return calculate_iterations_quality(
|
||||
quality, proof_of_space.size, difficulty, vdf_ips, min_block_time
|
||||
)
|
||||
@@ -86,11 +51,10 @@ def calculate_ips_from_iterations(
|
||||
other details, we can calculate the VDF speed (iterations per second) used to compute the
|
||||
constant factor in iterations, which is not written into the block.
|
||||
"""
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality()
|
||||
dec_iters = Decimal(int(difficulty) << 32) * (
|
||||
_quality_to_decimal(quality) / _expected_plot_size(proof_of_space.size)
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality_string()
|
||||
iters_rounded = (int(difficulty) << 32) // quality_str_to_quality(
|
||||
quality, proof_of_space.size
|
||||
)
|
||||
iters_rounded = int(dec_iters.to_integral_exact(rounding=ROUND_UP))
|
||||
min_iterations = uint64(iterations - iters_rounded)
|
||||
ips = min_iterations / min_block_time
|
||||
assert ips >= 1
|
||||
|
||||
+32
-17
@@ -62,8 +62,10 @@ class Farmer:
|
||||
of space is sufficiently good, and if so, we ask for the whole proof.
|
||||
"""
|
||||
|
||||
if challenge_response.quality in self.harvester_responses_challenge:
|
||||
log.warning(f"Have already seen quality {challenge_response.quality}")
|
||||
if challenge_response.quality_string in self.harvester_responses_challenge:
|
||||
log.warning(
|
||||
f"Have already seen quality string {challenge_response.quality_string}"
|
||||
)
|
||||
return
|
||||
weight: uint64 = self.challenge_to_weight[challenge_response.challenge_hash]
|
||||
height: uint32 = self.challenge_to_height[challenge_response.challenge_hash]
|
||||
@@ -75,7 +77,7 @@ class Farmer:
|
||||
raise RuntimeError("Did not find challenge")
|
||||
|
||||
number_iters: uint64 = calculate_iterations_quality(
|
||||
challenge_response.quality,
|
||||
challenge_response.quality_string,
|
||||
challenge_response.plot_size,
|
||||
difficulty,
|
||||
self.proof_of_time_estimate_ips,
|
||||
@@ -103,9 +105,11 @@ class Farmer:
|
||||
or estimate_secs < self.config["propagate_threshold"]
|
||||
):
|
||||
self.harvester_responses_challenge[
|
||||
challenge_response.quality
|
||||
challenge_response.quality_string
|
||||
] = challenge_response.challenge_hash
|
||||
request = harvester_protocol.RequestProofOfSpace(challenge_response.quality)
|
||||
request = harvester_protocol.RequestProofOfSpace(
|
||||
challenge_response.quality_string
|
||||
)
|
||||
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
@@ -129,7 +133,9 @@ class Farmer:
|
||||
if response.proof.pool_pubkey not in [sk.get_public_key() for sk in pool_sks]:
|
||||
raise RuntimeError("Pool pubkey not in list of approved keys")
|
||||
|
||||
challenge_hash: bytes32 = self.harvester_responses_challenge[response.quality]
|
||||
challenge_hash: bytes32 = self.harvester_responses_challenge[
|
||||
response.quality_string
|
||||
]
|
||||
challenge_weight: uint64 = self.challenge_to_weight[challenge_hash]
|
||||
challenge_height: uint32 = self.challenge_to_height[challenge_hash]
|
||||
new_proof_height: uint32 = uint32(challenge_height + 1)
|
||||
@@ -140,17 +146,17 @@ class Farmer:
|
||||
if difficulty == 0:
|
||||
raise RuntimeError("Did not find challenge")
|
||||
|
||||
computed_quality = response.proof.verify_and_get_quality()
|
||||
if response.quality != computed_quality:
|
||||
computed_quality_string = response.proof.verify_and_get_quality_string()
|
||||
if response.quality_string != computed_quality_string:
|
||||
raise RuntimeError("Invalid quality for proof of space")
|
||||
|
||||
self.harvester_responses_proofs[response.quality] = response.proof
|
||||
self.harvester_responses_proofs[response.quality_string] = response.proof
|
||||
self.harvester_responses_proof_hash_to_qual[
|
||||
response.proof.get_hash()
|
||||
] = response.quality
|
||||
] = response.quality_string
|
||||
|
||||
number_iters: uint64 = calculate_iterations_quality(
|
||||
computed_quality,
|
||||
computed_quality_string,
|
||||
response.proof.size,
|
||||
difficulty,
|
||||
self.proof_of_time_estimate_ips,
|
||||
@@ -160,7 +166,8 @@ class Farmer:
|
||||
|
||||
if estimate_secs < self.config["pool_share_threshold"]:
|
||||
request1 = harvester_protocol.RequestPartialProof(
|
||||
response.quality, bytes.fromhex(self.key_config["farmer_target"]),
|
||||
response.quality_string,
|
||||
bytes.fromhex(self.key_config["farmer_target"]),
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.HARVESTER,
|
||||
@@ -197,9 +204,15 @@ class Farmer:
|
||||
Receives a signature on a block header hash, which is required for submitting
|
||||
a block to the blockchain.
|
||||
"""
|
||||
header_hash: bytes32 = self.harvester_responses_header_hash[response.quality]
|
||||
proof_of_space: bytes32 = self.harvester_responses_proofs[response.quality]
|
||||
plot_pubkey = self.harvester_responses_proofs[response.quality].plot_pubkey
|
||||
header_hash: bytes32 = self.harvester_responses_header_hash[
|
||||
response.quality_string
|
||||
]
|
||||
proof_of_space: bytes32 = self.harvester_responses_proofs[
|
||||
response.quality_string
|
||||
]
|
||||
plot_pubkey = self.harvester_responses_proofs[
|
||||
response.quality_string
|
||||
].plot_pubkey
|
||||
|
||||
assert response.header_hash_signature.verify(
|
||||
[Util.hash256(header_hash)], [plot_pubkey]
|
||||
@@ -224,7 +237,9 @@ class Farmer:
|
||||
"""
|
||||
|
||||
farmer_target = bytes.fromhex(self.key_config["farmer_target"])
|
||||
plot_pubkey = self.harvester_responses_proofs[response.quality].plot_pubkey
|
||||
plot_pubkey = self.harvester_responses_proofs[
|
||||
response.quality_string
|
||||
].plot_pubkey
|
||||
|
||||
assert response.farmer_target_signature.verify(
|
||||
[Util.hash256(farmer_target)], [plot_pubkey]
|
||||
@@ -335,7 +350,7 @@ class Farmer:
|
||||
self.unfinished_challenges[proof_of_space_arrived.weight] = []
|
||||
else:
|
||||
self.unfinished_challenges[proof_of_space_arrived.weight].append(
|
||||
proof_of_space_arrived.quality
|
||||
proof_of_space_arrived.quality_string
|
||||
)
|
||||
|
||||
@api_request
|
||||
|
||||
+1
-1
@@ -738,7 +738,7 @@ class FullNode:
|
||||
request.challenge_hash,
|
||||
bytes(request.proof_of_space.proof),
|
||||
)
|
||||
assert quality_string
|
||||
assert len(quality_string) == 32
|
||||
|
||||
# Retrieves the correct tip for the challenge
|
||||
tips: List[Header] = self.blockchain.get_current_tips()
|
||||
|
||||
+15
-16
@@ -25,7 +25,7 @@ class Harvester:
|
||||
# From filename to prover
|
||||
self.provers: Dict[Path, DiskProver] = {}
|
||||
|
||||
# From quality to (challenge_hash, filename, index)
|
||||
# From quality string to (challenge_hash, filename, index)
|
||||
self.challenge_hashes: Dict[bytes32, Tuple[bytes32, Path, uint8]] = {}
|
||||
self._plot_notification_task = asyncio.create_task(self._plot_notification())
|
||||
self._is_shutdown: bool = False
|
||||
@@ -94,7 +94,7 @@ class Harvester:
|
||||
@api_request
|
||||
async def new_challenge(self, new_challenge: harvester_protocol.NewChallenge):
|
||||
"""
|
||||
The harvester receives a new challenge from the farmer, and looks up the quality
|
||||
The harvester receives a new challenge from the farmer, and looks up the quality string
|
||||
for any proofs of space that are are found in the plots. If proofs are found, a
|
||||
ChallengeResponse message is sent for each of the proofs found.
|
||||
"""
|
||||
@@ -114,16 +114,13 @@ class Harvester:
|
||||
new_challenge.challenge_hash
|
||||
)
|
||||
for index, quality_str in enumerate(quality_strings):
|
||||
quality = ProofOfSpace.quality_str_to_quality(
|
||||
new_challenge.challenge_hash, quality_str
|
||||
)
|
||||
self.challenge_hashes[quality] = (
|
||||
self.challenge_hashes[quality_str] = (
|
||||
new_challenge.challenge_hash,
|
||||
filename,
|
||||
uint8(index),
|
||||
)
|
||||
response: harvester_protocol.ChallengeResponse = harvester_protocol.ChallengeResponse(
|
||||
new_challenge.challenge_hash, quality, prover.get_size()
|
||||
new_challenge.challenge_hash, quality_str, prover.get_size()
|
||||
)
|
||||
all_responses.append(response)
|
||||
for response in all_responses:
|
||||
@@ -143,10 +140,12 @@ class Harvester:
|
||||
"""
|
||||
response: Optional[harvester_protocol.RespondProofOfSpace] = None
|
||||
try:
|
||||
# Using the quality find the right plot and index from our solutions
|
||||
challenge_hash, filename, index = self.challenge_hashes[request.quality]
|
||||
# Using the quality string, find the right plot and index from our solutions
|
||||
challenge_hash, filename, index = self.challenge_hashes[
|
||||
request.quality_string
|
||||
]
|
||||
except KeyError:
|
||||
log.warning(f"Quality {request.quality} not found")
|
||||
log.warning(f"Quality string {request.quality_string} not found")
|
||||
return
|
||||
if index is not None:
|
||||
proof_xs: bytes
|
||||
@@ -170,7 +169,7 @@ class Harvester:
|
||||
)
|
||||
|
||||
response = harvester_protocol.RespondProofOfSpace(
|
||||
request.quality, proof_of_space
|
||||
request.quality_string, proof_of_space
|
||||
)
|
||||
if response:
|
||||
yield OutboundMessage(
|
||||
@@ -187,10 +186,10 @@ class Harvester:
|
||||
The farmer requests a signature on the header hash, for one of the proofs that we found.
|
||||
A signature is created on the header hash using the plot private key.
|
||||
"""
|
||||
if request.quality not in self.challenge_hashes:
|
||||
if request.quality_string not in self.challenge_hashes:
|
||||
return
|
||||
|
||||
_, filename, _ = self.challenge_hashes[request.quality]
|
||||
_, filename, _ = self.challenge_hashes[request.quality_string]
|
||||
|
||||
plot_sk = PrivateKey.from_bytes(
|
||||
bytes.fromhex(self.plot_config["plots"][filename]["sk"])
|
||||
@@ -203,7 +202,7 @@ class Harvester:
|
||||
)
|
||||
|
||||
response: harvester_protocol.RespondHeaderSignature = harvester_protocol.RespondHeaderSignature(
|
||||
request.quality, header_hash_signature,
|
||||
request.quality_string, header_hash_signature,
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.FARMER,
|
||||
@@ -220,7 +219,7 @@ class Harvester:
|
||||
We look up the correct plot based on the quality, lookup the proof, and sign
|
||||
the farmer target hash using the plot private key. This will be used as a pool share.
|
||||
"""
|
||||
_, filename, _ = self.challenge_hashes[request.quality]
|
||||
_, filename, _ = self.challenge_hashes[request.quality_string]
|
||||
plot_sk = PrivateKey.from_bytes(
|
||||
bytes.fromhex(self.plot_config["plots"][filename]["sk"])
|
||||
)
|
||||
@@ -229,7 +228,7 @@ class Harvester:
|
||||
)
|
||||
|
||||
response: harvester_protocol.RespondPartialProof = harvester_protocol.RespondPartialProof(
|
||||
request.quality, farmer_target_signature
|
||||
request.quality_string, farmer_target_signature
|
||||
)
|
||||
yield OutboundMessage(
|
||||
NodeType.FARMER,
|
||||
|
||||
@@ -25,8 +25,9 @@ class ProofOfSpaceFinalized:
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class ProofOfSpaceArrived:
|
||||
previous_challenge_hash: bytes32
|
||||
weight: uint64
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -30,46 +30,46 @@ class NewChallenge:
|
||||
@cbor_message
|
||||
class ChallengeResponse:
|
||||
challenge_hash: bytes32
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
plot_size: uint8
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RequestProofOfSpace:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RespondProofOfSpace:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
proof: ProofOfSpace
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RequestHeaderSignature:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
header_hash: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RespondHeaderSignature:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
header_hash_signature: PrependSignature
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RequestPartialProof:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
farmer_target_hash: bytes32
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@cbor_message
|
||||
class RespondPartialProof:
|
||||
quality: bytes32
|
||||
quality_string: bytes32
|
||||
farmer_target_signature: PrependSignature
|
||||
|
||||
@@ -5,7 +5,7 @@ from src.types.sized_bytes import bytes32
|
||||
from src.util.cbor_message import cbor_message
|
||||
from src.util.ints import uint16
|
||||
|
||||
protocol_version = "0.0.4"
|
||||
protocol_version = "0.0.5"
|
||||
|
||||
"""
|
||||
Handshake when establishing a connection between two servers.
|
||||
|
||||
@@ -23,7 +23,7 @@ class BLSSignature(Streamable):
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@streamable
|
||||
class AGGSIGPair(Streamable):
|
||||
class PkMessagePair(Streamable):
|
||||
public_key: BLSPublicKey
|
||||
message_hash: bytes32
|
||||
|
||||
@@ -39,7 +39,7 @@ class BLSSignature(Streamable):
|
||||
sig = blspy.PrependSignature.aggregate(wrapped_sigs).serialize()
|
||||
return cls(sig)
|
||||
|
||||
def validate(self, hash_key_pairs: List[AGGSIGPair]) -> bool:
|
||||
def validate(self, hash_key_pairs: List[PkMessagePair]) -> bool:
|
||||
# check for special case of 0
|
||||
if len(hash_key_pairs) == 0:
|
||||
return True
|
||||
|
||||
@@ -22,7 +22,7 @@ class ProofOfSpace(Streamable):
|
||||
def get_plot_seed(self) -> bytes32:
|
||||
return self.calculate_plot_seed(self.pool_pubkey, self.plot_pubkey)
|
||||
|
||||
def verify_and_get_quality(self) -> Optional[bytes32]:
|
||||
def verify_and_get_quality_string(self) -> Optional[bytes32]:
|
||||
v: Verifier = Verifier()
|
||||
plot_seed: bytes32 = self.get_plot_seed()
|
||||
quality_str = v.validate_proof(
|
||||
@@ -30,12 +30,8 @@ class ProofOfSpace(Streamable):
|
||||
)
|
||||
if not quality_str:
|
||||
return None
|
||||
return self.quality_str_to_quality(self.challenge_hash, quality_str)
|
||||
return quality_str
|
||||
|
||||
@staticmethod
|
||||
def calculate_plot_seed(pool_pubkey: PublicKey, plot_pubkey: PublicKey) -> bytes32:
|
||||
return bytes32(sha256(bytes(pool_pubkey) + bytes(plot_pubkey)).digest())
|
||||
|
||||
@staticmethod
|
||||
def quality_str_to_quality(challenge_hash: bytes32, quality_str: bytes) -> bytes32:
|
||||
return bytes32(sha256(challenge_hash + quality_str).digest())
|
||||
|
||||
@@ -72,14 +72,14 @@ def conditions_by_opcode(
|
||||
|
||||
def hash_key_pairs_for_conditions_dict(
|
||||
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]]
|
||||
) -> List[BLSSignature.AGGSIGPair]:
|
||||
pairs: List[BLSSignature.AGGSIGPair] = []
|
||||
) -> List[BLSSignature.PkMessagePair]:
|
||||
pairs: List[BLSSignature.PkMessagePair] = []
|
||||
for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
|
||||
# TODO: check types
|
||||
# assert len(_) == 3
|
||||
blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
|
||||
message: bytes32 = bytes32(blspy.Util.hash256(cvp.var2))
|
||||
pairs.append(BLSSignature.AGGSIGPair(blspubkey, message))
|
||||
pairs.append(BLSSignature.PkMessagePair(blspubkey, message))
|
||||
return pairs
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +1,11 @@
|
||||
from decimal import Decimal
|
||||
from hashlib import sha256
|
||||
from math import log
|
||||
|
||||
from src.consensus.pot_iterations import (
|
||||
_expected_plot_size,
|
||||
_quality_to_decimal,
|
||||
calculate_iterations_quality,
|
||||
)
|
||||
from src.consensus.pot_iterations import calculate_iterations_quality
|
||||
from src.consensus.pos_quality import _expected_plot_size
|
||||
from src.util.ints import uint8, uint64
|
||||
|
||||
|
||||
class TestPotIterations:
|
||||
def test_pade_approximation(self):
|
||||
def test_approximation(input_dec, threshold):
|
||||
bytes_input = int(Decimal(input_dec) * pow(2, 256)).to_bytes(32, "big")
|
||||
print(_quality_to_decimal(bytes_input))
|
||||
assert (
|
||||
abs(1 - Decimal(-log(input_dec)) / _quality_to_decimal(bytes_input))
|
||||
< threshold
|
||||
)
|
||||
|
||||
# The approximations become better the closer to 1 the input gets
|
||||
test_approximation(0.7, 0.01)
|
||||
test_approximation(0.9, 0.001)
|
||||
test_approximation(0.99, 0.00001)
|
||||
test_approximation(0.9999, 0.0000001)
|
||||
test_approximation(0.99999999, 0.0000000001)
|
||||
|
||||
def test_win_percentage(self):
|
||||
"""
|
||||
Tests that the percentage of blocks won is proportional to the space of each farmer,
|
||||
|
||||
Reference in New Issue
Block a user