Store public-private mappings in wallet

This commit is contained in:
hackrush 2019-05-19 02:14:33 +05:30 committed by Lex Berezhny
parent 4dae1ae05f
commit 7a78d49e13
3 changed files with 48 additions and 97 deletions

View file

@ -1900,7 +1900,10 @@ class Daemon(metaclass=JSONRPCServerType):
if not preview: if not preview:
await tx.sign([account]) await tx.sign([account])
await account.ledger.broadcast(tx) await account.ledger.broadcast(tx)
account.add_channel_private_key(txo.ref, txo.private_key) channel_pubkey_hash = account.ledger.public_key_to_address(
txo.claim.channel.public_key_bytes
)
account.add_channel_private_key(channel_pubkey_hash, txo.ref.id, txo.private_key)
self.default_wallet.save() self.default_wallet.save()
await self.storage.save_claims([self._old_get_temp_claim_info( await self.storage.save_claims([self._old_get_temp_claim_info(
tx, txo, claim_address, claim, name, dewies_to_lbc(amount) tx, txo, claim_address, claim, name, dewies_to_lbc(amount)
@ -2038,7 +2041,10 @@ class Daemon(metaclass=JSONRPCServerType):
if not preview: if not preview:
await tx.sign([account]) await tx.sign([account])
await account.ledger.broadcast(tx) await account.ledger.broadcast(tx)
account.add_channel_private_key(new_txo.ref, new_txo.private_key) channel_pubkey_hash = account.ledger.public_key_to_address(
new_txo.claim.channel.public_key_bytes
)
account.add_channel_private_key(channel_pubkey_hash, new_txo.ref.id, new_txo.private_key)
self.default_wallet.save() self.default_wallet.save()
await self.storage.save_claims([self._old_get_temp_claim_info( await self.storage.save_claims([self._old_get_temp_claim_info(
tx, new_txo, claim_address, new_txo.claim, new_txo.claim_name, dewies_to_lbc(amount) tx, new_txo, claim_address, new_txo.claim, new_txo.claim_name, dewies_to_lbc(amount)

View file

@ -1,11 +1,12 @@
import hashlib
import json import json
import logging import logging
import binascii
from hashlib import sha256 from hashlib import sha256
from string import hexdigits from string import hexdigits
import ecdsa
from torba.client.baseaccount import BaseAccount, HierarchicalDeterministic from torba.client.baseaccount import BaseAccount, HierarchicalDeterministic
from torba.client.basetransaction import TXORef
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -37,12 +38,14 @@ class Account(BaseAccount):
super().apply(d) super().apply(d)
self.channel_keys.update(d.get('certificates', {})) self.channel_keys.update(d.get('certificates', {}))
def add_channel_private_key(self, ref: TXORef, private_key): def add_channel_private_key(self, channel_pubkey_hash, ref_id, private_key):
assert ref.id not in self.channel_keys, 'Trying to add a duplicate channel private key.' assert channel_pubkey_hash not in self.channel_keys, 'Trying to add a duplicate channel private key.'
self.channel_keys[ref.id] = private_key assert ref_id not in self.channel_keys, 'Trying to add a duplicate channel private key.'
self.channel_keys[ref_id] = private_key
self.channel_keys[channel_pubkey_hash] = private_key
def get_channel_private_key(self, ref: TXORef): def get_channel_private_key(self, channel_pubkey_hash):
return self.channel_keys.get(ref.id) return self.channel_keys.get(channel_pubkey_hash)
async def maybe_migrate_certificates(self): async def maybe_migrate_certificates(self):
if not self.channel_keys: if not self.channel_keys:
@ -51,111 +54,44 @@ class Account(BaseAccount):
addresses = {} addresses = {}
results = { results = {
'total': 0, 'total': 0,
'not-a-claim-tx': 0, 'old-tx-pri-key-map': 0,
'migrate-success': 0, 'migrate-success': 0,
'migrate-failed': 0, 'migrate-failed': 0,
'previous-success': 0, 'previous-success': 0,
'previous-corrupted': 0 'previous-corrupted': 0
} }
double_hex_encoded_to_pop = []
for maybe_claim_id in list(self.channel_keys): new_channel_keys = {}
if ':' not in maybe_claim_id:
try:
validate_claim_id(maybe_claim_id)
continue
except Exception:
try:
maybe_claim_id_bytes = maybe_claim_id
if isinstance(maybe_claim_id_bytes, str):
maybe_claim_id_bytes = maybe_claim_id_bytes.encode()
decoded_double_hex = binascii.unhexlify(maybe_claim_id_bytes).decode()
validate_claim_id(decoded_double_hex)
if decoded_double_hex in self.channel_keys:
log.warning("don't know how to migrate certificate %s", decoded_double_hex)
else:
log.info("claim id was double hex encoded, fixing it")
double_hex_encoded_to_pop.append((maybe_claim_id, decoded_double_hex))
except Exception:
continue
for double_encoded_claim_id, correct_claim_id in double_hex_encoded_to_pop: for maybe_outpoint in self.channel_keys:
self.channel_keys[correct_claim_id] = self.channel_keys.pop(double_encoded_claim_id)
for maybe_claim_id in list(self.channel_keys):
results['total'] += 1 results['total'] += 1
if ':' not in maybe_claim_id: if ':' in maybe_outpoint:
results['old-tx-pri-key-map'] += 1
try: try:
validate_claim_id(maybe_claim_id) private_key_pem = self.channel_keys[maybe_outpoint]
pubkey_hash = self._get_pubkey_address_from_private_key_pem(private_key_pem)
if pubkey_hash not in new_channel_keys and pubkey_hash not in self.channel_keys:
new_channel_keys[pubkey_hash] = private_key_pem
results['migrate-success'] += 1
except Exception as e: except Exception as e:
log.warning(
"Failed to migrate claim '%s': %s",
maybe_claim_id, str(e)
)
results['migrate-failed'] += 1
continue
claims = await self.ledger.network.get_claims_by_ids([maybe_claim_id])
if maybe_claim_id not in claims:
log.warning(
"Failed to migrate claim '%s', server did not return any claim information.",
maybe_claim_id
)
results['migrate-failed'] += 1
continue
claim = claims[maybe_claim_id]
tx = None
if claim:
tx = await self.ledger.db.get_transaction(txid=claim['txid'])
else:
log.warning(maybe_claim_id)
if tx is not None:
txo = tx.outputs[claim['nout']]
if not txo.script.is_claim_involved:
results['not-a-claim-tx'] += 1
raise ValueError(
"Certificate with claim_id {} doesn't point to a valid transaction."
.format(maybe_claim_id)
)
tx_nout = '{txid}:{nout}'.format(**claim)
self.channel_keys[tx_nout] = self.channel_keys[maybe_claim_id]
del self.channel_keys[maybe_claim_id]
log.info(
"Migrated certificate with claim_id '%s' ('%s') to a new look up key %s.",
maybe_claim_id, txo.script.values['claim_name'], tx_nout
)
results['migrate-success'] += 1
else:
if claim:
addresses.setdefault(claim['address'], 0)
addresses[claim['address']] += 1
log.warning(
"Failed to migrate claim '%s', it's not associated with any of your addresses.",
maybe_claim_id
)
else:
log.warning(
"Failed to migrate claim '%s', it appears abandoned.",
maybe_claim_id
)
results['migrate-failed'] += 1 results['migrate-failed'] += 1
log.warning("Failed to migrate certificate for %s, incorrect private key: %s",
maybe_outpoint, str(e))
else: else:
try: try:
txid, nout = maybe_claim_id.split(':') pubkey_hash = self._get_pubkey_address_from_private_key_pem(self.channel_keys[maybe_outpoint])
tx = await self.ledger.db.get_transaction(txid=txid) if pubkey_hash == maybe_outpoint:
if not tx:
log.warning(
"Claim migration failed to find a transaction for outpoint %s", maybe_claim_id
)
results['previous-corrupted'] += 1
continue
if tx.outputs[int(nout)].script.is_claim_involved:
results['previous-success'] += 1 results['previous-success'] += 1
else: else:
results['previous-corrupted'] += 1 results['previous-corrupted'] += 1
except Exception: except Exception as e:
log.exception("Couldn't verify certificate with look up key: %s", maybe_claim_id) log.warning("Corrupt public:private key-pair: %s", str(e))
results['previous-corrupted'] += 1 results['previous-corrupted'] += 1
for key in new_channel_keys:
self.channel_keys[key] = new_channel_keys[key]
self.wallet.save() self.wallet.save()
log.info('verifying and possibly migrating certificates:') log.info('verifying and possibly migrating certificates:')
log.info(json.dumps(results, indent=2)) log.info(json.dumps(results, indent=2))
@ -239,3 +175,9 @@ class Account(BaseAccount):
async def release_all_outputs(self): async def release_all_outputs(self):
await self.ledger.db.release_all_outputs(self) await self.ledger.db.release_all_outputs(self)
def _get_pubkey_address_from_private_key_pem(self, private_key_pem):
private_key = ecdsa.SigningKey.from_pem(private_key_pem, hashfunc=hashlib.sha256)
public_key_der = private_key.get_verifying_key().to_der()
return self.ledger.public_key_to_address(public_key_der)

View file

@ -63,7 +63,10 @@ class WalletDatabase(BaseDatabase):
if txo.claim.is_signed: if txo.claim.is_signed:
channel_ids.add(txo.claim.signing_channel_id) channel_ids.add(txo.claim.signing_channel_id)
if txo.claim.is_channel and my_account is not None: if txo.claim.is_channel and my_account is not None:
txo.private_key = my_account.get_channel_private_key(txo.ref) channel_pubkey_hash = my_account.ledger.public_key_to_address(
txo.claim.channel.public_key_bytes
)
txo.private_key = my_account.get_channel_private_key(channel_pubkey_hash)
if channel_ids: if channel_ids:
channels = { channels = {