Implement generator_for_single_coin() in python instead of clvm and apply quex' optimization to only computing the puzzle hash if the parent and amount matches (#13237)

This commit is contained in:
Arvid Norberg 2022-08-30 23:30:34 +02:00 committed by GitHub
parent 18c40d6269
commit 163552b8d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 44 additions and 74 deletions

View File

@ -382,11 +382,7 @@ class SimClient:
coin_record = await self.service.mempool_manager.coin_store.get_coin_record( # type: ignore[assignment]
coin_id,
)
error, puzzle, solution = get_puzzle_and_solution_for_coin(
generator,
coin_id,
self.service.defaults.MAX_BLOCK_COST_CLVM,
)
error, puzzle, solution = get_puzzle_and_solution_for_coin(generator, coin_record.coin)
if error:
return None
else:

View File

@ -1286,9 +1286,7 @@ class FullNodeAPI:
block_generator: Optional[BlockGenerator] = await self.full_node.blockchain.get_block_generator(block)
assert block_generator is not None
error, puzzle, solution = get_puzzle_and_solution_for_coin(
block_generator, coin_name, self.full_node.constants.MAX_BLOCK_COST_CLVM
)
error, puzzle, solution = get_puzzle_and_solution_for_coin(block_generator, coin_record.coin)
if error is not None:
return reject_msg

View File

@ -1,20 +1,25 @@
import logging
from typing import Dict, Optional
from typing import Dict, Optional, Tuple
from chia_rs import MEMPOOL_MODE, NO_NEG_DIV
from chia.types.blockchain_format.coin import Coin
from chia.consensus.cost_calculator import NPCResult
from chia.types.spend_bundle_conditions import SpendBundleConditions
from chia.full_node.generator import create_generator_args, setup_generator_args
from chia.full_node.generator import setup_generator_args
from chia.types.coin_record import CoinRecord
from chia.types.generator_types import BlockGenerator
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.errors import Err
from chia.util.ints import uint32, uint64, uint16
from chia.wallet.puzzles.generator_loader import GENERATOR_FOR_SINGLE_COIN_MOD
from chia.wallet.puzzles.rom_bootstrap_generator import get_generator
from chia.types.blockchain_format.program import Program
from chia.wallet.puzzles.load_clvm import load_serialized_clvm
from chia.consensus.default_constants import DEFAULT_CONSTANTS
GENERATOR_MOD = get_generator()
DESERIALIZE_MOD = load_serialized_clvm("chialisp_deserialisation.clvm", package_or_requirement="chia.wallet.puzzles")
log = logging.getLogger(__name__)
@ -55,17 +60,26 @@ def get_name_puzzle_conditions(
return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), None, uint64(0))
def get_puzzle_and_solution_for_coin(generator: BlockGenerator, coin_name: bytes, max_cost: int):
def get_puzzle_and_solution_for_coin(
generator: BlockGenerator, coin: Coin
) -> Tuple[Optional[Exception], Optional[Program], Optional[Program]]:
try:
block_program = generator.program
block_program_args = create_generator_args(generator.generator_refs)
args = [bytes(a) for a in generator.generator_refs]
cost, result = generator.program.run_with_cost(DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, DESERIALIZE_MOD, args)
cost, result = GENERATOR_FOR_SINGLE_COIN_MOD.run_with_cost(
max_cost, block_program, block_program_args, coin_name
)
puzzle = result.first()
solution = result.rest().first()
return None, puzzle, solution
requested_parent = Program.to(coin.parent_coin_info)
requested_amount = Program.to(coin.amount)
coin_spends = result.first()
for spend in coin_spends.as_iter():
parent, puzzle, amount, solution = spend.as_iter()
if (
parent == requested_parent
and amount == requested_amount
and Program.to(puzzle).get_tree_hash() == coin.puzzle_hash
):
return None, Program.to(puzzle), Program.to(solution)
return ValueError(f"coin not found {coin.name().hex()}"), None, None
except Exception as e:
return e, None, None

View File

@ -696,9 +696,7 @@ class FullNodeRpcApi:
block_generator: Optional[BlockGenerator] = await self.service.blockchain.get_block_generator(block)
assert block_generator is not None
error, puzzle, solution = get_puzzle_and_solution_for_coin(
block_generator, coin_name, self.service.constants.MAX_BLOCK_COST_CLVM
)
error, puzzle, solution = get_puzzle_and_solution_for_coin(block_generator, coin_record.coin)
if error is not None:
raise ValueError(f"Error: {error}")

View File

@ -1,35 +0,0 @@
(mod (block_program (block_ref) coinname)
(defconstant local_deserialize_mod
;; this monstrosity is the assembly output of `chialisp_deserialisation.clvm`
;; it's pasted in here because the compiler doesn't yet support nested `mod`
;; my apologies -- RK
(a (q 5 (a 62 (c 2 (c 5 ()))))
(c (q ((-1 . 127) -33 . -65) ((a (i (= 11 (q . -128)) (q 4 () (c 5 ())) (q 2 (i (>s 11 24) (q 2 26 (c 2 (c (a (i (>s 11 28) (q 2 (i (>s 11 20) (q 8) (q 4 (concat (logand (q . 31) 11) (substr 5 () (q . 1))) (c (substr 5 (q . 1)) ()))) 1) (q 4 (logand (q . 63) 11) (c 5 ()))) 1) ()))) (q 4 11 (c 5 ()))) 1)) 1) 4 (substr 21 () 9) (c (substr 21 9) ())) (c (c 5 19) (c 43 ())) (a 22 (c 2 (c 9 (c (a 62 (c 2 (c 21 ()))) ())))) 2 (i (= (substr 5 () (q . 1)) 16) (q 2 46 (c 2 (c (a 62 (c 2 (c (substr 5 (q . 1)) ()))) ()))) (q 2 18 (c 2 (c (substr 5 (q . 1)) (c (substr 5 () (q . 1)) ()))))) 1)
1))
)
; takes a lisp tree and returns the hash of it
(defun sha256tree1 (TREE)
(if (l TREE)
(sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
(sha256 1 TREE)))
(defun check_coin_spend ((parent puzzle amount solution) coinname)
(= (sha256 parent (sha256tree1 puzzle) amount) coinname)
)
(defun check_for_coinname (coin_spends coinname)
(if coin_spends
(if (check_coin_spend (f coin_spends) coinname)
(list (f (r (f coin_spends))) (f (r (r (r (f coin_spends))))))
(check_for_coinname (r coin_spends) coinname)
)
(x)
)
)
; main
(check_for_coinname (f (a block_program (list local_deserialize_mod block_ref))) coinname)
)

View File

@ -1 +0,0 @@
ff02ffff01ff02ff0cffff04ff02ffff04ffff05ffff02ff05ffff04ff0affff04ff13ff8080808080ffff04ff17ff8080808080ffff04ffff01ffffff09ffff0bff09ffff02ff0effff04ff02ffff04ff15ff80808080ff2d80ff0b80ff02ffff03ff05ffff01ff02ffff03ffff02ff08ffff04ff02ffff04ff09ffff04ff0bff8080808080ffff01ff04ff29ffff04ff81b9ff808080ffff01ff02ff0cffff04ff02ffff04ff0dffff04ff0bff808080808080ff0180ffff01ff088080ff0180ffff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080

View File

@ -1 +0,0 @@
9eb0d58814fff6aec8e9eb9522c08a68d8f004bf1506e6c98388758beee2f80e

View File

@ -1,3 +0,0 @@
from chia.wallet.puzzles.load_clvm import load_serialized_clvm
GENERATOR_FOR_SINGLE_COIN_MOD = load_serialized_clvm("generator_for_single_coin.clvm", package_or_requirement=__name__)

View File

@ -13,7 +13,6 @@ wallet_program_files = set(
"chia/wallet/puzzles/cat_v2.clvm",
"chia/wallet/puzzles/chialisp_deserialisation.clvm",
"chia/wallet/puzzles/rom_bootstrap_generator.clvm",
"chia/wallet/puzzles/generator_for_single_coin.clvm",
"chia/wallet/puzzles/lock.inner.puzzle.clvm",
"chia/wallet/puzzles/p2_conditions.clvm",
"chia/wallet/puzzles/p2_delegated_conditions.clvm",

View File

@ -9,7 +9,9 @@ from chia.consensus.condition_costs import ConditionCost
from chia.consensus.cost_calculator import NPCResult
from chia.full_node.bundle_tools import simple_solution_generator
from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions, get_puzzle_and_solution_for_coin
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program, SerializedProgram
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.generator_types import BlockGenerator
from chia.wallet.puzzles import p2_delegated_puzzle_or_hidden_puzzle
from tests.setup_nodes import test_constants
@ -79,11 +81,12 @@ class TestCostCalculation:
assert npc_result.error is None
assert len(bytes(program.program)) == 433
coin_name = npc_result.conds.spends[0].coin_id
error, puzzle, solution = get_puzzle_and_solution_for_coin(
program, coin_name, test_constants.MAX_BLOCK_COST_CLVM
)
coin_spend = spend_bundle.coin_spends[0]
assert coin_spend.coin.name() == npc_result.conds.spends[0].coin_id
error, puzzle, solution = get_puzzle_and_solution_for_coin(program, coin_spend.coin)
assert error is None
assert puzzle == coin_spend.puzzle_reveal.to_program()
assert solution == coin_spend.solution.to_program()
assert npc_result.conds.cost == ConditionCost.CREATE_COIN.value + ConditionCost.AGG_SIG.value + 404560
@ -155,10 +158,12 @@ class TestCostCalculation:
)
assert npc_result.error is None
coin_name = npc_result.conds.spends[0].coin_id
error, puzzle, solution = get_puzzle_and_solution_for_coin(
generator, coin_name, test_constants.MAX_BLOCK_COST_CLVM
coin = Coin(
bytes32.fromhex("3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa"),
bytes32.fromhex("14947eb0e69ee8fc8279190fc2d38cb4bbb61ba28f1a270cfd643a0e8d759576"),
300,
)
error, puzzle, solution = get_puzzle_and_solution_for_coin(generator, coin)
assert error is None
@pytest.mark.asyncio

View File

@ -146,14 +146,14 @@ class TestCompression(TestCase):
start, end = match_standard_transaction_at_any_index(original_generator)
ca = CompressorArg(uint32(0), SerializedProgram.from_bytes(original_generator), start, end)
c = compressed_spend_bundle_solution(ca, sb)
removal = sb.coin_spends[0].coin.name()
error, puzzle, solution = get_puzzle_and_solution_for_coin(c, removal, INFINITE_COST)
removal = sb.coin_spends[0].coin
error, puzzle, solution = get_puzzle_and_solution_for_coin(c, removal)
assert error is None
assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
assert bytes(solution) == bytes(sb.coin_spends[0].solution)
# Test non compressed generator as well
s = simple_solution_generator(sb)
error, puzzle, solution = get_puzzle_and_solution_for_coin(s, removal, INFINITE_COST)
error, puzzle, solution = get_puzzle_and_solution_for_coin(s, removal)
assert error is None
assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
assert bytes(solution) == bytes(sb.coin_spends[0].solution)