Author

Topic: General questions arising when adding Namecoin support to Armory (Read 5660 times)

member
Activity: 75
Merit: 10
Wow, this thread is a blast from the past!

It looks like the IETF spec currently has ACH, Bitcoin, and testnet Bitcoin. But it is general, so that Namecoin could be added in the future.
member
Activity: 80
Merit: 12
Looks like armory want to offer signature verification with a new field on the DNS records, i wonder if there's any way to make this compatible with NMC.
http://www.reddit.com/r/Bitcoin/comments/30m1p6/verisign_partners_with_armory_to_propose_a_new/
legendary
Activity: 3766
Merit: 1364
Armory Developer
I've taken a break from working on this, but I have a question to ask on behalf of the Namecoin devs.

Would Armory devs merge Namecoin specific transaction types into Armory if someone were to code it? This could be used in conjunction with an Armory plugin to automatically renew domain names to prevent expiration.

I don't think we have any plans to commit non Bitcoin code to our official repo. However, I can make key methods virtual (or create a fully virtual parent)s so someone implementing NMC or other coins can derive our transaction and block classes for their own purposes easily.
member
Activity: 75
Merit: 10
I've taken a break from working on this, but I have a question to ask on behalf of the Namecoin devs.

Would Armory devs merge Namecoin specific transaction types into Armory if someone were to code it? This could be used in conjunction with an Armory plugin to automatically renew domain names to prevent expiration.
newbie
Activity: 16
Merit: 0
The build was easy if you followed this guide https://bitcoinarmory.com/building-from-source/

At first I did not, and a lot of error told me to install this and that :-)

I will try to look for things to improve, good work!
legendary
Activity: 3766
Merit: 1364
Armory Developer
As a certain someone said, I'm up to my eyeballs with work currently, so I can't spend time debugging your changes. The issue is most likely occurring when the blocks are committed to the DB. BlockWriteBatcher reads commited blocks from DB and builds up the transaction history. It doesn't modify block data so there is no reason properly committed blocks would choke at that stage.

You should try to apply a block to the DB using BlockDataManager_LevelDB::addRawBlockToDB, then pulling it out with LMDBBlockDatabase::getStoredHeader. That should help you isolate the deserialization issue.
member
Activity: 75
Merit: 10
I just pushed a bunch of code. I still don't know what is wrong with the test, but I think I have all the actual code written (for ArmoryQt, I haven't really looked at armoryd yet). So I think it is okay to start reviewing the code. Though I still haven't heard from anyone else that tested the code besides myself. So there could be some obvious bugs that I am overlooking.
member
Activity: 75
Merit: 10
I saw that code. The problem is I don't modify HEADER_SIZE (because the actual 80 byte header is the same for Namecoin). Which is why I find all of this quite odd.
legendary
Activity: 3766
Merit: 1364
Armory Developer
I traced the issue to BlockWriteBatcher::applyBlocksToDB with gdb. I should have looked in the test log file initially, because there were errors there relating to BlockWriteBatcher. Specifically there seems to be a problem with the size of the headers.

This is what I expect is happening (as a peudo traceback):

Code:
BlockWriteBatcher::applyBlocksToDB
   BlockWriteBatcher::grabBlocksFromDB
      dis->iface_->getStoredHeader
         ... bunch of calls that end up with block deserialization ...
            StoredHeader::unserializeDBValue
               
This method eventually gets to this:

Code:
      // Unserialize the raw header into the SBH object
      brr.get_BinaryData(dataCopy_, HEADER_SIZE);
      BtcUtils::getHash256(dataCopy_, thisHash_);
      numTx_    = brr.get_uint32_t();
      numBytes_ = brr.get_uint32_t();

If you modified HEADER_SIZE, it's going to choke on non NMC blocks.
member
Activity: 75
Merit: 10
I traced the issue to BlockWriteBatcher::applyBlocksToDB with gdb. I should have looked in the test log file initially, because there were errors there relating to BlockWriteBatcher. Specifically there seems to be a problem with the size of the headers. Which I don't understand, because the output of createTestChain.py seems to indicate that the headers are 80 bytes as expected.

