mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-06 02:38:11 +01:00
Get Decred account key from seed.
This commit is contained in:
@@ -502,7 +502,9 @@ class BasicSwap(BaseApp):
|
||||
self.coin_clients[Coins.LTC_MWEB] = self.coin_clients[coin]
|
||||
|
||||
if self.coin_clients[coin]['connection_type'] == 'rpc':
|
||||
if coin == Coins.XMR:
|
||||
if coin == Coins.DCR:
|
||||
self.coin_clients[coin]['walletrpcport'] = chain_client_settings['walletrpcport']
|
||||
elif coin == Coins.XMR:
|
||||
self.coin_clients[coin]['rpctimeout'] = chain_client_settings.get('rpctimeout', 60)
|
||||
self.coin_clients[coin]['walletrpctimeout'] = chain_client_settings.get('walletrpctimeout', 120)
|
||||
self.coin_clients[coin]['walletrpctimeoutlong'] = chain_client_settings.get('walletrpctimeoutlong', 600)
|
||||
|
||||
@@ -38,6 +38,7 @@ from basicswap.util.address import (
|
||||
pubkeyToAddress,
|
||||
)
|
||||
from basicswap.util.crypto import (
|
||||
hash160,
|
||||
sha256,
|
||||
)
|
||||
from coincurve.keys import (
|
||||
@@ -70,8 +71,7 @@ from basicswap.contrib.test_framework.script import (
|
||||
OP_DROP,
|
||||
OP_HASH160, OP_EQUAL,
|
||||
SIGHASH_ALL,
|
||||
SegwitV0SignatureHash,
|
||||
hash160)
|
||||
SegwitV0SignatureHash)
|
||||
|
||||
from basicswap.basicswap_util import (
|
||||
TxLockTypes)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import logging
|
||||
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.interface.btc import Secp256k1Interface
|
||||
from basicswap.util.address import (
|
||||
@@ -13,8 +15,10 @@ from basicswap.util.address import (
|
||||
)
|
||||
from basicswap.util.crypto import (
|
||||
blake256,
|
||||
hash160,
|
||||
ripemd160,
|
||||
)
|
||||
from basicswap.util.extkey import ExtKeyPair
|
||||
from basicswap.interface.dcr.rpc import make_rpc_func
|
||||
|
||||
|
||||
@@ -45,7 +49,15 @@ class DCRInterface(Secp256k1Interface):
|
||||
self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
|
||||
self._rpcport = coin_settings['rpcport']
|
||||
self._rpcauth = coin_settings['rpcauth']
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
if 'walletrpcport' in coin_settings:
|
||||
self.rpc_wallet = make_rpc_func(coin_settings['walletrpcport'], self._rpcauth, host=self._rpc_host)
|
||||
else:
|
||||
self.rpc_wallet = None
|
||||
|
||||
self._use_segwit = coin_settings['use_segwit']
|
||||
|
||||
def pkh(self, pubkey: bytes) -> bytes:
|
||||
return ripemd160(blake256(pubkey))
|
||||
@@ -69,6 +81,41 @@ class DCRInterface(Secp256k1Interface):
|
||||
|
||||
def testDaemonRPC(self, with_wallet=True) -> None:
|
||||
if with_wallet:
|
||||
self.rpc_wallet('getwalletinfo')
|
||||
self.rpc_wallet('getinfo')
|
||||
else:
|
||||
self.rpc('getblockchaininfo')
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
# Only one wallet possible?
|
||||
return 1
|
||||
|
||||
def initialiseWallet(self, key: bytes) -> None:
|
||||
# Load with --create
|
||||
pass
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc('getnetworkinfo')['version']
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
return self.rpc('getblockchaininfo')
|
||||
|
||||
def using_segwit(self) -> bool:
|
||||
return self._use_segwit
|
||||
|
||||
def getWalletInfo(self):
|
||||
rv = self.rpc_wallet('getinfo')
|
||||
return rv
|
||||
|
||||
def getSeedHash(self, seed: bytes) -> bytes:
|
||||
# m / purpose' / coin_type' / account' / change / address_index
|
||||
# m/44'/coin_type'/0'/0/0
|
||||
|
||||
ek = ExtKeyPair(self.coin_type())
|
||||
ek.set_seed(seed)
|
||||
|
||||
coin_type = self.chainparams_network()['bip44']
|
||||
ek_purpose = ek.derive(44 | (1 << 31))
|
||||
ek_coin = ek_purpose.derive(coin_type | (1 << 31))
|
||||
ek_account = ek_coin.derive(0 | (1 << 31))
|
||||
|
||||
return hash160(ek_account.encode_p())
|
||||
|
||||
@@ -16,7 +16,6 @@ def callrpc(rpc_port, auth, method, params=[], host='127.0.0.1'):
|
||||
x.__handler = None
|
||||
v = x.json_request(method, params)
|
||||
x.close()
|
||||
print('[rm] v', v)
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -76,11 +76,11 @@ class NAVInterface(BTCInterface):
|
||||
# p2sh-p2wsh
|
||||
return True
|
||||
|
||||
def seedToMnemonic(self, key):
|
||||
def seedToMnemonic(self, key: bytes) -> None:
|
||||
return Mnemonic('english').to_mnemonic(key)
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
# load with -importmnemonic= parameter
|
||||
# Load with -importmnemonic= parameter
|
||||
pass
|
||||
|
||||
def getWalletSeedID(self):
|
||||
|
||||
@@ -93,7 +93,7 @@ class PARTInterface(BTCInterface):
|
||||
index_info = self.rpc('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo')
|
||||
return index_info['spentindex']
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
def initialiseWallet(self, key: bytes) -> None:
|
||||
raise ValueError('TODO')
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
|
||||
@@ -156,7 +156,7 @@ class XMRInterface(CoinInterface):
|
||||
pass
|
||||
self.rpc_wallet('open_wallet', params)
|
||||
|
||||
def initialiseWallet(self, key_view, key_spend, restore_height=None):
|
||||
def initialiseWallet(self, key_view: bytes, key_spend: bytes, restore_height=None) -> None:
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
@@ -90,7 +90,7 @@ def getKeyID(key_data: bytes) -> bytes:
|
||||
return ripemd160(sha256(key_data))
|
||||
|
||||
|
||||
def bech32Decode(hrp, addr):
|
||||
def bech32Decode(hrp: str, addr: str) -> bytes:
|
||||
hrpgot, data = bech32_decode(addr)
|
||||
if hrpgot != hrp:
|
||||
return None
|
||||
@@ -100,14 +100,14 @@ def bech32Decode(hrp, addr):
|
||||
return bytes(decoded)
|
||||
|
||||
|
||||
def bech32Encode(hrp, data):
|
||||
def bech32Encode(hrp: str, data: bytes) -> str:
|
||||
ret = bech32_encode(hrp, convertbits(data, 8, 5))
|
||||
if bech32Decode(hrp, ret) is None:
|
||||
return None
|
||||
return ret
|
||||
|
||||
|
||||
def decodeAddress(address: str):
|
||||
def decodeAddress(address: str) -> bytes:
|
||||
addr_data = b58decode(address)
|
||||
if addr_data is None:
|
||||
return None
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from Crypto.Hash import RIPEMD160, SHA256 # pycryptodome
|
||||
from basicswap.contrib.blake256.blake256 import blake_hash
|
||||
|
||||
from Crypto.Hash import HMAC, RIPEMD160, SHA256, SHA512 # pycryptodome
|
||||
|
||||
|
||||
def sha256(data: bytes) -> bytes:
|
||||
h = SHA256.new()
|
||||
@@ -14,6 +15,12 @@ def sha256(data: bytes) -> bytes:
|
||||
return h.digest()
|
||||
|
||||
|
||||
def sha512(data: bytes) -> bytes:
|
||||
h = SHA512.new()
|
||||
h.update(data)
|
||||
return h.digest()
|
||||
|
||||
|
||||
def ripemd160(data: bytes) -> bytes:
|
||||
h = RIPEMD160.new()
|
||||
h.update(data)
|
||||
@@ -24,5 +31,11 @@ def blake256(data: bytes) -> bytes:
|
||||
return blake_hash(data)
|
||||
|
||||
|
||||
def hash160(s: bytes) -> bytes:
|
||||
return ripemd160(sha256(s))
|
||||
def hash160(data: bytes) -> bytes:
|
||||
return ripemd160(sha256(data))
|
||||
|
||||
|
||||
def hmac_sha512(secret: bytes, data: bytes) -> bytes:
|
||||
h = HMAC.new(secret, digestmod=SHA512)
|
||||
h.update(data)
|
||||
return h.digest()
|
||||
|
||||
116
basicswap/util/extkey.py
Normal file
116
basicswap/util/extkey.py
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .crypto import blake256, hash160, hmac_sha512, ripemd160
|
||||
|
||||
from coincurve.keys import (
|
||||
PrivateKey,
|
||||
PublicKey)
|
||||
|
||||
|
||||
def BIP32Hash(chaincode: bytes, child_no: int, key_data_type: int, keydata: bytes):
|
||||
return hmac_sha512(chaincode, key_data_type.to_bytes(1) + keydata + child_no.to_bytes(4, 'big'))
|
||||
|
||||
|
||||
def hash160_dcr(data: bytes) -> bytes:
|
||||
return ripemd160(blake256(data))
|
||||
|
||||
|
||||
class ExtKeyPair():
|
||||
__slots__ = ('_depth', '_fingerprint', '_child_no', '_chaincode', '_key', '_pubkey', 'hash_func')
|
||||
|
||||
def __init__(self, coin_type=1):
|
||||
if coin_type == 4:
|
||||
self.hash_func = hash160_dcr
|
||||
else:
|
||||
self.hash_func = hash160
|
||||
|
||||
def set_seed(self, seed: bytes) -> None:
|
||||
hashout: bytes = hmac_sha512(b'Bitcoin seed', seed)
|
||||
self._key = hashout[:32]
|
||||
self._pubkey = None
|
||||
self._chaincode = hashout[32:]
|
||||
self._depth = 0
|
||||
self._child_no = 0
|
||||
self._fingerprint = b'\0' * 4
|
||||
|
||||
def has_key(self) -> bool:
|
||||
return False if self._key is None else True
|
||||
|
||||
def neuter(self) -> None:
|
||||
if self._key is None:
|
||||
raise ValueError('Already neutered')
|
||||
self._pubkey = PublicKey.from_secret(self._key).format()
|
||||
self._key = None
|
||||
|
||||
def derive(self, child_no: int):
|
||||
out = ExtKeyPair()
|
||||
out._depth = self._depth + 1
|
||||
out._child_no = child_no
|
||||
|
||||
if (child_no >> 31) == 0:
|
||||
if self._key:
|
||||
K = PublicKey.from_secret(self._key)
|
||||
k_encoded = K.format()
|
||||
else:
|
||||
K = PublicKey(self._pubkey)
|
||||
k_encoded = self._pubkey
|
||||
out._fingerprint = self.hash_func(k_encoded)[:4]
|
||||
new_hash = BIP32Hash(self._chaincode, child_no, k_encoded[0], k_encoded[1:])
|
||||
out._chaincode = new_hash[32:]
|
||||
|
||||
if self._key:
|
||||
k = PrivateKey(self._key)
|
||||
k.add(new_hash[:32], update=True)
|
||||
out._key = k.secret
|
||||
out._pubkey = None
|
||||
else:
|
||||
K.add(new_hash[:32], update=True)
|
||||
out._key = None
|
||||
out._pubkey = K.format()
|
||||
else:
|
||||
k = PrivateKey(self._key)
|
||||
out._fingerprint = self.hash_func(self._pubkey if self._pubkey else PublicKey.from_secret(self._key).format())[:4]
|
||||
new_hash = BIP32Hash(self._chaincode, child_no, 0, self._key)
|
||||
out._chaincode = new_hash[32:]
|
||||
k.add(new_hash[:32], update=True)
|
||||
out._key = k.secret
|
||||
out._pubkey = None
|
||||
|
||||
out.hash_func = self.hash_func
|
||||
return out
|
||||
|
||||
def encode_v(self) -> bytes:
|
||||
return self._depth.to_bytes(1) + \
|
||||
self._fingerprint + \
|
||||
self._child_no.to_bytes(4, 'big') + \
|
||||
self._chaincode + \
|
||||
b'\x00' + \
|
||||
self._key
|
||||
|
||||
def encode_p(self) -> bytes:
|
||||
pubkey = PublicKey.from_secret(self._key).format() if self._pubkey is None else self._pubkey
|
||||
return self._depth.to_bytes(1) + \
|
||||
self._fingerprint + \
|
||||
self._child_no.to_bytes(4, 'big') + \
|
||||
self._chaincode + \
|
||||
pubkey
|
||||
|
||||
def decode(self, data: bytes) -> None:
|
||||
if len(data) != 74:
|
||||
raise ValueError('Unexpected extkey length')
|
||||
self._depth = data[0]
|
||||
self._fingerprint = data[1:5]
|
||||
self._child_no = int.from_bytes(data[5:9], 'big')
|
||||
self._chaincode = data[9:41]
|
||||
|
||||
if data[41] == 0:
|
||||
self._key = data[42:]
|
||||
self._pubkey = None
|
||||
else:
|
||||
self._key = None
|
||||
self._pubkey = data[41:]
|
||||
Reference in New Issue
Block a user