TheTrunk
6 years ago
46 changed files with 4321 additions and 452 deletions
@ -0,0 +1,3 @@ |
|||
{ |
|||
"CurrentProjectSetting": "x86-Debug" |
|||
} |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"ExpandedNodes": [ |
|||
"" |
|||
], |
|||
"PreviewInSolutionExplorer": false |
|||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,349 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2014-2015 The Bitcoin Core developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Test addressindex generation and fetching |
|||
# |
|||
|
|||
import time |
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.util import * |
|||
from test_framework.script import * |
|||
from test_framework.mininode import * |
|||
import binascii |
|||
|
|||
class AddressIndexTest(BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing test directory "+self.options.tmpdir) |
|||
initialize_chain_clean(self.options.tmpdir, 4) |
|||
|
|||
def setup_network(self): |
|||
self.nodes = [] |
|||
# Nodes 0/1 are "wallet" nodes |
|||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-relaypriority=0"])) |
|||
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-addressindex"])) |
|||
# Nodes 2/3 are used for testing |
|||
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-addressindex", "-relaypriority=0"])) |
|||
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-addressindex"])) |
|||
connect_nodes(self.nodes[0], 1) |
|||
connect_nodes(self.nodes[0], 2) |
|||
connect_nodes(self.nodes[0], 3) |
|||
|
|||
self.is_network_split = False |
|||
self.sync_all() |
|||
|
|||
def run_test(self): |
|||
print "Mining blocks..." |
|||
self.nodes[0].generate(105) |
|||
self.sync_all() |
|||
|
|||
chain_height = self.nodes[1].getblockcount() |
|||
assert_equal(chain_height, 105) |
|||
assert_equal(self.nodes[1].getbalance(), 0) |
|||
assert_equal(self.nodes[2].getbalance(), 0) |
|||
|
|||
# Check that balances are correct |
|||
balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") |
|||
assert_equal(balance0["balance"], 0) |
|||
|
|||
# Check p2pkh and p2sh address indexes |
|||
print "Testing p2pkh and p2sh address index..." |
|||
|
|||
txid0 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 10) |
|||
self.nodes[0].generate(1) |
|||
|
|||
txidb0 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 10) |
|||
self.nodes[0].generate(1) |
|||
|
|||
txid1 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 15) |
|||
self.nodes[0].generate(1) |
|||
|
|||
txidb1 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 15) |
|||
self.nodes[0].generate(1) |
|||
|
|||
txid2 = self.nodes[0].sendtoaddress("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 20) |
|||
self.nodes[0].generate(1) |
|||
|
|||
txidb2 = self.nodes[0].sendtoaddress("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 20) |
|||
self.nodes[0].generate(1) |
|||
|
|||
self.sync_all() |
|||
|
|||
txids = self.nodes[1].getaddresstxids("mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs") |
|||
assert_equal(len(txids), 3) |
|||
assert_equal(txids[0], txid0) |
|||
assert_equal(txids[1], txid1) |
|||
assert_equal(txids[2], txid2) |
|||
|
|||
txidsb = self.nodes[1].getaddresstxids("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") |
|||
assert_equal(len(txidsb), 3) |
|||
assert_equal(txidsb[0], txidb0) |
|||
assert_equal(txidsb[1], txidb1) |
|||
assert_equal(txidsb[2], txidb2) |
|||
|
|||
# Check that limiting by height works |
|||
print "Testing querying txids by range of block heights.." |
|||
height_txids = self.nodes[1].getaddresstxids({ |
|||
"addresses": ["2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br"], |
|||
"start": 105, |
|||
"end": 110 |
|||
}) |
|||
assert_equal(len(height_txids), 2) |
|||
assert_equal(height_txids[0], txidb0) |
|||
assert_equal(height_txids[1], txidb1) |
|||
|
|||
# Check that multiple addresses works |
|||
multitxids = self.nodes[1].getaddresstxids({"addresses": ["2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs"]}) |
|||
assert_equal(len(multitxids), 6) |
|||
assert_equal(multitxids[0], txid0) |
|||
assert_equal(multitxids[1], txidb0) |
|||
assert_equal(multitxids[2], txid1) |
|||
assert_equal(multitxids[3], txidb1) |
|||
assert_equal(multitxids[4], txid2) |
|||
assert_equal(multitxids[5], txidb2) |
|||
|
|||
# Check that balances are correct |
|||
balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") |
|||
assert_equal(balance0["balance"], 45 * 100000000) |
|||
|
|||
# Check that outputs with the same address will only return one txid |
|||
print "Testing for txid uniqueness..." |
|||
addressHash = "6349a418fc4578d10a372b54b45c280cc8c4382f".decode("hex") |
|||
scriptPubKey = CScript([OP_HASH160, addressHash, OP_EQUAL]) |
|||
unspent = self.nodes[0].listunspent() |
|||
tx = CTransaction() |
|||
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] |
|||
tx.vout = [CTxOut(10, scriptPubKey), CTxOut(11, scriptPubKey)] |
|||
tx.rehash() |
|||
|
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
|
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
txidsmany = self.nodes[1].getaddresstxids("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") |
|||
assert_equal(len(txidsmany), 4) |
|||
assert_equal(txidsmany[3], sent_txid) |
|||
|
|||
# Check that balances are correct |
|||
print "Testing balances..." |
|||
balance0 = self.nodes[1].getaddressbalance("2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") |
|||
assert_equal(balance0["balance"], 45 * 100000000 + 21) |
|||
|
|||
# Check that balances are correct after spending |
|||
print "Testing balances after spending..." |
|||
privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" |
|||
address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" |
|||
addressHash2 = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") |
|||
scriptPubKey2 = CScript([OP_DUP, OP_HASH160, addressHash2, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
self.nodes[0].importprivkey(privkey2) |
|||
|
|||
unspent = self.nodes[0].listunspent() |
|||
tx = CTransaction() |
|||
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] |
|||
amount = unspent[0]["amount"] * 100000000 |
|||
tx.vout = [CTxOut(amount, scriptPubKey2)] |
|||
tx.rehash() |
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
spending_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
balance1 = self.nodes[1].getaddressbalance(address2) |
|||
assert_equal(balance1["balance"], amount) |
|||
|
|||
tx = CTransaction() |
|||
tx.vin = [CTxIn(COutPoint(int(spending_txid, 16), 0))] |
|||
send_amount = 1 * 100000000 + 12840 |
|||
change_amount = amount - send_amount - 10000 |
|||
tx.vout = [CTxOut(change_amount, scriptPubKey2), CTxOut(send_amount, scriptPubKey)] |
|||
tx.rehash() |
|||
|
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
balance2 = self.nodes[1].getaddressbalance(address2) |
|||
assert_equal(balance2["balance"], change_amount) |
|||
|
|||
# Check that deltas are returned correctly |
|||
deltas = self.nodes[1].getaddressdeltas({"addresses": [address2], "start": 1, "end": 200}) |
|||
balance3 = 0 |
|||
for delta in deltas: |
|||
balance3 += delta["satoshis"] |
|||
assert_equal(balance3, change_amount) |
|||
assert_equal(deltas[0]["address"], address2) |
|||
assert_equal(deltas[0]["blockindex"], 1) |
|||
|
|||
# Check that entire range will be queried |
|||
deltasAll = self.nodes[1].getaddressdeltas({"addresses": [address2]}) |
|||
assert_equal(len(deltasAll), len(deltas)) |
|||
|
|||
# Check that deltas can be returned from range of block heights |
|||
deltas = self.nodes[1].getaddressdeltas({"addresses": [address2], "start": 113, "end": 113}) |
|||
assert_equal(len(deltas), 1) |
|||
|
|||
# Check that unspent outputs can be queried |
|||
print "Testing utxos..." |
|||
utxos = self.nodes[1].getaddressutxos({"addresses": [address2]}) |
|||
assert_equal(len(utxos), 1) |
|||
assert_equal(utxos[0]["satoshis"], change_amount) |
|||
|
|||
# Check that indexes will be updated with a reorg |
|||
print "Testing reorg..." |
|||
|
|||
best_hash = self.nodes[0].getbestblockhash() |
|||
self.nodes[0].invalidateblock(best_hash) |
|||
self.nodes[1].invalidateblock(best_hash) |
|||
self.nodes[2].invalidateblock(best_hash) |
|||
self.nodes[3].invalidateblock(best_hash) |
|||
self.sync_all() |
|||
|
|||
balance4 = self.nodes[1].getaddressbalance(address2) |
|||
assert_equal(balance4, balance1) |
|||
|
|||
utxos2 = self.nodes[1].getaddressutxos({"addresses": [address2]}) |
|||
assert_equal(len(utxos2), 1) |
|||
assert_equal(utxos2[0]["satoshis"], amount) |
|||
|
|||
# Check sorting of utxos |
|||
self.nodes[2].generate(150) |
|||
|
|||
txidsort1 = self.nodes[2].sendtoaddress(address2, 50) |
|||
self.nodes[2].generate(1) |
|||
txidsort2 = self.nodes[2].sendtoaddress(address2, 50) |
|||
self.nodes[2].generate(1) |
|||
self.sync_all() |
|||
|
|||
utxos3 = self.nodes[1].getaddressutxos({"addresses": [address2]}) |
|||
assert_equal(len(utxos3), 3) |
|||
assert_equal(utxos3[0]["height"], 114) |
|||
assert_equal(utxos3[1]["height"], 264) |
|||
assert_equal(utxos3[2]["height"], 265) |
|||
|
|||
# Check mempool indexing |
|||
print "Testing mempool indexing..." |
|||
|
|||
privKey3 = "cVfUn53hAbRrDEuMexyfgDpZPhF7KqXpS8UZevsyTDaugB7HZ3CD" |
|||
address3 = "mw4ynwhS7MmrQ27hr82kgqu7zryNDK26JB" |
|||
addressHash3 = "aa9872b5bbcdb511d89e0e11aa27da73fd2c3f50".decode("hex") |
|||
scriptPubKey3 = CScript([OP_DUP, OP_HASH160, addressHash3, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
address4 = "2N8oFVB2vThAKury4vnLquW2zVjsYjjAkYQ" |
|||
scriptPubKey4 = CScript([OP_HASH160, addressHash3, OP_EQUAL]) |
|||
unspent = self.nodes[2].listunspent() |
|||
|
|||
tx = CTransaction() |
|||
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] |
|||
amount = unspent[0]["amount"] * 100000000 |
|||
tx.vout = [CTxOut(amount, scriptPubKey3)] |
|||
tx.rehash() |
|||
signed_tx = self.nodes[2].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
memtxid1 = self.nodes[2].sendrawtransaction(signed_tx["hex"], True) |
|||
time.sleep(2) |
|||
|
|||
tx2 = CTransaction() |
|||
tx2.vin = [CTxIn(COutPoint(int(unspent[1]["txid"], 16), unspent[1]["vout"]))] |
|||
amount = unspent[1]["amount"] * 100000000 |
|||
tx2.vout = [ |
|||
CTxOut(amount / 4, scriptPubKey3), |
|||
CTxOut(amount / 4, scriptPubKey3), |
|||
CTxOut(amount / 4, scriptPubKey4), |
|||
CTxOut(amount / 4, scriptPubKey4) |
|||
] |
|||
tx2.rehash() |
|||
signed_tx2 = self.nodes[2].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8")) |
|||
memtxid2 = self.nodes[2].sendrawtransaction(signed_tx2["hex"], True) |
|||
time.sleep(2) |
|||
|
|||
mempool = self.nodes[2].getaddressmempool({"addresses": [address3]}) |
|||
assert_equal(len(mempool), 3) |
|||
assert_equal(mempool[0]["txid"], memtxid1) |
|||
assert_equal(mempool[0]["address"], address3) |
|||
assert_equal(mempool[0]["index"], 0) |
|||
assert_equal(mempool[1]["txid"], memtxid2) |
|||
assert_equal(mempool[1]["index"], 0) |
|||
assert_equal(mempool[2]["txid"], memtxid2) |
|||
assert_equal(mempool[2]["index"], 1) |
|||
|
|||
self.nodes[2].generate(1); |
|||
self.sync_all(); |
|||
mempool2 = self.nodes[2].getaddressmempool({"addresses": [address3]}) |
|||
assert_equal(len(mempool2), 0) |
|||
|
|||
tx = CTransaction() |
|||
tx.vin = [ |
|||
CTxIn(COutPoint(int(memtxid2, 16), 0)), |
|||
CTxIn(COutPoint(int(memtxid2, 16), 1)) |
|||
] |
|||
tx.vout = [CTxOut(amount / 2 - 10000, scriptPubKey2)] |
|||
tx.rehash() |
|||
self.nodes[2].importprivkey(privKey3) |
|||
signed_tx3 = self.nodes[2].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
memtxid3 = self.nodes[2].sendrawtransaction(signed_tx3["hex"], True) |
|||
time.sleep(2) |
|||
|
|||
mempool3 = self.nodes[2].getaddressmempool({"addresses": [address3]}) |
|||
assert_equal(len(mempool3), 2) |
|||
assert_equal(mempool3[0]["prevtxid"], memtxid2) |
|||
assert_equal(mempool3[0]["prevout"], 0) |
|||
assert_equal(mempool3[1]["prevtxid"], memtxid2) |
|||
assert_equal(mempool3[1]["prevout"], 1) |
|||
|
|||
# sending and receiving to the same address |
|||
privkey1 = "cQY2s58LhzUCmEXN8jtAp1Etnijx78YRZ466w4ikX1V4UpTpbsf8" |
|||
address1 = "myAUWSHnwsQrhuMWv4Br6QsCnpB41vFwHn" |
|||
address1hash = "c192bff751af8efec15135d42bfeedf91a6f3e34".decode("hex") |
|||
address1script = CScript([OP_DUP, OP_HASH160, address1hash, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
|
|||
self.nodes[0].sendtoaddress(address1, 10) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
utxos = self.nodes[1].getaddressutxos({"addresses": [address1]}) |
|||
assert_equal(len(utxos), 1) |
|||
|
|||
tx = CTransaction() |
|||
tx.vin = [ |
|||
CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["outputIndex"])) |
|||
] |
|||
amount = utxos[0]["satoshis"] - 1000 |
|||
tx.vout = [CTxOut(amount, address1script)] |
|||
tx.rehash() |
|||
self.nodes[0].importprivkey(privkey1) |
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
mem_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
|
|||
self.sync_all() |
|||
mempool_deltas = self.nodes[2].getaddressmempool({"addresses": [address1]}) |
|||
assert_equal(len(mempool_deltas), 2) |
|||
|
|||
# Include chaininfo in results |
|||
print "Testing results with chain info..." |
|||
|
|||
deltas_with_info = self.nodes[1].getaddressdeltas({ |
|||
"addresses": [address2], |
|||
"start": 1, |
|||
"end": 200, |
|||
"chainInfo": True |
|||
}) |
|||
start_block_hash = self.nodes[1].getblockhash(1); |
|||
end_block_hash = self.nodes[1].getblockhash(200); |
|||
assert_equal(deltas_with_info["start"]["height"], 1) |
|||
assert_equal(deltas_with_info["start"]["hash"], start_block_hash) |
|||
assert_equal(deltas_with_info["end"]["height"], 200) |
|||
assert_equal(deltas_with_info["end"]["hash"], end_block_hash) |
|||
|
|||
utxos_with_info = self.nodes[1].getaddressutxos({"addresses": [address2], "chainInfo": True}) |
|||
expected_tip_block_hash = self.nodes[1].getblockhash(267); |
|||
assert_equal(utxos_with_info["height"], 267) |
|||
assert_equal(utxos_with_info["hash"], expected_tip_block_hash) |
|||
|
|||
print "Passed\n" |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
AddressIndexTest().main() |
@ -0,0 +1,139 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2014-2015 The Bitcoin Core developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Test addressindex generation and fetching |
|||
# |
|||
|
|||
import time |
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.util import * |
|||
from test_framework.script import * |
|||
from test_framework.mininode import * |
|||
import binascii |
|||
|
|||
class SpentIndexTest(BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing test directory "+self.options.tmpdir) |
|||
initialize_chain_clean(self.options.tmpdir, 4) |
|||
|
|||
def setup_network(self): |
|||
self.nodes = [] |
|||
# Nodes 0/1 are "wallet" nodes |
|||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) |
|||
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-spentindex"])) |
|||
# Nodes 2/3 are used for testing |
|||
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-spentindex"])) |
|||
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-spentindex", "-txindex"])) |
|||
connect_nodes(self.nodes[0], 1) |
|||
connect_nodes(self.nodes[0], 2) |
|||
connect_nodes(self.nodes[0], 3) |
|||
|
|||
self.is_network_split = False |
|||
self.sync_all() |
|||
|
|||
def run_test(self): |
|||
print "Mining blocks..." |
|||
self.nodes[0].generate(105) |
|||
self.sync_all() |
|||
|
|||
chain_height = self.nodes[1].getblockcount() |
|||
assert_equal(chain_height, 105) |
|||
|
|||
# Check that |
|||
print "Testing spent index..." |
|||
|
|||
privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" |
|||
address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" |
|||
addressHash = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") |
|||
scriptPubKey = CScript([OP_DUP, OP_HASH160, addressHash, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
unspent = self.nodes[0].listunspent() |
|||
tx = CTransaction() |
|||
amount = unspent[0]["amount"] * 100000000 |
|||
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] |
|||
tx.vout = [CTxOut(amount, scriptPubKey)] |
|||
tx.rehash() |
|||
|
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
print "Testing getspentinfo method..." |
|||
|
|||
# Check that the spentinfo works standalone |
|||
info = self.nodes[1].getspentinfo({"txid": unspent[0]["txid"], "index": unspent[0]["vout"]}) |
|||
assert_equal(info["txid"], txid) |
|||
assert_equal(info["index"], 0) |
|||
assert_equal(info["height"], 106) |
|||
|
|||
print "Testing getrawtransaction method..." |
|||
|
|||
# Check that verbose raw transaction includes spent info |
|||
txVerbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) |
|||
assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentTxId"], txid) |
|||
assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentIndex"], 0) |
|||
assert_equal(txVerbose["vout"][unspent[0]["vout"]]["spentHeight"], 106) |
|||
|
|||
# Check that verbose raw transaction includes input values |
|||
txVerbose2 = self.nodes[3].getrawtransaction(txid, 1) |
|||
assert_equal(txVerbose2["vin"][0]["value"], Decimal(unspent[0]["amount"])) |
|||
assert_equal(txVerbose2["vin"][0]["valueSat"], amount) |
|||
|
|||
# Check that verbose raw transaction includes address values and input values |
|||
privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" |
|||
address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" |
|||
addressHash2 = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") |
|||
scriptPubKey2 = CScript([OP_DUP, OP_HASH160, addressHash2, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
tx2 = CTransaction() |
|||
tx2.vin = [CTxIn(COutPoint(int(txid, 16), 0))] |
|||
tx2.vout = [CTxOut(amount, scriptPubKey2)] |
|||
tx.rehash() |
|||
self.nodes[0].importprivkey(privkey) |
|||
signed_tx2 = self.nodes[0].signrawtransaction(binascii.hexlify(tx2.serialize()).decode("utf-8")) |
|||
txid2 = self.nodes[0].sendrawtransaction(signed_tx2["hex"], True) |
|||
|
|||
# Check the mempool index |
|||
self.sync_all() |
|||
txVerbose3 = self.nodes[1].getrawtransaction(txid2, 1) |
|||
assert_equal(txVerbose3["vin"][0]["address"], address2) |
|||
assert_equal(txVerbose3["vin"][0]["value"], Decimal(unspent[0]["amount"])) |
|||
assert_equal(txVerbose3["vin"][0]["valueSat"], amount) |
|||
|
|||
# Check the database index |
|||
block_hash = self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
txVerbose4 = self.nodes[3].getrawtransaction(txid2, 1) |
|||
assert_equal(txVerbose4["vin"][0]["address"], address2) |
|||
assert_equal(txVerbose4["vin"][0]["value"], Decimal(unspent[0]["amount"])) |
|||
assert_equal(txVerbose4["vin"][0]["valueSat"], amount) |
|||
|
|||
|
|||
# Check block deltas |
|||
print "Testing getblockdeltas..." |
|||
|
|||
block = self.nodes[3].getblockdeltas(block_hash[0]) |
|||
assert_equal(len(block["deltas"]), 2) |
|||
assert_equal(block["deltas"][0]["index"], 0) |
|||
assert_equal(len(block["deltas"][0]["inputs"]), 0) |
|||
assert_equal(len(block["deltas"][0]["outputs"]), 0) |
|||
assert_equal(block["deltas"][1]["index"], 1) |
|||
assert_equal(block["deltas"][1]["txid"], txid2) |
|||
assert_equal(block["deltas"][1]["inputs"][0]["index"], 0) |
|||
assert_equal(block["deltas"][1]["inputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") |
|||
assert_equal(block["deltas"][1]["inputs"][0]["satoshis"], amount * -1) |
|||
assert_equal(block["deltas"][1]["inputs"][0]["prevtxid"], txid) |
|||
assert_equal(block["deltas"][1]["inputs"][0]["prevout"], 0) |
|||
assert_equal(block["deltas"][1]["outputs"][0]["index"], 0) |
|||
assert_equal(block["deltas"][1]["outputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") |
|||
assert_equal(block["deltas"][1]["outputs"][0]["satoshis"], amount) |
|||
|
|||
print "Passed\n" |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
SpentIndexTest().main() |
@ -0,0 +1,61 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2014-2015 The Bitcoin Core developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Test timestampindex generation and fetching |
|||
# |
|||
|
|||
import time |
|||
|
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.util import * |
|||
|
|||
|
|||
class TimestampIndexTest(BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing test directory "+self.options.tmpdir) |
|||
initialize_chain_clean(self.options.tmpdir, 4) |
|||
|
|||
def setup_network(self): |
|||
self.nodes = [] |
|||
# Nodes 0/1 are "wallet" nodes |
|||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) |
|||
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-timestampindex"])) |
|||
# Nodes 2/3 are used for testing |
|||
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"])) |
|||
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-timestampindex"])) |
|||
connect_nodes(self.nodes[0], 1) |
|||
connect_nodes(self.nodes[0], 2) |
|||
connect_nodes(self.nodes[0], 3) |
|||
|
|||
self.is_network_split = False |
|||
self.sync_all() |
|||
|
|||
def run_test(self): |
|||
print "Mining 25 blocks..." |
|||
blockhashes = self.nodes[0].generate(25) |
|||
time.sleep(3) |
|||
print "Mining 25 blocks..." |
|||
blockhashes.extend(self.nodes[0].generate(25)) |
|||
time.sleep(3) |
|||
print "Mining 25 blocks..." |
|||
blockhashes.extend(self.nodes[0].generate(25)) |
|||
self.sync_all() |
|||
low = self.nodes[1].getblock(blockhashes[0])["time"] |
|||
high = low + 76 |
|||
|
|||
print "Checking timestamp index..." |
|||
hashes = self.nodes[1].getblockhashes(high, low) |
|||
|
|||
assert_equal(len(hashes), len(blockhashes)) |
|||
|
|||
assert_equal(hashes, blockhashes) |
|||
|
|||
print "Passed\n" |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
TimestampIndexTest().main() |
@ -0,0 +1,73 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2014-2015 The Bitcoin Core developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Test txindex generation and fetching |
|||
# |
|||
|
|||
import time |
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.util import * |
|||
from test_framework.script import * |
|||
from test_framework.mininode import * |
|||
import binascii |
|||
|
|||
class TxIndexTest(BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing test directory "+self.options.tmpdir) |
|||
initialize_chain_clean(self.options.tmpdir, 4) |
|||
|
|||
def setup_network(self): |
|||
self.nodes = [] |
|||
# Nodes 0/1 are "wallet" nodes |
|||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) |
|||
self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-txindex"])) |
|||
# Nodes 2/3 are used for testing |
|||
self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-txindex"])) |
|||
self.nodes.append(start_node(3, self.options.tmpdir, ["-debug", "-txindex"])) |
|||
connect_nodes(self.nodes[0], 1) |
|||
connect_nodes(self.nodes[0], 2) |
|||
connect_nodes(self.nodes[0], 3) |
|||
|
|||
self.is_network_split = False |
|||
self.sync_all() |
|||
|
|||
def run_test(self): |
|||
print "Mining blocks..." |
|||
self.nodes[0].generate(105) |
|||
self.sync_all() |
|||
|
|||
chain_height = self.nodes[1].getblockcount() |
|||
assert_equal(chain_height, 105) |
|||
|
|||
print "Testing transaction index..." |
|||
|
|||
privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" |
|||
address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" |
|||
addressHash = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc".decode("hex") |
|||
scriptPubKey = CScript([OP_DUP, OP_HASH160, addressHash, OP_EQUALVERIFY, OP_CHECKSIG]) |
|||
unspent = self.nodes[0].listunspent() |
|||
tx = CTransaction() |
|||
amount = unspent[0]["amount"] * 100000000 |
|||
tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] |
|||
tx.vout = [CTxOut(amount, scriptPubKey)] |
|||
tx.rehash() |
|||
|
|||
signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) |
|||
txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) |
|||
self.nodes[0].generate(1) |
|||
self.sync_all() |
|||
|
|||
# Check verbose raw transaction results |
|||
verbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) |
|||
assert_equal(verbose["vout"][0]["valueSat"], 5000000000); |
|||
assert_equal(verbose["vout"][0]["value"], 50); |
|||
|
|||
print "Passed\n" |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
TxIndexTest().main() |
@ -0,0 +1,263 @@ |
|||
import binascii |
|||
import calendar |
|||
import json |
|||
import plyvel |
|||
import progressbar |
|||
import os |
|||
import stat |
|||
import struct |
|||
import subprocess |
|||
import sys |
|||
import tarfile |
|||
import time |
|||
|
|||
ZCASH_CLI = './src/zcash-cli' |
|||
USAGE = """ |
|||
Requirements: |
|||
- find |
|||
- xz |
|||
- %s (edit ZCASH_CLI in this script to alter the path) |
|||
- A running mainnet zcashd using the default datadir with -txindex=1 |
|||
|
|||
Example usage: |
|||
|
|||
make -C src/leveldb/ |
|||
virtualenv venv |
|||
. venv/bin/activate |
|||
pip install --global-option=build_ext --global-option="-L$(pwd)/src/leveldb/" --global-option="-I$(pwd)/src/leveldb/include/" plyvel |
|||
pip install progressbar2 |
|||
LD_LIBRARY_PATH=src/leveldb python qa/zcash/create_benchmark_archive.py |
|||
""" % ZCASH_CLI |
|||
|
|||
def check_deps(): |
|||
if subprocess.call(['which', 'find', 'xz', ZCASH_CLI], stdout=subprocess.PIPE): |
|||
print USAGE |
|||
sys.exit() |
|||
|
|||
def encode_varint(n): |
|||
v = bytearray() |
|||
l = 0 |
|||
while True: |
|||
v.append((n & 0x7F) | (0x80 if l else 0x00)) |
|||
if (n <= 0x7F): |
|||
break |
|||
n = (n >> 7) - 1 |
|||
l += 1 |
|||
return bytes(v)[::-1] |
|||
|
|||
def decode_varint(v): |
|||
n = 0 |
|||
for ch in range(len(v)): |
|||
n = (n << 7) | (ord(v[ch]) & 0x7F) |
|||
if (ord(v[ch]) & 0x80): |
|||
n += 1 |
|||
else: |
|||
return n |
|||
|
|||
def compress_amount(n): |
|||
if n == 0: |
|||
return 0 |
|||
e = 0 |
|||
while (((n % 10) == 0) and e < 9): |
|||
n /= 10 |
|||
e += 1 |
|||
if e < 9: |
|||
d = (n % 10) |
|||
assert(d >= 1 and d <= 9) |
|||
n /= 10 |
|||
return 1 + (n*9 + d - 1)*10 + e |
|||
else: |
|||
return 1 + (n - 1)*10 + 9 |
|||
|
|||
OP_DUP = 0x76 |
|||
OP_EQUAL = 0x87 |
|||
OP_EQUALVERIFY = 0x88 |
|||
OP_HASH160 = 0xa9 |
|||
OP_CHECKSIG = 0xac |
|||
def to_key_id(script): |
|||
if len(script) == 25 and \ |
|||
script[0] == OP_DUP and \ |
|||
script[1] == OP_HASH160 and \ |
|||
script[2] == 20 and \ |
|||
script[23] == OP_EQUALVERIFY and \ |
|||
script[24] == OP_CHECKSIG: |
|||
return script[3:23] |
|||
return bytes() |
|||
|
|||
def to_script_id(script): |
|||
if len(script) == 23 and \ |
|||
script[0] == OP_HASH160 and \ |
|||
script[1] == 20 and \ |
|||
script[22] == OP_EQUAL: |
|||
return script[2:22] |
|||
return bytes() |
|||
|
|||
def to_pubkey(script): |
|||
if len(script) == 35 and \ |
|||
script[0] == 33 and \ |
|||
script[34] == OP_CHECKSIG and \ |
|||
(script[1] == 0x02 or script[1] == 0x03): |
|||
return script[1:34] |
|||
if len(script) == 67 and \ |
|||
script[0] == 65 and \ |
|||
script[66] == OP_CHECKSIG and \ |
|||
script[1] == 0x04: |
|||
return script[1:66] # assuming is fully valid |
|||
return bytes() |
|||
|
|||
def compress_script(script): |
|||
result = bytearray() |
|||
|
|||
key_id = to_key_id(script) |
|||
if key_id: |
|||
result.append(0x00) |
|||
result.extend(key_id) |
|||
return bytes(result) |
|||
|
|||
script_id = to_script_id(script) |
|||
if script_id: |
|||
result.append(0x01) |
|||
result.extend(script_id) |
|||
return bytes(result) |
|||
|
|||
pubkey = to_pubkey(script) |
|||
if pubkey: |
|||
result.append(0x00) |
|||
result.extend(pubkey[1:33]) |
|||
if pubkey[0] == 0x02 or pubkey[0] == 0x03: |
|||
result[0] = pubkey[0] |
|||
return bytes(result) |
|||
elif pubkey[0] == 0x04: |
|||
result[0] = 0x04 | (pubkey[64] & 0x01) |
|||
return bytes(result) |
|||
|
|||
size = len(script) + 6 |
|||
result.append(encode_varint(size)) |
|||
result.extend(script) |
|||
return bytes(result) |
|||
|
|||
def deterministic_filter(tarinfo): |
|||
tarinfo.uid = tarinfo.gid = 0 |
|||
tarinfo.uname = tarinfo.gname = "root" |
|||
tarinfo.mtime = calendar.timegm(time.strptime('2017-05-17', '%Y-%m-%d')) |
|||
tarinfo.mode |= stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP |
|||
tarinfo.mode &= ~stat.S_IWGRP |
|||
if tarinfo.isdir(): |
|||
tarinfo.mode |= \ |
|||
stat.S_IXUSR | \ |
|||
stat.S_IXGRP | \ |
|||
stat.S_IXOTH |
|||
else: |
|||
tarinfo.mode &= \ |
|||
~stat.S_IXUSR & \ |
|||
~stat.S_IXGRP & \ |
|||
~stat.S_IXOTH |
|||
return tarinfo |
|||
|
|||
def create_benchmark_archive(blk_hash): |
|||
blk = json.loads(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash])) |
|||
print 'Height: %d' % blk['height'] |
|||
print 'Transactions: %d' % len(blk['tx']) |
|||
|
|||
os.mkdir('benchmark') |
|||
with open('benchmark/block-%d.dat' % blk['height'], 'wb') as f: |
|||
f.write(binascii.unhexlify(subprocess.check_output([ZCASH_CLI, 'getblock', blk_hash, 'false']).strip())) |
|||
|
|||
txs = [json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1']) |
|||
) for tx in blk['tx']] |
|||
|
|||
js_txs = len([tx for tx in txs if len(tx['vjoinsplit']) > 0]) |
|||
if js_txs: |
|||
print 'Block contains %d JoinSplit-containing transactions' % js_txs |
|||
return |
|||
|
|||
inputs = [(x['txid'], x['vout']) for tx in txs for x in tx['vin'] if x.has_key('txid')] |
|||
print 'Total inputs: %d' % len(inputs) |
|||
|
|||
unique_inputs = {} |
|||
for i in sorted(inputs): |
|||
if unique_inputs.has_key(i[0]): |
|||
unique_inputs[i[0]].append(i[1]) |
|||
else: |
|||
unique_inputs[i[0]] = [i[1]] |
|||
print 'Unique input transactions: %d' % len(unique_inputs) |
|||
|
|||
db_path = 'benchmark/block-%d-inputs' % blk['height'] |
|||
db = plyvel.DB(db_path, create_if_missing=True) |
|||
wb = db.write_batch() |
|||
bar = progressbar.ProgressBar(redirect_stdout=True) |
|||
print 'Collecting input coins for block' |
|||
for tx in bar(unique_inputs.keys()): |
|||
rawtx = json.loads(subprocess.check_output([ZCASH_CLI, 'getrawtransaction', tx, '1'])) |
|||
|
|||
mask_size = 0 |
|||
mask_code = 0 |
|||
b = 0 |
|||
while 2+b*8 < len(rawtx['vout']): |
|||
zero = True |
|||
i = 0 |
|||
while i < 8 and 2+b*8+i < len(rawtx['vout']): |
|||
if 2+b*8+i in unique_inputs[tx]: |
|||
zero = False |
|||
i += 1 |
|||
if not zero: |
|||
mask_size = b + 1 |
|||
mask_code += 1 |
|||
b += 1 |
|||
|
|||
coinbase = len(rawtx['vin']) == 1 and 'coinbase' in rawtx['vin'][0] |
|||
first = len(rawtx['vout']) > 0 and 0 in unique_inputs[tx] |
|||
second = len(rawtx['vout']) > 1 and 1 in unique_inputs[tx] |
|||
code = 8*(mask_code - (0 if first or second else 1)) + \ |
|||
(1 if coinbase else 0) + \ |
|||
(2 if first else 0) + \ |
|||
(4 if second else 0) |
|||
|
|||
coins = bytearray() |
|||
# Serialized format: |
|||
# - VARINT(nVersion) |
|||
coins.extend(encode_varint(rawtx['version'])) |
|||
# - VARINT(nCode) |
|||
coins.extend(encode_varint(code)) |
|||
# - unspentness bitvector, for vout[2] and further; least significant byte first |
|||
for b in range(mask_size): |
|||
avail = 0 |
|||
i = 0 |
|||
while i < 8 and 2+b*8+i < len(rawtx['vout']): |
|||
if 2+b*8+i in unique_inputs[tx]: |
|||
avail |= (1 << i) |
|||
i += 1 |
|||
coins.append(avail) |
|||
# - the non-spent CTxOuts (via CTxOutCompressor) |
|||
for i in range(len(rawtx['vout'])): |
|||
if i in unique_inputs[tx]: |
|||
coins.extend(encode_varint(compress_amount(int(rawtx['vout'][i]['valueZat'])))) |
|||
coins.extend(compress_script( |
|||
binascii.unhexlify(rawtx['vout'][i]['scriptPubKey']['hex']))) |
|||
# - VARINT(nHeight) |
|||
coins.extend(encode_varint(json.loads( |
|||
subprocess.check_output([ZCASH_CLI, 'getblockheader', rawtx['blockhash']]) |
|||
)['height'])) |
|||
|
|||
db_key = b'c' + bytes(binascii.unhexlify(tx)[::-1]) |
|||
db_val = bytes(coins) |
|||
wb.put(db_key, db_val) |
|||
|
|||
wb.write() |
|||
db.close() |
|||
|
|||
# Make reproducible archive |
|||
os.remove('%s/LOG' % db_path) |
|||
files = subprocess.check_output(['find', 'benchmark']).strip().split('\n') |
|||
archive_name = 'block-%d.tar' % blk['height'] |
|||
tar = tarfile.open(archive_name, 'w') |
|||
for name in sorted(files): |
|||
tar.add(name, recursive=False, filter=deterministic_filter) |
|||
tar.close() |
|||
subprocess.check_call(['xz', '-6', archive_name]) |
|||
print 'Created archive %s.xz' % archive_name |
|||
subprocess.call(['rm', '-r', 'benchmark']) |
|||
|
|||
if __name__ == '__main__': |
|||
check_deps() |
|||
create_benchmark_archive('0000000007cdb809e48e51dd0b530e8f5073e0a9e9bd7ae920fe23e874658c74') |
@ -0,0 +1,60 @@ |
|||
#!/usr/bin/env python2 |
|||
# Copyright (c) 2017 The Zcash developers |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
# |
|||
# Create a large wallet |
|||
# |
|||
# To use: |
|||
# - Copy to qa/rpc-tests/wallet_large.py |
|||
# - Add wallet_large.py to RPC tests list |
|||
# - ./qa/pull-tester/rpc-tests.sh wallet_large --nocleanup |
|||
# - Archive the resulting /tmp/test###### directory |
|||
# |
|||
|
|||
from test_framework.test_framework import BitcoinTestFramework |
|||
from test_framework.util import ( |
|||
assert_equal, |
|||
connect_nodes_bi, |
|||
initialize_chain_clean, |
|||
start_nodes, |
|||
) |
|||
|
|||
from decimal import Decimal |
|||
|
|||
|
|||
class LargeWalletTest(BitcoinTestFramework): |
|||
|
|||
def setup_chain(self): |
|||
print("Initializing test directory "+self.options.tmpdir) |
|||
initialize_chain_clean(self.options.tmpdir, 2) |
|||
|
|||
def setup_network(self): |
|||
self.nodes = start_nodes(2, self.options.tmpdir) |
|||
connect_nodes_bi(self.nodes, 0, 1) |
|||
self.is_network_split = False |
|||
self.sync_all() |
|||
|
|||
def run_test(self): |
|||
self.nodes[1].generate(103) |
|||
self.sync_all() |
|||
|
|||
inputs = [] |
|||
for i in range(200000): |
|||
taddr = self.nodes[0].getnewaddress() |
|||
inputs.append(self.nodes[1].sendtoaddress(taddr, Decimal("0.001"))) |
|||
if i % 1000 == 0: |
|||
self.nodes[1].generate(1) |
|||
self.sync_all() |
|||
|
|||
self.nodes[1].generate(1) |
|||
self.sync_all() |
|||
print('Node 0: %d transactions, %d UTXOs' % |
|||
(len(self.nodes[0].listtransactions()), len(self.nodes[0].listunspent()))) |
|||
print('Node 1: %d transactions, %d UTXOs' % |
|||
(len(self.nodes[1].listtransactions()), len(self.nodes[1].listunspent()))) |
|||
assert_equal(len(self.nodes[0].listunspent()), len(inputs)) |
|||
|
|||
if __name__ == '__main__': |
|||
LargeWalletTest().main() |
@ -1,47 +1,201 @@ |
|||
#!/bin/bash |
|||
#!/usr/bin/env python2 |
|||
# |
|||
# Execute all of the automated tests related to Zcash. |
|||
# |
|||
|
|||
set -eu |
|||
|
|||
SUITE_EXIT_STATUS=0 |
|||
REPOROOT="$(readlink -f "$(dirname "$0")"/../../)" |
|||
|
|||
function run_test_phase |
|||
{ |
|||
echo "===== BEGIN: $*" |
|||
set +e |
|||
eval "$@" |
|||
if [ $? -eq 0 ] |
|||
then |
|||
echo "===== PASSED: $*" |
|||
else |
|||
echo "===== FAILED: $*" |
|||
SUITE_EXIT_STATUS=1 |
|||
fi |
|||
set -e |
|||
import argparse |
|||
import os |
|||
import re |
|||
import subprocess |
|||
import sys |
|||
|
|||
REPOROOT = os.path.dirname( |
|||
os.path.dirname( |
|||
os.path.dirname( |
|||
os.path.abspath(__file__) |
|||
) |
|||
) |
|||
) |
|||
|
|||
def repofile(filename): |
|||
return os.path.join(REPOROOT, filename) |
|||
|
|||
|
|||
# |
|||
# Custom test runners |
|||
# |
|||
|
|||
RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH') |
|||
RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes') |
|||
RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes') |
|||
|
|||
def test_rpath_runpath(filename): |
|||
output = subprocess.check_output( |
|||
[repofile('qa/zcash/checksec.sh'), '--file', repofile(filename)] |
|||
) |
|||
if RE_RPATH_RUNPATH.search(output): |
|||
print('PASS: %s has no RPATH or RUNPATH.' % filename) |
|||
return True |
|||
else: |
|||
print('FAIL: %s has an RPATH or a RUNPATH.' % filename) |
|||
print(output) |
|||
return False |
|||
|
|||
def test_fortify_source(filename): |
|||
proc = subprocess.Popen( |
|||
[repofile('qa/zcash/checksec.sh'), '--fortify-file', repofile(filename)], |
|||
stdout=subprocess.PIPE, |
|||
) |
|||
line1 = proc.stdout.readline() |
|||
line2 = proc.stdout.readline() |
|||
proc.terminate() |
|||
if RE_FORTIFY_AVAILABLE.search(line1) and RE_FORTIFY_USED.search(line2): |
|||
print('PASS: %s has FORTIFY_SOURCE.' % filename) |
|||
return True |
|||
else: |
|||
print('FAIL: %s is missing FORTIFY_SOURCE.' % filename) |
|||
return False |
|||
|
|||
def check_security_hardening(): |
|||
ret = True |
|||
|
|||
# PIE, RELRO, Canary, and NX are tested by make check-security. |
|||
ret &= subprocess.call(['make', '-C', repofile('src'), 'check-security']) == 0 |
|||
|
|||
ret &= test_rpath_runpath('src/zcashd') |
|||
ret &= test_rpath_runpath('src/zcash-cli') |
|||
ret &= test_rpath_runpath('src/zcash-gtest') |
|||
ret &= test_rpath_runpath('src/zcash-tx') |
|||
ret &= test_rpath_runpath('src/test/test_bitcoin') |
|||
ret &= test_rpath_runpath('src/zcash/GenerateParams') |
|||
|
|||
# NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE |
|||
# is enabled for the entire binary. See issue #915. |
|||
ret &= test_fortify_source('src/zcashd') |
|||
ret &= test_fortify_source('src/zcash-cli') |
|||
ret &= test_fortify_source('src/zcash-gtest') |
|||
ret &= test_fortify_source('src/zcash-tx') |
|||
ret &= test_fortify_source('src/test/test_bitcoin') |
|||
ret &= test_fortify_source('src/zcash/GenerateParams') |
|||
|
|||
return ret |
|||
|
|||
def ensure_no_dot_so_in_depends(): |
|||
arch_dir = os.path.join( |
|||
REPOROOT, |
|||
'depends', |
|||
'x86_64-unknown-linux-gnu', |
|||
) |
|||
|
|||
exit_code = 0 |
|||
|
|||
if os.path.isdir(arch_dir): |
|||
lib_dir = os.path.join(arch_dir, 'lib') |
|||
libraries = os.listdir(lib_dir) |
|||
|
|||
for lib in libraries: |
|||
if lib.find(".so") != -1: |
|||
print lib |
|||
exit_code = 1 |
|||
else: |
|||
exit_code = 2 |
|||
print "arch-specific build dir not present: {}".format(arch_dir) |
|||
print "Did you build the ./depends tree?" |
|||
print "Are you on a currently unsupported architecture?" |
|||
|
|||
if exit_code == 0: |
|||
print "PASS." |
|||
else: |
|||
print "FAIL." |
|||
|
|||
return exit_code == 0 |
|||
|
|||
def util_test(): |
|||
return subprocess.call( |
|||
[repofile('src/test/bitcoin-util-test.py')], |
|||
cwd=repofile('src'), |
|||
env={'PYTHONPATH': repofile('src/test'), 'srcdir': repofile('src')} |
|||
) == 0 |
|||
|
|||
|
|||
# |
|||
# Tests |
|||
# |
|||
|
|||
STAGES = [ |
|||
'btest', |
|||
'gtest', |
|||
'sec-hard', |
|||
'no-dot-so', |
|||
'util-test', |
|||
'secp256k1', |
|||
'libsnark', |
|||
'univalue', |
|||
'rpc', |
|||
] |
|||
|
|||
STAGE_COMMANDS = { |
|||
'btest': [repofile('src/test/test_bitcoin'), '-p'], |
|||
'gtest': [repofile('src/zcash-gtest')], |
|||
'sec-hard': check_security_hardening, |
|||
'no-dot-so': ensure_no_dot_so_in_depends, |
|||
'util-test': util_test, |
|||
'secp256k1': ['make', '-C', repofile('src/secp256k1'), 'check'], |
|||
'libsnark': ['make', '-C', repofile('src'), 'libsnark-tests'], |
|||
'univalue': ['make', '-C', repofile('src/univalue'), 'check'], |
|||
'rpc': [repofile('qa/pull-tester/rpc-tests.sh')], |
|||
} |
|||
|
|||
cd "${REPOROOT}" |
|||
|
|||
# Test phases: |
|||
run_test_phase "${REPOROOT}/qa/zcash/check-security-hardening.sh" |
|||
run_test_phase "${REPOROOT}/qa/zcash/ensure-no-dot-so-in-depends.py" |
|||
# |
|||
# Test driver |
|||
# |
|||
|
|||
def run_stage(stage): |
|||
print('Running stage %s' % stage) |
|||
print('=' * (len(stage) + 14)) |
|||
print |
|||
|
|||
cmd = STAGE_COMMANDS[stage] |
|||
if type(cmd) == type([]): |
|||
ret = subprocess.call(cmd) == 0 |
|||
else: |
|||
ret = cmd() |
|||
|
|||
# If make check fails, show test-suite.log as part of our run_test_phase |
|||
# output (and fail the phase with false): |
|||
run_test_phase make check '||' \ |
|||
'{' \ |
|||
echo '=== ./src/test-suite.log ===' ';' \ |
|||
cat './src/test-suite.log' ';' \ |
|||
false ';' \ |
|||
'}' |
|||
print |
|||
print('-' * (len(stage) + 15)) |
|||
print('Finished stage %s' % stage) |
|||
print |
|||
|
|||
exit $SUITE_EXIT_STATUS |
|||
return ret |
|||
|
|||
def main(): |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument('--list-stages', dest='list', action='store_true') |
|||
parser.add_argument('stage', nargs='*', default=STAGES, |
|||
help='One of %s'%STAGES) |
|||
args = parser.parse_args() |
|||
|
|||
# Check for list |
|||
if args.list: |
|||
for s in STAGES: |
|||
print(s) |
|||
sys.exit(0) |
|||
|
|||
# Check validity of stages |
|||
for s in args.stage: |
|||
if s not in STAGES: |
|||
print("Invalid stage '%s' (choose from %s)" % (s, STAGES)) |
|||
sys.exit(1) |
|||
|
|||
# Run the stages |
|||
passed = True |
|||
for s in args.stage: |
|||
passed &= run_stage(s) |
|||
|
|||
if not passed: |
|||
print("!!! One or more test stages failed !!!") |
|||
sys.exit(1) |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -0,0 +1,82 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_ADDRESSINDEX_H |
|||
#define BITCOIN_ADDRESSINDEX_H |
|||
|
|||
#include "uint256.h" |
|||
#include "amount.h" |
|||
|
|||
struct CMempoolAddressDelta |
|||
{ |
|||
int64_t time; |
|||
CAmount amount; |
|||
uint256 prevhash; |
|||
unsigned int prevout; |
|||
|
|||
CMempoolAddressDelta(int64_t t, CAmount a, uint256 hash, unsigned int out) { |
|||
time = t; |
|||
amount = a; |
|||
prevhash = hash; |
|||
prevout = out; |
|||
} |
|||
|
|||
CMempoolAddressDelta(int64_t t, CAmount a) { |
|||
time = t; |
|||
amount = a; |
|||
prevhash.SetNull(); |
|||
prevout = 0; |
|||
} |
|||
}; |
|||
|
|||
struct CMempoolAddressDeltaKey |
|||
{ |
|||
int type; |
|||
uint160 addressBytes; |
|||
uint256 txhash; |
|||
unsigned int index; |
|||
int spending; |
|||
|
|||
CMempoolAddressDeltaKey(int addressType, uint160 addressHash, uint256 hash, unsigned int i, int s) { |
|||
type = addressType; |
|||
addressBytes = addressHash; |
|||
txhash = hash; |
|||
index = i; |
|||
spending = s; |
|||
} |
|||
|
|||
CMempoolAddressDeltaKey(int addressType, uint160 addressHash) { |
|||
type = addressType; |
|||
addressBytes = addressHash; |
|||
txhash.SetNull(); |
|||
index = 0; |
|||
spending = 0; |
|||
} |
|||
}; |
|||
|
|||
struct CMempoolAddressDeltaKeyCompare |
|||
{ |
|||
bool operator()(const CMempoolAddressDeltaKey& a, const CMempoolAddressDeltaKey& b) const { |
|||
if (a.type == b.type) { |
|||
if (a.addressBytes == b.addressBytes) { |
|||
if (a.txhash == b.txhash) { |
|||
if (a.index == b.index) { |
|||
return a.spending < b.spending; |
|||
} else { |
|||
return a.index < b.index; |
|||
} |
|||
} else { |
|||
return a.txhash < b.txhash; |
|||
} |
|||
} else { |
|||
return a.addressBytes < b.addressBytes; |
|||
} |
|||
} else { |
|||
return a.type < b.type; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
#endif // BITCOIN_ADDRESSINDEX_H
|
@ -0,0 +1,98 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_SPENTINDEX_H |
|||
#define BITCOIN_SPENTINDEX_H |
|||
|
|||
#include "uint256.h" |
|||
#include "amount.h" |
|||
|
|||
struct CSpentIndexKey { |
|||
uint256 txid; |
|||
unsigned int outputIndex; |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { |
|||
READWRITE(txid); |
|||
READWRITE(outputIndex); |
|||
} |
|||
|
|||
CSpentIndexKey(uint256 t, unsigned int i) { |
|||
txid = t; |
|||
outputIndex = i; |
|||
} |
|||
|
|||
CSpentIndexKey() { |
|||
SetNull(); |
|||
} |
|||
|
|||
void SetNull() { |
|||
txid.SetNull(); |
|||
outputIndex = 0; |
|||
} |
|||
|
|||
}; |
|||
|
|||
struct CSpentIndexValue { |
|||
uint256 txid; |
|||
unsigned int inputIndex; |
|||
int blockHeight; |
|||
CAmount satoshis; |
|||
int addressType; |
|||
uint160 addressHash; |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { |
|||
READWRITE(txid); |
|||
READWRITE(inputIndex); |
|||
READWRITE(blockHeight); |
|||
READWRITE(satoshis); |
|||
READWRITE(addressType); |
|||
READWRITE(addressHash); |
|||
} |
|||
|
|||
CSpentIndexValue(uint256 t, unsigned int i, int h, CAmount s, int type, uint160 a) { |
|||
txid = t; |
|||
inputIndex = i; |
|||
blockHeight = h; |
|||
satoshis = s; |
|||
addressType = type; |
|||
addressHash = a; |
|||
} |
|||
|
|||
CSpentIndexValue() { |
|||
SetNull(); |
|||
} |
|||
|
|||
void SetNull() { |
|||
txid.SetNull(); |
|||
inputIndex = 0; |
|||
blockHeight = 0; |
|||
satoshis = 0; |
|||
addressType = 0; |
|||
addressHash.SetNull(); |
|||
} |
|||
|
|||
bool IsNull() const { |
|||
return txid.IsNull(); |
|||
} |
|||
}; |
|||
|
|||
struct CSpentIndexKeyCompare |
|||
{ |
|||
bool operator()(const CSpentIndexKey& a, const CSpentIndexKey& b) const { |
|||
if (a.txid == b.txid) { |
|||
return a.outputIndex < b.outputIndex; |
|||
} else { |
|||
return a.txid < b.txid; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
#endif // BITCOIN_SPENTINDEX_H
|
@ -0,0 +1,59 @@ |
|||
// Copyright (c) 2012-2015 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#include "script/script.h" |
|||
#include "test/test_bitcoin.h" |
|||
|
|||
#include <boost/test/unit_test.hpp> |
|||
|
|||
using namespace std; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE(script_P2PKH_tests, BasicTestingSetup) |
|||
|
|||
BOOST_AUTO_TEST_CASE(IsPayToPublicKeyHash) |
|||
{ |
|||
// Test CScript::IsPayToPublicKeyHash()
|
|||
uint160 dummy; |
|||
CScript p2pkh; |
|||
p2pkh << OP_DUP << OP_HASH160 << ToByteVector(dummy) << OP_EQUALVERIFY << OP_CHECKSIG; |
|||
BOOST_CHECK(p2pkh.IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char direct[] = { |
|||
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG |
|||
}; |
|||
BOOST_CHECK(CScript(direct, direct+sizeof(direct)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char notp2pkh1[] = { |
|||
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG |
|||
}; |
|||
BOOST_CHECK(!CScript(notp2pkh1, notp2pkh1+sizeof(notp2pkh1)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char p2sh[] = { |
|||
OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUAL |
|||
}; |
|||
BOOST_CHECK(!CScript(p2sh, p2sh+sizeof(p2sh)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char extra[] = { |
|||
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_CHECKSIG |
|||
}; |
|||
BOOST_CHECK(!CScript(extra, extra+sizeof(extra)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char missing[] = { |
|||
OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, OP_EQUALVERIFY, OP_CHECKSIG, OP_RETURN |
|||
}; |
|||
BOOST_CHECK(!CScript(missing, missing+sizeof(missing)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char missing2[] = { |
|||
OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
|||
}; |
|||
BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing)).IsPayToPublicKeyHash()); |
|||
|
|||
static const unsigned char tooshort[] = { |
|||
OP_DUP, OP_HASH160, 2, 0,0, OP_EQUALVERIFY, OP_CHECKSIG |
|||
}; |
|||
BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(direct)).IsPayToPublicKeyHash()); |
|||
|
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
@ -0,0 +1,61 @@ |
|||
#!/usr/bin/python |
|||
# Copyright 2014 BitPay, Inc. |
|||
# Distributed under the MIT software license, see the accompanying |
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|||
|
|||
import subprocess |
|||
import os |
|||
import json |
|||
import sys |
|||
import buildenv |
|||
import shutil |
|||
|
|||
def assert_equal(thing1, thing2): |
|||
if thing1 != thing2: |
|||
raise AssertionError("%s != %s"%(str(thing1),str(thing2))) |
|||
|
|||
if __name__ == '__main__': |
|||
datadir = os.environ["srcdir"] + "/test/data" |
|||
execprog = './wallet-utility' + buildenv.exeext |
|||
execargs = '-datadir=' + datadir |
|||
execrun = execprog + ' ' + execargs |
|||
|
|||
proc = subprocess.Popen(execrun, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) |
|||
try: |
|||
outs = proc.communicate() |
|||
except OSError: |
|||
print("OSError, Failed to execute " + execprog) |
|||
sys.exit(1) |
|||
|
|||
output = json.loads(outs[0]) |
|||
|
|||
assert_equal(output[0], "13EngsxkRi7SJPPqCyJsKf34U8FoX9E9Av") |
|||
assert_equal(output[1], "1FKCLGTpPeYBUqfNxktck8k5nqxB8sjim8") |
|||
assert_equal(output[2], "13cdtE9tnNeXCZJ8KQ5WELgEmLSBLnr48F") |
|||
|
|||
execargs = '-datadir=' + datadir + ' -dumppass' |
|||
execrun = execprog + ' ' + execargs |
|||
|
|||
proc = subprocess.Popen(execrun, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) |
|||
try: |
|||
outs = proc.communicate() |
|||
except OSError: |
|||
print("OSError, Failed to execute " + execprog) |
|||
sys.exit(1) |
|||
|
|||
output = json.loads(outs[0]) |
|||
|
|||
assert_equal(output[0]['addr'], "13EngsxkRi7SJPPqCyJsKf34U8FoX9E9Av") |
|||
assert_equal(output[0]['pkey'], "5Jz5BWE2WQxp1hGqDZeisQFV1mRFR2AVBAgiXCbNcZyXNjD9aUd") |
|||
assert_equal(output[1]['addr'], "1FKCLGTpPeYBUqfNxktck8k5nqxB8sjim8") |
|||
assert_equal(output[1]['pkey'], "5HsX2b3v2GjngYQ5ZM4mLp2b2apw6aMNVaPELV1YmpiYR1S4jzc") |
|||
assert_equal(output[2]['addr'], "13cdtE9tnNeXCZJ8KQ5WELgEmLSBLnr48F") |
|||
assert_equal(output[2]['pkey'], "5KCWAs1wX2ESiL4PfDR8XYVSSETHFd2jaRGxt1QdanBFTit4XcH") |
|||
|
|||
if os.path.exists(datadir + '/database'): |
|||
if os.path.isdir(datadir + '/database'): |
|||
shutil.rmtree(datadir + '/database') |
|||
|
|||
if os.path.exists(datadir + '/db.log'): |
|||
os.remove(datadir + '/db.log') |
|||
sys.exit(0) |
@ -0,0 +1,339 @@ |
|||
#include <iostream> |
|||
#include <string> |
|||
|
|||
// Include local headers
|
|||
#include "wallet/walletdb.h" |
|||
#include "util.h" |
|||
#include "base58.h" |
|||
#include "wallet/crypter.h" |
|||
#include <boost/foreach.hpp> |
|||
|
|||
|
|||
void show_help() |
|||
{ |
|||
std::cout << |
|||
"This program outputs Bitcoin addresses and private keys from a wallet.dat file" << std::endl |
|||
<< std::endl |
|||
<< "Usage and options: " |
|||
<< std::endl |
|||
<< " -datadir=<directory> to tell the program where your wallet is" |
|||
<< std::endl |
|||
<< " -wallet=<name> (Optional) if your wallet is not named wallet.dat" |
|||
<< std::endl |
|||
<< " -regtest or -testnet (Optional) dumps addresses from regtest/testnet" |
|||
<< std::endl |
|||
<< " -dumppass (Optional)if you want to extract private keys associated with addresses" |
|||
<< std::endl |
|||
<< " -pass=<walletpassphrase> if you have encrypted private keys stored in your wallet" |
|||
<< std::endl; |
|||
} |
|||
|
|||
|
|||
class WalletUtilityDB : public CDB |
|||
{ |
|||
private: |
|||
typedef std::map<unsigned int, CMasterKey> MasterKeyMap; |
|||
MasterKeyMap mapMasterKeys; |
|||
unsigned int nMasterKeyMaxID; |
|||
SecureString mPass; |
|||
std::vector<CKeyingMaterial> vMKeys; |
|||
|
|||
public: |
|||
WalletUtilityDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose) |
|||
{ |
|||
nMasterKeyMaxID = 0; |
|||
mPass.reserve(100); |
|||
} |
|||
|
|||
std::string getAddress(CDataStream ssKey); |
|||
std::string getKey(CDataStream ssKey, CDataStream ssValue); |
|||
std::string getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass); |
|||
bool updateMasterKeys(CDataStream ssKey, CDataStream ssValue); |
|||
bool parseKeys(bool dumppriv, std::string masterPass); |
|||
|
|||
bool DecryptSecret(const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); |
|||
bool Unlock(); |
|||
bool DecryptKey(const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); |
|||
}; |
|||
|
|||
|
|||
/*
|
|||
* Address from a public key in base58 |
|||
*/ |
|||
std::string WalletUtilityDB::getAddress(CDataStream ssKey) |
|||
{ |
|||
CPubKey vchPubKey; |
|||
ssKey >> vchPubKey; |
|||
CKeyID id = vchPubKey.GetID(); |
|||
std::string strAddr = CBitcoinAddress(id).ToString(); |
|||
|
|||
return strAddr; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* Non encrypted private key in WIF |
|||
*/ |
|||
std::string WalletUtilityDB::getKey(CDataStream ssKey, CDataStream ssValue) |
|||
{ |
|||
std::string strKey; |
|||
CPubKey vchPubKey; |
|||
ssKey >> vchPubKey; |
|||
CPrivKey pkey; |
|||
CKey key; |
|||
|
|||
ssValue >> pkey; |
|||
if (key.Load(pkey, vchPubKey, true)) |
|||
strKey = CBitcoinSecret(key).ToString(); |
|||
|
|||
return strKey; |
|||
} |
|||
|
|||
|
|||
bool WalletUtilityDB::DecryptSecret(const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) |
|||
{ |
|||
CCrypter cKeyCrypter; |
|||
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); |
|||
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); |
|||
|
|||
BOOST_FOREACH(const CKeyingMaterial vMKey, vMKeys) |
|||
{ |
|||
if(!cKeyCrypter.SetKey(vMKey, chIV)) |
|||
continue; |
|||
if (cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext))) |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
|
|||
bool WalletUtilityDB::Unlock() |
|||
{ |
|||
CCrypter crypter; |
|||
CKeyingMaterial vMasterKey; |
|||
|
|||
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) |
|||
{ |
|||
if(!crypter.SetKeyFromPassphrase(mPass, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) |
|||
return false; |
|||
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) |
|||
continue; // try another master key
|
|||
vMKeys.push_back(vMasterKey); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool WalletUtilityDB::DecryptKey(const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) |
|||
{ |
|||
CKeyingMaterial vchSecret; |
|||
if(!DecryptSecret(vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) |
|||
return false; |
|||
|
|||
if (vchSecret.size() != 32) |
|||
return false; |
|||
|
|||
key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* Encrypted private key in WIF format |
|||
*/ |
|||
std::string WalletUtilityDB::getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass) |
|||
{ |
|||
mPass = masterPass.c_str(); |
|||
CPubKey vchPubKey; |
|||
ssKey >> vchPubKey; |
|||
CKey key; |
|||
|
|||
std::vector<unsigned char> vKey; |
|||
ssValue >> vKey; |
|||
|
|||
if (!Unlock()) |
|||
return ""; |
|||
|
|||
if(!DecryptKey(vKey, vchPubKey, key)) |
|||
return ""; |
|||
|
|||
std::string strKey = CBitcoinSecret(key).ToString(); |
|||
return strKey; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* Master key derivation |
|||
*/ |
|||
bool WalletUtilityDB::updateMasterKeys(CDataStream ssKey, CDataStream ssValue) |
|||
{ |
|||
unsigned int nID; |
|||
ssKey >> nID; |
|||
CMasterKey kMasterKey; |
|||
ssValue >> kMasterKey; |
|||
if (mapMasterKeys.count(nID) != 0) |
|||
{ |
|||
std::cout << "Error reading wallet database: duplicate CMasterKey id " << nID << std::endl; |
|||
return false; |
|||
} |
|||
mapMasterKeys[nID] = kMasterKey; |
|||
|
|||
if (nMasterKeyMaxID < nID) |
|||
nMasterKeyMaxID = nID; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* Look at all the records and parse keys for addresses and private keys |
|||
*/ |
|||
bool WalletUtilityDB::parseKeys(bool dumppriv, std::string masterPass) |
|||
{ |
|||
DBErrors result = DB_LOAD_OK; |
|||
std::string strType; |
|||
bool first = true; |
|||
|
|||
try { |
|||
Dbc* pcursor = GetCursor(); |
|||
if (!pcursor) |
|||
{ |
|||
LogPrintf("Error getting wallet database cursor\n"); |
|||
result = DB_CORRUPT; |
|||
} |
|||
|
|||
if (dumppriv) |
|||
{ |
|||
while (result == DB_LOAD_OK && true) |
|||
{ |
|||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); |
|||
CDataStream ssValue(SER_DISK, CLIENT_VERSION); |
|||
int result = ReadAtCursor(pcursor, ssKey, ssValue); |
|||
|
|||
if (result == DB_NOTFOUND) { |
|||
break; |
|||
} |
|||
else if (result != 0) |
|||
{ |
|||
LogPrintf("Error reading next record from wallet database\n"); |
|||
result = DB_CORRUPT; |
|||
break; |
|||
} |
|||
|
|||
ssKey >> strType; |
|||
if (strType == "mkey") |
|||
{ |
|||
updateMasterKeys(ssKey, ssValue); |
|||
} |
|||
} |
|||
pcursor->close(); |
|||
pcursor = GetCursor(); |
|||
} |
|||
|
|||
while (result == DB_LOAD_OK && true) |
|||
{ |
|||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); |
|||
CDataStream ssValue(SER_DISK, CLIENT_VERSION); |
|||
int ret = ReadAtCursor(pcursor, ssKey, ssValue); |
|||
|
|||
if (ret == DB_NOTFOUND) |
|||
{ |
|||
std::cout << " ]" << std::endl; |
|||
first = true; |
|||
break; |
|||
} |
|||
else if (ret != DB_LOAD_OK) |
|||
{ |
|||
LogPrintf("Error reading next record from wallet database\n"); |
|||
result = DB_CORRUPT; |
|||
break; |
|||
} |
|||
|
|||
ssKey >> strType; |
|||
|
|||
if (strType == "key" || strType == "ckey") |
|||
{ |
|||
std::string strAddr = getAddress(ssKey); |
|||
std::string strKey = ""; |
|||
|
|||
|
|||
if (dumppriv && strType == "key") |
|||
strKey = getKey(ssKey, ssValue); |
|||
if (dumppriv && strType == "ckey") |
|||
{ |
|||
if (masterPass == "") |
|||
{ |
|||
std::cout << "Encrypted wallet, please provide a password. See help below" << std::endl; |
|||
show_help(); |
|||
result = DB_LOAD_FAIL; |
|||
break; |
|||
} |
|||
strKey = getCryptedKey(ssKey, ssValue, masterPass); |
|||
} |
|||
|
|||
if (strAddr != "") |
|||
{ |
|||
if (first) |
|||
std::cout << "[ "; |
|||
else |
|||
std::cout << ", "; |
|||
} |
|||
|
|||
if (dumppriv) |
|||
{ |
|||
std::cout << "{\"addr\" : \"" + strAddr + "\", " |
|||
<< "\"pkey\" : \"" + strKey + "\"}" |
|||
<< std::flush; |
|||
} |
|||
else |
|||
{ |
|||
std::cout << "\"" + strAddr + "\""; |
|||
} |
|||
|
|||
first = false; |
|||
} |
|||
} |
|||
|
|||
pcursor->close(); |
|||
} catch (DbException &e) { |
|||
std::cout << "DBException caught " << e.get_errno() << std::endl; |
|||
} catch (std::exception &e) { |
|||
std::cout << "Exception caught " << std::endl; |
|||
} |
|||
|
|||
if (result == DB_LOAD_OK) |
|||
return true; |
|||
else |
|||
return false; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
ParseParameters(argc, argv); |
|||
std::string walletFile = GetArg("-wallet", "wallet.dat"); |
|||
std::string masterPass = GetArg("-pass", ""); |
|||
bool fDumpPass = GetBoolArg("-dumppass", false); |
|||
bool help = GetBoolArg("-h", false); |
|||
bool result = false; |
|||
|
|||
if (help) |
|||
{ |
|||
show_help(); |
|||
return 0; |
|||
} |
|||
|
|||
try { |
|||
SelectParamsFromCommandLine(); |
|||
result = WalletUtilityDB(walletFile, "r").parseKeys(fDumpPass, masterPass); |
|||
} |
|||
catch (const std::exception& e) { |
|||
std::cout << "Error opening wallet file " << walletFile << std::endl; |
|||
std::cout << e.what() << std::endl; |
|||
} |
|||
|
|||
if (result) |
|||
return 0; |
|||
else |
|||
return -1; |
|||
} |
Loading…
Reference in new issue