Auto merge of #2100 - str4d:2074-rpc, r=arcalinea
Bitcoin 0.12 RPC PRs 1 Cherry-picked from the following upstream PRs: - bitcoin/bitcoin#6266 - bitcoin/bitcoin#6257 - bitcoin/bitcoin#6271 - bitcoin/bitcoin#6158 - bitcoin/bitcoin#6307 - bitcoin/bitcoin#6290 - bitcoin/bitcoin#6262 - bitcoin/bitcoin#6088 - bitcoin/bitcoin#6339 - bitcoin/bitcoin#6299 (partial, remainder in #2099) - bitcoin/bitcoin#6350 - bitcoin/bitcoin#6247 - bitcoin/bitcoin#6362 - bitcoin/bitcoin#5486 - bitcoin/bitcoin#6417 - bitcoin/bitcoin#6398 (partial, remainder was included in #1950) - bitcoin/bitcoin#6444 - bitcoin/bitcoin#6456 (partial, remainder was included in #2082) - bitcoin/bitcoin#6380 - bitcoin/bitcoin#6970 Part of #2074.
This commit is contained in:
commit
a7cf698873
|
@ -30,8 +30,13 @@ testScripts=(
|
|||
'zapwallettxes.py'
|
||||
'proxy_test.py'
|
||||
'merkle_blocks.py'
|
||||
'fundrawtransaction.py'
|
||||
'signrawtransactions.py'
|
||||
'walletbackup.py'
|
||||
'nodehandling.py'
|
||||
'reindex.py'
|
||||
'decodescript.py'
|
||||
'disablewallet.py'
|
||||
'zcjoinsplit.py'
|
||||
'zcjoinsplitdoublespend.py'
|
||||
'getblocktemplate.py'
|
||||
|
@ -47,13 +52,11 @@ testScriptsExt=(
|
|||
'invalidateblock.py'
|
||||
'keypool.py'
|
||||
'receivedby.py'
|
||||
'reindex.py'
|
||||
'rpcbind_test.py'
|
||||
# 'script_test.py'
|
||||
'smartfees.py'
|
||||
'maxblocksinflight.py'
|
||||
'invalidblockrequest.py'
|
||||
'rawtransactions.py'
|
||||
# 'forknotify.py'
|
||||
'p2p-acceptblock.py'
|
||||
);
|
||||
|
|
|
@ -21,7 +21,7 @@ Run all possible tests with `qa/pull-tester/rpc-tests.sh -extended`.
|
|||
|
||||
Possible options:
|
||||
|
||||
````
|
||||
```
|
||||
-h, --help show this help message and exit
|
||||
--nocleanup Leave bitcoinds and test.* datadir on exit or error
|
||||
--noshutdown Don't stop bitcoinds after the test execution
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env python2
|
||||
# Copyright (c) 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.
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class DecodeScriptTest(BitcoinTestFramework):
|
||||
"""Tests decoding scripts via RPC command "decodescript"."""
|
||||
|
||||
def setup_chain(self):
|
||||
print('Initializing test directory ' + self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 1)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(1, self.options.tmpdir)
|
||||
self.is_network_split = False
|
||||
|
||||
def decodescript_script_sig(self):
|
||||
signature = '304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001'
|
||||
push_signature = '48' + signature
|
||||
public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
|
||||
push_public_key = '21' + public_key
|
||||
|
||||
# below are test cases for all of the standard transaction types
|
||||
|
||||
# 1) P2PK scriptSig
|
||||
# the scriptSig of a public key scriptPubKey simply pushes a signature onto the stack
|
||||
rpc_result = self.nodes[0].decodescript(push_signature)
|
||||
assert_equal(signature, rpc_result['asm'])
|
||||
|
||||
# 2) P2PKH scriptSig
|
||||
rpc_result = self.nodes[0].decodescript(push_signature + push_public_key)
|
||||
assert_equal(signature + ' ' + public_key, rpc_result['asm'])
|
||||
|
||||
# 3) multisig scriptSig
|
||||
# this also tests the leading portion of a P2SH multisig scriptSig
|
||||
# OP_0 <A sig> <B sig>
|
||||
rpc_result = self.nodes[0].decodescript('00' + push_signature + push_signature)
|
||||
assert_equal('0 ' + signature + ' ' + signature, rpc_result['asm'])
|
||||
|
||||
# 4) P2SH scriptSig
|
||||
# an empty P2SH redeemScript is valid and makes for a very simple test case.
|
||||
# thus, such a spending scriptSig would just need to pass the outer redeemScript
|
||||
# hash test and leave true on the top of the stack.
|
||||
rpc_result = self.nodes[0].decodescript('5100')
|
||||
assert_equal('1 0', rpc_result['asm'])
|
||||
|
||||
# 5) null data scriptSig - no such thing because null data scripts can not be spent.
|
||||
# thus, no test case for that standard transaction type is here.
|
||||
|
||||
def decodescript_script_pub_key(self):
|
||||
public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2'
|
||||
push_public_key = '21' + public_key
|
||||
public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777'
|
||||
push_public_key_hash = '14' + public_key_hash
|
||||
|
||||
# below are test cases for all of the standard transaction types
|
||||
|
||||
# 1) P2PK scriptPubKey
|
||||
# <pubkey> OP_CHECKSIG
|
||||
rpc_result = self.nodes[0].decodescript(push_public_key + 'ac')
|
||||
assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm'])
|
||||
|
||||
# 2) P2PKH scriptPubKey
|
||||
# OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
|
||||
rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac')
|
||||
assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm'])
|
||||
|
||||
# 3) multisig scriptPubKey
|
||||
# <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG
|
||||
# just imagine that the pub keys used below are different.
|
||||
# for our purposes here it does not matter that they are the same even though it is unrealistic.
|
||||
rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_public_key + push_public_key + '53ae')
|
||||
assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
|
||||
|
||||
# 4) P2SH scriptPubKey
|
||||
# OP_HASH160 <Hash160(redeemScript)> OP_EQUAL.
|
||||
# push_public_key_hash here should actually be the hash of a redeem script.
|
||||
# but this works the same for purposes of this test.
|
||||
rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87')
|
||||
assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm'])
|
||||
|
||||
# 5) null data scriptPubKey
|
||||
# use a signature look-alike here to make sure that we do not decode random data as a signature.
|
||||
# this matters if/when signature sighash decoding comes along.
|
||||
# would want to make sure that no such decoding takes place in this case.
|
||||
signature_imposter = '48304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001'
|
||||
# OP_RETURN <data>
|
||||
rpc_result = self.nodes[0].decodescript('6a' + signature_imposter)
|
||||
assert_equal('OP_RETURN ' + signature_imposter[2:], rpc_result['asm'])
|
||||
|
||||
# 6) a CLTV redeem script. redeem scripts are in-effect scriptPubKey scripts, so adding a test here.
|
||||
# OP_NOP2 is also known as OP_CHECKLOCKTIMEVERIFY.
|
||||
# just imagine that the pub keys used below are different.
|
||||
# for our purposes here it does not matter that they are the same even though it is unrealistic.
|
||||
#
|
||||
# OP_IF
|
||||
# <receiver-pubkey> OP_CHECKSIGVERIFY
|
||||
# OP_ELSE
|
||||
# <lock-until> OP_NOP2 OP_DROP
|
||||
# OP_ENDIF
|
||||
# <sender-pubkey> OP_CHECKSIG
|
||||
#
|
||||
# lock until block 500,000
|
||||
rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
|
||||
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
|
||||
|
||||
def run_test(self):
|
||||
self.decodescript_script_sig()
|
||||
self.decodescript_script_pub_key()
|
||||
|
||||
if __name__ == '__main__':
|
||||
DecodeScriptTest().main()
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python2
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Exercise API with -disablewallet.
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class DisableWalletTest (BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 1)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(1, self.options.tmpdir, [['-disablewallet']])
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
# Check regression: https://github.com/bitcoin/bitcoin/issues/6963#issuecomment-154548880
|
||||
x = self.nodes[0].validateaddress('t3b1jtLvxCstdo1pJs9Tjzc5dmWyvGQSZj8')
|
||||
assert(x['isvalid'] == False)
|
||||
x = self.nodes[0].validateaddress('tmGqwWtL7RsbxikDSN26gsbicxVr2xJNe86')
|
||||
assert(x['isvalid'] == True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
DisableWalletTest ().main ()
|
|
@ -0,0 +1,546 @@
|
|||
#!/usr/bin/env python2
|
||||
# Copyright (c) 2014 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
from pprint import pprint
|
||||
from time import sleep
|
||||
|
||||
# Create one-input, one-output, no-fee transaction:
|
||||
class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 3)
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(3, self.options.tmpdir,
|
||||
extra_args=[['-experimentalfeatures', '-developerencryptwallet']] * 4)
|
||||
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
|
||||
self.is_network_split=False
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
print "Mining blocks..."
|
||||
feeTolerance = Decimal(0.00000002) #if the fee's positive delta is higher than this value tests will fail, neg. delta always fail the tests
|
||||
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(101)
|
||||
self.sync_all()
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5);
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0);
|
||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),5.0);
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
###############
|
||||
# simple test #
|
||||
###############
|
||||
inputs = [ ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 1.0 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enought inputs
|
||||
|
||||
##############################
|
||||
# simple test with two coins #
|
||||
##############################
|
||||
inputs = [ ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 2.2 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enough inputs
|
||||
|
||||
##############################
|
||||
# simple test with two coins #
|
||||
##############################
|
||||
inputs = [ ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 2.6 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
assert_equal(len(dec_tx['vin']) > 0, True)
|
||||
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
|
||||
|
||||
|
||||
################################
|
||||
# simple test with two outputs #
|
||||
################################
|
||||
inputs = [ ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 2.6, self.nodes[1].getnewaddress() : 2.5 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
for out in dec_tx['vout']:
|
||||
totalOut += out['value']
|
||||
|
||||
assert_equal(len(dec_tx['vin']) > 0, True)
|
||||
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
|
||||
|
||||
|
||||
#########################################################################
|
||||
# test a fundrawtransaction with a VIN greater than the required amount #
|
||||
#########################################################################
|
||||
utx = False
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
for aUtx in listunspent:
|
||||
if aUtx['amount'] == 5.0:
|
||||
utx = aUtx
|
||||
break;
|
||||
|
||||
assert_equal(utx!=False, True)
|
||||
|
||||
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
|
||||
outputs = { self.nodes[0].getnewaddress() : 1.0 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
for out in dec_tx['vout']:
|
||||
totalOut += out['value']
|
||||
|
||||
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
|
||||
|
||||
|
||||
|
||||
#####################################################################
|
||||
# test a fundrawtransaction with which will not get a change output #
|
||||
#####################################################################
|
||||
utx = False
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
for aUtx in listunspent:
|
||||
if aUtx['amount'] == 5.0:
|
||||
utx = aUtx
|
||||
break;
|
||||
|
||||
assert_equal(utx!=False, True)
|
||||
|
||||
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
|
||||
outputs = { self.nodes[0].getnewaddress() : Decimal(5.0) - fee - feeTolerance }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
for out in dec_tx['vout']:
|
||||
totalOut += out['value']
|
||||
|
||||
assert_equal(rawtxfund['changepos'], -1)
|
||||
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
|
||||
|
||||
|
||||
|
||||
#########################################################################
|
||||
# test a fundrawtransaction with a VIN smaller than the required amount #
|
||||
#########################################################################
|
||||
utx = False
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
for aUtx in listunspent:
|
||||
if aUtx['amount'] == 1.0:
|
||||
utx = aUtx
|
||||
break;
|
||||
|
||||
assert_equal(utx!=False, True)
|
||||
|
||||
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
|
||||
outputs = { self.nodes[0].getnewaddress() : 1.0 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
|
||||
# 4-byte version + 1-byte vin count + 36-byte prevout then script_len
|
||||
rawtx = rawtx[:82] + "0100" + rawtx[84:]
|
||||
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
matchingOuts = 0
|
||||
for i, out in enumerate(dec_tx['vout']):
|
||||
totalOut += out['value']
|
||||
if outputs.has_key(out['scriptPubKey']['addresses'][0]):
|
||||
matchingOuts+=1
|
||||
else:
|
||||
assert_equal(i, rawtxfund['changepos'])
|
||||
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])
|
||||
|
||||
assert_equal(matchingOuts, 1)
|
||||
assert_equal(len(dec_tx['vout']), 2)
|
||||
|
||||
|
||||
###########################################
|
||||
# test a fundrawtransaction with two VINs #
|
||||
###########################################
|
||||
utx = False
|
||||
utx2 = False
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
for aUtx in listunspent:
|
||||
if aUtx['amount'] == 1.0:
|
||||
utx = aUtx
|
||||
if aUtx['amount'] == 5.0:
|
||||
utx2 = aUtx
|
||||
|
||||
|
||||
assert_equal(utx!=False, True)
|
||||
|
||||
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']},{'txid' : utx2['txid'], 'vout' : utx2['vout']} ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 6.0 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
matchingOuts = 0
|
||||
for out in dec_tx['vout']:
|
||||
totalOut += out['value']
|
||||
if outputs.has_key(out['scriptPubKey']['addresses'][0]):
|
||||
matchingOuts+=1
|
||||
|
||||
assert_equal(matchingOuts, 1)
|
||||
assert_equal(len(dec_tx['vout']), 2)
|
||||
|
||||
matchingIns = 0
|
||||
for vinOut in dec_tx['vin']:
|
||||
for vinIn in inputs:
|
||||
if vinIn['txid'] == vinOut['txid']:
|
||||
matchingIns+=1
|
||||
|
||||
assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params
|
||||
|
||||
#########################################################
|
||||
# test a fundrawtransaction with two VINs and two vOUTs #
|
||||
#########################################################
|
||||
utx = False
|
||||
utx2 = False
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
for aUtx in listunspent:
|
||||
if aUtx['amount'] == 1.0:
|
||||
utx = aUtx
|
||||
if aUtx['amount'] == 5.0:
|
||||
utx2 = aUtx
|
||||
|
||||
|
||||
assert_equal(utx!=False, True)
|
||||
|
||||
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']},{'txid' : utx2['txid'], 'vout' : utx2['vout']} ]
|
||||
outputs = { self.nodes[0].getnewaddress() : 6.0, self.nodes[0].getnewaddress() : 1.0 }
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
fee = rawtxfund['fee']
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
totalOut = 0
|
||||
matchingOuts = 0
|
||||
for out in dec_tx['vout']:
|
||||
totalOut += out['value']
|
||||
if outputs.has_key(out['scriptPubKey']['addresses'][0]):
|
||||
matchingOuts+=1
|
||||
|
||||
assert_equal(matchingOuts, 2)
|
||||
assert_equal(len(dec_tx['vout']), 3)
|
||||
|
||||
##############################################
|
||||
# test a fundrawtransaction with invalid vin #
|
||||
##############################################
|
||||
listunspent = self.nodes[2].listunspent()
|
||||
inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin!
|
||||
outputs = { self.nodes[0].getnewaddress() : 1.0}
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
|
||||
errorString = ""
|
||||
try:
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
except JSONRPCException,e:
|
||||
errorString = e.error['message']
|
||||
|
||||
assert_equal("Insufficient" in errorString, True);
|
||||
|
||||
|
||||
|
||||
############################################################
|
||||
#compare fee of a standard pubkeyhash transaction
|
||||
inputs = []
|
||||
outputs = {self.nodes[1].getnewaddress():1.1}
|
||||
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[0].fundrawtransaction(rawTx)
|
||||
|
||||
#create same transaction over sendtoaddress
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1);
|
||||
signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']
|
||||
|
||||
#compare fee
|
||||
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee);
|
||||
assert(feeDelta >= 0 and feeDelta <= feeTolerance)
|
||||
############################################################
|
||||
|
||||
############################################################
|
||||
#compare fee of a standard pubkeyhash transaction with multiple outputs
|
||||
inputs = []
|
||||
outputs = {self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():0.1,self.nodes[1].getnewaddress():1.3,self.nodes[1].getnewaddress():0.2,self.nodes[1].getnewaddress():0.3}
|
||||
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[0].fundrawtransaction(rawTx)
|
||||
#create same transaction over sendtoaddress
|
||||
txId = self.nodes[0].sendmany("", outputs);
|
||||
signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']
|
||||
|
||||
#compare fee
|
||||
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee);
|
||||
assert(feeDelta >= 0 and feeDelta <= feeTolerance)
|
||||
############################################################
|
||||
|
||||
|
||||
############################################################
|
||||
#compare fee of a 2of2 multisig p2sh transaction
|
||||
|
||||
# create 2of2 addr
|
||||
addr1 = self.nodes[1].getnewaddress()
|
||||
addr2 = self.nodes[1].getnewaddress()
|
||||
|
||||
addr1Obj = self.nodes[1].validateaddress(addr1)
|
||||
addr2Obj = self.nodes[1].validateaddress(addr2)
|
||||
|
||||
mSigObj = self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])
|
||||
|
||||
inputs = []
|
||||
outputs = {mSigObj:1.1}
|
||||
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[0].fundrawtransaction(rawTx)
|
||||
|
||||
#create same transaction over sendtoaddress
|
||||
txId = self.nodes[0].sendtoaddress(mSigObj, 1.1);
|
||||
signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']
|
||||
|
||||
#compare fee
|
||||
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee);
|
||||
assert(feeDelta >= 0 and feeDelta <= feeTolerance)
|
||||
############################################################
|
||||
|
||||
|
||||
############################################################
|
||||
#compare fee of a standard pubkeyhash transaction
|
||||
|
||||
# create 4of5 addr
|
||||
addr1 = self.nodes[1].getnewaddress()
|
||||
addr2 = self.nodes[1].getnewaddress()
|
||||
addr3 = self.nodes[1].getnewaddress()
|
||||
addr4 = self.nodes[1].getnewaddress()
|
||||
addr5 = self.nodes[1].getnewaddress()
|
||||
|
||||
addr1Obj = self.nodes[1].validateaddress(addr1)
|
||||
addr2Obj = self.nodes[1].validateaddress(addr2)
|
||||
addr3Obj = self.nodes[1].validateaddress(addr3)
|
||||
addr4Obj = self.nodes[1].validateaddress(addr4)
|
||||
addr5Obj = self.nodes[1].validateaddress(addr5)
|
||||
|
||||
mSigObj = self.nodes[1].addmultisigaddress(4, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'], addr4Obj['pubkey'], addr5Obj['pubkey']])
|
||||
|
||||
inputs = []
|
||||
outputs = {mSigObj:1.1}
|
||||
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[0].fundrawtransaction(rawTx)
|
||||
|
||||
#create same transaction over sendtoaddress
|
||||
txId = self.nodes[0].sendtoaddress(mSigObj, 1.1);
|
||||
signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']
|
||||
|
||||
#compare fee
|
||||
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee);
|
||||
assert(feeDelta >= 0 and feeDelta <= feeTolerance)
|
||||
############################################################
|
||||
|
||||
|
||||
############################################################
|
||||
# spend a 2of2 multisig transaction over fundraw
|
||||
|
||||
# create 2of2 addr
|
||||
addr1 = self.nodes[2].getnewaddress()
|
||||
addr2 = self.nodes[2].getnewaddress()
|
||||
|
||||
addr1Obj = self.nodes[2].validateaddress(addr1)
|
||||
addr2Obj = self.nodes[2].validateaddress(addr2)
|
||||
|
||||
mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])
|
||||
|
||||
|
||||
# send 1.2 BTC to msig addr
|
||||
txId = self.nodes[0].sendtoaddress(mSigObj, 1.2);
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
oldBalance = self.nodes[1].getbalance()
|
||||
inputs = []
|
||||
outputs = {self.nodes[1].getnewaddress():1.1}
|
||||
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[2].fundrawtransaction(rawTx)
|
||||
|
||||
signedTx = self.nodes[2].signrawtransaction(fundedTx['hex'])
|
||||
txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# make sure funds are received at node1
|
||||
assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())
|
||||
|
||||
############################################################
|
||||
# locked wallet test
|
||||
self.nodes[1].encryptwallet("test")
|
||||
self.nodes.pop(1)
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
|
||||
self.nodes = start_nodes(3, self.options.tmpdir)
|
||||
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
self.is_network_split=False
|
||||
self.sync_all()
|
||||
|
||||
error = False
|
||||
try:
|
||||
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2);
|
||||
except:
|
||||
error = True
|
||||
assert(error)
|
||||
|
||||
oldBalance = self.nodes[0].getbalance()
|
||||
|
||||
inputs = []
|
||||
outputs = {self.nodes[0].getnewaddress():1.1}
|
||||
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
|
||||
|
||||
#now we need to unlock
|
||||
self.nodes[1].walletpassphrase("test", 100)
|
||||
signedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
|
||||
txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# make sure funds are received at node1
|
||||
assert_equal(oldBalance+Decimal('11.10000000'), self.nodes[0].getbalance())
|
||||
|
||||
|
||||
|
||||
###############################################
|
||||
# multiple (~19) inputs tx test | Compare fee #
|
||||
###############################################
|
||||
|
||||
#empty node1, send some small coins from node0 to node1
|
||||
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True);
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
for i in range(0,20):
|
||||
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01);
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
#fund a tx with ~20 small inputs
|
||||
inputs = []
|
||||
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
|
||||
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
|
||||
|
||||
#create same transaction over sendtoaddress
|
||||
txId = self.nodes[1].sendmany("", outputs);
|
||||
signedFee = self.nodes[1].getrawmempool(True)[txId]['fee']
|
||||
|
||||
#compare fee
|
||||
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee);
|
||||
assert(feeDelta >= 0 and feeDelta <= feeTolerance*19) #~19 inputs
|
||||
|
||||
|
||||
#############################################
|
||||
# multiple (~19) inputs tx test | sign/send #
|
||||
#############################################
|
||||
|
||||
#again, empty node1, send some small coins from node0 to node1
|
||||
self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True);
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
for i in range(0,20):
|
||||
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01);
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
#fund a tx with ~20 small inputs
|
||||
oldBalance = self.nodes[0].getbalance()
|
||||
|
||||
inputs = []
|
||||
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
|
||||
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
|
||||
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
|
||||
fundedAndSignedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
|
||||
txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
assert_equal(oldBalance+Decimal('10.19000000'), self.nodes[0].getbalance()) #0.19+block reward
|
||||
|
||||
#####################################################
|
||||
# test fundrawtransaction with OP_RETURN and no vin #
|
||||
#####################################################
|
||||
|
||||
rawtx = "0100000000010000000000000000066a047465737400000000"
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||
|
||||
assert_equal(len(dec_tx['vin']), 0)
|
||||
assert_equal(len(dec_tx['vout']), 1)
|
||||
|
||||
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
|
||||
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||
|
||||
assert_greater_than(len(dec_tx['vin']), 0) # at least one vin
|
||||
assert_equal(len(dec_tx['vout']), 2) # one change output added
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RawTransactionsTest().main()
|
|
@ -4,7 +4,7 @@
|
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test REST interface
|
||||
# Test rpc http basics
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env python2
|
||||
# Copyright (c) 2014 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 node handling
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
import base64
|
||||
|
||||
try:
|
||||
import http.client as httplib
|
||||
except ImportError:
|
||||
import httplib
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
class NodeHandlingTest (BitcoinTestFramework):
|
||||
def run_test(self):
|
||||
###########################
|
||||
# setban/listbanned tests #
|
||||
###########################
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point
|
||||
self.nodes[2].setban("127.0.0.1", "add")
|
||||
time.sleep(3) #wait till the nodes are disconected
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
try:
|
||||
self.nodes[2].setban("127.0.0.1", "add") #throws exception because 127.0.0.1 is within range 127.0.0.0/24
|
||||
except:
|
||||
pass
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
|
||||
try:
|
||||
self.nodes[2].setban("127.0.0.1", "remove")
|
||||
except:
|
||||
pass
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].setban("127.0.0.0/24", "remove")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
|
||||
###########################
|
||||
# RPC disconnectnode test #
|
||||
###########################
|
||||
url = urlparse.urlparse(self.nodes[1].url)
|
||||
self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
|
||||
time.sleep(2) #disconnecting a node needs a little bit of time
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
assert(node['addr'] != url.hostname+":"+str(p2p_port(1)))
|
||||
|
||||
connect_nodes_bi(self.nodes,0,1) #reconnect the node
|
||||
found = False
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
if node['addr'] == url.hostname+":"+str(p2p_port(1)):
|
||||
found = True
|
||||
assert(found)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NodeHandlingTest ().main ()
|
|
@ -14,6 +14,7 @@ from struct import *
|
|||
import binascii
|
||||
import json
|
||||
import StringIO
|
||||
import decimal
|
||||
|
||||
try:
|
||||
import http.client as httplib
|
||||
|
@ -244,12 +245,43 @@ class RESTTest (BitcoinTestFramework):
|
|||
assert_equal(response_header_str.encode("hex")[0:354], response_header_hex_str[0:354])
|
||||
|
||||
# check json format
|
||||
json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
|
||||
json_obj = json.loads(json_string)
|
||||
assert_equal(json_obj['hash'], bb_hash)
|
||||
block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
|
||||
block_json_obj = json.loads(block_json_string)
|
||||
assert_equal(block_json_obj['hash'], bb_hash)
|
||||
|
||||
# compare with json block header
|
||||
response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
|
||||
assert_equal(response_header_json.status, 200)
|
||||
response_header_json_str = response_header_json.read()
|
||||
json_obj = json.loads(response_header_json_str, parse_float=decimal.Decimal)
|
||||
assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
|
||||
assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
|
||||
|
||||
#compare with normal RPC block response
|
||||
rpc_block_json = self.nodes[0].getblock(bb_hash)
|
||||
assert_equal(json_obj[0]['hash'], rpc_block_json['hash'])
|
||||
assert_equal(json_obj[0]['confirmations'], rpc_block_json['confirmations'])
|
||||
assert_equal(json_obj[0]['height'], rpc_block_json['height'])
|
||||
assert_equal(json_obj[0]['version'], rpc_block_json['version'])
|
||||
assert_equal(json_obj[0]['merkleroot'], rpc_block_json['merkleroot'])
|
||||
assert_equal(json_obj[0]['time'], rpc_block_json['time'])
|
||||
assert_equal(json_obj[0]['nonce'], rpc_block_json['nonce'])
|
||||
assert_equal(json_obj[0]['bits'], rpc_block_json['bits'])
|
||||
assert_equal(json_obj[0]['difficulty'], rpc_block_json['difficulty'])
|
||||
assert_equal(json_obj[0]['chainwork'], rpc_block_json['chainwork'])
|
||||
assert_equal(json_obj[0]['previousblockhash'], rpc_block_json['previousblockhash'])
|
||||
|
||||
#see if we can get 5 headers in one response
|
||||
self.nodes[1].generate(5)
|
||||
self.sync_all()
|
||||
response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/5/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
|
||||
assert_equal(response_header_json.status, 200)
|
||||
response_header_json_str = response_header_json.read()
|
||||
json_obj = json.loads(response_header_json_str)
|
||||
assert_equal(len(json_obj), 5) #now we should have 5 header objects
|
||||
|
||||
# do tx test
|
||||
tx_hash = json_obj['tx'][0]['txid'];
|
||||
tx_hash = block_json_obj['tx'][0]['txid'];
|
||||
json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
|
||||
json_obj = json.loads(json_string)
|
||||
assert_equal(json_obj['txid'], tx_hash)
|
||||
|
|
|
@ -380,5 +380,37 @@ class WalletTest (BitcoinTestFramework):
|
|||
assert_equal(Decimal(self.nodes[2].getbalance()), node2balance)
|
||||
assert_equal(Decimal(self.nodes[2].getbalance("*")), node2balance)
|
||||
|
||||
#send a tx with value in a string (PR#6380 +)
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
|
||||
txObj = self.nodes[0].gettransaction(txId)
|
||||
assert_equal(txObj['amount'], Decimal('-2.00000000'))
|
||||
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")
|
||||
txObj = self.nodes[0].gettransaction(txId)
|
||||
assert_equal(txObj['amount'], Decimal('-0.00010000'))
|
||||
|
||||
#check if JSON parser can handle scientific notation in strings
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")
|
||||
txObj = self.nodes[0].gettransaction(txId)
|
||||
assert_equal(txObj['amount'], Decimal('-0.00010000'))
|
||||
|
||||
#this should fail
|
||||
errorString = ""
|
||||
try:
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1f-4")
|
||||
except JSONRPCException,e:
|
||||
errorString = e.error['message']
|
||||
|
||||
assert_equal("Invalid amount" in errorString, True);
|
||||
|
||||
errorString = ""
|
||||
try:
|
||||
self.nodes[0].generate("2") #use a string to as block amount parameter must fail because it's not interpreted as amount
|
||||
except JSONRPCException,e:
|
||||
errorString = e.error['message']
|
||||
|
||||
assert_equal("not an integer" in errorString, True);
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletTest ().main ()
|
||||
|
|
|
@ -117,7 +117,6 @@ public:
|
|||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
fRequireRPCPassword = true;
|
||||
fMiningRequiresPeers = true;
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = true;
|
||||
|
@ -251,7 +250,6 @@ public:
|
|||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
fRequireRPCPassword = true;
|
||||
fMiningRequiresPeers = true;
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = true;
|
||||
|
@ -328,7 +326,6 @@ public:
|
|||
vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds.
|
||||
vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds.
|
||||
|
||||
fRequireRPCPassword = false;
|
||||
fMiningRequiresPeers = false;
|
||||
fDefaultConsistencyChecks = true;
|
||||
fRequireStandard = false;
|
||||
|
|
|
@ -56,7 +56,6 @@ public:
|
|||
/** Used if GenerateBitcoins is called with a negative number of threads */
|
||||
int DefaultMinerThreads() const { return nMinerThreads; }
|
||||
const CBlock& GenesisBlock() const { return genesis; }
|
||||
bool RequireRPCPassword() const { return fRequireRPCPassword; }
|
||||
/** Make miner wait to have peers to avoid wasting work */
|
||||
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
|
||||
/** Default value for -checkmempool and -checkblockindex argument */
|
||||
|
@ -103,7 +102,6 @@ protected:
|
|||
std::string strCurrencyUnits;
|
||||
CBlock genesis;
|
||||
std::vector<SeedSpec6> vFixedSeeds;
|
||||
bool fRequireRPCPassword = false;
|
||||
bool fMiningRequiresPeers = false;
|
||||
bool fDefaultConsistencyChecks = false;
|
||||
bool fRequireStandard = false;
|
||||
|
|
|
@ -12,6 +12,8 @@ class CCoinControl
|
|||
{
|
||||
public:
|
||||
CTxDestination destChange;
|
||||
//! If false, allows unselected inputs, but requires all selected inputs be used
|
||||
bool fAllowOtherInputs;
|
||||
|
||||
CCoinControl()
|
||||
{
|
||||
|
@ -21,6 +23,7 @@ public:
|
|||
void SetNull()
|
||||
{
|
||||
destChange = CNoDestination();
|
||||
fAllowOtherInputs = false;
|
||||
setSelected.clear();
|
||||
}
|
||||
|
||||
|
@ -50,7 +53,7 @@ public:
|
|||
setSelected.clear();
|
||||
}
|
||||
|
||||
void ListSelected(std::vector<COutPoint>& vOutpoints)
|
||||
void ListSelected(std::vector<COutPoint>& vOutpoints) const
|
||||
{
|
||||
vOutpoints.assign(setSelected.begin(), setSelected.end());
|
||||
}
|
||||
|
|
68
src/net.cpp
68
src/net.cpp
|
@ -332,6 +332,15 @@ CNode* FindNode(const CNetAddr& ip)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
CNode* FindNode(const CSubNet& subNet)
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
if (subNet.Match((CNetAddr)pnode->addr))
|
||||
return (pnode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CNode* FindNode(const std::string& addrName)
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
|
@ -440,11 +449,12 @@ void CNode::PushVersion()
|
|||
|
||||
|
||||
|
||||
std::map<CNetAddr, int64_t> CNode::setBanned;
|
||||
std::map<CSubNet, int64_t> CNode::setBanned;
|
||||
CCriticalSection CNode::cs_setBanned;
|
||||
|
||||
void CNode::ClearBanned()
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
setBanned.clear();
|
||||
}
|
||||
|
||||
|
@ -453,7 +463,24 @@ bool CNode::IsBanned(CNetAddr ip)
|
|||
bool fResult = false;
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
|
||||
for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++)
|
||||
{
|
||||
CSubNet subNet = (*it).first;
|
||||
int64_t t = (*it).second;
|
||||
|
||||
if(subNet.Match(ip) && GetTime() < t)
|
||||
fResult = true;
|
||||
}
|
||||
}
|
||||
return fResult;
|
||||
}
|
||||
|
||||
bool CNode::IsBanned(CSubNet subnet)
|
||||
{
|
||||
bool fResult = false;
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet);
|
||||
if (i != setBanned.end())
|
||||
{
|
||||
int64_t t = (*i).second;
|
||||
|
@ -464,14 +491,37 @@ bool CNode::IsBanned(CNetAddr ip)
|
|||
return fResult;
|
||||
}
|
||||
|
||||
bool CNode::Ban(const CNetAddr &addr) {
|
||||
void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) {
|
||||
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
|
||||
Ban(subNet, bantimeoffset, sinceUnixEpoch);
|
||||
}
|
||||
|
||||
void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) {
|
||||
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
if (setBanned[addr] < banTime)
|
||||
setBanned[addr] = banTime;
|
||||
}
|
||||
return true;
|
||||
if (bantimeoffset > 0)
|
||||
banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
|
||||
|
||||
LOCK(cs_setBanned);
|
||||
if (setBanned[subNet] < banTime)
|
||||
setBanned[subNet] = banTime;
|
||||
}
|
||||
|
||||
bool CNode::Unban(const CNetAddr &addr) {
|
||||
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
|
||||
return Unban(subNet);
|
||||
}
|
||||
|
||||
bool CNode::Unban(const CSubNet &subNet) {
|
||||
LOCK(cs_setBanned);
|
||||
if (setBanned.erase(subNet))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
banMap = setBanned; //create a thread safe copy
|
||||
}
|
||||
|
||||
|
||||
|
|
11
src/net.h
11
src/net.h
|
@ -68,6 +68,7 @@ unsigned int SendBufferSize();
|
|||
void AddOneShot(const std::string& strDest);
|
||||
void AddressCurrentlyConnected(const CService& addr);
|
||||
CNode* FindNode(const CNetAddr& ip);
|
||||
CNode* FindNode(const CSubNet& subNet);
|
||||
CNode* FindNode(const std::string& addrName);
|
||||
CNode* FindNode(const CService& ip);
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
|
||||
|
@ -288,7 +289,7 @@ protected:
|
|||
|
||||
// Denial-of-service detection/prevention
|
||||
// Key is IP address, value is banned-until-time
|
||||
static std::map<CNetAddr, int64_t> setBanned;
|
||||
static std::map<CSubNet, int64_t> setBanned;
|
||||
static CCriticalSection cs_setBanned;
|
||||
|
||||
// Whitelisted ranges. Any node connecting from these is automatically
|
||||
|
@ -613,7 +614,13 @@ public:
|
|||
// new code.
|
||||
static void ClearBanned(); // needed for unit testing
|
||||
static bool IsBanned(CNetAddr ip);
|
||||
static bool Ban(const CNetAddr &ip);
|
||||
static bool IsBanned(CSubNet subnet);
|
||||
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
|
||||
static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
|
||||
static bool Unban(const CNetAddr &ip);
|
||||
static bool Unban(const CSubNet &ip);
|
||||
static void GetBanned(std::map<CSubNet, int64_t> &banmap);
|
||||
|
||||
void copyStats(CNodeStats &stats);
|
||||
|
||||
static bool IsWhitelistedRange(const CNetAddr &ip);
|
||||
|
|
|
@ -1340,6 +1340,11 @@ bool operator!=(const CSubNet& a, const CSubNet& b)
|
|||
return !(a==b);
|
||||
}
|
||||
|
||||
bool operator<(const CSubNet& a, const CSubNet& b)
|
||||
{
|
||||
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
std::string NetworkErrorString(int err)
|
||||
{
|
||||
|
|
|
@ -125,6 +125,7 @@ class CSubNet
|
|||
|
||||
friend bool operator==(const CSubNet& a, const CSubNet& b);
|
||||
friend bool operator!=(const CSubNet& a, const CSubNet& b);
|
||||
friend bool operator<(const CSubNet& a, const CSubNet& b);
|
||||
};
|
||||
|
||||
/** A combination of a network address (CNetAddr) and a (TCP) port */
|
||||
|
|
|
@ -272,10 +272,13 @@ public:
|
|||
// which has units satoshis-per-kilobyte.
|
||||
// If you'd pay more than 1/3 in fees
|
||||
// to spend something, then we consider it dust.
|
||||
// A typical txout is 34 bytes big, and will
|
||||
// A typical spendable txout is 34 bytes big, and will
|
||||
// need a CTxIn of at least 148 bytes to spend:
|
||||
// so dust is a txout less than 54 satoshis
|
||||
// so dust is a spendable txout less than 54 satoshis
|
||||
// with default minRelayTxFee.
|
||||
if (scriptPubKey.IsUnspendable())
|
||||
return 0;
|
||||
|
||||
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
|
||||
return 3*minRelayTxFee.GetFee(nSize);
|
||||
}
|
||||
|
|
19
src/rest.cpp
19
src/rest.cpp
|
@ -65,6 +65,7 @@ public:
|
|||
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
|
||||
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
|
||||
extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
||||
extern UniValue blockheaderToJSON(const CBlockIndex* blockindex);
|
||||
|
||||
static RestErr RESTERR(enum HTTPStatusCode status, string message)
|
||||
{
|
||||
|
@ -134,14 +135,14 @@ static bool rest_headers(AcceptedConnection* conn,
|
|||
if (!ParseHashStr(hashStr, hash))
|
||||
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
||||
|
||||
std::vector<CBlockHeader> headers;
|
||||
std::vector<const CBlockIndex *> headers;
|
||||
headers.reserve(count);
|
||||
{
|
||||
LOCK(cs_main);
|
||||
BlockMap::const_iterator it = mapBlockIndex.find(hash);
|
||||
const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
|
||||
while (pindex != NULL && chainActive.Contains(pindex)) {
|
||||
headers.push_back(pindex->GetBlockHeader());
|
||||
headers.push_back(pindex);
|
||||
if (headers.size() == (unsigned long)count)
|
||||
break;
|
||||
pindex = chainActive.Next(pindex);
|
||||
|
@ -149,8 +150,8 @@ static bool rest_headers(AcceptedConnection* conn,
|
|||
}
|
||||
|
||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
|
||||
BOOST_FOREACH(const CBlockHeader &header, headers) {
|
||||
ssHeader << header;
|
||||
BOOST_FOREACH(const CBlockIndex *pindex, headers) {
|
||||
ssHeader << pindex->GetBlockHeader();
|
||||
}
|
||||
|
||||
switch (rf) {
|
||||
|
@ -166,6 +167,16 @@ static bool rest_headers(AcceptedConnection* conn,
|
|||
return true;
|
||||
}
|
||||
|
||||
case RF_JSON: {
|
||||
UniValue jsonHeaders(UniValue::VARR);
|
||||
BOOST_FOREACH(const CBlockIndex *pindex, headers) {
|
||||
jsonHeaders.push_back(blockheaderToJSON(pindex));
|
||||
}
|
||||
string strJSON = jsonHeaders.write() + "\n";
|
||||
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
|
||||
return true;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
|
||||
}
|
||||
|
|
|
@ -72,6 +72,32 @@ double GetNetworkDifficulty(const CBlockIndex* blockindex)
|
|||
return GetDifficultyINTERNAL(blockindex, true);
|
||||
}
|
||||
|
||||
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex()));
|
||||
int confirmations = -1;
|
||||
// Only report confirmations if the block is on the main chain
|
||||
if (chainActive.Contains(blockindex))
|
||||
confirmations = chainActive.Height() - blockindex->nHeight + 1;
|
||||
result.push_back(Pair("confirmations", confirmations));
|
||||
result.push_back(Pair("height", blockindex->nHeight));
|
||||
result.push_back(Pair("version", blockindex->nVersion));
|
||||
result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex()));
|
||||
result.push_back(Pair("time", (int64_t)blockindex->nTime));
|
||||
result.push_back(Pair("nonce", blockindex->nNonce.GetHex()));
|
||||
result.push_back(Pair("solution", HexStr(blockindex->nSolution)));
|
||||
result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits)));
|
||||
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
|
||||
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
|
||||
|
||||
if (blockindex->pprev)
|
||||
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
|
||||
CBlockIndex *pnext = chainActive.Next(blockindex);
|
||||
if (pnext)
|
||||
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
|
||||
{
|
||||
|
@ -114,7 +140,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
UniValue getblockcount(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
|
@ -277,6 +302,62 @@ UniValue getblockhash(const UniValue& params, bool fHelp)
|
|||
return pblockindex->GetBlockHash().GetHex();
|
||||
}
|
||||
|
||||
UniValue getblockheader(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getblockheader \"hash\" ( verbose )\n"
|
||||
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
|
||||
"If verbose is true, returns an Object with information about blockheader <hash>.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"hash\" (string, required) The block hash\n"
|
||||
"2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
|
||||
"\nResult (for verbose = true):\n"
|
||||
"{\n"
|
||||
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
|
||||
" \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n"
|
||||
" \"height\" : n, (numeric) The block height or index\n"
|
||||
" \"version\" : n, (numeric) The block version\n"
|
||||
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
|
||||
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
" \"nonce\" : n, (numeric) The nonce\n"
|
||||
" \"bits\" : \"1d00ffff\", (string) The bits\n"
|
||||
" \"difficulty\" : x.xxx, (numeric) The difficulty\n"
|
||||
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
|
||||
" \"nextblockhash\" : \"hash\" (string) The hash of the next block\n"
|
||||
"}\n"
|
||||
"\nResult (for verbose=false):\n"
|
||||
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
||||
+ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
std::string strHash = params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
|
||||
bool fVerbose = true;
|
||||
if (params.size() > 1)
|
||||
fVerbose = params[1].get_bool();
|
||||
|
||||
if (mapBlockIndex.count(hash) == 0)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
|
||||
CBlockIndex* pblockindex = mapBlockIndex[hash];
|
||||
|
||||
if (!fVerbose)
|
||||
{
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << pblockindex->GetBlockHeader();
|
||||
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
|
||||
return strHex;
|
||||
}
|
||||
|
||||
return blockheaderToJSON(pblockindex);
|
||||
}
|
||||
|
||||
UniValue getblock(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
|
@ -367,8 +448,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp)
|
|||
+ HelpExampleRpc("gettxoutsetinfo", "")
|
||||
);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
CCoinsStats stats;
|
||||
|
|
|
@ -70,6 +70,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "listunspent", 1 },
|
||||
{ "listunspent", 2 },
|
||||
{ "getblock", 1 },
|
||||
{ "getblockheader", 1 },
|
||||
{ "gettransaction", 1 },
|
||||
{ "getrawtransaction", 1 },
|
||||
{ "createrawtransaction", 0 },
|
||||
|
@ -77,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "signrawtransaction", 1 },
|
||||
{ "signrawtransaction", 2 },
|
||||
{ "sendrawtransaction", 1 },
|
||||
{ "fundrawtransaction", 1 },
|
||||
{ "gettxout", 1 },
|
||||
{ "gettxout", 2 },
|
||||
{ "gettxoutproof", 0 },
|
||||
|
@ -92,6 +94,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "estimatepriority", 0 },
|
||||
{ "prioritisetransaction", 1 },
|
||||
{ "prioritisetransaction", 2 },
|
||||
{ "setban", 2 },
|
||||
{ "setban", 3 },
|
||||
{ "zcrawjoinsplit", 1 },
|
||||
{ "zcrawjoinsplit", 2 },
|
||||
{ "zcrawjoinsplit", 3 },
|
||||
|
|
|
@ -111,20 +111,14 @@ UniValue getinfo(const UniValue& params, bool fHelp)
|
|||
#ifdef ENABLE_WALLET
|
||||
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
|
||||
{
|
||||
private:
|
||||
isminetype mine;
|
||||
|
||||
public:
|
||||
DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
|
||||
|
||||
UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
|
||||
|
||||
UniValue operator()(const CKeyID &keyID) const {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CPubKey vchPubKey;
|
||||
obj.push_back(Pair("isscript", false));
|
||||
if (mine == ISMINE_SPENDABLE) {
|
||||
pwalletMain->GetPubKey(keyID, vchPubKey);
|
||||
if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) {
|
||||
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
||||
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
||||
}
|
||||
|
@ -133,10 +127,9 @@ public:
|
|||
|
||||
UniValue operator()(const CScriptID &scriptID) const {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
CScript subscript;
|
||||
obj.push_back(Pair("isscript", true));
|
||||
if (mine != ISMINE_NO) {
|
||||
CScript subscript;
|
||||
pwalletMain->GetCScript(scriptID, subscript);
|
||||
if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) {
|
||||
std::vector<CTxDestination> addresses;
|
||||
txnouttype whichType;
|
||||
int nRequired;
|
||||
|
@ -202,11 +195,9 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
|
|||
#ifdef ENABLE_WALLET
|
||||
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
|
||||
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
|
||||
if (mine != ISMINE_NO) {
|
||||
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
|
||||
UniValue detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
|
||||
ret.pushKVs(detail);
|
||||
}
|
||||
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
|
||||
UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
|
||||
ret.pushKVs(detail);
|
||||
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
||||
ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
|
||||
#endif
|
||||
|
|
130
src/rpcnet.cpp
130
src/rpcnet.cpp
|
@ -214,6 +214,28 @@ UniValue addnode(const UniValue& params, bool fHelp)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue disconnectnode(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"disconnectnode \"node\" \n"
|
||||
"\nImmediately disconnects from the specified node.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8233\"")
|
||||
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8233\"")
|
||||
);
|
||||
|
||||
CNode* pNode = FindNode(params[0].get_str());
|
||||
if (pNode == NULL)
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
|
||||
|
||||
pNode->fDisconnect = true;
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
|
@ -408,6 +430,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getnetworkinfo", "")
|
||||
|
@ -439,5 +462,112 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
obj.push_back(Pair("localaddresses", localAddresses));
|
||||
obj.push_back(Pair("warnings", GetWarnings("statusbar")));
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue setban(const UniValue& params, bool fHelp)
|
||||
{
|
||||
string strCommand;
|
||||
if (params.size() >= 2)
|
||||
strCommand = params[1].get_str();
|
||||
if (fHelp || params.size() < 2 ||
|
||||
(strCommand != "add" && strCommand != "remove"))
|
||||
throw runtime_error(
|
||||
"setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n"
|
||||
"\nAttempts add or remove a IP/Subnet from the banned list.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n"
|
||||
"2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n"
|
||||
"3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n"
|
||||
"4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
|
||||
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
|
||||
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
|
||||
);
|
||||
|
||||
CSubNet subNet;
|
||||
CNetAddr netAddr;
|
||||
bool isSubnet = false;
|
||||
|
||||
if (params[0].get_str().find("/") != string::npos)
|
||||
isSubnet = true;
|
||||
|
||||
if (!isSubnet)
|
||||
netAddr = CNetAddr(params[0].get_str());
|
||||
else
|
||||
subNet = CSubNet(params[0].get_str());
|
||||
|
||||
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
|
||||
|
||||
if (strCommand == "add")
|
||||
{
|
||||
if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
|
||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
|
||||
|
||||
int64_t banTime = 0; //use standard bantime if not specified
|
||||
if (params.size() >= 3 && !params[2].isNull())
|
||||
banTime = params[2].get_int64();
|
||||
|
||||
bool absolute = false;
|
||||
if (params.size() == 4 && params[3].isTrue())
|
||||
absolute = true;
|
||||
|
||||
isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute);
|
||||
|
||||
//disconnect possible nodes
|
||||
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
|
||||
bannedNode->fDisconnect = true;
|
||||
}
|
||||
else if(strCommand == "remove")
|
||||
{
|
||||
if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) ))
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
|
||||
}
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue listbanned(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"listbanned\n"
|
||||
"\nList all banned IPs/Subnets.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("listbanned", "")
|
||||
+ HelpExampleRpc("listbanned", "")
|
||||
);
|
||||
|
||||
std::map<CSubNet, int64_t> banMap;
|
||||
CNode::GetBanned(banMap);
|
||||
|
||||
UniValue bannedAddresses(UniValue::VARR);
|
||||
for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
|
||||
{
|
||||
UniValue rec(UniValue::VOBJ);
|
||||
rec.push_back(Pair("address", (*it).first.ToString()));
|
||||
rec.push_back(Pair("banned_until", (*it).second));
|
||||
bannedAddresses.push_back(rec);
|
||||
}
|
||||
|
||||
return bannedAddresses;
|
||||
}
|
||||
|
||||
UniValue clearbanned(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"clearbanned\n"
|
||||
"\nClear all banned IPs.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("clearbanned", "")
|
||||
+ HelpExampleRpc("clearbanned", "")
|
||||
);
|
||||
|
||||
CNode::ClearBanned();
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ enum RPCErrorCode
|
|||
RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks
|
||||
RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added
|
||||
RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before
|
||||
RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes
|
||||
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet
|
||||
|
||||
//! Wallet errors
|
||||
RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.)
|
||||
|
|
|
@ -123,8 +123,8 @@ void RPCTypeCheckObj(const UniValue& o,
|
|||
|
||||
CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
if (!value.isNum())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number");
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
|
||||
|
@ -280,11 +280,15 @@ static const CRPCCommand vRPCCommands[] =
|
|||
/* P2P networking */
|
||||
{ "network", "getnetworkinfo", &getnetworkinfo, true },
|
||||
{ "network", "addnode", &addnode, true },
|
||||
{ "network", "disconnectnode", &disconnectnode, true },
|
||||
{ "network", "getaddednodeinfo", &getaddednodeinfo, true },
|
||||
{ "network", "getconnectioncount", &getconnectioncount, true },
|
||||
{ "network", "getnettotals", &getnettotals, true },
|
||||
{ "network", "getpeerinfo", &getpeerinfo, true },
|
||||
{ "network", "ping", &ping, true },
|
||||
{ "network", "setban", &setban, true },
|
||||
{ "network", "listbanned", &listbanned, true },
|
||||
{ "network", "clearbanned", &clearbanned, true },
|
||||
|
||||
/* Block chain and UTXO */
|
||||
{ "blockchain", "getblockchaininfo", &getblockchaininfo, true },
|
||||
|
@ -292,6 +296,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "blockchain", "getblockcount", &getblockcount, true },
|
||||
{ "blockchain", "getblock", &getblock, true },
|
||||
{ "blockchain", "getblockhash", &getblockhash, true },
|
||||
{ "blockchain", "getblockheader", &getblockheader, true },
|
||||
{ "blockchain", "getchaintips", &getchaintips, true },
|
||||
{ "blockchain", "getdifficulty", &getdifficulty, true },
|
||||
{ "blockchain", "getmempoolinfo", &getmempoolinfo, true },
|
||||
|
@ -328,6 +333,9 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, true },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
|
||||
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
|
||||
#ifdef ENABLE_WALLET
|
||||
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
|
||||
#endif
|
||||
|
||||
/* Utility functions */
|
||||
{ "util", "createmultisig", &createmultisig, true },
|
||||
|
@ -967,13 +975,6 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
|
|||
if (!valRequest.read(strRequest))
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
|
||||
|
||||
// Return immediately if in warmup
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (fRPCInWarmup)
|
||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
|
||||
}
|
||||
|
||||
string strReply;
|
||||
|
||||
// singleton request
|
||||
|
@ -1051,6 +1052,13 @@ void ServiceConnection(AcceptedConnection *conn)
|
|||
|
||||
UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const
|
||||
{
|
||||
// Return immediately if in warmup
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (fRPCInWarmup)
|
||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
|
||||
}
|
||||
|
||||
// Find method
|
||||
const CRPCCommand *pcmd = tableRPC[strMethod];
|
||||
if (!pcmd)
|
||||
|
|
|
@ -158,8 +158,12 @@ extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rp
|
|||
extern UniValue getpeerinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue ping(const UniValue& params, bool fHelp);
|
||||
extern UniValue addnode(const UniValue& params, bool fHelp);
|
||||
extern UniValue disconnectnode(const UniValue& params, bool fHelp);
|
||||
extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getnettotals(const UniValue& params, bool fHelp);
|
||||
extern UniValue setban(const UniValue& params, bool fHelp);
|
||||
extern UniValue listbanned(const UniValue& params, bool fHelp);
|
||||
extern UniValue clearbanned(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue importprivkey(const UniValue& params, bool fHelp);
|
||||
|
@ -231,6 +235,7 @@ extern UniValue listlockunspent(const UniValue& params, bool fHelp);
|
|||
extern UniValue createrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue decoderawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue decodescript(const UniValue& params, bool fHelp);
|
||||
extern UniValue fundrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue sendrawtransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxoutproof(const UniValue& params, bool fHelp);
|
||||
|
@ -243,6 +248,7 @@ extern UniValue settxfee(const UniValue& params, bool fHelp);
|
|||
extern UniValue getmempoolinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue getrawmempool(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblockhash(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblockheader(const UniValue& params, bool fHelp);
|
||||
extern UniValue getblock(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp);
|
||||
extern UniValue gettxout(const UniValue& params, bool fHelp);
|
||||
|
|
|
@ -281,3 +281,39 @@ CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecke
|
|||
|
||||
return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/** Dummy signature checker which accepts all signatures. */
|
||||
class DummySignatureChecker : public BaseSignatureChecker
|
||||
{
|
||||
public:
|
||||
DummySignatureChecker() {}
|
||||
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
const DummySignatureChecker dummyChecker;
|
||||
}
|
||||
|
||||
const BaseSignatureChecker& DummySignatureCreator::Checker() const
|
||||
{
|
||||
return dummyChecker;
|
||||
}
|
||||
|
||||
bool DummySignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const
|
||||
{
|
||||
// Create a dummy signature that is a valid DER-encoding
|
||||
vchSig.assign(72, '\000');
|
||||
vchSig[0] = 0x30;
|
||||
vchSig[1] = 69;
|
||||
vchSig[2] = 0x02;
|
||||
vchSig[3] = 33;
|
||||
vchSig[4] = 0x01;
|
||||
vchSig[4 + 33] = 0x02;
|
||||
vchSig[5 + 33] = 32;
|
||||
vchSig[6 + 33] = 0x01;
|
||||
vchSig[6 + 33 + 32] = SIGHASH_ALL;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ public:
|
|||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const;
|
||||
};
|
||||
|
||||
/** A signature creator that just produces 72-byte empty signatyres. */
|
||||
class DummySignatureCreator : public BaseSignatureCreator {
|
||||
public:
|
||||
DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {}
|
||||
const BaseSignatureChecker& Checker() const;
|
||||
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const;
|
||||
};
|
||||
|
||||
/** Produce a script signature using a generic signature creator. */
|
||||
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig);
|
||||
|
||||
|
|
|
@ -227,4 +227,87 @@ BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr)
|
|||
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("::ffff:127.0.0.1")).ToString(), "127.0.0.1");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rpc_ban)
|
||||
{
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||
|
||||
UniValue r;
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add")));
|
||||
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
UniValue ar = r.get_array();
|
||||
UniValue o1 = ar[0].get_obj();
|
||||
UniValue adr = find_value(o1, "address");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255");
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));;
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add 1607731200 true")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
o1 = ar[0].get_obj();
|
||||
adr = find_value(o1, "address");
|
||||
UniValue banned_until = find_value(o1, "banned_until");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
|
||||
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
|
||||
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add 200")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
o1 = ar[0].get_obj();
|
||||
adr = find_value(o1, "address");
|
||||
banned_until = find_value(o1, "banned_until");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
|
||||
int64_t now = GetTime();
|
||||
BOOST_CHECK(banned_until.get_int64() > now);
|
||||
BOOST_CHECK(banned_until.get_int64()-now <= 200);
|
||||
|
||||
// must throw an exception because 127.0.0.1 is in already banned suubnet range
|
||||
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.1 add")), runtime_error);
|
||||
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0/24 remove")));;
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/255.255.0.0 add")));
|
||||
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.1.1 add")), runtime_error);
|
||||
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||
|
||||
|
||||
BOOST_CHECK_THROW(r = CallRPC(string("setban test add")), runtime_error); //invalid IP
|
||||
|
||||
//IPv6 tests
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban FE80:0000:0000:0000:0202:B3FF:FE1E:8329 add")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
o1 = ar[0].get_obj();
|
||||
adr = find_value(o1, "address");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/30 add")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
o1 = ar[0].get_obj();
|
||||
adr = find_value(o1, "address");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/ffff:fffc:0:0:0:0:0:0");
|
||||
|
||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add")));
|
||||
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||
ar = r.get_array();
|
||||
o1 = ar[0].get_obj();
|
||||
adr = find_value(o1, "address");
|
||||
BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -248,6 +248,12 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
|
|||
}
|
||||
BOOST_CHECK(!notFound);
|
||||
|
||||
/*********************************
|
||||
* fundrawtransaction
|
||||
*********************************/
|
||||
BOOST_CHECK_THROW(CallRPC("fundrawtransaction 28z"), runtime_error);
|
||||
BOOST_CHECK_THROW(CallRPC("fundrawtransaction 01000000000180969800000000001976a91450ce0a4b0ee0ddeb633da85199728b940ac3fe9488ac00000000"), runtime_error);
|
||||
|
||||
/*
|
||||
* getblocksubsidy
|
||||
*/
|
||||
|
|
|
@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(univalue_object)
|
|||
}
|
||||
|
||||
static const char *json1 =
|
||||
"[1.10000000,{\"key1\":\"str\",\"key2\":800,\"key3\":{\"name\":\"martian\"}}]";
|
||||
"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]";
|
||||
|
||||
BOOST_AUTO_TEST_CASE(univalue_readwrite)
|
||||
{
|
||||
|
@ -306,7 +306,9 @@ BOOST_AUTO_TEST_CASE(univalue_readwrite)
|
|||
BOOST_CHECK_EQUAL(obj.size(), 3);
|
||||
|
||||
BOOST_CHECK(obj["key1"].isStr());
|
||||
BOOST_CHECK_EQUAL(obj["key1"].getValStr(), "str");
|
||||
std::string correctValue("str");
|
||||
correctValue.push_back('\0');
|
||||
BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue);
|
||||
BOOST_CHECK(obj["key2"].isNum());
|
||||
BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800");
|
||||
BOOST_CHECK(obj["key3"].isObject());
|
||||
|
|
|
@ -225,7 +225,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
|||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
}
|
||||
stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight;
|
||||
}
|
||||
stats.hashSerialized = ss.GetHash();
|
||||
stats.nTotalAmount = nTotalAmount;
|
||||
return true;
|
||||
|
|
|
@ -2260,6 +2260,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
|||
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
|
||||
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
|
||||
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
|
||||
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in ZEC/KB\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getwalletinfo", "")
|
||||
|
@ -2278,6 +2279,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
|||
obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
|
||||
if (pwalletMain->IsCrypted())
|
||||
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
|
||||
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -2419,6 +2421,60 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
return results;
|
||||
}
|
||||
|
||||
UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"fundrawtransaction \"hexstring\"\n"
|
||||
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
|
||||
"This will not modify existing inputs, and will add one change output to the outputs.\n"
|
||||
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
|
||||
"The inputs added will not be signed, use signrawtransaction for that.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
|
||||
" \"fee\": n, (numeric) The fee added to the transaction\n"
|
||||
" \"changepos\": n (numeric) The position of the added change output, or -1\n"
|
||||
"}\n"
|
||||
"\"hex\" \n"
|
||||
"\nExamples:\n"
|
||||
"\nCreate a transaction with no inputs\n"
|
||||
+ HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
|
||||
"\nAdd sufficient unsigned inputs to meet the output value\n"
|
||||
+ HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
|
||||
"\nSign the transaction\n"
|
||||
+ HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
|
||||
"\nSend the transaction\n"
|
||||
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
|
||||
|
||||
// parse hex string from parameter
|
||||
CTransaction origTx;
|
||||
if (!DecodeHexTx(origTx, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
CMutableTransaction tx(origTx);
|
||||
CAmount nFee;
|
||||
string strFailReason;
|
||||
int nChangePos = -1;
|
||||
if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("hex", EncodeHexTx(tx)));
|
||||
result.push_back(Pair("changepos", nChangePos));
|
||||
result.push_back(Pair("fee", ValueFromAmount(nFee)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp) {
|
||||
|
|
|
@ -2219,7 +2219,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
|||
isminetype mine = IsMine(pcoin->vout[i]);
|
||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
|
||||
!IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
|
||||
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
|
||||
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i)))
|
||||
vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
|
||||
}
|
||||
}
|
||||
|
@ -2407,25 +2407,108 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||
}
|
||||
|
||||
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
|
||||
if (coinControl && coinControl->HasSelected())
|
||||
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)
|
||||
{
|
||||
BOOST_FOREACH(const COutput& out, vCoins)
|
||||
{
|
||||
if(!out.fSpendable)
|
||||
continue;
|
||||
if (!out.fSpendable)
|
||||
continue;
|
||||
nValueRet += out.tx->vout[out.i].nValue;
|
||||
setCoinsRet.insert(make_pair(out.tx, out.i));
|
||||
}
|
||||
return (nValueRet >= nTargetValue);
|
||||
}
|
||||
|
||||
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
|
||||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)));
|
||||
// calculate value from preset inputs and store them
|
||||
set<pair<const CWalletTx*, uint32_t> > setPresetCoins;
|
||||
CAmount nValueFromPresetInputs = 0;
|
||||
|
||||
std::vector<COutPoint> vPresetInputs;
|
||||
if (coinControl)
|
||||
coinControl->ListSelected(vPresetInputs);
|
||||
BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs)
|
||||
{
|
||||
map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
|
||||
if (it != mapWallet.end())
|
||||
{
|
||||
const CWalletTx* pcoin = &it->second;
|
||||
// Clearly invalid input, fail
|
||||
if (pcoin->vout.size() <= outpoint.n)
|
||||
return false;
|
||||
nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue;
|
||||
setPresetCoins.insert(make_pair(pcoin, outpoint.n));
|
||||
} else
|
||||
return false; // TODO: Allow non-wallet inputs
|
||||
}
|
||||
|
||||
// remove preset inputs from vCoins
|
||||
for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
|
||||
{
|
||||
if (setPresetCoins.count(make_pair(it->tx, it->i)))
|
||||
it = vCoins.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
bool res = nTargetValue <= nValueFromPresetInputs ||
|
||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
|
||||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
|
||||
|
||||
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
|
||||
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
|
||||
|
||||
// add preset inputs to the total value selected
|
||||
nValueRet += nValueFromPresetInputs;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl)
|
||||
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason)
|
||||
{
|
||||
vector<CRecipient> vecSend;
|
||||
|
||||
// Turn the txout set into a CRecipient vector
|
||||
BOOST_FOREACH(const CTxOut& txOut, tx.vout)
|
||||
{
|
||||
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
|
||||
vecSend.push_back(recipient);
|
||||
}
|
||||
|
||||
CCoinControl coinControl;
|
||||
coinControl.fAllowOtherInputs = true;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
coinControl.Select(txin.prevout);
|
||||
|
||||
CReserveKey reservekey(this);
|
||||
CWalletTx wtx;
|
||||
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
|
||||
return false;
|
||||
|
||||
if (nChangePosRet != -1)
|
||||
tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]);
|
||||
|
||||
// Add new txins (keeping original txin scriptSig/order)
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
{
|
||||
bool found = false;
|
||||
BOOST_FOREACH(const CTxIn& origTxIn, tx.vin)
|
||||
{
|
||||
if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
tx.vin.push_back(txin);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
||||
int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
unsigned int nSubtractFeeFromAmount = 0;
|
||||
|
@ -2636,23 +2719,43 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend,
|
|||
|
||||
// Sign
|
||||
int nIn = 0;
|
||||
CTransaction txNewConst(txNew);
|
||||
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
|
||||
if (!SignSignature(*this, *coin.first, txNew, nIn++))
|
||||
{
|
||||
bool signSuccess;
|
||||
const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
|
||||
CScript& scriptSigRes = txNew.vin[nIn].scriptSig;
|
||||
if (sign)
|
||||
signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes);
|
||||
else
|
||||
signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes);
|
||||
|
||||
if (!signSuccess)
|
||||
{
|
||||
strFailReason = _("Signing transaction failed");
|
||||
return false;
|
||||
}
|
||||
nIn++;
|
||||
}
|
||||
|
||||
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
// Remove scriptSigs if we used dummy signatures for fee calculation
|
||||
if (!sign) {
|
||||
BOOST_FOREACH (CTxIn& vin, txNew.vin)
|
||||
vin.scriptSig = CScript();
|
||||
}
|
||||
|
||||
// Embed the constructed transaction data in wtxNew.
|
||||
*static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
|
||||
|
||||
// Limit size
|
||||
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
|
||||
if (nBytes >= MAX_TX_SIZE)
|
||||
{
|
||||
strFailReason = _("Transaction too large");
|
||||
return false;
|
||||
}
|
||||
|
||||
dPriority = wtxNew.ComputePriority(dPriority, nBytes);
|
||||
|
||||
// Can we complete this as a free transaction?
|
||||
|
|
|
@ -912,8 +912,9 @@ public:
|
|||
CAmount GetWatchOnlyBalance() const;
|
||||
CAmount GetUnconfirmedWatchOnlyBalance() const;
|
||||
CAmount GetImmatureWatchOnlyBalance() const;
|
||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
|
||||
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason);
|
||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet,
|
||||
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||
|
||||
static CFeeRate minTxFee;
|
||||
|
|
Loading…
Reference in New Issue