Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/primitives/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,40 @@ std::string CTxIn::ToString() const
return str;
}


bool CTxIn::SpendsNestedPayToWitnessPubKeyHashOutput(CScript spentScriptPubKey) const {
if ((!spentScriptPubKey.IsPayToScriptHash()) || scriptWitness.IsNull()) {
return false;
}

return scriptSig.IsNestedPayToWitnessPubKeyHashScriptSig();
}

bool CTxIn::SpendsNestedPayToWitnessScriptHashOutput(CScript spentScriptPubKey) const {
if (!(spentScriptPubKey.IsPayToScriptHash()) || scriptWitness.IsNull()) {
return false;
}

return scriptSig.IsNestedPayToWitnessScriptHashScriptSig();
}

bool CTxIn::SpendsNativePayToWitnessPubKeyHashOutput(CScript spentScriptPubKey) const {
if (scriptWitness.IsNull()) {
return false;
}

return spentScriptPubKey.IsNativePayToWitnessPubKeyHash();
}

bool CTxIn::SpendsNativePayToWitnessScriptHashOutput(CScript spentScriptPubKey) const {
if (scriptWitness.IsNull()) {
return false;
}

return spentScriptPubKey.IsPayToWitnessScriptHash();
}


CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
{
nValue = nValueIn;
Expand Down
5 changes: 5 additions & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class CTxIn
}

std::string ToString() const;

bool SpendsNestedPayToWitnessPubKeyHashOutput(CScript spentScriptPubKey) const;
bool SpendsNestedPayToWitnessScriptHashOutput(CScript spentScriptPubKey) const;
bool SpendsNativePayToWitnessPubKeyHashOutput(CScript spentScriptPubKey) const;
bool SpendsNativePayToWitnessScriptHashOutput(CScript spentScriptPubKey) const;
};

