mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 02:33:24 -03:00
Compare commits
6 commits
719db974a6
...
5c4567888f
Author | SHA1 | Date | |
---|---|---|---|
|
5c4567888f | ||
|
66aa6a47bd | ||
|
7c123c08dd | ||
|
8da5ab60b5 | ||
|
abb8228944 | ||
|
925fdfd67f |
6 changed files with 291 additions and 5 deletions
|
@ -421,6 +421,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|||
}
|
||||
|
||||
++nPackagesSelected;
|
||||
pblocktemplate->m_package_feerates.emplace_back(packageFees, static_cast<int32_t>(packageSize));
|
||||
|
||||
// Update transactions that depend on each of these
|
||||
nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <primitives/block.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/feefrac.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -39,6 +40,9 @@ struct CBlockTemplate
|
|||
std::vector<CAmount> vTxFees;
|
||||
std::vector<int64_t> vTxSigOpsCost;
|
||||
std::vector<unsigned char> vchCoinbaseCommitment;
|
||||
/* A vector of package fee rates, ordered by the sequence in which
|
||||
* packages are selected for inclusion in the block template.*/
|
||||
std::vector<FeeFrac> m_package_feerates;
|
||||
};
|
||||
|
||||
// Container for tracking updates to ancestor feerate as we include (parent)
|
||||
|
|
|
@ -348,7 +348,7 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
|
|||
// Construct a would-be spend of this output, to update sigdata with.
|
||||
// Note that ProduceSignature is used to fill in metadata (not actual signatures),
|
||||
// so provider does not need to provide any private keys (it can be a HidingSigningProvider).
|
||||
MutableTransactionSignatureCreator creator(tx, /*input_idx=*/0, out.nValue, SIGHASH_ALL);
|
||||
MutableTransactionSignatureCreator creator(tx, /*input_idx=*/0, out.nValue, SIGHASH_DEFAULT);
|
||||
ProduceSignature(provider, creator, out.scriptPubKey, sigdata);
|
||||
|
||||
// Put redeem_script, witness_script, key paths, into PSBTOutput.
|
||||
|
@ -486,7 +486,8 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
|
|||
bool complete = true;
|
||||
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true);
|
||||
int sighash_type = psbtx.inputs.at(i).sighash_type.value_or(SIGHASH_DEFAULT);
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, sighash_type, nullptr, true);
|
||||
}
|
||||
|
||||
return complete;
|
||||
|
|
156
src/psbt.h
156
src/psbt.h
|
@ -50,6 +50,9 @@ static constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15;
|
|||
static constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16;
|
||||
static constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17;
|
||||
static constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18;
|
||||
static constexpr uint8_t PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a;
|
||||
static constexpr uint8_t PSBT_IN_MUSIG2_PUB_NONCE = 0x1b;
|
||||
static constexpr uint8_t PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c;
|
||||
static constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC;
|
||||
|
||||
// Output types
|
||||
|
@ -59,6 +62,7 @@ static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
|
|||
static constexpr uint8_t PSBT_OUT_TAP_INTERNAL_KEY = 0x05;
|
||||
static constexpr uint8_t PSBT_OUT_TAP_TREE = 0x06;
|
||||
static constexpr uint8_t PSBT_OUT_TAP_BIP32_DERIVATION = 0x07;
|
||||
static constexpr uint8_t PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08;
|
||||
static constexpr uint8_t PSBT_OUT_PROPRIETARY = 0xFC;
|
||||
|
||||
// The separator is 0x00. Reading this in means that the unserializer can interpret it
|
||||
|
@ -217,6 +221,11 @@ struct PSBTInput
|
|||
XOnlyPubKey m_tap_internal_key;
|
||||
uint256 m_tap_merkle_root;
|
||||
|
||||
// MuSig2 fields
|
||||
std::map<CPubKey, std::vector<CPubKey>> m_musig2_participants;
|
||||
std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, std::vector<uint8_t>>> m_musig2_pubnonces;
|
||||
std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, uint256>> m_musig2_partial_sigs;
|
||||
|
||||
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
|
||||
std::set<PSBTProprietary> m_proprietary;
|
||||
std::optional<int> sighash_type;
|
||||
|
@ -337,6 +346,43 @@ struct PSBTInput
|
|||
SerializeToVector(s, PSBT_IN_TAP_MERKLE_ROOT);
|
||||
SerializeToVector(s, m_tap_merkle_root);
|
||||
}
|
||||
|
||||
// Write MuSig2 Participants
|
||||
for (const auto& [agg_key, part_pubs] : m_musig2_participants) {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS), Span{agg_key});
|
||||
std::vector<unsigned char> value;
|
||||
VectorWriter s_value{value, 0};
|
||||
for (auto& pk : part_pubs) {
|
||||
s_value << Span{pk};
|
||||
}
|
||||
s << value;
|
||||
}
|
||||
|
||||
// Write MuSig2 pubnonces
|
||||
for (const auto& [agg_key_leaf_hash, pubnonces] : m_musig2_pubnonces) {
|
||||
const auto& [agg_key, leaf_hash] = agg_key_leaf_hash;
|
||||
for (const auto& [pubkey, pubnonce] : pubnonces) {
|
||||
if (leaf_hash.IsNull()) {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_IN_MUSIG2_PUB_NONCE), Span{pubkey}, Span{agg_key});
|
||||
} else {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_IN_MUSIG2_PUB_NONCE), Span{pubkey}, Span{agg_key}, leaf_hash);
|
||||
}
|
||||
s << pubnonce;
|
||||
}
|
||||
}
|
||||
|
||||
// Write MuSig2 partial signatures
|
||||
for (const auto& [agg_key_leaf_hash, psigs] : m_musig2_partial_sigs) {
|
||||
const auto& [agg_key, leaf_hash] = agg_key_leaf_hash;
|
||||
for (const auto& [pubkey, psig] : psigs) {
|
||||
if (leaf_hash.IsNull()) {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_IN_MUSIG2_PARTIAL_SIG), Span{pubkey}, Span{agg_key});
|
||||
} else {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_IN_MUSIG2_PARTIAL_SIG), Span{pubkey}, Span{agg_key}, leaf_hash);
|
||||
}
|
||||
SerializeToVector(s, psig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write script sig
|
||||
|
@ -672,6 +718,80 @@ struct PSBTInput
|
|||
UnserializeFromVector(s, m_tap_merkle_root);
|
||||
break;
|
||||
}
|
||||
case PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS:
|
||||
{
|
||||
if (!key_lookup.emplace(key).second) {
|
||||
throw std::ios_base::failure("Duplicate Key, input participant pubkeys for an aggregate key already provided");
|
||||
} else if (key.size() != 34) {
|
||||
throw std::ios_base::failure("Input musig2 participants pubkeys aggregate key is more than 33 bytes");
|
||||
}
|
||||
std::array<unsigned char, 33> agg_key_bytes;
|
||||
skey >> AsWritableBytes(Span{agg_key_bytes});
|
||||
CPubKey agg_key(agg_key_bytes);
|
||||
|
||||
std::vector<CPubKey> participants;
|
||||
std::vector<unsigned char> val;
|
||||
s >> val;
|
||||
SpanReader s_val{val};
|
||||
while (!s_val.empty()) {
|
||||
std::array<unsigned char, 33> part_key_bytes;
|
||||
s_val >> AsWritableBytes(Span{part_key_bytes});
|
||||
participants.emplace_back(Span{part_key_bytes});
|
||||
}
|
||||
|
||||
m_musig2_participants.emplace(agg_key, participants);
|
||||
break;
|
||||
}
|
||||
case PSBT_IN_MUSIG2_PUB_NONCE:
|
||||
{
|
||||
if (!key_lookup.emplace(key).second) {
|
||||
throw std::ios_base::failure("Duplicate Key, input musig2 pubnonce already provided");
|
||||
} else if (key.size() != 67 && key.size() != 99) {
|
||||
throw std::ios_base::failure("Input musig2 pubnonce key is not expected size");
|
||||
}
|
||||
std::array<unsigned char, 33> part_pubkey_bytes;
|
||||
std::array<unsigned char, 33> agg_pubkey_bytes;
|
||||
uint256 leaf_hash;
|
||||
|
||||
skey >> AsWritableBytes(Span{part_pubkey_bytes}) >> AsWritableBytes(Span{agg_pubkey_bytes});
|
||||
CPubKey agg_pub{Span{agg_pubkey_bytes}};
|
||||
CPubKey part_pub{Span{part_pubkey_bytes}};
|
||||
|
||||
if (!skey.empty()) {
|
||||
skey >> leaf_hash;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pubnonce;
|
||||
s >> pubnonce;
|
||||
|
||||
m_musig2_pubnonces[std::make_pair(agg_pub, leaf_hash)].emplace(part_pub, pubnonce);
|
||||
break;
|
||||
}
|
||||
case PSBT_IN_MUSIG2_PARTIAL_SIG:
|
||||
{
|
||||
if (!key_lookup.emplace(key).second) {
|
||||
throw std::ios_base::failure("Duplicate Key, input musig2 partial sig already provided");
|
||||
} else if (key.size() != 67 && key.size() != 99) {
|
||||
throw std::ios_base::failure("Input musig2 partial sig key is not expected size");
|
||||
}
|
||||
std::array<unsigned char, 33> part_pubkey_bytes;
|
||||
std::array<unsigned char, 33> agg_pubkey_bytes;
|
||||
uint256 leaf_hash;
|
||||
|
||||
skey >> AsWritableBytes(Span{part_pubkey_bytes}) >> AsWritableBytes(Span{agg_pubkey_bytes});
|
||||
CPubKey agg_pub{Span{agg_pubkey_bytes}};
|
||||
CPubKey part_pub{Span{part_pubkey_bytes}};
|
||||
|
||||
if (!skey.empty()) {
|
||||
skey >> leaf_hash;
|
||||
}
|
||||
|
||||
uint256 partial_sig;
|
||||
UnserializeFromVector(s, partial_sig);
|
||||
|
||||
m_musig2_partial_sigs[std::make_pair(agg_pub, leaf_hash)].emplace(part_pub, partial_sig);
|
||||
break;
|
||||
}
|
||||
case PSBT_IN_PROPRIETARY:
|
||||
{
|
||||
PSBTProprietary this_prop;
|
||||
|
@ -719,6 +839,7 @@ struct PSBTOutput
|
|||
XOnlyPubKey m_tap_internal_key;
|
||||
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> m_tap_tree;
|
||||
std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
|
||||
std::map<CPubKey, std::vector<CPubKey>> m_musig2_participants;
|
||||
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
|
||||
std::set<PSBTProprietary> m_proprietary;
|
||||
|
||||
|
@ -781,6 +902,17 @@ struct PSBTOutput
|
|||
s << value;
|
||||
}
|
||||
|
||||
// Write MuSig2 Participants
|
||||
for (const auto& [agg_key, part_pubs] : m_musig2_participants) {
|
||||
SerializeToVector(s, CompactSizeWriter(PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS), Span{agg_key});
|
||||
std::vector<unsigned char> value;
|
||||
VectorWriter s_value{value, 0};
|
||||
for (auto& pk : part_pubs) {
|
||||
s_value << Span{pk};
|
||||
}
|
||||
s << value;
|
||||
}
|
||||
|
||||
// Write unknown things
|
||||
for (auto& entry : unknown) {
|
||||
s << entry.first;
|
||||
|
@ -907,6 +1039,30 @@ struct PSBTOutput
|
|||
m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len)));
|
||||
break;
|
||||
}
|
||||
case PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS:
|
||||
{
|
||||
if (!key_lookup.emplace(key).second) {
|
||||
throw std::ios_base::failure("Duplicate Key, output participant pubkeys for an aggregate key already provided");
|
||||
} else if (key.size() != 34) {
|
||||
throw std::ios_base::failure("Output musig2 participants pubkeys aggregate key is more than 33 bytes");
|
||||
}
|
||||
std::array<unsigned char, 33> agg_key_bytes;
|
||||
skey.read(AsWritableBytes(Span{agg_key_bytes}));
|
||||
CPubKey agg_key(agg_key_bytes);
|
||||
|
||||
std::vector<CPubKey> participants;
|
||||
std::vector<unsigned char> val;
|
||||
s >> val;
|
||||
SpanReader s_val{val};
|
||||
while (!s_val.empty()) {
|
||||
std::array<unsigned char, 33> part_key_bytes;
|
||||
s_val.read(AsWritableBytes(Span{part_key_bytes}));
|
||||
participants.emplace_back(Span{part_key_bytes});
|
||||
}
|
||||
|
||||
m_musig2_participants.emplace(agg_key, participants);
|
||||
break;
|
||||
}
|
||||
case PSBT_OUT_PROPRIETARY:
|
||||
{
|
||||
PSBTProprietary this_prop;
|
||||
|
|
|
@ -916,6 +916,37 @@ const RPCResult decodepsbt_inputs{
|
|||
}},
|
||||
{RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"},
|
||||
{RPCResult::Type::STR_HEX, "taproot_merkle_root", /*optional=*/ true, "The hex-encoded Taproot merkle root"},
|
||||
{RPCResult::Type::ARR, "musig2_participant_pubkeys", /*optional*/true, "",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The plain aggregate public key for which the participants create."},
|
||||
{RPCResult::Type::ARR, "participant_pubkeys", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "pubkey", "The plain public keys that are aggregated for aggregate_pubkey."},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::ARR, "musig2_pubnonces", /*optional*/true, "",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "participant_pubkey", "The plain public key of the participant that created this pubnonce."},
|
||||
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The plain aggregate public key for which this pubnonce is for."},
|
||||
{RPCResult::Type::STR_HEX, "leaf_hash", /*optional=*/true, "The hash of the leaf script that contains the aggregate pubkey being signed for. Omitted when signing for the internal key."},
|
||||
{RPCResult::Type::STR_HEX, "pubnonce", "The public nonce itself."},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::ARR, "musig2_partial_sigs", /*optional*/true, "",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "participant_pubkey", "The plain public key of the participant that created this partial signature."},
|
||||
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The plain aggregate public key for which this partial signature is for."},
|
||||
{RPCResult::Type::STR_HEX, "leaf_hash", /*optional=*/true, "The hash of the leaf script that contains the aggregate pubkey being signed for. Omitted when signing for the internal key."},
|
||||
{RPCResult::Type::STR_HEX, "partial_sig", "The partial signature itself."},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
|
||||
|
@ -983,6 +1014,17 @@ const RPCResult decodepsbt_outputs{
|
|||
}},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::ARR, "musig2_participant_pubkeys", /*optional*/true, "",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "aggregate_pubkey", "The plain aggregate public key for which the participants create."},
|
||||
{RPCResult::Type::ARR, "participant_pubkeys", "",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "pubkey", "The plain public keys that are aggregated for aggregate_pubkey."},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown output fields",
|
||||
{
|
||||
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
|
||||
|
@ -1304,6 +1346,52 @@ static RPCHelpMan decodepsbt()
|
|||
in.pushKV("taproot_merkle_root", HexStr(input.m_tap_merkle_root));
|
||||
}
|
||||
|
||||
// Write MuSig2 fields
|
||||
if (!input.m_musig2_participants.empty()) {
|
||||
UniValue musig_keys(UniValue::VARR);
|
||||
for (const auto& [agg, parts] : input.m_musig2_participants) {
|
||||
UniValue musig_part(UniValue::VOBJ);
|
||||
musig_part.pushKV("aggregate_pubkey", HexStr(agg));
|
||||
UniValue part_keys(UniValue::VARR);
|
||||
for (const auto& pub : parts) {
|
||||
part_keys.push_back(HexStr(pub));
|
||||
}
|
||||
musig_part.pushKV("participant_pubkeys", part_keys);
|
||||
musig_keys.push_back(musig_part);
|
||||
}
|
||||
in.pushKV("musig2_participant_pubkeys", musig_keys);
|
||||
}
|
||||
if (!input.m_musig2_pubnonces.empty()) {
|
||||
UniValue musig_pubnonces(UniValue::UniValue::VARR);
|
||||
for (const auto& [agg_lh, part_pubnonce] : input.m_musig2_pubnonces) {
|
||||
const auto& [agg, lh] = agg_lh;
|
||||
for (const auto& [part, pubnonce] : part_pubnonce) {
|
||||
UniValue info(UniValue::VOBJ);
|
||||
info.pushKV("participant_pubkey", HexStr(part));
|
||||
info.pushKV("aggregate_pubkey", HexStr(agg));
|
||||
if (!lh.IsNull()) info.pushKV("leaf_hash", HexStr(lh));
|
||||
info.pushKV("pubnonce", HexStr(pubnonce));
|
||||
musig_pubnonces.push_back(info);
|
||||
}
|
||||
}
|
||||
in.pushKV("musig2_pubnonces", musig_pubnonces);
|
||||
}
|
||||
if (!input.m_musig2_partial_sigs.empty()) {
|
||||
UniValue musig_partial_sigs(UniValue::UniValue::VARR);
|
||||
for (const auto& [agg_lh, part_psig] : input.m_musig2_partial_sigs) {
|
||||
const auto& [agg, lh] = agg_lh;
|
||||
for (const auto& [part, psig] : part_psig) {
|
||||
UniValue info(UniValue::VOBJ);
|
||||
info.pushKV("participant_pubkey", HexStr(part));
|
||||
info.pushKV("aggregate_pubkey", HexStr(agg));
|
||||
if (!lh.IsNull()) info.pushKV("leaf_hash", HexStr(lh));
|
||||
info.pushKV("partial_sig", HexStr(psig));
|
||||
musig_partial_sigs.push_back(info);
|
||||
}
|
||||
}
|
||||
in.pushKV("musig2_partial_sigs", musig_partial_sigs);
|
||||
}
|
||||
|
||||
// Proprietary
|
||||
if (!input.m_proprietary.empty()) {
|
||||
UniValue proprietary(UniValue::VARR);
|
||||
|
@ -1399,6 +1487,22 @@ static RPCHelpMan decodepsbt()
|
|||
out.pushKV("taproot_bip32_derivs", std::move(keypaths));
|
||||
}
|
||||
|
||||
// Write MuSig2 fields
|
||||
if (!output.m_musig2_participants.empty()) {
|
||||
UniValue musig_keys(UniValue::VARR);
|
||||
for (const auto& [agg, parts] : output.m_musig2_participants) {
|
||||
UniValue musig_part(UniValue::VOBJ);
|
||||
musig_part.pushKV("aggregate_pubkey", HexStr(agg));
|
||||
UniValue part_keys(UniValue::VARR);
|
||||
for (const auto& pub : parts) {
|
||||
part_keys.push_back(HexStr(pub));
|
||||
}
|
||||
musig_part.pushKV("participant_pubkeys", part_keys);
|
||||
musig_keys.push_back(musig_part);
|
||||
}
|
||||
out.pushKV("musig2_participant_pubkeys", musig_keys);
|
||||
}
|
||||
|
||||
// Proprietary
|
||||
if (!output.m_proprietary.empty()) {
|
||||
UniValue proprietary(UniValue::VARR);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/feefrac.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
|
@ -123,19 +125,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
tx.vout[0].nValue = 5000000000LL - 1000;
|
||||
// This tx has a low fee: 1000 satoshis
|
||||
Txid hashParentTx = tx.GetHash(); // save this txid for later use
|
||||
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto parent_tx{entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, parent_tx);
|
||||
|
||||
// This tx has a medium fee: 10000 satoshis
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = 5000000000LL - 10000;
|
||||
Txid hashMediumFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto medium_fee_tx{entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, medium_fee_tx);
|
||||
|
||||
// This tx has a high fee, but depends on the first transaction
|
||||
tx.vin[0].prevout.hash = hashParentTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
||||
Txid hashHighFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
|
||||
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, high_fee_tx);
|
||||
|
||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||
BOOST_REQUIRE(block_template);
|
||||
|
@ -145,6 +150,21 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
||||
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
|
||||
|
||||
// Test the inclusion of package feerates in the block template and ensure they are sequential.
|
||||
const auto block_package_feerates = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock()->m_package_feerates;
|
||||
BOOST_CHECK(block_package_feerates.size() == 2);
|
||||
|
||||
// parent_tx and high_fee_tx are added to the block as a package.
|
||||
const auto combined_txs_fee = parent_tx.GetFee() + high_fee_tx.GetFee();
|
||||
const auto combined_txs_size = parent_tx.GetTxSize() + high_fee_tx.GetTxSize();
|
||||
FeeFrac package_feefrac{combined_txs_fee, combined_txs_size};
|
||||
// The package should be added first.
|
||||
BOOST_CHECK(block_package_feerates[0] == package_feefrac);
|
||||
|
||||
// The medium_fee_tx should be added next.
|
||||
FeeFrac medium_tx_feefrac{medium_fee_tx.GetFee(), medium_fee_tx.GetTxSize()};
|
||||
BOOST_CHECK(block_package_feerates[1] == medium_tx_feefrac);
|
||||
|
||||
// Test that a package below the block min tx fee doesn't get included
|
||||
tx.vin[0].prevout.hash = hashHighFeeTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
||||
|
|
Loading…
Add table
Reference in a new issue