diff --git a/electrum/commands.py b/electrum/commands.py index 933415cde..ebcaacce0 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -1415,6 +1415,7 @@ def add_global_options(parser): group.add_argument("--testnet", action="store_true", dest="testnet", default=False, help="Use Testnet") group.add_argument("--regtest", action="store_true", dest="regtest", default=False, help="Use Regtest") group.add_argument("--simnet", action="store_true", dest="simnet", default=False, help="Use Simnet") + group.add_argument("--signet", action="store_true", dest="signet", default=False, help="Use Signet") group.add_argument("-o", "--offline", action="store_true", dest="offline", default=False, help="Run offline") def add_wallet_option(parser): diff --git a/electrum/constants.py b/electrum/constants.py index 6e5f7435e..0ea93d319 100644 --- a/electrum/constants.py +++ b/electrum/constants.py @@ -60,11 +60,13 @@ class AbstractNet: class BitcoinMainnet(AbstractNet): + NET_NAME = "mainnet" TESTNET = False WIF_PREFIX = 0x80 ADDRTYPE_P2PKH = 0 ADDRTYPE_P2SH = 5 SEGWIT_HRP = "bc" + BOLT11_HRP = SEGWIT_HRP GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" DEFAULT_PORTS = {'t': '50001', 's': '50002'} DEFAULT_SERVERS = read_json('servers.json', {}) @@ -98,11 +100,13 @@ class BitcoinMainnet(AbstractNet): class BitcoinTestnet(AbstractNet): + NET_NAME = "testnet" TESTNET = True WIF_PREFIX = 0xef ADDRTYPE_P2PKH = 111 ADDRTYPE_P2SH = 196 SEGWIT_HRP = "tb" + BOLT11_HRP = SEGWIT_HRP GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" DEFAULT_PORTS = {'t': '51001', 's': '51002'} DEFAULT_SERVERS = read_json('servers_testnet.json', {}) @@ -134,7 +138,9 @@ class BitcoinTestnet(AbstractNet): class BitcoinRegtest(BitcoinTestnet): + NET_NAME = "regtest" SEGWIT_HRP = "bcrt" + BOLT11_HRP = SEGWIT_HRP GENESIS = "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" DEFAULT_SERVERS = read_json('servers_regtest.json', {}) CHECKPOINTS = [] @@ -143,19 +149,35 @@ class BitcoinRegtest(BitcoinTestnet): class BitcoinSimnet(BitcoinTestnet): + NET_NAME = "simnet" WIF_PREFIX = 0x64 ADDRTYPE_P2PKH = 0x3f ADDRTYPE_P2SH = 0x7b SEGWIT_HRP = "sb" + BOLT11_HRP = SEGWIT_HRP GENESIS = "683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6" DEFAULT_SERVERS = read_json('servers_regtest.json', {}) CHECKPOINTS = [] LN_DNS_SEEDS = [] +class BitcoinSignet(BitcoinTestnet): + + NET_NAME = "signet" + BOLT11_HRP = "tbs" + GENESIS = "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6" + DEFAULT_SERVERS = read_json('servers_signet.json', {}) + CHECKPOINTS = [] + LN_DNS_SEEDS = [] + + # don't import net directly, import the module instead (so that net is singleton) net = BitcoinMainnet +def set_signet(): + global net + net = BitcoinSignet + def set_simnet(): global net net = BitcoinSimnet @@ -168,7 +190,6 @@ def set_testnet(): global net net = BitcoinTestnet - def set_regtest(): global net net = BitcoinRegtest diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index c45c21004..f7af50165 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -541,7 +541,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.setGeometry(100, 100, 840, 400) def watching_only_changed(self): - name = "Electrum Testnet" if constants.net.TESTNET else "Electrum" + name = "Electrum" + if constants.net.TESTNET: + name += " " + constants.net.NET_NAME.capitalize() title = '%s %s - %s' % (name, ELECTRUM_VERSION, self.wallet.basename()) extra = [self.wallet.db.get('wallet_type', '?')] diff --git a/electrum/lnaddr.py b/electrum/lnaddr.py index cd6954ee8..0f2c0ef2a 100644 --- a/electrum/lnaddr.py +++ b/electrum/lnaddr.py @@ -178,6 +178,9 @@ def pull_tagged(stream): return (CHARSET[tag], stream.read(length * 5), stream) def lnencode(addr: 'LnAddr', privkey) -> str: + # see https://github.com/lightningnetwork/lightning-rfc/pull/844 + if constants.net.NET_NAME == "signet": + addr.currency = constants.net.BOLT11_HRP if addr.amount: amount = addr.currency + shorten_amount(addr.amount) else: @@ -367,6 +370,9 @@ class SerializableKey: def lndecode(invoice: str, *, verbose=False, expected_hrp=None) -> LnAddr: if expected_hrp is None: expected_hrp = constants.net.SEGWIT_HRP + # see https://github.com/lightningnetwork/lightning-rfc/pull/844 + if constants.net.NET_NAME == "signet": + expected_hrp = constants.net.BOLT11_HRP decoded_bech32 = bech32_decode(invoice, ignore_long_length=True) hrp = decoded_bech32.hrp data = decoded_bech32.data diff --git a/electrum/servers_signet.json b/electrum/servers_signet.json new file mode 100644 index 000000000..134e2682a --- /dev/null +++ b/electrum/servers_signet.json @@ -0,0 +1,14 @@ +{ + "127.0.0.1": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.2" + }, + "signet-electrumx.wakiyamap.dev": { + "pruning": "-", + "s": "50002", + "t": "50001", + "version": "1.4" + } +} diff --git a/electrum/simple_config.py b/electrum/simple_config.py index 3ab248da7..96e313096 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py @@ -128,6 +128,9 @@ class SimpleConfig(Logger): elif self.get('simnet'): path = os.path.join(path, 'simnet') make_dir(path, allow_symlink=False) + elif self.get('signet'): + path = os.path.join(path, 'signet') + make_dir(path, allow_symlink=False) self.logger.info(f"electrum directory {path}") return path diff --git a/electrum/trampoline.py b/electrum/trampoline.py index 07eed3ce8..f49f6fbda 100644 --- a/electrum/trampoline.py +++ b/electrum/trampoline.py @@ -63,11 +63,17 @@ TRAMPOLINE_NODES_TESTNET = { 'endurance': LNPeerAddr(host='34.250.234.192', port=9735, pubkey=bytes.fromhex('03933884aaf1d6b108397e5efe5c86bcf2d8ca8d2f700eda99db9214fc2712b134')), } +TRAMPOLINE_NODES_SIGNET = { + 'wakiyamap.dev': LNPeerAddr(host='signet-electrumx.wakiyamap.dev', port=9735, pubkey=bytes.fromhex('02dadf6c28f3284d591cd2a4189d1530c1ff82c07059ebea150a33ab76e7364b4a')), +} + def hardcoded_trampoline_nodes(): - if constants.net in (constants.BitcoinMainnet,): + if constants.net.NET_NAME == "mainnet": return TRAMPOLINE_NODES_MAINNET - if constants.net in (constants.BitcoinTestnet,): + if constants.net.NET_NAME == "testnet": return TRAMPOLINE_NODES_TESTNET + if constants.net.NET_NAME == "signet": + return TRAMPOLINE_NODES_SIGNET return {} def trampolines_by_id(): diff --git a/electrum/util.py b/electrum/util.py index 8f88b2b8d..12b69a82d 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -804,12 +804,29 @@ testnet_block_explorers = { {'tx': 'tx/', 'addr': 'address/'}), } +signet_block_explorers = { + 'bc-2.jp': ('https://explorer.bc-2.jp/', + {'tx': 'tx/', 'addr': 'address/'}), + 'mempool.space': ('https://mempool.space/signet/', + {'tx': 'tx/', 'addr': 'address/'}), + 'bitcoinexplorer.org': ('https://signet.bitcoinexplorer.org/', + {'tx': 'tx/', 'addr': 'address/'}), + 'wakiyamap.dev': ('https://signet-explorer.wakiyamap.dev/', + {'tx': 'tx/', 'addr': 'address/'}), + 'system default': ('blockchain:/', + {'tx': 'tx/', 'addr': 'address/'}), +} + _block_explorer_default_api_loc = {'tx': 'tx/', 'addr': 'address/'} def block_explorer_info(): from . import constants - return mainnet_block_explorers if not constants.net.TESTNET else testnet_block_explorers + if constants.net.NET_NAME == "testnet": + return testnet_block_explorers + elif constants.net.NET_NAME == "signet": + return signet_block_explorers + return mainnet_block_explorers def block_explorer(config: 'SimpleConfig') -> Optional[str]: diff --git a/run_electrum b/run_electrum index 3a086dc4f..0b593704c 100755 --- a/run_electrum +++ b/run_electrum @@ -362,6 +362,8 @@ def main(): constants.set_regtest() elif config.get('simnet'): constants.set_simnet() + elif config.get('signet'): + constants.set_signet() cmdname = config.get('cmd')