Skip to content

Commit

Permalink
Highest-used addr logic impl! Wlt version updated
Browse files Browse the repository at this point in the history
  • Loading branch information
etotheipi committed Dec 17, 2011
1 parent 5119f5c commit 2fa18ee
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 60 deletions.
10 changes: 5 additions & 5 deletions ArmoryQt.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,6 @@ def setWltExtraProp(self, wltID, propName, value):
key = 'Wallet_%s_%s' % (wltID, propName)
if not self.wltExtraProps.has_key(wltID):
self.wltExtraProps[wltID] = {}
print wltID, propName, value, key
self.wltExtraProps[wltID][propName] = value
self.settings.set(key, value)

Expand Down Expand Up @@ -591,7 +590,8 @@ def createCombinedLedger(self, wltIDList=None, withZeroConf=True):
elif currIdx==3:
wltIDList = listWatching
else:
raise WalletExistsError, 'Bad combo-box selection: ' + str(currIdx)
pass
#raise WalletExistsError, 'Bad combo-box selection: ' + str(currIdx)


self.combinedLedger = []
Expand Down Expand Up @@ -765,8 +765,8 @@ def addWalletToApplication(self, newWallet, walletIsNew=True):

self.walletBalances.append(wlt.getBalance())
self.walletSubLedgers.append([])
for addr160 in wlt.getLinearAddr160List():
ledger = wlt.getTxLedger(addr160)
for addr in wlt.getLinearAddrList():
ledger = wlt.getTxLedger(addr.getAddr160())
self.walletSubLedgers[-1].append(ledger)
self.walletLedgers.append(wlt.getTxLedger())
else:
Expand Down Expand Up @@ -838,7 +838,7 @@ def createNewWallet(self):
dlgfork = DlgForkWallet(self)
if dlgfork.exec_():
newPath = str(dlgfork.edtPath.text())
newWallet.forkOnlineWallet(newPath)
newWallet.forkWallet(newPath)


#############################################################################
Expand Down
143 changes: 100 additions & 43 deletions armoryengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@
USE_TESTNET = True

# Version Numbers -- numDigits [var, 2, 2, 3]
BTCARMORY_VERSION = (0,50,0,0) # (Major, Minor, Minor++, even-more-minor)
PYBTCADDRESS_VERSION = (1,00,0,0) # (Major, Minor, Minor++, even-more-minor)
PYBTCWALLET_VERSION = (1,00,0,0) # (Major, Minor, Minor++, even-more-minor)
BTCARMORY_VERSION = (0, 50, 0, 0) # (Major, Minor, Minor++, even-more-minor)
PYBTCADDRESS_VERSION = (1, 00, 0, 0) # (Major, Minor, Minor++, even-more-minor)
PYBTCWALLET_VERSION = (1, 10, 0, 0) # (Major, Minor, Minor++, even-more-minor)

def getVersionString(vquad, numPieces=4):
vstr = '%d.%02d' % vquad[:2]
Expand Down Expand Up @@ -5042,6 +5042,7 @@ class PyBtcWallet(object):
data). This is used to improve blockchain searching
Short Name -- (32) Null-terminated user-supplied short name for wlt
Long Name -- (256) Null-terminated user-supplied description for wlt
Highest Used-- (8) The chain index of the highest used address
---
Crypto/KDF -- (512) information identifying the types and parameters
of encryption used to secure wallet, and key
Expand Down Expand Up @@ -5116,7 +5117,7 @@ def __init__(self):
self.labelDescr = ''
self.linearAddr160List = []
self.chainIndexMap = {}
self.addrPoolSize = 500
self.addrPoolSize = 200

# For file sync features
self.walletPath = ''
Expand All @@ -5139,7 +5140,7 @@ def __init__(self):
self.wltUniqueIDB58 = '' # Base58 version of reversed-wltUniqueIDBin
self.lastComputedChainAddr160 = ''
self.lastComputedChainIndex = 0
self.highestUsedChainIndex = -1
self.highestUsedChainIndex = 0

# All PyBtcAddress serializations are exact same size, figure it out now
self.pybtcaddrSize = len(PyBtcAddress().serialize())
Expand All @@ -5152,6 +5153,7 @@ def __init__(self):
self.offsetWltFlags = -1
self.offsetLabelName = -1
self.offsetLabelDescr = -1
self.offsetTopUsed = -1
self.offsetRootAddr = -1
self.offsetKdfParams = -1
self.offsetCrypto = -1
Expand Down Expand Up @@ -5426,7 +5428,8 @@ def getNewAddress(self):
self.fillAddressPool(self.addrPoolSize)

