Bitcoin Forum
November 12, 2024, 12:39:14 AM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1] 2 3 »  All
  Print  
Author Topic: General questions arising when adding Namecoin support to Armory  (Read 5650 times)
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 11, 2014, 02:13:35 AM
Last edit: November 20, 2014, 04:13:20 PM by josephbisch
 #1

So I am working on adding Namecoin support to Armory.

I think that I got a lot of the work done already. My issue is that Namecoin is based off of an old version of Bitcoin. Therefore it uses block files like ~/.namecoin/blk0001.dat instead of ~/.bitcoin/blocks/blk00000.dat, which is used by modern Bitcoin clients. Armory is currently designed to look for blocks of the latter naming convention.

What I want to do is modify the C++ code to find the Namecoin blocks when the --namecoin flag is set and to find Bitcoin blocks when there is no --namecoin flag. I've figured out that I want to modify the getBlkFilename function in cppForSwig/BtcUtils.h. The problem is I need to do so based on a variable called COIN in armoryengine/ArmoryUtils.py (it's not in the GitHub repo, just in my local repo). The variable COIN will either be the string 'Namecoin' or the string 'Bitcoin' based on flags set when running Armory.

So my question is how would I go about accessing COIN from BtcUtils.h? Or is there some other way to determine the network in use that I am missing?
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 11, 2014, 03:13:13 AM
 #2

Hi Joseph,

The short answer is "you don't". The useful answer is that Armory loads the C++ side from BDM.py. There it creates a BlockDataManagerConfig object. When you create a BlockDataManagerConfig, you could specify parameters there that affect the C++ side's behavior, like the file format convention for blk files.

You're developing against the "dev" branch, right? You should.
etotheipi
Legendary
*
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
November 11, 2014, 04:30:40 AM
 #3

FYI, here's the old transitional code for the same method:

https://github.com/etotheipi/BitcoinArmory/blob/v0.88-beta/cppForSwig/BtcUtils.h#L736

And as njaard said:  you don't access python from C++.  If you need something, put it in the C++ side, and call it from python.  SWIG is amazing (being able to call C++ objects and methods as if they're native python objects), but it doesn't go both ways, and in fact it can get quite complicated to do so.


Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 11, 2014, 02:03:16 PM
 #4

First of all, yes I am developing against the dev branch. When I am done, I plan on stashing my changes, bringing dev up-to-date, reapplying my changes, and making a git diff to email.

Looking at BlockDataManagerConfig and BDM.py, I see that the Python code passes the magic bytes to the C++ code. So I think the best way to do what I want to do is by checking the magic bytes.

The comments in cppForSwig/BlockUtils.h reference a GetInstance() method. I don't see any GetInstance() method, so I assume it was removed? How would I safely get an instance of BlockDataManager_LevelDB? I think I want an instance of BlockDataManager_LevelDB, because that class has getMagicBytes().
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 11, 2014, 04:41:46 PM
 #5

You cannot get an instance of BlockDataManager_LevelDB from anywhere, anymore. But anywhere that calls getBlkFilename has an instance of BlockDataManagerConfig available.

I recommend you put a boolean in in BlockDataManagerConfig that sets things to namecoin mode, and then check that boolean wherever getBlkFilename is called, and maybe write a new function, or use etotheipi's old version and modify its parameters based on that.
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 11, 2014, 08:51:22 PM
 #6

I think I may have gotten in over my head with trying to add Namecoin support. I don't think I'm going to be able to finish the C++ parts (myself at least).

I hardcoded Namecoin as the network, because I couldn't figure out how to access the configuration data from within the detectAllBlkFiles method. As an aside, I made the variable a string, so that it can be used for future altcoin additions.

Now I get a bunch of errors like the following when building the db.

Code:
-ERROR - 1415733082: (StoredBlockObj.cpp:332) Merkle root mismatch! Raw block data is corrupt!
-ERROR - 1415733082: (BlockUtils.cpp:315) Error parsing block (corrupt?) - block header valid (hash=faa064f45e4bec7b52b83f78517d632fe43d6064afd8d55587e202a56c70a633) (error encountered processing block at byte 229714131 file /home/joseph/.namecoin/testnet/blk0001.dat, blocksize 666)

The merkle root calculation should be independent of the chain in use, so I don't think that is it. The dat file should be parsable. So I don't understand what caused the error.

FWIW, I get these errors with Namecoin and Namecoin Testnet.

I'll look more into it when I have time later, but I just wanted to post where I'm at now, in case anyone has any tips.
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 11, 2014, 08:57:55 PM
 #7

You got so far into the the file (229714131 bytes) that it makes me think maybe the file itself is corrupt. Try blowing away namecoind data and download its blockchain again?

But conversely, you got it in the testnet as well, so that seems unlikely.
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 11, 2014, 09:04:37 PM
 #8

If my math is right, it's only 229 MB into the file, when the whole namecoin blockchain as reported by bitinfocharts.com is 1.97 GB. My entire ~/.namecoin directory is 2.2 GB, and that includes the testnet blockchain.
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 11, 2014, 09:19:19 PM
 #9

Open up a hex editor on that file, go to that byte offset, and see if there's anything unusual.
etotheipi
Legendary
*
Offline Offline

Activity: 1428
Merit: 1093


Core Armory Developer


View Profile WWW
November 11, 2014, 09:43:33 PM
 #10

If my math is right, it's only 229 MB into the file, when the whole namecoin blockchain as reported by bitinfocharts.com is 1.97 GB. My entire ~/.namecoin directory is 2.2 GB, and that includes the testnet blockchain.

This m might be a simple question for a namecoin guru:  what is the difference in block formats between Bitcoin and namecoin?  It might be something stupid and simple.  Could it have to do with the name reservation system?  Do transactions have a different structure when they register namecoin names? 

Founder and CEO of Armory Technologies, Inc.
Armory Bitcoin Wallet: Bringing cold storage to the average user!
Only use Armory software signed by the Armory Offline Signing Key (0x98832223)

