mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Merge 2aff9a36c3 into merged_master (Bitcoin PR bitcoin/bitcoin#30352)
This commit is contained in:
commit
cd6ec40d0f
24 changed files with 202 additions and 6 deletions
10
doc/release-notes-30352.md
Normal file
10
doc/release-notes-30352.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
P2P and network changes
|
||||
-----------------------
|
||||
|
||||
- Pay To Anchor(P2A) is a new standard witness output type for spending,
|
||||
a newly recognised output template. This allows for key-less anchor
|
||||
outputs, with compact spending conditions for additional efficiencies on
|
||||
top of an equivalent `sh(OP_TRUE)` output, in addition to the txid stability
|
||||
of the spending transaction.
|
||||
N.B. propagation of this output spending on the network will be limited
|
||||
until a sufficient number of nodes on the network adopt this upgrade.
|
||||
|
|
@ -100,6 +100,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
addressRet = tap;
|
||||
return true;
|
||||
}
|
||||
case TxoutType::ANCHOR: {
|
||||
addressRet = PayToAnchor();
|
||||
return true;
|
||||
}
|
||||
case TxoutType::WITNESS_UNKNOWN: {
|
||||
addressRet = WitnessUnknown{vSolutions[0][0], vSolutions[1]};
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <pubkey.h>
|
||||
#include <script/script.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/hash_type.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -148,6 +149,13 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
struct PayToAnchor : public WitnessUnknown
|
||||
{
|
||||
PayToAnchor() : WitnessUnknown(1, {0x4e, 0x73}) {
|
||||
Assume(CScript::IsPayToAnchor(1, {0x4e, 0x73}));
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A txout script categorized into standard templates.
|
||||
* * CNoDestination: Optionally a script, no corresponding address.
|
||||
|
|
@ -157,11 +165,12 @@ public:
|
|||
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH address)
|
||||
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH address)
|
||||
* * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR address)
|
||||
* * PayToAnchor: TxoutType::ANCHOR destination (P2A address)
|
||||
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W??? address)
|
||||
* * NullData: TxoutType::NULL_DATA destination (OP_RETURN) // ELEMENTS
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
using CTxDestination = std::variant<CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown, NullData>;
|
||||
using CTxDestination = std::variant<CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, PayToAnchor, WitnessUnknown, NullData>;
|
||||
|
||||
/** Check whether a CTxDestination corresponds to one with an address. */
|
||||
bool IsValidDestination(const CTxDestination& dest);
|
||||
|
|
|
|||
|
|
@ -282,6 +282,10 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
return tap;
|
||||
}
|
||||
|
||||
if (CScript::IsPayToAnchor(version, data)) {
|
||||
return PayToAnchor();
|
||||
}
|
||||
|
||||
if (version > 16) {
|
||||
error_str = "Invalid Bech32 address witness version";
|
||||
return CNoDestination();
|
||||
|
|
|
|||
|
|
@ -249,6 +249,11 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
// get the scriptPubKey corresponding to this input:
|
||||
CScript prevScript = prev.scriptPubKey;
|
||||
|
||||
// witness stuffing detected
|
||||
if (prevScript.IsPayToAnchor()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool p2sh = false;
|
||||
if (prevScript.IsPayToScriptHash()) {
|
||||
std::vector <std::vector<unsigned char> > stack;
|
||||
|
|
|
|||
|
|
@ -630,6 +630,7 @@ static RPCHelpMan decodescript()
|
|||
case TxoutType::SCRIPTHASH:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
case TxoutType::ANCHOR:
|
||||
// Should not be wrapped
|
||||
return false;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
|
@ -676,6 +677,7 @@ static RPCHelpMan decodescript()
|
|||
case TxoutType::WITNESS_V0_KEYHASH:
|
||||
case TxoutType::WITNESS_V0_SCRIPTHASH:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
case TxoutType::ANCHOR:
|
||||
// Should not be wrapped
|
||||
return false;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
|
|
|||
|
|
@ -333,6 +333,14 @@ public:
|
|||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const PayToAnchor& anchor) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("isscript", true);
|
||||
obj.pushKV("iswitness", true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessUnknown& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
|
|
|
|||
|
|
@ -3313,6 +3313,8 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
}
|
||||
return set_success(serror);
|
||||
}
|
||||
} else if (!is_p2sh && CScript::IsPayToAnchor(witversion, program)) {
|
||||
return true;
|
||||
} else {
|
||||
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
|
||||
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
|
||||
|
|
|
|||
|
|
@ -318,6 +318,23 @@ bool CScript::IsPayToWitnessPubkeyHash() const
|
|||
// END ELEMENTS
|
||||
//
|
||||
|
||||
bool CScript::IsPayToAnchor() const
|
||||
{
|
||||
return (this->size() == 4 &&
|
||||
(*this)[0] == OP_1 &&
|
||||
(*this)[1] == 0x02 &&
|
||||
(*this)[2] == 0x4e &&
|
||||
(*this)[3] == 0x73);
|
||||
}
|
||||
|
||||
bool CScript::IsPayToAnchor(int version, const std::vector<unsigned char>& program)
|
||||
{
|
||||
return version == 1 &&
|
||||
program.size() == 2 &&
|
||||
program[0] == 0x4e &&
|
||||
program[1] == 0x73;
|
||||
}
|
||||
|
||||
bool CScript::IsPayToScriptHash() const
|
||||
{
|
||||
// Extra-fast test for pay-to-script-hash CScripts:
|
||||
|
|
|
|||
|
|
@ -597,6 +597,14 @@ public:
|
|||
|
||||
bool IsPayToPubkeyHash() const;
|
||||
bool IsPayToWitnessPubkeyHash() const;
|
||||
/*
|
||||
* OP_1 <0x4e73>
|
||||
*/
|
||||
bool IsPayToAnchor() const;
|
||||
/** Checks if output of IsWitnessProgram comes from a P2A output script
|
||||
*/
|
||||
static bool IsPayToAnchor(int version, const std::vector<unsigned char>& program);
|
||||
|
||||
bool IsPayToScriptHash() const;
|
||||
bool IsPayToWitnessScriptHash() const;
|
||||
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;
|
||||
|
|
|
|||
|
|
@ -484,10 +484,10 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
// ELEMENTS
|
||||
case TxoutType::FEE:
|
||||
return false;
|
||||
|
||||
case TxoutType::OP_TRUE:
|
||||
return Params().anyonecanspend_aremine;
|
||||
|
||||
case TxoutType::ANCHOR:
|
||||
return true;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
assert(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ std::string GetTxnOutputType(TxoutType t)
|
|||
case TxoutType::SCRIPTHASH: return "scripthash";
|
||||
case TxoutType::MULTISIG: return "multisig";
|
||||
case TxoutType::NULL_DATA: return "nulldata";
|
||||
case TxoutType::ANCHOR: return "anchor";
|
||||
case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
||||
case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
|
||||
|
|
@ -179,6 +180,9 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
|
|||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
return TxoutType::WITNESS_V1_TAPROOT;
|
||||
}
|
||||
if (scriptPubKey.IsPayToAnchor()) {
|
||||
return TxoutType::ANCHOR;
|
||||
}
|
||||
if (witnessversion != 0) {
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ template <typename C> class Span;
|
|||
enum class TxoutType {
|
||||
NONSTANDARD,
|
||||
// 'standard' transaction types:
|
||||
ANCHOR, //!< anyone can spend script
|
||||
PUBKEY,
|
||||
PUBKEYHASH,
|
||||
SCRIPTHASH,
|
||||
|
|
|
|||
|
|
@ -76,11 +76,13 @@ FUZZ_TARGET(script, .init = initialize_script)
|
|||
assert(which_type == TxoutType::PUBKEY ||
|
||||
which_type == TxoutType::NONSTANDARD ||
|
||||
which_type == TxoutType::NULL_DATA ||
|
||||
which_type == TxoutType::MULTISIG);
|
||||
which_type == TxoutType::MULTISIG ||
|
||||
which_type == TxoutType::ANCHOR);
|
||||
}
|
||||
if (which_type == TxoutType::NONSTANDARD ||
|
||||
which_type == TxoutType::NULL_DATA ||
|
||||
which_type == TxoutType::MULTISIG) {
|
||||
which_type == TxoutType::MULTISIG ||
|
||||
which_type == TxoutType::ANCHOR) {
|
||||
assert(!extract_destination_ret);
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +96,7 @@ FUZZ_TARGET(script, .init = initialize_script)
|
|||
(void)Solver(script, solutions);
|
||||
|
||||
(void)script.HasValidOps();
|
||||
(void)script.IsPayToAnchor();
|
||||
(void)script.IsPayToScriptHash();
|
||||
(void)script.IsPayToWitnessScriptHash();
|
||||
(void)script.IsPushOnly();
|
||||
|
|
|
|||
|
|
@ -216,6 +216,9 @@ CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) no
|
|||
[&] {
|
||||
tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
|
||||
},
|
||||
[&] {
|
||||
tx_destination = PayToAnchor{};
|
||||
},
|
||||
[&] {
|
||||
std::vector<unsigned char> program{ConsumeRandomLengthByteVector(fuzzed_data_provider, /*max_length=*/40)};
|
||||
if (program.size() < 2) {
|
||||
|
|
|
|||
|
|
@ -128,6 +128,20 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
|
|||
BOOST_CHECK(solutions[0] == std::vector<unsigned char>{16});
|
||||
BOOST_CHECK(solutions[1] == ToByteVector(uint256::ONE));
|
||||
|
||||
// TxoutType::ANCHOR
|
||||
std::vector<unsigned char> anchor_bytes{0x4e, 0x73};
|
||||
s.clear();
|
||||
s << OP_1 << anchor_bytes;
|
||||
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::ANCHOR);
|
||||
BOOST_CHECK(solutions.empty());
|
||||
|
||||
// Sanity-check IsPayToAnchor
|
||||
int version{-1};
|
||||
std::vector<unsigned char> witness_program;
|
||||
BOOST_CHECK(s.IsPayToAnchor());
|
||||
BOOST_CHECK(s.IsWitnessProgram(version, witness_program));
|
||||
BOOST_CHECK(CScript::IsPayToAnchor(version, witness_program));
|
||||
|
||||
// TxoutType::NONSTANDARD
|
||||
s.clear();
|
||||
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
|
||||
|
|
@ -186,6 +200,18 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
|
|||
s.clear();
|
||||
s << OP_0 << std::vector<unsigned char>(19, 0x01);
|
||||
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
|
||||
|
||||
// TxoutType::ANCHOR but wrong witness version
|
||||
s.clear();
|
||||
s << OP_2 << std::vector<unsigned char>{0x4e, 0x73};
|
||||
BOOST_CHECK(!s.IsPayToAnchor());
|
||||
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
|
||||
|
||||
// TxoutType::ANCHOR but wrong 2-byte data push
|
||||
s.clear();
|
||||
s << OP_1 << std::vector<unsigned char>{0xff, 0xff};
|
||||
BOOST_CHECK(!s.IsPayToAnchor());
|
||||
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
|
||||
|
|
|
|||
|
|
@ -1315,6 +1315,19 @@ BOOST_AUTO_TEST_CASE(sign_invalid_miniscript)
|
|||
BOOST_CHECK(!SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
|
||||
}
|
||||
|
||||
/* P2A input should be considered signed. */
|
||||
BOOST_AUTO_TEST_CASE(sign_paytoanchor)
|
||||
{
|
||||
FillableSigningProvider keystore;
|
||||
SignatureData sig_data;
|
||||
CMutableTransaction prev, curr;
|
||||
prev.vout.emplace_back(CAsset(), 0, GetScriptForDestination(PayToAnchor{}));
|
||||
|
||||
curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
|
||||
|
||||
BOOST_CHECK(SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_standard_push)
|
||||
{
|
||||
ScriptError err;
|
||||
|
|
|
|||
|
|
@ -1043,6 +1043,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
|
|||
t.vout[0].nValue = 239;
|
||||
CheckIsNotStandard(t, "dust");
|
||||
}
|
||||
|
||||
// Check anchor outputs
|
||||
t.vout[0].scriptPubKey = CScript() << OP_1 << std::vector<unsigned char>{0x4e, 0x73};
|
||||
BOOST_CHECK(t.vout[0].scriptPubKey.IsPayToAnchor());
|
||||
t.vout[0].nValue = 240;
|
||||
CheckIsStandard(t);
|
||||
t.vout[0].nValue = 239;
|
||||
CheckIsNotStandard(t, "dust");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -519,6 +519,7 @@ public:
|
|||
}
|
||||
|
||||
UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
|
||||
UniValue operator()(const PayToAnchor& id) const { return UniValue(UniValue::VOBJ); }
|
||||
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
|
||||
UniValue operator()(const NullData& id) const { return NullUniValue; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -930,6 +930,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
|
|||
case TxoutType::NONSTANDARD:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
case TxoutType::ANCHOR:
|
||||
return "unrecognized script";
|
||||
// ELEMENTS
|
||||
case TxoutType::OP_TRUE:
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ IsMineResult IsMineInner(const LegacyDataSPKM& keystore, const CScript& scriptPu
|
|||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
case TxoutType::FEE:
|
||||
case TxoutType::ANCHOR:
|
||||
break;
|
||||
case TxoutType::PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from test_framework.messages import (
|
|||
COutPoint,
|
||||
CTransaction,
|
||||
CTxIn,
|
||||
# CTxInWitness,
|
||||
CTxInWitness,
|
||||
CTxOut,
|
||||
CTxOutValue,
|
||||
MAX_BLOCK_WEIGHT,
|
||||
|
|
@ -38,6 +38,7 @@ from test_framework.script_util import (
|
|||
keys_to_multisig_script,
|
||||
# MIN_PADDING,
|
||||
MIN_STANDARD_TX_NONWITNESS_SIZE,
|
||||
PAY_TO_ANCHOR,
|
||||
script_to_p2sh_script,
|
||||
script_to_p2wsh_script,
|
||||
)
|
||||
|
|
@ -379,6 +380,58 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
# CVE-2017-12842 does not affect elements transactions
|
||||
assert_equal(MIN_STANDARD_TX_NONWITNESS_SIZE - 1, 64)
|
||||
|
||||
self.log.info('OP_1 <0x4e73> is able to be created and spent')
|
||||
anchor_value = 10000
|
||||
create_anchor_tx = self.wallet.send_to(from_node=node, scriptPubKey=PAY_TO_ANCHOR, amount=anchor_value)
|
||||
self.generate(node, 1)
|
||||
|
||||
# First spend has non-empty witness, will be rejected to prevent third party wtxid malleability
|
||||
anchor_nonempty_wit_spend = CTransaction()
|
||||
anchor_nonempty_wit_spend.vin.append(CTxIn(COutPoint(int(create_anchor_tx["txid"], 16), create_anchor_tx["sent_vout"]), b""))
|
||||
anchor_nonempty_wit_spend.vout.append(CTxOut(anchor_value - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
|
||||
anchor_nonempty_wit_spend.vout.append(CTxOut(int(fee*COIN))) # ELEMENTS: explicit fee output
|
||||
anchor_nonempty_wit_spend.wit.vtxinwit.append(CTxInWitness())
|
||||
anchor_nonempty_wit_spend.wit.vtxinwit[0].scriptWitness.stack.append(b"f")
|
||||
anchor_nonempty_wit_spend.rehash()
|
||||
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': anchor_nonempty_wit_spend.rehash(), 'allowed': False, 'reject-reason': 'bad-witness-nonstandard'}],
|
||||
rawtxs=[anchor_nonempty_wit_spend.serialize().hex()],
|
||||
maxfeerate=0,
|
||||
)
|
||||
|
||||
# Clear witness stuffing
|
||||
anchor_spend = anchor_nonempty_wit_spend
|
||||
anchor_spend.wit.vtxinwit[0].scriptWitness.stack = []
|
||||
anchor_spend.rehash()
|
||||
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': anchor_spend.rehash(), 'allowed': True, 'vsize': anchor_spend.get_vsize(), 'fees': { 'base': Decimal('0.00000700')}}],
|
||||
rawtxs=[anchor_spend.serialize().hex()],
|
||||
maxfeerate=0,
|
||||
)
|
||||
|
||||
self.log.info('But cannot be spent if nested sh()')
|
||||
nested_anchor_tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
|
||||
nested_anchor_tx.vout[0].scriptPubKey = script_to_p2sh_script(PAY_TO_ANCHOR)
|
||||
nested_anchor_tx.rehash()
|
||||
self.generateblock(node, self.wallet.get_address(), [nested_anchor_tx.serialize().hex()])
|
||||
|
||||
nested_anchor_spend = CTransaction()
|
||||
nested_anchor_spend.vin.append(CTxIn(COutPoint(nested_anchor_tx.sha256, 0), b""))
|
||||
nested_anchor_spend.vin[0].scriptSig = CScript([bytes(PAY_TO_ANCHOR)])
|
||||
nested_anchor_spend.vout.append(CTxOut(nested_anchor_tx.vout[0].nValue.getAmount() - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
|
||||
nested_anchor_spend.vout.append(CTxOut(int(fee*COIN))) # ELEMENTS: explicit fee output
|
||||
nested_anchor_spend.rehash()
|
||||
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': nested_anchor_spend.rehash(), 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Witness version reserved for soft-fork upgrades)'}],
|
||||
rawtxs=[nested_anchor_spend.serialize().hex()],
|
||||
maxfeerate=0,
|
||||
)
|
||||
# but is consensus-legal
|
||||
self.generateblock(node, self.wallet.get_address(), [nested_anchor_spend.serialize().hex()])
|
||||
|
||||
self.log.info('Spending a confirmed bare multisig is okay')
|
||||
address = self.wallet.get_address()
|
||||
tx = tx_from_hex(raw_tx_reference)
|
||||
|
|
|
|||
|
|
@ -187,6 +187,16 @@ class DecodeScriptTest(BitcoinTestFramework):
|
|||
assert_equal('1 ' + xonly_public_key, rpc_result['asm'])
|
||||
assert 'segwit' not in rpc_result
|
||||
|
||||
self.log.info("- P2A (anchor)")
|
||||
# 1 <4e73>
|
||||
witprog_hex = '4e73'
|
||||
rpc_result = self.nodes[0].decodescript('5102' + witprog_hex)
|
||||
assert_equal('anchor', rpc_result['type'])
|
||||
# in the disassembly, the witness program is shown as single decimal due to its small size
|
||||
witprog_as_decimal = int.from_bytes(bytes.fromhex(witprog_hex), 'little')
|
||||
assert_equal(f'1 {witprog_as_decimal}', rpc_result['asm'])
|
||||
assert_equal('ert1pfees5fana6', rpc_result['address'])
|
||||
|
||||
def decoderawtransaction_asm_sighashtype(self):
|
||||
"""Test decoding scripts via RPC command "decoderawtransaction".
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import unittest
|
|||
from test_framework.script import (
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_1,
|
||||
OP_15,
|
||||
OP_16,
|
||||
OP_CHECKMULTISIG,
|
||||
|
|
@ -42,6 +43,8 @@ assert MIN_PADDING == 5
|
|||
DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
|
||||
assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
|
||||
|
||||
PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")])
|
||||
|
||||
def key_to_p2pk_script(key):
|
||||
key = check_key(key)
|
||||
return CScript([key, OP_CHECKSIG])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue