1 #! /usr/bin/env python3
5 Test with "python3 electrum.py doctest".
6 The tests will start the tor-browser so you probably
7 want to use a tester that handles user interaction or
8 run the tests from the command line and answer prompts as needed.
10 goodcrypto.com converted from bash to python and added basic tests.
13 >>> sh.Command(sys.argv[0])()
22 from dataclasses import dataclass
23 from pathlib import Path
25 # Make electrum use the cursor size configured in GNOME
27 os.environ["XCURSOR_SIZE"] = subprocess.check_output(
28 ["/usr/bin/gsettings", "get", "org.gnome.desktop.interface", "cursor-size"],
32 translation = gettext.translation("tails", "/usr/share/locale", fallback=True)
33 _ = translation.gettext
35 HOME_DIR = os.environ["HOME"]
36 CONF_DIR = Path(HOME_DIR, ".electrum")
41 text: str # "donate today to keep Tails thriving"
42 address: str # bc1asdfoobar
43 label: str # sth like "Donation to Tails"
44 probability: int # expressed in percent (ie to set it to 20%, just use 20)
47 def full_address(self):
48 return f"{self.address}?label={self.label}"
52 class CampaignOptions:
53 def __init__(self, *options: CampaignOption):
54 self.options = [opt for opt in options if opt.probability]
55 if sum(opt.probability for opt in self.options) != 100: # noqa: PLR2004
56 raise ValueError("probabilities must sum to 100")
58 def choose(self) -> CampaignOption:
59 num = random.randint(1, 100)
60 for opt in self.options:
61 num -= opt.probability
64 assert False, "We should never be there" # noqa: PT015,B011
67 # We use different addresses to track which version of the incentive leads to
69 CAMPAIGN = CampaignOptions(
72 "<b>Shape the future of Tails, today.</b>\n\n"
73 'Contribute to <a href="https://tails.net/news/2024-fundraiser/">our ongoing fundraiser</a>!',
75 "Tails 2024 fundraiser",
76 "bitcoin:bc1qunww28s9za0jx7n2r49yy5mqsgtgfwszz39kfr",
81 "<b>Your donations help us innovate and stay independent.</b>\n\n"
82 'Support our <a href="https://tails.net/news/2024-fundraiser/">ongoing fundraiser</a>!',
84 "bitcoin:bc1qceyaeuv7pfedshv8wzwwcwknhzjjnwmuv5qck9",
85 "Donate to Tails 2024",
90 "bitcoin:bc1qdptm9qzul00aejye7vfwwlas0xr0srj04573w4",
96 "bitcoin:bc1ql9dltq68wvkscj7zlrj2ajtgeupc6whsf62uef",
102 TPS_FEATURE_NAME = "Electrum"
106 # If we're called with arguments, don't show any messages or adjust
107 # any config and just start Electrum, to avoid breaking scripts /
108 # command-line usage.
112 if not electrum_config_is_persistent():
113 if not verify_start():
116 # Disable update checking for all users (even those who made their config
117 # persistent before we changed this value in the default config), because
118 # users can't easily update to a new version anyway if it's not in Debian.
120 ["/usr/bin/electrum", "--offline", "setconfig", "check_updates", "false"],
122 stdout=subprocess.DEVNULL,
125 if should_show_donation_message():
126 show_donation_message()
131 def exec_electrum(*args):
132 os.execv("/usr/bin/electrum", ["/usr/bin/electrum", *list(args)])
135 def should_show_donation_message() -> bool:
136 # Don't show the message if electrum is not persistent
137 if not electrum_config_is_persistent():
140 # Only show the message if the user has a wallet
141 if not any(Path(CONF_DIR, "wallets").iterdir()):
144 # Check the environment variable
145 if os.environ.get("ELECTRUM_DONATION_MESSAGE") == "0":
147 elif os.environ.get("ELECTRUM_DONATION_MESSAGE") == "1":
150 # If the other conditions are met, show the message in 1/10 of the
152 return random.randint(0, 9) == 0 # noqa: S311
155 def show_donation_message():
156 # To test the donation message, execute `ELECTRUM_DONATION_MESSAGE=1 electrum` with a Persistent Storage and a wallet
158 # results 0 == True; 1 == False; 5 == Timeout
172 "tails-logo-drawing",
178 if p.returncode == 0:
179 # Show a GNOME notification to let the user know that we're
180 # opening Electrum with our donation address.
181 subprocess.check_call(
183 "/usr/bin/notify-send",
185 _("Opening Electrum with our donation address..."),
186 _("Thank you for making the Internet a safer place!"),
189 exec_electrum(CAMPAIGN.full_address)
194 def electrum_config_is_persistent():
195 """Return True iff electrum config is persistent.
197 >>> electrum_config_is_persistent()
203 ["/usr/local/lib/tpscli", "is-active", TPS_FEATURE_NAME],
207 except subprocess.CalledProcessError as e:
208 if e.returncode != 1:
214 """Ask user whether to start Electrum.
216 >>> verify_start() in (True, False)
220 disabled_text = _("Persistence is disabled for Electrum")
222 "When you reboot Tails, all of Electrum's data will be lost, including your Bitcoin wallet.\nIt is strongly recommended to only run Electrum when its persistence feature is activated."
224 question_text = _("Do you want to start Electrum anyway?")
225 dialog_msg = "<b><big>{}</big></b>\n\n{}\n\n{}\n".format(
226 disabled_text, warning_text, question_text
228 launch_text = _("_Launch")
229 exit_text = _("_Exit")
248 return p.returncode == 0
251 if __name__ == "__main__":
252 if len(sys.argv) > 1 and sys.argv[1] == "doctest":