Ms.aggsig me challenge (#1843)

* Start aggsig me

* Pass in genesis challenge

* Update DID, and remove useless functions from coinbase.py

* Unused imports

* Revert GUI

* Use a different constant so forks of chia can easily change it
This commit is contained in:
Mariano Sorgente 2021-04-14 14:03:14 +09:00 committed by GitHub
parent 0d8cfa1cff
commit a2ca057b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 84 additions and 71 deletions

View File

@ -380,7 +380,9 @@ async def validate_block_body(
)
if error:
return error, None
for pk, m in pkm_pairs_for_conditions_dict(npc.condition_dict, npc.coin_name):
for pk, m in pkm_pairs_for_conditions_dict(
npc.condition_dict, npc.coin_name, constants.AGG_SIG_ME_ADDITIONAL_DATA
):
pairs_pks.append(pk)
pairs_msgs.append(m)

View File

@ -1,4 +1,4 @@
from blspy import AugSchemeMPL, G1Element, G2Element, PrivateKey
from blspy import G1Element
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
@ -7,18 +7,7 @@ from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_for_
def create_puzzlehash_for_pk(pub_key: G1Element) -> bytes32:
return puzzle_for_pk(bytes(pub_key)).get_tree_hash()
def signature_for_coinbase(coin: Coin, pool_private_key: PrivateKey):
# noinspection PyTypeChecker
return G2Element.from_bytes(bytes(AugSchemeMPL.sign(pool_private_key, bytes(coin))))
def sign_coinbase_coin(coin: Coin, private_key: PrivateKey):
if private_key is None:
raise ValueError("unknown private key")
return signature_for_coinbase(coin, private_key)
return puzzle_for_pk(pub_key).get_tree_hash()
def pool_parent_id(block_height: uint32, genesis_challenge: bytes32) -> uint32:

View File

@ -34,6 +34,8 @@ class ConsensusConstants:
# Used as the initial cc rc challenges, as well as first block back pointers, and first SES back pointer
# We override this value based on the chain being run (testnet0, testnet1, mainnet, etc)
GENESIS_CHALLENGE: bytes32
# Forks of chia should change this value to provide replay attack protection
AGG_SIG_ME_ADDITIONAL_DATA: bytes
GENESIS_PRE_FARM_POOL_PUZZLE_HASH: bytes32 # The block at height must pay out to this pool puzzle hash
GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: bytes32 # The block at height must pay out to this farmer puzzle hash
MAX_VDF_WITNESS_SIZE: int # The maximum number of classgroup elements within an n-wesolowski proof

View File

@ -29,6 +29,8 @@ testnet_kwargs = {
# We override this value based on the chain being run (testnet0, testnet1, mainnet, etc)
# Default used for tests is std_hash(b'')
"GENESIS_CHALLENGE": bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
# Forks of chia should change this value to provide replay attack protection. This is set to mainnet genesis chall
"AGG_SIG_ME_ADDITIONAL_DATA": bytes.fromhex("ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb"),
"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bytes.fromhex(
"d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc"
),

View File

@ -333,7 +333,9 @@ class MempoolManager:
break
if validate_signature:
for pk, message in pkm_pairs_for_conditions_dict(npc.condition_dict, npc.coin_name):
for pk, message in pkm_pairs_for_conditions_dict(
npc.condition_dict, npc.coin_name, self.constants.AGG_SIG_ME_ADDITIONAL_DATA
):
pks.append(pk)
msgs.append(message)
if error:

View File

@ -251,10 +251,10 @@ class BlockTools:
raise ValueError(f"Do not have key {pool_pk}")
def get_farmer_wallet_tool(self) -> WalletTool:
return WalletTool(self.farmer_master_sk)
return WalletTool(self.constants, self.farmer_master_sk)
def get_pool_wallet_tool(self) -> WalletTool:
return WalletTool(self.pool_master_sk)
return WalletTool(self.constants, self.pool_master_sk)
def get_consecutive_blocks(
self,

View File

@ -68,30 +68,23 @@ def conditions_by_opcode(
def pkm_pairs_for_conditions_dict(
conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
coin_name: bytes32,
conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]], coin_name: bytes32, additional_data: bytes
) -> List[Tuple[G1Element, bytes]]:
assert coin_name is not None
ret: List[Tuple[G1Element, bytes]] = []
for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
# TODO: check types
# assert len(_) == 3
assert cvp.vars[1] is not None
ret.append((G1Element.from_bytes(cvp.vars[0]), cvp.vars[1]))
if coin_name is not None:
for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
ret.append((G1Element.from_bytes(cvp.vars[0]), cvp.vars[1] + coin_name))
for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
assert len(cwa.vars) == 2
assert cwa.vars[0] is not None and cwa.vars[1] is not None
ret.append((G1Element.from_bytes(cwa.vars[0]), cwa.vars[1]))
for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
assert len(cwa.vars) == 2
assert cwa.vars[0] is not None and cwa.vars[1] is not None
ret.append((G1Element.from_bytes(cwa.vars[0]), cwa.vars[1] + coin_name + additional_data))
return ret
def aggsig_in_conditions_dict(
conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]]
) -> List[ConditionWithArgs]:
agg_sig_conditions = []
for _ in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
agg_sig_conditions.append(_)
return agg_sig_conditions
def created_outputs_for_conditions_dict(
conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
input_coin_name: bytes32,

View File

@ -2,6 +2,7 @@ from typing import Dict, List, Optional
from blspy import AugSchemeMPL, G2Element, PrivateKey
from chia.consensus.constants import ConsensusConstants
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
@ -42,7 +43,8 @@ class WalletTool:
next_address = 0
pubkey_num_lookup: Dict[bytes, uint32] = {}
def __init__(self, sk: Optional[PrivateKey] = None):
def __init__(self, constants: ConsensusConstants, sk: Optional[PrivateKey] = None):
self.constants = constants
self.current_balance = 0
self.my_utxos: set = set()
if sk is not None:
@ -168,7 +170,9 @@ class WalletTool:
raise ValueError(err)
conditions_dict = conditions_by_opcode(con)
for _, msg in pkm_pairs_for_conditions_dict(conditions_dict, bytes(coin_solution.coin.name())):
for _, msg in pkm_pairs_for_conditions_dict(
conditions_dict, bytes(coin_solution.coin.name()), self.constants.AGG_SIG_ME_ADDITIONAL_DATA
):
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)
aggsig = AugSchemeMPL.aggregate(signatures)

