$100 to help figure out DASH transaction ids
Posted: Sun Feb 19, 2017 12:50 pm
Updated: this issue has been resolved, and a description of the resolution is below. The reward is no longer available.
We are in the last steps of adding X11 mining to the pool. Everything else, like counting balances, modifying the website to support multiple algorithms, and checking shares, is working except for submitting blocks to DASH.
I'm writing a post to track the problems I'm encountering trying to successfully submit blocks to the DASH network. This is an ongoing issue that I've been working on for about 70 hours. I thought I would post it here to see if someone could assist.
The key problem is that the following line in the DASH client code is being triggered and printing to the logs (main.cpp, 3715, in dash-master):
To debug this issue, I modified our software to print out the blocks that we were sending to submitblock. In one example, we mined a testnet block with the following hex:
I also modified the latest checkin from dash-master to LogPrintf the data received in the submitblock RPC call. The logs demonstrated that the received hex matches the sent hex. The block in this example contains one transaction, the coinbase, and the coinbase transaction is:
I confirmed that this transaction is present verbatim in the block above using a find/replace. I modified the DASH daemon to print out the value returned by .ToString() for the transaction, and it prints:
I confirmed that the "scriptPubKey" values match the values we provided in our code.
Next, I computed the hash of the transaction using our code, which is doublesha(trans). I made sure to do so at the last possible moment. The output equals the hash at transaction creation time, indicating that nothing is modifying the transaction before it is sent to the DASH daemon. This hash is 87da6c80c2c7a8034951e68bd845892b98291c7401857f362245fa03f300d80f, which you'll see in the block header from the submitted block is also the merkle root of the block because there is only one transaction. I tried reversing the byte order of this hash but it made no difference with the hashMerkleRoot mismatch problem.
It was suggested to me that the cause of this problem could be that the proof of work being submitted was too low, but I reviewed the DASH code and determined that the check for proof of work occurs before this line. I also modified our code to send random nonces so that the proof of work would be invalid, and the error message changed, indicating that if there were a proof of work error, it would occur before this problem.
After more investigation, I determined that the DASH daemon does not compute the same hash for this transaction as we do. At main.cpp, line 3713 (or so), I added a line:
And it prints:
The "hash" is the merkle root of the one transaction I provided in submitblock, as expected. I compared the second output to the output from CTransaction.ToString() and noticed that they are the same (at least the first few characters that are printed), indicating that the DASH daemon computes these hashes differently than I do. I then used decoderawtransaction in the dash-cli, and it also prints this value as the "txid." Next, I tried multiple modifications of the hashing algorithm in our code to see if I could result in a matching hash. I tried doublesha(trans), sha(trans), x11(trans), doublex11(trans), and all four of these against the transaction in reversed byte order. None of those matches the txid or hash the DASH daemon computes for the transaction.
Finally, I again tried submitting blocks to the litecoin daemon and all other daemons, and they still accept doublesha(trans) as the txid, as they always have. I then thought that a diff between litecoin and dash-master might be useful, but there are far too many changes to make sense of it.
At this point, the issue appears to be that I don't have a clear understanding of how DASH computes a transaction id. One step I thought I would try is trying to get the DASH client to serialize a transaction after it had been deserialized, to see if what comes out is the same as what I provided in submitblock. If it is different, then I could compare the bytes that are different and try to find why and where they are modified. Unfortunately, I've been trying to figure out how the "<<" operators and streams work in the C++ code for the past six hours with no luck.
If someone can help me understand how to follow the C++ code to figure out how to serialize a transaction, that would be greatly appreciated and would reduce the time required on Monday.
------
If someone can go further and explain exactly what algorithm is being used and what is being hashed in the above example to compute the DASH transaction id of this transaction, I'll pay $100. The correct answer will include three facts: what algorithms are being used by DASH to hash the coinbase transaction in the example, the hex of the transaction DASH is hashing (because the endianness or something else may be different), and what caused our hash to be different from the dameon's hash.
If you are helping for payment, make the first correct post that we can reproduce. We'll pay you immediately after we verify what we're doing wrong. Our judgment on who (if anyone) contributed to resolving the issue and therefore gets paid is final.
We are in the last steps of adding X11 mining to the pool. Everything else, like counting balances, modifying the website to support multiple algorithms, and checking shares, is working except for submitting blocks to DASH.
I'm writing a post to track the problems I'm encountering trying to successfully submit blocks to the DASH network. This is an ongoing issue that I've been working on for about 70 hours. I thought I would post it here to see if someone could assist.
The key problem is that the following line in the DASH client code is being triggered and printing to the logs (main.cpp, 3715, in dash-master):
Code: Select all
return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"),
REJECT_INVALID, "bad-txnmrklroot", true);
Code: Select all
00000020001580da24043d2fe44bb9e36731432005c1f42b3cc28ab20cdeaca41b00000087da6c80c2c7a8034951e68bd845892b98291c7401857f362245fa03f300d80f07c4a958d13c011efca135000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f030660020425c4a95808980000017e0000000c2f364d55394439514652522f000000000240230e43000000001976a9142fe1230bd7859268277431bd2c15d1d92bc6b45f88ac40230e43000000001976a9148f95e6fae491957622ed7fc8021d01e48d7188fb88ac000000000a50726f68617368696e67
Code: Select all
02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f030660020425c4a95808980000017e0000000c2f364d55394439514652522f000000000240230e43000000001976a9142fe1230bd7859268277431bd2c15d1d92bc6b45f88ac40230e43000000001976a9148f95e6fae491957622ed7fc8021d01e48d7188fb88ac000000000a50726f68617368696e67
Code: Select all
CTransaction(hash=2c8d760a35, ver=2, vin.size=1, vout.size=2, nLockTime=0)
CTxIn(COutPoint(0000000000000000000000000000000000000000000000000000000000000000, 4294967295), coinbase 030660020425c4a95808980000017e0000000c2f364d55394439514652522f, nSequence=0)
CTxOut(nValue=11.25000000, scriptPubKey=76a9142fe1230bd7859268277431bd)
CTxOut(nValue=11.25000000, scriptPubKey=76a9148f95e6fae491957622ed7fc8)
Next, I computed the hash of the transaction using our code, which is doublesha(trans). I made sure to do so at the last possible moment. The output equals the hash at transaction creation time, indicating that nothing is modifying the transaction before it is sent to the DASH daemon. This hash is 87da6c80c2c7a8034951e68bd845892b98291c7401857f362245fa03f300d80f, which you'll see in the block header from the submitted block is also the merkle root of the block because there is only one transaction. I tried reversing the byte order of this hash but it made no difference with the hashMerkleRoot mismatch problem.
It was suggested to me that the cause of this problem could be that the proof of work being submitted was too low, but I reviewed the DASH code and determined that the check for proof of work occurs before this line. I also modified our code to send random nonces so that the proof of work would be invalid, and the error message changed, indicating that if there were a proof of work error, it would occur before this problem.
After more investigation, I determined that the DASH daemon does not compute the same hash for this transaction as we do. At main.cpp, line 3713 (or so), I added a line:
Code: Select all
LogPrintf("hash: %s, root2: %s\n", block.hashMerkleRoot.GetHex(), hashMerkleRoot2.GetHex());
Code: Select all
hash: 0fd800f303fa4522367f8501741c29982b8945d88be6514903a8c7c2806cda87, root2: 2c8d760a3582c7fffbe882c4c754708f228c3d1649955f2df2bd31e240e54ce2,
Finally, I again tried submitting blocks to the litecoin daemon and all other daemons, and they still accept doublesha(trans) as the txid, as they always have. I then thought that a diff between litecoin and dash-master might be useful, but there are far too many changes to make sense of it.
At this point, the issue appears to be that I don't have a clear understanding of how DASH computes a transaction id. One step I thought I would try is trying to get the DASH client to serialize a transaction after it had been deserialized, to see if what comes out is the same as what I provided in submitblock. If it is different, then I could compare the bytes that are different and try to find why and where they are modified. Unfortunately, I've been trying to figure out how the "<<" operators and streams work in the C++ code for the past six hours with no luck.
If someone can help me understand how to follow the C++ code to figure out how to serialize a transaction, that would be greatly appreciated and would reduce the time required on Monday.
------
If someone can go further and explain exactly what algorithm is being used and what is being hashed in the above example to compute the DASH transaction id of this transaction, I'll pay $100. The correct answer will include three facts: what algorithms are being used by DASH to hash the coinbase transaction in the example, the hex of the transaction DASH is hashing (because the endianness or something else may be different), and what caused our hash to be different from the dameon's hash.
If you are helping for payment, make the first correct post that we can reproduce. We'll pay you immediately after we verify what we're doing wrong. Our judgment on who (if anyone) contributed to resolving the issue and therefore gets paid is final.