130 lines
4.3 KiB
Python
Executable file
130 lines
4.3 KiB
Python
Executable file
#!/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
|
|
|
|
"""
|
|
Bitcoin protocol
|
|
"""
|
|
|
|
from ..protocol import CoinProtocol,parsed_wif,parsed_addr,_finfo,_nw
|
|
from .common import *
|
|
|
|
class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp
|
|
"""
|
|
All Bitcoin code and chain forks inherit from this class
|
|
"""
|
|
mod_clsname = 'Bitcoin'
|
|
network_names = _nw('mainnet','testnet','regtest')
|
|
addr_ver_bytes = { '00': 'p2pkh', '05': 'p2sh' }
|
|
addr_len = 20
|
|
wif_ver_num = { 'std': '80' }
|
|
mmtypes = ('L','C','S','B')
|
|
dfl_mmtype = 'L'
|
|
coin_amt = 'BTCAmt'
|
|
max_tx_fee = '0.003'
|
|
sighash_type = 'ALL'
|
|
block0 = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
|
|
forks = [
|
|
_finfo(478559,'00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148','BCH',False),
|
|
]
|
|
caps = ('rbf','segwit')
|
|
mmcaps = ('key','addr','rpc','tx')
|
|
base_coin = 'BTC'
|
|
base_proto = 'Bitcoin'
|
|
# From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00,
|
|
# but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal).
|
|
witness_vernum_hex = '00'
|
|
witness_vernum = int(witness_vernum_hex,16)
|
|
bech32_hrp = 'bc'
|
|
sign_mode = 'daemon'
|
|
avg_bdi = int(9.7 * 60) # average block discovery interval (historical)
|
|
halving_interval = 210000
|
|
max_halvings = 64
|
|
start_subsidy = 50
|
|
ignore_daemon_version = False
|
|
max_int = 0xffffffff
|
|
|
|
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(
|
|
bytes.fromhex(self.wif_ver_num[pubkey_type])
|
|
+ privbytes
|
|
+ (b'',b'\x01')[bool(compressed)])
|
|
|
|
def parse_wif(self,wif):
|
|
key = b58chk_decode(wif)
|
|
|
|
for k,v in self.wif_ver_num.items():
|
|
v = bytes.fromhex(v)
|
|
if key[:len(v)] == v:
|
|
pubkey_type = k
|
|
key = key[len(v):]
|
|
break
|
|
else:
|
|
raise ValueError('Invalid WIF version number')
|
|
|
|
if len(key) == self.privkey_len + 1:
|
|
assert key[-1] == 0x01, f'{key[-1]!r}: invalid compressed key suffix byte'
|
|
compressed = True
|
|
elif len(key) == self.privkey_len:
|
|
compressed = False
|
|
else:
|
|
raise ValueError(f'{len(key)}: invalid key length')
|
|
|
|
return parsed_wif(
|
|
sec = key[:self.privkey_len],
|
|
pubkey_type = pubkey_type,
|
|
compressed = compressed )
|
|
|
|
def parse_addr(self,addr):
|
|
|
|
if 'B' in self.mmtypes and addr[:len(self.bech32_hrp)] == self.bech32_hrp:
|
|
import mmgen.contrib.bech32 as bech32
|
|
ret = bech32.decode(self.bech32_hrp,addr)
|
|
|
|
if ret[0] != self.witness_vernum:
|
|
from ..util import msg
|
|
msg(f'{ret[0]}: Invalid witness version number')
|
|
return False
|
|
|
|
return parsed_addr( bytes(ret[1]), 'bech32' ) if ret[1] else False
|
|
|
|
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(
|
|
self.addr_fmt_to_ver_bytes(('p2pkh','p2sh')[p2sh],return_hex=False) + pubkey_hash
|
|
)
|
|
|
|
# Segwit:
|
|
def pubkey2redeem_script(self,pubkey):
|
|
# https://bitcoincore.org/en/segwit_wallet_dev/
|
|
# The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed
|
|
# by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash})
|
|
return bytes.fromhex(self.witness_vernum_hex + '14') + hash160(pubkey)
|
|
|
|
def pubkey2segwitaddr(self,pubkey):
|
|
return self.pubhash2addr(
|
|
hash160( self.pubkey2redeem_script(pubkey)), p2sh=True )
|
|
|
|
def pubhash2bech32addr(self,pubhash):
|
|
d = list(pubhash)
|
|
import mmgen.contrib.bech32 as bech32
|
|
return bech32.bech32_encode(self.bech32_hrp,[self.witness_vernum]+bech32.convertbits(d,8,5))
|
|
|
|
class testnet(mainnet):
|
|
addr_ver_bytes = { '6f': 'p2pkh', 'c4': 'p2sh' }
|
|
wif_ver_num = { 'std': 'ef' }
|
|
bech32_hrp = 'tb'
|
|
|
|
class regtest(testnet):
|
|
bech32_hrp = 'bcrt'
|
|
halving_interval = 150
|