Merge remote-tracking branch 'bitpay/0.12.1-bitcore' into dashBitcore1
Resolved Conflicts: .travis.yml (ignoring changes, leaving dash version) src/Makefile.am src/main.cpp src/main.h src/rpcclient.cpp src/rpcserver.cpp src/script/script.cpp src/script/script.h
This commit is contained in:
commit
48d0edcbdf
@ -7,7 +7,7 @@ build_darwin_OTOOL: = $(shell xcrun -f otool)
|
|||||||
build_darwin_NM: = $(shell xcrun -f nm)
|
build_darwin_NM: = $(shell xcrun -f nm)
|
||||||
build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool)
|
build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool)
|
||||||
build_darwin_SHA256SUM = shasum -a 256
|
build_darwin_SHA256SUM = shasum -a 256
|
||||||
build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o
|
build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -o
|
||||||
|
|
||||||
#darwin host on darwin builder. overrides darwin host preferences.
|
#darwin host on darwin builder. overrides darwin host preferences.
|
||||||
darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION)
|
darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
build_linux_SHA256SUM = sha256sum
|
build_linux_SHA256SUM = sha256sum
|
||||||
build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o
|
build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -o
|
||||||
|
@ -31,7 +31,7 @@ $(package)_config_opts += -no-iconv
|
|||||||
$(package)_config_opts += -no-gif
|
$(package)_config_opts += -no-gif
|
||||||
$(package)_config_opts += -no-freetype
|
$(package)_config_opts += -no-freetype
|
||||||
$(package)_config_opts += -no-nis
|
$(package)_config_opts += -no-nis
|
||||||
$(package)_config_opts += -no-pch
|
$(package)_config_opts += -pch
|
||||||
$(package)_config_opts += -no-qml-debug
|
$(package)_config_opts += -no-qml-debug
|
||||||
$(package)_config_opts += -nomake examples
|
$(package)_config_opts += -nomake examples
|
||||||
$(package)_config_opts += -nomake tests
|
$(package)_config_opts += -nomake tests
|
||||||
|
@ -97,6 +97,9 @@ testScripts = [
|
|||||||
'walletbackup.py',
|
'walletbackup.py',
|
||||||
'nodehandling.py',
|
'nodehandling.py',
|
||||||
'reindex.py',
|
'reindex.py',
|
||||||
|
'addressindex.py',
|
||||||
|
'timestampindex.py',
|
||||||
|
'spentindex.py',
|
||||||
'decodescript.py',
|
'decodescript.py',
|
||||||
'p2p-fullblocktest.py', # TODO: works, needs dash_hash
|
'p2p-fullblocktest.py', # TODO: works, needs dash_hash
|
||||||
'blockchain.py',
|
'blockchain.py',
|
||||||
|
328
qa/rpc-tests/addressindex.py
Executable file
328
qa/rpc-tests/addressindex.py
Executable file
@ -0,0 +1,328 @@
|
|||||||
|
#!/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"]))
|
||||||
|
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"]))
|
||||||
|
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": 0, "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"], 5000000000)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
print "Passed\n"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
AddressIndexTest().main()
|
@ -7,6 +7,7 @@ import socket
|
|||||||
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
|
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import *
|
from test_framework.util import *
|
||||||
|
from test_framework.netutil import test_ipv6_local
|
||||||
'''
|
'''
|
||||||
Test plan:
|
Test plan:
|
||||||
- Start bitcoind's with different proxy configurations
|
- Start bitcoind's with different proxy configurations
|
||||||
@ -34,6 +35,7 @@ addnode connect to generic DNS name
|
|||||||
|
|
||||||
class ProxyTest(BitcoinTestFramework):
|
class ProxyTest(BitcoinTestFramework):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.have_ipv6 = test_ipv6_local()
|
||||||
# Create two proxies on different ports
|
# Create two proxies on different ports
|
||||||
# ... one unauthenticated
|
# ... one unauthenticated
|
||||||
self.conf1 = Socks5Configuration()
|
self.conf1 = Socks5Configuration()
|
||||||
@ -45,29 +47,36 @@ class ProxyTest(BitcoinTestFramework):
|
|||||||
self.conf2.addr = ('127.0.0.1', 14000 + (os.getpid() % 1000))
|
self.conf2.addr = ('127.0.0.1', 14000 + (os.getpid() % 1000))
|
||||||
self.conf2.unauth = True
|
self.conf2.unauth = True
|
||||||
self.conf2.auth = True
|
self.conf2.auth = True
|
||||||
|
if self.have_ipv6:
|
||||||
# ... one on IPv6 with similar configuration
|
# ... one on IPv6 with similar configuration
|
||||||
self.conf3 = Socks5Configuration()
|
self.conf3 = Socks5Configuration()
|
||||||
self.conf3.af = socket.AF_INET6
|
self.conf3.af = socket.AF_INET6
|
||||||
self.conf3.addr = ('::1', 15000 + (os.getpid() % 1000))
|
self.conf3.addr = ('::1', 15000 + (os.getpid() % 1000))
|
||||||
self.conf3.unauth = True
|
self.conf3.unauth = True
|
||||||
self.conf3.auth = True
|
self.conf3.auth = True
|
||||||
|
else:
|
||||||
|
print "Warning: testing without local IPv6 support"
|
||||||
|
|
||||||
self.serv1 = Socks5Server(self.conf1)
|
self.serv1 = Socks5Server(self.conf1)
|
||||||
self.serv1.start()
|
self.serv1.start()
|
||||||
self.serv2 = Socks5Server(self.conf2)
|
self.serv2 = Socks5Server(self.conf2)
|
||||||
self.serv2.start()
|
self.serv2.start()
|
||||||
|
if self.have_ipv6:
|
||||||
self.serv3 = Socks5Server(self.conf3)
|
self.serv3 = Socks5Server(self.conf3)
|
||||||
self.serv3.start()
|
self.serv3.start()
|
||||||
|
|
||||||
def setup_nodes(self):
|
def setup_nodes(self):
|
||||||
# Note: proxies are not used to connect to local nodes
|
# Note: proxies are not used to connect to local nodes
|
||||||
# this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost
|
# this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost
|
||||||
return start_nodes(4, self.options.tmpdir, extra_args=[
|
args = [
|
||||||
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
|
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'],
|
||||||
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
|
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'],
|
||||||
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
|
['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'],
|
||||||
['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion']
|
[]
|
||||||
])
|
]
|
||||||
|
if self.have_ipv6:
|
||||||
|
args[3] = ['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion']
|
||||||
|
return start_nodes(4, self.options.tmpdir, extra_args=args)
|
||||||
|
|
||||||
def node_test(self, node, proxies, auth, test_onion=True):
|
def node_test(self, node, proxies, auth, test_onion=True):
|
||||||
rv = []
|
rv = []
|
||||||
@ -84,6 +93,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||||||
assert_equal(cmd.password, None)
|
assert_equal(cmd.password, None)
|
||||||
rv.append(cmd)
|
rv.append(cmd)
|
||||||
|
|
||||||
|
if self.have_ipv6:
|
||||||
# Test: outgoing IPv6 connection through node
|
# Test: outgoing IPv6 connection through node
|
||||||
node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
|
node.addnode("[1233:3432:2434:2343:3234:2345:6546:4534]:5443", "onetry")
|
||||||
cmd = proxies[1].queue.get()
|
cmd = proxies[1].queue.get()
|
||||||
@ -135,8 +145,9 @@ class ProxyTest(BitcoinTestFramework):
|
|||||||
rv = self.node_test(self.nodes[2], [self.serv2, self.serv2, self.serv2, self.serv2], True)
|
rv = self.node_test(self.nodes[2], [self.serv2, self.serv2, self.serv2, self.serv2], True)
|
||||||
# Check that credentials as used for -proxyrandomize connections are unique
|
# Check that credentials as used for -proxyrandomize connections are unique
|
||||||
credentials = set((x.username,x.password) for x in rv)
|
credentials = set((x.username,x.password) for x in rv)
|
||||||
assert_equal(len(credentials), 4)
|
assert_equal(len(credentials), len(rv))
|
||||||
|
|
||||||
|
if self.have_ipv6:
|
||||||
# proxy on IPv6 localhost
|
# proxy on IPv6 localhost
|
||||||
self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False, False)
|
self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False, False)
|
||||||
|
|
||||||
@ -167,6 +178,7 @@ class ProxyTest(BitcoinTestFramework):
|
|||||||
assert_equal(n2[net]['proxy_randomize_credentials'], True)
|
assert_equal(n2[net]['proxy_randomize_credentials'], True)
|
||||||
assert_equal(n2['onion']['reachable'], True)
|
assert_equal(n2['onion']['reachable'], True)
|
||||||
|
|
||||||
|
if self.have_ipv6:
|
||||||
n3 = networks_dict(self.nodes[3].getnetworkinfo())
|
n3 = networks_dict(self.nodes[3].getnetworkinfo())
|
||||||
for net in ['ipv4','ipv6']:
|
for net in ['ipv4','ipv6']:
|
||||||
assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
|
assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr))
|
||||||
|
119
qa/rpc-tests/spentindex.py
Executable file
119
qa/rpc-tests/spentindex.py
Executable file
@ -0,0 +1,119 @@
|
|||||||
|
#!/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
|
||||||
|
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)
|
||||||
|
|
||||||
|
print "Passed\n"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
SpentIndexTest().main()
|
@ -137,3 +137,18 @@ def addr_to_hex(addr):
|
|||||||
else:
|
else:
|
||||||
raise ValueError('Could not parse address %s' % addr)
|
raise ValueError('Could not parse address %s' % addr)
|
||||||
return binascii.hexlify(bytearray(addr))
|
return binascii.hexlify(bytearray(addr))
|
||||||
|
|
||||||
|
def test_ipv6_local():
|
||||||
|
'''
|
||||||
|
Check for (local) IPv6 support.
|
||||||
|
'''
|
||||||
|
import socket
|
||||||
|
# By using SOCK_DGRAM this will not actually make a connection, but it will
|
||||||
|
# fail if there is no route to IPv6 localhost.
|
||||||
|
have_ipv6 = True
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
|
s.connect(('::1', 0))
|
||||||
|
except socket.error:
|
||||||
|
have_ipv6 = False
|
||||||
|
return have_ipv6
|
||||||
|
51
qa/rpc-tests/timestampindex.py
Executable file
51
qa/rpc-tests/timestampindex.py
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/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 5 blocks..."
|
||||||
|
blockhashes = self.nodes[0].generate(5)
|
||||||
|
low = self.nodes[0].getblock(blockhashes[0])["time"]
|
||||||
|
high = self.nodes[0].getblock(blockhashes[4])["time"]
|
||||||
|
self.sync_all()
|
||||||
|
print "Checking timestamp index..."
|
||||||
|
hashes = self.nodes[1].getblockhashes(high, low)
|
||||||
|
assert_equal(len(hashes), 5)
|
||||||
|
assert_equal(sorted(blockhashes), sorted(hashes))
|
||||||
|
print "Passed\n"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
TimestampIndexTest().main()
|
73
qa/rpc-tests/txindex.py
Executable file
73
qa/rpc-tests/txindex.py
Executable file
@ -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()
|
@ -80,6 +80,8 @@ endif
|
|||||||
# dash core #
|
# dash core #
|
||||||
BITCOIN_CORE_H = \
|
BITCOIN_CORE_H = \
|
||||||
activemasternode.h \
|
activemasternode.h \
|
||||||
|
addressindex.h \
|
||||||
|
spentindex.h \
|
||||||
addrman.h \
|
addrman.h \
|
||||||
alert.h \
|
alert.h \
|
||||||
amount.h \
|
amount.h \
|
||||||
|
@ -70,6 +70,7 @@ BITCOIN_TESTS =\
|
|||||||
test/sanity_tests.cpp \
|
test/sanity_tests.cpp \
|
||||||
test/scheduler_tests.cpp \
|
test/scheduler_tests.cpp \
|
||||||
test/script_P2SH_tests.cpp \
|
test/script_P2SH_tests.cpp \
|
||||||
|
test/script_P2PKH_tests.cpp \
|
||||||
test/script_tests.cpp \
|
test/script_tests.cpp \
|
||||||
test/scriptnum_tests.cpp \
|
test/scriptnum_tests.cpp \
|
||||||
test/serialize_tests.cpp \
|
test/serialize_tests.cpp \
|
||||||
|
82
src/addressindex.h
Normal file
82
src/addressindex.h
Normal file
@ -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
|
@ -262,6 +262,23 @@ CTxDestination CBitcoinAddress::Get() const
|
|||||||
return CNoDestination();
|
return CNoDestination();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type) const
|
||||||
|
{
|
||||||
|
if (!IsValid()) {
|
||||||
|
return false;
|
||||||
|
} else if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) {
|
||||||
|
memcpy(&hashBytes, &vchData[0], 20);
|
||||||
|
type = 1;
|
||||||
|
return true;
|
||||||
|
} else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) {
|
||||||
|
memcpy(&hashBytes, &vchData[0], 20);
|
||||||
|
type = 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
|
bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
|
||||||
{
|
{
|
||||||
if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
|
if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
|
||||||
|
@ -116,6 +116,7 @@ public:
|
|||||||
|
|
||||||
CTxDestination Get() const;
|
CTxDestination Get() const;
|
||||||
bool GetKeyID(CKeyID &keyID) const;
|
bool GetKeyID(CKeyID &keyID) const;
|
||||||
|
bool GetIndexKey(uint160& hashBytes, int& type) const;
|
||||||
bool IsScript() const;
|
bool IsScript() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -400,6 +400,10 @@ std::string HelpMessage(HelpMessageMode mode)
|
|||||||
#endif
|
#endif
|
||||||
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
|
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
|
||||||
|
|
||||||
|
strUsage += HelpMessageOpt("-addressindex", strprintf(_("Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u)"), DEFAULT_ADDRESSINDEX));
|
||||||
|
strUsage += HelpMessageOpt("-timestampindex", strprintf(_("Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u)"), DEFAULT_TIMESTAMPINDEX));
|
||||||
|
strUsage += HelpMessageOpt("-spentindex", strprintf(_("Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u)"), DEFAULT_SPENTINDEX));
|
||||||
|
|
||||||
strUsage += HelpMessageGroup(_("Connection options:"));
|
strUsage += HelpMessageGroup(_("Connection options:"));
|
||||||
strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
|
strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
|
||||||
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
|
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
|
||||||
|
254
src/main.cpp
254
src/main.cpp
@ -75,6 +75,9 @@ int nScriptCheckThreads = 0;
|
|||||||
bool fImporting = false;
|
bool fImporting = false;
|
||||||
bool fReindex = false;
|
bool fReindex = false;
|
||||||
bool fTxIndex = true;
|
bool fTxIndex = true;
|
||||||
|
bool fAddressIndex = false;
|
||||||
|
bool fTimestampIndex = false;
|
||||||
|
bool fSpentIndex = false;
|
||||||
bool fHavePruned = false;
|
bool fHavePruned = false;
|
||||||
bool fPruneMode = false;
|
bool fPruneMode = false;
|
||||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
||||||
@ -1474,6 +1477,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
|||||||
// Store transaction in memory
|
// Store transaction in memory
|
||||||
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
|
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
|
||||||
|
|
||||||
|
// Add memory address index
|
||||||
|
if (fAddressIndex) {
|
||||||
|
pool.addAddressIndex(entry, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add memory spent index
|
||||||
|
if (fSpentIndex) {
|
||||||
|
pool.addSpentIndex(entry, view);
|
||||||
|
}
|
||||||
|
|
||||||
// trim mempool and check if tx was trimmed
|
// trim mempool and check if tx was trimmed
|
||||||
if (!fOverrideMempoolLimit) {
|
if (!fOverrideMempoolLimit) {
|
||||||
LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
||||||
@ -1500,6 +1513,55 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes)
|
||||||
|
{
|
||||||
|
if (!fTimestampIndex)
|
||||||
|
return error("Timestamp index not enabled");
|
||||||
|
|
||||||
|
if (!pblocktree->ReadTimestampIndex(high, low, hashes))
|
||||||
|
return error("Unable to get hashes for timestamps");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value)
|
||||||
|
{
|
||||||
|
if (!fSpentIndex)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mempool.getSpentIndex(key, value))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!pblocktree->ReadSpentIndex(key, value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetAddressIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex, int start, int end)
|
||||||
|
{
|
||||||
|
if (!fAddressIndex)
|
||||||
|
return error("address index not enabled");
|
||||||
|
|
||||||
|
if (!pblocktree->ReadAddressIndex(addressHash, type, addressIndex, start, end))
|
||||||
|
return error("unable to get txids for address");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetAddressUnspent(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs)
|
||||||
|
{
|
||||||
|
if (!fAddressIndex)
|
||||||
|
return error("address index not enabled");
|
||||||
|
|
||||||
|
if (!pblocktree->ReadAddressUnspentIndex(addressHash, type, unspentOutputs))
|
||||||
|
return error("unable to get txids for address");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
|
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
|
||||||
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
|
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
|
||||||
{
|
{
|
||||||
@ -2178,11 +2240,46 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
|
|||||||
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
|
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
|
||||||
return error("DisconnectBlock(): block and undo data inconsistent");
|
return error("DisconnectBlock(): block and undo data inconsistent");
|
||||||
|
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex;
|
||||||
|
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > spentIndex;
|
||||||
|
|
||||||
// undo transactions in reverse order
|
// undo transactions in reverse order
|
||||||
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
|
|
||||||
|
if (fAddressIndex) {
|
||||||
|
|
||||||
|
for (unsigned int k = tx.vout.size(); k-- > 0;) {
|
||||||
|
const CTxOut &out = tx.vout[k];
|
||||||
|
|
||||||
|
if (out.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
||||||
|
|
||||||
|
// undo receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// undo unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
||||||
|
|
||||||
|
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
|
// undo receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// undo unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Check that all outputs are available and match the outputs in the block itself
|
// Check that all outputs are available and match the outputs in the block itself
|
||||||
// exactly.
|
// exactly.
|
||||||
{
|
{
|
||||||
@ -2212,10 +2309,45 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
|
|||||||
const CTxInUndo &undo = txundo.vprevout[j];
|
const CTxInUndo &undo = txundo.vprevout[j];
|
||||||
if (!ApplyTxInUndo(undo, view, out))
|
if (!ApplyTxInUndo(undo, view, out))
|
||||||
fClean = false;
|
fClean = false;
|
||||||
|
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
|
||||||
|
if (fSpentIndex) {
|
||||||
|
// undo and delete the spent index
|
||||||
|
spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fAddressIndex) {
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
|
||||||
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
|
||||||
|
|
||||||
|
// undo spending activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1));
|
||||||
|
|
||||||
|
// restore unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
||||||
|
|
||||||
|
|
||||||
|
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
|
// undo spending activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1));
|
||||||
|
|
||||||
|
// restore unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// move best block pointer to prevout block
|
// move best block pointer to prevout block
|
||||||
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
||||||
|
|
||||||
@ -2224,6 +2356,15 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fAddressIndex) {
|
||||||
|
if (!pblocktree->EraseAddressIndex(addressIndex)) {
|
||||||
|
return AbortNode(state, "Failed to delete address index");
|
||||||
|
}
|
||||||
|
if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) {
|
||||||
|
return AbortNode(state, "Failed to write address unspent index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fClean;
|
return fClean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2484,9 +2625,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||||
vPos.reserve(block.vtx.size());
|
vPos.reserve(block.vtx.size());
|
||||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex;
|
||||||
|
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > spentIndex;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
{
|
{
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
|
const uint256 txhash = tx.GetHash();
|
||||||
|
|
||||||
nInputs += tx.vin.size();
|
nInputs += tx.vin.size();
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
nSigOps += GetLegacySigOpCount(tx);
|
||||||
@ -2513,6 +2659,43 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
REJECT_INVALID, "bad-txns-nonfinal");
|
REJECT_INVALID, "bad-txns-nonfinal");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fAddressIndex || fSpentIndex)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < tx.vin.size(); j++) {
|
||||||
|
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
|
||||||
|
uint160 hashBytes;
|
||||||
|
int addressType;
|
||||||
|
|
||||||
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||||
|
addressType = 2;
|
||||||
|
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
||||||
|
addressType = 1;
|
||||||
|
} else {
|
||||||
|
hashBytes.SetNull();
|
||||||
|
addressType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fAddressIndex && addressType > 0) {
|
||||||
|
// record spending activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(addressType, hashBytes, pindex->nHeight, i, txhash, j, true), prevout.nValue * -1));
|
||||||
|
|
||||||
|
// remove address from unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(addressType, hashBytes, input.prevout.hash, input.prevout.n), CAddressUnspentValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fSpentIndex) {
|
||||||
|
// add the spent index to determine the txid and input that spent an output
|
||||||
|
// and to find the amount and address from an input
|
||||||
|
spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->nHeight, prevout.nValue, addressType, hashBytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (fStrictPayToScriptHash)
|
if (fStrictPayToScriptHash)
|
||||||
{
|
{
|
||||||
// Add in sigops done by pay-to-script-hash inputs;
|
// Add in sigops done by pay-to-script-hash inputs;
|
||||||
@ -2534,6 +2717,35 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
control.Add(vChecks);
|
control.Add(vChecks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fAddressIndex) {
|
||||||
|
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
||||||
|
const CTxOut &out = tx.vout[k];
|
||||||
|
|
||||||
|
if (out.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
||||||
|
|
||||||
|
// record receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(2, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// record unspent output
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
|
||||||
|
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
|
// record receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// record unspent output
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CTxUndo undoDummy;
|
CTxUndo undoDummy;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
blockundo.vtxundo.push_back(CTxUndo());
|
blockundo.vtxundo.push_back(CTxUndo());
|
||||||
@ -2584,6 +2796,24 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
if (!pblocktree->WriteTxIndex(vPos))
|
if (!pblocktree->WriteTxIndex(vPos))
|
||||||
return AbortNode(state, "Failed to write transaction index");
|
return AbortNode(state, "Failed to write transaction index");
|
||||||
|
|
||||||
|
if (fAddressIndex) {
|
||||||
|
if (!pblocktree->WriteAddressIndex(addressIndex)) {
|
||||||
|
return AbortNode(state, "Failed to write address index");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pblocktree->UpdateAddressUnspentIndex(addressUnspentIndex)) {
|
||||||
|
return AbortNode(state, "Failed to write address unspent index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fSpentIndex)
|
||||||
|
if (!pblocktree->UpdateSpentIndex(spentIndex))
|
||||||
|
return AbortNode(state, "Failed to write transaction index");
|
||||||
|
|
||||||
|
if (fTimestampIndex)
|
||||||
|
if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(pindex->nTime, pindex->GetBlockHash())))
|
||||||
|
return AbortNode(state, "Failed to write timestamp index");
|
||||||
|
|
||||||
// add this block to the view's block chain
|
// add this block to the view's block chain
|
||||||
view.SetBestBlock(pindex->GetBlockHash());
|
view.SetBestBlock(pindex->GetBlockHash());
|
||||||
|
|
||||||
@ -4056,6 +4286,18 @@ bool static LoadBlockIndexDB()
|
|||||||
pblocktree->ReadFlag("txindex", fTxIndex);
|
pblocktree->ReadFlag("txindex", fTxIndex);
|
||||||
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
|
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
// Check whether we have an address index
|
||||||
|
pblocktree->ReadFlag("addressindex", fAddressIndex);
|
||||||
|
LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
// Check whether we have a timestamp index
|
||||||
|
pblocktree->ReadFlag("timestampindex", fTimestampIndex);
|
||||||
|
LogPrintf("%s: timestamp index %s\n", __func__, fTimestampIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
// Check whether we have a spent index
|
||||||
|
pblocktree->ReadFlag("spentindex", fSpentIndex);
|
||||||
|
LogPrintf("%s: spent index %s\n", __func__, fSpentIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
// Load pointer to end of best chain
|
// Load pointer to end of best chain
|
||||||
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
|
||||||
if (it == mapBlockIndex.end())
|
if (it == mapBlockIndex.end())
|
||||||
@ -4216,6 +4458,18 @@ bool InitBlockIndex(const CChainParams& chainparams)
|
|||||||
// Use the provided setting for -txindex in the new database
|
// Use the provided setting for -txindex in the new database
|
||||||
fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
|
fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
|
||||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||||
|
|
||||||
|
// Use the provided setting for -addressindex in the new database
|
||||||
|
fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX);
|
||||||
|
pblocktree->WriteFlag("addressindex", fAddressIndex);
|
||||||
|
|
||||||
|
// Use the provided setting for -timestampindex in the new database
|
||||||
|
fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX);
|
||||||
|
pblocktree->WriteFlag("timestampindex", fTimestampIndex);
|
||||||
|
|
||||||
|
fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX);
|
||||||
|
pblocktree->WriteFlag("spentindex", fSpentIndex);
|
||||||
|
|
||||||
LogPrintf("Initializing databases...\n");
|
LogPrintf("Initializing databases...\n");
|
||||||
|
|
||||||
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
|
||||||
|
284
src/main.h
284
src/main.h
@ -18,6 +18,7 @@
|
|||||||
#include "script/script_error.h"
|
#include "script/script_error.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
#include "versionbits.h"
|
#include "versionbits.h"
|
||||||
|
#include "spentindex.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
@ -115,6 +116,9 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
|
|||||||
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
||||||
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
||||||
static const bool DEFAULT_TXINDEX = true;
|
static const bool DEFAULT_TXINDEX = true;
|
||||||
|
static const bool DEFAULT_ADDRESSINDEX = false;
|
||||||
|
static const bool DEFAULT_TIMESTAMPINDEX = false;
|
||||||
|
static const bool DEFAULT_SPENTINDEX = false;
|
||||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||||
|
|
||||||
static const bool DEFAULT_TESTSAFEMODE = false;
|
static const bool DEFAULT_TESTSAFEMODE = false;
|
||||||
@ -310,6 +314,279 @@ struct CNodeStateStats {
|
|||||||
std::vector<int> vHeightInFlight;
|
std::vector<int> vHeightInFlight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CTimestampIndexIteratorKey {
|
||||||
|
unsigned int timestamp;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata32be(s, timestamp);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
timestamp = ser_readdata32be(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTimestampIndexIteratorKey(unsigned int time) {
|
||||||
|
timestamp = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTimestampIndexIteratorKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
timestamp = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CTimestampIndexKey {
|
||||||
|
unsigned int timestamp;
|
||||||
|
uint256 blockHash;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 36;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata32be(s, timestamp);
|
||||||
|
blockHash.Serialize(s, nType, nVersion);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
timestamp = ser_readdata32be(s);
|
||||||
|
blockHash.Unserialize(s, nType, nVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTimestampIndexKey(unsigned int time, uint256 hash) {
|
||||||
|
timestamp = time;
|
||||||
|
blockHash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTimestampIndexKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
timestamp = 0;
|
||||||
|
blockHash.SetNull();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressUnspentKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
uint256 txhash;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 57;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s, nType, nVersion);
|
||||||
|
txhash.Serialize(s, nType, nVersion);
|
||||||
|
ser_writedata32(s, index);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s, nType, nVersion);
|
||||||
|
txhash.Unserialize(s, nType, nVersion);
|
||||||
|
index = ser_readdata32(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentKey(unsigned int addressType, uint160 addressHash, uint256 txid, size_t indexValue) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
txhash = txid;
|
||||||
|
index = indexValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
txhash.SetNull();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressUnspentValue {
|
||||||
|
CAmount satoshis;
|
||||||
|
CScript script;
|
||||||
|
int blockHeight;
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
|
READWRITE(satoshis);
|
||||||
|
READWRITE(*(CScriptBase*)(&script));
|
||||||
|
READWRITE(blockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentValue(CAmount sats, CScript scriptPubKey, int height) {
|
||||||
|
satoshis = sats;
|
||||||
|
script = scriptPubKey;
|
||||||
|
blockHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressUnspentValue() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
satoshis = -1;
|
||||||
|
script.clear();
|
||||||
|
blockHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNull() const {
|
||||||
|
return (satoshis == -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
int blockHeight;
|
||||||
|
unsigned int txindex;
|
||||||
|
uint256 txhash;
|
||||||
|
size_t index;
|
||||||
|
bool spending;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 66;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s, nType, nVersion);
|
||||||
|
// Heights are stored big-endian for key sorting in LevelDB
|
||||||
|
ser_writedata32be(s, blockHeight);
|
||||||
|
ser_writedata32be(s, txindex);
|
||||||
|
txhash.Serialize(s, nType, nVersion);
|
||||||
|
ser_writedata32(s, index);
|
||||||
|
char f = spending;
|
||||||
|
ser_writedata8(s, f);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s, nType, nVersion);
|
||||||
|
blockHeight = ser_readdata32be(s);
|
||||||
|
txindex = ser_readdata32be(s);
|
||||||
|
txhash.Unserialize(s, nType, nVersion);
|
||||||
|
index = ser_readdata32(s);
|
||||||
|
char f = ser_readdata8(s);
|
||||||
|
spending = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexKey(unsigned int addressType, uint160 addressHash, int height, int blockindex,
|
||||||
|
uint256 txid, size_t indexValue, bool isSpending) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
blockHeight = height;
|
||||||
|
txindex = blockindex;
|
||||||
|
txhash = txid;
|
||||||
|
index = indexValue;
|
||||||
|
spending = isSpending;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
blockHeight = 0;
|
||||||
|
txindex = 0;
|
||||||
|
txhash.SetNull();
|
||||||
|
index = 0;
|
||||||
|
spending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexIteratorKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s, nType, nVersion);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s, nType, nVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorKey(unsigned int addressType, uint160 addressHash) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CAddressIndexIteratorHeightKey {
|
||||||
|
unsigned int type;
|
||||||
|
uint160 hashBytes;
|
||||||
|
int blockHeight;
|
||||||
|
|
||||||
|
size_t GetSerializeSize(int nType, int nVersion) const {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Serialize(Stream& s, int nType, int nVersion) const {
|
||||||
|
ser_writedata8(s, type);
|
||||||
|
hashBytes.Serialize(s, nType, nVersion);
|
||||||
|
ser_writedata32be(s, blockHeight);
|
||||||
|
}
|
||||||
|
template<typename Stream>
|
||||||
|
void Unserialize(Stream& s, int nType, int nVersion) {
|
||||||
|
type = ser_readdata8(s);
|
||||||
|
hashBytes.Unserialize(s, nType, nVersion);
|
||||||
|
blockHeight = ser_readdata32be(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorHeightKey(unsigned int addressType, uint160 addressHash, int height) {
|
||||||
|
type = addressType;
|
||||||
|
hashBytes = addressHash;
|
||||||
|
blockHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAddressIndexIteratorHeightKey() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
type = 0;
|
||||||
|
hashBytes.SetNull();
|
||||||
|
blockHeight = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct CDiskTxPos : public CDiskBlockPos
|
struct CDiskTxPos : public CDiskBlockPos
|
||||||
{
|
{
|
||||||
unsigned int nTxOffset; // after header
|
unsigned int nTxOffset; // after header
|
||||||
@ -440,6 +717,13 @@ public:
|
|||||||
ScriptError GetScriptError() const { return error; }
|
ScriptError GetScriptError() const { return error; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes);
|
||||||
|
bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||||
|
bool GetAddressIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
|
||||||
|
int start = 0, int end = 0);
|
||||||
|
bool GetAddressUnspent(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs);
|
||||||
|
|
||||||
/** Functions for disk access for blocks */
|
/** Functions for disk access for blocks */
|
||||||
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
|
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
|
||||||
|
@ -276,6 +276,40 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
|
|||||||
return mempoolToJSON(fVerbose);
|
return mempoolToJSON(fVerbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue getblockhashes(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 2)
|
||||||
|
throw runtime_error(
|
||||||
|
"getblockhashes timestamp\n"
|
||||||
|
"\nReturns array of hashes of blocks within the timestamp range provided.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. high (numeric, required) The newer block timestamp\n"
|
||||||
|
"2. low (numeric, required) The older block timestamp\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"[\n"
|
||||||
|
" \"hash\" (string) The block hash\n"
|
||||||
|
"]\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getblockhashes", "1231614698 1231024505")
|
||||||
|
+ HelpExampleRpc("getblockhashes", "1231614698, 1231024505")
|
||||||
|
);
|
||||||
|
|
||||||
|
unsigned int high = params[0].get_int();
|
||||||
|
unsigned int low = params[1].get_int();
|
||||||
|
std::vector<uint256> blockHashes;
|
||||||
|
|
||||||
|
if (!GetTimestampIndex(high, low, blockHashes)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
for (std::vector<uint256>::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) {
|
||||||
|
result.push_back(it->GetHex());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
UniValue getblockhash(const UniValue& params, bool fHelp)
|
UniValue getblockhash(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
|
@ -115,6 +115,14 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "spork", 1 },
|
{ "spork", 1 },
|
||||||
{ "voteraw", 1 },
|
{ "voteraw", 1 },
|
||||||
{ "voteraw", 4 },
|
{ "voteraw", 4 },
|
||||||
|
{ "getblockhashes", 0 },
|
||||||
|
{ "getblockhashes", 1 },
|
||||||
|
{ "getspentinfo", 0},
|
||||||
|
{ "getaddresstxids", 0},
|
||||||
|
{ "getaddressbalance", 0},
|
||||||
|
{ "getaddressdeltas", 0},
|
||||||
|
{ "getaddressutxos", 0},
|
||||||
|
{ "getaddressmempool", 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
class CRPCConvertTable
|
class CRPCConvertTable
|
||||||
|
471
src/rpcmisc.cpp
471
src/rpcmisc.cpp
@ -12,6 +12,7 @@
|
|||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
#include "rpcserver.h"
|
#include "rpcserver.h"
|
||||||
#include "timedata.h"
|
#include "timedata.h"
|
||||||
|
#include "txmempool.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "spork.h"
|
#include "spork.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
@ -521,3 +522,473 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &address)
|
||||||
|
{
|
||||||
|
if (type == 2) {
|
||||||
|
address = CBitcoinAddress(CScriptID(hash)).ToString();
|
||||||
|
} else if (type == 1) {
|
||||||
|
address = CBitcoinAddress(CKeyID(hash)).ToString();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getAddressesFromParams(const UniValue& params, std::vector<std::pair<uint160, int> > &addresses)
|
||||||
|
{
|
||||||
|
if (params[0].isStr()) {
|
||||||
|
CBitcoinAddress address(params[0].get_str());
|
||||||
|
uint160 hashBytes;
|
||||||
|
int type = 0;
|
||||||
|
if (!address.GetIndexKey(hashBytes, type)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
addresses.push_back(std::make_pair(hashBytes, type));
|
||||||
|
} else if (params[0].isObject()) {
|
||||||
|
|
||||||
|
UniValue addressValues = find_value(params[0].get_obj(), "addresses");
|
||||||
|
if (!addressValues.isArray()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UniValue> values = addressValues.getValues();
|
||||||
|
|
||||||
|
for (std::vector<UniValue>::iterator it = values.begin(); it != values.end(); ++it) {
|
||||||
|
|
||||||
|
CBitcoinAddress address(it->get_str());
|
||||||
|
uint160 hashBytes;
|
||||||
|
int type = 0;
|
||||||
|
if (!address.GetIndexKey(hashBytes, type)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
addresses.push_back(std::make_pair(hashBytes, type));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool heightSort(std::pair<CAddressUnspentKey, CAddressUnspentValue> a,
|
||||||
|
std::pair<CAddressUnspentKey, CAddressUnspentValue> b) {
|
||||||
|
return a.second.blockHeight < b.second.blockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timestampSort(std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> a,
|
||||||
|
std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> b) {
|
||||||
|
return a.second.time < b.second.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getaddressmempool(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"getaddressmempool\n"
|
||||||
|
"\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"addresses\"\n"
|
||||||
|
" [\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" ,...\n"
|
||||||
|
" ]\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"[\n"
|
||||||
|
" {\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" \"txid\" (string) The related txid\n"
|
||||||
|
" \"index\" (number) The related input or output index\n"
|
||||||
|
" \"satoshis\" (number) The difference of satoshis\n"
|
||||||
|
" \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n"
|
||||||
|
" \"prevtxid\" (string) The previous txid (if spending)\n"
|
||||||
|
" \"prevout\" (string) The previous transaction output index (if spending)\n"
|
||||||
|
" }\n"
|
||||||
|
"]\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'")
|
||||||
|
+ HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::pair<uint160, int> > addresses;
|
||||||
|
|
||||||
|
if (!getAddressesFromParams(params, addresses)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > indexes;
|
||||||
|
|
||||||
|
if (!mempool.getAddressIndex(addresses, indexes)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(indexes.begin(), indexes.end(), timestampSort);
|
||||||
|
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
|
||||||
|
for (std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> >::iterator it = indexes.begin();
|
||||||
|
it != indexes.end(); it++) {
|
||||||
|
|
||||||
|
std::string address;
|
||||||
|
if (!getAddressFromIndex(it->first.type, it->first.addressBytes, address)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue delta(UniValue::VOBJ);
|
||||||
|
delta.push_back(Pair("address", address));
|
||||||
|
delta.push_back(Pair("txid", it->first.txhash.GetHex()));
|
||||||
|
delta.push_back(Pair("index", (int)it->first.index));
|
||||||
|
delta.push_back(Pair("satoshis", it->second.amount));
|
||||||
|
delta.push_back(Pair("timestamp", it->second.time));
|
||||||
|
if (it->second.amount < 0) {
|
||||||
|
delta.push_back(Pair("prevtxid", it->second.prevhash.GetHex()));
|
||||||
|
delta.push_back(Pair("prevout", (int)it->second.prevout));
|
||||||
|
}
|
||||||
|
result.push_back(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getaddressutxos(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"getaddressutxos\n"
|
||||||
|
"\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"addresses\"\n"
|
||||||
|
" [\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" ,...\n"
|
||||||
|
" ]\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult\n"
|
||||||
|
"[\n"
|
||||||
|
" {\n"
|
||||||
|
" \"address\" (string) The address base58check encoded\n"
|
||||||
|
" \"txid\" (string) The output txid\n"
|
||||||
|
" \"height\" (number) The block height\n"
|
||||||
|
" \"outputIndex\" (number) The output index\n"
|
||||||
|
" \"script\" (strin) The script hex encoded\n"
|
||||||
|
" \"satoshis\" (number) The number of satoshis of the output\n"
|
||||||
|
" }\n"
|
||||||
|
"]\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'")
|
||||||
|
+ HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::pair<uint160, int> > addresses;
|
||||||
|
|
||||||
|
if (!getAddressesFromParams(params, addresses)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
||||||
|
if (!GetAddressUnspent((*it).first, (*it).second, unspentOutputs)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(unspentOutputs.begin(), unspentOutputs.end(), heightSort);
|
||||||
|
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
|
||||||
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) {
|
||||||
|
UniValue output(UniValue::VOBJ);
|
||||||
|
std::string address;
|
||||||
|
if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_back(Pair("address", address));
|
||||||
|
output.push_back(Pair("txid", it->first.txhash.GetHex()));
|
||||||
|
output.push_back(Pair("outputIndex", (int)it->first.index));
|
||||||
|
output.push_back(Pair("script", HexStr(it->second.script.begin(), it->second.script.end())));
|
||||||
|
output.push_back(Pair("satoshis", it->second.satoshis));
|
||||||
|
output.push_back(Pair("height", it->second.blockHeight));
|
||||||
|
result.push_back(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getaddressdeltas(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1 || !params[0].isObject())
|
||||||
|
throw runtime_error(
|
||||||
|
"getaddressdeltas\n"
|
||||||
|
"\nReturns all changes for an address (requires addressindex to be enabled).\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"addresses\"\n"
|
||||||
|
" [\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" ,...\n"
|
||||||
|
" ]\n"
|
||||||
|
" \"start\" (number) The start block height\n"
|
||||||
|
" \"end\" (number) The end block height\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"[\n"
|
||||||
|
" {\n"
|
||||||
|
" \"satoshis\" (number) The difference of satoshis\n"
|
||||||
|
" \"txid\" (string) The related txid\n"
|
||||||
|
" \"index\" (number) The related input or output index\n"
|
||||||
|
" \"height\" (number) The block height\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" }\n"
|
||||||
|
"]\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'")
|
||||||
|
+ HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
UniValue startValue = find_value(params[0].get_obj(), "start");
|
||||||
|
UniValue endValue = find_value(params[0].get_obj(), "end");
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
|
||||||
|
if (startValue.isNum() && endValue.isNum()) {
|
||||||
|
start = startValue.get_int();
|
||||||
|
end = endValue.get_int();
|
||||||
|
if (end < start) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<uint160, int> > addresses;
|
||||||
|
|
||||||
|
if (!getAddressesFromParams(params, addresses)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
||||||
|
if (start > 0 && end > 0) {
|
||||||
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
|
||||||
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
||||||
|
std::string address;
|
||||||
|
if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue delta(UniValue::VOBJ);
|
||||||
|
delta.push_back(Pair("satoshis", it->second));
|
||||||
|
delta.push_back(Pair("txid", it->first.txhash.GetHex()));
|
||||||
|
delta.push_back(Pair("index", (int)it->first.index));
|
||||||
|
delta.push_back(Pair("blockindex", (int)it->first.txindex));
|
||||||
|
delta.push_back(Pair("height", it->first.blockHeight));
|
||||||
|
delta.push_back(Pair("address", address));
|
||||||
|
result.push_back(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getaddressbalance(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"getaddressbalance\n"
|
||||||
|
"\nReturns the balance for an address(es) (requires addressindex to be enabled).\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"addresses\"\n"
|
||||||
|
" [\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" ,...\n"
|
||||||
|
" ]\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"balance\" (string) The current balance in satoshis\n"
|
||||||
|
" \"received\" (string) The total number of satoshis received (including change)\n"
|
||||||
|
"}\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'")
|
||||||
|
+ HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::pair<uint160, int> > addresses;
|
||||||
|
|
||||||
|
if (!getAddressesFromParams(params, addresses)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
||||||
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CAmount balance = 0;
|
||||||
|
CAmount received = 0;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
||||||
|
if (it->second > 0) {
|
||||||
|
received += it->second;
|
||||||
|
}
|
||||||
|
balance += it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue result(UniValue::VOBJ);
|
||||||
|
result.push_back(Pair("balance", balance));
|
||||||
|
result.push_back(Pair("received", received));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getaddresstxids(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"getaddresstxids\n"
|
||||||
|
"\nReturns the txids for an address(es) (requires addressindex to be enabled).\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"addresses\"\n"
|
||||||
|
" [\n"
|
||||||
|
" \"address\" (string) The base58check encoded address\n"
|
||||||
|
" ,...\n"
|
||||||
|
" ]\n"
|
||||||
|
" \"start\" (number) The start block height\n"
|
||||||
|
" \"end\" (number) The end block height\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"[\n"
|
||||||
|
" \"transactionid\" (string) The transaction id\n"
|
||||||
|
" ,...\n"
|
||||||
|
"]\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}'")
|
||||||
|
+ HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX\"]}")
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::pair<uint160, int> > addresses;
|
||||||
|
|
||||||
|
if (!getAddressesFromParams(params, addresses)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
if (params[0].isObject()) {
|
||||||
|
UniValue startValue = find_value(params[0].get_obj(), "start");
|
||||||
|
UniValue endValue = find_value(params[0].get_obj(), "end");
|
||||||
|
if (startValue.isNum() && endValue.isNum()) {
|
||||||
|
start = startValue.get_int();
|
||||||
|
end = endValue.get_int();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
||||||
|
if (start > 0 && end > 0) {
|
||||||
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex, start, end)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!GetAddressIndex((*it).first, (*it).second, addressIndex)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::pair<int, std::string> > txids;
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
|
||||||
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) {
|
||||||
|
int height = it->first.blockHeight;
|
||||||
|
std::string txid = it->first.txhash.GetHex();
|
||||||
|
|
||||||
|
if (addresses.size() > 1) {
|
||||||
|
txids.insert(std::make_pair(height, txid));
|
||||||
|
} else {
|
||||||
|
if (txids.insert(std::make_pair(height, txid)).second) {
|
||||||
|
result.push_back(txid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addresses.size() > 1) {
|
||||||
|
for (std::set<std::pair<int, std::string> >::const_iterator it=txids.begin(); it!=txids.end(); it++) {
|
||||||
|
result.push_back(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue getspentinfo(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (fHelp || params.size() != 1 || !params[0].isObject())
|
||||||
|
throw runtime_error(
|
||||||
|
"getspentinfo\n"
|
||||||
|
"\nReturns the txid and index where an output is spent.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"txid\" (string) The hex string of the txid\n"
|
||||||
|
" \"index\" (number) The start block height\n"
|
||||||
|
"}\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"txid\" (string) The transaction id\n"
|
||||||
|
" \"index\" (number) The spending input index\n"
|
||||||
|
" ,...\n"
|
||||||
|
"}\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'")
|
||||||
|
+ HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}")
|
||||||
|
);
|
||||||
|
|
||||||
|
UniValue txidValue = find_value(params[0].get_obj(), "txid");
|
||||||
|
UniValue indexValue = find_value(params[0].get_obj(), "index");
|
||||||
|
|
||||||
|
if (!txidValue.isStr() || !indexValue.isNum()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid or index");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 txid = ParseHashV(txidValue, "txid");
|
||||||
|
int outputIndex = indexValue.get_int();
|
||||||
|
|
||||||
|
CSpentIndexKey key(txid, outputIndex);
|
||||||
|
CSpentIndexValue value;
|
||||||
|
|
||||||
|
if (!GetSpentIndex(key, value)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
obj.push_back(Pair("txid", value.txid.GetHex()));
|
||||||
|
obj.push_back(Pair("index", (int)value.inputIndex));
|
||||||
|
obj.push_back(Pair("height", value.blockHeight));
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@ -62,7 +62,8 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud
|
|||||||
|
|
||||||
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
||||||
{
|
{
|
||||||
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
|
uint256 txid = tx.GetHash();
|
||||||
|
entry.push_back(Pair("txid", txid.GetHex()));
|
||||||
entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
|
entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
|
||||||
entry.push_back(Pair("version", tx.nVersion));
|
entry.push_back(Pair("version", tx.nVersion));
|
||||||
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
|
||||||
@ -78,6 +79,20 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
|||||||
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
|
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
|
||||||
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||||
in.push_back(Pair("scriptSig", o));
|
in.push_back(Pair("scriptSig", o));
|
||||||
|
|
||||||
|
// Add address and value info if spentindex enabled
|
||||||
|
CSpentIndexValue spentInfo;
|
||||||
|
CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n);
|
||||||
|
if (GetSpentIndex(spentKey, spentInfo)) {
|
||||||
|
in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis)));
|
||||||
|
in.push_back(Pair("valueSat", spentInfo.satoshis));
|
||||||
|
if (spentInfo.addressType == 1) {
|
||||||
|
in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
|
||||||
|
} else if (spentInfo.addressType == 2) {
|
||||||
|
in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
in.push_back(Pair("sequence", (int64_t)txin.nSequence));
|
in.push_back(Pair("sequence", (int64_t)txin.nSequence));
|
||||||
vin.push_back(in);
|
vin.push_back(in);
|
||||||
@ -88,10 +103,21 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
|||||||
const CTxOut& txout = tx.vout[i];
|
const CTxOut& txout = tx.vout[i];
|
||||||
UniValue out(UniValue::VOBJ);
|
UniValue out(UniValue::VOBJ);
|
||||||
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
|
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
|
||||||
|
out.push_back(Pair("valueSat", txout.nValue));
|
||||||
out.push_back(Pair("n", (int64_t)i));
|
out.push_back(Pair("n", (int64_t)i));
|
||||||
UniValue o(UniValue::VOBJ);
|
UniValue o(UniValue::VOBJ);
|
||||||
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
|
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
|
||||||
out.push_back(Pair("scriptPubKey", o));
|
out.push_back(Pair("scriptPubKey", o));
|
||||||
|
|
||||||
|
// Add spent information if spentindex is enabled
|
||||||
|
CSpentIndexValue spentInfo;
|
||||||
|
CSpentIndexKey spentKey(txid, i);
|
||||||
|
if (GetSpentIndex(spentKey, spentInfo)) {
|
||||||
|
out.push_back(Pair("spentTxId", spentInfo.txid.GetHex()));
|
||||||
|
out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex));
|
||||||
|
out.push_back(Pair("spentHeight", spentInfo.blockHeight));
|
||||||
|
}
|
||||||
|
|
||||||
vout.push_back(out);
|
vout.push_back(out);
|
||||||
}
|
}
|
||||||
entry.push_back(Pair("vout", vout));
|
entry.push_back(Pair("vout", vout));
|
||||||
@ -102,15 +128,17 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
|
|||||||
if (mi != mapBlockIndex.end() && (*mi).second) {
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
||||||
CBlockIndex* pindex = (*mi).second;
|
CBlockIndex* pindex = (*mi).second;
|
||||||
if (chainActive.Contains(pindex)) {
|
if (chainActive.Contains(pindex)) {
|
||||||
|
entry.push_back(Pair("height", pindex->nHeight));
|
||||||
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
|
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
|
||||||
entry.push_back(Pair("time", pindex->GetBlockTime()));
|
entry.push_back(Pair("time", pindex->GetBlockTime()));
|
||||||
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
|
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
|
||||||
}
|
} else {
|
||||||
else
|
entry.push_back(Pair("height", -1));
|
||||||
entry.push_back(Pair("confirmations", 0));
|
entry.push_back(Pair("confirmations", 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UniValue getrawtransaction(const UniValue& params, bool fHelp)
|
UniValue getrawtransaction(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
|
@ -280,6 +280,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||||||
{ "blockchain", "getbestblockhash", &getbestblockhash, true },
|
{ "blockchain", "getbestblockhash", &getbestblockhash, true },
|
||||||
{ "blockchain", "getblockcount", &getblockcount, true },
|
{ "blockchain", "getblockcount", &getblockcount, true },
|
||||||
{ "blockchain", "getblock", &getblock, true },
|
{ "blockchain", "getblock", &getblock, true },
|
||||||
|
{ "blockchain", "getblockhashes", &getblockhashes, true },
|
||||||
{ "blockchain", "getblockhash", &getblockhash, true },
|
{ "blockchain", "getblockhash", &getblockhash, true },
|
||||||
{ "blockchain", "getblockheader", &getblockheader, true },
|
{ "blockchain", "getblockheader", &getblockheader, true },
|
||||||
{ "blockchain", "getblockheaders", &getblockheaders, true },
|
{ "blockchain", "getblockheaders", &getblockheaders, true },
|
||||||
@ -292,8 +293,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||||||
{ "blockchain", "verifytxoutproof", &verifytxoutproof, true },
|
{ "blockchain", "verifytxoutproof", &verifytxoutproof, true },
|
||||||
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
|
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
|
||||||
{ "blockchain", "verifychain", &verifychain, true },
|
{ "blockchain", "verifychain", &verifychain, true },
|
||||||
{ "blockchain", "invalidateblock", &invalidateblock, true },
|
{ "blockchain", "getspentinfo", &getspentinfo, false },
|
||||||
{ "blockchain", "reconsiderblock", &reconsiderblock, true },
|
|
||||||
|
|
||||||
/* Mining */
|
/* Mining */
|
||||||
{ "mining", "getblocktemplate", &getblocktemplate, true },
|
{ "mining", "getblocktemplate", &getblocktemplate, true },
|
||||||
@ -318,6 +318,13 @@ static const CRPCCommand vRPCCommands[] =
|
|||||||
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
|
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Address index */
|
||||||
|
{ "addressindex", "getaddressmempool", &getaddressmempool, true },
|
||||||
|
{ "addressindex", "getaddressutxos", &getaddressutxos, false },
|
||||||
|
{ "addressindex", "getaddressdeltas", &getaddressdeltas, false },
|
||||||
|
{ "addressindex", "getaddresstxids", &getaddresstxids, false },
|
||||||
|
{ "addressindex", "getaddressbalance", &getaddressbalance, false },
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
{ "util", "createmultisig", &createmultisig, true },
|
{ "util", "createmultisig", &createmultisig, true },
|
||||||
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
|
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
|
||||||
|
@ -172,6 +172,12 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri
|
|||||||
extern void EnsureWalletIsUnlocked();
|
extern void EnsureWalletIsUnlocked();
|
||||||
|
|
||||||
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
|
extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp
|
||||||
|
extern UniValue getaddressmempool(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getaddressutxos(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getaddressdeltas(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getaddresstxids(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getaddressbalance(const UniValue& params, bool fHelp);
|
||||||
|
|
||||||
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
|
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
|
||||||
extern UniValue ping(const UniValue& params, bool fHelp);
|
extern UniValue ping(const UniValue& params, bool fHelp);
|
||||||
extern UniValue addnode(const UniValue& params, bool fHelp);
|
extern UniValue addnode(const UniValue& params, bool fHelp);
|
||||||
@ -274,6 +280,7 @@ extern UniValue getdifficulty(const UniValue& params, bool fHelp);
|
|||||||
extern UniValue settxfee(const UniValue& params, bool fHelp);
|
extern UniValue settxfee(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
|
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getblockhashes(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getblockhash(const UniValue& params, bool fHelp);
|
extern UniValue getblockhash(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
||||||
extern UniValue getblockheaders(const UniValue& params, bool fHelp);
|
extern UniValue getblockheaders(const UniValue& params, bool fHelp);
|
||||||
@ -284,6 +291,7 @@ extern UniValue verifychain(const UniValue& params, bool fHelp);
|
|||||||
extern UniValue getchaintips(const UniValue& params, bool fHelp);
|
extern UniValue getchaintips(const UniValue& params, bool fHelp);
|
||||||
extern UniValue invalidateblock(const UniValue& params, bool fHelp);
|
extern UniValue invalidateblock(const UniValue& params, bool fHelp);
|
||||||
extern UniValue reconsiderblock(const UniValue& params, bool fHelp);
|
extern UniValue reconsiderblock(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue getspentinfo(const UniValue& params, bool fHelp);
|
||||||
|
|
||||||
bool StartRPC();
|
bool StartRPC();
|
||||||
void InterruptRPC();
|
void InterruptRPC();
|
||||||
|
@ -224,6 +224,17 @@ bool CScript::IsNormalPaymentScript() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::IsPayToPublicKeyHash() const
|
||||||
|
{
|
||||||
|
// Extra-fast test for pay-to-pubkey-hash CScripts:
|
||||||
|
return (this->size() == 25 &&
|
||||||
|
(*this)[0] == OP_DUP &&
|
||||||
|
(*this)[1] == OP_HASH160 &&
|
||||||
|
(*this)[2] == 0x14 &&
|
||||||
|
(*this)[23] == OP_EQUALVERIFY &&
|
||||||
|
(*this)[24] == OP_CHECKSIG);
|
||||||
|
}
|
||||||
|
|
||||||
bool CScript::IsPayToScriptHash() const
|
bool CScript::IsPayToScriptHash() const
|
||||||
{
|
{
|
||||||
// Extra-fast test for pay-to-script-hash CScripts:
|
// Extra-fast test for pay-to-script-hash CScripts:
|
||||||
|
@ -609,6 +609,8 @@ public:
|
|||||||
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
||||||
|
|
||||||
bool IsNormalPaymentScript() const;
|
bool IsNormalPaymentScript() const;
|
||||||
|
bool IsPayToPublicKeyHash() const;
|
||||||
|
|
||||||
bool IsPayToScriptHash() const;
|
bool IsPayToScriptHash() const;
|
||||||
|
|
||||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||||
|
@ -91,6 +91,11 @@ template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
|
|||||||
obj = htole32(obj);
|
obj = htole32(obj);
|
||||||
s.write((char*)&obj, 4);
|
s.write((char*)&obj, 4);
|
||||||
}
|
}
|
||||||
|
template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
|
||||||
|
{
|
||||||
|
obj = htobe32(obj);
|
||||||
|
s.write((char*)&obj, 4);
|
||||||
|
}
|
||||||
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
|
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
|
||||||
{
|
{
|
||||||
obj = htole64(obj);
|
obj = htole64(obj);
|
||||||
@ -114,6 +119,12 @@ template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
|
|||||||
s.read((char*)&obj, 4);
|
s.read((char*)&obj, 4);
|
||||||
return le32toh(obj);
|
return le32toh(obj);
|
||||||
}
|
}
|
||||||
|
template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
|
||||||
|
{
|
||||||
|
uint32_t obj;
|
||||||
|
s.read((char*)&obj, 4);
|
||||||
|
return be32toh(obj);
|
||||||
|
}
|
||||||
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
||||||
{
|
{
|
||||||
uint64_t obj;
|
uint64_t obj;
|
||||||
|
98
src/spentindex.h
Normal file
98
src/spentindex.h
Normal file
@ -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
|
59
src/test/script_P2PKH_tests.cpp
Normal file
59
src/test/script_P2PKH_tests.cpp
Normal file
@ -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()
|
132
src/txdb.cpp
132
src/txdb.cpp
@ -21,6 +21,10 @@ using namespace std;
|
|||||||
static const char DB_COINS = 'c';
|
static const char DB_COINS = 'c';
|
||||||
static const char DB_BLOCK_FILES = 'f';
|
static const char DB_BLOCK_FILES = 'f';
|
||||||
static const char DB_TXINDEX = 't';
|
static const char DB_TXINDEX = 't';
|
||||||
|
static const char DB_ADDRESSINDEX = 'a';
|
||||||
|
static const char DB_ADDRESSUNSPENTINDEX = 'u';
|
||||||
|
static const char DB_TIMESTAMPINDEX = 's';
|
||||||
|
static const char DB_SPENTINDEX = 'p';
|
||||||
static const char DB_BLOCK_INDEX = 'b';
|
static const char DB_BLOCK_INDEX = 'b';
|
||||||
|
|
||||||
static const char DB_BEST_BLOCK = 'B';
|
static const char DB_BEST_BLOCK = 'B';
|
||||||
@ -163,6 +167,134 @@ bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>
|
|||||||
return WriteBatch(batch);
|
return WriteBatch(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) {
|
||||||
|
return Read(make_pair(DB_SPENTINDEX, key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::UpdateSpentIndex(const std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> >&vect) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
for (std::vector<std::pair<CSpentIndexKey,CSpentIndexValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) {
|
||||||
|
if (it->second.IsNull()) {
|
||||||
|
batch.Erase(make_pair(DB_SPENTINDEX, it->first));
|
||||||
|
} else {
|
||||||
|
batch.Write(make_pair(DB_SPENTINDEX, it->first), it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue > >&vect) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) {
|
||||||
|
if (it->second.IsNull()) {
|
||||||
|
batch.Erase(make_pair(DB_ADDRESSUNSPENTINDEX, it->first));
|
||||||
|
} else {
|
||||||
|
batch.Write(make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) {
|
||||||
|
|
||||||
|
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash)));
|
||||||
|
|
||||||
|
while (pcursor->Valid()) {
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
std::pair<char,CAddressUnspentKey> key;
|
||||||
|
if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash) {
|
||||||
|
CAddressUnspentValue nValue;
|
||||||
|
if (pcursor->GetValue(nValue)) {
|
||||||
|
unspentOutputs.push_back(make_pair(key.second, nValue));
|
||||||
|
pcursor->Next();
|
||||||
|
} else {
|
||||||
|
return error("failed to get address unspent value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
|
||||||
|
batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second);
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::EraseAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
|
||||||
|
batch.Erase(make_pair(DB_ADDRESSINDEX, it->first));
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
|
||||||
|
int start, int end) {
|
||||||
|
|
||||||
|
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
||||||
|
if (start > 0 && end > 0) {
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start)));
|
||||||
|
} else {
|
||||||
|
pcursor->Seek(make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (pcursor->Valid()) {
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
std::pair<char,CAddressIndexKey> key;
|
||||||
|
if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash) {
|
||||||
|
if (end > 0 && key.second.blockHeight > end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CAmount nValue;
|
||||||
|
if (pcursor->GetValue(nValue)) {
|
||||||
|
addressIndex.push_back(make_pair(key.second, nValue));
|
||||||
|
pcursor->Next();
|
||||||
|
} else {
|
||||||
|
return error("failed to get address index value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) {
|
||||||
|
CDBBatch batch(&GetObfuscateKey());
|
||||||
|
batch.Write(make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0);
|
||||||
|
return WriteBatch(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &hashes) {
|
||||||
|
|
||||||
|
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
||||||
|
pcursor->Seek(make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low)));
|
||||||
|
|
||||||
|
while (pcursor->Valid()) {
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
std::pair<char, CTimestampIndexKey> key;
|
||||||
|
if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp <= high) {
|
||||||
|
hashes.push_back(key.second.blockHash);
|
||||||
|
pcursor->Next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
||||||
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
||||||
}
|
}
|
||||||
|
21
src/txdb.h
21
src/txdb.h
@ -17,6 +17,15 @@
|
|||||||
class CBlockFileInfo;
|
class CBlockFileInfo;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
struct CDiskTxPos;
|
struct CDiskTxPos;
|
||||||
|
struct CAddressUnspentKey;
|
||||||
|
struct CAddressUnspentValue;
|
||||||
|
struct CAddressIndexKey;
|
||||||
|
struct CAddressIndexIteratorKey;
|
||||||
|
struct CAddressIndexIteratorHeightKey;
|
||||||
|
struct CTimestampIndexKey;
|
||||||
|
struct CTimestampIndexIteratorKey;
|
||||||
|
struct CSpentIndexKey;
|
||||||
|
struct CSpentIndexValue;
|
||||||
class uint256;
|
class uint256;
|
||||||
|
|
||||||
//! -dbcache default (MiB)
|
//! -dbcache default (MiB)
|
||||||
@ -57,6 +66,18 @@ public:
|
|||||||
bool ReadReindexing(bool &fReindex);
|
bool ReadReindexing(bool &fReindex);
|
||||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||||
|
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||||
|
bool UpdateSpentIndex(const std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> >&vect);
|
||||||
|
bool UpdateAddressUnspentIndex(const std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue > >&vect);
|
||||||
|
bool ReadAddressUnspentIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &vect);
|
||||||
|
bool WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
||||||
|
bool EraseAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount> > &vect);
|
||||||
|
bool ReadAddressIndex(uint160 addressHash, int type,
|
||||||
|
std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
|
||||||
|
int start = 0, int end = 0);
|
||||||
|
bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex);
|
||||||
|
bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector<uint256> &vect);
|
||||||
bool WriteFlag(const std::string &name, bool fValue);
|
bool WriteFlag(const std::string &name, bool fValue);
|
||||||
bool ReadFlag(const std::string &name, bool &fValue);
|
bool ReadFlag(const std::string &name, bool &fValue);
|
||||||
bool LoadBlockIndexGuts();
|
bool LoadBlockIndexGuts();
|
||||||
|
@ -422,6 +422,145 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
const CTransaction& tx = entry.GetTx();
|
||||||
|
std::vector<CMempoolAddressDeltaKey> inserted;
|
||||||
|
|
||||||
|
uint256 txhash = tx.GetHash();
|
||||||
|
for (unsigned int j = 0; j < tx.vin.size(); j++) {
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(input);
|
||||||
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
|
||||||
|
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1);
|
||||||
|
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
||||||
|
mapAddress.insert(make_pair(key, delta));
|
||||||
|
inserted.push_back(key);
|
||||||
|
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
||||||
|
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
|
||||||
|
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
||||||
|
mapAddress.insert(make_pair(key, delta));
|
||||||
|
inserted.push_back(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
||||||
|
const CTxOut &out = tx.vout[k];
|
||||||
|
if (out.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
||||||
|
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0);
|
||||||
|
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
||||||
|
inserted.push_back(key);
|
||||||
|
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
|
std::pair<addressDeltaMap::iterator,bool> ret;
|
||||||
|
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0);
|
||||||
|
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
||||||
|
inserted.push_back(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mapAddressInserted.insert(make_pair(txhash, inserted));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
|
||||||
|
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++) {
|
||||||
|
addressDeltaMap::iterator ait = mapAddress.lower_bound(CMempoolAddressDeltaKey((*it).second, (*it).first));
|
||||||
|
while (ait != mapAddress.end() && (*ait).first.addressBytes == (*it).first && (*ait).first.type == (*it).second) {
|
||||||
|
results.push_back(*ait);
|
||||||
|
ait++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::removeAddressIndex(const uint256 txhash)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
addressDeltaMapInserted::iterator it = mapAddressInserted.find(txhash);
|
||||||
|
|
||||||
|
if (it != mapAddressInserted.end()) {
|
||||||
|
std::vector<CMempoolAddressDeltaKey> keys = (*it).second;
|
||||||
|
for (std::vector<CMempoolAddressDeltaKey>::iterator mit = keys.begin(); mit != keys.end(); mit++) {
|
||||||
|
mapAddress.erase(*mit);
|
||||||
|
}
|
||||||
|
mapAddressInserted.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
const CTransaction& tx = entry.GetTx();
|
||||||
|
std::vector<CSpentIndexKey> inserted;
|
||||||
|
|
||||||
|
uint256 txhash = tx.GetHash();
|
||||||
|
for (unsigned int j = 0; j < tx.vin.size(); j++) {
|
||||||
|
const CTxIn input = tx.vin[j];
|
||||||
|
const CTxOut &prevout = view.GetOutputFor(input);
|
||||||
|
uint160 addressHash;
|
||||||
|
int addressType;
|
||||||
|
|
||||||
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
|
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||||
|
addressType = 2;
|
||||||
|
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
|
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
||||||
|
addressType = 1;
|
||||||
|
} else {
|
||||||
|
addressHash.SetNull();
|
||||||
|
addressType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n);
|
||||||
|
CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, addressType, addressHash);
|
||||||
|
|
||||||
|
mapSpent.insert(make_pair(key, value));
|
||||||
|
inserted.push_back(key);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mapSpentInserted.insert(make_pair(txhash, inserted));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
mapSpentIndex::iterator it;
|
||||||
|
|
||||||
|
it = mapSpent.find(key);
|
||||||
|
if (it != mapSpent.end()) {
|
||||||
|
value = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::removeSpentIndex(const uint256 txhash)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
mapSpentIndexInserted::iterator it = mapSpentInserted.find(txhash);
|
||||||
|
|
||||||
|
if (it != mapSpentInserted.end()) {
|
||||||
|
std::vector<CSpentIndexKey> keys = (*it).second;
|
||||||
|
for (std::vector<CSpentIndexKey>::iterator mit = keys.begin(); mit != keys.end(); mit++) {
|
||||||
|
mapSpent.erase(*mit);
|
||||||
|
}
|
||||||
|
mapSpentInserted.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CTxMemPool::removeUnchecked(txiter it)
|
void CTxMemPool::removeUnchecked(txiter it)
|
||||||
{
|
{
|
||||||
const uint256 hash = it->GetTx().GetHash();
|
const uint256 hash = it->GetTx().GetHash();
|
||||||
@ -435,6 +574,8 @@ void CTxMemPool::removeUnchecked(txiter it)
|
|||||||
mapTx.erase(it);
|
mapTx.erase(it);
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
minerPolicyEstimator->removeTx(hash);
|
minerPolicyEstimator->removeTx(hash);
|
||||||
|
removeAddressIndex(hash);
|
||||||
|
removeSpentIndex(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates descendants of entry that are not already in setDescendants, and adds to
|
// Calculates descendants of entry that are not already in setDescendants, and adds to
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include "addressindex.h"
|
||||||
|
#include "spentindex.h"
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "coins.h"
|
#include "coins.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
@ -420,6 +422,18 @@ private:
|
|||||||
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
|
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
|
||||||
txlinksMap mapLinks;
|
txlinksMap mapLinks;
|
||||||
|
|
||||||
|
typedef std::map<CMempoolAddressDeltaKey, CMempoolAddressDelta, CMempoolAddressDeltaKeyCompare> addressDeltaMap;
|
||||||
|
addressDeltaMap mapAddress;
|
||||||
|
|
||||||
|
typedef std::map<uint256, std::vector<CMempoolAddressDeltaKey> > addressDeltaMapInserted;
|
||||||
|
addressDeltaMapInserted mapAddressInserted;
|
||||||
|
|
||||||
|
typedef std::map<CSpentIndexKey, CSpentIndexValue, CSpentIndexKeyCompare> mapSpentIndex;
|
||||||
|
mapSpentIndex mapSpent;
|
||||||
|
|
||||||
|
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
|
||||||
|
mapSpentIndexInserted mapSpentInserted;
|
||||||
|
|
||||||
void UpdateParent(txiter entry, txiter parent, bool add);
|
void UpdateParent(txiter entry, txiter parent, bool add);
|
||||||
void UpdateChild(txiter entry, txiter child, bool add);
|
void UpdateChild(txiter entry, txiter child, bool add);
|
||||||
|
|
||||||
@ -451,6 +465,15 @@ public:
|
|||||||
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
|
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
|
||||||
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
|
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
|
||||||
|
|
||||||
|
void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view);
|
||||||
|
bool getAddressIndex(std::vector<std::pair<uint160, int> > &addresses,
|
||||||
|
std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta> > &results);
|
||||||
|
bool removeAddressIndex(const uint256 txhash);
|
||||||
|
|
||||||
|
void addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view);
|
||||||
|
bool getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||||
|
bool removeSpentIndex(const uint256 txhash);
|
||||||
|
|
||||||
void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
|
void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
|
||||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||||
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
||||||
|
Loading…
Reference in New Issue
Block a user