Armory was built in Python because of its extraordinary flexibility and ease of extensibility. Even the parts of Armory that are implemented in C++ have been made accessible through familiar Python syntax using SWIG. Just about everything related to Bitcoin and Armory is accessible by importing armoryengine.py in the base BitcoinArmory directory. Both ArmoryQt.py and armoryd.py are just large python scripts that use that engine, and therefore all functionality needed for a full client implementation is available using just armoryengine.py.

Below is some sample python code that uses armoryengine.py. There’s many more in the “extras” directory when you clone our git repository. Unfortunately, many of the scripts are outdated and will need be updated to be ultimately useful, but 98% of the code is functional. For now, the most reliable example code can be found in armoryd.py, which implements the full array of Armory functionality, including the event loop, networking loop, and wallet access. If you are looking for complete functionality, you might consider just modifying armoryd.py itself for your application rather than starting from scratch. Below are some code samples for writing new scripts.

Setting Up Your Development Environment

Follow the instructions on the Building From Source page.

Basic Bitcoin Utilities In armoryengine.py

After importing armoryengine.py, you will have access to a wide variety of generic Bitcoin utilities, all accessible from python. This includes easy conversions between:

  • Integers
  • Hex strings
  • Binary strings
  • Base58 strings
  • Address strings (Base58 with network byte and checksum)
  • VAR_INTs
  • Coin formats
  • Private keys
  • Public keys

It also includes wrappers for all the hash functions and cryptography (ECDSA). The sample code showing all these is long and would clutter the page, so it has been included at the bottom of this page.

Accessing A Wallet (No Blockchain)

The simplest and most common script is loading a wallet and getting the next address from it. Most users do this with a watching-only wallet: they want to generate unlimited deterministic addresses from a web server without exposing the private keys to the internet. This script works regardless of the wallet type.

#! /usr/bin/python **from** armoryengine.ALL **import** * wlt = PyBtcWallet().readWalletFile( CLI_ARGS[0] ) **print** wlt.getNextUnusedAddress().getAddrStr()

That’s it! That is all you need to open a wallet file, grab the next address, mark it used, then print the address string to the terminal. Simply run that script in the BitcoinArmory directory with a wallet file as the first argument:

$ python getNextAddress.py armory_2b1i32B43_.wallet 13oH966qRBKy5s9a8Uszq1yHegoTvtbi37 $ python getNextAddress.py armory_2b1i32B43_.wallet 1486e8pQ1Hd2Zmdc3JXQ1pmUrUohbirCQ9 $ python getNextAddress.py armory_2b1i32B43_.wallet 1QFuMJwsHDiY9EthT8afJqjUtVLHs75Q6e

Note that the above code has no access to the blockchain or the Bitcoin network, and therefore cannot check balances, create transactions, or see incoming transactions. See the next sections for examples of how to load the blockchain and synchronize your wallets with it. Also note that we have used “CLI_ARGS” instead of “argv” for accessing the command-line arguments. Doing this allows you to pass along standard Armory command-line arguments to your script (such as --testnet, --datadir, etc), and armoryengine.py will process them as expected, and cram all remaining arguments into CLI_ARGS. See the top of armoryengine.py to see the available terminal flags.

Watching Your Wallet Balance

Fully-commented script with error checking: extras/BDM_basics_watchBalance.py

Checking the wallet balance and creating transactions requires communicating with the BlockDataManager (TheBDM). You do this by reading your wallet file, registering it with TheBDM, initiating TheBDM, and then waiting for various signals to come back. Note that, unless you have built the Armory databases with the –supernode option, Armory will automatically initiate a rescan if there are any addresses in the wallet not there in previous loads.

#! /usr/bin/python
from armoryengine.ALL import *

# Read it into a PyBtcWallet object
wlt = PyBtcWallet().readWalletFile( CLI_ARGS[0] )

def printWalletBalance(args):
   # Print all three types of balances you can query for a wallet
   print 'Current top block:', TheBDM.getTopBlockHeight()

   for balType in ['full', 'spendable', 'unconfirmed']:
      balInt = wlt.getBalance(balType)
      balStr = coin2str(balInt)
      typeStr = balType.upper().rjust(16)
      print '%s balance for wallet %s: %s BTC' % (typeStr, wlt.uniqueIDB58, balStr)

