Skip to content

Commit 52e9d3a

Browse files
committed
electrum plugin: Support multi-signature wallets out-of-the-box (#60)
1 parent d692e4f commit 52e9d3a

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
are now identified by the descriptor checksum, addresses have descriptors associated with them,
1212
and a new `bip32_origins` field is available based on the descriptor origin information.
1313

14+
- Support for Electrum multi-signature wallets
15+
16+
For a manual server setup, this requires using the `sortedmulti()` descriptor.
17+
For example, for a 2-of-3 wallet: `sortedmulti(2,xpub1...,xpub2...,xpub3...)`.
18+
19+
With the Electrum plugin, this should Just Work™.
20+
1421
- Alpha release of [`libbwt`](https://github.com/shesek/bwt/blob/master/doc/libbwt.md) (#64), a C FFI interface for managing the bwt servers,
1522
and of [`nodejs-bwt-daemon`](https://github.com/shesek/bwt/tree/master/contrib/nodejs-bwt-daemon) (#65), a nodejs package that wraps it.
1623

contrib/electrum-plugin/bwt.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import re
77

88
from electrum import constants
9+
from electrum.bip32 import BIP32Node
910
from electrum.plugin import BasePlugin, hook
1011
from electrum.i18n import _
1112
from electrum.util import UserFacingException
@@ -74,8 +75,12 @@ def start(self):
7475
args.extend([ '--unix-listener-path', self.socket_path ])
7576

7677
for wallet in self.wallets:
77-
for xpub in wallet.get_master_public_keys():
78+
if wallet.m is None:
79+
xpub = wallet.get_master_public_key()
7880
args.extend([ '--xpub', xpub ])
81+
else: # Multisig wallet
82+
for desc in get_multisig_descriptors(wallet):
83+
args.extend([ '--descriptor', desc ])
7984

8085
for i in range(self.verbose):
8186
args.append('-v')
@@ -198,6 +203,31 @@ def proc_logger(proc, log_handler):
198203
else:
199204
log_handler('INFO', 'bwt', line)
200205

206+
DESCRIPTOR_MAP_SH = {
207+
'p2sh': 'sh(%s)',
208+
'p2wsh': 'wsh(%s)',
209+
'p2wsh-p2sh': 'sh(wsh(%s))',
210+
}
211+
212+
def get_multisig_descriptors(wallet):
213+
descriptor_fmt = DESCRIPTOR_MAP_SH[wallet.txin_type]
214+
if not descriptor_fmt:
215+
_logger.warn('missing descriptor type for %s' % wallet.txin_type)
216+
return ()
217+
218+
xpubs = [convert_to_std_xpub(xpub) for xpub in wallet.get_master_public_keys()]
219+
def get_descriptor(child_index):
220+
desc_keys = ['%s/%d/*' % (xpub, child_index) for xpub in xpubs]
221+
return descriptor_fmt % 'sortedmulti(%d,%s)' % (wallet.m, ','.join(desc_keys))
222+
223+
# one for receive, one for change
224+
return (get_descriptor(0), get_descriptor(1))
225+
226+
# Convert SLIP32 ypubs/zpubs into standard BIP32 xpubs
227+
def convert_to_std_xpub(xpub):
228+
return BIP32Node.from_xkey(xpub) \
229+
._replace(xtype='standard') \
230+
.to_xpub()
201231

202232
def get_network_name():
203233
if constants.net == constants.BitcoinMainnet:

0 commit comments

Comments
 (0)