def signature_for_solution(self, solution): signatures = [] conditions_dict = conditions_by_opcode(conditions_for_solution(solution)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = self.sign(_) signatures.append(signature) return BLSSignature.aggregate(signatures)
def generate_recovery_transaction(self, coins, root_public_key, secret_key, escrow_duration): recovery_pubkey = root_public_key.public_child( 0).get_public_key().serialize() signatures = [] coin_solutions = [] secret_key = BLSPrivateKey(secret_key) for coin in coins: pubkey = self.find_pubkey_for_escrow_puzzle( coin, root_public_key, escrow_duration) puzzle = self.get_escrow_puzzle_with_params( recovery_pubkey, pubkey.serialize(), escrow_duration) op_create_coin = ConditionOpcode.CREATE_COIN[0] puzzlehash = f'0x' + str(hexbytes(self.get_new_puzzlehash())) solution_src = sexp( quote(sexp(sexp(op_create_coin, puzzlehash, coin.amount))), sexp(), 1) solution = Program(binutils.assemble(solution_src)) puzzle_solution_list = clvm.to_sexp_f([puzzle, solution]) coin_solution = CoinSolution(coin, puzzle_solution_list) coin_solutions.append(coin_solution) conditions_dict = conditions_by_opcode( conditions_for_solution(puzzle_solution_list)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = secret_key.sign(_.message_hash) signatures.append(signature) coin_solution_list = CoinSolutionList(coin_solutions) aggsig = BLSSignature.aggregate(signatures) spend_bundle = SpendBundle(coin_solution_list, aggsig) return spend_bundle
def generate_signatures(pst, private_wallet): """ For a given unfinalized SpendBundle, look at the hints to see if the given private wallet can generate any signatures, and generate them. """ hd_hints = pst.get("hd_hints") sigs = {} private_fingerprint = private_wallet.fingerprint() for coin_solution in pst.get("coin_solutions"): solution = coin_solution.solution # run maximal_solution and get conditions conditions_dict = conditions_dict_for_solution(solution) # look for AGG_SIG conditions hkp_list = hash_key_pairs_for_conditions_dict(conditions_dict) # see if we have enough info to build signatures for aggsig_pair in hkp_list: pub_key = aggsig_pair.public_key message_hash = aggsig_pair.message_hash fp = fingerprint_for_pk(pub_key) if fp in hd_hints: hint = hd_hints[fp] if private_fingerprint == hint.get("hd_fingerprint"): private_key = private_wallet.private_child(hint.get("index")) signature = private_key.sign(message_hash) sigs[aggsig_pair] = signature return list(sigs.values())
def sigs_to_aggsig_sig_dict(wallet, pst, sigs): """ Figure out which signatures in sigs correspond to which aggsig pairs in the unfinalized SpendBundle pst. Return a dictionary with keys that are aggsig pairs and signature values. This is an n^2 algorithm, so not ideal, but fine for small M and N. """ all_sigs_dict = {} all_aggsigs = set() for coin_solution in pst.get("coin_solutions"): solution = coin_solution.solution conditions_dict = conditions_dict_for_solution(solution) hkp_list = hash_key_pairs_for_conditions_dict(conditions_dict) all_aggsigs.update(hkp_list) for sig in sigs: for aggsig in all_aggsigs: if sig.validate([aggsig]): all_sigs_dict[aggsig] = sig all_aggsigs.remove(aggsig) break return all_sigs_dict
def sign_transaction(self, spends: (Program, [CoinSolution])): sigs = [] for puzzle, solution in spends: pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash) code_ = [puzzle, solution.solution] sexp = clvm.to_sexp_f(code_) conditions_dict = conditions_by_opcode( conditions_for_solution(sexp)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = secretkey.sign(_.message_hash) sigs.append(signature) aggsig = BLSSignature.aggregate(sigs) solution_list = CoinSolutionList( [CoinSolution(coin_solution.coin, clvm.to_sexp_f([puzzle, coin_solution.solution])) for (puzzle, coin_solution) in spends]) spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle
def finalize_pst(wallet, pst, sigs): """ Return a pair (SpendBundle or None, summary_list). If we have a finalized SpendBundle, it's returned, otherwise None, The summary_list item is a list of items (coin, hkp_list, sigs_to_use, m) which allows the UI to give the end user information about which coins still need signatures. Note that hkp is short for hash_key_pair (ie. aggsig pair) """ m = wallet.m() coin_solutions = [] sig_dict = sigs_to_aggsig_sig_dict(wallet, pst, sigs) all_sigs_to_use = [] summary_list = [] for coin_solution in pst.get("coin_solutions"): coin, solution = coin_solution.coin, coin_solution.solution # run maximal_solution and get conditions conditions_dict = conditions_dict_for_solution(solution) # look for AGG_SIG conditions hkp_list = hash_key_pairs_for_conditions_dict(conditions_dict) # see if we have enough info to build signatures found_list = [] sigs_to_use = [] for aggsig_pair in hkp_list: add_me = 0 if len(sigs_to_use) < m: if aggsig_pair in sig_dict: sigs_to_use.append(sig_dict[aggsig_pair]) add_me = 1 found_list.append(add_me) all_sigs_to_use.extend(sigs_to_use) conditions = pst.get("conditions") delegated_puzzle = puzzle_for_conditions(conditions) delegated_solution = solution_for_conditions(conditions) index = wallet.index_for_puzzle_hash(coin.puzzle_hash, GAP_LIMIT) pub_keys = wallet.pub_keys_for_index(index) actual_solution = solution_for_delegated_puzzle( m, pub_keys, found_list, delegated_puzzle, delegated_solution ) coin_solution = CoinSolution(coin, actual_solution) coin_solutions.append(coin_solution) summary = (coin, hkp_list, sigs_to_use, m) summary_list.append(summary) if len(all_sigs_to_use) > 0: aggregated_sig = all_sigs_to_use[0].aggregate(all_sigs_to_use) spend_bundle = SpendBundle(coin_solutions, aggregated_sig) try: if validate_spend_bundle_signature(spend_bundle): return spend_bundle, summary_list except Exception: pass return None, summary_list