async def create_spend_bundle_relative_amount(self, cc_amount, zero_coin: Coin = None): # 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) 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 CoinSolutions solution = [ innersol, coin.as_list(), lineage_proof, None, None, None, None, None, ] full_solution = Program.to([puzzle_reveal, solution]) list_of_solutions.append(CoinSolution(coin, full_solution)) aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
async def generate_login_link(self, launcher_id: bytes32) -> Optional[str]: for pool_state in self.pool_state.values(): pool_config: PoolWalletConfig = pool_state["pool_config"] if pool_config.launcher_id == launcher_id: authentication_sk: Optional[ PrivateKey] = await find_authentication_sk( self.all_root_sks, pool_config.authentication_public_key) if authentication_sk is None: self.log.error( f"Could not find authentication sk for pk: {pool_config.authentication_public_key}" ) continue assert authentication_sk.get_g1( ) == pool_config.authentication_public_key authentication_token_timeout = pool_state[ "authentication_token_timeout"] authentication_token = get_current_authentication_token( authentication_token_timeout) message: bytes32 = std_hash( AuthenticationPayload("get_login", pool_config.launcher_id, pool_config.target_puzzle_hash, authentication_token)) signature: G2Element = AugSchemeMPL.sign( authentication_sk, message) return ( pool_config.pool_url + f"/login?launcher_id={launcher_id.hex()}&authentication_token={authentication_token}" f"&signature={bytes(signature).hex()}") return None
def get_plot_signature(self, header_data: HeaderData, plot_pk: G1Element) -> Optional[G2Element]: """ Returns the plot signature of the header data. """ farmer_sk = master_sk_to_farmer_sk(self.all_sks[0][0]) for _, plot_info in self.plots.items(): agg_pk = ProofOfSpace.generate_plot_public_key( plot_info.local_sk.get_g1(), plot_info.farmer_public_key) if agg_pk == plot_pk: m = header_data.get_hash() harv_share = AugSchemeMPL.sign(plot_info.local_sk, m, agg_pk) farm_share = AugSchemeMPL.sign(farmer_sk, m, agg_pk) return AugSchemeMPL.aggregate([harv_share, farm_share]) return None
async def start_pool_server(args): global server global runner private_key: PrivateKey = AugSchemeMPL.key_gen(std_hash(b"123")) config = load_config(DEFAULT_ROOT_PATH, "config.yaml") overrides = config["network_overrides"]["constants"][ config["selected_network"]] constants: ConsensusConstants = DEFAULT_CONSTANTS.replace_str_to_bytes( **overrides) server = PoolServer(private_key, config, constants, args) await server.start() # TODO(pool): support TLS app = web.Application() app.add_routes([ web.get("/", server.wrap_http_handler(server.index)), web.get("/get_pool_info", server.wrap_http_handler(server.get_pool_info)), web.post("/submit_partial", server.wrap_http_handler(server.submit_partial)), ]) runner = aiohttp.web.AppRunner(app, access_log=None) await runner.setup() site = aiohttp.web.TCPSite(runner, config["self_hostname"], int(3000)) await site.start() await asyncio.sleep(10000000)
async def download_backup(host: str, private_key: PrivateKey): session = aiohttp.ClientSession() try: backup_privkey = master_sk_to_backup_sk(private_key) backup_pubkey = bytes(backup_privkey.get_g1()).hex() # Get nonce nonce_request = {"pubkey": backup_pubkey} nonce_url = f"{host}/get_download_nonce" nonce_response = await post(session, nonce_url, nonce_request) nonce = nonce_response["nonce"] # Sign nonce signature = bytes( AugSchemeMPL.sign(backup_privkey, std_hash(hexstr_to_bytes(nonce)))).hex() # Request backup url get_backup_url = f"{host}/download_backup" backup_request = {"pubkey": backup_pubkey, "signature": signature} backup_response = await post(session, get_backup_url, backup_request) if backup_response["success"] is False: raise ValueError("No backup on backup service") # Download from s3 backup_url = backup_response["url"] backup_text = await get(session, backup_url) await session.close() return backup_text except Exception as e: await session.close() # Pass exception raise e
async def get_farmer(self, request_obj) -> web.Response: # TODO(pool): add rate limiting launcher_id = hexstr_to_bytes(request_obj.rel_url.query["launcher_id"]) authentication_token = uint64(request_obj.rel_url.query["authentication_token"]) authentication_token_error: Optional[web.Response] = check_authentication_token( launcher_id, authentication_token, self.pool.authentication_token_timeout ) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id) if farmer_record is None: return error_response( PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {launcher_id.hex()} unknown." ) # Validate provided signature signature: G2Element = G2Element.from_bytes(hexstr_to_bytes(request_obj.rel_url.query["signature"])) message = std_hash(launcher_id + bytes(authentication_token)) if not AugSchemeMPL.verify(farmer_record.authentication_public_key, message, signature): return error_response( PoolErrorCode.INVALID_SIGNATURE, f"Failed to verify signature {signature} for launcher_id {launcher_id.hex()}.", ) response: GetFarmerResponse = GetFarmerResponse( farmer_record.authentication_public_key, farmer_record.payout_instructions, farmer_record.difficulty, farmer_record.points, ) self.pool.log.info(f"get_farmer response {response.to_json_dict()}, " f"launcher_id: {launcher_id.hex()}") return obj_to_response(response)
async def sign_clawback_transaction(self, spends: List[Tuple[Program, CoinSolution]], 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_solution in spends: solution_list.append(coin_solution) return SpendBundle(solution_list, aggsig)
async def get_login(self, request_obj) -> web.Response: # TODO(pool): add rate limiting launcher_id = request_obj.rel_url.query["launcher_id"] authentication_token = request_obj.rel_url.query["authentication_token"] authentication_token_error = check_authentication_token( launcher_id, authentication_token, self.pool.authentication_token_timeout ) if authentication_token_error is not None: return authentication_token_error farmer_record: Optional[FarmerRecord] = await self.pool.store.get_farmer_record(launcher_id) if farmer_record is None: return error_response(PoolErrorCode.FARMER_NOT_KNOWN, f"Farmer with launcher_id {launcher_id} unknown.") # Validate provided signature signature = request_obj.rel_url.query["signature"] message = std_hash(launcher_id + bytes(authentication_token)) if not AugSchemeMPL.verify(farmer_record.authentication_public_key, message, signature): return error_response( PoolErrorCode.INVALID_SIGNATURE, f"Failed to verify signature {signature} for launcher_id {launcher_id}.", ) self.pool.log.info(f"Login successful for launcher_id: {launcher_id}") # TODO(pool) Do what ever you like with the successful login return obj_to_response({"login_data", "Put server side login information here?"})
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
async def create_wallet_backup(self, file_path: Path): all_wallets = await self.get_all_wallet_info_entries() for wallet in all_wallets: if wallet.id == 1: all_wallets.remove(wallet) break backup_pk = master_sk_to_backup_sk(self.private_key) now = uint64(int(time.time())) wallet_backup = WalletInfoBackup(all_wallets) backup: Dict[str, Any] = {} data = wallet_backup.to_json_dict() data["version"] = __version__ data["fingerprint"] = self.private_key.get_g1().get_fingerprint() data["timestamp"] = now data["start_height"] = await self.get_start_height() key_base_64 = base64.b64encode(bytes(backup_pk)) f = Fernet(key_base_64) data_bytes = json.dumps(data).encode() encrypted = f.encrypt(data_bytes) meta_data: Dict[str, Any] = {"timestamp": now, "pubkey": bytes(backup_pk.get_g1()).hex()} meta_data_bytes = json.dumps(meta_data).encode() signature = bytes(AugSchemeMPL.sign(backup_pk, std_hash(encrypted) + std_hash(meta_data_bytes))).hex() backup["data"] = encrypted.decode() backup["meta_data"] = meta_data backup["signature"] = signature backup_file_text = json.dumps(backup) file_path.write_text(backup_file_text)
def get_pool_key_signature(self, pool_target: PoolTarget, pool_pk: G1Element) -> Optional[G2Element]: for sk, _ in self.all_sks: sk_child = master_sk_to_pool_sk(sk) if sk_child.get_g1() == pool_pk: return AugSchemeMPL.sign(sk_child, bytes(pool_target)) return None
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, origin_id: bytes, innerpuz: Program): # 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([coin.parent_coin_info, coin.amount, innersol]) list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = bytes(coin.puzzle_hash) + 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) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
def get_pairings(cache: LRUCache, pks: List[G1Element], msgs: List[bytes], force_cache: bool) -> List[GTElement]: pairings: List[Optional[GTElement]] = [] missing_count: int = 0 for pk, msg in zip(pks, msgs): aug_msg: bytes = bytes(pk) + msg h: bytes = bytes(std_hash(aug_msg)) pairing: Optional[GTElement] = cache.get(h) if not force_cache and pairing is None: missing_count += 1 # Heuristic to avoid more expensive sig validation with pairing # cache when it's empty and cached pairings won't be useful later # (e.g. while syncing) if missing_count > len(pks) // 2: return [] pairings.append(pairing) for i, pairing in enumerate(pairings): if pairing is None: aug_msg = bytes(pks[i]) + msgs[i] aug_hash: G2Element = AugSchemeMPL.g2_from_message(aug_msg) pairing = pks[i].pair(aug_hash) h = bytes(std_hash(aug_msg)) cache.put(h, pairing) pairings[i] = pairing return pairings
def sign(self, public_key: bytes, message_hash: bytes32) -> G2Element: secret_exponent = self.get(public_key) if not secret_exponent: raise ValueError("unknown pubkey %s" % public_key.hex()) bls_private_key = PrivateKey.from_bytes( secret_exponent.to_bytes(32, "big")) return AugSchemeMPL.sign(bls_private_key, message_hash)
async def request_signatures(self, request: harvester_protocol.RequestSignatures): """ The farmer requests a signature on the header hash, for one of the proofs that we found. A signature is created on the header hash using the harvester private key. This can also be used for pooling. """ plot_filename = Path(request.plot_identifier[64:]).resolve() try: plot_info = self.harvester.provers[plot_filename] except KeyError: self.harvester.log.warning(f"KeyError plot {plot_filename} does not exist.") return local_sk = plot_info.local_sk agg_pk = ProofOfSpace.generate_plot_public_key(local_sk.get_g1(), plot_info.farmer_public_key) # This is only a partial signature. When combined with the farmer's half, it will # form a complete PrependSignature. message_signatures: List[Tuple[bytes32, G2Element]] = [] for message in request.messages: signature: G2Element = AugSchemeMPL.sign(local_sk, message, agg_pk) message_signatures.append((message, signature)) response: harvester_protocol.RespondSignatures = harvester_protocol.RespondSignatures( request.plot_identifier, request.challenge_hash, request.sp_hash, local_sk.get_g1(), plot_info.farmer_public_key, message_signatures, ) return make_msg(ProtocolMessageTypes.respond_signatures, response)
def init_plots(self, root_path): plot_dir = get_plot_dir() mkdir(plot_dir) temp_dir = plot_dir / "tmp" mkdir(temp_dir) args = Namespace() # Can't go much lower than 20, since plots start having no solutions and more buggy args.size = 22 # Uses many plots for testing, in order to guarantee proofs of space at every height args.num = 20 args.buffer = 100 args.farmer_public_key = bytes(self.farmer_pk).hex() args.pool_public_key = bytes(self.pool_pk).hex() args.tmp_dir = temp_dir args.tmp2_dir = plot_dir args.final_dir = plot_dir args.plotid = None args.memo = None args.buckets = 0 args.stripe_size = 2000 args.num_threads = 0 args.nobitfield = False args.exclude_final_dir = False test_private_keys = [AugSchemeMPL.key_gen(std_hash(i.to_bytes(2, "big"))) for i in range(args.num)] try: # No datetime in the filename, to get deterministic filenames and not re-plot create_plots( args, root_path, use_datetime=False, test_private_keys=test_private_keys, ) except KeyboardInterrupt: shutil.rmtree(plot_dir, ignore_errors=True) sys.exit(1)
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()
def private_key_from_mnemonic_seed_file(filename: Path) -> PrivateKey: """ Create a private key from a mnemonic seed file. """ mnemonic = filename.read_text().rstrip() seed = mnemonic_to_seed(mnemonic, "") return AugSchemeMPL.key_gen(seed)
def sign_tx(pks: List[str], spend_bundle: SpendBundle): # This field is the ADDITIONAL_DATA found in the constants additional_data: bytes = bytes.fromhex( "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb") puzzle_hash_to_sk: Dict[bytes32, PrivateKey] = {} for p in pks: child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(p)) # master_private_key = PrivateKey.from_bytes( # bytes.fromhex(p)) # child_sk = master_sk_to_wallet_sk(master_private_key, 242) child_pk: G1Element = child_sk.get_g1() puzzle = puzzle_for_pk(child_pk) puzzle_hash = puzzle.get_tree_hash() puzzle_hash_to_sk[puzzle_hash] = child_sk aggregate_signature: G2Element = G2Element() for coin_solution in spend_bundle.coin_solutions: if coin_solution.coin.puzzle_hash not in puzzle_hash_to_sk: return sk: PrivateKey = puzzle_hash_to_sk[coin_solution.coin.puzzle_hash] synthetic_secret_key: PrivateKey = calculate_synthetic_secret_key( sk, DEFAULT_HIDDEN_PUZZLE_HASH) err, conditions_dict, cost = conditions_dict_for_solution( coin_solution.puzzle_reveal, coin_solution.solution, 11000000000) if err or conditions_dict is None: print( f"Sign transaction failed, con:{conditions_dict}, error: {err}" ) return pk_msgs = pkm_pairs_for_conditions_dict( conditions_dict, bytes(coin_solution.coin.name()), additional_data) assert len(pk_msgs) == 1 _, msg = pk_msgs[0] signature = AugSchemeMPL.sign(synthetic_secret_key, msg) aggregate_signature = AugSchemeMPL.aggregate( [aggregate_signature, signature]) new_spend_bundle = SpendBundle(spend_bundle.coin_solutions, aggregate_signature) # print(json.dumps(new_spend_bundle.to_json_dict())) return json.dumps(new_spend_bundle.to_json_dict())
def aggregate(cls, spend_bundles) -> "SpendBundle": coin_solutions: List[CoinSolution] = [] sigs = [] for _ in spend_bundles: coin_solutions += _.coin_solutions sigs.append(_.aggregated_signature) aggregated_signature = AugSchemeMPL.aggregate(sigs) return cls(coin_solutions, aggregated_signature)
def sign(message: str, fingerprint: int, hd_path: str, as_bytes: bool): k = Keychain() private_keys = k.get_all_private_keys() path: List[uint32] = [ uint32(int(i)) for i in hd_path.split("/") if i != "m" ] for sk, _ in private_keys: if sk.get_g1().get_fingerprint() == fingerprint: for c in path: sk = AugSchemeMPL.derive_child_sk(sk, c) data = bytes.fromhex(message) if as_bytes else bytes( message, "utf-8") print("Public key:", sk.get_g1()) print("Signature:", AugSchemeMPL.sign(sk, data)) return None print(f"Fingerprint {fingerprint} not found in keychain")
def aggregate(cls, spend_bundles) -> "SpendBundle": coin_spends: List[CoinSpend] = [] sigs: List[G2Element] = [] for bundle in spend_bundles: coin_spends += bundle.coin_spends sigs.append(bundle.aggregated_signature) aggregated_signature = AugSchemeMPL.aggregate(sigs) return cls(coin_spends, aggregated_signature)
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.solution) 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())): signature = AugSchemeMPL.sign(synthetic_secret_key, msg) signatures.append(signature) aggsig = AugSchemeMPL.aggregate(signatures) spend_bundle = SpendBundle(coin_solutions, aggsig) return spend_bundle
async def test_plot_signature(self, initial_blockchain): blocks, b = initial_blockchain # Time too far in the past block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( blocks[9].header.data, AugSchemeMPL.sign( AugSchemeMPL.key_gen(bytes([5] * 32)), token_bytes(32) ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_PLOT_SIGNATURE
def create_not_ready_alert_file(alert_file_path: Path, key): file_dict = { "ready": False, } data: str = json.dumps(file_dict) signature = AugSchemeMPL.sign(key, bytes(data, "utf-8")) file_data = {"data": data, "signature": f"{signature}"} file_data_json = json.dumps(file_data) alert_file_path.write_text(file_data_json)
def signature_for_solution(self, coin_solution: CoinSolution) -> AugSchemeMPL: signatures = [] err, conditions, cost = conditions_for_solution(coin_solution.puzzle_reveal, coin_solution.solution) assert conditions is not None conditions_dict = conditions_by_opcode(conditions) for public_key, message_hash in pkm_pairs_for_conditions_dict(conditions_dict, coin_solution.coin.name()): signature = self.sign(bytes(public_key), message_hash) signatures.append(signature) return AugSchemeMPL.aggregate(signatures)
def __init__(self, sk: Optional[PrivateKey] = None): self.current_balance = 0 self.my_utxos: set = set() if sk is not None: self.private_key = sk else: self.private_key = AugSchemeMPL.key_gen(DEFAULT_SEED) self.generator_lookups: Dict = {} self.puzzle_pk_cache: Dict = {} self.get_new_puzzle()
def validate_alert(text: str, pubkey: str) -> bool: json_obj = json.loads(text) data = json_obj["data"] message = bytes(data, "UTF-8") signature = json_obj["signature"] signature = SignatureMPL.from_bytes(hexstr_to_bytes(signature)) pubkey_bls = PublicKeyMPL.from_bytes(hexstr_to_bytes(pubkey)) sig_match_my = AugSchemeMPL.verify(pubkey_bls, message, signature) return sig_match_my
async def sign_transaction( self, spends: List[Tuple[Program, CoinSolution]] ) -> Optional[SpendBundle]: signatures = [] for puzzle, solution in spends: # Get keys keys = await self.wallet_state_manager.get_keys(solution.coin.puzzle_hash) if not keys: self.log.error( f"Sign transaction failed, No Keys for puzzlehash {solution.coin.puzzle_hash}" ) return None pubkey, secretkey = keys code_ = [puzzle, solution.solution] sexp = Program.to(code_) # Get AGGSIG conditions err, con, cost = conditions_for_solution(sexp) if err or not con: self.log.error(f"Sign transcation failed, con:{con}, error: {err}") return None conditions_dict = conditions_by_opcode(con) # Create signature for _, msg in pkm_pairs_for_conditions_dict( conditions_dict, bytes(solution.coin) ): signature = AugSchemeMPL.sign(secretkey, msg) signatures.append(signature) # Aggregate signatures aggsig = AugSchemeMPL.aggregate(signatures) solution_list: List[CoinSolution] = [ CoinSolution( coin_solution.coin, Program.to([puzzle, coin_solution.solution]) ) for (puzzle, coin_solution) in spends ] spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle
async def rl_sign_transaction( self, spends: List[Tuple[Program, CoinSolution]]) -> SpendBundle: sigs = [] for puzzle, solution in spends: pubkey, secretkey = await self.get_keys(solution.coin.puzzle_hash) signature = AugSchemeMPL.sign( secretkey, Program(solution.solution).get_tree_hash()) sigs.append(signature) aggsig = AugSchemeMPL.aggregate(sigs) solution_list: List[CoinSolution] = [] for puzzle, coin_solution in spends: solution_list.append( CoinSolution(coin_solution.coin, Program.to([puzzle, coin_solution.solution]))) return SpendBundle(solution_list, aggsig)