Please donate to the Armory project by clicking here!    (or donate directly via 1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX -- yes, it's a real address!)
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 11, 2014, 09:59:46 PM
 #11

I started redownloading the namecoin testnet blockchain before your post, so I used the hex editor on a different block (there were many that had the error).

The output:

Code:
joseph@crunchbang:~$ hexdump -s 229723055 -n 602 .namecoin/testnet/blk0001.dat
db14baf bffa feb5 025a 0000 0101 0001 dc8f c35c
db14bbf 6542 81ba 1967 8a6e 9340 f339 fd89 da53
db14bcf 044b a3f1 f01b e71a 4f41 773d a45e 078c
db14bdf bf53 7980 3b9f 6f16 512b 0215 83b5 1f61
db14bef 3a4a 64f7 98f9 c283 9ed2 ae73 77f5 5462
db14bff 9b9d 1a4e 0000 0000 0001 0000 0001 0000
db14c0f 0000 0000 0000 0000 0000 0000 0000 0000
db14c1f 0000 0000 0000 0000 0000 0000 ff00 ffff
db14c2f 53ff 6c03 04b2 2f06 3250 4853 042f 79a1
db14c3f 5462 fa08 6dbe 176d 468b 088f 0bcc a082
db14c4f b34a 408f ec03 ca6c a668 8605 d7eb ac26
db14c5f 8444 d787 7ef5 0157 0000 0000 0000 4f00
db14c6f ffff a6f1 000d 0d00 6e2f 646f 5365 7274
db14c7f 7461 6d75 002f 0000 0100 59a4 9503 0000
db14c8f 0000 7619 14a9 b321 6366 e343 edc3 5e25
db14c9f 4784 c340 32f6 d76e 74a9 ac88 0000 0000
db14caf 2ef2 a81a e5d6 5fcb bceb b5d9 12cb 2cea
db14cbf 34e0 5fbd a566 9378 4267 0000 0000 0000
db14ccf 6803 01ff 25f7 891b 4812 cee4 1554 ff25
db14cdf 097c 8cf4 9eb2 3133 5d05 eaa7 2f5c 014d
db14cef 8397 e83f 2e96 316c ab8b a1f3 186f 1ef6
db14cff 5fc0 7e4a 043a fd0f 9b7c bc7c b17b e7ba
db14d0f 5032 2bc7 f953 84be 80ea b4c7 7495 5837
db14d1f ee51 4b6f eb3a 35b4 c216 ad8a bd39 39e8
db14d2f 00f2 0000 0000 0000 0000 0002 0000 a2c2
db14d3f 46d5 de9e 9b06 f509 3610 b8f9 1168 c716
db14d4f baf7 f0cf 376e 0b18 0000 0000 0000 fb61
db14d5f 1039 9acf 9b12 adab 26d5 26a9 0ec0 b47c
db14d6f f36d 5929 da55 dca9 b6c0 a374 51bc 84a7
db14d7f 5462 c8bc 1a0e d68b e044 0101 0000 0100
db14d8f 0000 0000 0000 0000 0000 0000 0000 0000
*
db14daf ffff ffff 0408 9b9d 1a4e 0101 ff52 ffff
db14dbf 01ff f200 2a05 0001 0000 4143 6a04 80e0
db14dcf 9599 b6d9 259c 8be6 4399 8952 948a 4392
db14ddf 36f4 521d 1385 3515 2c44 3d34 bcb4 487f
db14def b808 8f5c 5c93 29f7 acd6 210b 033c 9123
db14dff e768 dcca 3726 7b6e 0088               
db14e09

It starts with the version/magic bytes (bffa feb5), which appear as fabfb5fe in the Armory code, but there are no errors regarding the magic bytes, so I guess it is supposed to be like that. Next is supposed to be the prev_block (025a 0000 0101 0001 dc8f c35c 6542 81ba 1967 8a6e 9340 f339 fd89 da53 044b a3f1), but that doesn't look like it has enough leading zeros. The big strings of zeros later on in a couple of places look suspicious too.

I will try to find a namecoin expert to check to see if there is some difference in the format.
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 11, 2014, 10:51:44 PM
 #12

It's hexdump that is exchanging the order of each pair of bytes. Give it the option -C
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 12, 2014, 12:08:06 AM
 #13

Thanks, it is indeed the correct magic bytes when using the -C option.

I got a response from one of the Namecoin guys, who referred me to another Namecoin guy that will know more.

It looks like we have two main differences. One is that there are merged mining headers in addition to the normal data. Two is that Namecoin still uses BDB instead of LevelDB. But that isn't really an issue, since the underlying data format is the same (with the exception of the addition of the merged mining headers), right?

A version of Namecoin based off of a more recent version of Bitcoin is in the works, but there is no ETA on it.
njaard
Newbie
*
Offline Offline

Activity: 29
Merit: 0


View Profile
November 12, 2014, 01:52:54 AM
 #14

Whether they use BDB or LevelDB is not relevant because those blk files are not BDB or LevelDB files, so indeed that does not make a difference.

That their headers have a different format does make a difference (and I would not be surprised if they started using merged mining at the time of when your patched Armory failed).

In order to support their slightly different block format, you'll need to modify the class BlockHeader. I would recommend, for the time being, not bother making a program that supports both bitcoin and namecoin without recompiling.
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 12, 2014, 08:07:20 PM
 #15

I took a look at BlockHeader (and saw how it has version and prevBlock and all that), but I think I really want the TxCalcLength function in BtcUtils.h. That is because the merged mining data comes before the transactions, so I need to offset the start of the transactions by the length of the merged mining data.

BlockObj.cpp calls TxCalcLength. I added a parameter to TxCalcLength to pass the block height and I used getBlockHeight for the parameter, but it is just uint32_max, so I guess it isn't set at that point in the execution. My plan is to increment the pointer in TxCalcLength to skip the merged mining data. I will use the block height to decide whether to increment or not, because the merged mining only starts at block 19200.

If it was reading the transaction data incorrectly then that would explain the merkle root mismatch errors I was getting.

In addition to the help from the Namecoin devs, I found some helpful information on the wiki. One thing I learned from the devs is that the hash of merge mined blocks won't necessarily have leading zeros as you can see here with the first merge mined block, which makes sense now that I think about it. So that explains why the block I was looking at with hexdump doesn't have leading zeros for it's prevBlockHash.

The wiki page confirms that the auxpow block (auxpow refers to the Namecoin block when a block is merge mined) has it's extra data inserted between the nonce and txn_count. The extra data includes variable length fields, so it's not going to be trivial to calculate the offset to find where the txn_count starts.

Boy, when I started I thought this would just be a matter of tweaking a few parameters in Python and changing the text to say Namecoin. I was sure wrong!
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 12, 2014, 10:59:03 PM
 #16

As I said before, I think I got in over my head with the C++. I originally thought I could add Namecoin support by just modifying the Python code. Even when it became apparent that I would need to work with the C++ code, it looked a lot simpler than it does now.

It may seem premature to give up already considering that I probably have only spent a few days on Namecoin support, but it is apparent now that I won't be able to do it myself.

I feel like I'm taking more than I am contributing at this point. Thank you to everyone who has helped me. I'll post the diff of what I have so far tomorrow and hopefully someone who knows more than I do comes along and finishes the C++ side of things. I should stick to what I know really well, which is Debian packaging and Python coding. Smiley

Sorry if I got anyone excited at the prospect of Namecoin support in Armory.
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 13, 2014, 02:59:25 PM
 #17

So here is the diff so far:

Code:
diff --git a/ArmoryQt.py b/ArmoryQt.py
index de98353..54afbe0 100644
--- a/ArmoryQt.py
+++ b/ArmoryQt.py
@@ -96,6 +96,18 @@ class ArmoryMainWindow(QMainWindow):
          self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_green_h56.png'))
          if Colors.isDarkBkgd:
             self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_white_text_green_h56.png'))
+      elif USE_NAMECOIN:
+         self.setWindowTitle('Armory - Namecoin Wallet Management')
+         self.iconfile = ':/armory_icon_32x32.png'
+         self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_h44.png'))
+         if Colors.isDarkBkgd:
+            self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_white_text_h65.png'))
+      elif USE_NAMECOIN_TESTNET:
+         self.setWindowTitle('Armory - Namecoin Wallet Management [TESTNET]')
+         self.iconfile = ':/armory_icon_green_32x32.png'
+         self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_green_h56.png'))
+         if Colors.isDarkBkgd:
+            self.lblLogoIcon.setPixmap(QPixmap(':/armory_logo_white_text_green_h56.png'))
       else:
          self.setWindowTitle('Armory - Bitcoin Wallet Management')
          self.iconfile = ':/armory_icon_32x32.png'
@@ -110,7 +122,7 @@ class ArmoryMainWindow(QMainWindow):
          self.setWindowIcon(QIcon(self.iconfile))
       else:
          self.notifCtr = ArmoryMac.MacNotificationHandler.None
-         if USE_TESTNET:
+         if USE_TESTNET or USE_NAMECOIN_TESTNET:
             self.iconfile = ':/armory_icon_green_fullres.png'
             ArmoryMac.MacDockIconHandler.instance().setMainWindow(self)
             ArmoryMac.MacDockIconHandler.instance().setIcon(QIcon(self.iconfile))
@@ -533,7 +545,7 @@ class ArmoryMainWindow(QMainWindow):
       self.mainDisplayTabs.addTab(self.tabAnnounce,  'Announcements')
 
       ##########################################################################
-      if USE_TESTNET and not CLI_OPTIONS.disableModules:
+      if (USE_TESTNET or USE_NAMECOIN_TESTNET) and not CLI_OPTIONS.disableModules:
          self.loadArmoryModules()   
       ##########################################################################
 
@@ -958,7 +970,7 @@ class ArmoryMainWindow(QMainWindow):
             LOGWARN('Sig on "%s" is valid: %s' % (name, str(isSignedByATI)))
             
 
-         if not isSignedByATI and not USE_TESTNET:
+         if not isSignedByATI and not (USE_TESTNET or USE_NAMECOIN_TESTNET):
             reply = QMessageBox.warning(self, tr("UNSIGNED Module"), tr("""
                Armory detected the following module which is
                <font color="%s"><b>unsigned</b></font> and may be dangerous:
@@ -1321,7 +1333,7 @@ class ArmoryMainWindow(QMainWindow):
       self.sysTray = QSystemTrayIcon(self)
       self.sysTray.setIcon( QIcon(self.iconfile) )
       self.sysTray.setVisible(True)
-      self.sysTray.setToolTip('Armory' + (' [Testnet]' if USE_TESTNET else ''))
+      self.sysTray.setToolTip('Armory' + (' [Testnet]' if USE_TESTNET or USE_NAMECOIN_TESTNET else ''))
       self.connect(self.sysTray, SIGNAL('messageClicked()'), self.bringArmoryToFront)
       self.connect(self.sysTray, SIGNAL('activated(QSystemTrayIcon::ActivationReason)'), \
                    self.sysTrayActivated)
@@ -1421,7 +1433,7 @@ class ArmoryMainWindow(QMainWindow):
       """
       LOGINFO('setupUriRegistration')
 
-      if USE_TESTNET:
+      if USE_TESTNET or USE_NAMECOIN or USE_NAMECOIN_TESTNET:
          return
 
       if OS_LINUX:
@@ -5081,8 +5093,12 @@ class ArmoryMainWindow(QMainWindow):
             lastBlkTime = info['toptime']
 
          # Use a reference point if we are starting from scratch
-         refBlock = max(290746,      lastBlkNum)
-         refTime  = max(1394922889,  lastBlkTime)
+         if COIN == 'Namecoin':
+            refBlock = max(204428,      lastBlkNum)
+            refTime  = max(1415390883,  lastBlkTime)
+         else:
+            refBlock = max(290746,      lastBlkNum)
+            refTime  = max(1394922889,  lastBlkTime)
 
 
          # Ten min/block is pretty accurate, even from genesis (about 1% slow)
@@ -6929,7 +6945,7 @@ def checkForAlreadyOpenError():
          armoryExists.append(proc.pid)
       if bexe in proc.name:
          LOGINFO('Found bitcoind PID: %d', proc.pid)
-         if ('testnet' in proc.name) == USE_TESTNET:
+         if ('testnet' in proc.name) == USE_TESTNET or USE_NAMECOIN_TESTNET:
             bitcoindExists.append(proc.pid)
 
    if len(armoryExists)>0:
@@ -6956,7 +6972,7 @@ if 1:
       checkForAlreadyOpen()
 
    pixLogo = QPixmap(':/splashlogo.png')
-   if USE_TESTNET:
+   if USE_TESTNET or USE_NAMECOIN_TESTNET:
       pixLogo = QPixmap(':/splashlogo_testnet.png')
    SPLASH = QSplashScreen(pixLogo)
    SPLASH.setMask(pixLogo.mask())
diff --git a/SDM.py b/SDM.py
index 3b80b55..238b5d2 100644
--- a/SDM.py
+++ b/SDM.py
@@ -20,7 +20,7 @@ from armoryengine.ArmoryUtils import BITCOIN_PORT, LOGERROR, hex_to_binary, \
    launchProcess, killProcessTree, killProcess, LOGWARN, RightNow, HOUR, \
    PyBackgroundThread, touchFile, DISABLE_TORRENTDL, secondsToHumanTime, \
    bytesToHumanSize, MAGIC_BYTES, deleteBitcoindDBs, TheTDM, satoshiIsAvailable,\
-   MEGABYTE, ARMORY_HOME_DIR, CLI_OPTIONS
+   MEGABYTE, ARMORY_HOME_DIR, CLI_OPTIONS, COIN, USE_NAMECOIN_TESTNET
 from bitcoinrpc_jsonrpc import authproxy
 
 
@@ -277,7 +277,10 @@ class SatoshiDaemonManager(object):
       LOGINFO('Total size of files in %s is %s' % (blockDir, sizeStr))
 
       # If they have only a small portion of the blockchain, do it
-      szThresh = 100*MEGABYTE if USE_TESTNET else 6*GIGABYTE
+      if USE_TESTNET or USE_NAMECOIN_TESTNET:
+         szThresh = 100*MEGABYTE
+      else:
+         szThresh = 6*GIGABYTE
       if blockDirSize < szThresh:
          return True
 
@@ -308,7 +311,7 @@ class SatoshiDaemonManager(object):
       self.failedFindHome = False
       # If we are supplied a path, then ignore the extra exe search paths
       if pathToBitcoindExe==None:
-         pathToBitcoindExe = self.findBitcoind(extraExeSearch)
+         pathToBitcoindExe = self.findDaemon(extraExeSearch)
          if len(pathToBitcoindExe)==0:
             LOGDEBUG('Failed to find bitcoind')
             self.failedFindExe = True
@@ -374,7 +377,7 @@ class SatoshiDaemonManager(object):
 
 
    #############################################################################
-   def findBitcoind(self, extraSearchPaths=[]):
+   def findDaemon(self, extraSearchPaths=[]):
       self.foundExe = []
 
       searchPaths = list(extraSearchPaths)  # create a copy
@@ -382,7 +385,7 @@ class SatoshiDaemonManager(object):
       if OS_WINDOWS:
          # Making sure the search path argument comes with /daemon and /Bitcoin on Windows
 
-         searchPaths.extend([os.path.join(sp, 'Bitcoin') for sp in searchPaths])
+         searchPaths.extend([os.path.join(sp, COIN) for sp in searchPaths])
          searchPaths.extend([os.path.join(sp, 'daemon') for sp in searchPaths])
 
          possBaseDir = []         
@@ -407,7 +410,7 @@ class SatoshiDaemonManager(object):
                   shell = win32com.client.Dispatch('WScript.Shell')
                   targ = shell.CreateShortCut(path).Targetpath
                   targDir = os.path.dirname(targ)
-                  LOGINFO('Found Bitcoin-Qt link on desktop: %s', targDir)
+                  LOGINFO('Found %s-Qt link on desktop: %s', COIN, targDir)
                   possBaseDir.append( targDir )
 
          # Also look in default place in ProgramFiles dirs
@@ -417,12 +420,12 @@ class SatoshiDaemonManager(object):
 
          # Now look at a few subdirs of the
          searchPaths.extend(possBaseDir)
-         searchPaths.extend([os.path.join(p, 'Bitcoin', 'daemon') for p in possBaseDir])
+         searchPaths.extend([os.path.join(p, COIN, 'daemon') for p in possBaseDir])
          searchPaths.extend([os.path.join(p, 'daemon') for p in possBaseDir])
-         searchPaths.extend([os.path.join(p, 'Bitcoin') for p in possBaseDir])
+         searchPaths.extend([os.path.join(p, COIN) for p in possBaseDir])
 
          for p in searchPaths:
-            testPath = os.path.join(p, 'bitcoind.exe')
+            testPath = os.path.join(p, COIN.lower() + '.exe')
             if os.path.exists(testPath):
                self.foundExe.append(testPath)
 
@@ -433,18 +436,18 @@ class SatoshiDaemonManager(object):
          else:
             searchPaths.extend([os.path.join(p, 'bin/32') for p in extraSearchPaths])
 
-         searchPaths.extend(['/usr/lib/bitcoin/'])
+         searchPaths.extend(['/usr/lib/' + COIN.lower() + '/'])
          searchPaths.extend(os.getenv("PATH").split(':'))
 
          for p in searchPaths:
-            testPath = os.path.join(p, 'bitcoind')
+            testPath = os.path.join(p, COIN.lower() + 'd')
             if os.path.exists(testPath):
                self.foundExe.append(testPath)
 
          try:
-            locs = subprocess_check_output(['whereis','bitcoind']).split()
+            locs = subprocess_check_output(['whereis',COIN.lower() + 'd']).split()
             if len(locs)>1:
-               locs = filter(lambda x: os.path.basename(x)=='bitcoind', locs)
+               locs = filter(lambda x: os.path.basename(x)==COIN.lower() + 'd', locs)
                LOGINFO('"whereis" returned: %s', str(locs))
                self.foundExe.extend(locs)
          except:
@@ -461,10 +464,10 @@ class SatoshiDaemonManager(object):
                foundIt=True
 
          if not foundIt:
-            LOGERROR('Bitcoind could not be found in the specified installation:')
+            LOGERROR(COIN + 'd could not be found in the specified installation:')
             for p in extraSearchPaths:
                LOGERROR('   %s', p)
-            LOGERROR('Bitcoind is being started from:')
+            LOGERROR(COIN + 'd is being started from:')
             LOGERROR('   %s', self.foundExe[0])
 
       return self.foundExe
@@ -607,12 +610,16 @@ class SatoshiDaemonManager(object):
 
       pargs = [self.executable]
 
-      if USE_TESTNET:
+      if USE_TESTNET or USE_NAMECOIN_TESTNET:
          testhome = self.satoshiHome[:]
          if self.satoshiHome.endswith('/testnet3/'):
             pargs.append('-datadir=%s' % self.satoshiHome[:-10])
          elif self.satoshiHome.endswith('/testnet3'):
             pargs.append('-datadir=%s' % self.satoshiHome[:-9])
+         elif self.satoshiHome.endswith('/testnet/'):
+            pargs.append('-datadir=%s' % self.satoshiHome[:-9])
+         elif self.satoshiHome.endswith('/testnet'):
+            pargs.append('-datadir=%s' % self.satoshiHome[:-8])
          pargs.append('-testnet')
       else:
          pargs.append('-datadir=%s' % self.satoshiHome)
diff --git a/armoryd.py b/armoryd.py
index 0198b8b..3d36f36 100644
--- a/armoryd.py
+++ b/armoryd.py
@@ -1495,7 +1495,8 @@ class Armory_Json_Rpc_Server(jsonrpc.JSONRPC):
                #'proxy':             '',
                'difficulty':        TheBDM.getTopBlockDifficulty() \
                                     if isReady else -1,
-               'testnet':           USE_TESTNET,
+               'testnet':           USE_TESTNET or USE_NAMECOIN_TESTNET,
+               'chain':             COIN
                'keypoolsize':       self.curWlt.addrPoolSize
              }
 
diff --git a/armoryengine/ArmoryUtils.py b/armoryengine/ArmoryUtils.py
index ed61fa8..103872f 100644
--- a/armoryengine/ArmoryUtils.py
+++ b/armoryengine/ArmoryUtils.py
@@ -62,6 +62,7 @@ ARMORY_INFO_SIGN_ADDR = '1NWvhByxfTXPYNT4zMBmEY3VL8QJQtQoei'
 ARMORY_INFO_SIGN_PUBLICKEY = ('04'
       'af4abc4b24ef57547dd13a1110e331645f2ad2b99dfe1189abb40a5b24e4ebd8'
       'de0c1c372cc46bbee0ce3d1d49312e416a1fa9c7bb3e32a7eb3867d1c6d1f715')
+# We can leave SATOSHI_PUBLIC_KEY here, since it is the same for both Bitcoin and Namecoin
 SATOSHI_PUBLIC_KEY = ( '04'
       'fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0'
       'ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284')
@@ -80,6 +81,8 @@ parser.add_option("--satoshi-rpcport", dest="satoshiRpcport",default='DEFAULT',t
 parser.add_option("--dbdir",           dest="leveldbDir",  default='DEFAULT', type='str',          help="Location to store blocks database (defaults to --datadir)")
 parser.add_option("--rpcport",         dest="rpcport",     default='DEFAULT', type="str",          help="RPC port for running armoryd.py")
 parser.add_option("--testnet",         dest="testnet",     default=False,     action="store_true", help="Use the testnet protocol")
+parser.add_option("--namecoin",        dest="namecoin",    default=False,     action="store_true", help="Use Namecoin instead of Bitcoin protocol")
+parser.add_option("--namecoin-testnet", dest="namecoinTestnet", default=False, action="store_true", help="Use Namecoin testnet protocol")
 parser.add_option("--offline",         dest="offline",     default=False,     action="store_true", help="Force Armory to run in offline mode")
 parser.add_option("--nettimeout",      dest="nettimeout",  default=2,         type="int",          help="Timeout for detecting internet connection at startup")
 parser.add_option("--interport",       dest="interport",   default=-1,        type="int",          help="Port for inter-process communication between Armory instances")
@@ -240,6 +243,18 @@ for opt,val in CLI_OPTIONS.__dict__.iteritems():
 USE_TESTNET = CLI_OPTIONS.testnet
 #USE_TESTNET = True
 
+# Use CLI args to determine namecoin or not
+USE_NAMECOIN = CLI_OPTIONS.namecoin
+
+# Use CLI args to determine namecoin testnet or not
+USE_NAMECOIN_TESTNET = CLI_OPTIONS.namecoinTestnet
+
+COIN = 'Bitcoin'
+if USE_NAMECOIN or USE_NAMECOIN_TESTNET:
+   MIN_TX_FEE = 500000
+   MIN_RELAY_TX_FEE = 100000
+   COIN = 'Namecoin'
+
 # Set default port for inter-process communication
 if CLI_OPTIONS.interport < 0:
    CLI_OPTIONS.interport = 8223 + (1 if USE_TESTNET else 0)
@@ -257,7 +272,14 @@ USER_HOME_DIR    = ''
 BTC_HOME_DIR     = ''
 ARMORY_HOME_DIR  = ''
 LEVELDB_DIR      = ''
-SUBDIR = 'testnet3' if USE_TESTNET else ''
+if USE_TESTNET:
+   SUBDIR = 'testnet3'
+elif USE_NAMECOIN:
+   SUBDIR = ''
+elif USE_NAMECOIN_TESTNET:
+   SUBDIR = 'testnet'
+else:
+   SUBDIR = ''
 if OS_WINDOWS:
    OS_NAME         = 'Windows'
    OS_VARIANT      = platform.win32_ver()
@@ -267,27 +289,51 @@ if OS_WINDOWS:
    rt = ctypes.windll.shell32.SHGetFolderPathW(0, 26, 0, 0, ctypes.byref(buffer))
    USER_HOME_DIR = unicode(buffer.value)
               
-   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, 'Bitcoin', SUBDIR)
+   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, COIN, SUBDIR)
+   if COIN == 'Namecoin':
+      if USE_NAMECOIN:
+         SUBDIR = 'namecoin'
+      elif USE_NAMECOIN_TESTNET:
+         SUBDIR = 'namecoin_testnet'
+      BLKFILE_DIR = BTC_HOME_DIR
+   else:
+      BLKFILE_DIR = os.path.join(BTC_HOME_DIR, 'blocks')
    ARMORY_HOME_DIR = os.path.join(USER_HOME_DIR, 'Armory', SUBDIR)
-   BLKFILE_DIR     = os.path.join(BTC_HOME_DIR, 'blocks')
-   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR, 'blk00000.dat')
+   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR,
+           'blk0001.dat' if COIN == 'Namecoin' else 'blk00000.dat')
 elif OS_LINUX:
    OS_NAME         = 'Linux'
    OS_VARIANT      = platform.linux_distribution()
    USER_HOME_DIR   = os.getenv('HOME')
