Skip to content

Commit

Permalink
Merge remote-tracking branch 'goatpig/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Rudd-O committed Jun 29, 2018
2 parents 2bdf249 + fee1f91 commit 6452925
Show file tree
Hide file tree
Showing 20 changed files with 373 additions and 168 deletions.
45 changes: 23 additions & 22 deletions ArmoryQt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2240,7 +2240,7 @@ def getFileSave(self, title='Save Wallet File', \

types = ffilter
types.append('All files (*)')
typesStr = ';; '.join(types)
typesStr = ';; '.join(str(_type) for _type in types)

# Open the native file save dialog and grab the saved file/path unless
# we're in OS X, where native dialogs sometimes freeze. Looks like a Qt
Expand Down Expand Up @@ -3529,7 +3529,7 @@ def clickReceiveCoins(self):
if len(self.walletMap)==0:
reply = QMessageBox.information(self, self.tr('No Wallets!'), self.tr(
'You have not created any wallets which means there is '
'nowhere to store you bitcoins! Would you like to '
'nowhere to store your bitcoins! Would you like to '
'create a wallet now?'), \
QMessageBox.Yes | QMessageBox.No)
if reply==QMessageBox.Yes:
Expand Down Expand Up @@ -4387,7 +4387,7 @@ def GetDashStateText(self, mgmtMode, state):
'<br><br>'
'Now would be a good time to make paper (or digital) backups of '
'your wallet(s) if you have not done so already! You are protected '
'<i>forever</i> from hard-drive loss, or forgetting you password. '
'<i>forever</i> from hard-drive loss, or forgetting your password. '
'If you do not have a backup, you could lose all of your '
'Bitcoins forever!', "", len(self.walletMap))

Expand Down Expand Up @@ -5345,32 +5345,33 @@ def doTheSystemTrayThing(self):
# If coins were either received or sent from the loaded wlt/lbox
dispLines = QStringList()
totalStr = coin2strNZS(abs(le.getValue()))
title = None
if le.getValue() > 0:
title = self.tr('Bitcoins Received!')
dispLines.append(self.tr('Amount: %1 BTC').arg(totalStr))
dispLines.append(self.tr('From: %2').arg(wltName))
elif le.getValue() < 0:
# Also display the address of where they went
txref = TheBDM.bdv().getTxByHash(le.getTxHash())
nOut = txref.getNumTxOut()
recipStr = ''
for i in range(0, nOut):
script = txref.getTxOutCopy(i).getScript()
if pywlt.hasScrAddr(script_to_scrAddr(script)):
continue
if len(recipStr)==0:
recipStr = self.getDisplayStringForScript(script, 45)['String']
else:
recipStr = self.tr('<Multiple Recipients>')
try:
recipStr = ''
for addr in le.getScrAddrList():
if pywlt.hasScrAddr(addr):
continue
if len(recipStr)==0:
recipStr = hash160_to_addrStr(addr[1:], addr[0])
else:
recipStr = self.tr('<Multiple Recipients>')

title = self.tr('Bitcoins Sent!')
dispLines.append(unicode(self.tr('Amount: %1 BTC').arg(totalStr)))
dispLines.append(unicode(self.tr('From: %1').arg(wltName )))
dispLines.append(unicode(self.tr('To: %1').arg(recipStr)))
title = self.tr('Bitcoins Sent!')
dispLines.append(unicode(self.tr('Amount: %1 BTC').arg(totalStr)))
dispLines.append(unicode(self.tr('From: %1').arg(wltName )))
dispLines.append(unicode(self.tr('To: %1').arg(recipStr)))
except Exception as e:
LOGERROR('tx broadcast systray display failed with error: %s' % e)

self.showTrayMsg(title, dispLines.join("\n"), \
QSystemTrayIcon.Information, 10000)
LOGINFO(title + '\n' + dispLines.join("\n"))
if title:
self.showTrayMsg(title, dispLines.join("\n"), \
QSystemTrayIcon.Information, 10000)
LOGINFO(title + '\n' + dispLines.join("\n"))

