├── README.md ├── start-dn.py └── yield-generators ├── README.md └── yg-acyclic.py /README.md: -------------------------------------------------------------------------------- 1 | # custom-scripts 2 | Alternatives to the default scripts for Joinmarket (especially yield-generators) 3 | -------------------------------------------------------------------------------- /start-dn.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import sys 4 | from twisted.python.log import startLogging 5 | from optparse import OptionParser 6 | from jmbase import get_log, commands 7 | from jmclient import (Maker, jm_single, load_program_config, 8 | JMClientProtocolFactory, start_reactor, 9 | add_base_options, JMMakerClientProtocol, 10 | get_mchannels) 11 | import jmdaemon 12 | from jmbase.support import EXIT_ARGERROR, JM_APP_NAME, JM_CORE_VERSION 13 | 14 | jlog = get_log() 15 | 16 | # need to patch the Maker client protocol so that it doesn't 17 | # insist on a non-empty offer list: 18 | class DNMakerClientProtocol(JMMakerClientProtocol): 19 | 20 | @commands.JMUp.responder 21 | def on_JM_UP(self): 22 | d = self.callRemote(commands.JMSetup, 23 | role="MAKER", 24 | initdata=self.client.offerlist, 25 | use_fidelity_bond=(self.client.fidelity_bond is not None)) 26 | self.defaultCallbacks(d) 27 | return {'accepted': True} 28 | 29 | class DNJMClientProtocolFactory(JMClientProtocolFactory): 30 | def __init__(self, client, proto_type="TAKER"): 31 | self.client = client 32 | self.proto_client = None 33 | self.proto_type = proto_type 34 | if self.proto_type == "MAKER": 35 | self.protocol = DNMakerClientProtocol 36 | 37 | # Next we make a patch to the daemon-side so that orderbook 38 | # requests are ignored, otherwise our empty offer list will 39 | # causes crashes: 40 | def announce_no_orders(self, orderlist, nick, fidelity_bond_proof_msg, new_mc): 41 | return 42 | jmdaemon.MessageChannelCollection.announce_orders = announce_no_orders 43 | 44 | # Now we create a super-dumbed down type of Maker, 45 | # with no offers and no wallet: 46 | class DNMaker(Maker): 47 | def __init__(self): 48 | # Note: we do not call the superclass init; 49 | # nothing needs to be done, and there is no wallet. 50 | # We set items that get referred to in client-daemon 51 | # communication: 52 | self.fidelity_bond = None 53 | self.offerlist = [] 54 | self.aborted = False 55 | 56 | # implementations of ABC methods 57 | def create_my_orders(self): 58 | return [] 59 | 60 | # none of the remainder can ever get called: 61 | def oid_to_order(self, cjorder, amount): 62 | pass 63 | 64 | def on_tx_unconfirmed(self, cjorder, txid): 65 | pass 66 | 67 | def on_tx_confirmed(self, cjorder, txid, confirmations): 68 | pass 69 | 70 | def get_fidelity_bond_template(self): 71 | return None 72 | 73 | def directory_node_startup(): 74 | parser = OptionParser(usage='usage: %prog [options]') 75 | add_base_options(parser) 76 | (options, args) = parser.parse_args() 77 | # for string access, convert to dict: 78 | options = vars(options) 79 | if len(args) != 1: 80 | parser.error('One argument required: string to be published in the MOTD of the directory node.') 81 | sys.exit(EXIT_ARGERROR) 82 | operator_message = args[0] 83 | # It's possible to set `no-blockchain` in the config file, but this just 84 | # makes it easier for the user: 85 | load_program_config(config_path=options["datadir"], bs="no-blockchain") 86 | # note: you *must* only have the onionmc, no IRC, for this to work, 87 | # and of course you must only have your own d-node configured here: 88 | mchan_config = get_mchannels()[0] 89 | node_location = mchan_config["directory_nodes"] 90 | # before starting, patch the server handshake default to include our MOTD customization 91 | # default acceptance false; code must switch it on: 92 | jmdaemon.onionmc.server_handshake_json[ 93 | "motd"] = "DIRECTORY NODE: {}\nJOINMARKET VERSION: {}\n{}".format( 94 | node_location, JM_CORE_VERSION, operator_message) 95 | maker = DNMaker() 96 | jlog.info('starting directory node') 97 | clientfactory = DNJMClientProtocolFactory(maker, proto_type="MAKER") 98 | nodaemon = jm_single().config.getint("DAEMON", "no_daemon") 99 | daemon = bool(nodaemon) 100 | if jm_single().config.get("BLOCKCHAIN", "network") in ["regtest", "testnet", "signet"]: 101 | startLogging(sys.stdout) 102 | start_reactor(jm_single().config.get("DAEMON", "daemon_host"), 103 | jm_single().config.getint("DAEMON", "daemon_port"), 104 | clientfactory, daemon=daemon) 105 | 106 | if __name__ == "__main__": 107 | directory_node_startup() 108 | 109 | -------------------------------------------------------------------------------- /yield-generators/README.md: -------------------------------------------------------------------------------- 1 | # yield-generators 2 | Different yield generators are collected here, which can replace joinmarket's defaults `yield-generator-basic.py`, `yg-privacyenhanced.py` if desired. 3 | 4 | Each file beginning with "yg-" is a different one. Read the comment at the beginning of a yield generator's file to see what it is doing differently and what its intended purpose is. 5 | 6 | To use one of these, copy the yg-file of your choice into your joinmarket-clientserver/scripts directory, modify its default settings (optional) and start it just like you would start the default yield generator: `python yg-filename.py walletfilename.jmdat`. 7 | -------------------------------------------------------------------------------- /yield-generators/yg-acyclic.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | from __future__ import (absolute_import, division, 3 | print_function, unicode_literals) 4 | from builtins import * # noqa: F401 5 | from future.utils import iteritems 6 | 7 | from jmbase import jmprint 8 | from jmclient import YieldGeneratorBasic, ygmain 9 | 10 | 11 | # YIELD GENERATOR SETTINGS ARE NOW IN YOUR joinmarket.cfg CONFIG FILE 12 | # (You can also use command line flags; see --help for this script). 13 | 14 | 15 | class YieldGeneratorAcyclic(YieldGeneratorBasic): 16 | """A yield-generator bot that sends funds linearly through the 17 | mixdepths, but not back from the "lowest" depth to the beginning. 18 | Instead, it lets funds accumulate there, so that they can then be manually 19 | sent elsewhere as needed.""" 20 | 21 | def __init__(self, wallet_service, offerconfig): 22 | super(YieldGeneratorAcyclic, self).__init__(wallet_service, offerconfig) 23 | 24 | def get_available_mixdepths(self): 25 | balances = self.wallet_service.get_balance_by_mixdepth(verbose=False) 26 | return {m: b for m, b in iteritems(balances) 27 | if m < self.wallet_service.mixdepth} 28 | 29 | 30 | if __name__ == "__main__": 31 | ygmain(YieldGeneratorAcyclic, nickserv_password='') 32 | jmprint('done', "success") 33 | --------------------------------------------------------------------------------