mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-13 12:33:40 +02:00
Fix signmessage to be Electrum-compatible
Verification of message signatures against segwit addresses is not currently functional/possible in Core, and additionally the signing function used for messages in jmbitcoin, derived from coincurve, was not compatible with Electrum either. This commit uses the functionality in python-bitcointx's signmessage module, and now the wallet method `signmessage` creates signatures on messages against segwit addresses that are verifiable in Electrum. Test cases of p2sh and native are added.
This commit is contained in:
parent
a5e886944c
commit
2e50d0334c
4 changed files with 62 additions and 8 deletions
|
|
@ -27,10 +27,12 @@ from bitcointx.core import (x, b2x, b2lx, lx, COutPoint, CTxOut, CTxIn,
|
|||
coins_to_satoshi, satoshi_to_coins)
|
||||
from bitcointx.core.key import KeyStore
|
||||
from bitcointx.wallet import (P2SHCoinAddress, P2SHCoinAddressError,
|
||||
P2WPKHCoinAddress, P2WPKHCoinAddressError)
|
||||
P2WPKHCoinAddress, P2WPKHCoinAddressError,
|
||||
CBitcoinKey)
|
||||
from bitcointx.core.script import (CScript, OP_0, SignatureHash, SIGHASH_ALL,
|
||||
SIGVERSION_WITNESS_V0, CScriptWitness)
|
||||
from bitcointx.core.psbt import (PartiallySignedTransaction, PSBT_Input,
|
||||
PSBT_Output)
|
||||
from bitcointx.signmessage import SignMessage
|
||||
from .blocks import get_transactions_in_block
|
||||
|
||||
|
|
|
|||
|
|
@ -218,13 +218,19 @@ class BTCEngine(object):
|
|||
@staticmethod
|
||||
def sign_message(privkey, message):
|
||||
"""
|
||||
Note: only (currently) used for manual
|
||||
signing of text messages by keys,
|
||||
*not* used in Joinmarket communication protocol.
|
||||
args:
|
||||
privkey: bytes
|
||||
message: bytes
|
||||
returns:
|
||||
base64-encoded signature
|
||||
"""
|
||||
return btc.ecdsa_sign(message, privkey, True, False)
|
||||
# note: only supported on mainnet
|
||||
assert get_network() == "mainnet"
|
||||
k = btc.CBitcoinKey(BTCEngine.privkey_to_wif(privkey))
|
||||
return btc.SignMessage(k, btc.BitcoinMessage(message)).decode("ascii")
|
||||
|
||||
@classmethod
|
||||
def script_to_address(cls, script):
|
||||
|
|
|
|||
|
|
@ -1052,7 +1052,17 @@ def wallet_dumpprivkey(wallet, hdpath):
|
|||
return wallet.get_wif_path(path) # will raise exception on invalid path
|
||||
|
||||
|
||||
def wallet_signmessage(wallet, hdpath, message):
|
||||
def wallet_signmessage(wallet, hdpath, message, out_str=True):
|
||||
""" Given a wallet, a BIP32 HD path (as can be output
|
||||
from the display method) and a message string, returns
|
||||
a base64 encoded signature along with the corresponding
|
||||
address and message.
|
||||
If `out_str` is True, returns human readable representation,
|
||||
otherwise returns tuple of (signature, message, address).
|
||||
"""
|
||||
if not get_network() == "mainnet":
|
||||
return "Error: message signing is only supported on mainnet."
|
||||
|
||||
msg = message.encode('utf-8')
|
||||
|
||||
if not hdpath:
|
||||
|
|
@ -1062,8 +1072,12 @@ def wallet_signmessage(wallet, hdpath, message):
|
|||
|
||||
path = wallet.path_repr_to_path(hdpath)
|
||||
sig = wallet.sign_message(msg, path)
|
||||
retval = "Signature: {}\nTo verify this in Bitcoin Core".format(sig)
|
||||
return retval + " use the RPC command 'verifymessage'"
|
||||
addr = wallet.get_address_from_path(path)
|
||||
if not out_str:
|
||||
return (sig, message, addr)
|
||||
return ("Signature: {}\nMessage: {}\nAddress: {}\n"
|
||||
"To verify this in Electrum use Tools->Sign/verify "
|
||||
"message.".format(sig, message, addr))
|
||||
|
||||
def wallet_signpsbt(wallet_service, psbt):
|
||||
if not psbt:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,39 @@
|
|||
import pytest
|
||||
from jmbitcoin import select_chain_params
|
||||
from jmclient import (SegwitLegacyWallet, SegwitWallet, get_network,
|
||||
jm_single, VolatileStorage, load_test_config)
|
||||
from jmclient.wallet_utils import (bip32pathparse, WalletView,
|
||||
WalletViewAccount, WalletViewBranch,
|
||||
WalletViewEntry, wallet_signmessage)
|
||||
|
||||
from jmclient.wallet_utils import bip32pathparse, WalletView, \
|
||||
WalletViewAccount, WalletViewBranch, WalletViewEntry
|
||||
|
||||
# The below signatures have all been verified against Electrum 4.0.9:
|
||||
@pytest.mark.parametrize('seed, hdpath, walletcls, message, sig, addr', [
|
||||
[b"\x01"*16, "m/84'/0'/0'/0/0", SegwitWallet, "hello",
|
||||
"IOLk6ct/8aKtvTNnEAc+xojIWKv5FOwnzHGcnHkTJJwRBAyhrZ2ZyB0Re+dKS4SEav3qgjQeqMYRm+7mHi4sFKA=",
|
||||
"bc1qq53d9372u8d50jfd5agq9zv7m7zdnzwducuqgz"],
|
||||
[b"\x01"*16, "m/49'/0'/0'/0/0", SegwitLegacyWallet, "hello",
|
||||
"HxVaQuXyBpl1UKutiusJjeLfKHwJYBzUiWuu6hEbmNFeSZGt/mbXKJ071ANR1gvdICbS/AnEa2RKDq9xMd/nU8s=",
|
||||
"3AdTcqdoLHFGNq6znkahJDT41u65HAwiRv"],
|
||||
[b"\x02"*16, "m/84'/0'/2'/1/0", SegwitWallet, "sign me",
|
||||
"IA/V5DG7u108aNzCnpNPHqfrJAL8pF4GQ0sSqpf4Vlg5UWizauXzh2KskoD6Usl13hzqXBi4XDXl7Xxo5z6M298=",
|
||||
"bc1q8mm69xs740sr0l2umrhmpl4ewhxfudxg2zvjw5"],
|
||||
[b"\x02"*16, "m/49'/0'/2'/1/0", SegwitLegacyWallet, "sign me",
|
||||
"H4cAtoE+zL+Mr+U8jm9DiYxZlym5xeZM3mcgymLz+TF4YYr4lgnM8qTZhFwlK4izcPaLuF27LFEoGJ/ltleIHUI=",
|
||||
"3Qan1D4Vcy1yMGHfR9j7szDuC8QxSFVScA"],
|
||||
])
|
||||
def test_signmessage(seed, hdpath, walletcls, message, sig, addr):
|
||||
load_test_config()
|
||||
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
|
||||
select_chain_params("bitcoin/mainnet")
|
||||
storage = VolatileStorage()
|
||||
walletcls.initialize(
|
||||
storage, get_network(), entropy=seed, max_mixdepth=3)
|
||||
wallet = walletcls(storage)
|
||||
s, m, a = wallet_signmessage(wallet, hdpath, message,
|
||||
out_str=False)
|
||||
assert (s, m, a) == (sig, message, addr)
|
||||
jm_single().config.set("BLOCKCHAIN", "network", "testnet")
|
||||
select_chain_params("bitcoin/regtest")
|
||||
|
||||
def test_bip32_pathparse():
|
||||
assert bip32pathparse("m/2/1/0017")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue