aboutsummaryrefslogtreecommitdiff
path: root/src/script/standard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/standard.cpp')
-rw-r--r--src/script/standard.cpp113
1 files changed, 74 insertions, 39 deletions
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 5fb98cc307..6101738061 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -16,9 +16,6 @@
typedef std::vector<unsigned char> valtype;
-bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
-unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
-
CScriptID::CScriptID(const CScript& in) : BaseHash(Hash160(in)) {}
CScriptID::CScriptID(const ScriptHash& in) : BaseHash(static_cast<uint160>(in)) {}
@@ -91,56 +88,83 @@ static constexpr bool IsSmallInteger(opcodetype opcode)
return opcode >= OP_1 && opcode <= OP_16;
}
-static constexpr bool IsPushdataOp(opcodetype opcode)
-{
- return opcode > OP_FALSE && opcode <= OP_PUSHDATA4;
-}
-
-static constexpr bool IsValidMultisigKeyCount(int n_keys)
-{
- return n_keys > 0 && n_keys <= MAX_PUBKEYS_PER_MULTISIG;
-}
-
-static bool GetMultisigKeyCount(opcodetype opcode, valtype data, int& count)
+/** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,
+ * whether it's OP_n or through a push. */
+static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
{
+ int count;
if (IsSmallInteger(opcode)) {
count = CScript::DecodeOP_N(opcode);
- return IsValidMultisigKeyCount(count);
- }
-
- if (IsPushdataOp(opcode)) {
- if (!CheckMinimalPush(data, opcode)) return false;
+ } else if (IsPushdataOp(opcode)) {
+ if (!CheckMinimalPush(data, opcode)) return {};
try {
count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
- return IsValidMultisigKeyCount(count);
} catch (const scriptnum_error&) {
- return false;
+ return {};
}
+ } else {
+ return {};
}
-
- return false;
+ if (count < min || count > max) return {};
+ return count;
}
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
- int num_keys;
CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
- if (!script.GetOp(it, opcode, data) || !GetMultisigKeyCount(opcode, data, required_sigs)) return false;
+ if (!script.GetOp(it, opcode, data)) return false;
+ auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
+ if (!req_sigs) return false;
+ required_sigs = *req_sigs;
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
pubkeys.emplace_back(std::move(data));
}
- if (!GetMultisigKeyCount(opcode, data, num_keys)) return false;
-
- if (pubkeys.size() != static_cast<unsigned long>(num_keys) || num_keys < required_sigs) return false;
+ auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
+ if (!num_keys) return false;
+ if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
return (it + 1 == script.end());
}
+std::optional<std::pair<int, std::vector<Span<const unsigned char>>>> MatchMultiA(const CScript& script)
+{
+ std::vector<Span<const unsigned char>> keyspans;
+
+ // Redundant, but very fast and selective test.
+ if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
+
+ // Parse keys
+ auto it = script.begin();
+ while (script.end() - it >= 34) {
+ if (*it != 32) return {};
+ ++it;
+ keyspans.emplace_back(&*it, 32);
+ it += 32;
+ if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
+ ++it;
+ }
+ if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
+
+ // Parse threshold.
+ opcodetype opcode;
+ std::vector<unsigned char> data;
+ if (!script.GetOp(it, opcode, data)) return {};
+ if (it == script.end()) return {};
+ if (*it != OP_NUMEQUAL) return {};
+ ++it;
+ if (it != script.end()) return {};
+ auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
+ if (!threshold) return {};
+
+ // Construct result.
+ return std::pair{*threshold, std::move(keyspans)};
+}
+
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
{
vSolutionsRet.clear();
@@ -348,9 +372,9 @@ bool IsValidDestination(const CTxDestination& dest) {
}
/* Lexicographically sort a and b's hash, and compute parent hash. */
if (a.hash < b.hash) {
- ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
+ ret.hash = (HashWriter{HASHER_TAPBRANCH} << a.hash << b.hash).GetSHA256();
} else {
- ret.hash = (CHashWriter(HASHER_TAPBRANCH) << b.hash << a.hash).GetSHA256();
+ ret.hash = (HashWriter{HASHER_TAPBRANCH} << b.hash << a.hash).GetSHA256();
}
return ret;
}
@@ -366,13 +390,7 @@ void TaprootSpendData::Merge(TaprootSpendData other)
merkle_root = other.merkle_root;
}
for (auto& [key, control_blocks] : other.scripts) {
- // Once P0083R3 is supported by all our targeted platforms,
- // this loop body can be replaced with:
- // scripts[key].merge(std::move(control_blocks));
- auto& target = scripts[key];
- for (auto& control_block: control_blocks) {
- target.insert(std::move(control_block));
- }
+ scripts[key].merge(std::move(control_blocks));
}
}
@@ -431,7 +449,7 @@ TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_v
if (!IsValid()) return *this;
/* Construct NodeInfo object with leaf hash and (if track is true) also leaf information. */
NodeInfo node;
- node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ node.hash = (HashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
if (track) node.leaves.emplace_back(LeafInfo{script, leaf_version, {}});
/* Insert into the branch. */
Insert(std::move(node), depth);
@@ -464,6 +482,7 @@ WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_
TaprootSpendData TaprootBuilder::GetSpendData() const
{
assert(IsComplete());
+ assert(m_output_key.IsFullyValid());
TaprootSpendData spd;
spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
spd.internal_key = m_internal_key;
@@ -588,7 +607,7 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
node.done = true;
stack.pop_back();
} else if (node.sub[0]->done && !node.sub[1]->done && !node.sub[1]->explored && !node.sub[1]->hash.IsNull() &&
- (CHashWriter{HASHER_TAPBRANCH} << node.sub[1]->hash << node.sub[1]->hash).GetSHA256() == node.hash) {
+ (HashWriter{HASHER_TAPBRANCH} << node.sub[1]->hash << node.sub[1]->hash).GetSHA256() == node.hash) {
// Whenever there are nodes with two identical subtrees under it, we run into a problem:
// the control blocks for the leaves underneath those will be identical as well, and thus
// they will all be matched to the same path in the tree. The result is that at the location
@@ -621,3 +640,19 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
return ret;
}
+
+std::vector<std::tuple<uint8_t, uint8_t, CScript>> TaprootBuilder::GetTreeTuples() const
+{
+ assert(IsComplete());
+ std::vector<std::tuple<uint8_t, uint8_t, CScript>> tuples;
+ if (m_branch.size()) {
+ const auto& leaves = m_branch[0]->leaves;
+ for (const auto& leaf : leaves) {
+ assert(leaf.merkle_branch.size() <= TAPROOT_CONTROL_MAX_NODE_COUNT);
+ uint8_t depth = (uint8_t)leaf.merkle_branch.size();
+ uint8_t leaf_ver = (uint8_t)leaf.leaf_version;
+ tuples.push_back(std::make_tuple(depth, leaf_ver, leaf.script));
+ }
+ }
+ return tuples;
+}