TheBDM.RegisterEventForSignal(printWalletBalance, FINISH_LOAD_BLOCKCHAIN_ACTION)
TheBDM.RegisterEventForSignal(printWalletBalance, NEW_BLOCK_ACTION)
wlt.registerWallet(isNew=False)
TheBDM.goOnline()

# Pause main thread indefinitely while BDM thread works
while(True):
   time.sleep(1)

The above code immediately produces the output below, and will print the new balance on every new block.

$ python BDMbasics_watchBalance.py armory_4buuUcTy_WatchOnly.wallet
...
Current top block: 348051
          FULL balance for wallet 4buuUcTy: 5.46937069 BTC
     SPENDABLE balance for wallet 4buuUcTy: 5.46937069 BTC
   UNCONFIRMED balance for wallet 4buuUcTy: 0.00000000 BTC

There is no limit on the number of wallets that can be registered. If the wallet was just created and is guaranteed not to have any transaction history, you can make the registerWallet() call with isNew=True to avoid rescanning (since there’s no history to be found by scanning).

Getting a List of Spendable Coins in Your Wallet

Fully commented version with error checking: extras/BDM_basics_listUTXO.py

The following example shows how you can print a list of Unspent TxOuts (UTXOs) currently available to the specified wallet. It uses the method “pprintUnspentTxOutList()” which can found in armoryengine/CoinSelection.py. That file also contains methods for selecting spendable coins in your wallet to create transactions. It can be used to see how UTXOs are retrieved from the BDM and used.

#! /usr/bin/python
from armoryengine.ALL import * 
wlt = PyBtcWallet().readWalletFile(CLI_ARGS[0])
cvShutdown = threading.Condition(None)  # used to signal main thread shutdown

def listUTXOs(args):
   print 'Printing UTXOs for wallet: ' + wlt.uniqueIDB58
   utxos = wlt.getFullUTXOList()
   pprintUnspentTxOutList(utxos)
   cvShutdown.acquire(); cvShutdown.notify_all(); cvShutdown.release()

TheBDM.RegisterEventForSignal(listUTXOs, FINISH_LOAD_BLOCKCHAIN_ACTION)
wlt.registerWallet(isNew=False)
TheBDM.goOnline()

# This will pause the main thread until notification is received
cvShutdown.acquire(); cvShutdown.wait(); cvShutdown.release()

When run the output will look something like this:

$ python BDMbasics_listUTXOs.py armory_4buuUcTy_WatchOnly.wallet 
...
Printing UTXOs for wallet: 4buuUcTy
Coin Selection: (Total = 0.06218233 BTC)
   Owner Address                        TxOutValue       NumConf
   1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv   0.05000000 BTC   510
   1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv   0.00010000 BTC   125
   1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv   0.01000000 BTC   122
   1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX   0.00040332 BTC   16059
   1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX   0.00167901 BTC   16056

Tracing Transaction Flows in the Blockchain

Tracing transaction flows in the Bitcoin network can be done easily with Armory and python, but only if you build the Armory database in supernode mode. If you’ve never run Armory before, you can do so by adding the “–supernode” flag on the command line when you run armoryd.py, ArmoryQt.py or any script. If you have an existing non-supernode database, you can delete it manually from your home directory (Linux: /home/username/.armory/databases), or use the “–rebuild” option the first time.

Once you have built the supernode database, you will be able to run the example script located here: extras/BDM_basics_traceTxHistory.py

Networking And Zero-Confirmation Transactions

Once you get to the point that you want to handle live-network transactions, you should consider simply adapting armoryd.py, which has a full networking loop implemented, and has a function that gets called every time a new zero-confirmation transaction is received.

Transaction Creation And Signing

The following scripts contain code for creating transactions and signing them from the command line. That code can easily be merged with other code. Some of them may be outdated, but most of the code is functional (replace any BDM_LoadBlockchain() calls with TheBDM.setOnlineMode(True)):

https://github.com/etotheipi/BitcoinArmory/blob/master/extras/createTxFromAddrList.py
https://github.com/etotheipi/BitcoinArmory/blob/master/extras/cli_sign_txdp.py

Generic Bitcoin And Armory Utilities

The following demonstrates many of the utility methods available in armoryengine.py, mentioned at the top of this page:

#! /usr/bin/python
from armoryengine.ALL import *
# Integer/Hex/Binary/Base58 Conversions
print '\nInteger/Hex/Binary/Base58 Conversions'
print  1, hex_to_int('0f33')
print  2, hex_to_int('0f33', BIGENDIAN)
print  3, int_to_hex(13071)
print  4, int_to_hex(13071, widthBytes=4)
print  5, int_to_hex( 3891, widthBytes=4, endOut=BIGENDIAN)
print  6, [int_to_binary(65535, widthBytes=4)]
print  7, binary_to_int('ffff')
print  8, binary_to_hex('\x00\xff\xe3\x4f')
print  9, [hex_to_binary('00ffe34f')]
print 10, binary_to_base58('\x00\xff\xe3\x4f')
print 11, [base58_to_binary('12Ux6i')]
print '\nHash functions:'
print 12, binary_to_hex(  sha256('Single-SHA256') )
print 13, binary_to_hex( hash256('Double-SHA256') )
print 14, binary_to_hex( hash160('ripemd160(sha256(X))') )
print 15, binary_to_hex( HMAC512('secret', 'MsgAuthCode') )[:24]
print '\nMay need to switch endian to match online tools'
addr160Hex = binary_to_hex( hash160('\x00'*65) )
print 16, hex_switchEndian( addr160Hex )
print 17, binary_to_hex( hash160('\x00'*65), BIGENDIAN )
print '\nAddress Conversions:'
donateStr  = '1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv'
donate160  = addrStr_to_hash160(donateStr)
donateStr2 = hash160_to_addrStr(donate160)
print 18, binary_to_hex(donate160)
print 19, binary_to_hex(donate160, BIGENDIAN)
print 20, donateStr2
print '\nBuiltin Constants and magic numbers:'
print 21, 'BITCOIN_PORT:    ', BITCOIN_PORT
print 22, 'BITCOIN_RPC_PORT:', BITCOIN_RPC_PORT
print 23, 'ARMORY_RPC_PORT: ', ARMORY_RPC_PORT
print 24, 'MAGIC_BYTES:     ', binary_to_hex(MAGIC_BYTES)
print 25, 'GENESIS_BLK_HASH:', GENESIS_BLOCK_HASH_HEX
print 26, 'GENESIS_TX_HASH: ', GENESIS_TX_HASH_HEX    
print 27, 'ADDRBYTE:        ', binary_to_hex(ADDRBYTE)
print 28, 'NETWORK:         ', NETWORKS[ADDRBYTE]
print 29, 'P2SHBYTE:        ', binary_to_hex(P2SHBYTE)
print 30, 'PRIVKEYBYTE:     ', binary_to_hex(PRIVKEYBYTE)
print '\nDetected values and CLI_OPTIONS:'
print 31, '   Operating System      :', OS_NAME
print 32, '   OS Variant            :', OS_VARIANT
print 33, '   User home-directory   :', USER_HOME_DIR
print 34, '   Satoshi BTC directory :', BTC_HOME_DIR
print 35, '   Armory home dir       :', ARMORY_HOME_DIR
print 36, '   LevelDB directory     :', LEVELDB_DIR
print 37, '   Armory settings file  :', SETTINGS_PATH
print 38, '   Armory log file       :', ARMORY_LOG_FILE
print '\nSystem Specs:'
print 39, '   Total Available RAM   : %0.2f GB' % SystemSpecs.Memory
print 40, '   CPU ID string         :', SystemSpecs.CpuStr
print 41, '   Number of CPU cores   : %d cores' % SystemSpecs.NumCores
print 42, '   System is 64-bit      :', str(SystemSpecs.IsX64)
print 43, '   Preferred Encoding    :', locale.getpreferredencoding()
print '\nRandom other utilities'
print 44, '   Curr unix time        :', RightNow()
print 45, '   Curr formatted time   :', unixTimeToFormatStr(RightNow())
print 46, '   123456 seconds is     :', secondsToHumanTime(123456)
print 47, '   123456 bytes is       :', bytesToHumanSize(123456)
print '\nCoin2Str functions align the decimal point'
print 48, '   coin2str(0.01 BTC)    :', coin2str(0.01 * ONE_BTC)
print 49, '   coin2str(0.01 BTC)    :', coin2str(1000000, maxZeros=4)
print 50, '   coin2str(0.01 BTC)    :', coin2str(1000000, maxZeros=0)
print 51, '   coin2str(0.01 BTC)    :', coin2str(2300500000, maxZeros=0)
print 51, '   coin2str(0.01 BTC)    :', coin2str(160400000000, maxZeros=0)
print 51, '   coin2str(0.01 BTC)    :', coin2str(10000000, maxZeros=0)
print '\nRaw crypto operations:'
privKey = SecureBinaryData('\xa3'*32)
pubKey  = CryptoECDSA().ComputePublicKey(privKey)
addrStr = hash160_to_addrStr( hash160(pubKey.toBinStr()) )
print 'Raw Private Key:', privKey.toHexStr()
print 'Raw Public Key: ', pubKey.toHexStr()
print 'Raw Address Str:', addrStr
print 'Encoded PrivKey:', encodePrivKeyBase58(privKey.toBinStr())
print '\nPyBtcAddress Operations'
addrObj  = PyBtcAddress().createFromPlainKeyData(privKey)
privKey  = addrObj.serializePlainPrivateKey()
pubKey   = addrObj.serializePublicKey()
addrStr  = addrObj.getAddrStr()
addr160  = addrObj.getAddr160()
binSig   = addrObj.generateDERSignature('A msg to be signed!')
verified = addrObj.verifyDERSignature('A msg to be signed!', binSig)
print 'Obj Private Key:', binary_to_hex(privKey)
print 'Obj Public Key: ', binary_to_hex(pubKey)
print 'Obj Address Str:', addrStr
print 'Obj Address 160:', hex_switchEndian( binary_to_hex(addr160) )
print 'Obj Signature:  ', binary_to_hex(binSig)
print 'Obj SigVerifies:', verified
print '\nUse .pprint() members of objects for debugging'
addrObj.pprint()
print '\nUse pprintHex to visually break up large blocks of hex'
pprintHex( binary_to_hex( sha256('a')[:13] * 12 ) )