Code:
Log file opened at 1416341503: namecoinTestsLog.txt
-INFO  - 1416341503: (BlockUtils.cpp:626) Set blkfile dir: ./blkfiletest
-INFO  - 1416341503: (BlockUtils.cpp:627) Set leveldb dir: ./ldbtestdir
-INFO  - 1416341503: (lmdb_wrapper.cpp:370) Opening databases...
-INFO  - 1416341503: (BlockDataViewer.cpp:37) Registering Wallet wallet1
-INFO  - 1416341513: (BlockUtils.cpp:948) Executing: doInitialSyncOnLoad
-INFO  - 1416341516: (BlockUtils.cpp:1020) Total number of blk*.dat files: 1
-INFO  - 1416341517: (BlockUtils.cpp:1021) Total blockchain bytes: 5,432
-INFO  - 1416341519: (BlockUtils.cpp:1365) Reading headers from db
-INFO  - 1416341519: (BlockUtils.cpp:1378) Found 7 headers in db
-DEBUG - 1416341520: (Blockchain.cpp:165) Organizing chain w/ rebuild
-WARN  - 1416341522: (BlockUtils.cpp:1059) --- Fetching SSH summaries for 6 registered addresses
-INFO  - 1416341524: (BlockUtils.cpp:1072) Left off at file 0, offset 4729
-INFO  - 1416341526: (BlockUtils.cpp:1075) Reading headers and building chain...
-INFO  - 1416341526: (BlockUtils.cpp:1076) Starting at block file 0 offset 4729
-INFO  - 1416341527: (BlockUtils.cpp:1078) Block height 0
-WARN  - 1416341529: (Blockchain.cpp:37) Somehow tried to add header that's already in map
-WARN  - 1416341529: (Blockchain.cpp:38)     Header Hash: 1964e08c87932373561aec082a68b80abf62f57e5a02c4d521ebbd839cf60de8
-DEBUG - 1416341530: (Blockchain.cpp:165) Organizing chain w/ rebuild
-ERROR - 1416341531: (StoredBlockObj.cpp:221) Asked to unserialize a non-80-byte header
-ERROR - 1416341531: (lmdb_wrapper.cpp:1353) Attempting to put uninitialized bare header into DB
-INFO  - 1416341535: (BlockUtils.cpp:1251) Loading block data... file 0 offset 4729
-INFO  - 1416341535: (BlockUtils.cpp:331) Reading raw blocks finished at file 0 offset 5432
-WARN  - 1416341763: (BlockUtils.cpp:810) Scanning from 0 to 0
-ERROR - 1416342155: (lmdb_wrapper.cpp:1199) Block height exceeds DupID lookup table
-ERROR - 1416342155: (BlockWriteBatcher.cpp:1281) No block in DB at height 0
-ERROR - 1416342156: (lmdb_wrapper.cpp:1199) Block height exceeds DupID lookup table
-ERROR - 1416342156: (BlockWriteBatcher.cpp:1281) No block in DB at height 0