-   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, '.bitcoin', SUBDIR)
+   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, '.' + COIN.lower(), SUBDIR)
+   if COIN == 'Namecoin':
+      if USE_NAMECOIN:
+         SUBDIR = 'namecoin'
+      elif USE_NAMECOIN_TESTNET:
+         SUBDIR = 'namecoin_testnet'
+      BLKFILE_DIR = BTC_HOME_DIR
+   else:
+      BLKFILE_DIR = os.path.join(BTC_HOME_DIR, 'blocks')
    ARMORY_HOME_DIR = os.path.join(USER_HOME_DIR, '.armory', SUBDIR)
-   BLKFILE_DIR     = os.path.join(BTC_HOME_DIR, 'blocks')
-   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR, 'blk00000.dat')
+   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR,
+           'blk0001.dat' if COIN == 'Namecoin' else 'blk00000.dat')
 elif OS_MACOSX:
    platform.mac_ver()
    OS_NAME         = 'MacOSX'
    OS_VARIANT      = platform.mac_ver()
    USER_HOME_DIR   = os.path.expanduser('~/Library/Application Support')
-   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, 'Bitcoin', SUBDIR)
+   BTC_HOME_DIR    = os.path.join(USER_HOME_DIR, COIN, SUBDIR)
+   if COIN == 'Namecoin':
+      if USE_NAMECOIN:
+         SUBDIR = 'namecoin'
+      elif USE_NAMECOIN_TESTNET:
+         SUBDIR = 'namecoin_testnet'
+      BLKFILE_DIR = BTC_HOME_DIR
+   else:
+      BLKFILE_DIR = os.path.join(BTC_HOME_DIR, 'blocks')
    ARMORY_HOME_DIR = os.path.join(USER_HOME_DIR, 'Armory', SUBDIR)