View File

@ -530,14 +530,16 @@ class CCWallet:
self.log.info(f"Successfully selected coins: {used_coins}")
return used_coins
async def get_sigs(self, innerpuz: Program, innersol: Program, coin_name) -> List[G2Element]:
async def get_sigs(self, innerpuz: Program, innersol: Program, coin_name: bytes32) -> List[G2Element]:
puzzle_hash = innerpuz.get_tree_hash()
pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash)
synthetic_secret_key = calculate_synthetic_secret_key(private, DEFAULT_HIDDEN_PUZZLE_HASH)
sigs: List[G2Element] = []
error, conditions, cost = conditions_dict_for_solution(innerpuz, innersol)
if conditions is not None:
for _, msg in pkm_pairs_for_conditions_dict(conditions, coin_name):
for _, msg in pkm_pairs_for_conditions_dict(
conditions, coin_name, self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
):
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
sigs.append(signature)
return sigs

View File

@ -68,7 +68,7 @@ def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
if error:
print(f"*** error {error}")
elif conditions is not None:
for pk, m in pkm_pairs_for_conditions_dict(conditions, coin_name):
for pk, m in pkm_pairs_for_conditions_dict(conditions, coin_name, bytes([3] * 32)):
pks.append(pk)
msgs.append(m)
print()

View File

@ -459,7 +459,7 @@ class DIDWallet:
)
list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)]
# sign for AGG_SIG_ME
message = bytes(puzhash) + bytes(coin.name())
message = puzhash + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)
@ -527,7 +527,7 @@ class DIDWallet:
message_spend_bundle = SpendBundle([message_spend], AugSchemeMPL.aggregate([]))
# sign for AGG_SIG_ME
message = bytes(innermessage) + bytes(coin.name())
message = innermessage + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)
@ -792,7 +792,7 @@ class DIDWallet:
fullsol = Program.to([coin.parent_coin_info, coin.amount, innersol])
list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)]
# sign for AGG_SIG_ME
message = bytes(coin.puzzle_hash) + bytes(coin.name())
message = coin.puzzle_hash + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)

View File

@ -1,5 +1,6 @@
from typing import Callable, List, Optional
import blspy
from blspy import AugSchemeMPL, PrivateKey
from chia.types.coin_solution import CoinSolution
@ -9,11 +10,12 @@ from chia.util.condition_tools import conditions_dict_for_solution, pkm_pairs_fo
async def sign_coin_solutions(
coin_solutions: List[CoinSolution],
secret_key_for_public_key_f: Callable[[bytes], Optional[PrivateKey]],
secret_key_for_public_key_f: Callable[[blspy.G1Element], Optional[PrivateKey]],
additional_data: bytes,
) -> SpendBundle:
signatures = []
pk_list = []
msg_list = []
signatures: List[blspy.G2Element] = []
pk_list: List[blspy.G1Element] = []
msg_list: List[bytes] = []
for coin_solution in coin_solutions:
# Get AGG_SIG conditions
err, conditions_dict, cost = conditions_dict_for_solution(coin_solution.puzzle_reveal, coin_solution.solution)
@ -22,16 +24,18 @@ async def sign_coin_solutions(
raise ValueError(error_msg)
# Create signature
for _, msg in pkm_pairs_for_conditions_dict(conditions_dict, bytes(coin_solution.coin.name())):
pk_list.append(_)
for pk, msg in pkm_pairs_for_conditions_dict(
conditions_dict, bytes(coin_solution.coin.name()), additional_data
):
pk_list.append(pk)
msg_list.append(msg)
secret_key = secret_key_for_public_key_f(_)
secret_key = secret_key_for_public_key_f(pk)
if secret_key is None:
e_msg = f"no secret key for {_}"
e_msg = f"no secret key for {pk}"
raise ValueError(e_msg)
assert bytes(secret_key.get_g1()) == bytes(_)
assert bytes(secret_key.get_g1()) == bytes(pk)
signature = AugSchemeMPL.sign(secret_key, msg)
assert AugSchemeMPL.verify(_, msg, signature)
assert AugSchemeMPL.verify(pk, msg, signature)
signatures.append(signature)
# Aggregate signatures

View File

@ -328,7 +328,11 @@ class Wallet:
return spends
async def sign_transaction(self, coin_solutions: List[CoinSolution]) -> SpendBundle:
return await sign_coin_solutions(coin_solutions, self.secret_key_store.secret_key_for_public_key)
return await sign_coin_solutions(
coin_solutions,
self.secret_key_store.secret_key_for_public_key,
self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA,
)
async def generate_signed_transaction(
self,
@ -354,7 +358,9 @@ class Wallet:
self.log.info("About to sign a transaction")
await self.hack_populate_secret_keys_for_coin_solutions(transaction)
spend_bundle: SpendBundle = await sign_coin_solutions(
transaction, self.secret_key_store.secret_key_for_public_key
transaction,
self.secret_key_store.secret_key_for_public_key,
self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA,
)
now = uint64(int(time.time()))
@ -414,5 +420,9 @@ class Wallet:
list_of_solutions.append(CoinSolution(coin, puzzle, solution))
await self.hack_populate_secret_keys_for_coin_solutions(list_of_solutions)
spend_bundle = await sign_coin_solutions(list_of_solutions, self.secret_key_store.secret_key_for_public_key)
spend_bundle = await sign_coin_solutions(
list_of_solutions,
self.secret_key_store.secret_key_for_public_key,
self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA,
)
return spend_bundle

View File

@ -1621,7 +1621,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_reorg_from_genesis(self, empty_blockchain):
b = empty_blockchain
WALLET_A = WalletTool()
WALLET_A = WalletTool(b.constants)
WALLET_A_PUZZLE_HASHES = [WALLET_A.get_new_puzzlehash() for _ in range(5)]
blocks = bt.get_consecutive_blocks(15)
@ -1660,7 +1660,7 @@ class TestReorgs:
@pytest.mark.asyncio
async def test_reorg_transaction(self, empty_blockchain):
b = empty_blockchain
wallet_a = WalletTool()
wallet_a = WalletTool(b.constants)
WALLET_A_PUZZLE_HASHES = [wallet_a.get_new_puzzlehash() for _ in range(5)]
coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
receiver_puzzlehash = WALLET_A_PUZZLE_HASHES[1]

View File

@ -18,7 +18,7 @@ from tests.setup_nodes import bt, setup_two_nodes, test_constants
BURN_PUZZLE_HASH = b"0" * 32
WALLET_A = WalletTool()
WALLET_A = WalletTool(test_constants)
WALLET_A_PUZZLE_HASHES = [WALLET_A.get_new_puzzlehash() for _ in range(5)]
log = logging.getLogger(__name__)

View File

@ -83,7 +83,7 @@ def do_test_spend(
# make sure we can actually sign the solution
signatures = []
for coin_solution in spend_bundle.coin_solutions:
signature = key_lookup.signature_for_solution(coin_solution)
signature = key_lookup.signature_for_solution(coin_solution, bytes([2] * 32))
signatures.append(signature)
return SpendBundle(spend_bundle.coin_solutions, AugSchemeMPL.aggregate(signatures))

View File

@ -18,8 +18,6 @@ from chia.util.ints import uint64
from chia.util.wallet_tools import WalletTool
from tests.setup_nodes import bt, test_constants
WALLET_A = WalletTool()
@pytest.fixture(scope="module")
def event_loop():
@ -29,6 +27,8 @@ def event_loop():
constants = test_constants
WALLET_A = WalletTool(constants)
def get_future_reward_coins(block: FullBlock) -> Tuple[Coin, Coin]:
pool_amount = calculate_pool_reward(block.height)

View File

@ -67,7 +67,7 @@ async def wallet_nodes():
server_1 = full_node_1.full_node.server
server_2 = full_node_2.full_node.server
wallet_a = bt.get_pool_wallet_tool()
wallet_receiver = WalletTool()
wallet_receiver = WalletTool(full_node_1.full_node.constants)
yield full_node_1, full_node_2, server_1, server_2, wallet_a, wallet_receiver
async for _ in async_gen:
@ -95,7 +95,7 @@ async def wallet_nodes_mainnet():
server_1 = full_node_1.full_node.server
server_2 = full_node_2.full_node.server
wallet_a = bt.get_pool_wallet_tool()
wallet_receiver = WalletTool()
wallet_receiver = WalletTool(full_node_1.full_node.constants)
yield full_node_1, full_node_2, server_1, server_2, wallet_a, wallet_receiver
async for _ in async_gen:

View File

@ -109,8 +109,8 @@ class TestRpc:
additions, removals = await client.get_additions_and_removals(blocks[-1].header_hash)
assert len(additions) >= 2 and len(removals) == 0
wallet = WalletTool()
wallet_receiver = WalletTool(AugSchemeMPL.key_gen(std_hash(b"123123")))
wallet = WalletTool(full_node_api_1.full_node.constants)
wallet_receiver = WalletTool(full_node_api_1.full_node.constants, AugSchemeMPL.key_gen(std_hash(b"123123")))
ph = wallet.get_new_puzzlehash()
ph_2 = wallet.get_new_puzzlehash()
ph_receiver = wallet_receiver.get_new_puzzlehash()

View File

@ -4,6 +4,7 @@ from secrets import token_bytes
from blspy import AugSchemeMPL, PrivateKey
from clvm_tools import binutils
from chia.consensus.default_constants import DEFAULT_CONSTANTS
from chia.types.blockchain_format.program import Program
from chia.types.condition_opcodes import ConditionOpcode
from chia.types.condition_with_args import ConditionWithArgs
@ -101,7 +102,7 @@ if __name__ == "__main__":
Naive way to calculate cost ratio between vByte and CLVM cost unit.
AggSig has assigned cost of 20vBytes, simple CLVM program is benchmarked against it.
"""
wallet_tool = WalletTool()
wallet_tool = WalletTool(DEFAULT_CONSTANTS)
benchmark_all_operators()
secret_key: PrivateKey = AugSchemeMPL.key_gen(bytes([2] * 32))
puzzles = []

View File

@ -24,12 +24,14 @@ class KeyTool(dict):
bls_private_key = PrivateKey.from_bytes(secret_exponent.to_bytes(32, "big"))
return AugSchemeMPL.sign(bls_private_key, message_hash)
def signature_for_solution(self, coin_solution: CoinSolution) -> AugSchemeMPL:
def signature_for_solution(self, coin_solution: CoinSolution, additional_data: bytes) -> AugSchemeMPL:
signatures = []
err, conditions, cost = conditions_for_solution(coin_solution.puzzle_reveal, coin_solution.solution)
assert conditions is not None
conditions_dict = conditions_by_opcode(conditions)
for public_key, message_hash in pkm_pairs_for_conditions_dict(conditions_dict, coin_solution.coin.name()):
for public_key, message_hash in pkm_pairs_for_conditions_dict(
conditions_dict, coin_solution.coin.name(), additional_data
):
signature = self.sign(bytes(public_key), message_hash)
signatures.append(signature)
return AugSchemeMPL.aggregate(signatures)

View File

@ -579,7 +579,7 @@ class TestDIDWallet:
list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)]
# sign for AGG_SIG_ME
message = bytes(coin.puzzle_hash) + bytes(coin.name())
message = coin.puzzle_hash + coin.name() + did_wallet.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await did_wallet.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(did_wallet.wallet_state_manager.private_key, index)