Edit: I put the block file through my Python parser and the data being output looks okay to me. These blocks don't even have the extra Namecoin data yet. And the weirdest thing is that this issue only presents itself when running the tests.
legendary
Activity: 3766
Merit: 1364
Armory Developer
I copied that test over to my NamecoinTests.cpp file (removing the lockbox portion, since NMC doesn't support that currently). I also copied the necessary functions to run that test. When running the test, I get output indicating that it is running, but it appears to be stuck running that test.

I used createTestChain.py, modifying it to load the Namecoin genesis block instead of the Bitcoin one, setting fakeblock to True, and using the Namecoin magic bytes.

The line "TheBDM.doInitialSyncOnLoad(nullProgress);" seems to be the one that is hanging. Does fakeblock need to be False when running createTestChain.py for this test?

It can be either. We set it to false to skip difficulty (use 0 for each block's nonce) in order to create test chains a lot faster.

The line hanging is the the one initializing the backend. You should set a few breakpoints there and see where it is choking. 
member
Activity: 75
Merit: 10
I copied that test over to my NamecoinTests.cpp file (removing the lockbox portion, since NMC doesn't support that currently). I also copied the necessary functions to run that test. When running the test, I get output indicating that it is running, but it appears to be stuck running that test.

I used createTestChain.py, modifying it to load the Namecoin genesis block instead of the Bitcoin one, setting fakeblock to True, and using the Namecoin magic bytes.

The line "TheBDM.doInitialSyncOnLoad(nullProgress);" seems to be the one that is hanging. Does fakeblock need to be False when running createTestChain.py for this test?
member
Activity: 75
Merit: 10
I pushed a bunch more changes, including more text changes and support for "namecoin:" URIs. Also I disabled the torrent for Namecoin.

Now I think the only things left are the unit test, support for Namecoin in the built-in downloader, and support for Namecoin in armoryd.
legendary
Activity: 3766
Merit: 1364
Armory Developer
member
Activity: 75
Merit: 10
Is there an example test that inits the BDM with a Bitcoin block (so that I can adapt it to Namecoin)? Because I wrote a test that calls unserializeFullBlock and then checks a lot of the data in the block, but I don't think that is what you had in mind, because it has nothing to do with the BDM.

You can see what tests I have so far.
member
Activity: 75
Merit: 10
I already forked the repo (available here fyi), so I'll add a new file to it for the unit test under cppForSwig/gtest/.

I'm not sure what issue you are running into, but I just tried Namecoin mainnet and I am stuck synchronizing at 22%. It has been at most a few days since I last synced Namecoin mainnet, so it shouldn't be as low as 22%. I've been mostly testing Namecoin testnet anyway, so if it is your issue, then it isn't a big deal to me. Namecoin mainnet is just under a couple of gigs, fyi.

I think I am going to make a separate thread for testing Namecoin integration. I'm not getting anyone to test it and this thread's poor choice of title probably has to do something with that. I'll continue posting my questions/comments about development here.
legendary
Activity: 3766
Merit: 1364
Armory Developer
About that unit test for NMC, should I put it in CppBlockUtilsTests.cpp or make a separate file for it? I think I'll try to work on it tomorrow.

I think the unit test, text changes (replacing Bitcoin with Namecoin), and making it so Namecoin is not hardcoded in the C++ are the only things stopping this from being ready to be reviewed by Armory devs. But then again I haven't received any indication that anyone else besides me has been testing this code, so it is possible there are bugs I haven't discovered. I'm unsure about the fee calculation code, though in my tests it appeared to work correctly.

I might have to hand the code off with Namecoin hardcoded in the C++, because I am really lost on how to access the config data. I should be ready sometime this week for the code to be reviewed!

You should obviously fork the repo and keep your NMC unit test in a dedicated file to cover your changes so it doesn't get modified after merging in changes on our end. That way you have a static unit test to throw at our changes to make sure your modifications remain consistent pull to pull.

dev should be back to a functional state since last Sunday afternoon. It may go under for a while again though. Depending on how large NMC mainnet is you may run in the issue I'm currently working on, so let me know and I'll get you to bypass it for now.

Edit: Starting namecoind myself helped with the issues. I'm amazed at the speed of rebuilding and rescanning the db with the recent updates. It happened almost instantaneously (granted I was on Namecoin testnet, so there was a lot less data than Bitcoin mainnet, but still).

As CircusPeanut said, testnet has never worked with auto managed bitcoind. There's some snafu with the path resolving on the Python side I think, but this is such low priority we have left this issue unattended for over a year now.

As for the speed, I got rid of some scan concurrency recently, so that got a bit slower but in a few commits I'll crank the speed up again. For reference I get Bitcoin testnet supernode scanned in less than 3 minutes.
member
Activity: 75
Merit: 10
I pushed an update that replaces Bitcoin/bitcoins/BTC with a function (getCoinText()) that returns the correct name of coin in the correct format. I should have replaced most instances with the function except for a few cases. Those are:

References to Casascius coins. There are no NMC Casascius coins, so I guess I will just get rid of that text when NMC is activated. Or I could leave it as an example of the type of keys it is referring to.

The torrent download (like in the settings panel). I don't think there is a torrent for NMC, so I will completely disable that.

The "bitcoin:" links (I realized I still haven't changed that for NMC (namecoin:), so I still need to do that).

Let me know if you discover another location where you don't see NMC or Namecoin or namecoins when you are testing with Namecoin enabled.
member
Activity: 75
Merit: 10
About that unit test for NMC, should I put it in CppBlockUtilsTests.cpp or make a separate file for it? I think I'll try to work on it tomorrow.

I think the unit test, text changes (replacing Bitcoin with Namecoin), and making it so Namecoin is not hardcoded in the C++ are the only things stopping this from being ready to be reviewed by Armory devs. But then again I haven't received any indication that anyone else besides me has been testing this code, so it is possible there are bugs I haven't discovered. I'm unsure about the fee calculation code, though in my tests it appeared to work correctly.

I might have to hand the code off with Namecoin hardcoded in the C++, because I am really lost on how to access the config data. I should be ready sometime this week for the code to be reviewed!
member
Activity: 75
Merit: 10
If anyone reading this wants to help speed up the process of getting Namecoin support into Armory, they can help by testing Namecoin in Armory. If you do, make sure you only use testnet Namecoins, because this hasn't been thoroughly tested. To do so:

1) Git clone this repository
2) git checkout namecoin
3) make
4) python ArmoryQt.py --namecoin-testnet

