mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-29 21:42:32 +02:00
Merge 28471 via fix_pkg_eval_sigops_pr28471-25+knots
This commit is contained in:
commit
6e34d1b4d9
@ -18,16 +18,19 @@ tip or some preceding transaction in the package.
|
|||||||
|
|
||||||
The following rules are enforced for all packages:
|
The following rules are enforced for all packages:
|
||||||
|
|
||||||
* Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_SIZE=101KvB` total size
|
* Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_WEIGHT=404000` total weight
|
||||||
(#20833)
|
(#20833)
|
||||||
|
|
||||||
- *Rationale*: This is already enforced as mempool ancestor/descendant limits. If
|
- *Rationale*: We want package size to be as small as possible to mitigate DoS via package
|
||||||
transactions in a package are all related, exceeding this limit would mean that the package
|
validation. However, we want to make sure that the limit does not restrict ancestor
|
||||||
can either be split up or it wouldn't pass individual mempool policy.
|
packages that would be allowed if submitted individually.
|
||||||
|
|
||||||
- Note that, if these mempool limits change, package limits should be reconsidered. Users may
|
- Note that, if these mempool limits change, package limits should be reconsidered. Users may
|
||||||
also configure their mempool limits differently.
|
also configure their mempool limits differently.
|
||||||
|
|
||||||
|
- Note that the this is transaction weight, not "virtual" size as with other limits to allow
|
||||||
|
simpler context-less checks.
|
||||||
|
|
||||||
* Packages must be topologically sorted. (#20833)
|
* Packages must be topologically sorted. (#20833)
|
||||||
|
|
||||||
* Packages cannot have conflicting transactions, i.e. no two transactions in a package can spend
|
* Packages cannot have conflicting transactions, i.e. no two transactions in a package can spend
|
||||||
|
@ -23,10 +23,10 @@ bool CheckPackage(const Package& txns, PackageValidationState& state)
|
|||||||
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
|
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
|
||||||
}
|
}
|
||||||
|
|
||||||
const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
|
const int64_t total_weight = std::accumulate(txns.cbegin(), txns.cend(), 0,
|
||||||
[](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
|
[](int64_t sum, const auto& tx) { return sum + GetTransactionWeight(*tx); });
|
||||||
// If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
|
// If the package only contains 1 tx, it's better to report the policy violation on individual tx weight.
|
||||||
if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
|
if (package_count > 1 && total_weight > MAX_PACKAGE_WEIGHT) {
|
||||||
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
|
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,18 +15,22 @@
|
|||||||
|
|
||||||
/** Default maximum number of transactions in a package. */
|
/** Default maximum number of transactions in a package. */
|
||||||
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
|
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
|
||||||
/** Default maximum total virtual size of transactions in a package in KvB. */
|
/** Default maximum total weight of transactions in a package in weight
|
||||||
static constexpr uint32_t MAX_PACKAGE_SIZE{101};
|
to allow for context-less checks. This must allow a superset of sigops
|
||||||
static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT);
|
weighted vsize limited transactions to not disallow transactions we would
|
||||||
|
have otherwise accepted individually. */
|
||||||
|
static constexpr uint32_t MAX_PACKAGE_WEIGHT = 404'000;
|
||||||
|
static_assert(MAX_PACKAGE_WEIGHT >= MAX_STANDARD_TX_WEIGHT);
|
||||||
|
|
||||||
// If a package is submitted, it must be within the mempool's ancestor/descendant limits. Since a
|
// If a package is to be evaluated, it must be at least as large as the mempool's ancestor/descendant limits,
|
||||||
// submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor
|
// otherwise transactions that would be individually accepted may be rejected in a package erroneously.
|
||||||
|
// Since a submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor
|
||||||
// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the
|
// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the
|
||||||
// defaults reflect this constraint.
|
// defaults reflect this constraint.
|
||||||
static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT);
|
static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT);
|
||||||
static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT);
|
static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT);
|
||||||
static_assert(DEFAULT_ANCESTOR_SIZE_LIMIT_KVB >= MAX_PACKAGE_SIZE);
|
static_assert(MAX_PACKAGE_WEIGHT >= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * WITNESS_SCALE_FACTOR * 1000);
|
||||||
static_assert(DEFAULT_DESCENDANT_SIZE_LIMIT_KVB >= MAX_PACKAGE_SIZE);
|
static_assert(MAX_PACKAGE_WEIGHT >= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * WITNESS_SCALE_FACTOR * 1000);
|
||||||
|
|
||||||
/** A "reason" why a package was invalid. It may be that one or more of the included
|
/** A "reason" why a package was invalid. It may be that one or more of the included
|
||||||
* transactions is invalid or the package itself violates our rules.
|
* transactions is invalid or the package itself violates our rules.
|
||||||
@ -47,7 +51,7 @@ class PackageValidationState : public ValidationState<PackageValidationResult> {
|
|||||||
|
|
||||||
/** Context-free package policy checks:
|
/** Context-free package policy checks:
|
||||||
* 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
|
* 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
|
||||||
* 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE.
|
* 2. The total weight cannot exceed MAX_PACKAGE_WEIGHT.
|
||||||
* 3. If any dependencies exist between transactions, parents must appear before children.
|
* 3. If any dependencies exist between transactions, parents must appear before children.
|
||||||
* 4. Transactions cannot conflict, i.e., spend the same inputs.
|
* 4. Transactions cannot conflict, i.e., spend the same inputs.
|
||||||
*/
|
*/
|
||||||
|
@ -49,14 +49,14 @@ BOOST_FIXTURE_TEST_CASE(package_sanitization_tests, TestChain100Setup)
|
|||||||
BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);
|
BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);
|
||||||
BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");
|
BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");
|
||||||
|
|
||||||
// Packages can't have a total size of more than 101KvB.
|
// Packages can't have a total weight of more than 404'000WU.
|
||||||
CTransactionRef large_ptx = create_placeholder_tx(150, 150);
|
CTransactionRef large_ptx = create_placeholder_tx(150, 150);
|
||||||
Package package_too_large;
|
Package package_too_large;
|
||||||
auto size_large = GetVirtualTransactionSize(*large_ptx);
|
auto size_large = GetTransactionWeight(*large_ptx);
|
||||||
size_t total_size{0};
|
size_t total_weight{0};
|
||||||
while (total_size <= MAX_PACKAGE_SIZE * 1000) {
|
while (total_weight <= MAX_PACKAGE_WEIGHT) {
|
||||||
package_too_large.push_back(large_ptx);
|
package_too_large.push_back(large_ptx);
|
||||||
total_size += size_large;
|
total_weight += size_large;
|
||||||
}
|
}
|
||||||
BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
|
BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
|
||||||
PackageValidationState state_too_large;
|
PackageValidationState state_too_large;
|
||||||
@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
|
|||||||
|
|
||||||
// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
|
// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
|
||||||
CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
|
CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
|
||||||
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000);
|
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000);
|
||||||
auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /*test_accept=*/true);
|
auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /*test_accept=*/true);
|
||||||
BOOST_CHECK(result_single_large.m_state.IsInvalid());
|
BOOST_CHECK(result_single_large.m_state.IsInvalid());
|
||||||
BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
||||||
|
@ -195,13 +195,29 @@ util::Result<CTxMemPool::setEntries> CTxMemPool::CalculateAncestorsAndCheckLimit
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::CheckPackageLimits(const Package& package,
|
bool CTxMemPool::CheckPackageLimits(const Package& package,
|
||||||
|
const int64_t total_vsize,
|
||||||
const Limits& limits,
|
const Limits& limits,
|
||||||
std::string &errString) const
|
std::string &errString) const
|
||||||
{
|
{
|
||||||
|
size_t pack_count = package.size();
|
||||||
|
|
||||||
|
// Package itself is busting mempool limits; should be rejected even if no staged_ancestors exist
|
||||||
|
if (pack_count > static_cast<uint64_t>(m_limits.ancestor_count)) {
|
||||||
|
errString = strprintf("package count %u exceeds ancestor count limit [limit: %u]", pack_count, m_limits.ancestor_count);
|
||||||
|
return false;
|
||||||
|
} else if (pack_count > static_cast<uint64_t>(m_limits.descendant_count)) {
|
||||||
|
errString = strprintf("package count %u exceeds descendant count limit [limit: %u]", pack_count, m_limits.descendant_count);
|
||||||
|
return false;
|
||||||
|
} else if (total_vsize > m_limits.ancestor_size_vbytes) {
|
||||||
|
errString = strprintf("package size %u exceeds ancestor size limit [limit: %u]", total_vsize, m_limits.ancestor_size_vbytes);
|
||||||
|
return false;
|
||||||
|
} else if (total_vsize > m_limits.descendant_size_vbytes) {
|
||||||
|
errString = strprintf("package size %u exceeds descendant size limit [limit: %u]", total_vsize, m_limits.descendant_size_vbytes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CTxMemPoolEntry::Parents staged_ancestors;
|
CTxMemPoolEntry::Parents staged_ancestors;
|
||||||
size_t total_size = 0;
|
|
||||||
for (const auto& tx : package) {
|
for (const auto& tx : package) {
|
||||||
total_size += GetVirtualTransactionSize(*tx);
|
|
||||||
for (const auto& input : tx->vin) {
|
for (const auto& input : tx->vin) {
|
||||||
std::optional<txiter> piter = GetIter(input.prevout.hash);
|
std::optional<txiter> piter = GetIter(input.prevout.hash);
|
||||||
if (piter) {
|
if (piter) {
|
||||||
@ -216,7 +232,7 @@ bool CTxMemPool::CheckPackageLimits(const Package& package,
|
|||||||
// When multiple transactions are passed in, the ancestors and descendants of all transactions
|
// When multiple transactions are passed in, the ancestors and descendants of all transactions
|
||||||
// considered together must be within limits even if they are not interdependent. This may be
|
// considered together must be within limits even if they are not interdependent. This may be
|
||||||
// stricter than the limits for each individual transaction.
|
// stricter than the limits for each individual transaction.
|
||||||
const auto ancestors{CalculateAncestorsAndCheckLimits(total_size, package.size(),
|
const auto ancestors{CalculateAncestorsAndCheckLimits(total_vsize, package.size(),
|
||||||
staged_ancestors, limits)};
|
staged_ancestors, limits)};
|
||||||
// It's possible to overestimate the ancestor/descendant totals.
|
// It's possible to overestimate the ancestor/descendant totals.
|
||||||
if (!ancestors.has_value()) errString = "possibly " + util::ErrorString(ancestors).original;
|
if (!ancestors.has_value()) errString = "possibly " + util::ErrorString(ancestors).original;
|
||||||
|
@ -607,10 +607,12 @@ public:
|
|||||||
* @param[in] package Transaction package being evaluated for acceptance
|
* @param[in] package Transaction package being evaluated for acceptance
|
||||||
* to mempool. The transactions need not be direct
|
* to mempool. The transactions need not be direct
|
||||||
* ancestors/descendants of each other.
|
* ancestors/descendants of each other.
|
||||||
|
* @param[in] total_vsize Sum of virtual sizes for all transactions in package.
|
||||||
* @param[in] limits Maximum number and size of ancestors and descendants
|
* @param[in] limits Maximum number and size of ancestors and descendants
|
||||||
* @param[out] errString Populated with error reason if a limit is hit.
|
* @param[out] errString Populated with error reason if a limit is hit.
|
||||||
*/
|
*/
|
||||||
bool CheckPackageLimits(const Package& package,
|
bool CheckPackageLimits(const Package& package,
|
||||||
|
int64_t total_vsize,
|
||||||
const Limits& limits,
|
const Limits& limits,
|
||||||
std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
|
@ -623,6 +623,7 @@ private:
|
|||||||
// Enforce package mempool ancestor/descendant limits (distinct from individual
|
// Enforce package mempool ancestor/descendant limits (distinct from individual
|
||||||
// ancestor/descendant limits done in PreChecks).
|
// ancestor/descendant limits done in PreChecks).
|
||||||
bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
||||||
|
int64_t total_vsize,
|
||||||
PackageValidationState& package_state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
PackageValidationState& package_state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
||||||
|
|
||||||
// Run the script checks using our policy flags. As this can be slow, we should
|
// Run the script checks using our policy flags. As this can be slow, we should
|
||||||
@ -978,6 +979,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
||||||
|
const int64_t total_vsize,
|
||||||
PackageValidationState& package_state)
|
PackageValidationState& package_state)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
@ -988,7 +990,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
|
|||||||
{ return !m_pool.exists(GenTxid::Txid(tx->GetHash()));}));
|
{ return !m_pool.exists(GenTxid::Txid(tx->GetHash()));}));
|
||||||
|
|
||||||
std::string err_string;
|
std::string err_string;
|
||||||
if (!m_pool.CheckPackageLimits(txns, m_limits, err_string)) {
|
if (!m_pool.CheckPackageLimits(txns, total_vsize, m_limits, err_string)) {
|
||||||
// This is a package-wide error, separate from an individual transaction error.
|
// This is a package-wide error, separate from an individual transaction error.
|
||||||
return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
|
return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
|
||||||
}
|
}
|
||||||
@ -1281,7 +1283,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
|
|||||||
// because it's unnecessary. Also, CPFP carve out can increase the limit for individual
|
// because it's unnecessary. Also, CPFP carve out can increase the limit for individual
|
||||||
// transactions, but this exemption is not extended to packages in CheckPackageLimits().
|
// transactions, but this exemption is not extended to packages in CheckPackageLimits().
|
||||||
std::string err_string;
|
std::string err_string;
|
||||||
if (txns.size() > 1 && !PackageMempoolChecks(txns, package_state)) {
|
if (txns.size() > 1 && !PackageMempoolChecks(txns, m_total_vsize, package_state)) {
|
||||||
return PackageMempoolAcceptResult(package_state, std::move(results));
|
return PackageMempoolAcceptResult(package_state, std::move(results));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test sigop limit mempool policy (`-bytespersigop` parameter)"""
|
"""Test sigop limit mempool policy (`-bytespersigop` parameter)"""
|
||||||
|
from decimal import Decimal
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
@ -25,6 +26,7 @@ from test_framework.script import (
|
|||||||
OP_TRUE,
|
OP_TRUE,
|
||||||
)
|
)
|
||||||
from test_framework.script_util import (
|
from test_framework.script_util import (
|
||||||
|
keys_to_multisig_script,
|
||||||
script_to_p2wsh_script,
|
script_to_p2wsh_script,
|
||||||
)
|
)
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
@ -32,9 +34,10 @@ from test_framework.util import (
|
|||||||
assert_equal,
|
assert_equal,
|
||||||
assert_greater_than,
|
assert_greater_than,
|
||||||
assert_greater_than_or_equal,
|
assert_greater_than_or_equal,
|
||||||
|
assert_raises_rpc_error,
|
||||||
)
|
)
|
||||||
from test_framework.wallet import MiniWallet
|
from test_framework.wallet import MiniWallet
|
||||||
|
from test_framework.wallet_util import generate_keypair
|
||||||
|
|
||||||
DEFAULT_BYTES_PER_SIGOP = 20 # default setting
|
DEFAULT_BYTES_PER_SIGOP = 20 # default setting
|
||||||
|
|
||||||
@ -133,6 +136,45 @@ class BytesPerSigOpTest(BitcoinTestFramework):
|
|||||||
assert_equal(entry_parent['descendantcount'], 2)
|
assert_equal(entry_parent['descendantcount'], 2)
|
||||||
assert_equal(entry_parent['descendantsize'], parent_tx.get_vsize() + sigop_equivalent_vsize)
|
assert_equal(entry_parent['descendantsize'], parent_tx.get_vsize() + sigop_equivalent_vsize)
|
||||||
|
|
||||||
|
def test_sigops_package(self):
|
||||||
|
self.log.info("Test a overly-large sigops-vbyte hits package limits")
|
||||||
|
# Make a 2-transaction package which fails vbyte checks even though
|
||||||
|
# separately they would work.
|
||||||
|
self.restart_node(0, extra_args=["-bytespersigop=5000"] + self.extra_args[0])
|
||||||
|
|
||||||
|
def create_bare_multisig_tx(utxo_to_spend=None):
|
||||||
|
_, pubkey = generate_keypair()
|
||||||
|
amount_for_bare = 50000
|
||||||
|
tx_dict = self.wallet.create_self_transfer(fee=Decimal("3"), utxo_to_spend=utxo_to_spend)
|
||||||
|
tx_utxo = tx_dict["new_utxo"]
|
||||||
|
tx = tx_dict["tx"]
|
||||||
|
tx.vout.append(CTxOut(amount_for_bare, keys_to_multisig_script([pubkey], k=1)))
|
||||||
|
tx.vout[0].nValue -= amount_for_bare
|
||||||
|
tx_utxo["txid"] = tx.rehash()
|
||||||
|
tx_utxo["value"] -= Decimal("0.00005000")
|
||||||
|
return (tx_utxo, tx)
|
||||||
|
|
||||||
|
tx_parent_utxo, tx_parent = create_bare_multisig_tx()
|
||||||
|
tx_child_utxo, tx_child = create_bare_multisig_tx(tx_parent_utxo)
|
||||||
|
|
||||||
|
# Separately, the parent tx is ok
|
||||||
|
parent_individual_testres = self.nodes[0].testmempoolaccept([tx_parent.serialize().hex()])[0]
|
||||||
|
assert parent_individual_testres["allowed"]
|
||||||
|
# Multisig is counted as MAX_PUBKEYS_PER_MULTISIG = 20 sigops
|
||||||
|
assert_equal(parent_individual_testres["vsize"], 5000 * 20)
|
||||||
|
|
||||||
|
# But together, it's exceeding limits in the *package* context. If sigops adjusted vsize wasn't being checked
|
||||||
|
# here, it would get further in validation and give too-long-mempool-chain error instead.
|
||||||
|
packet_test = self.nodes[0].testmempoolaccept([tx_parent.serialize().hex(), tx_child.serialize().hex()])
|
||||||
|
assert_equal([x["package-error"] for x in packet_test], ["package-mempool-limits", "package-mempool-limits"])
|
||||||
|
|
||||||
|
# When we actually try to submit, the parent makes it into the mempool, but the child would exceed ancestor vsize limits
|
||||||
|
assert_raises_rpc_error(-26, "too-long-mempool-chain", self.nodes[0].submitpackage, [tx_parent.serialize().hex(), tx_child.serialize().hex()])
|
||||||
|
assert tx_parent.rehash() in self.nodes[0].getrawmempool()
|
||||||
|
|
||||||
|
# Transactions are tiny in weight
|
||||||
|
assert_greater_than(2000, tx_parent.get_weight() + tx_child.get_weight())
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.wallet = MiniWallet(self.nodes[0])
|
self.wallet = MiniWallet(self.nodes[0])
|
||||||
|
|
||||||
@ -149,6 +191,8 @@ class BytesPerSigOpTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
self.generate(self.wallet, 1)
|
self.generate(self.wallet, 1)
|
||||||
|
|
||||||
|
self.test_sigops_package()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
BytesPerSigOpTest().main()
|
BytesPerSigOpTest().main()
|
||||||
|
@ -119,3 +119,15 @@ def generate_wif_key():
|
|||||||
k = ECKey()
|
k = ECKey()
|
||||||
k.generate()
|
k.generate()
|
||||||
return bytes_to_wif(k.get_bytes(), k.is_compressed)
|
return bytes_to_wif(k.get_bytes(), k.is_compressed)
|
||||||
|
|
||||||
|
def generate_keypair(compressed=True, wif=False):
|
||||||
|
"""Generate a new random keypair and return the corresponding ECKey /
|
||||||
|
bytes objects. The private key can also be provided as WIF (wallet
|
||||||
|
import format) string instead, which is often useful for wallet RPC
|
||||||
|
interaction."""
|
||||||
|
privkey = ECKey()
|
||||||
|
privkey.generate(compressed)
|
||||||
|
pubkey = privkey.get_pubkey().get_bytes()
|
||||||
|
if wif:
|
||||||
|
privkey = bytes_to_wif(privkey.get_bytes(), compressed)
|
||||||
|
return privkey, pubkey
|
||||||
|
Loading…
Reference in New Issue
Block a user