# Wait for 5 seconds before processing the next queue object.
self.notifyBlockedUntil = RightNow() + 5
Expand Down
82 changes: 82 additions & 0 deletions README_ArmoryDB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# ArmoryDB
ArmoryDB is a binary used by Armory. The code is written in C++11 and is used to control the [LMDB](http://symas.com/mdb/) databases used by Armory. These databases contain information about the wallets/lockboxes in Armory and information about transactions relevant to the wallets.

ArmoryDB is automatically called whenever Armory starts up; the user needs not intervene. Only advanced users who know what they're doing need any further.

## ArmoryDB usage
ArmoryDB works by reading the blockchain downloaded by Bitcoin Core and finding any transactions relevant to the wallets loaded into Armory. This means that the entire blockchain must be rescanned whenever a new wallet or lockbox is loaded. Once a wallet/lockbox has been loaded and the blockchain fully scanned for that wallet, ArmoryDB will keep an eye on the blockchain. Any transactions relevant to the addresses controlled by wallets/lockboxes will be resolved. In addition, as Armory builds its own mempool by talking to the Core node, any relevant zero-confirmation transactions will be resolved by ArmoryDB.

As of v0.96.4, Armory calls ArmoryDB using a particular set of flags. The Armory Python log (`armorylog.txt`) shows attempts at executing ArmoryDB using some default parameters, assuming that `/home/snakamoto` is the user's root directory.

```
2018-01-13 13:32:17 (WARNING) -- SDM.py:396 - Spawning DB with command: /home/snakamoto/Armory/ArmoryDB --db-type="DB_FULL" --cookie --satoshi-datadir="/home/snakamoto/.bitcoin/blocks" --datadir="/home/snakamoto/Armory/" --dbdir="/home/snakamoto/Armory/databases"
2018-01-13 13:32:17 (INFO) -- ArmoryUtils.py:679 - Executing popen: ['/home/snakamoto/Armory/ArmoryDB', '--db-type="DB_FULL"', '--cookie', '--satoshi-datadir="/home/snakamoto/.bitcoin/blocks"', '--datadir="/home/snakamoto/Armory/"', '--dbdir="/home/snakamoto/Armory/databases"']
```

The flags are explained below, as seen in the Armory source code. By default, like Armory, ArmoryDB works on the mainnet network.

* cookie: Create a cookie file holding a random authentication key to allow local clients to make use of elevated commands (e.g., `shutdown`). (Default: False)
* datadir: Path to the Armory data folder. (Default: Same as Armory)
* db-type: Sets the db type. Database type cannot be changed in between Armory runs. Once a database has been built with a certain type, the database will always function according to the initial type; specifying another type will do nothing. Changing the database type requires rebuilding the database. (Default: DB\_FULL)
* dbdir: Path to folder containing the Armory database file directory. If empty, a new database will be created. (Default: Same as Armory)
* satoshi-datadir: Path to blockchain data folder (blkXXXXX.dat files). (Default: Same as Armory)

The database types are as follows:

* DB\_BARE: Tracks wallet history only. Smallest DB, as the DB doesn't resolve a wallet's relevant transaction hashes until requested. (In other words, database accesses will be relatively slow.) This was the default database type in Armory v0.94.
* DB\_FULL: Tracks wallet history and resolves all relevant transaction hashes. (In other words, the database can instantly pull up relevant transaction data). ~1GB minimum size for the database. Default database type as of v0.95.
* DB\_SUPER: Tracks the entire blockchain history. Any transaction hash can be instantly resolved into its relevant data. Not fully implemented yet, and the database will be at least ~100GB large.

There are additional flags.

* checkchain: A test mode of sorts. It checks all the signatures in the blockchain. (Default: False)
* clear\_mempool: Delete all zero confirmation transactions from the database. (Default: False)
* fcgi-port: Sets the database listening port. The database listens to external connections (e.g., from Armory) via FCGI and can be placed behind an HTTP daemon in order to obtain remote access to ArmoryDB. (Default: 9001 (mainnet) / 19001 (testnet) / 19002 (regtest))
* listen-all: Listen to all incoming IPs (not just localhost). (Default: False)
* ram-usage: Defines the RAM use during database scan operations. One point averages 128MB of RAM (without accounting for the base amount, ~400MB). Can't be lower than one point. Can be changed in between Armory runs. (Default: 50)
* rebuild: Delete all DB data, and build the database and scan the blockchain data from scratch.
* regtest: Run database against the regression test network.
* rescan: Delete all processed history data and rescan blockchain from the first block.
* rescanSSH: Delete balance and transaction count data, and rescan the data. Much faster than rescan or rebuild.
* satoshirpc-port: Set the P2P port of the Core node to which ArmoryDB will attempt to connect. (Default: Same as Armory)
* testnet: Run database against the testnet network.
* thread-count: Defines how many processing threads can be used during database builds and scans. Can't be lower than one thread. Can be changed in between Armory runs. (Default: The maximum number of available CPU threads.)
* zcthread-count: Defines the maximum number on threads the zero-confirmation (ZC) parser can create for processing incoming transcations from the Core network node. (Default: 100)

Note that the flags may be added to the Armory root data directory in an ArmoryDB config file (`armorydb.conf`). The file will set the parameters every time ArmoryDB is started. Command line flags, including flags used by Armory, will override config values. (Changing Armory's default values will require recompilation.) An example file that mirrors the default parameters used by Armory can be seen below. Yeah!

```
db-type="DB_FULL"
cookie=1
satoshi-datadir="/home/snakamoto/.bitcoin/blocks""
datadir="/home/snakamoto/Armory/"
dbdir="/home/snakamoto/Armory/databases"
```

As always, check the source code for the most up-to-date information.

## ArmoryDB connection design
ArmoryDB *must* run alongside the Bitcoin Core node. This is because ArmoryDB does a memory map on the blockchain files. This can only be done if ArmoryDB and the node are running on the same OS and, ideally, on the same storage device. The IP address of the Core node is hardcoded (localhost) and can't be changed without recompiling Armory (and changing the design at your own risk!). Only the node's port can be changed via the `satoshirpc-port` parameter. This design may be changed in the future.

It is possible for Armory and other clients to talk to ArmoryDB remotely. Possibilities for reaching ArmoryDB include placing ArmoryDB behind an HTTP daemon or logging into the ArmoryDB machine remotely via VPN. Talking to ArmoryDB is done via JSON-encoded packets, as seen in the `armoryd` project.

## Dependencies
* fcgi - Communication protocol - [Source of goatpig's libfcgi fork](https://github.com/toshic/libfcgi)
* Clang (macOS) - Installed automatically by [Xcode](https://developer.apple.com/xcode/)
* GNU Compiler Collection (Linux) - Install package `g++`
* LMDB - Database engine, modified to suit Armory's use cases - [LMDB page](http://symas.com/mdb/)
* Visual Studio compiler (Windows) - [Visual Studio page](https://www.visualstudio.com/)

## Troubleshooting
Occasionally, a user may have trouble connecting to the Bitcoin Core node. Often, this is because a version of ArmoryDB from a previous run of Armory didn't shut down properly. If a user is unable to connect to the Core node, the following steps are recommended.

* Shut down Armory.
* Check the operating system's task manager 30-60 seconds later.
* If ArmoryDB is still running, shut it down manually.
* Restart Armory.

## License
Distributed under the MIT License. See the [LICENSE file](LICENSE) for more information.

## Copyright
Copyright (C) 2017-2018 goatpig
10 changes: 6 additions & 4 deletions SDM.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def parseLinkList(theData):


################################################################################
# jgarzik'sjj jsonrpc-bitcoin code -- stupid-easy to talk to bitcoind
class SatoshiDaemonManager(object):
"""
Use an existing implementation of bitcoind
Expand Down Expand Up @@ -131,11 +130,14 @@ def setSatoshiDir(self, newDir):
if 'testnet' in newDir or 'regtest' in newDir:
self.satoshiRoot, tail = os.path.split(newDir)

path = os.path.dirname(os.path.abspath(__file__))
execDir = os.path.dirname(inspect.getsourcefile(SatoshiDaemonManager))
if execDir.endswith('.zip'):
execDir = os.path.dirname(execDir)

if OS_MACOSX:
# OSX separates binaries/start scripts from the Python code. Back up!
path = os.path.join(path, '../../bin/')
self.dbExecutable = os.path.join(path, 'ArmoryDB')
execDir = os.path.join(execDir, '../../bin/')
self.dbExecutable = os.path.join(execDir, 'ArmoryDB')

if OS_WINDOWS:
self.dbExecutable += ".exe"
Expand Down
2 changes: 1 addition & 1 deletion armoryengine/ArmoryUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
LEVELDB_HEADERS = 'leveldb_headers'

# Version Numbers
BTCARMORY_VERSION = (0, 96, 3, 991) # (Major, Minor, Bugfix, AutoIncrement)
BTCARMORY_VERSION = (0, 96, 4, 0) # (Major, Minor, Bugfix, AutoIncrement)
PYBTCWALLET_VERSION = (1, 35, 0, 0) # (Major, Minor, Bugfix, AutoIncrement)

# ARMORY_DONATION_ADDR = '1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv'
Expand Down
5 changes: 3 additions & 2 deletions armoryengine/PyBtcWallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1894,8 +1894,9 @@ def getAddrCommentIfAvail(self, txHash):

addrComments = []
for a160 in self.txAddrMap[txHash]:
if self.commentsMap.has_key(a160) and '[[' not in self.commentsMap[a160]:
addrComments.append(self.commentsMap[a160])
h160 = a160[1:]
if self.commentsMap.has_key(h160) and '[[' not in self.commentsMap[h160]:
addrComments.append(self.commentsMap[h160])

return '; '.join(addrComments)

Expand Down
2 changes: 2 additions & 0 deletions armoryengine/PyBtcWalletRecovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def __init__(self):
self.UID = ''
self.labelName = ''
self.WalletPath = ''

self.pybtcaddrSize = len(PyBtcAddress().serialize())

"""
Modes:
Expand Down
14 changes: 13 additions & 1 deletion armorymodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,19 @@ def data(self, index, role=Qt.DisplayRole):
if row>=len(self.addr160List):
return QVariant('')
addr = self.wlt.addrMap[self.addr160List[row]]
cppaddr = self.wlt.cppWallet.getAddrObjByIndex(addr.chainIndex)
addr_index = addr.chainIndex

try:
if addr_index == -2:
addr_index = self.wlt.cppWallet.getAssetIndexForAddr(addr.getAddr160())
index_import = self.wlt.cppWallet.convertToImportIndex(addr_index)
cppaddr = self.wlt.cppWallet.getImportAddrObjByIndex(index_import)
else:
cppaddr = self.wlt.cppWallet.getAddrObjByIndex(addr_index)
except:
LOGERROR('failed to grab address by index %d, original id: %d' % (addr_index, addr.chainIndex))
return QVariant()

addr160 = cppaddr.getAddrHash()
addrB58 = cppaddr.getScrAddr()
chainIdx = addr.chainIndex+1 # user must get 1-indexed
Expand Down
17 changes: 15 additions & 2 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
v0.96.4
v0.96.4, released March 30th 2018
== Added ==
- Updated fee estimate query from node to improve estimateSmartFee call introduced in Bitcoin Core 0.15.
- The block confirmation target slider now spans from 2 to 100 blocks (previously 2 to 6).
Expand Down Expand Up @@ -30,11 +30,24 @@ v0.96.4
- When in offline mode, the default signer option will now pick the proper signer for SegWit transactions.
- Fixed --ram-usage control. --ram-usage=1 will no longer hang the DB during bootstrap scans.
- As a result, --ram-usage defaults to 50 no instead of 4.
- Fixed fee calculation when checking "MAX" with SegWit UTXOs
- The Coin control dialog will no longer spawn invisible
- When creating a fragmented backup, fragments will no longer be cycled unecessarily
- Fixed imported address rendering in the Address Book dialog
- The Transaction Info dialog will now display address comments in the comment field if there is no comment
attached to the transaction itself
- The Transaction Info dialog will now properly display fee/byte fee for unconfirmed transactions
- The main transaction ledger will now display comments attached to outgoing addresses for each relevant transaction
- Fixed selecting an arbitrary wallet file in the Recovery Tool dialog.

== Removed ==
- You cannot sign messages with P2SH addresses. This functionality never existed in Armory to begin with, as it
did not produce single sig P2SH addresses prior to 0.96. It will be introduced in 0.97

== Minor ==
- You can now resize the Transaction Info dialog.

v0.96.3 released September 21st 2017
v0.96.3, released September 21st 2017
== Vulnerability Fix ==
- Fragmented backups were using a faulty implementation of Shamir's Secret Sharing (SSS).
One of the requirement of SSS security parameters is that the coefficients of the curve are chozen randomly. The implementation
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT([BitcoinArmory], [0.96.3.991], [moothecowlord@gmail.com])
AC_INIT([BitcoinArmory], [0.96.4], [moothecowlord@gmail.com])

AM_INIT_AUTOMAKE([1.10 subdir-objects foreign -Wall -Werror])

Expand Down
2 changes: 1 addition & 1 deletion cppForSwig/BitcoinP2P.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ void BitcoinP2P::connectLoop(void)
version.setVersionHeaderIPv4(70012, services, timestamp,
node_addr_, clientsocketaddr);

version.userAgent_ = "Armory:0.96.3.991";
version.userAgent_ = "Armory:0.96.4";
version.startHeight_ = -1;

sendMessage(move(version));
Expand Down
2 changes: 1 addition & 1 deletion cppForSwig/BlockDataManagerConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ bool NodeChainState::processState(

if (pct_int != prev_pct_int_)
{
LOGINFO << "waiting on node sync: " << pct_ << "%";
LOGINFO << "waiting on node sync: " << float(pct_ * 100.0) << "%";
prev_pct_int_ = pct_int;
}

Expand Down
13 changes: 10 additions & 3 deletions cppForSwig/CoinSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,17 @@ uint64_t CoinSelection::getFeeForMaxVal(
}

if (witnessSize != 0)
txSize += 2 + utxoVec_.size();
{
txSize += 2;
if (coinControlVec.size() == 0)
txSize += utxoVec_.size();
else
txSize += coinControlVec.size();
}

float fee = fee_byte * (txSize + witnessSize * 0.75f);
return uint64_t(fee);
uint64_t fee = uint64_t(fee_byte * float(txSize));
fee += uint64_t(float(witnessSize) * 0.25f * fee_byte);
return fee;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
44 changes: 44 additions & 0 deletions cppForSwig/LedgerEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,50 @@ void LedgerEntry::computeLedgerMap(map<BinaryData, LedgerEntry> &leMap,
usesWitness,
isChained);

/*
When signing a tx online, the wallet knows the txhash, therefor it can register all
comments on outgoing addresses under the txhash.
When the tx is signed offline, there is no guarantee that the txhash will be known
when the offline tx is crafted. Therefor the comments for each outgoing address are
registered under that address only.
In order for the GUI to be able to resolve outgoing address comments, the ledger entry
needs to carry those for pay out transactions
*/

if (value < 0)
{
try
{
//grab tx by hash
auto&& payout_tx = db->getFullTxCopy(txioVec.first);

//get scrAddr for each txout
for (unsigned i=0; i < payout_tx.getNumTxOut(); i++)
{
auto&& txout = payout_tx.getTxOutCopy(i);
scrAddrSet.insert(txout.getScrAddressStr());
}
}
catch (exception&)
{
LMDBEnv::Transaction zctx;
db->beginDBTransaction(&zctx, ZERO_CONF, LMDB::ReadOnly);

StoredTx stx;
if (!db->getStoredZcTx(stx, txioVec.first))
{
LOGWARN << "failed to get tx for ledger parsing";
}
else
{
for (auto& txout : stx.stxoMap_)
scrAddrSet.insert(txout.second.getScrAddress());
}
}
}

le.scrAddrSet_ = move(scrAddrSet);
leMap[txioVec.first] = le;
}
Expand Down
Loading

0 comments on commit 6452925

Please sign in to comment.