add proto.common module

This commit is contained in:
The MMGen Project 2022-02-03 20:40:40 +00:00
commit 362d8cfeed
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
9 changed files with 69 additions and 53 deletions

View file

@ -20,7 +20,7 @@
addrgen.py: Address and view key generation classes for the MMGen suite
"""
from .proto.btc import hash160,_b58chk_encode
from .proto.common import hash160,b58chk_encode
from .addr import CoinAddr,MMGenAddrType,MoneroViewKey,ZcashViewKey
# decorator for to_addr() and to_viewkey()
@ -118,14 +118,14 @@ class addr_generator:
@check_data
def to_addr(self,data):
ret = _b58chk_encode(
ret = b58chk_encode(
self.proto.addr_fmt_to_ver_bytes('zcash_z')
+ data.pubkey )
return CoinAddr( self.proto, ret )
@check_data
def to_viewkey(self,data):
ret = _b58chk_encode(
ret = b58chk_encode(
self.proto.addr_fmt_to_ver_bytes('viewkey')
+ data.viewkey_bytes )
return ZcashViewKey( self.proto, ret )

View file

@ -281,7 +281,7 @@ class AddrList(MMGenObject): # Address info for a single seed ID
return out
def gen_wallet_passwd(self,privbytes):
from .proto.btc import hash256
from .proto.common import hash256
return WalletPassword( hash256(privbytes)[:16].hex() )
def check_format(self,addr):

View file

@ -556,10 +556,10 @@ class CoinInfo(object):
return '1'
def phash2addr(ver_num,pk_hash):
from .proto.btc import _b58chk_encode
from .proto.common import b58chk_encode
bl = ver_num.bit_length()
ver_bytes = int.to_bytes(ver_num,bl//8 + bool(bl%8),'big')
return _b58chk_encode(ver_bytes + pk_hash)
return b58chk_encode(ver_bytes + pk_hash)
low = phash2addr(ver_num,b'\x00'*20)
high = phash2addr(ver_num,b'\xff'*20)

View file

@ -12,38 +12,8 @@
Bitcoin protocol
"""
from ..protocol import CoinProtocol,parsed_wif,parsed_addr,_finfo,_b58a,_nw
import hashlib
def hash160(in_bytes): # OP_HASH160
return hashlib.new('ripemd160',hashlib.sha256(in_bytes).digest()).digest()
def hash256(in_bytes): # OP_HASH256
return hashlib.sha256(hashlib.sha256(in_bytes).digest()).digest()
# From en.bitcoin.it:
# The Base58 encoding used is home made, and has some differences.
# Especially, leading zeroes are kept as single zeroes when conversion happens.
# Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
# The 'zero address':
# 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20)
def _b58chk_encode(in_bytes):
lzeroes = len(in_bytes) - len(in_bytes.lstrip(b'\x00'))
def do_enc(n):
while n:
yield _b58a[n % 58]
n //= 58
return ('1' * lzeroes) + ''.join(do_enc(int.from_bytes(in_bytes+hash256(in_bytes)[:4],'big')))[::-1]
def _b58chk_decode(s):
lzeroes = len(s) - len(s.lstrip('1'))
res = sum(_b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1]))
bl = res.bit_length()
out = b'\x00' * lzeroes + res.to_bytes(bl//8 + bool(bl%8),'big')
if out[-4:] != hash256(out[:-4])[:4]:
raise ValueError('_b58chk_decode(): incorrect checksum')
return out[:-4]
from ..protocol import CoinProtocol,parsed_wif,parsed_addr,_finfo,_nw
from .common import *
class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp
"""
@ -83,13 +53,13 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp
def bytes2wif(self,privbytes,pubkey_type,compressed): # input is preprocessed hex
assert len(privbytes) == self.privkey_len, f'{len(privbytes)} bytes: incorrect private key length!'
assert pubkey_type in self.wif_ver_num, f'{pubkey_type!r}: invalid pubkey_type'
return _b58chk_encode(
return b58chk_encode(
bytes.fromhex(self.wif_ver_num[pubkey_type])
+ privbytes
+ (b'',b'\x01')[bool(compressed)])
def parse_wif(self,wif):
key = _b58chk_decode(wif)
key = b58chk_decode(wif)
for k,v in self.wif_ver_num.items():
v = bytes.fromhex(v)
@ -126,11 +96,11 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp
return parsed_addr( bytes(ret[1]), 'bech32' ) if ret[1] else False
return self.parse_addr_bytes(_b58chk_decode(addr))
return self.parse_addr_bytes(b58chk_decode(addr))
def pubhash2addr(self,pubkey_hash,p2sh):
assert len(pubkey_hash) == 20, f'{len(pubkey_hash)}: invalid length for pubkey hash'
return _b58chk_encode(
return b58chk_encode(
self.addr_fmt_to_ver_bytes(('p2pkh','p2sh')[p2sh],return_hex=False) + pubkey_hash
)

47
mmgen/proto/common.py Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen
# https://gitlab.com/mmgen/mmgen
"""
Functions and constants used by multiple protocols
"""
import hashlib
b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def hash160(in_bytes): # OP_HASH160
return hashlib.new('ripemd160',hashlib.sha256(in_bytes).digest()).digest()
def hash256(in_bytes): # OP_HASH256
return hashlib.sha256(hashlib.sha256(in_bytes).digest()).digest()
# From en.bitcoin.it:
# The Base58 encoding used is home made, and has some differences.
# Especially, leading zeroes are kept as single zeroes when conversion happens.
# Test: 5JbQQTs3cnoYN9vDYaGY6nhQ1DggVsY4FJNBUfEfpSQqrEp3srk
# The 'zero address':
# 1111111111111111111114oLvT2 (pubkeyhash = '\0'*20)
def b58chk_encode(in_bytes):
lzeroes = len(in_bytes) - len(in_bytes.lstrip(b'\x00'))
def do_enc(n):
while n:
yield b58a[n % 58]
n //= 58
return ('1' * lzeroes) + ''.join(do_enc(int.from_bytes(in_bytes+hash256(in_bytes)[:4],'big')))[::-1]
def b58chk_decode(s):
lzeroes = len(s) - len(s.lstrip('1'))
res = sum(b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1]))
bl = res.bit_length()
out = b'\x00' * lzeroes + res.to_bytes(bl//8 + bool(bl%8),'big')
if out[-4:] != hash256(out[:-4])[:4]:
raise ValueError('b58chk_decode(): incorrect checksum')
return out[:-4]

View file

@ -31,8 +31,6 @@ parsed_addr = namedtuple('parsed_addr',['bytes','fmt'])
_finfo = namedtuple('fork_info',['height','hash','name','replayable'])
_nw = namedtuple('coin_networks',['mainnet','testnet','regtest'])
_b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' # shared by Bitcoin and Monero
class CoinProtocol(MMGenObject):
proto_info = namedtuple('proto_info',['name','trust_level']) # trust levels: see altcoin.py

View file

@ -133,7 +133,7 @@ class tool_cmd(tool_cmd_base):
if self.mmtype.name == 'segwit':
return self.proto.pubkey2segwitaddr( pubkey )
else:
from ..proto.btc import hash160
from ..proto.common import hash160
return self.pubhash2addr( hash160(pubkey).hex() )
def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
@ -146,7 +146,7 @@ class tool_cmd(tool_cmd_base):
assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
assert redeem_scripthex[:4] == '0014', f'{redeem_scripthex!r}: invalid redeem script'
assert len(redeem_scripthex) == 44, f'{len(redeem_scripthex)//2} bytes: invalid redeem script length'
from ..proto.btc import hash160
from ..proto.common import hash160
return self.pubhash2addr( hash160(bytes.fromhex(redeem_scripthex)).hex() )
def pubhash2addr(self,pubhashhex:'sstr'):

View file

@ -78,7 +78,7 @@ class tool_cmd(tool_cmd_base):
def hash160(self,hexstr:'sstr'):
"compute ripemd160(sha256(data)) (convert hex pubkey to hex addr)"
from ..proto.btc import hash160
from ..proto.common import hash160
return hash160( bytes.fromhex(hexstr) ).hex()
def hash256(self,string_or_bytes:str,file_input=False,hex_input=False): # TODO: handle stdin
@ -143,13 +143,13 @@ class tool_cmd(tool_cmd_base):
def hextob58chk(self,hexstr:'sstr'):
"convert a hexadecimal number to base58-check encoding"
from ..proto.btc import _b58chk_encode
return _b58chk_encode( bytes.fromhex(hexstr) )
from ..proto.common import b58chk_encode
return b58chk_encode( bytes.fromhex(hexstr) )
def b58chktohex(self,b58chk_num:'sstr'):
"convert a base58-check encoded number to hexadecimal"
from ..proto.btc import _b58chk_decode
return _b58chk_decode(b58chk_num).hex()
from ..proto.common import b58chk_decode
return b58chk_decode(b58chk_num).hex()
def hextob32(self,hexstr:'sstr',pad=0):
"convert a hexadecimal number to MMGen's flavor of base 32"

View file

@ -26,7 +26,8 @@ from .common import *
from .objmethods import Hilite,InitErrors
from .obj import CoinTxID
from .seed import SeedID
from .protocol import init_proto,_b58a
from .protocol import init_proto
from .proto.common import b58a
from .addr import CoinAddr,AddrIdx
from .addrlist import KeyAddrList,AddrIdxList
from .rpc import MoneroRPCClientRaw,MoneroWalletRPCClient,json_encoder
@ -36,7 +37,7 @@ xmrwallet_uarg_info = (
lambda e,hp: {
'daemon': e('HOST:PORT', hp),
'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', rf'({hp})(?::({hp}))?'),
'transfer_spec': e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{_b58a}]+),([0-9.]+)'),
'transfer_spec': e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
'sweep_spec': e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
})(
namedtuple('uarg_info_entry',['annot','pat']),