The output of the above script (with shortening of arbitrary hex blobs):

Integer/Hex/Binary/Base58 Conversions
1 13071
2 3891
3 0f33
4 0f330000
5 00000f33
6 ['\xff\xff\x00\x00']
7 1717986918
8 00ffe34f
9 ['\x00\xff\xe3O']
10 12Ux6i
11 ['\x00\xff\xe3O']
Hash functions:
12 4a9d5c06c3c48c552fe00b25a75cf26c6dc6d3c7ebd5a2dc5d0f94adeeb69a5c
13 67294d212772d4be04636a7060acf419216627bb48a3b7faad33efbe2f6256aa
14 f2c7c888d94e2afd80dc71907b7ceee0d3c5c314
15 a245baadd62d2174d454335d
May need to switch endian to match online tools
16 a3fad5fe0b300c5f25af814dc70394ba1dc3601b
17 a3fad5fe0b300c5f25af814dc70394ba1dc3601b
Address Conversions:
18 6c22eb00e3f93acac5ae5d81a9db78a645dfc9c7
19 c7c9df45a678dba9815daec5ca3af9e300eb226c
20 1ArmoryXcfq7TnCSuZa9fQjRYwJ4bkRKfv
Builtin Constants and magic numbers:
21 BITCOIN_PORT:     8333
22 BITCOIN_RPC_PORT: 8332
23 ARMORY_RPC_PORT:  8225
24 MAGIC_BYTES:      f9beb4d9
25 GENESIS_BLK_HASH: ...ae63f74f931e8365e15a089c68d6190000000000
26 GENESIS_TX_HASH:  ...67768f617fc81bc3888a51323a9fb8aa4b1e5e4a
27 ADDRBYTE:         00
28 NETWORK:          Main Network
29 P2SHBYTE:         05
30 PRIVKEYBYTE:      80
Detected values and CLI_OPTIONS:
31    Operating System      : Linux
32    OS Variant            : ('Ubuntu', '13.10', 'saucy')
33    User home-directory   : /home/user
34    Satoshi BTC directory : /home/user/.bitcoin/
35    Armory home dir       : /home/user/.armory/
36    LevelDB directory     : /home/user/.armory/databases
37    Armory settings file  : /home/user/.armory/ArmorySettings.txt
38    Armory log file       : /home/user/.armory/armorylog.txt
System Specs:
39    Total Available RAM   : 15.8 GB
40    CPU ID string         : Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz
41    Number of CPU cores   : 4 cores
42    System is 64-bit      : True
43    Preferred Encoding    : UTF-8
Random other utilities
44    Curr unix time        : 1385965568.67
45    Curr formatted time   : 2013-Dec-02 01:26am
46    123456 seconds is     : 1.5 days
47    123456 bytes is       : 120.6 kB
Coin2Str functions align the decimal point
48    coin2str(0.01 BTC)    :         0.01000000
49    coin2str(0.01 BTC)    :         0.0100    
50    coin2str(0.01 BTC)    :         0.01      
51    coin2str(0.01 BTC)    :        23.005     
51    coin2str(0.01 BTC)    :      1604       
51    coin2str(0.01 BTC)    :         0.1
Raw crypto operations:
Raw Private Key: a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3...
Raw Public Key:  04def24e149639253723c9876cf0078a...
Raw Address Str: 1H9cQkySJ57GTarfZXKTukpXdkHajBKJWv
Encoded PrivKey: 5K4MXSXg1oD7kCvNiVcibVjcFFidqyVKzLavmWUZodwSPQfkNz8
PyBtcAddress Operations
Obj Private Key: a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3...
Obj Public Key:  04def24e149639253723c9876cf0078a05...
Obj Address Str: 1H9cQkySJ57GTarfZXKTukpXdkHajBKJWv
Obj Address 160: c19f22704f3b790e65e5f61aff4a9a3011e622b1
Obj Signature:   3045022100984fd6d9ca50a41ced3daaef...
Obj SigVerifies: True
Use .pprint() members of objects for debugging
BTC Address      : 1H9cQkySJ57GTarfZXKTukpXdkHajBKJWv
Hash160[BE]      : b122e611309a4aff1af6e5650e793b4f70229fc1
Wallet Location  : -1
Chained Address  : True
Have (priv,pub)  : (True,True)
First/Last Time  : (4294967295,0)
First/Last Block : (4294967295,0)
PubKeyX(BE)      : def24e149639253723c9876cf0078a056...
PubKeyY(BE)      : a0f013035e85c2996c32aa11fa46a436e...
Encryption parameters:
   UseEncryption : False
   IsLocked      : False
   KeyChanged    : False
   ChainIndex    : 0
   Chaincode     : -----------------------------------
   InitVector    : -----------------------------------
PrivKeyPlain(BE) : a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3...
PrivKeyCiphr(BE) : -----------------------------------
Use pprintHex to visually break up large blocks of hex
ca978112 ca1bbdca fac231b3 9aca9781 12ca1bbd cafac231 b39aca97 8112ca1b 
bdcafac2 31b39aca 978112ca 1bbdcafa c231b39a ca978112 ca1bbdca fac231b3 
9aca9781 12ca1bbd cafac231 b39aca97 8112ca1b bdcafac2 31b39aca 978112ca 
1bbdcafa c231b39a ca978112 ca1bbdca fac231b3 9aca9781 12ca1bbd cafac231 
b39aca97 8112ca1b bdcafac2 31b39aca 978112ca 1bbdcafa c231b3