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()
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 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 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)
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 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
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 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)])
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)
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 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))
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 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
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 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 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 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 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)
async def sign_coin_spends( coin_spends: List[CoinSpend], secret_key_for_public_key_f: Any, # Potentially awaitable function from G1Element => Optional[PrivateKey] additional_data: bytes, max_cost: int, ) -> SpendBundle: """ Sign_coin_spends runs the puzzle code with the given argument and searches the result for an AGG_SIG_ME condition, which it attempts to sign by requesting a matching PrivateKey corresponding with the given G1Element (public key) specified in the resulting condition output. It's important to note that as mentioned in the documentation about the standard spend that the public key presented to the secret_key_for_public_key_f function provided to sign_coin_spends must be prepared to do the key derivations required by the coin types it's allowed to spend (at least the derivation of the standard spend as done by calculate_synthetic_secret_key with DEFAULT_PUZZLE_HASH). If a coin performed a different key derivation, the pk presented to this function would be similarly alien, and would need to be tried against the first stage derived keys (those returned by master_sk_to_wallet_sk from the ['sk'] member of wallet rpc's get_private_key method). """ signatures: List[blspy.G2Element] = [] pk_list: List[blspy.G1Element] = [] msg_list: List[bytes] = [] for coin_spend in coin_spends: # Get AGG_SIG conditions err, conditions_dict, cost = conditions_dict_for_solution( coin_spend.puzzle_reveal, coin_spend.solution, max_cost) 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 pk_bytes, msg in pkm_pairs_for_conditions_dict( conditions_dict, coin_spend.coin.name(), additional_data): pk = blspy.G1Element.from_bytes(pk_bytes) pk_list.append(pk) msg_list.append(msg) if inspect.iscoroutinefunction(secret_key_for_public_key_f): secret_key = await secret_key_for_public_key_f(pk) else: secret_key = secret_key_for_public_key_f(pk) if secret_key is None: e_msg = f"no secret key for {pk}" raise ValueError(e_msg) assert bytes(secret_key.get_g1()) == bytes(pk) signature = AugSchemeMPL.sign(secret_key, msg) assert AugSchemeMPL.verify(pk, msg, signature) signatures.append(signature) # Aggregate signatures aggsig = AugSchemeMPL.aggregate(signatures) assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig) return SpendBundle(coin_spends, aggsig)
async def rl_sign_transaction(self, spends: List[CoinSolution]) -> SpendBundle: sigs = [] for coin_solution in spends: pubkey, secretkey = await self.get_keys(coin_solution.coin.puzzle_hash) signature = AugSchemeMPL.sign(secretkey, coin_solution.solution.get_tree_hash()) sigs.append(signature) aggsig = AugSchemeMPL.aggregate(sigs) return SpendBundle(spends, aggsig)
async def combine_coins(self, coins): # Overall structure: # Create len-1 spends that just assert that the final coin is created with full value. # Create 1 spend for the final coin that asserts the other spends occurred and # Creates the new coin. beginning_balance = self.balance() beginning_coins = len(self.usable_coins) # We need the final coin to know what the announced coin name will be. final_coin = CoinWrapper(coins[-1].name(), self.puzzle_hash, sum(map(lambda x: x.amount, coins)), self.puzzle) destroyed_coin_solutions = [] # Each coin wants agg_sig_me so we aggregate them at the end. signatures = [] for c in coins[:-1]: announce_conditions = [ # Each coin expects the final coin creation announcement [ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, std_hash(coins[-1].name() + final_coin.name()) ] ] coin_solution, signature = c.create_standard_spend( self.sk_, announce_conditions) destroyed_coin_solutions.append(coin_solution) signatures.append(signature) final_coin_creation = [ [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, final_coin.name()], [ConditionOpcode.CREATE_COIN, self.puzzle_hash, final_coin.amount], ] coin_solution, signature = coins[-1].create_standard_spend( self.sk_, final_coin_creation) destroyed_coin_solutions.append(coin_solution) signatures.append(signature) signature = AugSchemeMPL.aggregate(signatures) spend_bundle = SpendBundle(destroyed_coin_solutions, signature) pushed = await self.parent.push_tx(spend_bundle) # We should have the same amount of money. assert beginning_balance == self.balance() # We should have shredded n-1 coins and replaced one. assert len(self.usable_coins) == beginning_coins - (len(coins) - 1) return SpendResult(pushed)
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()
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
async def sign_clawback_transaction(self, spends: List[Tuple[Program, CoinSpend]], clawback_pubkey) -> SpendBundle: sigs = [] for puzzle, solution in spends: pubkey, secretkey = await self.get_keys_pk(clawback_pubkey) signature = AugSchemeMPL.sign(secretkey, solution.solution.get_tree_hash()) sigs.append(signature) aggsig = AugSchemeMPL.aggregate(sigs) solution_list = [] for puzzle, coin_spend in spends: solution_list.append(coin_spend) return SpendBundle(solution_list, aggsig)
async def check_conditions( condition_solution: Program, expected_err: Optional[Err] = None, spend_reward_index: int = -2 ): blocks = initial_blocks() coin = list(blocks[spend_reward_index].get_included_reward_coins())[0] coin_solution = CoinSolution(coin, EASY_PUZZLE, condition_solution) spend_bundle = SpendBundle([coin_solution], G2Element()) # now let's try to create a block with the spend bundle and ensure that it doesn't validate await check_spend_bundle_validity(bt.constants, blocks, spend_bundle, expected_err=expected_err)
def generate_secure_bundle( self, selected_coins: List[Coin], announcements: List[Announcement], offered_amount: uint64, tail_str: Optional[str] = None, ) -> SpendBundle: announcement_assertions: List[List] = [[63, a.name()] for a in announcements] selected_coin_amount: int = sum([c.amount for c in selected_coins]) non_primaries: List[Coin] = [] if len( selected_coins) < 2 else selected_coins[1:] inner_solution: List[List] = [ [51, Offer.ph(), offered_amount], # Offered coin [51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change *announcement_assertions, ] if tail_str is None: bundle = SpendBundle( [ CoinSpend( selected_coins[0], acs, Program.to(inner_solution), ), *[ CoinSpend(c, acs, Program.to([])) for c in non_primaries ], ], G2Element(), ) else: spendable_cats: List[SpendableCAT] = [ SpendableCAT( c, str_to_tail_hash(tail_str), acs, Program.to([ [51, 0, -113, str_to_tail(tail_str), Program.to([])], # Use the TAIL rather than lineage *(inner_solution if c == selected_coins[0] else []), ]), ) for c in selected_coins ] bundle = unsigned_spend_bundle_for_spendable_cats( CAT_MOD, spendable_cats) return bundle
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
async def rl_generate_signed_aggregation_transaction( self, rl_info, consolidating_coin, rl_parent, rl_coin): if (rl_info.limit is None or rl_info.interval is None or rl_info.user_pubkey is None or rl_info.admin_pubkey is None): raise ValueError("One or more of the elements of rl_info is None") if self.rl_coin_record is None: raise ValueError("Rl coin record is None") list_of_coin_solutions = [] self.rl_coin_record = await self._get_rl_coin_record() pubkey, secretkey = await self.get_keys( self.rl_coin_record.coin.puzzle_hash) # Spend wallet coin puzzle = rl_puzzle_for_pk( rl_info.user_pubkey, rl_info.limit, rl_info.interval, rl_info.rl_origin_id, rl_info.admin_pubkey, ) solution = rl_make_solution_mode_2( rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, rl_coin.parent_coin_info, rl_coin.amount, rl_parent.amount, rl_parent.parent_coin_info, ) signature = AugSchemeMPL.sign(secretkey, solution.get_tree_hash()) rl_spend = CoinSolution(self.rl_coin_record.coin, puzzle, solution) list_of_coin_solutions.append(rl_spend) # Spend consolidating coin puzzle = rl_make_aggregation_puzzle( self.rl_coin_record.coin.puzzle_hash) solution = rl_make_aggregation_solution( consolidating_coin.name(), self.rl_coin_record.coin.parent_coin_info, self.rl_coin_record.coin.amount, ) agg_spend = CoinSolution(consolidating_coin, puzzle, solution) list_of_coin_solutions.append(agg_spend) aggsig = AugSchemeMPL.aggregate([signature]) return SpendBundle(list_of_coin_solutions, aggsig)
def make_block_generator(count: int) -> SerializedProgram: puzzle_hash_db: Dict = dict() coins = [make_fake_coin(_, puzzle_hash_db) for _ in range(count)] coin_solutions = [] for coin in coins: puzzle_reveal = puzzle_hash_db[coin.puzzle_hash] conditions = conditions_for_payment(coin) solution = solution_for_conditions(conditions) coin_solution = CoinSolution(coin, puzzle_reveal, solution) coin_solutions.append(coin_solution) spend_bundle = SpendBundle(coin_solutions, blspy.G2Element()) return best_solution_program(spend_bundle)