-   BLKFILE_DIR     = os.path.join(BTC_HOME_DIR, 'blocks')
-   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR, 'blk00000.dat')
+   BLKFILE_1stFILE = os.path.join(BLKFILE_DIR,
+           'blk0001.dat' if COIN == 'Namecoin' else 'blk00000.dat')
 else:
    print '***Unknown operating system!'
    print '***Cannot determine default directory locations'
@@ -302,6 +348,8 @@ BLOCKCHAINS = {}
 BLOCKCHAINS['\xf9\xbe\xb4\xd9'] = "Main Network"
 BLOCKCHAINS['\xfa\xbf\xb5\xda'] = "Old Test Network"
 BLOCKCHAINS['\x0b\x11\x09\x07'] = "Test Network (testnet3)"
+BLOCKCHAINS['\xf9\xbe\xb4\xfe'] = "Namecoin Network"
+BLOCKCHAINS['\xfa\xbf\xb5\xfe'] = "Namecoin Test Network"
 
 NETWORKS = {}
 NETWORKS['\x00'] = "Main Network"
@@ -354,6 +402,11 @@ if not CLI_OPTIONS.satoshiHome.lower()=='default':
       testnetTry = os.path.join(CLI_OPTIONS.satoshiHome, 'testnet3')
       if os.path.exists(testnetTry):
          CLI_OPTIONS.satoshiHome = testnetTry
+   if USE_NAMECOIN_TESTNET:
+      namecoinTestnetTry = os.path.join(CLI_OPTIONS.satoshiHome,
+            'testnet')
+      if os.path.exists(namecoinTestnetTry):
+         CLI_OPTIONS.satoshiHome = namecoinTestnetTry
 
    if not os.path.exists(CLI_OPTIONS.satoshiHome):
       print 'Directory "%s" does not exist!  Using default!' % \
@@ -432,7 +485,58 @@ if not os.path.exists(LEVELDB_DIR):
 
 
 ##### MAIN NETWORK IS DEFAULT #####
-if not USE_TESTNET:
+if USE_TESTNET:
+   BITCOIN_PORT = 18333
+   BITCOIN_RPC_PORT = 18332
+   ARMORY_RPC_PORT     = 18225
+   MAGIC_BYTES  = '\x0b\x11\x09\x07'
+   GENESIS_BLOCK_HASH_HEX  = '43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000'
+   GENESIS_BLOCK_HASH      = 'CI\x7f\xd7\xf8&\x95q\x08\xf4\xa3\x0f\xd9\xce\xc3\xae\xbay\x97 \x84\xe9\x0e\xad\x01\xea3\t\x00\x00\x00\x00'
+   GENESIS_TX_HASH_HEX     = '3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a'
+   GENESIS_TX_HASH         = ';\xa3\xed\xfdz{\x12\xb2z\xc7,>gv\x8fa\x7f\xc8\x1b\xc3\x88\x8aQ2:\x9f\xb8\xaaK\x1e^J'
+   ADDRBYTE = '\x6f'
+   P2SHBYTE = '\xc4'
+   PRIVKEYBYTE = '\xef'
+
+   #
+   BLOCKEXPLORE_NAME     = 'blockexplorer.com'
+   BLOCKEXPLORE_URL_TX   = 'http://blockexplorer.com/testnet/tx/%s'
+   BLOCKEXPLORE_URL_ADDR = 'http://blockexplorer.com/testnet/address/%s'
+elif USE_NAMECOIN:
+   BITCOIN_PORT = 8334
+   BITCOIN_RPC_PORT = 8336
+   ARMORY_RPC_PORT = 8228
+   MAGIC_BYTES = '\xf9\xbe\xb4\xfe'
+   GENESIS_BLOCK_HASH_HEX  = '70c7a9f0a2fb3d48e635a70d5b157c807e58c8fb45eb2c5e2cb7620000000000'
+   GENESIS_BLOCK_HASH      = 'p\xc7\xa9\xf0\xa2\xfb=H\xe65\xa7\r[\x15|\x80~X\xc8\xfbE\xeb,^,\xb7b\x00\x00\x00\x00\x00'
+   GENESIS_TX_HASH_HEX     = '0dcbd3e6f061215bf3b3383c8ce2ec201bc65acde32595449ac86890bd2dc641'
+   GENESIS_TX_HASH         = '\r\xcb\xd3\xe6\xf0a![\xf3\xb38<\x8c\xe2\xec \x1b\xc6Z\xcd\xe3%\x95D\x9a\xc8h\x90\xbd-\xc6A'
+   ADDRBYTE = '\x34'
+   P2SHBYTE = ''
+   PRIVKEYBYTE = '\xb4'
+
+   #
+   BLOCKEXPLORE_NAME     = 'https://bitinfocharts.com/namecoin/'
+   BLOCKEXPLORE_URL_TX   = 'https://bitinfocharts.com/namecoin/tx/%s'
+   BLOCKEXPLORE_URL_ADDR = 'https://bitinfocharts.com/namecoin/address/%s'
+elif USE_NAMECOIN_TESTNET:
+   BITCOIN_PORT = 18334
+   BITCOIN_RPC_PORT = 18336
+   ARMORY_RPC_PORT = 18228
+   MAGIC_BYTES = '\xfa\xbf\xb5\xfe'
+   GENESIS_BLOCK_HASH_HEX  = '08b067b31dc139ee8e7a76a4f2cfcca477c4c06e1ef89f4ae308951907000000'
+   GENESIS_BLOCK_HASH      = '\x08\xb0g\xb3\x1d\xc19\xee\x8ezv\xa4\xf2\xcf\xcc\xa4w\xc4\xc0n\x1e\xf8\x9fJ\xe3\x08\x95\x19\x07\x00\x00\x00'
+   GENESIS_TX_HASH_HEX     = '3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a'
+   GENESIS_TX_HASH         = ';\xa3\xed\xfdz{\x12\xb2z\xc7,>gv\x8fa\x7f\xc8\x1b\xc3\x88\x8aQ2:\x9f\xb8\xaaK\x1e^J'
+   ADDRBYTE = '\x6f'
+   P2SHBYTE = ''
+   PRIVKEYBYTE = '\xef'
+
+   #
+   BLOCKEXPLORE_NAME     = 'http://testnet.explorer.namecoin.info/'
+   BLOCKEXPLORE_URL_TX   = 'http://testnet.explorer.namecoin.info/tx/%s'
+   BLOCKEXPLORE_URL_ADDR = 'http://testnet.explorer.namecoin.info/a/%s'
+else:
    # TODO:  The testnet genesis tx hash can't be the same...?
    BITCOIN_PORT = 8333
    BITCOIN_RPC_PORT = 8332
@@ -450,23 +554,6 @@ if not USE_TESTNET:
    BLOCKEXPLORE_NAME     = 'blockchain.info'
    BLOCKEXPLORE_URL_TX   = 'https://blockchain.info/tx/%s'
    BLOCKEXPLORE_URL_ADDR = 'https://blockchain.info/address/%s'
-else:
-   BITCOIN_PORT = 18333
-   BITCOIN_RPC_PORT = 18332
-   ARMORY_RPC_PORT     = 18225
-   MAGIC_BYTES  = '\x0b\x11\x09\x07'
-   GENESIS_BLOCK_HASH_HEX  = '43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000'
-   GENESIS_BLOCK_HASH      = 'CI\x7f\xd7\xf8&\x95q\x08\xf4\xa3\x0f\xd9\xce\xc3\xae\xbay\x97 \x84\xe9\x0e\xad\x01\xea3\t\x00\x00\x00\x00'
-   GENESIS_TX_HASH_HEX     = '3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a'
-   GENESIS_TX_HASH         = ';\xa3\xed\xfdz{\x12\xb2z\xc7,>gv\x8fa\x7f\xc8\x1b\xc3\x88\x8aQ2:\x9f\xb8\xaaK\x1e^J'
-   ADDRBYTE = '\x6f'
-   P2SHBYTE = '\xc4'
-   PRIVKEYBYTE = '\xef'
-
-   #
-   BLOCKEXPLORE_NAME     = 'blockexplorer.com'
-   BLOCKEXPLORE_URL_TX   = 'http://blockexplorer.com/testnet/tx/%s'
-   BLOCKEXPLORE_URL_ADDR = 'http://blockexplorer.com/testnet/address/%s'
 
 # These are the same regardless of network
 # They are the way data is stored in the database which is network agnostic
