Skip to content

Commit

Permalink
add ordisrespector v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Retropex committed Jan 24, 2024
1 parent 44d8b13 commit 332431e
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 4 deletions.
63 changes: 63 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,66 @@ int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, un
{
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;
}
5 changes: 5 additions & 0 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <cstdint>
#include <string>
#include <utility>

class CCoinsViewCache;
class CFeeRate;
Expand Down Expand Up @@ -168,4 +169,8 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx)
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
50 changes: 50 additions & 0 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,56 @@ bool CScript::HasValidOps() const
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)
{
opcodeRet = OP_INVALIDOPCODE;
Expand Down
7 changes: 5 additions & 2 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,15 +544,18 @@ class CScript : public CScriptBase
bool HasValidOps() const;

/**
* Returns whether the script is guaranteed to fail at execution,
* Returns whether the scriptPubKey is guaranteed to fail at execution,
* regardless of the initial stack. This allows outputs to be pruned
* instantly when entering the UTXO set.
* instantly when entering the UTXO set. Note that this is incorrect for
* witness scripts, which are not always limited by MAX_SCRIPT_SIZE.
*/
bool IsUnspendable() const
{
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
}

size_t DatacarrierBytes() const;

void clear()
{
// The default prevector::clear() does not release memory
Expand Down
Loading

0 comments on commit 332431e

Please sign in to comment.