Skip to content

Commit 455fca8

Browse files
committed
policy: Add OP_1 <0x4e73> as a standard output type
These outputs are called anchors, and allow key-less anchor spends which are vsize-minimized versus keyed anchors which require larger outputs when creating and inputs when spending.
1 parent 8754d05 commit 455fca8

17 files changed

+106
-3
lines changed

src/addresstype.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
8787
addressRet = tap;
8888
return true;
8989
}
90+
case TxoutType::ANCHOR: {
91+
addressRet = PayToAnchor();
92+
return true;
93+
}
9094
case TxoutType::WITNESS_UNKNOWN: {
9195
addressRet = WitnessUnknown{vSolutions[0][0], vSolutions[1]};
9296
return true;

src/addresstype.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <pubkey.h>
1010
#include <script/script.h>
1111
#include <uint256.h>
12+
#include <util/check.h>
1213
#include <util/hash_type.h>
1314

1415
#include <algorithm>
@@ -116,6 +117,13 @@ struct WitnessUnknown
116117
}
117118
};
118119

120+
struct PayToAnchor : public WitnessUnknown
121+
{
122+
PayToAnchor() : WitnessUnknown(1, {0x4e, 0x73}) {
123+
Assume(CScript::IsPayToAnchor(1, {0x4e, 0x73}));
124+
};
125+
};
126+
119127
/**
120128
* A txout script categorized into standard templates.
121129
* * CNoDestination: Optionally a script, no corresponding address.
@@ -125,10 +133,11 @@ struct WitnessUnknown
125133
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH address)
126134
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH address)
127135
* * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR address)
136+
* * PayToAnchor: TxoutType::ANCHOR destination (P2A address)
128137
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W??? address)
129138
* A CTxDestination is the internal data type encoded in a bitcoin address
130139
*/
131-
using CTxDestination = std::variant<CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>;
140+
using CTxDestination = std::variant<CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown>;
132141

133142
/** Check whether a CTxDestination corresponds to one with an address. */
134143
bool IsValidDestination(const CTxDestination& dest);

src/key_io.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
181181
return tap;
182182
}
183183

184+
if (CScript::IsPayToAnchor(version, data)) {
185+
return PayToAnchor();
186+
}
187+
184188
if (version > 16) {
185189
error_str = "Invalid Bech32 address witness version";
186190
return CNoDestination();

src/rpc/rawtransaction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ static RPCHelpMan decodescript()
557557
case TxoutType::SCRIPTHASH:
558558
case TxoutType::WITNESS_UNKNOWN:
559559
case TxoutType::WITNESS_V1_TAPROOT:
560+
case TxoutType::ANCHOR:
560561
// Should not be wrapped
561562
return false;
562563
} // no default case, so the compiler can warn about missing cases
@@ -599,6 +600,7 @@ static RPCHelpMan decodescript()
599600
case TxoutType::WITNESS_V0_KEYHASH:
600601
case TxoutType::WITNESS_V0_SCRIPTHASH:
601602
case TxoutType::WITNESS_V1_TAPROOT:
603+
case TxoutType::ANCHOR:
602604
// Should not be wrapped
603605
return false;
604606
} // no default case, so the compiler can warn about missing cases

src/rpc/util.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ class DescribeAddressVisitor
332332
return obj;
333333
}
334334

335+
UniValue operator()(const PayToAnchor& anchor) const
336+
{
337+
UniValue obj(UniValue::VOBJ);
338+
obj.pushKV("isscript", true);
339+
obj.pushKV("iswitness", true);
340+
return obj;
341+
}
342+
335343
UniValue operator()(const WitnessUnknown& id) const
336344
{
337345
UniValue obj(UniValue::VOBJ);

src/script/script.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
204204
return subscript.GetSigOpCount(true);
205205
}
206206

207+
bool CScript::IsPayToAnchor() const
208+
{
209+
return (this->size() == 4 &&
210+
(*this)[0] == OP_1 &&
211+
(*this)[1] == 0x02 &&
212+
(*this)[2] == 0x4e &&
213+
(*this)[3] == 0x73);
214+
}
215+
216+
bool CScript::IsPayToAnchor(int version, const std::vector<unsigned char>& program)
217+
{
218+
return version == 1 &&
219+
program.size() == 2 &&
220+
program[0] == 0x4e &&
221+
program[1] == 0x73;
222+
}
223+
207224
bool CScript::IsPayToScriptHash() const
208225
{
209226
// Extra-fast test for pay-to-script-hash CScripts:

src/script/script.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,14 @@ class CScript : public CScriptBase
533533
*/
534534
unsigned int GetSigOpCount(const CScript& scriptSig) const;
535535

536+
/*
537+
* OP_1 <0x4e73>
538+
*/
539+
bool IsPayToAnchor() const;
540+
/** Checks if output of IsWitnessProgram comes from a P2A output script
541+
*/
542+
static bool IsPayToAnchor(int version, const std::vector<unsigned char>& program);
543+
536544
bool IsPayToScriptHash() const;
537545
bool IsPayToWitnessScriptHash() const;
538546
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;

src/script/sign.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
475475

476476
case TxoutType::WITNESS_V1_TAPROOT:
477477
return SignTaproot(provider, creator, WitnessV1Taproot(XOnlyPubKey{vSolutions[0]}), sigdata, ret);
478+
479+
case TxoutType::ANCHOR:
480+
return true;
478481
} // no default case, so the compiler can warn about missing cases
479482
assert(false);
480483
}

src/script/solver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ std::string GetTxnOutputType(TxoutType t)
2424
case TxoutType::SCRIPTHASH: return "scripthash";
2525
case TxoutType::MULTISIG: return "multisig";
2626
case TxoutType::NULL_DATA: return "nulldata";
27+
case TxoutType::ANCHOR: return "anchor";
2728
case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
2829
case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
2930
case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
@@ -165,6 +166,9 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
165166
vSolutionsRet.push_back(std::move(witnessprogram));
166167
return TxoutType::WITNESS_V1_TAPROOT;
167168
}
169+
if (scriptPubKey.IsPayToAnchor()) {
170+
return TxoutType::ANCHOR;
171+
}
168172
if (witnessversion != 0) {
169173
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
170174
vSolutionsRet.push_back(std::move(witnessprogram));

src/script/solver.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ template <typename C> class Span;
2222
enum class TxoutType {
2323
NONSTANDARD,
2424
// 'standard' transaction types:
25+
ANCHOR, //!< anyone can spend script
2526
PUBKEY,
2627
PUBKEYHASH,
2728
SCRIPTHASH,

0 commit comments

Comments
 (0)