def test_shatrees_match(self): """Checks to see that all .sha256tree files match their .hex files""" for prog_path in wallet_program_files: # load the .hex file as a program hex_filename = path_with_ext(prog_path, ".hex") clvm_hex = hex_filename.read_text() # .decode("utf8") clvm_blob = bytes.fromhex(clvm_hex) s = SerializedProgram.from_bytes(clvm_blob) p = Program.from_bytes(clvm_blob) # load the checked-in shatree existing_sha = path_with_ext( prog_path, ".hex.sha256tree").read_text().strip() self.assertEqual( s.get_tree_hash().hex(), existing_sha, msg= f"Checked-in shatree hash file does not match shatree hash of loaded SerializedProgram: {prog_path}", # noqa ) self.assertEqual( p.get_tree_hash().hex(), existing_sha, msg= f"Checked-in shatree hash file does not match shatree hash of loaded Program: {prog_path}", )
def test_deserialization_simple_list(self): # ("hello" "friend") b = hexstr_to_bytes("ff8568656c6c6fff86667269656e6480") cost, output = DESERIALIZE_MOD.run_with_cost([b]) print(cost, output) prog = Program.to(output) assert prog == Program.from_bytes(b)
def test_deserialization_large_numbers(self): # '(99999999999999999999999999999999999999999999999999999999999999999 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -99999999999999999999999999999999999999999999999999999999999999999999999999999)' # noqa b = hexstr_to_bytes( "ff9c00f316271c7fc3908a8bef464e3945ef7a253609ffffffffffffffffffb00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1ff22ea0179500526edb610f148ec0c614155678491902d6000000000000000000180" # noqa ) # noqa cost, output = DESERIALIZE_MOD.run_with_cost([b]) print(cost, output) prog = Program.to(output) assert prog == Program.from_bytes(b)
def test_deserialization_password_coin(self): # (i (= (sha256 2) (q 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824)) (c (q 51) (c 5 (c (q 100) (q ())))) (q "wrong password")) # noqa b = hexstr_to_bytes( "ff04ffff0affff0bff0280ffff01ffa02cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b98248080ffff05ffff01ff3380ffff05ff05ffff05ffff01ff6480ffff01ff8080808080ffff01ff8e77726f6e672070617373776f72648080" # noqa ) # noqa cost, output = DESERIALIZE_MOD.run_with_cost([b]) print(cost, output) prog = Program.to(output) assert prog == Program.from_bytes(b)
async def test_clvm_strict_mode(self): block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR)) disassembly = binutils.disassemble(block) # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). program = SerializedProgram.from_bytes( binutils.assemble( f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None
async def create_wallet_for_cc( wallet_state_manager: Any, wallet: Wallet, genesis_checker_hex: str, ) -> CCWallet: self = CCWallet() self.cost_of_single_tx = None self.base_puzzle_program = None self.base_inner_puzzle_hash = None self.standard_wallet = wallet self.log = logging.getLogger(__name__) self.wallet_state_manager = wallet_state_manager self.cc_info = CCInfo( Program.from_bytes(bytes.fromhex(genesis_checker_hex)), []) info_as_string = bytes(self.cc_info).hex() self.wallet_info = await wallet_state_manager.user_store.create_wallet( "CC Wallet", WalletType.COLOURED_COIN, info_as_string) if self.wallet_info is None: raise Exception("wallet_info is None") await self.wallet_state_manager.add_new_wallet(self, self.id()) return self
async def respond_to_offer( self, file_path: Path ) -> Tuple[bool, Optional[TradeRecord], Optional[str]]: has_wallets = await self.maybe_create_wallets_for_offer(file_path) if not has_wallets: return False, None, "Unknown Error" trade_offer = None try: trade_offer_hex = file_path.read_text() trade_offer = TradeRecord.from_bytes( hexstr_to_bytes(trade_offer_hex)) except Exception as e: return False, None, f"Error: {e}" 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() 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.solution.first() solution: Program = coinsol.solution.rest().first() # work out the deficits between coin amount and expected output for each r = cc_utils.uncurry_cc(puzzle) if r: # Calculate output amounts mod_hash, genesis_checker, inner_puzzle = r colour = bytes(genesis_checker).hex() if colour not in wallets: wallets[ colour] = await self.wallet_state_manager.get_wallet_for_colour( colour) unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet( wallets[colour].id()) if coinsol.coin in [record.coin for record in unspent]: return False, None, "can't respond to own offer" innersol = solution.first() total = get_output_amount_for_puzzle_and_solution( inner_puzzle, innersol) if colour in cc_discrepancies: cc_discrepancies[colour] += coinsol.coin.amount - total else: cc_discrepancies[colour] = coinsol.coin.amount - total # Store coinsol and output amount for later if colour in cc_coinsol_outamounts: cc_coinsol_outamounts[colour].append((coinsol, total)) else: cc_coinsol_outamounts[colour] = [(coinsol, total)] else: # standard chia coin unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet( 1) if coinsol.coin in [record.coin for record in unspent]: return False, None, "can't respond to own offer" if chia_discrepancy is None: chia_discrepancy = get_output_discrepancy_for_puzzle_and_solution( coinsol.coin, puzzle, solution) else: chia_discrepancy += get_output_discrepancy_for_puzzle_and_solution( coinsol.coin, puzzle, solution) coinsols.append(coinsol) chia_spend_bundle: Optional[SpendBundle] = None if chia_discrepancy is not None: chia_spend_bundle = await self.wallet_state_manager.main_wallet.create_spend_bundle_relative_chia( chia_discrepancy, []) if chia_spend_bundle is not None: for coinsol in coinsols: chia_spend_bundle.coin_solutions.append(coinsol) zero_spend_list: List[SpendBundle] = [] spend_bundle = None # create coloured coin self.log.info(cc_discrepancies) for colour in cc_discrepancies.keys(): if cc_discrepancies[colour] < 0: my_cc_spends = await wallets[colour].select_coins( abs(cc_discrepancies[colour])) else: if chia_spend_bundle is None: to_exclude: List = [] else: to_exclude = chia_spend_bundle.removals() my_cc_spends = await wallets[colour].select_coins(0) if my_cc_spends is None or my_cc_spends == set(): zero_spend_bundle: SpendBundle = await wallets[ colour].generate_zero_val_coin(False, to_exclude) if zero_spend_bundle is None: return ( False, None, "Unable to generate zero value coin. Confirm that you have chia available", ) zero_spend_list.append(zero_spend_bundle) additions = zero_spend_bundle.additions() removals = zero_spend_bundle.removals() my_cc_spends = set() for add in additions: if add not in removals and add.amount == 0: my_cc_spends.add(add) if my_cc_spends == set() or my_cc_spends is None: return False, None, "insufficient funds" # Create SpendableCC list and innersol_list with both my coins and the offered coins # Firstly get the output coin my_output_coin = my_cc_spends.pop() spendable_cc_list = [] innersol_list = [] genesis_id = genesis_coin_id_for_genesis_coin_checker( Program.from_bytes(bytes.fromhex(colour))) # Make the rest of the coins assert the output coin is consumed for coloured_coin in my_cc_spends: inner_solution = self.wallet_state_manager.main_wallet.make_solution( consumed=[my_output_coin.name()]) inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( coloured_coin.puzzle_hash) assert inner_puzzle is not None sigs = await wallets[colour].get_sigs(inner_puzzle, inner_solution, coloured_coin.name()) sigs.append(aggsig) aggsig = AugSchemeMPL.aggregate(sigs) lineage_proof = await wallets[ colour].get_lineage_proof_for_coin(coloured_coin) spendable_cc_list.append( SpendableCC(coloured_coin, genesis_id, inner_puzzle, lineage_proof)) innersol_list.append(inner_solution) # 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.solution.first() solution = cc_coinsol.solution.rest().first() r = uncurry_cc(puzzle) if r: mod_hash, genesis_coin_checker, inner_puzzle = r inner_solution = solution.first() lineage_proof = solution.rest().rest().first() spendable_cc_list.append( SpendableCC(cc_coinsol.coin, genesis_id, inner_puzzle, lineage_proof)) innersol_list.append(inner_solution) # Finish the output coin SpendableCC with new information newinnerpuzhash = await wallets[colour].get_new_inner_hash() outputamount = sum([ c.amount for c in my_cc_spends ]) + cc_discrepancies[colour] + my_output_coin.amount inner_solution = self.wallet_state_manager.main_wallet.make_solution( primaries=[{ "puzzlehash": newinnerpuzhash, "amount": outputamount }]) inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( my_output_coin.puzzle_hash) assert inner_puzzle is not None lineage_proof = await wallets[colour].get_lineage_proof_for_coin( my_output_coin) spendable_cc_list.append( SpendableCC(my_output_coin, genesis_id, inner_puzzle, lineage_proof)) innersol_list.append(inner_solution) sigs = await wallets[colour].get_sigs(inner_puzzle, inner_solution, my_output_coin.name()) sigs.append(aggsig) aggsig = AugSchemeMPL.aggregate(sigs) if spend_bundle is None: spend_bundle = spend_bundle_for_spendable_ccs( CC_MOD, Program.from_bytes(bytes.fromhex(colour)), spendable_cc_list, innersol_list, [aggsig], ) else: new_spend_bundle = spend_bundle_for_spendable_ccs( CC_MOD, Program.from_bytes(bytes.fromhex(colour)), spendable_cc_list, innersol_list, [aggsig], ) spend_bundle = SpendBundle.aggregate( [spend_bundle, new_spend_bundle]) # reset sigs and aggsig so that they aren't included next time around sigs = [] aggsig = AugSchemeMPL.aggregate(sigs) my_tx_records = [] if zero_spend_list is not None and spend_bundle is not None: zero_spend_list.append(spend_bundle) spend_bundle = SpendBundle.aggregate(zero_spend_list) if spend_bundle is None: return False, None, "spend_bundle missing" # Add transaction history for this trade now = uint64(int(time.time())) if chia_spend_bundle is not None: spend_bundle = SpendBundle.aggregate( [spend_bundle, chia_spend_bundle]) # debug_spend_bundle(spend_bundle) if chia_discrepancy < 0: tx_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=now, to_puzzle_hash=token_bytes(), amount=uint64(abs(chia_discrepancy)), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=chia_spend_bundle, additions=chia_spend_bundle.additions(), removals=chia_spend_bundle.removals(), wallet_id=uint32(1), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), type=uint32(TransactionType.OUTGOING_TRADE.value), name=chia_spend_bundle.name(), ) else: tx_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(chia_discrepancy)), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=chia_spend_bundle, additions=chia_spend_bundle.additions(), removals=chia_spend_bundle.removals(), wallet_id=uint32(1), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), type=uint32(TransactionType.INCOMING_TRADE.value), name=chia_spend_bundle.name(), ) my_tx_records.append(tx_record) for colour, amount in cc_discrepancies.items(): wallet = wallets[colour] if chia_discrepancy > 0: tx_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(amount)), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=wallet.id(), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), type=uint32(TransactionType.OUTGOING_TRADE.value), name=spend_bundle.name(), ) else: tx_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(amount)), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=wallet.id(), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), type=uint32(TransactionType.INCOMING_TRADE.value), name=token_bytes(), ) my_tx_records.append(tx_record) tx_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=uint32(0), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), type=uint32(TransactionType.OUTGOING_TRADE.value), name=spend_bundle.name(), ) now = uint64(int(time.time())) trade_record: TradeRecord = TradeRecord( confirmed_at_index=uint32(0), accepted_at_time=now, created_at_time=now, my_offer=False, sent=uint32(0), spend_bundle=offer_spend_bundle, tx_spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), trade_id=std_hash(spend_bundle.name() + bytes(now)), status=uint32(TradeStatus.PENDING_CONFIRM.value), sent_to=[], ) await self.save_trade(trade_record) await self.wallet_state_manager.add_pending_transaction(tx_record) for tx in my_tx_records: await self.wallet_state_manager.add_transaction(tx) return True, trade_record, None
This roughly corresponds to bitcoin's taproot. """ import hashlib from typing import Union from blspy import G1Element, PrivateKey from clvm.casts import int_from_bytes from src.types.blockchain_format.program import Program from src.types.blockchain_format.sized_bytes import bytes32 from .load_clvm import load_clvm from .p2_conditions import puzzle_for_conditions DEFAULT_HIDDEN_PUZZLE = Program.from_bytes(bytes.fromhex("ff0980")) DEFAULT_HIDDEN_PUZZLE_HASH = DEFAULT_HIDDEN_PUZZLE.get_tree_hash() # this puzzle `(x)` always fails MOD = load_clvm("p2_delegated_puzzle_or_hidden_puzzle.clvm") SYNTHETIC_MOD = load_clvm("calculate_synthetic_public_key.clvm") PublicKeyProgram = Union[bytes, Program] GROUP_ORDER = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 def calculate_synthetic_offset(public_key: G1Element, hidden_puzzle_hash: bytes32) -> int: blob = hashlib.sha256(bytes(public_key) + hidden_puzzle_hash).digest() offset = int_from_bytes(blob)
def load_clvm(clvm_filename, package_or_requirement=__name__) -> Program: return Program.from_bytes(bytes(load_serialized_clvm(clvm_filename, package_or_requirement=__name__)))
cc_puzzle_hash_for_inner_puzzle_hash, spendable_cc_list_from_coin_solution, spend_bundle_for_spendable_ccs, CC_MOD, ) from src.wallet.puzzles.genesis_by_coin_id_with_0 import create_genesis_or_zero_coin_checker from src.wallet.puzzles.genesis_by_puzzle_hash_with_0 import create_genesis_puzzle_or_zero_coin_checker CONDITIONS = dict( (k, bytes(v)[0]) for k, v in ConditionOpcode.__members__.items()) NULL_SIGNATURE = G2Element.generator() * 0 ANYONE_CAN_SPEND_PUZZLE = Program.to(1) # simply return the conditions NULL_F = Program.from_bytes(bytes.fromhex("ff01ff8080")) # (q ()) PUZZLE_TABLE: Dict[bytes32, Program] = dict( (_.get_tree_hash(), _) for _ in [ANYONE_CAN_SPEND_PUZZLE]) def hash_to_puzzle_f(puzzle_hash: bytes32) -> Optional[Program]: return PUZZLE_TABLE.get(puzzle_hash) def add_puzzles_to_puzzle_preimage_db(puzzles: List[Program]) -> None: for _ in puzzles: PUZZLE_TABLE[_.get_tree_hash()] = _ def int_as_bytes32(v: int) -> bytes32:
def test_serialization(self): s0 = SerializedProgram.from_bytes(b"\x00") p0 = Program.from_bytes(b"\x00") print(s0, p0)