MWEB: Add StealthAddress to CTxDestination and add pegin and hogex script consts & logic

This commit is contained in:
David Burkett 2022-01-29 14:28:57 -05:00 committed by Loshan T
parent 9d1f530a5f
commit d8e940f301
12 changed files with 162 additions and 3 deletions

View File

@ -132,6 +132,7 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
bech32_hrp = "ltc";
mweb_hrp = "ltcmweb";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
@ -236,6 +237,7 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "tltc";
mweb_hrp = "tmweb";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
@ -341,6 +343,7 @@ public:
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "rltc";
mweb_hrp = "tmweb";
}
/**

View File

@ -89,6 +89,7 @@ public:
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP() const { return bech32_hrp; }
const std::string& MWEB_HRP() const { return mweb_hrp; }
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
@ -104,6 +105,7 @@ protected:
std::vector<std::string> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
std::string bech32_hrp;
std::string mweb_hrp;
std::string strNetworkID;
CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds;

View File

@ -66,6 +66,16 @@ public:
return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
}
std::string operator()(const StealthAddress& id) const
{
std::vector<uint8_t> serialized = id.Serialized();
std::vector<uint8_t> converted = {0};
ConvertBits<8, 5, true>([&](unsigned char c) { converted.push_back(c); }, serialized.begin(), serialized.end());
return bech32::Encode(bech32::Encoding::BECH32, m_params.MWEB_HRP(), converted);
}
std::string operator()(const CNoDestination& no) const { return {}; }
};
@ -138,6 +148,19 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
return unk;
}
}
auto decoded = bech32::Decode(str, true);
if (decoded.encoding == bech32::Encoding::BECH32 && decoded.hrp == params.MWEB_HRP()) {
std::vector<uint8_t> converted;
converted.reserve(((decoded.data.size() - 1) * 5) / 8);
if (ConvertBits<5, 8, false>([&](unsigned char c) { converted.push_back(c); }, decoded.data.begin() + 1, decoded.data.end())) {
if (converted.size() == 66) {
return StealthAddress(BigInt<33>(converted.data()), BigInt<33>(converted.data() + 33));
}
}
}
return CNoDestination();
}
} // namespace

View File

@ -154,7 +154,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
const CBlockIndex* pnext;
int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS | SERIALIZE_NO_MWEB));
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
result.pushKV("height", blockindex->nHeight);

View File

@ -224,6 +224,7 @@ public:
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
obj.pushKV("iswitness", false);
obj.pushKV("ismweb", false);
return obj;
}
@ -232,6 +233,7 @@ public:
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", true);
obj.pushKV("iswitness", false);
obj.pushKV("ismweb", false);
return obj;
}
@ -242,6 +244,7 @@ public:
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
obj.pushKV("witness_program", HexStr(id));
obj.pushKV("ismweb", false);
return obj;
}
@ -252,6 +255,7 @@ public:
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
obj.pushKV("witness_program", HexStr(id));
obj.pushKV("ismweb", false);
return obj;
}
@ -261,6 +265,16 @@ public:
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", (int)id.version);
obj.pushKV("witness_program", HexStr(Span<const unsigned char>(id.program, id.length)));
obj.pushKV("ismweb", false);
return obj;
}
UniValue operator()(const StealthAddress& id) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
obj.pushKV("iswitness", false);
obj.pushKV("ismweb", true);
return obj;
}
};

View File

@ -5,6 +5,7 @@
#include <script/script.h>
#include <mw/models/crypto/Hash.h>
#include <util/strencodings.h>
#include <string>
@ -233,6 +234,40 @@ bool CScript::IsWitnessProgram(int& version, std::vector<unsigned char>& program
return false;
}
bool CScript::IsMWEBPegin(mw::Hash* const kernel_id) const
{
int version;
std::vector<uint8_t> program;
if (IsWitnessProgram(version, program)) {
if (version == MWEB_PEGIN_WITNESS_VERSION && program.size() == WITNESS_MWEB_PEGIN_SIZE) {
if (kernel_id != nullptr) {
*kernel_id = mw::Hash(std::move(program));
}
return true;
}
}
return false;
}
bool CScript::IsMWEBHogAddr(mw::Hash* const header_hash) const
{
int version;
std::vector<uint8_t> program;
if (IsWitnessProgram(version, program)) {
if (version == MWEB_HOG_ADDR_WITNESS_VERSION && program.size() == WITNESS_MWEB_HEADERHASH_SIZE) {
if (header_hash != nullptr) {
*header_hash = mw::Hash(std::move(program));
}
return true;
}
}
return false;
}
bool CScript::IsPushOnly(const_iterator pc) const
{
while (pc < end())

View File

@ -7,6 +7,7 @@
#define BITCOIN_SCRIPT_SCRIPT_H
#include <crypto/common.h>
#include <mw/models/crypto/Hash.h>
#include <prevector.h>
#include <serialize.h>
@ -19,6 +20,18 @@
#include <string>
#include <vector>
// MWEB: Size of the witness program of the first output in HogEx transactions
static constexpr size_t WITNESS_MWEB_HEADERHASH_SIZE = 32;
// MWEB: Size of the witness program for peg-in transactions
static constexpr size_t WITNESS_MWEB_PEGIN_SIZE = 32;
// MWEB: Version of MWEB witness programs for HogAddr outputs
static constexpr int MWEB_HOG_ADDR_WITNESS_VERSION = 8;
// MWEB: Version of MWEB witness programs for peg-in transactions
static constexpr int MWEB_PEGIN_WITNESS_VERSION = 9;
// Maximum number of bytes pushable to the stack
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
@ -528,6 +541,9 @@ public:
bool IsPayToWitnessScriptHash() const;
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;
bool IsMWEBPegin(mw::Hash* const kernel_id = nullptr) const;
bool IsMWEBHogAddr(mw::Hash* const header_hash) const;
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
bool IsPushOnly(const_iterator pc) const;
bool IsPushOnly() const;

View File

@ -56,6 +56,8 @@ std::string GetTxnOutputType(TxoutType t)
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";
case TxoutType::WITNESS_MWEB_PEGIN: return "witness_mweb_pegin";
case TxoutType::WITNESS_MWEB_HOGADDR: return "witness_mweb_hogaddr";
case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
} // no default case, so the compiler can warn about missing cases
assert(false);
@ -136,6 +138,14 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V1_TAPROOT;
}
if (witnessversion == MWEB_PEGIN_WITNESS_VERSION && witnessprogram.size() == WITNESS_MWEB_PEGIN_SIZE) {
vSolutionsRet.push_back(witnessprogram);
return TxoutType::WITNESS_MWEB_PEGIN;
}
if (witnessversion == MWEB_HOG_ADDR_WITNESS_VERSION && witnessprogram.size() == WITNESS_MWEB_HEADERHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
return TxoutType::WITNESS_MWEB_HOGADDR;
}
if (witnessversion != 0) {
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
@ -295,6 +305,11 @@ public:
{
return CScript() << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
}
CScript operator()(const StealthAddress& id) const
{
return CScript();
}
};
} // namespace
@ -319,6 +334,26 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
return script;
}
CScript GetScriptForPegin(const mw::Hash& kernel_id)
{
CScript script;
script << CScript::EncodeOP_N(MWEB_PEGIN_WITNESS_VERSION);
script << kernel_id.vec();
return script;
}
bool IsValidDestination(const CTxDestination& dest) {
return dest.which() != 0;
}
bool IsPegInOutput(const CTxOutput& output)
{
if (!output.IsMWEB()) {
std::vector<std::vector<uint8_t>> solutions_data;
auto which_type = Solver(output.GetTxOut().scriptPubKey, solutions_data);
return which_type == TxoutType::WITNESS_MWEB_PEGIN;
}
return false;
}

View File

@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
#include <mw/models/wallet/StealthAddress.h>
#include <script/interpreter.h>
#include <uint256.h>
@ -130,6 +131,8 @@ enum class TxoutType {
WITNESS_V0_SCRIPTHASH,
WITNESS_V0_KEYHASH,
WITNESS_V1_TAPROOT,
WITNESS_MWEB_PEGIN, //!< Hash of the peg-in kernel
WITNESS_MWEB_HOGADDR, //!< HogAddr (first output of HogEx)
WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
@ -209,9 +212,10 @@ struct WitnessUnknown
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
* (taproot outputs do not require their own type as long as no wallet support exists)
* * StealthAddress: MWEB destination
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown, StealthAddress> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@ -265,4 +269,8 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
bool IsPegInOutput(const CTxOutput& output);
CScript GetScriptForPegin(const mw::Hash& kernel_id);
#endif // BITCOIN_SCRIPT_STANDARD_H

View File

@ -1473,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
static CMutableTransaction TxFromHex(const std::string& str)
{
CMutableTransaction tx;
VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS | SERIALIZE_NO_MWEB, ParseHex(str), 0) >> tx;
return tx;
}
@ -1500,6 +1500,19 @@ static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
return scriptwitness;
}
// MWEB: Pegin Script
BOOST_AUTO_TEST_CASE(script_mweb_pegin)
{
CScript script;
mw::Hash kernel_id = SecretKey::Random().vec();
script << OP_9 << kernel_id.vec();
mw::Hash kernel_id2;
BOOST_CHECK(script.IsMWEBPegin(&kernel_id2));
BOOST_CHECK(kernel_id == kernel_id2);
}
#if defined(HAVE_CONSENSUS_LIB)
/* Test simple (successful) usage of bitcoinconsensus_verify_script */

View File

@ -3735,6 +3735,14 @@ public:
return obj;
}
UniValue operator()(const StealthAddress& id) const
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("scan_pubkey", id.GetScanPubKey().ToHex());
obj.pushKV("spend_pubkey", id.GetSpendPubKey().ToHex());
return obj;
}
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};

View File

@ -97,6 +97,8 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
case TxoutType::WITNESS_MWEB_PEGIN:
case TxoutType::WITNESS_MWEB_HOGADDR:
break;
case TxoutType::PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();