Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: new accounts #125

Merged
merged 3 commits into from
Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 29 additions & 21 deletions decred/decred/dcr/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ class Account:

def __init__(
self,
idx,
pubKeyEncrypted,
privKeyEncrypted,
name,
Expand All @@ -802,6 +803,7 @@ def __init__(
):
"""
Args:
idx (int): The BIP-0044 account index.
pubKeyEncrypted (ByteArray): The encrypted public key bytes.
privKeyEncrypted (ByteArray): The encrypted private key bytes.
name (str): Name for the account.
Expand All @@ -815,6 +817,7 @@ def __init__(
signals (Signals): A signaller. Only used if db and blockchain are
specified.
"""
self.idx = idx
self.pubKeyEncrypted = pubKeyEncrypted
self.privKeyEncrypted = privKeyEncrypted
self.name = name
Expand Down Expand Up @@ -867,6 +870,7 @@ def blob(acct):
"""Satisfies the encode.Blobber API"""
return (
BuildyBytes(0)
.addData(encode.intToBytes(acct.idx))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the encoding without updating the version. Sync from mnemonic seed. I am planning on stopping doing this soon, maybe after we resolve #102.

.addData(acct.pubKeyEncrypted)
.addData(acct.privKeyEncrypted)
.addData(acct.name.encode("utf-8"))
Expand All @@ -881,18 +885,19 @@ def blob(acct):
def unblob(b):
"""Satisfies the encode.Blobber API"""
ver, d = encode.decodeBlob(b)
unblob_check("Account", ver, len(d), {0: 7})
unblob_check("Account", ver, len(d), {0: 8})

iFunc = encode.intFromBytes

pubEnc = ByteArray(d[0])
privEnc = ByteArray(d[1])
name = d[2].decode("utf-8")
netID = d[3].decode("utf-8")
acct = Account(pubEnc, privEnc, name, netID)
acct.cursorExt = iFunc(d[4], signed=True)
acct.cursorInt = iFunc(d[5])
acct.gapLimit = iFunc(d[6])
idx = iFunc(d[0])
pubEnc = ByteArray(d[1])
privEnc = ByteArray(d[2])
name = d[3].decode("utf-8")
netID = d[4].decode("utf-8")
acct = Account(idx, pubEnc, privEnc, name, netID)
acct.cursorExt = iFunc(d[5], signed=True)
acct.cursorInt = iFunc(d[6])
acct.gapLimit = iFunc(d[7])
return acct

def serialize(self):
Expand Down Expand Up @@ -1432,7 +1437,7 @@ def nextExternalAddress(self):
self.nextBranchAddress(self.extPub, extAddrs, self.addrExtDB)
addr = extAddrs[idx]
if self.blockchain:
self.blockchain.subscribeAddresses(addr)
self.blockchain.subscribeAddresses(addr, self.addressSignal)
return addr

def nextInternalAddress(self):
Expand Down Expand Up @@ -1955,14 +1960,13 @@ def sync(self):
signals.balance(self.calcBalance())
self.generateGapAddresses()

# If there is a chosen stake pool, sync the purchaseInfo.
# TODO: Save purchase info
# If there is a chosen stake pool, authorize the pool.
stakePool = self.stakePool()
if stakePool:
try:
stakePool.getPurchaseInfo()
stakePool.authorize(self.votingAddress().string())
Comment on lines -1963 to +1967
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoeGruffins I didn't see any problem calling authorize here instead of below. authorize calls purchaseInfo internally. Is this OK?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

except Exception as e:
log.error("error getting VSP purchase info: %s" % e)
log.error("error authorizing VSP: %s" % e)

# First, look at addresses that have been generated but not seen. Run in
# loop until the gap limit is reached.
Expand All @@ -1975,35 +1979,39 @@ def sync(self):
requestedTxs += 1
self.addTxid(addr, txid)
addrs = self.generateGapAddresses()
log.debug("%d address transactions sets fetched" % requestedTxs)
log.debug(
f"{requestedTxs} address transactions fetched for account {self.name}"
)

# start with a search for all known addresses
addresses = self.allAddresses()

# Until the server stops returning UTXOs, keep requesting more addresses
# to check.
utxoCount = 0
while True:
# Update the account with known UTXOs.
blockchainUTXOs = blockchain.UTXOs(addresses)
if not blockchainUTXOs:
break
utxoCount += len(blockchainUTXOs)
self.resolveUTXOs(blockchainUTXOs)
addresses = self.generateGapAddresses()
if not addresses:
break
log.debug(f"{utxoCount} utxos for account {self.name}")

# update the staking info and authorize the stake pool.
# Update the staking info.
self.updateStakeStats()
pool = self.stakePool()
if pool:
pool.authorize(self.votingAddress().string())

# Subscribe to block and address updates.
blockchain.addressReceiver = self.addressSignal
blockchain.subscribeBlocks(self.blockSignal)
watchAddresses = self.watchAddrs()
if watchAddresses:
blockchain.subscribeAddresses(watchAddresses)
blockchain.subscribeAddresses(watchAddresses, self.addressSignal)
log.debug(
f"subscribed to {len(watchAddresses)} addresses for account {self.name}"
)
# Signal the new balance.
signals.balance(self.calcBalance())

Expand Down
36 changes: 31 additions & 5 deletions decred/decred/dcr/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 14 additions & 12 deletions decred/decred/dcr/dcrdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,6 @@ def __init__(self, db, netParams, datapath, skipConnect=False):
db = database.KeyValueDatabase(db)
self.db = db
self.netParams = netParams
# The blockReceiver and addressReceiver will be set when the respective
# subscribe* method is called.
self.blockReceiver = None
self.addressReceiver = None
self.datapath = datapath
self.dcrdata = None
self.txDB = db.child("tx", blobber=msgtx.MsgTx)
Expand All @@ -421,6 +417,8 @@ def __init__(self, db, netParams, datapath, skipConnect=False):
self.txBlockMap = db.child("blocklink")
self.tipHeight = None
self.subsidyCache = calc.SubsidyCache(netParams)
self.addrSubscribers = {}
self.blockSubscribers = []
Comment on lines +420 to +421
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different receivers for different addresses allows the DcrdataBlockchain to be reused by different accounts.

if not skipConnect:
self.connect()

Expand Down Expand Up @@ -448,7 +446,7 @@ def subscribeBlocks(self, receiver):
receiver (func(object)): A function or method that accepts the block
notifications.
"""
self.blockReceiver = receiver
self.blockSubscribers.append(receiver)
self.dcrdata.subscribeBlocks()

def getAgendasInfo(self):
Expand All @@ -460,7 +458,7 @@ def getAgendasInfo(self):
"""
return agenda.AgendasInfo.parse(self.dcrdata.stake.vote.info())

def subscribeAddresses(self, addrs, receiver=None):
def subscribeAddresses(self, addrs, receiver):
"""
Subscribe to notifications for the provided addresses.

Expand All @@ -470,10 +468,8 @@ def subscribeAddresses(self, addrs, receiver=None):
notifications.
"""
log.debug("subscribing to addresses %s" % repr(addrs))
if receiver:
self.addressReceiver = receiver
elif self.addressReceiver is None:
raise DecredError("must set receiver to subscribe to addresses")
for addr in addrs:
self.addrSubscribers[addr] = receiver
self.dcrdata.subscribeAddresses(addrs)

def processNewUTXO(self, utxo):
Expand Down Expand Up @@ -840,11 +836,17 @@ def pubsubSignal(self, sig):
try:
if sigType == "address":
msg = sig["message"]
addr = msg["address"]
log.debug("signal received for %s" % msg["address"])
self.addressReceiver(msg["address"], msg["transaction"])
receiver = self.addrSubscribers.get(addr)
if not receiver:
log.error("no receiver registered for address %s", addr)
return
receiver(addr, msg["transaction"])
elif sigType == "newblock":
self.tipHeight = sig["message"]["block"]["height"]
self.blockReceiver(sig)
for receiver in self.blockSubscribers:
receiver(sig)
elif sigType == "subscribeResp":
# should check for error.
pass
Expand Down
32 changes: 25 additions & 7 deletions decred/decred/wallet/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def addAccount(self, cryptoKey, acctName):
idx = len(self.acctDB)
coinExtKey = self.coinKey(cryptoKey)
db = self.dbForAcctIdx(idx)
account = createAccount(
acct = createAccount(
cryptoKey,
coinExtKey,
self.coinType,
Expand All @@ -182,9 +182,20 @@ def addAccount(self, cryptoKey, acctName):
db,
self.signals,
)
self.acctDB[idx] = account
self.accounts[idx] = account
return account
blockchain = chains.chain(self.coinType)
acct.load(self.dbForAcctIdx(idx), blockchain, self.signals)
self.acctDB[idx] = acct
self.accounts[idx] = acct
return acct

def saveAccount(self, idx):
"""
Save the account at the specified index.

Args:
idx: The account index.
"""
self.acctDB[idx] = self.accounts[idx]

def account(self, idx):
"""
Expand Down Expand Up @@ -260,11 +271,11 @@ def createNewAccountManager(root, cryptoKey, coinType, netParams, db):


def createAccount(
cryptoKey, coinExtKey, coinType, acct, netName, acctName, db, signals
cryptoKey, coinExtKey, coinType, acctIdx, netName, acctName, db, signals
):
# Create the zeroth account.
# Derive the account key for the first account according to BIP0044.
acctKeyPriv = coinExtKey.deriveAccountKey(acct)
acctKeyPriv = coinExtKey.deriveAccountKey(acctIdx)

# Ensure the branch keys can be derived for the provided seed according
# to BIP0044.
Expand All @@ -275,7 +286,14 @@ def createAccount(
constructor = chains.AccountConstructors[coinType]
blockchain = chains.chain(coinType)
account = constructor(
pubKeyEncrypted, privKeyEncrypted, acctName, netName, db, blockchain, signals
acctIdx,
pubKeyEncrypted,
privKeyEncrypted,
acctName,
netName,
db,
blockchain,
signals,
)
# Open the account.
account.unlock(cryptoKey)
Expand Down
6 changes: 3 additions & 3 deletions decred/tests/unit/dcr/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def newAccount(self, db, blockchain=None):
privKeyEncrypted = crypto.encrypt(self.cryptoKey, acctKey.serialize())
pubKeyEncrypted = crypto.encrypt(self.cryptoKey, acctKeyPub.serialize())
return account.Account(
pubKeyEncrypted, privKeyEncrypted, "acctName", "mainnet", db, blockchain
0, pubKeyEncrypted, privKeyEncrypted, "acctName", "mainnet", db, blockchain
)

def test_main(self):
Expand Down Expand Up @@ -651,7 +651,7 @@ def utxos4a(*a):
return [utxo]

acct.blockchain.UTXOs = utxos4a
acct.blockchain.subscribeAddresses = lambda addrs: None
acct.blockchain.subscribeAddresses = lambda addrs, receiver: None
acct.blockchain.subscribeBlocks = lambda a: None
acct.sync()
assert Signals.b.available == newVal
Expand Down Expand Up @@ -1081,7 +1081,7 @@ def deriveChildAddress(self, i, netParams):

def test_nextExternalInternalAddress(self):
class FakeBlockchain:
subscribeAddresses = lambda addr: None
subscribeAddresses = lambda addr, receiver: None

db = KeyValueDatabase(":memory:").child("tmp")
acct = self.newAccount(db, FakeBlockchain)
Expand Down
3 changes: 1 addition & 2 deletions decred/tests/unit/dcr/test_dcrdata_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,6 @@ def addrReceiver(addr, txid):
ddb.dcrdata.ps.sent = []

# subscribeAddresses
with pytest.raises(DecredError):
ddb.subscribeAddresses([])
ddb.subscribeAddresses(["new_one"], addrReceiver)
assert ddb.dcrdata.ps.sent[0]["message"]["message"] == "address:new_one"

Expand All @@ -383,6 +381,7 @@ def addrReceiver(addr, txid):
assert ddb.pubsubSignal(dict(event="ping")) is None
assert ddb.pubsubSignal(dict(event="unknown")) is None
# pubsubSignal address
ddb.subscribeAddresses(["the_address"], addrReceiver)
sig = dict(
event="address",
message=dict(address="the_address", transaction="transaction"),
Expand Down
2 changes: 1 addition & 1 deletion tinywallet/tinywallet/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ def main():
qApp = QtWidgets.QApplication(sys.argv)
qApp.setStyleSheet(Q.QUTILITY_STYLE)
qApp.setPalette(Q.lightThemePalette)
qApp.setWindowIcon(QtGui.QIcon(screens.pixmapFromSvg(DCR.LOGO, 64, 64)))
qApp.setWindowIcon(QtGui.QIcon(DCR.LOGO))
qApp.setApplicationName("Tiny Decred")
loadFonts()

Expand Down
Loading