mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-13 03:30:42 +02:00
Merge 28408 via match_more_datacarrier-28+knots
This commit is contained in:
commit
570cb5cb1d
@ -680,9 +680,9 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||||||
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||||
argsman.AddArg("-bytespersigopstrict", strprintf("Minimum bytes per sigop in transactions we relay and mine (default: %u)", DEFAULT_BYTES_PER_SIGOP_STRICT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
argsman.AddArg("-bytespersigopstrict", strprintf("Minimum bytes per sigop in transactions we relay and mine (default: %u)", DEFAULT_BYTES_PER_SIGOP_STRICT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||||
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||||
|
argsman.AddArg("-datacarrierfullcount", strprintf("Apply datacarriersize limit to all known datacarrier methods (default: %u)", DEFAULT_DATACARRIER_FULLCOUNT), ArgsManager::ALLOW_ANY | (DEFAULT_DATACARRIER_FULLCOUNT ? uint32_t{ArgsManager::DEBUG_ONLY} : 0), OptionsCategory::NODE_RELAY);
|
||||||
argsman.AddArg("-datacarriersize",
|
argsman.AddArg("-datacarriersize",
|
||||||
strprintf("Relay and mine transactions whose data-carrying raw scriptPubKey "
|
strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (default: %u)",
|
||||||
"is of this size or less (default: %u)",
|
|
||||||
MAX_OP_RETURN_RELAY),
|
MAX_OP_RETURN_RELAY),
|
||||||
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||||
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", (DEFAULT_MEMPOOL_RBF_POLICY == RBFPolicy::Always)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", (DEFAULT_MEMPOOL_RBF_POLICY == RBFPolicy::Always)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||||
|
@ -68,6 +68,7 @@ struct MemPoolOptions {
|
|||||||
* If nullopt, any size is nonstandard.
|
* If nullopt, any size is nonstandard.
|
||||||
*/
|
*/
|
||||||
std::optional<unsigned> max_datacarrier_bytes{DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} : std::nullopt};
|
std::optional<unsigned> max_datacarrier_bytes{DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} : std::nullopt};
|
||||||
|
bool datacarrier_fullcount{DEFAULT_DATACARRIER_FULLCOUNT};
|
||||||
bool permit_bare_pubkey{DEFAULT_PERMIT_BAREPUBKEY};
|
bool permit_bare_pubkey{DEFAULT_PERMIT_BAREPUBKEY};
|
||||||
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
|
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
|
||||||
bool require_standard{true};
|
bool require_standard{true};
|
||||||
|
@ -151,6 +151,7 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
|
|||||||
} else {
|
} else {
|
||||||
mempool_opts.max_datacarrier_bytes = std::nullopt;
|
mempool_opts.max_datacarrier_bytes = std::nullopt;
|
||||||
}
|
}
|
||||||
|
mempool_opts.datacarrier_fullcount = argsman.GetBoolArg("-datacarrierfullcount", DEFAULT_DATACARRIER_FULLCOUNT);
|
||||||
|
|
||||||
mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);
|
mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);
|
||||||
|
|
||||||
|
@ -372,3 +372,66 @@ int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, un
|
|||||||
{
|
{
|
||||||
return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop);
|
return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<CScript, unsigned int> GetScriptForTransactionInput(CScript prevScript, const CTxIn& txin)
|
||||||
|
{
|
||||||
|
bool p2sh = false;
|
||||||
|
if (prevScript.IsPayToScriptHash()) {
|
||||||
|
std::vector <std::vector<unsigned char> > stack;
|
||||||
|
if (!EvalScript(stack, txin.scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE)) {
|
||||||
|
return std::make_pair(CScript(), 0);
|
||||||
|
}
|
||||||
|
if (stack.empty()) {
|
||||||
|
return std::make_pair(CScript(), 0);
|
||||||
|
}
|
||||||
|
prevScript = CScript(stack.back().begin(), stack.back().end());
|
||||||
|
p2sh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int witnessversion = 0;
|
||||||
|
std::vector<unsigned char> witnessprogram;
|
||||||
|
|
||||||
|
if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram)) {
|
||||||
|
// For P2SH, scriptSig is always push-only, so the actual script is only the last stack item
|
||||||
|
// For non-P2SH, prevScript is likely the real script, but not part of this transaction, and scriptSig could very well be executable, so return the latter instead
|
||||||
|
return std::make_pair(p2sh ? prevScript : txin.scriptSig, WITNESS_SCALE_FACTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span stack{txin.scriptWitness.stack};
|
||||||
|
|
||||||
|
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
|
||||||
|
if (stack.empty()) return std::make_pair(CScript(), 0); // invalid
|
||||||
|
auto& script_data = stack.back();
|
||||||
|
prevScript = CScript(script_data.begin(), script_data.end());
|
||||||
|
return std::make_pair(prevScript, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) {
|
||||||
|
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
|
||||||
|
SpanPopBack(stack);
|
||||||
|
}
|
||||||
|
if (stack.size() >= 2) {
|
||||||
|
SpanPopBack(stack); // Ignore control block
|
||||||
|
prevScript = CScript(stack.back().begin(), stack.back().end());
|
||||||
|
return std::make_pair(prevScript, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(CScript(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view)
|
||||||
|
{
|
||||||
|
size_t ret{0};
|
||||||
|
|
||||||
|
for (const CTxIn& txin : tx.vin) {
|
||||||
|
const CTxOut &utxo = view.AccessCoin(txin.prevout).out;
|
||||||
|
auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin);
|
||||||
|
ret += script.DatacarrierBytes();
|
||||||
|
}
|
||||||
|
for (const CTxOut& txout : tx.vout) {
|
||||||
|
ret += txout.scriptPubKey.DatacarrierBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
class CCoinsViewCache;
|
class CCoinsViewCache;
|
||||||
class CFeeRate;
|
class CFeeRate;
|
||||||
@ -87,6 +88,8 @@ static const bool DEFAULT_ACCEPT_DATACARRIER = true;
|
|||||||
* +2 for the pushdata opcodes.
|
* +2 for the pushdata opcodes.
|
||||||
*/
|
*/
|
||||||
static const unsigned int MAX_OP_RETURN_RELAY = 83;
|
static const unsigned int MAX_OP_RETURN_RELAY = 83;
|
||||||
|
/** Default for -datacarrierfullcount */
|
||||||
|
static constexpr bool DEFAULT_DATACARRIER_FULLCOUNT{false};
|
||||||
/**
|
/**
|
||||||
* An extra transaction can be added to a package, as long as it only has one
|
* An extra transaction can be added to a package, as long as it only has one
|
||||||
* ancestor and is no larger than this. Not really any reason to make this
|
* ancestor and is no larger than this. Not really any reason to make this
|
||||||
@ -194,4 +197,8 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx)
|
|||||||
return GetVirtualTransactionInputSize(tx, 0, 0);
|
return GetVirtualTransactionInputSize(tx, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<CScript, unsigned int> GetScriptForTransactionInput(CScript prevScript, const CTxIn&);
|
||||||
|
|
||||||
|
size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view);
|
||||||
|
|
||||||
#endif // BITCOIN_POLICY_POLICY_H
|
#endif // BITCOIN_POLICY_POLICY_H
|
||||||
|
@ -303,6 +303,56 @@ bool CScript::HasValidOps() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t CScript::DatacarrierBytes() const
|
||||||
|
{
|
||||||
|
size_t counted{0};
|
||||||
|
opcodetype opcode, last_opcode{OP_INVALIDOPCODE};
|
||||||
|
std::vector<unsigned char> push_data;
|
||||||
|
unsigned int inside_noop{0}, inside_conditional{0};
|
||||||
|
CScript::const_iterator opcode_it = begin(), data_began = begin();
|
||||||
|
for (CScript::const_iterator it = begin(); it < end(); last_opcode = opcode) {
|
||||||
|
opcode_it = it;
|
||||||
|
if (!GetOp(it, opcode, push_data)) {
|
||||||
|
// Invalid scripts are necessarily all data
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode == OP_IF || opcode == OP_NOTIF) {
|
||||||
|
++inside_conditional;
|
||||||
|
} else if (opcode == OP_ENDIF) {
|
||||||
|
if (!inside_conditional) return size(); // invalid
|
||||||
|
--inside_conditional;
|
||||||
|
} else if (opcode == OP_RETURN && !inside_conditional) {
|
||||||
|
// unconditional OP_RETURN is unspendable
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match OP_FALSE OP_IF
|
||||||
|
if (inside_noop) {
|
||||||
|
switch (opcode) {
|
||||||
|
case OP_IF: case OP_NOTIF:
|
||||||
|
++inside_noop;
|
||||||
|
break;
|
||||||
|
case OP_ENDIF:
|
||||||
|
if (0 == --inside_noop) {
|
||||||
|
counted += it - data_began + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: /* do nothing */;
|
||||||
|
}
|
||||||
|
} else if (opcode == OP_IF && last_opcode == OP_FALSE) {
|
||||||
|
inside_noop = 1;
|
||||||
|
data_began = opcode_it;
|
||||||
|
// Match <data> OP_DROP
|
||||||
|
} else if (opcode <= OP_PUSHDATA4) {
|
||||||
|
data_began = opcode_it;
|
||||||
|
} else if (opcode == OP_DROP && last_opcode <= OP_PUSHDATA4) {
|
||||||
|
counted += it - data_began;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counted;
|
||||||
|
}
|
||||||
|
|
||||||
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
|
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
|
||||||
{
|
{
|
||||||
opcodeRet = OP_INVALIDOPCODE;
|
opcodeRet = OP_INVALIDOPCODE;
|
||||||
|
@ -562,6 +562,8 @@ public:
|
|||||||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
|
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t DatacarrierBytes() const;
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
// The default prevector::clear() does not release memory
|
// The default prevector::clear() does not release memory
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <common/system.h>
|
#include <common/system.h>
|
||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <key.h>
|
#include <key.h>
|
||||||
|
#include <policy/policy.h>
|
||||||
#include <rpc/util.h>
|
#include <rpc/util.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <script/script_error.h>
|
#include <script/script_error.h>
|
||||||
@ -1504,6 +1505,261 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
|
|||||||
BOOST_CHECK(!script.HasValidOps());
|
BOOST_CHECK(!script.HasValidOps());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_DataCarrierBytes)
|
||||||
|
{
|
||||||
|
using zeros = std::vector<unsigned char>;
|
||||||
|
|
||||||
|
// empty script
|
||||||
|
BOOST_CHECK_EQUAL(0, (CScript()).DatacarrierBytes());
|
||||||
|
// series of pushes are not data
|
||||||
|
BOOST_CHECK_EQUAL(0, (CScript() << OP_0 << OP_0 << OP_0).DatacarrierBytes());
|
||||||
|
// unspendable if first op is OP_RETURN, then length(1), zeros(11)
|
||||||
|
BOOST_CHECK_EQUAL(13, (CScript() << OP_RETURN << zeros(11)).DatacarrierBytes());
|
||||||
|
// invalid script (no data following PUSHDATA) makes it all data
|
||||||
|
BOOST_CHECK_EQUAL(2, (CScript() << OP_0 << OP_PUSHDATA4).DatacarrierBytes());
|
||||||
|
// no data here
|
||||||
|
BOOST_CHECK_EQUAL(0, (CScript() << OP_TRUE << OP_IF << OP_ENDIF).DatacarrierBytes());
|
||||||
|
// specific data pattern, entire script is data
|
||||||
|
BOOST_CHECK_EQUAL(4, (CScript() << OP_FALSE << OP_IF << OP_7 << OP_ENDIF).DatacarrierBytes());
|
||||||
|
// consecutive data
|
||||||
|
BOOST_CHECK_EQUAL(6, (CScript() << OP_FALSE << OP_IF << OP_ENDIF << OP_FALSE << OP_IF << OP_ENDIF).DatacarrierBytes());
|
||||||
|
// nested data (all is data)
|
||||||
|
BOOST_CHECK_EQUAL(6, (CScript() << OP_FALSE << OP_IF << OP_TRUE << OP_IF << OP_ENDIF << OP_ENDIF).DatacarrierBytes());
|
||||||
|
// pushing then immediately dropping is data: length(1), zero(11), OP_DROP
|
||||||
|
BOOST_CHECK_EQUAL(13, (CScript() << zeros(11) << OP_DROP).DatacarrierBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
|
||||||
|
{
|
||||||
|
using zeros = std::vector<unsigned char>;
|
||||||
|
|
||||||
|
{ // P2PK - no datacarrier bytes (tx_in doesn't matter)
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << zeros(65) << OP_CHECKSIG;
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == tx_in.scriptSig);
|
||||||
|
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2PKH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_DUP << OP_HASH160 << zeros(20) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
|
// signature, pubkey
|
||||||
|
tx_in.scriptSig = CScript() << zeros(72) << zeros(33);
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == tx_in.scriptSig);
|
||||||
|
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2SH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
CScript redeem_script = CScript() << OP_DROP << OP_TRUE;
|
||||||
|
prev_script = CScript() << OP_HASH160 << zeros(20) << OP_EQUAL;
|
||||||
|
// signature, pubkey, redeem_script
|
||||||
|
tx_in.scriptSig = CScript() << OP_7 << std::vector<unsigned char>(redeem_script.begin(), redeem_script.end());
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == redeem_script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2SH - with datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
// arbitrary amount of data (27 bytes)
|
||||||
|
CScript redeem_script = CScript() << OP_RETURN << zeros(27);
|
||||||
|
prev_script = CScript() << OP_HASH160 << zeros(20) << OP_EQUAL;
|
||||||
|
// signature, pubkey, redeem_script
|
||||||
|
tx_in.scriptSig = CScript() << OP_7 << std::vector<unsigned char>(redeem_script.begin(), redeem_script.end());
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == redeem_script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
|
||||||
|
// OP_RETURN(1), length(1), zeros(27) = 29
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 29);
|
||||||
|
}
|
||||||
|
{ // P2WPKH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
// P2WPKH is [OP_0, hash160(pubkey)]
|
||||||
|
prev_script = CScript() << OP_0 << zeros(20);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(65); // signature
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(33); // pubkey
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
// should have no script at all since it's wrapped P2WPKH
|
||||||
|
BOOST_CHECK(ret_script == CScript());
|
||||||
|
BOOST_CHECK_EQUAL(scale, 0);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2WSH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_0 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(65); // arbitrary value to satisfy redeem script
|
||||||
|
CScript redeem_script = CScript() << OP_0;
|
||||||
|
auto redeem_vec{std::vector<unsigned char>(redeem_script.begin(), redeem_script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(redeem_vec);
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == redeem_script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2WSH - some datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_0 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(65); // arbitrary value to satisfy redeem script
|
||||||
|
CScript redeem_script = CScript() << OP_FALSE << OP_IF << zeros(10) << OP_ENDIF;
|
||||||
|
auto redeem_vec{std::vector<unsigned char>(redeem_script.begin(), redeem_script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(redeem_vec);
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == redeem_script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
// OP_FALSE(1), OP_IF(1), length(1), zeros(10), OP_ENDIF(1)
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 14);
|
||||||
|
}
|
||||||
|
{ // P2SH-P2WPKH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
// P2WPKH is [OP_0, hash160(pubkey)]
|
||||||
|
CScript redeem_script = CScript() << OP_0 << zeros(20);
|
||||||
|
prev_script = CScript() << OP_HASH160 << zeros(20) << OP_EQUAL;
|
||||||
|
tx_in.scriptSig = CScript() << std::vector<unsigned char>(redeem_script.begin(), redeem_script.end());
|
||||||
|
// this should return the redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
// should have no script at all since it's wrapped P2WPKH
|
||||||
|
BOOST_CHECK(ret_script == CScript());
|
||||||
|
// data bytes in the witness get discounted (*1 instead of *4)
|
||||||
|
BOOST_CHECK_EQUAL(scale, 0);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2SH-P2WSH - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
// P2WSH is [OP_0, sha256(redeem_script)]
|
||||||
|
CScript redeem_script = CScript() << OP_0 << zeros(32);
|
||||||
|
prev_script = CScript() << OP_HASH160 << zeros(20) << OP_EQUAL;
|
||||||
|
tx_in.scriptSig = CScript() << std::vector<unsigned char>(redeem_script.begin(), redeem_script.end());
|
||||||
|
CScript witness_redeem_script = CScript() << OP_TRUE << OP_IF << zeros(10) << OP_ENDIF;
|
||||||
|
|
||||||
|
// in real life, one or more values (to satisfy the redeem script) would be pushed to the stack
|
||||||
|
CScript wit = CScript() << OP_7;
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(wit.begin(), wit.end());
|
||||||
|
// and then finally the redeem script itself (as the last stack element)
|
||||||
|
auto redeem_vec{std::vector<unsigned char>(witness_redeem_script.begin(), witness_redeem_script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(redeem_vec);
|
||||||
|
|
||||||
|
// this should return the witness redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
// should have no script at all since it's wrapped P2WPKH
|
||||||
|
BOOST_CHECK(ret_script == witness_redeem_script);
|
||||||
|
// data bytes in the witness get discounted (*1 instead of *4)
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2SH-P2WSH - some datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
// P2WSH is [OP_0, sha256(redeem_script)]
|
||||||
|
CScript redeem_script = CScript() << OP_0 << zeros(32);
|
||||||
|
prev_script = CScript() << OP_HASH160 << zeros(20) << OP_EQUAL;
|
||||||
|
tx_in.scriptSig = CScript() << std::vector<unsigned char>(redeem_script.begin(), redeem_script.end());
|
||||||
|
CScript witness_redeem_script = CScript() << OP_FALSE << OP_IF << zeros(10) << OP_ENDIF;
|
||||||
|
|
||||||
|
// in real life, one or more values (to satisfy the redeem script) would be pushed to the stack
|
||||||
|
CScript wit = CScript() << OP_7;
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(wit.begin(), wit.end());
|
||||||
|
// and then finally the redeem script itself (as the last stack element)
|
||||||
|
auto redeem_vec{std::vector<unsigned char>(witness_redeem_script.begin(), witness_redeem_script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(redeem_vec);
|
||||||
|
|
||||||
|
// this should return the witness redeem script
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
// should have no script at all since it's wrapped P2WPKH
|
||||||
|
BOOST_CHECK(ret_script == witness_redeem_script);
|
||||||
|
// data bytes in the witness get discounted (*1 instead of *4)
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
// OP_FALSE(1), OP_IF(1), length(1), zeros(10), OP_ENDIF(1) = 14
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 14);
|
||||||
|
}
|
||||||
|
{ // P2TR keypath - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_1 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(65); // signature
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == CScript());
|
||||||
|
BOOST_CHECK_EQUAL(scale, 0);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2TR keypath - annex but no script - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_1 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(65); // signature
|
||||||
|
std::vector<unsigned char> annex{0x50, 0, 0};
|
||||||
|
tx_in.scriptWitness.stack.push_back(annex);
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == CScript());
|
||||||
|
BOOST_CHECK_EQUAL(scale, 0);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2TR scriptpath - no datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_1 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
// stack: zero or more arbitrary values (script arguments); script; control block
|
||||||
|
// (here we have two arbitrary values)
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(85); // arbitrary value
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(10); // arbitrary value
|
||||||
|
CScript script = CScript() << OP_7 << OP_8;
|
||||||
|
auto script_vec{std::vector<unsigned char>(script.begin(), script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(script_vec);
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(33); // control block
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
|
||||||
|
}
|
||||||
|
{ // P2TR scriptpath - some datacarrier bytes
|
||||||
|
CScript prev_script; // scriptPubKey
|
||||||
|
CTxIn tx_in;
|
||||||
|
prev_script = CScript() << OP_1 << zeros(32);
|
||||||
|
// segwit: empty scriptsig
|
||||||
|
tx_in.scriptSig = CScript();
|
||||||
|
// stack: zero or more arbitrary values (script arguments); script; control block
|
||||||
|
// (here we have one arbitrary value)
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(85); // arbitrary value
|
||||||
|
CScript script = CScript() << OP_RETURN << OP_7 << OP_8;
|
||||||
|
auto script_vec{std::vector<unsigned char>(script.begin(), script.end())};
|
||||||
|
tx_in.scriptWitness.stack.push_back(script_vec);
|
||||||
|
tx_in.scriptWitness.stack.emplace_back(33); // control block
|
||||||
|
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
|
||||||
|
BOOST_CHECK(ret_script == script);
|
||||||
|
BOOST_CHECK_EQUAL(scale, 1);
|
||||||
|
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static CMutableTransaction TxFromHex(const std::string& str)
|
static CMutableTransaction TxFromHex(const std::string& str)
|
||||||
{
|
{
|
||||||
CMutableTransaction tx;
|
CMutableTransaction tx;
|
||||||
|
@ -1014,6 +1014,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||||||
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
|
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_pool.m_opts.datacarrier_fullcount && (!ignore_rejects.count("txn-datacarrier-exceeded")) && DatacarrierBytes(tx, m_view) > m_pool.m_opts.max_datacarrier_bytes.value_or(0)) {
|
||||||
|
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
// Check for non-standard witnesses.
|
// Check for non-standard witnesses.
|
||||||
if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view, "bad-witness-", reason, ignore_rejects)) {
|
if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view, "bad-witness-", reason, ignore_rejects)) {
|
||||||
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, reason);
|
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, reason);
|
||||||
|
@ -738,7 +738,7 @@ def spenders_taproot_active():
|
|||||||
scripts = [
|
scripts = [
|
||||||
("pk_codesep", CScript(random_checksig_style(pubs[1]) + bytes([OP_CODESEPARATOR]))), # codesep after checksig
|
("pk_codesep", CScript(random_checksig_style(pubs[1]) + bytes([OP_CODESEPARATOR]))), # codesep after checksig
|
||||||
("codesep_pk", CScript(bytes([OP_CODESEPARATOR]) + random_checksig_style(pubs[1]))), # codesep before checksig
|
("codesep_pk", CScript(bytes([OP_CODESEPARATOR]) + random_checksig_style(pubs[1]))), # codesep before checksig
|
||||||
("branched_codesep", CScript([random.randbytes(random.randrange(2, 511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
|
("branched_codesep", CScript([random.randbytes(random.randrange(2, 75)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
|
||||||
# Note that the first data push in the "branched_codesep" script has the purpose of
|
# Note that the first data push in the "branched_codesep" script has the purpose of
|
||||||
# randomizing the sighash, both by varying script size and content. In order to
|
# randomizing the sighash, both by varying script size and content. In order to
|
||||||
# avoid MINIMALDATA script verification errors caused by not-minimal-encoded data
|
# avoid MINIMALDATA script verification errors caused by not-minimal-encoded data
|
||||||
@ -1044,7 +1044,7 @@ def spenders_taproot_active():
|
|||||||
# Test that an input stack size of 1000 elements is permitted, but 1001 isn't.
|
# Test that an input stack size of 1000 elements is permitted, but 1001 isn't.
|
||||||
add_spender(spenders, "tapscript/1000inputs", leaf="t23", **common, inputs=[getter("sign")] + [b'' for _ in range(999)], failure={"leaf": "t24", "inputs": [getter("sign")] + [b'' for _ in range(1000)]}, **ERR_STACK_SIZE)
|
add_spender(spenders, "tapscript/1000inputs", leaf="t23", **common, inputs=[getter("sign")] + [b'' for _ in range(999)], failure={"leaf": "t24", "inputs": [getter("sign")] + [b'' for _ in range(1000)]}, **ERR_STACK_SIZE)
|
||||||
# Test that pushing a MAX_SCRIPT_ELEMENT_SIZE byte stack element is valid, but one longer is not.
|
# Test that pushing a MAX_SCRIPT_ELEMENT_SIZE byte stack element is valid, but one longer is not.
|
||||||
add_spender(spenders, "tapscript/pushmaxlimit", leaf="t25", **common, **SINGLE_SIG, failure={"leaf": "t26"}, **ERR_PUSH_LIMIT)
|
add_spender(spenders, "tapscript/pushmaxlimit", standard=False, leaf="t25", **common, **SINGLE_SIG, failure={"leaf": "t26"}, **ERR_PUSH_LIMIT)
|
||||||
# Test that 999-of-999 multisig works (but 1000-of-1000 triggers stack size limits)
|
# Test that 999-of-999 multisig works (but 1000-of-1000 triggers stack size limits)
|
||||||
add_spender(spenders, "tapscript/bigmulti", leaf="t33", **common, inputs=big_spend_inputs, num=999, failure={"leaf": "t34", "num": 1000}, **ERR_STACK_SIZE)
|
add_spender(spenders, "tapscript/bigmulti", leaf="t33", **common, inputs=big_spend_inputs, num=999, failure={"leaf": "t34", "num": 1000}, **ERR_STACK_SIZE)
|
||||||
# Test that the CLEANSTACK rule is consensus critical in tapscript
|
# Test that the CLEANSTACK rule is consensus critical in tapscript
|
||||||
@ -1285,7 +1285,7 @@ class TaprootTest(BitcoinTestFramework):
|
|||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
self.num_nodes = 1
|
self.num_nodes = 1
|
||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
self.extra_args = [["-par=1"]]
|
self.extra_args = [["-par=1", "-datacarrierfullcount"]]
|
||||||
|
|
||||||
def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):
|
def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user