Those instructions work for me on Linux. They may vary for other OS.

You might need to go to options, uncheck "let Armory run bitcoind in the background", and then manually run namecoind before starting Armory (run namecoind with the -testnet flag). I needed to do so.

If you do test Namecoin integration, please let me know, even if you don't experience any errors.

One thing to note is that I haven't changed the text, so Armory will still say Bitcoin and BTC even though you provided the --namecoin-testnet flag to it. But if you look at the addresses being generated, you will see that they are testnet addresses.
member
Activity: 75
Merit: 10
Was this line of this commit supposed to be removed? Because I am getting "AttributeError: 'ArmoryMainWindow' object has no attribute 'lastSDMState'" when trying to start ArmoryQt.py with an up-to-date dev branch. I'm sure this is probably already known, or some error on my part. Adding the line back allows ArmoryQt.py to launch, and it appears to work.

Edit: Nope, ArmoryQt.py does launch, but it gets stuck at the block syncing stage. It won't proceed to create the Armory db.

Thanks for the heads up.

I put that line back so it won't crash there.

There still seem to be issues with running BitcoinD from Armory on Testnet. I usually uncheck "Let Armory start Bitcoin..." in the Settings menu, and start BitcoinD myself when I'm working with Testnet.

Thanks, I tried putting the line back myself and had issues with both Namecoin and Bitcoin as my edit said, so I will try it with starting namecoind and bitcoind myself.

Edit: Starting namecoind myself helped with the issues. I'm amazed at the speed of rebuilding and rescanning the db with the recent updates. It happened almost instantaneously (granted I was on Namecoin testnet, so there was a lot less data than Bitcoin mainnet, but still).
full member
Activity: 123
Merit: 100
Was this line of this commit supposed to be removed? Because I am getting "AttributeError: 'ArmoryMainWindow' object has no attribute 'lastSDMState'" when trying to start ArmoryQt.py with an up-to-date dev branch. I'm sure this is probably already known, or some error on my part. Adding the line back allows ArmoryQt.py to launch, and it appears to work.

Edit: Nope, ArmoryQt.py does launch, but it gets stuck at the block syncing stage. It won't proceed to create the Armory db.

Thanks for the heads up.

I put that line back so it won't crash there.

There still seem to be issues with running BitcoinD from Armory on Testnet. I usually uncheck "Let Armory start Bitcoin..." in the Settings menu, and start BitcoinD myself when I'm working with Testnet.
member
Activity: 75
Merit: 10
Thanks, it's good to know that the dev branch is in a broken state and it's not just me.

I pushed what I have so far to my GitHub fork.

I think I disabled multisig for Namecoin, but I haven't tested it due to the state of dev. Looking through the code, it appears that leaving P2SHBYTE as an empty string is enough to disable P2SH for Namecoin (because when doing comparisons, an empty string is never going to == a byte), so I think I have disabled that too.

I looked at the fee calculation code and there are three functions for calculating fees. It looks like one of the more complex parts of the Namecoin integration. I guess I can leave the multisig calculation one alone for now, since Namecoin doesn't currently support multisig. That leaves calcMinSuggestedFees and calcMinSuggestedFeesNew and it looks like I need to modify both for Namecoin.

I'll watch the dev branch to see when ArmoryQt.py is runnable again. In the meantime I'll finish up the Namecoin support including that unit test you asked for.
legendary
Activity: 3766
Merit: 1364
Armory Developer
the ZC value issue was fixed, but the dev branch is in shambles currently.
member
Activity: 75
Merit: 10
Was this line of this commit supposed to be removed? Because I am getting "AttributeError: 'ArmoryMainWindow' object has no attribute 'lastSDMState'" when trying to start ArmoryQt.py with an up-to-date dev branch. I'm sure this is probably already known, or some error on my part. Adding the line back allows ArmoryQt.py to launch, and it appears to work.

Edit: Nope, ArmoryQt.py does launch, but it gets stuck at the block syncing stage. It won't proceed to create the Armory db.
member
Activity: 75
Merit: 10
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.

If you sent it to yourself (i.e. another address in the same wallet) then the list on the transaction tab will only show the fee, since that is the net amount leaving the account.  The symbol next to the transaction will be different - there is one for outgoing, one for incoming, and one for send to self.


I sent it from an address in Armory to an address in Namecoin-qt.
hero member
Activity: 547
Merit: 500
Decor in numeris
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.

If you sent it to yourself (i.e. another address in the same wallet) then the list on the transaction tab will only show the fee, since that is the net amount leaving the account.  The symbol next to the transaction will be different - there is one for outgoing, one for incoming, and one for send to self.
member
Activity: 75
Merit: 10
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.
legendary
Activity: 3766
Merit: 1364
Armory Developer
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 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   {
      //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   {
      //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.
member
Activity: 75
Merit: 10
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.
member
Activity: 75
Merit: 10
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
                unsigned 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 * offsetsIn=NULL,
                                 vector * 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("""
             Armory 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.
member
Activity: 75
Merit: 10
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.
member
Activity: 75
Merit: 10
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!
newbie
Activity: 29
Merit: 0
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.
member
Activity: 75
Merit: 10
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.
newbie
Activity: 29
Merit: 0
It's hexdump that is exchanging the order of each pair of bytes. Give it the option -C
member
Activity: 75
Merit: 10
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.
legendary
Activity: 1428
Merit: 1093
Core Armory Developer
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? 
newbie
Activity: 29
Merit: 0
Open up a hex editor on that file, go to that byte offset, and see if there's anything unusual.
member
Activity: 75
Merit: 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.
newbie
Activity: 29
Merit: 0
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.
member
Activity: 75
Merit: 10
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.
newbie
Activity: 29
Merit: 0
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.
member
Activity: 75
Merit: 10
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().
legendary
Activity: 1428
Merit: 1093
Core Armory Developer
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.

newbie
Activity: 29
Merit: 0
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.
member
Activity: 75
Merit: 10
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?
Jump to: