Skip to content

Commit

Permalink
New aggregate signature conditions.
Browse files Browse the repository at this point in the history
  • Loading branch information
AmineKhaldi committed Jul 14, 2023
1 parent 344a7bb commit 8b76c90
Show file tree
Hide file tree
Showing 28 changed files with 527 additions and 246 deletions.
2 changes: 1 addition & 1 deletion chia/consensus/block_body_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ async def validate_block_body(
pairs_msgs: List[bytes] = []
if npc_result:
assert npc_result.conds is not None
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, constants.AGG_SIG_ME_ADDITIONAL_DATA)
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, constants.agg_sig_additional_data())

# 22. Verify aggregated signature
# TODO: move this to pre_validate_blocks_multiprocessing so we can sync faster
Expand Down
22 changes: 20 additions & 2 deletions chia/consensus/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import dataclasses
import logging
from typing import Any
from typing import Any, Dict

from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.condition_opcodes import ConditionOpcode
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint8, uint32, uint64, uint128

Expand Down Expand Up @@ -41,7 +42,13 @@ 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
# Forks of chia should change these values to provide replay attack protection
AGG_SIG_PARENT_ADDITIONAL_DATA: bytes
AGG_SIG_PUZZLE_ADDITIONAL_DATA: bytes
AGG_SIG_AMOUNT_ADDITIONAL_DATA: bytes
AGG_SIG_PUZZLE_AMOUNT_ADDITIONAL_DATA: bytes
AGG_SIG_PARENT_AMOUNT_ADDITIONAL_DATA: bytes
AGG_SIG_PARENT_PUZZLE_ADDITIONAL_DATA: bytes
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
Expand Down Expand Up @@ -99,3 +106,14 @@ def replace_str_to_bytes(self, **changes: Any) -> "ConsensusConstants":
filtered_changes[k] = v

return dataclasses.replace(self, **filtered_changes)

def agg_sig_additional_data(self) -> Dict[ConditionOpcode, bytes]:
return {
ConditionOpcode.AGG_SIG_PARENT: self.AGG_SIG_PARENT_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_PUZZLE: self.AGG_SIG_PUZZLE_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_AMOUNT: self.AGG_SIG_AMOUNT_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT: self.AGG_SIG_PUZZLE_AMOUNT_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_PARENT_AMOUNT: self.AGG_SIG_PARENT_AMOUNT_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_PARENT_PUZZLE: self.AGG_SIG_PARENT_PUZZLE_ADDITIONAL_DATA,
ConditionOpcode.AGG_SIG_ME: self.AGG_SIG_ME_ADDITIONAL_DATA,
}
14 changes: 13 additions & 1 deletion chia/consensus/default_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,19 @@
# 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
# Forks of chia should change these values to provide replay attack protection. This is set to mainnet genesis chall
"AGG_SIG_PARENT_ADDITIONAL_DATA": bytes.fromhex("baf5d69c647c91966170302d18521b0a85663433d161e72c826ed08677b53a74"),
"AGG_SIG_PUZZLE_ADDITIONAL_DATA": bytes.fromhex("284fa2ef486c7a41cc29fc99c9d08376161e93dd37817edb8219f42dca7592c4"),
"AGG_SIG_AMOUNT_ADDITIONAL_DATA": bytes.fromhex("cda186a9cd030f7a130fae45005e81cae7a90e0fa205b75f6aebc0d598e0348e"),
"AGG_SIG_PUZZLE_AMOUNT_ADDITIONAL_DATA": bytes.fromhex(
"0f7d90dff0613e6901e24dae59f1e690f18b8f5fbdcf1bb192ac9deaf7de22ad"
),
"AGG_SIG_PARENT_AMOUNT_ADDITIONAL_DATA": bytes.fromhex(
"585796bd90bb553c0430b87027ffee08d88aba0162c6e1abbbcc6b583f2ae7f9"
),
"AGG_SIG_PARENT_PUZZLE_ADDITIONAL_DATA": bytes.fromhex(
"2ebfdae17b29d83bae476a25ea06f0c4bd57298faddbbc3ec5ad29b9b86ce5df"
),
"AGG_SIG_ME_ADDITIONAL_DATA": bytes.fromhex("ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb"),
"GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bytes.fromhex(
"d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc"
Expand Down
2 changes: 1 addition & 1 deletion chia/consensus/multiprocess_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def batch_pre_validate_blocks(
if validate_signatures:
if npc_result is not None and block.transactions_info is not None:
assert npc_result.conds
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, constants.AGG_SIG_ME_ADDITIONAL_DATA)
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, constants.agg_sig_additional_data())
# Using AugSchemeMPL.aggregate_verify, so it's safe to use from_bytes_unchecked
pks_objects: List[G1Element] = [G1Element.from_bytes_unchecked(pk) for pk in pairs_pks]
if not AugSchemeMPL.aggregate_verify(
Expand Down
2 changes: 1 addition & 1 deletion chia/data_layer/data_layer_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,7 @@ async def sign(self, coin_spend: CoinSpend) -> SpendBundle:
return await sign_coin_spends(
[coin_spend],
self.standard_wallet.secret_key_store.secret_key_for_public_key,
self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA,
self.wallet_state_manager.constants.agg_sig_additional_data(),
self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM,
)

Expand Down
2 changes: 1 addition & 1 deletion chia/full_node/full_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1854,7 +1854,7 @@ async def add_unfinished_block(
# blockchain.run_generator throws on errors, so npc_result is
# guaranteed to represent a successful run
assert npc_result.conds is not None
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, self.constants.AGG_SIG_ME_ADDITIONAL_DATA)
pairs_pks, pairs_msgs = pkm_pairs(npc_result.conds, self.constants.agg_sig_additional_data())
if not cached_bls.aggregate_verify(
pairs_pks, pairs_msgs, block.transactions_info.aggregated_signature, True
):
Expand Down
4 changes: 1 addition & 3 deletions chia/full_node/mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ def validate_clvm_and_signature(
the NPCResult and a cache of the new pairings validated (if not error)
"""

additional_data = constants.AGG_SIG_ME_ADDITIONAL_DATA

try:
bundle: SpendBundle = SpendBundle.from_bytes(spend_bundle_bytes)
program = simple_solution_generator(bundle)
Expand All @@ -76,7 +74,7 @@ def validate_clvm_and_signature(
pks: List[bytes48] = []
msgs: List[bytes] = []
assert result.conds is not None
pks, msgs = pkm_pairs(result.conds, additional_data)
pks, msgs = pkm_pairs(result.conds, constants.agg_sig_additional_data())

# Verify aggregated signature
cache: LRUCache[bytes32, GTElement] = LRUCache(10000)
Expand Down
2 changes: 1 addition & 1 deletion chia/pools/pool_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ async def pk_to_sk(pk: G1Element) -> PrivateKey:
return await sign_coin_spends(
[coin_spend],
pk_to_sk,
self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA,
self.wallet_state_manager.constants.agg_sig_additional_data(),
self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM,
)

Expand Down
9 changes: 9 additions & 0 deletions chia/simulator/block_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,15 @@ def compute_cost_test(
elif hard_fork and condition == ConditionOpcode.SOFTFORK.value:
arg = cond.rest().first().as_int()
condition_cost += arg * 10000
elif hard_fork and condition in [
ConditionOpcode.AGG_SIG_PARENT,
ConditionOpcode.AGG_SIG_PUZZLE,
ConditionOpcode.AGG_SIG_AMOUNT,
ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
]:
condition_cost += ConditionCost.AGG_SIG.value
return None, uint64(clvm_cost + size_cost + condition_cost)
except Exception:
return uint16(Err.GENERATOR_RUNTIME_ERROR.value), uint64(0)
Expand Down
49 changes: 49 additions & 0 deletions chia/simulator/wallet_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,55 @@ def sign_transaction(self, coin_spends: List[CoinSpend]) -> SpendBundle:
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_PARENT, []):
msg = (
cwa.vars[1]
+ bytes(coin_spend.coin.parent_coin_info)
+ self.constants.AGG_SIG_PARENT_ADDITIONAL_DATA
)
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_PUZZLE, []):
msg = cwa.vars[1] + bytes(coin_spend.coin.puzzle_hash) + self.constants.AGG_SIG_PUZZLE_ADDITIONAL_DATA
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_AMOUNT, []):
msg = cwa.vars[1] + int_to_bytes(coin_spend.coin.amount) + self.constants.AGG_SIG_AMOUNT_ADDITIONAL_DATA
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT, []):
msg = (
cwa.vars[1]
+ bytes(coin_spend.coin.puzzle_hash)
+ int_to_bytes(coin_spend.coin.amount)
+ self.constants.AGG_SIG_PUZZLE_AMOUNT_ADDITIONAL_DATA
)
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_PARENT_AMOUNT, []):
msg = (
cwa.vars[1]
+ bytes(coin_spend.coin.parent_coin_info)
+ int_to_bytes(coin_spend.coin.amount)
+ self.constants.AGG_SIG_PARENT_AMOUNT_ADDITIONAL_DATA
)
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_PARENT_PUZZLE, []):
msg = (
cwa.vars[1]
+ bytes(coin_spend.coin.parent_coin_info)
+ bytes(coin_spend.coin.puzzle_hash)
+ self.constants.AGG_SIG_PARENT_PUZZLE_ADDITIONAL_DATA
)
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)

for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
msg = cwa.vars[1] + bytes(coin_spend.coin.name()) + self.constants.AGG_SIG_ME_ADDITIONAL_DATA
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
Expand Down
11 changes: 10 additions & 1 deletion chia/types/coin_spend.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ def compute_additions_with_cost(
raise ValidationError(Err.BLOCK_COST_EXCEEDS_MAX, "compute_additions() for CoinSpend")
atoms = cond.as_iter()
op = next(atoms).atom
if op in [ConditionOpcode.AGG_SIG_ME, ConditionOpcode.AGG_SIG_UNSAFE]:
if op in [
ConditionOpcode.AGG_SIG_PARENT,
ConditionOpcode.AGG_SIG_PUZZLE,
ConditionOpcode.AGG_SIG_AMOUNT,
ConditionOpcode.AGG_SIG_PUZZLE_AMOUNT,
ConditionOpcode.AGG_SIG_PARENT_AMOUNT,
ConditionOpcode.AGG_SIG_PARENT_PUZZLE,
ConditionOpcode.AGG_SIG_UNSAFE,
ConditionOpcode.AGG_SIG_ME,
]:
cost += ConditionCost.AGG_SIG.value
continue
if op != ConditionOpcode.CREATE_COIN.value:
Expand Down
6 changes: 6 additions & 0 deletions chia/types/condition_opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ class ConditionOpcode(bytes, enum.Enum):

# the conditions below require bls12-381 signatures

AGG_SIG_PARENT = bytes([43])
AGG_SIG_PUZZLE = bytes([44])
AGG_SIG_AMOUNT = bytes([45])
AGG_SIG_PUZZLE_AMOUNT = bytes([46])
AGG_SIG_PARENT_AMOUNT = bytes([47])
AGG_SIG_PARENT_PUZZLE = bytes([48])
AGG_SIG_UNSAFE = bytes([49])
AGG_SIG_ME = bytes([50])

Expand Down
5 changes: 4 additions & 1 deletion chia/types/spend_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from chia.consensus.default_constants import DEFAULT_CONSTANTS
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.condition_opcodes import ConditionOpcode
from chia.util.errors import Err, ValidationError
from chia.util.streamable import Streamable, recurse_jsonify, streamable, streamable_from_dict
from chia.wallet.util.debug_spend_bundle import debug_spend_bundle
Expand Down Expand Up @@ -68,7 +69,9 @@ def fees(self) -> int:
def name(self) -> bytes32:
return self.get_hash()

def debug(self, agg_sig_additional_data: bytes = DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA) -> None:
def debug(
self, agg_sig_additional_data: Dict[ConditionOpcode, bytes] = DEFAULT_CONSTANTS.agg_sig_additional_data()
) -> None:
debug_spend_bundle(self, agg_sig_additional_data)

# TODO: this should be removed
Expand Down
Loading

0 comments on commit 8b76c90

Please sign in to comment.