@@ -3706,4 +3793,4 @@ def isInternetAvailable():
 def onlineModeIsPossible(btcdir=BTC_HOME_DIR):
    return (CLI_OPTIONS.forceOnline or isInternetAvailable()) and \
       satoshiIsAvailable() and \
-      os.path.exists(os.path.join(btcdir, 'blocks'))
+      (os.path.exists(os.path.join(btcdir, 'blocks')) if COIN == 'Bitcoin' else 1)
diff --git a/armoryengine/BDM.py b/armoryengine/BDM.py
index 01b13a0..4e9695a 100644
--- a/armoryengine/BDM.py
+++ b/armoryengine/BDM.py
@@ -294,16 +294,21 @@ class BlockDataManager(object):
 
       
       blkdir = ""
+      blk1st = ""
       
       if forInit == False:
       # Check for the existence of the Bitcoin-Qt directory         
          if not os.path.exists(self.btcdir):
             raise FileExistsError, ('Directory does not exist: %s' % self.btcdir)
   
-         blkdir = os.path.join(self.btcdir, 'blocks')
-         blk1st = os.path.join(blkdir, 'blk00000.dat')
+         if COIN == 'Namecoin':
+            blkdir = self.btcdir
+            blk1st = os.path.join(blkdir, 'blk0001.dat')
+         else:
+            blkdir = os.path.join(self.btcdir, 'blocks')
+            blk1st = os.path.join(blkdir, 'blk00000.dat')
   
-         # ... and its blk000X.dat files
+         # ... and its blk000X.dat or blk0000x.dat files
          if not os.path.exists(blk1st):
             LOGERROR('Blockchain data not available: %s', blk1st)
             raise FileExistsError, ('Blockchain data not available: %s' % blk1st)
@@ -327,6 +332,7 @@ class BlockDataManager(object):
       bdmConfig.homeDirLocation = armoryHomeDir
       bdmConfig.blkFileLocation = blockdir
       bdmConfig.levelDBLocation = leveldbdir
+      bdmConfig.chain = COIN
       bdmConfig.setGenesisBlockHash(GENESIS_BLOCK_HASH)
       bdmConfig.setGenesisTxHash(GENESIS_TX_HASH)
       bdmConfig.setMagicBytes(MAGIC_BYTES)
diff --git a/armoryengine/PyBtcWallet.py b/armoryengine/PyBtcWallet.py
index b27c9dc..79acda3 100644
--- a/armoryengine/PyBtcWallet.py
+++ b/armoryengine/PyBtcWallet.py
@@ -198,7 +198,7 @@ class PyBtcWallet(object):
       self.linearAddr160List = []
       self.chainIndexMap = {}
       self.txAddrMap = {}    # cache for getting tx-labels based on addr search
-      if USE_TESTNET:
+      if USE_TESTNET or USE_NAMECOIN_TESTNET:
          self.addrPoolSize = 10  # this makes debugging so much easier!
       else:
          self.addrPoolSize = CLI_OPTIONS.keypool
diff --git a/cppForSwig/BlockDataManagerConfig.h b/cppForSwig/BlockDataManagerConfig.h
index ff15423..5e27530 100644
--- a/cppForSwig/BlockDataManagerConfig.h
+++ b/cppForSwig/BlockDataManagerConfig.h
@@ -29,6 +29,7 @@ struct BlockDataManagerConfig
    string homeDirLocation;
    string blkFileLocation;
    string levelDBLocation;
+   string chain;
   
    BinaryData genesisBlockHash;
    BinaryData genesisTxHash;
diff --git a/cppForSwig/BlockObj.cpp b/cppForSwig/BlockObj.cpp
index 03e12a5..a1f4bd5 100644
--- a/cppForSwig/BlockObj.cpp
+++ b/cppForSwig/BlockObj.cpp
@@ -507,7 +507,8 @@ void TxOut::pprint(ostream & os, int nIndent, bool pBigendian)
 /////////////////////////////////////////////////////////////////////////////
 void Tx::unserialize(uint8_t const * ptr, size_t size)
 {
-   uint32_t nBytes = BtcUtils::TxCalcLength(ptr, size, &offsetsTxIn_, &offsetsTxOut_);
+   uint32_t nBytes = BtcUtils::TxCalcLength(ptr, size, getBlockHeight(), &offsetsTxIn_,
+          &offsetsTxOut_);
   
    if (nBytes > size)
       throw BlockDeserializingException();
diff --git a/cppForSwig/BlockUtils.cpp b/cppForSwig/BlockUtils.cpp
index 44c1bad..305c4b3 100644
--- a/cppForSwig/BlockUtils.cpp
+++ b/cppForSwig/BlockUtils.cpp
@@ -81,7 +81,8 @@ public:
       }
       while(numBlkFiles < UINT16_MAX)
       {
-         string path = BtcUtils::getBlkFilename(blkFileLocation_, numBlkFiles);
+         string chain = "Namecoin";//config().chain;
+         string path = BtcUtils::getBlkFilename(chain, blkFileLocation_, numBlkFiles);
          uint64_t filesize = BtcUtils::GetFileSize(path);
          if(filesize == FILE_DOES_NOT_EXIST)
             break;
diff --git a/cppForSwig/BtcUtils.h b/cppForSwig/BtcUtils.h
index 2f77baa..dab0591 100644
--- a/cppForSwig/BtcUtils.h
+++ b/cppForSwig/BtcUtils.h
@@ -67,6 +67,10 @@ class LedgerEntry;
 #define MAINNET_GENESIS_HASH_HEX    "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
 #define MAINNET_GENESIS_TX_HASH_HEX "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"
 
+#define NAMECOIN_MAGIC_BYTES "f9beb4fe"
+
+#define NAMECOIN_TESTNET_MAGIC_BYTES "fabfb5fe"
+
 #define BITMASK(X) (2**X - 1)
 
 #define HASH160PREFIX WRITE_UINT8_LE((uint8_t)SCRIPT_PREFIX_HASH160)
@@ -756,9 +760,16 @@ public:
    /////////////////////////////////////////////////////////////////////////////
    static size_t TxCalcLength(uint8_t const * ptr,
                                 size_t size,
+                                uint32_t nBlk,
                                 vector<size_t> * offsetsIn=NULL,
                                 vector<size_t> * offsetsOut=NULL)
    {
+      /*if(nBlk >= 19200) {
+         // Check for merge mined blocks and increment ptr???
+         // Problem is that nBlk isn't currently the block height.
+         // getBlockHeight() returns uint32_max!
+      }*/
+     
       BinaryRefReader brr(ptr, size); 
       
       if (brr.getSizeRemaining() < 4)
@@ -1342,13 +1353,18 @@ public:
 
    // This got more complicated when Bitcoin-Qt 0.8 switched from
    // blk0001.dat to blocks/blk00000.dat
-   static string getBlkFilename(string dir, uint32_t fblkNum)
+   static string getBlkFilename(string chain, string dir, uint32_t fblkNum)
    {
       /// Update:  It's been enough time since the hardfork that just about
       //           everyone must've upgraded to 0.8+ by now... remove pre-0.8
       //           compatibility.
       char* fname = new char[1024];
-      sprintf(fname, "%s/blk%05d.dat", dir.c_str(), fblkNum);
+      if(chain == "Namecoin") {
+         sprintf(fname, "%s/blk%04d.dat", dir.c_str(), fblkNum+1);
+      }
+      else {
+         sprintf(fname, "%s/blk%05d.dat", dir.c_str(), fblkNum);
+      }
       string strName(fname);
       delete[] fname;
       return strName;
diff --git a/cppForSwig/gtest/CppBlockUtilsTests.cpp b/cppForSwig/gtest/CppBlockUtilsTests.cpp
index 50be814..e4a76af 100644
--- a/cppForSwig/gtest/CppBlockUtilsTests.cpp
+++ b/cppForSwig/gtest/CppBlockUtilsTests.cpp
@@ -4749,6 +4749,7 @@ protected:
       config_.armoryDbType = ARMORY_DB_FULL;
       config_.pruneType = DB_PRUNE_NONE;
       config_.levelDBLocation = string("ldbtestdir");
+      config_.chain = "Bitcoin"
 
       config_.genesisBlockHash = ghash_;
       config_.genesisTxHash = gentx_;
@@ -6486,7 +6487,7 @@ protected:
       mkdir(homedir_);
 
       // Put the first 5 blocks into the blkdir
-      blk0dat_ = BtcUtils::getBlkFilename(blkdir_, 0);
+      blk0dat_ = BtcUtils::getBlkFilename(config_.chain, blkdir_, 0);
       BtcUtils::copyFile("../reorgTest/blk_0_to_4.dat", blk0dat_);
 
       config.armoryDbType = ARMORY_DB_BARE;
@@ -6613,7 +6614,7 @@ protected:
       mkdir(blkdir_);
       mkdir(homedir_);
       
-      blk0dat_ = BtcUtils::getBlkFilename(blkdir_, 0);
+      blk0dat_ = BtcUtils::getBlkFilename(config_.chain, blkdir_, 0);
    }
   
    /////////////////////////////////////////////////////////////////////////////
@@ -8022,7 +8023,7 @@ protected:
       mkdir(homedir_);
 
       // Put the first 5 blocks into the blkdir
-      blk0dat_ = BtcUtils::getBlkFilename(blkdir_, 0);
+      blk0dat_ = BtcUtils::getBlkFilename(config_.chain, blkdir_, 0);
       BtcUtils::copyFile("../reorgTest/blk_0_to_4.dat", blk0dat_);
 
       BlockDataManagerConfig config;
@@ -8168,14 +8169,14 @@ TEST_F(BlockUtilsSuper, HeadersOnly_Reorg)
    EXPECT_EQ(iface_->getTopBlockHeight(HEADERS), 4);
    EXPECT_EQ(iface_->getTopBlockHash(HEADERS), blkHash4);
 
-   BtcUtils::copyFile("../reorgTest/blk_3A.dat", BtcUtils::getBlkFilename(blkdir_, 1));
+   BtcUtils::copyFile("../reorgTest/blk_3A.dat", BtcUtils::getBlkFilename(config_.chain, blkdir_, 1));
    TheBDM.readBlkFileUpdate();
    EXPECT_EQ(iface_->getTopBlockHeight(HEADERS), 4);
    EXPECT_EQ(iface_->getTopBlockHash(HEADERS), blkHash4);
    EXPECT_FALSE(TheBDM.blockchain().getHeaderByHash(blkHash3A).isMainBranch());
    EXPECT_TRUE( TheBDM.blockchain().getHeaderByHash(blkHash3 ).isMainBranch());
 
-   BtcUtils::copyFile("../reorgTest/blk_4A.dat", BtcUtils::getBlkFilename(blkdir_, 2));
+   BtcUtils::copyFile("../reorgTest/blk_4A.dat", BtcUtils::getBlkFilename(config_.chain, blkdir_, 2));
    TheBDM.readBlkFileUpdate();
    EXPECT_EQ(iface_->getTopBlockHeight(HEADERS), 4);
    EXPECT_EQ(iface_->getTopBlockHash(HEADERS), blkHash4);
@@ -8184,7 +8185,7 @@ TEST_F(BlockUtilsSuper, HeadersOnly_Reorg)
    EXPECT_FALSE(TheBDM.blockchain().getHeaderByHash(blkHash4A).isMainBranch());
    EXPECT_TRUE( TheBDM.blockchain().getHeaderByHash(blkHash4 ).isMainBranch());
 
-   BtcUtils::copyFile("../reorgTest/blk_5A.dat", BtcUtils::getBlkFilename(blkdir_, 3));
+   BtcUtils::copyFile("../reorgTest/blk_5A.dat", BtcUtils::getBlkFilename(config_.chain, blkdir_, 3));
    TheBDM.readBlkFileUpdate();
    EXPECT_EQ(iface_->getTopBlockHeight(HEADERS), 5);
    EXPECT_EQ(iface_->getTopBlockHeight(HEADERS), 5);
@@ -8467,7 +8468,7 @@ protected:
       mkdir(homedir_);
 
       // Put the first 5 blocks into the blkdir
-      blk0dat_ = BtcUtils::getBlkFilename(blkdir_, 0);
+      blk0dat_ = BtcUtils::getBlkFilename(config_.chain, blkdir_, 0);
       BtcUtils::copyFile("../reorgTest/blk_0_to_4.dat", blk0dat_);
       
       BlockDataManagerConfig config;
diff --git a/qtdefines.py b/qtdefines.py
index aff2f1f..9a15fe2 100644
--- a/qtdefines.py
+++ b/qtdefines.py
@@ -746,6 +746,12 @@ class ArmoryDialog(QDialog):
       if USE_TESTNET:
          self.setWindowTitle('Armory - Bitcoin Wallet Management [TESTNET]')
          self.setWindowIcon(QIcon(':/armory_icon_green_32x32.png'))
+      elif USE_NAMECOIN:
+         self.setWindowTitle('Armory - Namecoin Wallet Management')
+         self.setWindowIcon(QIcon(':/armory_icon_32x32.png'))
+      elif USE_NAMECOIN_TESTNET:
+         self.setWindowTitle('Armory - Namecoin Wallet Management [TESTNET]')
+         self.setWindowIcon(QIcon(':/armory_icon_green_32x32.png'))
       else:
          self.setWindowTitle('Armory - Bitcoin Wallet Management')
          self.setWindowIcon(QIcon(':/armory_icon_32x32.png'))
diff --git a/qtdialogs.py b/qtdialogs.py
index 82ad491..97dfe84 100644
--- a/qtdialogs.py
+++ b/qtdialogs.py
@@ -7353,7 +7353,7 @@ class DlgPrintBackup(ArmoryDialog):
 
       doMask = self.chkSecurePrint.isChecked()
 
-      if USE_TESTNET:
+      if USE_TESTNET or USE_NAMECOIN_TESTNET:
          self.scene.drawPixmapFile(':/armory_logo_green_h56.png')
       else:
          self.scene.drawPixmapFile(':/armory_logo_h36.png')
diff --git a/ui/MultiSigDialogs.py b/ui/MultiSigDialogs.py
index e180c43..c74dc06 100644
--- a/ui/MultiSigDialogs.py
+++ b/ui/MultiSigDialogs.py
@@ -1346,6 +1346,13 @@ class DlgLockboxManager(ArmoryDialog):
       if USE_TESTNET:
          blkExploreTitle = 'View on blockexplorer.com'
          blkExploreURL   = 'http://blockexplorer.com/testnet/tx/%s' % txHash
+      elif USE_NAMECOIN:
+         blkExploreTitle = 'View on bitinfocharts.com'
+         blkExploreURL   = 'https://bitinfocharts.com/namecoin/tx/%s' % txHash
+      elif USE_NAMECOIN_TESTNET:
+         # TODO: Find an explorer that is up-to-date
+         blkExploreTitle = 'View on namecoin.info'
+         blkExploreURL   = 'http://testnet.explorer.namecoin.info/tx/%s' % txHash
       else:
          blkExploreTitle = 'View on blockchain.info'
          blkExploreURL   = 'https://blockchain.info/tx/%s' % txHash
diff --git a/ui/TxFrames.py b/ui/TxFrames.py
index caf4192..7608b30 100644
--- a/ui/TxFrames.py
+++ b/ui/TxFrames.py
@@ -153,7 +153,7 @@ class SendBitcoinsFrame(ArmoryFrame):
          Click this button to copy a "bitcoin:" link directly into Armory."""))
       self.connect(btnEnterURI, SIGNAL("clicked()"), self.clickEnterURI)
       fromFrameList = [self.frmSelectedWlt]
-      if not USE_TESTNET:
+      if not(USE_TESTNET or USE_NAMECOIN or USE_NAMECOIN_TESTNET):
          btnDonate = QPushButton("Donate to Armory Developers!")
          ttipDonate = self.main.createToolTipWidget(\
             'Making this software was a lot of work.  You can give back '
@@ -262,7 +262,9 @@ class SendBitcoinsFrame(ArmoryFrame):
            loadCount % donateFreq == (donateFreq-1) and \
            not loadCount == lastPestering and \
            not dnaaDonate and \
-           not USE_TESTNET:
+           not USE_TESTNET and \
+           not USE_NAMECOIN and \
+           not USE_NAMECOIN_TESTNET:
          result = MsgBoxWithDNAA(MSGBOX.Question, 'Please donate!', tr("""
             <i>Armory</i> is the result of thousands of hours of development
             by very talented coders.  Yet, this software
diff --git a/ui/Wizards.py b/ui/Wizards.py
index e37e57d..965e083 100644
--- a/ui/Wizards.py
+++ b/ui/Wizards.py
@@ -8,7 +8,7 @@
 
 from PyQt4.Qt import * #@UnusedWildImport
 from PyQt4.QtGui import * #@UnusedWildImport
-from armoryengine.ArmoryUtils import USE_TESTNET
+from armoryengine.ArmoryUtils import USE_TESTNET, USE_NAMECOIN, USE_NAMECOIN_TESTNET
 from ui.WalletFrames import NewWalletFrame, SetPassphraseFrame, VerifyPassphraseFrame,\
    WalletBackupFrame, WizardCreateWatchingOnlyWalletFrame
 from ui.TxFrames import SendBitcoinsFrame, SignBroadcastOfflineTxFrame,\
@@ -35,6 +35,12 @@ class ArmoryWizard(QWizard):
       if USE_TESTNET:
          self.setWindowTitle('Armory - Bitcoin Wallet Management [TESTNET]')
          self.setWindowIcon(QIcon(':/armory_icon_green_32x32.png'))
+      elif USE_NAMECOIN:
+         self.setWindowTitle('Armory - Namecoin Wallet Management')
+         self.setWindowIcon(QIcon(':/armory_icon_32x32.png'))
+      elif USE_NAMECOIN_TESTNET:
+         self.setWindowTitle('Armory - Namecoin Wallet Management [TESTNET]')
+         self.setWindowIcon(QIcon(':/armory_icon_green_32x32.png'))
       else:
          self.setWindowTitle('Armory - Bitcoin Wallet Management')
          self.setWindowIcon(QIcon(':/armory_icon_32x32.png'))

Let me know if you want it as a file uploaded somewhere.

Some notes:

The fee calculation logic may need to be changed for Namecoin. I did change the MIN_TX_FEE and MIN_RELAY_TX_FEE, but not the fee calculation logic.

P2SH and multisig will need to be disabled until the new version of Namecoin that is rebased against Bitcoin Core is released.

BlockDataManagerConfig.h has a string chain, which is set in the Python code to either 'Bitcoin' or 'Namecoin' depending on which is being used. The variable chain isn't actually being used right now, because I couldn't figure out how to use it. I have 'Namecoin' hardcoded in BlockUtils.cpp (and that part is working correctly to read the correct block files). If you want to switch between Namecoin and Bitcoin, you need to recompile.

I think TxCalcLength needs to be modified in BtcUtils.h, so that Armory knows that the transaction part of the block starts after the merged mining data. A couple of posts ago I posted helpful information I found on the wiki about merged mining. The difficult part is that the merge mined data is not a constant size. And a block may not necessarily be merge mined even though it is at height 19200 or greater.

Namecoin currently has the donate to ATI button hidden, because I don't know of a Namecoin donation address.

I tested this, but at this stage all I could test was that the correct addresses were being generated and that the correct block files were detected.

And finally, I did not change all the instances of the word Bitcoin to Namecoin, so that still needs to be done. I figured it would just make the diff harder to read at this point.
josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 14, 2014, 09:37:28 PM
 #18

I said I gave up, but I continued playing around with block parsing (except I did so in Python, because I am more comfortable with that). I noticed that merge mined blocks have a different version than non merge mined blocks. That was the key I was missing. You need to check the 0x100 bit to see if a block has merge mined data or not.

Going back to the Armory code, I created a class that unserializes the merge mined data (no need to be able to serialize it, since we don't care about storing the actual data). I mirrored the structure of the BlockHeader class. I'm a little lost on where to call the unserialize method. I tried putting it in the blockHeaderCallback after unserializing the block header, but before nTx. The problem with that is that only 90 bytes are provided to blockHeaderCallback. The 90 bytes consist of 80 for the block header and 10 for the nTx, which is a var_int. Obviously trying to unserialize the merge mined data results in an attempt to go past the end of the 90 bytes, which results in an error.

I tried looking for some place earlier in the execution where we have the entire block's data, so that I could skip the merge mined data there, but couldn't find it.

I don't think I want to modify the BlockHeader class, because the merge mined data is not actually part of the block header. It comes right after the block header, but before the number of transactions.
goatpig
Moderator
Legendary
*
Offline Offline

Activity: 3752
Merit: 1364

Armory Developer


View Profile
November 15, 2014, 12:00:37 PM
 #19

blockHeaderCallback only cares for headers, it doesnt unserialize the blocks' content. It needs to know the end offset of each block, but I'm guessing block size in header is correct so any amount of extra data is irrelevant as long as the first 80 bytes are aligned like in Bitcoin.

In StoredHeader::unserializeFullBlock, this is where the magic happens:

Quote
  vector<BinaryData> allTxHashes;
   BlockHeader bh(brr);
   uint32_t nTx = (uint32_t)brr.get_var_int();

   createFromBlockHeader(bh);
   numTx_ = nTx;
  
   numBytes_ = HEADER_SIZE + BtcUtils::calcVarIntSize(numTx_);
   if(dataCopy_.getSize() != HEADER_SIZE)
   {
      LOGERR << "Unserializing header did not produce 80-byte object!";
      return;
   }
  
   if (numBytes_ > brr.getSize())
   {
      LOGERR << "Anticipated size of block header is more than what we have";
      throw BlockDeserializingException();
   }

   BtcUtils::getHash256(dataCopy_, thisHash_);

   for(uint32_t tx=0; tx<nTx; tx++)
   {
      //unserialize tx here
   }



1) Based on block version, either ignore the following block or define HEADER_SIZE_NMC and check against that:

Quote
  if(dataCopy_.getSize() != HEADER_SIZE)
   {
      LOGERR << "Unserializing header did not produce 80-byte object!";
      return;
   }

2) Push brr forward by the amount of unnecessary bytes in between the header and the block data. It should look something like this:

Quote
  //Somewhere in the header
   #define HEADER_VERSION_NMC                someval        //header version number for NMC
   #define NMCHEADER_NUMBYTESTOIGNORE someotherval //amount of bytes to skip after the header to get to txdata

   ///////
   //back to unserializeFullBlock

   BtcUtils::getHash256(dataCopy_, thisHash_);

   if(bh.getVersion() == HEADER_VERSION_NMC)
      brr.advance(NMC_HEADER_NUMBYTESTOIGNORE);
  
   for(uint32_t tx=0; tx<nTx; tx++)
   {
      //unresialize tx here
   }

This will get you through unserializing and adding blocks to DB, I suppose you'll run into other snafus, but I can't tell you were on top of my head. The best thing you could do is create a separate C++ unit test that inits the BDM with a NMC block, with all the necessary comments to identify the parts of the code that need changed. You could also consider overloading these calls in a dedicated version of the class.

josephbisch (OP)
Member
**
Offline Offline

Activity: 75
Merit: 10


View Profile
November 15, 2014, 05:35:31 PM
 #20

Thanks for the help! I am able to receive and send testnet Namecoins with Armory! I haven't tried testing it with mainnet yet.

I did run into a small bug where, after sending a transaction, the pop-up in the taskbar and the transactions tab on the main screen say I only sent 0.005, when I really sent 1 plus a 0.005 fee. I am on the dev branch, but haven't brought it up to date for a few days. So maybe it is a known bug that is already fixed. I'll do more testing to determine whether this is Namecoin specific or not and see if it is fixed with an up-to-date dev branch. Once there is a confirmation the transactions tab correctly lists the amount as 1.005. Also, when double clicking on the transaction, the window that pops up displays the correct amount, even if there is not yet a confirmation.

I need to disable P2SH and multisig for Namecoin for now. I might need to tweak the fee calculation code too. Then I'll try to write that unit test.
Pages: [1] 2 3 »  All
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!