self.highestUsedChainIndex += 1
return self.getAddress160ByChainIndex(self.highestUsedChainIndex)
new160 = self.getAddress160ByChainIndex(self.highestUsedChainIndex)
return self.addrMap[new160]

"""
if len(self.lastComputedChainAddr160) == 20:
Expand Down Expand Up @@ -5494,19 +5497,66 @@ def fillAddressPool(self, numPool=500):
for i in range(numToCreate):
self.computeNextAddress()

#############################################################################
def detectHighestUsedIndex(self, writeResultToWallet=False):
if not TheBDM.isInitialized():
print 'Cannot detect any usage information without the blockchain'
return -1

oldSync = self.doBlockchainSync
self.doBlockchainSync = BLOCKCHAIN_READONLY
self.syncWithBlockchain()
self.doBlockchainSync = oldSync

highestIndex = 0
for addr in self.getLinearAddrList(withAddrPool=True):
a160 = addr.getAddr160()
if len(self.getTxLedger(a160)) > 0:
highestIndex = max(highestIndex, addr.chainIndex)

if writeResultToWallet:
self.highestUsedChainIndex = highestIndex
self.walletFileSafeUpdate( [[WLT_UPDATE_MODIFY, self.offsetTopUsed, \
int_to_binary(highestIndex, widthBytes=8)]])


return highestIndex



#############################################################################
def forkOnlineWallet(self, newWalletFile=None, \
shortLabel='', longLabel=''):
if not self.addrMap['ROOT'].hasPrivKey():
print 'This wallet is already void of any private key data!'
def writeFreshWalletFile(self, path, newName='', newDescr=''):
newFile = open(path, 'w')
bp = BinaryPacker()
self.packHeader(bp)
newFile.write(bp.getBinaryString())

for addr160,addrObj in self.addrMap.iteritems():
if not addr160=='ROOT':
newFile.write('\x00' + addr160 + addrObj.serialize())

if not newWalletFile:
wltpieces = os.path.splitext(self.walletPath)
wltname = wltpieces[0] + 'WatchingOnly' + wltpieces[1]
newWalletFile = os.path.join(ARMORY_HOME_DIR, wltname)
for hashVal,comment in self.commentsMap.iteritems():
twoByteLength = int_to_binary(len(comment), widthBytes=2)
if len(hashVal)==20:
typestr = int_to_binary(WLT_DATATYPE_ADDRCOMMENT)
newFile.write(typestr + hashVal + twoByteLength + comment)
elif len(hashVal)==32:
typestr = int_to_binary(WLT_DATATYPE_TXCOMMENT)
newFile.write(typestr + hashVal + twoByteLength + comment)

for addr160,opevalData in self.opevalMap.iteritems():
pass

newFile.close()


#############################################################################
def forkOnlineWallet(self, newWalletFile, shortLabel='', longLabel=''):
"""
Make a copy of this wallet that contains no private key data
"""
if noPriv and not self.addrMap['ROOT'].hasPrivKey():
print 'This wallet is already void of any private key data!'

onlineWallet = PyBtcWallet()
onlineWallet.fileTypeStr = self.fileTypeStr
Expand All @@ -5515,8 +5565,14 @@ def forkOnlineWallet(self, newWalletFile=None, \
onlineWallet.wltCreateDate = self.wltCreateDate
onlineWallet.useEncryption = False
onlineWallet.watchingOnly = True
onlineWallet.labelName = (self.labelName + ' (Watch-Only)')[:32]
onlineWallet.labelDescr = longLabel

if not shortLabel:
shortLabel = self.labelName
if not longLabel:
longLabel = self.labelDescr

onlineWallet.labelName = (shortLabel + ' (Watch)')[:32]
onlineWallet.labelDescr = (longLabel + ' (Watching-only copy)')[:256]

newAddrMap = {}
for addr160,addrObj in self.addrMap.iteritems():
Expand All @@ -5533,27 +5589,7 @@ def forkOnlineWallet(self, newWalletFile=None, \
onlineWallet.lastComputedChainAddr160 = self.lastComputedChainAddr160
onlineWallet.lastComputedChainIndex = self.lastComputedChainIndex

newFile = open(newWalletFile, 'w')
bp = BinaryPacker()
onlineWallet.packHeader(bp)
newFile.write(bp.getBinaryString())

for addr160,addrObj in onlineWallet.addrMap.iteritems():
if not addr160=='ROOT':
newFile.write('\x00' + addr160 + addrObj.serialize())

for addr160,comment in onlineWallet.commentsMap.iteritems():
twoByteLength = int_to_binary(len(comment), widthBytes=2)
newFile.write('\x01' + addr160 + twoByteLength + comment)

for addr160,opevalData in onlineWallet.opevalMap.iteritems():
pass

newFile.close()

fileparts = os.path.splitext(newWalletFile)
walletFileBackup = fileparts[0] + 'backup' + fileparts[1]
shutil.copy(newWalletFile, walletFileBackup)
onlineWallet.writeFreshWalletFile(newWalletFile, shortLabel, longLabel)
return True


Expand Down Expand Up @@ -6027,6 +6063,11 @@ def packHeader(self, binPacker):
self.offsetLabelDescr = binPacker.getSize() - startByte
binPacker.put(BINARY_CHUNK, self.labelDescr, width=256)

# Highest used address:
if getVersionInt(self.version) >= getVersionInt((1, 10, 0, 0)):
self.offsetTopUsed = binPacker.getSize() - startByte
binPacker.put(INT64, self.highestUsedChainIndex)

# Key-derivation function parameters
self.offsetKdfParams = binPacker.getSize() - startByte
binPacker.put(BINARY_CHUNK, self.serializeKdfParams(), width=256)
Expand Down Expand Up @@ -6088,6 +6129,12 @@ def unpackHeader(self, binUnpacker):
self.offsetLabelDescr = binUnpacker.getPosition()
self.labelDescr = binUnpacker.get(BINARY_CHUNK, 256).strip('\x00')

# Highest used address:
print self.version
if getVersionInt(self.version) >= getVersionInt((1, 10, 0, 0)):
self.offsetTopUsed = binUnpacker.getPosition()
self.highestUsedChainIndex = binUnpacker.get(INT64)

# Read the key-derivation function parameters
self.offsetKdfParams = binUnpacker.getPosition()
self.kdf = self.unserializeKdfParams(binUnpacker)
Expand Down Expand Up @@ -6856,7 +6903,8 @@ def getAddrListSortedByChainIndex(self, withRoot=False):
""" Returns Addr160 list """
addrList = []
for addr160 in self.linearAddr160List:
addrList.append( [addrObj.chainIndex, addr160, self.addrMap[addr160]] )
addr=self.addrMap[addr160]
addrList.append( [addr.chainIndex, addr160, addr] )

addrList.sort(key=lambda x: x[0])
return addrList
Expand All @@ -6874,15 +6922,24 @@ def getAddrList(self):


#############################################################################
def getLinearAddr160List(self, withImported=True):
def getLinearAddrList(self, withImported=True, withAddrPool=False):
"""
Retrieves a list of addresses, by hash, in the order they
appear in the wallet file. Can ignore the imported addresses
to get only chained addresses, if necessary
to get only chained addresses, if necessary.
I could do this with one list comprehension, but it would be long.
I'm resisting the urge...
"""
return [self.addrMap[a160] for a160 in self.linearAddr160List \
if ( (self.addrMap[a160].chainIndex>=0 or withImported) and \
not a160=='ROOT')]
addrList = []
for a160 in self.linearAddr160List:
addr = self.addrMap[a160]
if not a160=='ROOT' and (withImported or addr.chainIndex>=0):
# Either we want imported addresses, or this isn't one
if (withAddrPool or addr.chainIndex<=self.highestUsedChainIndex):
addrList.append(addr)

return addrList


#############################################################################
Expand Down
7 changes: 5 additions & 2 deletions armorymodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,11 @@ def __init__(self, wlt, mainWindow):
super(WalletAddrDispModel, self).__init__()
self.main = mainWindow
self.wlt = wlt
self.addr160List = wlt.addrMap.keys()
self.addr160List.remove('ROOT')
self.addr160List = [a.getAddr160() for a in wlt.getLinearAddrList()]

def reset(self):
self.addr160List = [a.getAddr160() for a in self.wlt.getLinearAddrList()]
QAbstractTableModel.reset(self)


def rowCount(self, index=QModelIndex()):
Expand Down
39 changes: 39 additions & 0 deletions convertWalletVersion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from armoryengine import *
import CppBlockUtils as Cpp
import armoryengine

print 'Loading wallets...'
wltPaths = []
for f in os.listdir(ARMORY_HOME_DIR):
fullPath = os.path.join(ARMORY_HOME_DIR, f)
if os.path.isfile(fullPath) and not fullPath.endswith('backup.wallet'):
openfile = open(fullPath, 'r')
first8 = openfile.read(8)
openfile.close()
if first8=='\xbaWALLET\x00':
wltPaths.append(fullPath)

for fpath in wltPaths:
wltLoad = PyBtcWallet().readWalletFile(fpath)
wltID = wltLoad.wltUniqueIDB58
print 'Read wallet:', wltID, 'version:', wltLoad.version
oldPath = wltLoad.walletPath
print 'Will upgrade version to:', oldPath, PYBTCWALLET_VERSION

fileparts = os.path.splitext(oldPath)
oldBackup = fileparts[0] + 'backup' + fileparts[1]
tempPath = oldPath + '.new_version'

print 'Forking wallet...'
wltLoad.version = PYBTCWALLET_VERSION
wltLoad.writeFreshWalletFile(tempPath)

os.remove(oldPath)
os.remove(oldBackup)
shutil.move(tempPath, oldPath)






10 changes: 5 additions & 5 deletions stddialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,14 +1030,14 @@ def __init__(self, currcomment='', ctype='', parent=None):

layout = QGridLayout()
lbl = None
if ctype and currcomment: lbl = QLabel('change %s comment:'%ctype)
if not ctype and currcomment: lbl = QLabel('change comment:')
if ctype and not currcomment: lbl = QLabel('add %s comment:'%ctype)
if not ctype and not currcomment: lbl = QLabel('add comment:')
if ctype and currcomment: lbl = QLabel('Change %s Comment:'%ctype)
if not ctype and currcomment: lbl = QLabel('Change Comment:')
if ctype and not currcomment: lbl = QLabel('Add %s Comment:'%ctype)
if not ctype and not currcomment: lbl = QLabel('Add Comment:')
self.edtComment = QLineEdit()
self.edtComment.setText(currcomment)
h,w = relaxedSizeNChar(self, 50)
self.edtcomment.setMinimumSize(h,w)
self.edtComment.setMinimumSize(h,w)
layout.addWidget(lbl, 0,0)
layout.addWidget(self.edtComment, 1,0)
layout.addWidget(buttonbox, 2,0)
Expand Down
10 changes: 5 additions & 5 deletions unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
Test_SimpleAddress = False
Test_MultiSigTx = False
Test_TxSimpleCreate = False
Test_EncryptedAddress = False
Test_EncryptedWallet = False
Test_EncryptedAddress = True
Test_EncryptedWallet = True
Test_TxDistProposals = False
Test_SelectCoins = False
Test_CryptoTiming = False
Expand Down Expand Up @@ -1001,7 +1001,8 @@ def restartConnection(protoObj, failReason):
print '\n(5) Get new address from locked wallet'
print 'Locking wallet'
wlt.lock()
wlt.getNewAddress()
for i in range(10):
wlt.getNewAddress()
wlt.pprint(indent=' '*5, allAddrInfo=debugPrint)

print '\n(5) Re-reading wallet from file, compare the two wallets'
Expand All @@ -1011,9 +1012,8 @@ def restartConnection(protoObj, failReason):

#############################################################################
# !!! #forkOnlineWallet()
# TODO: FIGURE THIS PART OUT: ONLINE FORKING DOESN"T QUITE WORK YET!
print '\n(6)Testing forking encrypted wallet for online mode'
wlt.forkOnlineWallet('OnlineVersionOfEncryptedWallet.bin')
wlt.forkWallet('OnlineVersionOfEncryptedWallet.bin')
wlt2.readWalletFile('OnlineVersionOfEncryptedWallet.bin')
wlt2.pprint(indent=' '*5, allAddrInfo=debugPrint)

Expand Down

0 comments on commit 2fa18ee

Please sign in to comment.