/** An output of a transaction. It contains the public key that the next input
Expand Down
240 changes: 235 additions & 5 deletions src/rpc/blockchain.cpp

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,31 @@ bool CScript::IsPayToWitnessScriptHash() const
(*this)[1] == 0x20);
}

bool CScript::IsNativePayToWitnessPubKeyHash() const
{
// Extra-fast test for native pay-to-witness-pubkey-hash CScripts:
return (this->size() == 22 &&
(*this)[0] == OP_0 &&
(*this)[1] == 0x14);
}

bool CScript::IsNestedPayToWitnessPubKeyHashScriptSig() const
{
return (this->size() == 23 &&
(*this)[0] == 0x16 &&
(*this)[1] == 0x00 &&
(*this)[2] == 0x14);
}

bool CScript::IsNestedPayToWitnessScriptHashScriptSig() const
{
return (this->size() == 35 &&
(*this)[0] == 0x22 &&
(*this)[1] == 0x00 &&
(*this)[2] == 0x20);
}


// A witness program is any valid CScript that consists of a 1-byte push opcode
// followed by a data push between 2 and 40 bytes.
bool CScript::IsWitnessProgram(int& version, std::vector<unsigned char>& program) const
Expand Down
4 changes: 4 additions & 0 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ class CScript : public CScriptBase

bool IsPayToScriptHash() const;
bool IsPayToWitnessScriptHash() const;
bool IsNativePayToWitnessPubKeyHash() const;
bool IsNestedPayToWitnessPubKeyHashScriptSig() const;
bool IsNestedPayToWitnessScriptHashScriptSig() const;

bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;

/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
Expand Down
4 changes: 4 additions & 0 deletions src/test/script_standard_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
BOOST_CHECK(s.IsNativePayToWitnessPubKeyHash());
BOOST_CHECK(!s.IsPayToWitnessScriptHash());

// TX_WITNESS_V0_SCRIPTHASH
uint256 scriptHash;
Expand All @@ -111,6 +113,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
BOOST_CHECK(s.IsPayToWitnessScriptHash());
BOOST_CHECK(!s.IsNativePayToWitnessPubKeyHash());

// TX_NONSTANDARD
s.clear();
Expand Down
474 changes: 324 additions & 150 deletions test/functional/data/rpc_getblockstats.json

Large diffs are not rendered by default.

65 changes: 32 additions & 33 deletions test/functional/rpc_getblockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,16 @@
assert_equal,
assert_raises_rpc_error,
)
from decimal import Decimal
import json
import os
import time

TESTSDIR = os.path.dirname(os.path.realpath(__file__))

class GetblockstatsTest(BitcoinTestFramework):

start_height = 101
max_stat_pos = 2
STATS_NEED_TXINDEX = [
'avgfee',
'avgfeerate',
'maxfee',
'maxfeerate',
'medianfee',
'feerate_percentiles',
'minfee',
'minfeerate',
'totalfee',
'utxo_size_inc',
]

def add_options(self, parser):
parser.add_argument('--gen-test-data', dest='gen_test_data',
Expand All @@ -44,24 +32,45 @@ def add_options(self, parser):
help='Test data file')

def set_test_params(self):
self.num_nodes = 2
self.extra_args = [['-txindex'], ['-paytxfee=0.003']]
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [['-txindex']]

def get_stats(self):
return [self.nodes[0].getblockstats(hash_or_height=self.start_height + i) for i in range(self.max_stat_pos+1)]
return [self.nodes[0].getblockstats(hash_or_height=self.start_height + i) for i in range(self.max_stat_pos + 1)]

def generate_test_data(self, filename):
mocktime = time.time()
mocktime = 1525107225
self.nodes[0].setmocktime(mocktime)
self.nodes[0].generate(101)

self.nodes[0].sendtoaddress(address=self.nodes[1].getnewaddress(), amount=10, subtractfeefromamount=True)
pk1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey']
pk2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey']

nested_p2wpkh_address = self.nodes[0].getnewaddress('', 'p2sh-segwit')
native_p2wpkh_address = self.nodes[0].getnewaddress('', 'bech32')
native_p2wsh_address = self.nodes[0].addmultisigaddress(2, [pk1, pk2], '', 'bech32')['address']
nested_p2wsh_address = self.nodes[0].addmultisigaddress(2, [pk1, pk2], '', 'p2sh-segwit')['address']

# testing batching, nested p2wpkh, dust, native p2wsh, and nested p2wsh metrics
outputs = {nested_p2wpkh_address: 4,
self.nodes[0].getnewaddress('', 'legacy'): 0.00001,
native_p2wsh_address: 2,
nested_p2wsh_address: 30}
self.nodes[0].sendmany('', outputs)

self.nodes[0].generate(1)
self.sync_all()

self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=True)
self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=10, subtractfeefromamount=False)
self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1, subtractfeefromamount=True)
# testing RBF, consolidation, native_p2wpkh and spending metrics
inputs = self.nodes[0].listunspent()
outputs = {native_p2wpkh_address: sum(i['amount'] for i in inputs) - Decimal('0.001')}
rawtx = self.nodes[0].createrawtransaction(inputs=inputs, outputs=outputs, locktime=0, replaceable=True)
signed_tx = self.nodes[0].signrawtransactionwithwallet(hexstring=rawtx)
self.nodes[0].sendrawtransaction(hexstring=signed_tx['hex'])

self.nodes[0].settxfee(amount=0.003)
self.nodes[0].sendtoaddress(address=nested_p2wpkh_address, amount=1, subtractfeefromamount=True)
self.sync_all()
self.nodes[0].generate(1)

Expand Down Expand Up @@ -93,11 +102,12 @@ def load_test_data(self, filename):

# Set the timestamps from the file so that the nodes can get out of Initial Block Download
self.nodes[0].setmocktime(mocktime)
self.nodes[1].setmocktime(mocktime)
self.sync_all()

for b in blocks:
self.nodes[0].submitblock(b)


def run_test(self):
test_data = os.path.join(TESTSDIR, self.options.test_data)
if self.options.gen_test_data:
Expand All @@ -107,9 +117,6 @@ def run_test(self):

self.sync_all()
stats = self.get_stats()
expected_stats_noindex = []
for stat_row in stats:
expected_stats_noindex.append({k: v for k, v in stat_row.items() if k not in self.STATS_NEED_TXINDEX})

# Make sure all valid statistics are included but nothing else is
expected_keys = self.expected_stats[0].keys()
Expand All @@ -127,10 +134,6 @@ def run_test(self):
stats_by_hash = self.nodes[0].getblockstats(hash_or_height=blockhash)
assert_equal(stats_by_hash, self.expected_stats[i])

# Check with the node that has no txindex
stats_no_txindex = self.nodes[1].getblockstats(hash_or_height=blockhash, stats=list(expected_stats_noindex[i].keys()))
assert_equal(stats_no_txindex, expected_stats_noindex[i])

# Make sure each stat can be queried on its own
for stat in expected_keys:
for i in range(self.max_stat_pos+1):
Expand Down Expand Up @@ -168,10 +171,6 @@ def run_test(self):
# Make sure we aren't always returning inv_sel_stat as the culprit stat
assert_raises_rpc_error(-8, 'Invalid selected statistic aaa%s' % inv_sel_stat,
self.nodes[0].getblockstats, hash_or_height=1, stats=['minfee' , 'aaa%s' % inv_sel_stat])

assert_raises_rpc_error(-8, 'One or more of the selected stats requires -txindex enabled',
self.nodes[1].getblockstats, hash_or_height=self.start_height + self.max_stat_pos)

# Mainchain's genesis block shouldn't be found on regtest
assert_raises_rpc_error(-5, 'Block not found', self.nodes[0].getblockstats,
hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')
Expand Down