Leave programs in SpendBundle serialized (#2380)

This commit is contained in:
Arvid Norberg 2021-04-27 19:52:21 +02:00 committed by GitHub
parent a2401ccfd4
commit 8717ca4b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 75 additions and 43 deletions

View File

@ -1,5 +1,5 @@
import re
from typing import Optional, Tuple, List, Any
from typing import Optional, Tuple, List, Union
from clvm import SExp
from clvm_tools import binutils
@ -10,19 +10,19 @@ from chia.types.coin_solution import CoinSolution
from chia.types.generator_types import BlockGenerator, CompressorArg
from chia.types.spend_bundle import SpendBundle
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint32
from chia.util.ints import uint32, uint64
def spend_bundle_to_coin_solution_entry_list(bundle: SpendBundle) -> List[Any]:
r = []
def spend_bundle_to_serialized_coin_solution_entry_list(bundle: SpendBundle) -> bytes:
r = b""
for coin_solution in bundle.coin_solutions:
entry = [
coin_solution.coin.parent_coin_info,
coin_solution.puzzle_reveal,
coin_solution.coin.amount,
coin_solution.solution,
]
r.append(entry)
r += b"\xff"
r += b"\xff" + SExp.to(coin_solution.coin.parent_coin_info).as_bin()
r += b"\xff" + bytes(coin_solution.puzzle_reveal)
r += b"\xff" + SExp.to(coin_solution.coin.amount).as_bin()
r += b"\xff" + bytes(coin_solution.solution)
r += b"\x80"
r += b"\x80"
return r
@ -30,10 +30,14 @@ def simple_solution_generator(bundle: SpendBundle) -> BlockGenerator:
"""
Simply quotes the solutions we know.
"""
cse_list = spend_bundle_to_coin_solution_entry_list(bundle)
block_program = SerializedProgram.from_bytes(SExp.to((binutils.assemble("#q"), [cse_list])).as_bin())
generator = BlockGenerator(block_program, [])
return generator
cse_list = spend_bundle_to_serialized_coin_solution_entry_list(bundle)
block_program = b"\xff"
block_program += SExp.to(binutils.assemble("#q")).as_bin()
block_program += b"\xff" + cse_list + b"\x80"
return BlockGenerator(SerializedProgram.from_bytes(block_program), [])
STANDARD_TRANSACTION_PUZZLE_PREFIX = r"""ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01""" # noqa
@ -59,12 +63,12 @@ def match_standard_transaction_at_any_index(generator_body: bytes) -> Optional[T
return None
def match_standard_transaction_exactly_and_return_pubkey(puzzle: Program) -> Optional[bytes]:
def match_standard_transaction_exactly_and_return_pubkey(puzzle: SerializedProgram) -> Optional[bytes]:
m = STANDARD_TRANSACTION_PUZZLE_PATTERN.fullmatch(bytes(puzzle).hex())
return None if m is None else hexstr_to_bytes(m.group(1))
def compress_cse_puzzle(puzzle: Program):
def compress_cse_puzzle(puzzle: SerializedProgram) -> Optional[bytes]:
return match_standard_transaction_exactly_and_return_pubkey(puzzle)
@ -72,11 +76,11 @@ def compress_coin_solution(coin_solution: CoinSolution):
compressed_puzzle = compress_cse_puzzle(coin_solution.puzzle_reveal)
return [
[coin_solution.coin.parent_coin_info, coin_solution.coin.amount],
[compressed_puzzle, coin_solution.solution],
[compressed_puzzle, Program.from_bytes(bytes(coin_solution.solution))],
]
def puzzle_suitable_for_compression(puzzle: Program):
def puzzle_suitable_for_compression(puzzle: SerializedProgram) -> bool:
return True if match_standard_transaction_exactly_and_return_pubkey(puzzle) else False
@ -88,7 +92,7 @@ def bundle_suitable_for_compression(bundle: SpendBundle):
def compressed_coin_solution_entry_list(bundle: SpendBundle) -> List:
compressed_cse_list = []
compressed_cse_list: List[List[Union[List[uint64], List[Union[bytes, None, Program]]]]] = []
for coin_solution in bundle.coin_solutions:
compressed_cse_list.append(compress_coin_solution(coin_solution))
return compressed_cse_list

View File

@ -57,7 +57,7 @@ def create_compressed_generator(
return BlockGenerator(program, [generator_arg])
def setup_generator_args(self: BlockGenerator):
def setup_generator_args(self: BlockGenerator) -> Tuple[SerializedProgram, Program]:
args = create_generator_args(self.generator_refs())
return self.program, args

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
from typing import List
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program, INFINITE_COST
from chia.types.blockchain_format.program import SerializedProgram, INFINITE_COST
from chia.util.chain_utils import additions_for_solution
from chia.util.streamable import Streamable, streamable
@ -17,8 +17,8 @@ class CoinSolution(Streamable):
"""
coin: Coin
puzzle_reveal: Program
solution: Program
puzzle_reveal: SerializedProgram
solution: SerializedProgram
def additions(self) -> List[Coin]:
return additions_for_solution(self.coin.name(), self.puzzle_reveal, self.solution, INFINITE_COST)

View File

@ -1,7 +1,7 @@
from typing import List
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.program import SerializedProgram
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.condition_tools import (
conditions_dict_for_solution,
@ -9,7 +9,9 @@ from chia.util.condition_tools import (
)
def additions_for_solution(coin_name: bytes32, puzzle_reveal: Program, solution: Program, max_cost: int) -> List[Coin]:
def additions_for_solution(
coin_name: bytes32, puzzle_reveal: SerializedProgram, solution: SerializedProgram, max_cost: int
) -> List[Coin]:
"""
Checks the conditions created by CoinSolution and returns the list of all coins created
"""

View File

@ -4,7 +4,7 @@ from blspy import G1Element
from chia.types.announcement import Announcement
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.program import Program, SerializedProgram
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.condition_opcodes import ConditionOpcode
from chia.types.condition_with_args import ConditionWithArgs
@ -176,8 +176,8 @@ def puzzle_announcement_names_for_conditions_dict(
def conditions_dict_for_solution(
puzzle_reveal: Program,
solution: Program,
puzzle_reveal: SerializedProgram,
solution: SerializedProgram,
max_cost: int,
) -> Tuple[Optional[Err], Optional[Dict[ConditionOpcode, List[ConditionWithArgs]]], uint64]:
error, result, cost = conditions_for_solution(puzzle_reveal, solution, max_cost)
@ -187,8 +187,8 @@ def conditions_dict_for_solution(
def conditions_for_solution(
puzzle_reveal: Program,
solution: Program,
puzzle_reveal: SerializedProgram,
solution: SerializedProgram,
max_cost: int,
) -> Tuple[Optional[Err], Optional[List[ConditionWithArgs]], uint64]:
# get the standard script for a puzzle hash and feed in the solution

View File

@ -224,7 +224,7 @@ def spendable_cc_list_from_coin_solution(coin_solution: CoinSolution, hash_to_pu
spendable_cc_list = []
coin = coin_solution.coin
puzzle = coin_solution.puzzle_reveal
puzzle = Program.from_bytes(bytes(coin_solution.puzzle_reveal))
r = uncurry_cc(puzzle)
if r:
mod_hash, genesis_coin_checker, inner_puzzle = r

View File

@ -72,7 +72,7 @@ def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
pks.append(pk)
msgs.append(m)
print()
r = puzzle_reveal.run(solution)
r = puzzle_reveal.run_with_cost(INFINITE_COST, solution)
print(disassemble(r))
print()
if conditions and len(conditions) > 0:

View File

@ -549,7 +549,7 @@ class RLWallet:
sigs = []
for coin_solution in spends:
pubkey, secretkey = await self.get_keys(coin_solution.coin.puzzle_hash)
signature = AugSchemeMPL.sign(secretkey, Program.to(coin_solution.solution).get_tree_hash())
signature = AugSchemeMPL.sign(secretkey, coin_solution.solution.get_tree_hash())
sigs.append(signature)
aggsig = AugSchemeMPL.aggregate(sigs)

View File

@ -11,6 +11,7 @@ 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
from chia.types.spend_bundle import SpendBundle
from chia.types.coin_solution import CoinSolution
from chia.util.byte_types import hexstr_to_bytes
from chia.util.db_wrapper import DBWrapper
from chia.util.hash import std_hash
@ -361,16 +362,16 @@ class TradeManager:
if trade_offer is not None:
offer_spend_bundle: SpendBundle = trade_offer.spend_bundle
coinsols = [] # [] of CoinSolutions
cc_coinsol_outamounts: Dict[bytes32, List[Tuple[Any, int]]] = dict()
coinsols: List[CoinSolution] = [] # [] of CoinSolutions
cc_coinsol_outamounts: Dict[bytes32, List[Tuple[CoinSolution, int]]] = dict()
aggsig = offer_spend_bundle.aggregated_signature
cc_discrepancies: Dict[bytes32, int] = dict()
chia_discrepancy = None
wallets: Dict[bytes32, Any] = dict() # colour to wallet dict
for coinsol in offer_spend_bundle.coin_solutions:
puzzle: Program = coinsol.puzzle_reveal
solution: Program = coinsol.solution
puzzle: Program = Program.from_bytes(bytes(coinsol.puzzle_reveal))
solution: Program = Program.from_bytes(bytes(coinsol.solution))
# work out the deficits between coin amount and expected output for each
r = cc_utils.uncurry_cc(puzzle)
@ -473,8 +474,8 @@ class TradeManager:
# Create SpendableCC for each of the coloured coins received
for cc_coinsol_out in cc_coinsol_outamounts[colour]:
cc_coinsol = cc_coinsol_out[0]
puzzle = cc_coinsol.puzzle_reveal
solution = cc_coinsol.solution
puzzle = Program.from_bytes(bytes(cc_coinsol.puzzle_reveal))
solution = Program.from_bytes(bytes(cc_coinsol.solution))
r = uncurry_cc(puzzle)
if r:

View File

@ -64,8 +64,8 @@ def get_discrepancies_for_spend_bundle(
try:
cc_discrepancies: Dict[str, int] = dict()
for coinsol in trade_offer.coin_solutions:
puzzle = coinsol.puzzle_reveal
solution = coinsol.solution
puzzle: Program = Program.from_bytes(bytes(coinsol.puzzle_reveal))
solution: Program = Program.from_bytes(bytes(coinsol.solution))
# work out the deficits between coin amount and expected output for each
r = cc_utils.uncurry_cc(puzzle)
if r:

View File

@ -1,6 +1,6 @@
# flake8: noqa: F501
from dataclasses import dataclass
from typing import List
from typing import List, Any
from unittest import TestCase
from chia.full_node.bundle_tools import (
@ -9,6 +9,7 @@ from chia.full_node.bundle_tools import (
compressed_spend_bundle_solution,
match_standard_transaction_at_any_index,
simple_solution_generator,
spend_bundle_to_serialized_coin_solution_entry_list,
)
from chia.full_node.generator import run_generator, create_generator_args
from chia.types.blockchain_format.program import Program, SerializedProgram, INFINITE_COST
@ -21,6 +22,10 @@ from chia.wallet.puzzles.load_clvm import load_clvm
from tests.core.make_block_generator import make_spend_bundle
from clvm import SExp
import io
from clvm.serialize import sexp_from_stream
from clvm_tools import binutils
TEST_GEN_DESERIALIZE = load_clvm("test_generator_deserialize.clvm", package_or_requirement="chia.wallet.puzzles")
@ -77,6 +82,19 @@ def create_multiple_ref_generator(args: MultipleCompressorArg, spend_bundle: Spe
return BlockGenerator(program, generator_args)
def spend_bundle_to_coin_solution_entry_list(bundle: SpendBundle) -> List[Any]:
r = []
for coin_solution in bundle.coin_solutions:
entry = [
coin_solution.coin.parent_coin_info,
sexp_from_stream(io.BytesIO(bytes(coin_solution.puzzle_reveal)), SExp.to),
coin_solution.coin.amount,
sexp_from_stream(io.BytesIO(bytes(coin_solution.solution)), SExp.to),
]
r.append(entry)
return r
class TestCompression(TestCase):
def test_spend_bundle_suitable(self):
sb: SpendBundle = make_spend_bundle(1)
@ -120,6 +138,13 @@ class TestCompression(TestCase):
assert result_s is not None
assert result_c == result_s
def test_spend_byndle_coin_solution(self):
for i in range(0, 10):
sb: SpendBundle = make_spend_bundle(i)
cs1 = SExp.to(spend_bundle_to_coin_solution_entry_list(sb)).as_bin()
cs2 = spend_bundle_to_serialized_coin_solution_entry_list(sb)
assert cs1 == cs2
class TestDecompression(TestCase):
def __init__(self, *args, **kwargs):