async def load_attest_files_for_recovery_spend(self, filenames): spend_bundle_list = [] info_dict = {} try: for i in filenames: f = open(i) info = f.read().split(":") info_dict[info[0]] = [ bytes.fromhex(info[2]), bytes.fromhex(info[3]), uint64(info[4]), ] new_sb = SpendBundle.from_bytes(bytes.fromhex(info[1])) spend_bundle_list.append(new_sb) f.close() # info_dict {0xidentity: "(0xparent_info 0xinnerpuz amount)"} my_recovery_list: List[bytes] = self.did_info.backup_ids # convert info dict into recovery list - same order as wallet info_list = [] for entry in my_recovery_list: if entry.hex() in info_dict: info_list.append([ info_dict[entry.hex()][0], info_dict[entry.hex()][1], info_dict[entry.hex()][2], ]) else: info_list.append([]) message_spend_bundle = SpendBundle.aggregate(spend_bundle_list) return info_list, message_spend_bundle except Exception: raise
def to_spend_bundle(self) -> SpendBundle: # Before we serialze this as a SpendBundle, we need to serialze the `requested_payments` as dummy CoinSpends additional_coin_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): puzzle_reveal: Program = construct_cat_puzzle( CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD inner_solutions = [] nonces: List[bytes32] = [p.nonce for p in payments] for nonce in list( dict.fromkeys(nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) additional_coin_spends.append( CoinSpend( Coin( ZERO_32, puzzle_reveal.get_tree_hash(), uint64(0), ), puzzle_reveal, Program.to(inner_solutions), )) return SpendBundle.aggregate([ SpendBundle(additional_coin_spends, G2Element()), self.bundle, ])
def aggregate(cls, offers: List["Offer"]) -> "Offer": total_requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = {} total_bundle = SpendBundle([], G2Element()) for offer in offers: # First check for any overlap in inputs total_inputs: Set[Coin] = { cs.coin for cs in total_bundle.coin_spends } offer_inputs: Set[Coin] = { cs.coin for cs in offer.bundle.coin_spends } if total_inputs & offer_inputs: raise ValueError("The aggregated offers overlap inputs") # Next, do the aggregation for tail, payments in offer.requested_payments.items(): if tail in total_requested_payments: total_requested_payments[tail].extend(payments) else: total_requested_payments[tail] = payments total_bundle = SpendBundle.aggregate([total_bundle, offer.bundle]) return cls(total_requested_payments, total_bundle)
async def sign(self, spend_bundle: SpendBundle) -> SpendBundle: sigs: List[G2Element] = [] for spend in spend_bundle.coin_spends: matched, puzzle_args = match_cat_puzzle( spend.puzzle_reveal.to_program()) if matched: _, _, inner_puzzle = puzzle_args puzzle_hash = inner_puzzle.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) error, conditions, cost = conditions_dict_for_solution( spend.puzzle_reveal.to_program(), spend.solution.to_program(), self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM, ) if conditions is not None: synthetic_pk = synthetic_secret_key.get_g1() for pk, msg in pkm_pairs_for_conditions_dict( conditions, spend.coin.name(), self.wallet_state_manager.constants. AGG_SIG_ME_ADDITIONAL_DATA): try: assert bytes(synthetic_pk) == pk sigs.append( AugSchemeMPL.sign(synthetic_secret_key, msg)) except AssertionError: raise ValueError( "This spend bundle cannot be signed by the CAT wallet" ) agg_sig = AugSchemeMPL.aggregate(sigs) return SpendBundle.aggregate([spend_bundle, SpendBundle([], agg_sig)])
async def create_spend(self, puzhash: bytes32): assert self.did_info.current_inner is not None coins = await self.select_coins(1) assert coins is not None coin = coins.pop() # innerpuz solution is (mode amount new_puz identity my_puz) innersol: Program = Program.to([0, coin.amount, puzhash, coin.name(), coin.puzzle_hash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.my_did, ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to( [ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ] ) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = bytes(puzhash) + bytes(coin.name()) 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) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) return spend_bundle
async def main() -> None: rpc_port: uint16 = uint16(8555) self_hostname = "localhost" path = DEFAULT_ROOT_PATH config = load_config(path, "config.yaml") client = await FullNodeRpcClient.create(self_hostname, rpc_port, path, config) try: farmer_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[1] pool_prefarm = ( await client.get_block_record_by_height(1)).reward_claims_incorporated[0] pool_amounts = int(calculate_pool_reward(uint32(0)) / 2) farmer_amounts = int(calculate_base_farmer_reward(uint32(0)) / 2) print(farmer_prefarm.amount, farmer_amounts) assert farmer_amounts == farmer_prefarm.amount // 2 assert pool_amounts == pool_prefarm.amount // 2 address1 = "xch1rdatypul5c642jkeh4yp933zu3hw8vv8tfup8ta6zfampnyhjnusxdgns6" # Key 1 address2 = "xch1duvy5ur5eyj7lp5geetfg84cj2d7xgpxt7pya3lr2y6ke3696w9qvda66e" # Key 2 ph1 = decode_puzzle_hash(address1) ph2 = decode_puzzle_hash(address2) p_farmer_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {farmer_amounts}) (51 0x{ph2.hex()} {farmer_amounts})))" )) p_pool_2 = Program.to( binutils.assemble( f"(q . ((51 0x{ph1.hex()} {pool_amounts}) (51 0x{ph2.hex()} {pool_amounts})))" )) p_solution = Program.to(binutils.assemble("()")) sb_farmer = SpendBundle( [CoinSolution(farmer_prefarm, p_farmer_2, p_solution)], G2Element()) sb_pool = SpendBundle( [CoinSolution(pool_prefarm, p_pool_2, p_solution)], G2Element()) print(sb_pool, sb_farmer) res = await client.push_tx(sb_farmer) # res = await client.push_tx(sb_pool) print(res) up = await client.get_coin_records_by_puzzle_hash( farmer_prefarm.puzzle_hash, True) uf = await client.get_coin_records_by_puzzle_hash( pool_prefarm.puzzle_hash, True) print(up) print(uf) finally: client.close()
def update_coin_store_for_spend_bundle(self, spend_bundle: SpendBundle, now: CoinTimestamp): err = self.validate_spend_bundle(spend_bundle, now) if err != 0: raise BadSpendBundleError(f"validation failure {err}") for spent_coin in spend_bundle.removals(): coin_name = spent_coin.name() coin_record = self._db[coin_name] self._db[coin_name] = replace(coin_record, spent_block_index=now.height, spent=True) for new_coin in spend_bundle.additions(): self._add_coin_entry(new_coin, now)
async def do_spend( self, sim: SpendSim, sim_client: SimClient, tail: Program, coins: List[Coin], lineage_proofs: List[Program], inner_solutions: List[Program], expected_result: Tuple[MempoolInclusionStatus, Err], reveal_limitations_program: bool = True, signatures: List[G2Element] = [], extra_deltas: Optional[List[int]] = None, additional_spends: List[SpendBundle] = [], limitations_solutions: Optional[List[Program]] = None, cost_str: str = "", ): if limitations_solutions is None: limitations_solutions = [Program.to([])] * len(coins) if extra_deltas is None: extra_deltas = [0] * len(coins) spendable_cat_list: List[SpendableCAT] = [] for coin, innersol, proof, limitations_solution, extra_delta in zip( coins, inner_solutions, lineage_proofs, limitations_solutions, extra_deltas ): spendable_cat_list.append( SpendableCAT( coin, tail.get_tree_hash(), acs, innersol, limitations_solution=limitations_solution, lineage_proof=proof, extra_delta=extra_delta, limitations_program_reveal=tail if reveal_limitations_program else Program.to([]), ) ) spend_bundle: SpendBundle = unsigned_spend_bundle_for_spendable_cats( CAT_MOD, spendable_cat_list, ) agg_sig = AugSchemeMPL.aggregate(signatures) result = await sim_client.push_tx( SpendBundle.aggregate( [ *additional_spends, spend_bundle, SpendBundle([], agg_sig), # "Signing" the spend bundle ] ) ) assert result == expected_result self.cost[cost_str] = cost_of_spend_bundle(spend_bundle) await sim.farm_block()
async def push_tx(self, spend_bundle: SpendBundle) -> Tuple[MempoolInclusionStatus, Optional[Err]]: try: cost_result: NPCResult = await self.service.mempool_manager.pre_validate_spendbundle( spend_bundle, None, spend_bundle.name() ) except ValidationError as e: return MempoolInclusionStatus.FAILED, e.code cost, status, error = await self.service.mempool_manager.add_spendbundle( spend_bundle, cost_result, spend_bundle.name() ) return status, error
def test_only_odd_coins_0(): blocks = initial_blocks() farmed_coin = list(blocks[-1].get_included_reward_coins())[0] metadata = [("foo", "bar")] ANYONE_CAN_SPEND_PUZZLE = Program.to(1) launcher_amount = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) lineage_proof, launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_solution = CoinSolution(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_solution], G2Element())]) run = asyncio.get_event_loop().run_until_complete coins_added, coins_removed = run( check_spend_bundle_validity(bt.constants, blocks, spend_bundle)) coin_set_added = set([_.coin for _ in coins_added]) coin_set_removed = set([_.coin for _ in coins_removed]) launcher_coin = launcher_spend_bundle.coin_solutions[0].coin assert launcher_coin in coin_set_added assert launcher_coin in coin_set_removed assert farmed_coin in coin_set_removed # breakpoint() singleton_expected_puzzle_hash = singleton_puzzle_hash( launcher_id, launcher_puzzle_hash, initial_singleton_puzzle) expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert expected_singleton_coin in coin_set_added # next up: spend the expected_singleton_coin # it's an adapted `ANYONE_CAN_SPEND_PUZZLE` # then try a bad lineage proof # then try writing two odd coins # then try writing zero odd coins # then, destroy the singleton with the -113 hack return 0
def spend_coin_to_singleton( puzzle_db: PuzzleDB, launcher_puzzle: Program, coin_store: CoinStore, now: CoinTimestamp) -> Tuple[List[Coin], List[CoinSpend]]: farmed_coin_amount = 100000 metadata = [("foo", "bar")] now = CoinTimestamp(10012300, 1) farmed_coin = coin_store.farm_coin(ANYONE_CAN_SPEND_PUZZLE.get_tree_hash(), now, amount=farmed_coin_amount) now.seconds += 500 now.height += 1 launcher_amount: uint64 = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( puzzle_db, farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_spend = CoinSpend(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_spend], G2Element())]) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) launcher_coin = launcher_spend_bundle.coin_spends[0].coin assert_coin_spent(coin_store, launcher_coin) assert_coin_spent(coin_store, farmed_coin) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_expected_puzzle = singleton_puzzle( launcher_id, # type: ignore[arg-type] launcher_puzzle_hash, initial_singleton_puzzle, ) singleton_expected_puzzle_hash = singleton_expected_puzzle.get_tree_hash() expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert_coin_spent(coin_store, expected_singleton_coin, is_spent=False) return additions, removals
def do_inspect_spend_bundle_cmd(ctx, bundles, print_results=True, **kwargs): if kwargs and (len(kwargs['spend']) > 0) and (len(kwargs['aggsig']) > 0): spend_bundle_objs = [ SpendBundle( do_inspect_coin_spend_cmd(ctx, kwargs["spend"], print_results=False), AugSchemeMPL.aggregate([ G2Element(bytes.fromhex(sanitize_bytes(aggsig))) for aggsig in kwargs["aggsig"] ])) ] else: spend_bundle_objs = [] try: if type(bundles[0]) == str: spend_bundle_objs = streamable_load(SpendBundle, bundles) else: spend_bundle_objs = bundles except: print( "One or more of the specified objects was not a spend bundle") if print_results: inspect_callback(spend_bundle_objs, ctx, id_calc=(lambda e: e.name()), type='SpendBundle') else: return spend_bundle_objs
def make_and_spend_bundle( db: CoinStore, coin: Coin, delegated_puzzle: Program, coinsols: List[CoinSolution], exception: Optional[Exception] = None, ex_msg: str = "", fail_msg: str = "", ): signature: G2Element = sign_delegated_puz(delegated_puzzle, coin) spend_bundle = SpendBundle( coinsols, signature, ) try: db.update_coin_store_for_spend_bundle( spend_bundle, T1, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) if exception is not None: raise AssertionError(fail_msg) except Exception as e: if exception is not None: assert type(e) is exception assert str(e) == ex_msg else: raise e
def issue_cc_from_farmed_coin( mod_code: Program, coin_checker_for_farmed_coin, block_id: int, inner_puzzle_hash: bytes32, amount: int, ) -> Tuple[Program, SpendBundle]: """ This is an example of how to issue a cc. """ # get a farmed coin farmed_puzzle = ANYONE_CAN_SPEND_PUZZLE farmed_puzzle_hash = farmed_puzzle.get_tree_hash() # mint a cc farmed_coin = generate_farmed_coin(block_id, farmed_puzzle_hash, amount=uint64(amount)) genesis_coin_checker = coin_checker_for_farmed_coin(farmed_coin) minted_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(mod_code, genesis_coin_checker, inner_puzzle_hash) output_conditions = [[ConditionOpcode.CREATE_COIN, minted_cc_puzzle_hash, farmed_coin.amount]] # for this very simple puzzle, the solution is simply the output conditions # this is just a coincidence... for more complicated puzzles, you'll likely have to do some real work solution = Program.to(output_conditions) coin_spend = CoinSpend(farmed_coin, farmed_puzzle, solution) spend_bundle = SpendBundle([coin_spend], NULL_SIGNATURE) return genesis_coin_checker, spend_bundle
def main(): # Mnemonics can be generated using `chia keys generate_and_print`, or `chia keys generate`. The latter stored # the key in the OS keychain (unencrypted file if linux). mnemonic: str = "neither medal holiday echo link dog sleep idea turkey logic security sword save taxi chapter artwork toddler wealth local mind manual never unlock narrow" seed: bytes = mnemonic_to_seed(mnemonic, passphrase="") master_private_key: PrivateKey = AugSchemeMPL.key_gen(seed) intermediate_sk: PrivateKey = AugSchemeMPL.derive_child_sk( master_private_key, 12381) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 8444) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 2) print( f"Parent public key is: {intermediate_sk.get_g1()}. Please use this within `create_unsigned_tx.py`" ) # If you want to use hardened keys which are more secure against quantum computers, you need to export # The public keys # create_hardened_child_public_keys(mnemonic, 1000) try: with open("tx_3.json", "r") as f: spend_bundle_json = f.read() except Exception: print( "Error: create your transaction (spend bundle) json and put it into the json file." ) return spend_bundle_json_dict: Dict = json.loads(spend_bundle_json) spend_bundle: SpendBundle = SpendBundle.from_json_dict( spend_bundle_json_dict) sign_tx(intermediate_sk, spend_bundle, use_hardened_keys=False)
def sign_transaction(self, coin_solutions: List[CoinSolution]) -> SpendBundle: signatures = [] solution: Program puzzle: Program for coin_solution in coin_solutions: # type: ignore # noqa secret_key = self.get_private_key_for_puzzle_hash( coin_solution.coin.puzzle_hash) synthetic_secret_key = calculate_synthetic_secret_key( secret_key, DEFAULT_HIDDEN_PUZZLE_HASH) err, con, cost = conditions_for_solution( coin_solution.puzzle_reveal, coin_solution.solution, self.constants.MAX_BLOCK_COST_CLVM) if not con: raise ValueError(err) conditions_dict = conditions_by_opcode(con) 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) spend_bundle = SpendBundle(coin_solutions, aggsig) return spend_bundle
def sign_transaction(self, coin_spends: List[CoinSpend]) -> SpendBundle: signatures = [] solution: Program puzzle: Program for coin_spend in coin_spends: # noqa secret_key = self.get_private_key_for_puzzle_hash(coin_spend.coin.puzzle_hash) synthetic_secret_key = calculate_synthetic_secret_key(secret_key, DEFAULT_HIDDEN_PUZZLE_HASH) err, con, cost = conditions_for_solution( coin_spend.puzzle_reveal, coin_spend.solution, self.constants.MAX_BLOCK_COST_CLVM ) if not con: raise ValueError(err) conditions_dict = conditions_by_opcode(con) for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_UNSAFE, []): msg = cwa.vars[1] 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) signatures.append(signature) aggsig = AugSchemeMPL.aggregate(signatures) spend_bundle = SpendBundle(coin_spends, aggsig) return spend_bundle
async def push_tx(self, request: Dict) -> Optional[Dict]: if "spend_bundle" not in request: raise ValueError("Spend bundle not in request") # This is for backwards compatibility since CoinSolution has been renamed to CoinSpend if "coin_solutions" in request["spend_bundle"]: request["spend_bundle"]["coin_spends"] = request[ "spend_bundle"].pop("coin_solutions") spend_bundle = SpendBundle.from_json_dict(request["spend_bundle"]) spend_name = spend_bundle.name() if self.service.mempool_manager.get_spendbundle( spend_name) is not None: status = MempoolInclusionStatus.SUCCESS error = None else: status, error = await self.service.respond_transaction( spend_bundle, spend_name) if status != MempoolInclusionStatus.SUCCESS: if self.service.mempool_manager.get_spendbundle( spend_name) is not None: # Already in mempool status = MempoolInclusionStatus.SUCCESS error = None if status == MempoolInclusionStatus.FAILED: assert error is not None raise ValueError( f"Failed to include transaction {spend_name}, error {error.name}" ) return { "status": status.name, }
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program): assert self.did_info.origin_coin is not None # innerpuz solution is (mode amount message new_puzhash) innersol = Program.to([1, coin.amount, [], innerpuz.get_tree_hash()]) # full solution is (lineage_proof my_amount inner_solution) fullsol = Program.to( [ [self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount], coin.amount, innersol, ] ) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([innerpuz.get_tree_hash(), coin.amount, []]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA ) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) record: Optional[DerivationRecord] = await self.wallet_state_manager.puzzle_store.record_for_pubkey(pubkey) assert record is not None private = master_sk_to_wallet_sk_unhardened(self.wallet_state_manager.private_key, record.index) signature = AugSchemeMPL.sign(private, message) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
async def push_tx(self, request: Dict) -> Optional[Dict]: if "spend_bundle" not in request: raise ValueError("Spend bundle not in request") spend_bundle = SpendBundle.from_json_dict(request["spend_bundle"]) spend_name = spend_bundle.name() if self.service.mempool_manager.get_spendbundle( spend_name) is not None: status = MempoolInclusionStatus.SUCCESS error = None else: status, error = await self.service.respond_transaction( spend_bundle, spend_name) if status != MempoolInclusionStatus.SUCCESS: if self.service.mempool_manager.get_spendbundle( spend_name) is not None: # Already in mempool status = MempoolInclusionStatus.SUCCESS error = None if status == MempoolInclusionStatus.FAILED: assert error is not None raise ValueError( f"Failed to include transaction {spend_name}, error {error.name}" ) return { "status": status.name, }
async def sign_coin_solutions( coin_solutions: List[CoinSolution], secret_key_for_public_key_f: Callable[[bytes], Optional[PrivateKey]], ) -> SpendBundle: signatures = [] pk_list = [] msg_list = [] 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) if err or conditions_dict is None: error_msg = f"Sign transaction failed, con:{conditions_dict}, error: {err}" raise ValueError(error_msg) # Create signature for _, msg in pkm_pairs_for_conditions_dict(conditions_dict, bytes(coin_solution.coin.name())): pk_list.append(_) msg_list.append(msg) secret_key = secret_key_for_public_key_f(_) if secret_key is None: e_msg = f"no secret key for {_}" raise ValueError(e_msg) assert bytes(secret_key.get_g1()) == bytes(_) signature = AugSchemeMPL.sign(secret_key, msg) assert AugSchemeMPL.verify(_, msg, signature) signatures.append(signature) # Aggregate signatures aggsig = AugSchemeMPL.aggregate(signatures) assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig) return SpendBundle(coin_solutions, aggsig)
def launcher_conditions_and_spend_bundle( parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program = LAUNCHER_PUZZLE, ) -> Tuple[Program, bytes32, List[Program], SpendBundle]: launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) singleton_full_puzzle = SINGLETON_MOD.curry( SINGLETON_MOD_HASH, launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) launcher_solution = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) coin_spend = CoinSpend(launcher_coin, launcher_puzzle, launcher_solution) spend_bundle = SpendBundle([coin_spend], G2Element()) lineage_proof = Program.to([parent_coin_id, launcher_amount]) return lineage_proof, launcher_coin.name( ), expected_conditions, spend_bundle
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program): assert self.did_info.origin_coin is not None # innerpuz solution is (mode amount message my_id my_puzhash parent_innerpuzhash_amounts_for_recovery_ids) innersol = Program.to([ 0, coin.amount, coin.puzzle_hash, coin.name(), coin.puzzle_hash, [] ]) # full solution is (parent_info my_amount innersolution) fullsol = Program.to([ [ self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount ], coin.parent_coin_info, coin.amount, innersol, ]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([coin.amount, coin.puzzle_hash]).get_tree_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) signature = AugSchemeMPL.sign(private, message) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
def from_spend_bundle(cls, bundle: SpendBundle) -> "Offer": # Because of the `to_spend_bundle` method, we need to parse the dummy CoinSpends as `requested_payments` requested_payments: Dict[Optional[bytes32], List[NotarizedPayment]] = {} leftover_coin_spends: List[CoinSpend] = [] for coin_spend in bundle.coin_spends: if coin_spend.coin.parent_coin_info == ZERO_32: matched, curried_args = match_cat_puzzle( coin_spend.puzzle_reveal.to_program()) if matched: _, tail_hash_program, _ = curried_args tail_hash: Optional[bytes32] = bytes32( tail_hash_program.as_python()) else: tail_hash = None notarized_payments: List[NotarizedPayment] = [] for payment_group in coin_spend.solution.to_program().as_iter( ): nonce = bytes32(payment_group.first().as_python()) payment_args_list: List[Program] = payment_group.rest( ).as_iter() notarized_payments.extend([ NotarizedPayment.from_condition_and_nonce( condition, nonce) for condition in payment_args_list ]) requested_payments[tail_hash] = notarized_payments else: leftover_coin_spends.append(coin_spend) return cls( requested_payments, SpendBundle(leftover_coin_spends, bundle.aggregated_signature))
async def make_and_spend_bundle( self, sim: SpendSim, sim_client: SimClient, coin: Coin, delegated_puzzle: Program, coinsols: List[CoinSpend], ex_error: Optional[Err] = None, fail_msg: str = "", ): signature: G2Element = self.sign_delegated_puz(delegated_puzzle, coin) spend_bundle = SpendBundle( coinsols, signature, ) try: result, error = await sim_client.push_tx(spend_bundle) if error is None: await sim.farm_block() elif ex_error is not None: assert error == ex_error else: raise TransactionPushError(error) except AssertionError: raise AssertionError(fail_msg)
def do_test_spend( puzzle_reveal: Program, solution: Program, payments: Iterable[Tuple[bytes32, int]], key_lookup: KeyTool, farm_time: CoinTimestamp = T1, spend_time: CoinTimestamp = T2, ) -> SpendBundle: """ This method will farm a coin paid to the hash of `puzzle_reveal`, then try to spend it with `solution`, and verify that the created coins correspond to `payments`. The `key_lookup` is used to create a signed version of the `SpendBundle`, although at this time, signatures are not verified. """ coin_db = CoinStore() puzzle_hash = puzzle_reveal.get_tree_hash() # farm it coin = coin_db.farm_coin(puzzle_hash, farm_time) # spend it coin_spend = CoinSpend(coin, puzzle_reveal, solution) spend_bundle = SpendBundle([coin_spend], G2Element()) coin_db.update_coin_store_for_spend_bundle(spend_bundle, spend_time, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) # ensure all outputs are there for puzzle_hash, amount in payments: for coin in coin_db.coins_for_puzzle_hash(puzzle_hash): if coin.amount == amount: break else: assert 0 # make sure we can actually sign the solution signatures = [] for coin_spend in spend_bundle.coin_spends: signature = key_lookup.signature_for_solution(coin_spend, bytes([2] * 32)) signatures.append(signature) return SpendBundle(spend_bundle.coin_spends, AugSchemeMPL.aggregate(signatures))
async def test_delegated_tail(self, setup_sim): sim, sim_client = setup_sim try: standard_acs = Program.to(1) standard_acs_ph: bytes32 = standard_acs.get_tree_hash() await sim.farm_block(standard_acs_ph) starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(standard_acs_ph))[0].coin sk = PrivateKey.from_bytes(secret_exponent_for_index(1).to_bytes(32, "big")) tail: Program = DelegatedLimitations.construct([Program.to(sk.get_g1())]) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim_client.push_tx( SpendBundle( [CoinSpend(starting_coin, standard_acs, Program.to([[51, cat_ph, starting_coin.amount]]))], G2Element(), ) ) await sim.farm_block() # We're signing a different tail to use here name_as_program = Program.to(starting_coin.name()) new_tail: Program = GenesisById.construct([name_as_program]) checker_solution: Program = DelegatedLimitations.solve( [name_as_program], { "signed_program": { "identifier": "genesis_by_id", "args": [str(name_as_program)], }, "program_arguments": {}, }, ) signature: G2Element = AugSchemeMPL.sign(sk, new_tail.get_tree_hash()) await self.do_spend( sim, sim_client, tail, [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), starting_coin.amount], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), signatures=[signature], limitations_solutions=[checker_solution], cost_str="Delegated Genesis", ) finally: await sim.close()
async def create_spend_bundle_relative_amount(self, cc_amount, zero_coin: Coin = None ) -> Optional[SpendBundle]: # If we're losing value then get coloured coins with at least that much value # If we're gaining value then our amount doesn't matter if cc_amount < 0: cc_spends = await self.select_coins(abs(cc_amount)) else: if zero_coin is None: return None cc_spends = set() cc_spends.add(zero_coin) if cc_spends is None: return None # Calculate output amount given relative difference and sum of actual values spend_value = sum([coin.amount for coin in cc_spends]) cc_amount = spend_value + cc_amount # Loop through coins and create solution for innerpuzzle list_of_solutions = [] output_created = None sigs: List[G2Element] = [] for coin in cc_spends: if output_created is None: newinnerpuzhash = await self.get_new_inner_hash() innersol = self.standard_wallet.make_solution( primaries=[{ "puzzlehash": newinnerpuzhash, "amount": cc_amount }]) output_created = coin else: innersol = self.standard_wallet.make_solution( consumed=[output_created.name()]) innerpuz: Program = await self.inner_puzzle_for_cc_puzhash( coin.puzzle_hash) sigs = sigs + await self.get_sigs(innerpuz, innersol, coin.name()) lineage_proof = await self.get_lineage_proof_for_coin(coin) puzzle_reveal = cc_puzzle_for_inner_puzzle( CC_MOD, self.cc_info.my_genesis_checker, innerpuz) # Use coin info to create solution and add coin and solution to list of CoinSpends solution = [ innersol, coin.as_list(), lineage_proof, None, None, None, None, None, ] list_of_solutions.append( CoinSpend(coin, puzzle_reveal, Program.to(solution))) aggsig = AugSchemeMPL.aggregate(sigs) return SpendBundle(list_of_solutions, aggsig)
def test_version_override(self): coin_spend = CoinSpend( COIN, OFFER_MOD, SOLUTION, ) spend_bundle = SpendBundle([coin_spend], G2Element()) compressed = compress_object_with_puzzles(bytes(spend_bundle), LATEST_VERSION) compressed_earlier = compress_object_with_puzzles( bytes(spend_bundle), 1) assert len(bytes(spend_bundle)) > len(bytes(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed_earlier)) assert len(bytes(compressed_earlier)) > len(bytes(compressed))
async def push_tx( self, spend_bundle: SpendBundle ) -> Tuple[MempoolInclusionStatus, Optional[Err]]: cost_result: NPCResult = await self.service.mempool_manager.pre_validate_spendbundle( spend_bundle) cost, status, error = await self.service.mempool_manager.add_spendbundle( spend_bundle, cost_result, spend_bundle.name()) return status, error