Remove challenge signature

This commit is contained in:
Mariano Sorgente 2020-06-25 12:11:29 +09:00 committed by Gene Hoffman
parent 0dea6db7cd
commit 73398e0159
15 changed files with 94 additions and 147 deletions

View File

@ -74,9 +74,9 @@ testnet_kwargs = {
"DIFFICULTY_STARTING": 2 ** 31,
"DIFFICULTY_FACTOR": 3, # The next difficulty is truncated to range [prev / FACTOR, prev * FACTOR]
# These 3 constants must be changed at the same time
"DIFFICULTY_EPOCH": 128, # The number of blocks per epoch
"DIFFICULTY_EPOCH": 256, # The number of blocks per epoch
"DIFFICULTY_WARP_FACTOR": 4, # DELAY divides EPOCH in order to warp efficiently.
"DIFFICULTY_DELAY": 32, # EPOCH / WARP_FACTOR
"DIFFICULTY_DELAY": 64, # EPOCH / WARP_FACTOR
"SIGNIFICANT_BITS": 12, # The number of bits to look at in difficulty and min iters. The rest are zeroed
"DISCRIMINANT_SIZE_BITS": 1024, # Max is 1024 (based on ClassGroupElement int size)
"NUMBER_ZERO_BITS_CHALLENGE_SIG": 7, # H(plot signature of the challenge) must start with these many zeroes

View File

@ -2,7 +2,7 @@ import asyncio
import logging
from typing import Dict, List, Set, Optional, Callable, Tuple
from blspy import Util, InsecureSignature
from blspy import Util, InsecureSignature, PrependSignature
from src.util.keychain import Keychain
from src.consensus.constants import ConsensusConstants
@ -185,10 +185,6 @@ class Farmer:
and request a pool partial, a header signature, or both, if the proof is good enough.
"""
assert response.proof_of_possession.verify(
[Util.hash256(b"")], [response.harvester_pk]
)
challenge_hash: bytes32 = response.proof.challenge_hash
challenge_weight: uint128 = self.challenge_to_weight[challenge_hash]
difficulty: uint64 = uint64(0)
@ -224,19 +220,6 @@ class Farmer:
)
estimate_secs: float = number_iters / self.proof_of_time_estimate_ips
found = False
for pk in self._get_public_keys():
if (
ProofOfSpace.generate_plot_pubkey(response.harvester_pk, pk)
== response.proof.plot_pubkey
):
found = True
if not found:
log.error(
f"Don't have the private key required for farming plot with plot pk: {response.proof.plot_pubkey.hex()}"
)
return
if estimate_secs < self.config["pool_share_threshold"]:
# TODO: implement pooling
pass
@ -265,14 +248,23 @@ class Farmer:
]
validates: bool = False
for sk in self._get_private_keys():
sig = sk.sign_insecure(header_hash)
agg_sig = InsecureSignature.aggregate(response.message_signature, sig)
validates = agg_sig.verify(
[Util.hash256(header_hash)], [proof_of_space.plot_pubkey]
agg_pk = ProofOfSpace.generate_plot_pubkey(
response.harvester_pk, sk.get_public_key()
)
if validates:
break
if agg_pk == proof_of_space.plot_pubkey:
new_m = bytes(agg_pk) + Util.hash256(header_hash)
farmer_share = sk.sign_insecure(new_m)
agg_sig = PrependSignature.from_insecure_sig(
InsecureSignature.aggregate(
response.message_signature, farmer_share
)
)
validates = agg_sig.verify(
[Util.hash256(new_m)], [proof_of_space.plot_pubkey]
)
if validates:
break
assert validates
pos_hash: bytes32 = proof_of_space.get_hash()
@ -348,17 +340,8 @@ class Farmer:
] = proof_of_space_finalized.height
if get_proofs:
signatures = []
for sk in self._get_private_keys():
signatures.append(
(
sk.get_public_key(),
sk.sign_insecure(proof_of_space_finalized.challenge_hash),
)
)
message = harvester_protocol.NewChallenge(
proof_of_space_finalized.challenge_hash, signatures
proof_of_space_finalized.challenge_hash
)
yield OutboundMessage(
NodeType.HARVESTER,

View File

@ -28,7 +28,6 @@ class Harvester:
config: Dict
provers: Dict[Path, PlotInfo]
failed_to_open_filenames: List[Path]
challenge_signatures: Dict[Tuple[bytes32, Path], InsecureSignature]
no_key_filenames: List[Path]
farmer_pubkeys: List[PublicKey]
root_path: Path
@ -47,9 +46,6 @@ class Harvester:
self.failed_to_open_filenames = []
self.no_key_filenames = []
# From (challenge_hash, filename) to farmer signature
self.challenge_signatures = {}
self._is_shutdown = False
self._plot_notification_task = None
self.global_connections: Optional[PeerConnections] = None
@ -172,11 +168,7 @@ class Harvester:
ChallengeResponse message is sent for each of the proofs found.
"""
start = time.time()
challenge_size = len(new_challenge.challenge_hash)
if challenge_size != 32:
raise ValueError(
f"Invalid challenge size {challenge_size}, 32 was expected"
)
assert len(new_challenge.challenge_hash) == 32
loop = asyncio.get_running_loop()
@ -226,19 +218,12 @@ class Harvester:
harvester_sig = plot_info.harvester_sk.sign_insecure(
new_challenge.challenge_hash
)
agg_sig = None
for (farmer_pk, farmer_sig) in new_challenge.farmer_challenge_signatures:
if farmer_pk == plot_info.farmer_public_key:
agg_sig = InsecureSignature.aggregate(harvester_sig, farmer_sig)
if agg_sig is None:
log.error("Farmer does not have the correct keys for this plot")
return
# This is actually secure, check proof_of_space.py for explanation
h = BitArray(std_hash(bytes(agg_sig)))
if h[: self.constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"]].int == 0:
if ProofOfSpace.can_create_proof(
plot_info.prover.get_id(),
new_challenge.challenge_hash,
self.constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"],
):
awaitables.append(lookup_challenge(filename, plot_info.prover))
self.challenge_signatures[(new_challenge.challenge_hash, filename)]
# Concurrently executes all lookups on disk, to take advantage of multiple disk parallelism
for sublist_awaitable in asyncio.as_completed(awaitables):
@ -267,14 +252,6 @@ class Harvester:
index = request.response_number
proof_xs: bytes
plot_info = self.provers[filename]
challenge_signature = self.challenge_signatures[(challenge_hash, filename)]
assert (
BitArray(std_hash(challenge_signature))[
: self.constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"]
].int
== 0
)
try:
try:
@ -302,17 +279,11 @@ class Harvester:
plot_info.farmer_address,
plot_info.pool_address,
plot_pubkey,
challenge_signature,
uint8(self.provers[filename].get_size()),
proof_xs,
)
proof_of_possession = plot_info.harvester_sk.sign_prepend(b"")
response = harvester_protocol.RespondProofOfSpace(
request.plot_id,
request.response_number,
proof_of_space,
plot_info.harvester_sk.get_public_key(),
proof_of_possession,
request.plot_id, request.response_number, proof_of_space,
)
if response:
yield OutboundMessage(
@ -331,13 +302,21 @@ class Harvester:
plot_info = self.provers[Path(request.plot_id).resolve()]
plot_sk = plot_info.harvester_sk
signature: InsecureSignature = plot_sk.sign_insecure(request.message)
assert signature.verify(
[Util.hash256(request.message)], [plot_sk.get_public_key()]
agg_pk = ProofOfSpace.generate_plot_pubkey(
plot_sk.get_public_key(), plot_info.farmer_public_key
)
new_m = bytes(agg_pk) + Util.hash256(request.message)
# This is only a partial signature. When combined with the farmer's half, it will
# form a complete PrependSignature.
signature: InsecureSignature = plot_sk.sign_insecure(new_m)
response: harvester_protocol.RespondSignature = harvester_protocol.RespondSignature(
request.challenge_hash, request.plot_id, request.response_number, signature,
request.challenge_hash,
request.plot_id,
request.response_number,
plot_sk.get_public_key(),
signature,
)
yield OutboundMessage(

View File

@ -24,7 +24,6 @@ class HarvesterHandshake:
@cbor_message
class NewChallenge:
challenge_hash: bytes32
farmer_challenge_signatures: List[Tuple[PublicKey, InsecureSignature]]
@dataclass(frozen=True)
@ -51,8 +50,6 @@ class RespondProofOfSpace:
plot_id: str
response_number: uint8
proof: ProofOfSpace
harvester_pk: PublicKey
proof_of_possession: PrependSignature
@dataclass(frozen=True)
@ -70,4 +67,5 @@ class RespondSignature:
challenge_hash: bytes32
plot_id: str
response_number: uint8
harvester_pk: PublicKey
message_signature: InsecureSignature

View File

@ -18,7 +18,6 @@ class ProofOfSpace(Streamable):
farmer_puzzle_hash: bytes32
pool_puzzle_hash: bytes32
plot_pubkey: PublicKey
challenge_signature: InsecureSignature
size: uint8
proof: bytes
@ -33,17 +32,23 @@ class ProofOfSpace(Streamable):
quality_str = v.validate_proof(
plot_seed, self.size, self.challenge_hash, bytes(self.proof)
)
if not self.challenge_signature.verify(
[Util.hash256(self.challenge_hash)], [self.plot_pubkey]
):
return None
h = BitArray(std_hash(bytes(self.challenge_signature)))
if h[:num_zero_bits].int != 0:
if not self.can_create_proof(plot_seed, self.challenge_hash, num_zero_bits):
return None
if not quality_str:
return None
return quality_str
@staticmethod
def can_create_proof(
plot_seed: bytes32, challenge_hash: bytes32, num_zero_bits: uint8
) -> bool:
h = BitArray(std_hash(bytes(challenge_hash) + bytes(plot_seed)))
if h[:num_zero_bits].int != 0:
return False
return True
@staticmethod
def calculate_plot_seed(
farmer_puzzle_hash: bytes32,

View File

@ -46,6 +46,8 @@ from src.util.mempool_check_conditions import get_name_puzzle_conditions
from src.util.config import load_config, load_config_cli, save_config
from src.util.plot_tools import load_plots, PlotInfo, stream_plot_info
from tests.wallet_tools import WalletTool
def get_plot_dir():
cache_path = (
@ -83,7 +85,8 @@ class BlockTools:
self.keychain = Keychain("testing-1.8", True)
self.keychain.delete_all_keys()
self.keychain.add_private_key_seed(b"block_tools")
self.keychain.add_private_key_seed(b"block_tools farmer key")
self.keychain.add_private_key_seed(b"block_tools pool key")
plot_dir = get_plot_dir()
mkdir(plot_dir)
@ -126,7 +129,7 @@ class BlockTools:
str(plot_dir),
str(plot_dir),
str(plot_dir),
filename,
str(filename),
k,
stream_plot_info(
farmer_address, pool_address, farmer_pk, sk
@ -195,6 +198,14 @@ class BlockTools:
farm_share = farmer_sk.sign_insecure(challenge_hash)
return InsecureSignature.aggregate(harv_share, farm_share)
def get_farmer_wallet_tool(self):
esks = self.keychain.get_all_private_keys()
return WalletTool(esks[1][0])
def get_pool_wallet_tool(self):
esks = self.keychain.get_all_private_keys()
return WalletTool(esks[1][0])
def get_consecutive_blocks(
self,
test_constants: ConsensusConstants,
@ -464,7 +475,6 @@ class BlockTools:
selected_plot_info = None
selected_proof_index = 0
selected_quality: Optional[bytes] = None
selected_challenge_signature = None
best_quality = 0
plots = list(self.plots.values())
if self.use_any_pos:
@ -483,7 +493,6 @@ class BlockTools:
if len(qualities) > 0 and first_bits == 0:
selected_plot_info = plot_info
selected_quality = qualities[0]
selected_challenge_signature = challenge_signature
break
else:
for i in range(len(plots)):
@ -501,13 +510,11 @@ class BlockTools:
if qual_int > best_quality and first_bits == 0:
best_quality = qual_int
selected_quality = quality
selected_challenge_signature = challenge_signature
selected_plot_info = plot_info
selected_proof_index = j
j += 1
assert selected_plot_info is not None
assert selected_challenge_signature is not None
if selected_quality is None:
raise RuntimeError("No proofs for this challenge")
@ -523,7 +530,6 @@ class BlockTools:
selected_plot_info.farmer_address,
selected_plot_info.pool_address,
plot_pk,
selected_challenge_signature,
selected_plot_info.prover.get_size(),
proof_xs,
)

View File

@ -258,7 +258,6 @@ class TestBlockValidation:
blocks[9].proof_of_space.farmer_puzzle_hash,
blocks[9].proof_of_space.pool_puzzle_hash,
blocks[9].proof_of_space.plot_pubkey,
blocks[9].proof_of_space.challenge_signature,
blocks[9].proof_of_space.size,
bytes(bad_pos_proof),
)
@ -307,7 +306,6 @@ class TestBlockValidation:
blocks[9].proof_of_space.farmer_puzzle_hash,
blocks[9].proof_of_space.pool_puzzle_hash,
blocks[9].proof_of_space.plot_pubkey,
blocks[9].proof_of_space.challenge_signature,
blocks[9].proof_of_space.size,
bytes(bad_pos_proof),
)

View File

@ -45,14 +45,11 @@ class TestBlockchainTransactions:
@pytest.mark.asyncio
async def test_basic_blockchain_tx(self, two_nodes):
num_blocks = 10
wallet_a = WalletTool()
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
wallet_a = bt.get_farmer_wallet_tool()
wallet_receiver = WalletTool()
receiver_puzzlehash = wallet_receiver.get_new_puzzlehash()
blocks = bt.get_consecutive_blocks(
test_constants, num_blocks, [], 10, b"", coinbase_puzzlehash
)
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10, b"")
full_node_1, full_node_2, server_1, server_2 = two_nodes
for block in blocks:
@ -90,7 +87,7 @@ class TestBlockchainTransactions:
dic_h = {11: (program, aggsig)}
new_blocks = bt.get_consecutive_blocks(
test_constants, 1, blocks, 10, b"", coinbase_puzzlehash, dic_h
test_constants, 1, blocks, 10, b"", dic_h
)
next_block = new_blocks[11]

View File

@ -65,11 +65,8 @@ async def two_nodes():
async def wb(num_blocks, two_nodes):
full_node_1, _, _, _ = two_nodes
wallet_a = WalletTool()
coinbase_puzzlehash = wallet_a.get_new_puzzlehash()
wallet_receiver = WalletTool()
blocks = bt.get_consecutive_blocks(
test_constants, num_blocks, [], 10, reward_puzzlehash=coinbase_puzzlehash
)
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
for i in range(1, num_blocks):
async for _ in full_node_1.respond_block(fnp.RespondBlock(blocks[i])):
pass
@ -501,12 +498,7 @@ class TestFullNodeProtocol:
blocks_list = await get_block_path(full_node_1)
blocks_new = bt.get_consecutive_blocks(
test_constants,
1,
blocks_list[:],
4,
reward_puzzlehash=coinbase_puzzlehash,
seed=b"Another seed 4",
test_constants, 1, blocks_list[:], 4, seed=b"Another seed 4",
)
for block in blocks_new:
[_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))]
@ -518,7 +510,6 @@ class TestFullNodeProtocol:
1,
blocks_new[:],
4,
reward_puzzlehash=coinbase_puzzlehash,
seed=i.to_bytes(4, "big") + b"Another seed",
)
candidates.append(blocks_new_2[-1])
@ -621,11 +612,7 @@ class TestFullNodeProtocol:
# Don't propagate at old height
[_ async for _ in full_node_1.respond_block(fnp.RespondBlock(candidates[0]))]
blocks_new_3 = bt.get_consecutive_blocks(
test_constants,
1,
blocks_new[:] + [candidates[0]],
10,
reward_puzzlehash=coinbase_puzzlehash,
test_constants, 1, blocks_new[:] + [candidates[0]], 10,
)
unf_block_new = FullBlock(
blocks_new_3[-1].proof_of_space,

View File

@ -2,8 +2,7 @@ import asyncio
import pytest
from src.rpc.farmer_rpc_api import FarmerRpcApi
from src.rpc.harvester_rpc_api import HarvesterRpcApi
from secrets import token_bytes
from blspy import PrivateKey
from chiapos import DiskPlotter
from src.types.proof_of_space import ProofOfSpace
@ -12,6 +11,9 @@ from src.rpc.harvester_rpc_client import HarvesterRpcClient
from src.rpc.rpc_server import start_rpc_server
from src.util.ints import uint16
from src.util.config import load_config
from src.rpc.farmer_rpc_api import FarmerRpcApi
from src.rpc.harvester_rpc_api import HarvesterRpcApi
from tests.setup_nodes import setup_farmer_harvester, test_constants, bt
from tests.block_tools import get_plot_dir
from tests.time_out_assert import time_out_assert
@ -91,11 +93,6 @@ class TestRpc:
assert num_plots > 0
plot_dir = get_plot_dir() / "subdir"
plotter = DiskPlotter()
pool_pk = harvester.pool_pubkeys[0]
plot_sk = PrivateKey.from_seed(b"Farmer harvester rpc test seed")
plot_seed = ProofOfSpace.calculate_plot_seed(
pool_pk, plot_sk.get_public_key()
)
filename = "test_farmer_harvester_rpc_plot.plot"
plotter.create_plot_disk(
str(plot_dir),
@ -104,7 +101,7 @@ class TestRpc:
filename,
18,
b"genesis",
plot_seed,
token_bytes(32),
128,
)

View File

@ -24,13 +24,7 @@ class TestCostCalculation:
num_blocks = 2
bt = BlockTools()
blocks = bt.get_consecutive_blocks(
test_constants,
num_blocks,
[],
10,
reward_puzzlehash=wallet_tool.get_new_puzzlehash(),
)
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10,)
spend_bundle = wallet_tool.generate_signed_transaction(
blocks[1].get_coinbase().amount,

View File

@ -31,9 +31,7 @@ class TestFilter:
num_blocks = 2
ph = await wallet.get_new_puzzlehash()
bt = BlockTools()
blocks = bt.get_consecutive_blocks(
test_constants, num_blocks, [], 10, reward_puzzlehash=ph,
)
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
for i in range(1, num_blocks):
byte_array_tx: List[bytes] = []

View File

@ -21,13 +21,7 @@ class TestMerkleSet:
num_blocks = 10
bt = BlockTools()
blocks = bt.get_consecutive_blocks(
test_constants,
num_blocks,
[],
10,
reward_puzzlehash=wallet_tool.get_new_puzzlehash(),
)
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10,)
merkle_set = MerkleSet()
merkle_set_reverse = MerkleSet()

View File

@ -136,7 +136,7 @@ class TestWalletSimulator:
await time_out_assert(5, wallet.get_confirmed_balance, funds)
await full_node_1.reorg_from_index_to_new_index(
ReorgProtocol(uint32(5), uint32(num_blocks + 3), token_bytes())
ReorgProtocol(uint32(5), uint32(num_blocks + 3))
)
funds = sum(

View File

@ -37,11 +37,14 @@ class WalletTool:
next_address = 0
pubkey_num_lookup: Dict[str, int] = {}
def __init__(self):
def __init__(self, esk: Optional[ExtendedPrivateKey] = None):
self.current_balance = 0
self.my_utxos: set = set()
self.seed = urandom(1024)
self.extended_secret_key = ExtendedPrivateKey.from_seed(self.seed)
if esk is not None:
self.extended_secret_key = esk
else:
self.seed = urandom(1024)
self.extended_secret_key = ExtendedPrivateKey.from_seed(self.seed)
self.generator_lookups: Dict = {}
self.name = "MyChiaWallet"
self.puzzle_pk_cache: Dict = {}
@ -86,6 +89,14 @@ class WalletTool:
def puzzle_for_pk(self, pubkey):
return puzzle_for_pk(pubkey)
def get_first_puzzle(self):
pubkey = self.extended_secret_key.public_child(
self.next_address
).get_public_key()
puzzle = puzzle_for_pk(bytes(pubkey))
self.puzzle_pk_cache[puzzle.get_tree_hash()] = 0
return puzzle
def get_new_puzzle(self):
pubkey_a = self.get_next_public_key()
pubkey = bytes(pubkey_a)